From db138d0f3b1f071931df08f16ed87b3a56cd63a4 Mon Sep 17 00:00:00 2001 From: ghandic Date: Sun, 8 Jan 2023 18:28:12 +0800 Subject: [PATCH 1/8] First draft to show how to make syntactically nicer with type annotations --- spyne/decorator.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/spyne/decorator.py b/spyne/decorator.py index 38ad8ccf7..1256e5d93 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -26,6 +26,8 @@ decorator is a simple example of this. """ +from functools import wraps +import inspect import spyne.const.xml from copy import copy @@ -570,3 +572,30 @@ def srpc(*params, **kparams): def mrpc(*params, **kparams): kparams["_no_self"] = False return rpc(*params, **kparams) + + +def typed_rpc(*args, **kwargs): + no_args = False + if len(args) == 1 and not kwargs and callable(args[0]): + # Called without args + func = args[0] + no_args = True + def _typed_rpc(func): + inputs = [] + definition = inspect.signature(func) + for param_name, param_type in definition.parameters.items(): + if param_name == "self": + continue + inputs.append(param_type.annotation) + + new_func = rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) + + @wraps(new_func) + def wrapper(*args, **kwargs): + return new_func(*args, **kwargs) + + return wrapper + if no_args: + return _typed_rpc(func) + else: + return _typed_rpc \ No newline at end of file From b8790f1748dc38dbf5c160108ef003e4ce749a2f Mon Sep 17 00:00:00 2001 From: ghandic Date: Tue, 10 Jan 2023 21:12:43 +1100 Subject: [PATCH 2/8] Adding exception handling as per request --- spyne/decorator.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spyne/decorator.py b/spyne/decorator.py index 1256e5d93..2bcb3d421 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -576,6 +576,10 @@ def mrpc(*params, **kparams): def typed_rpc(*args, **kwargs): no_args = False + + if len(args) > 0 and inspect.isclass(args[0]): + raise Exception("*params must be empty when type annotations are used") + if len(args) == 1 and not kwargs and callable(args[0]): # Called without args func = args[0] @@ -588,6 +592,9 @@ def _typed_rpc(func): continue inputs.append(param_type.annotation) + if definition.return_annotation is inspect._empty: + raise Exception("_returns must be omitted when type annotations are used. Please annotate the return type") + new_func = rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) @wraps(new_func) From 0d2fda340f80c9c6651e7d956f259078bc9e06f6 Mon Sep 17 00:00:00 2001 From: ghandic Date: Tue, 10 Jan 2023 21:33:54 +1100 Subject: [PATCH 3/8] Adding tests - unsure how to run... --- spyne/decorator.py | 9 +++-- spyne/test/test_service.py | 83 +++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/spyne/decorator.py b/spyne/decorator.py index 2bcb3d421..986ebea13 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -574,11 +574,14 @@ def mrpc(*params, **kparams): return rpc(*params, **kparams) +class TypedRPCException(Exception): + ... + def typed_rpc(*args, **kwargs): no_args = False if len(args) > 0 and inspect.isclass(args[0]): - raise Exception("*params must be empty when type annotations are used") + raise ValueError("*params must be empty when type annotations are used") if len(args) == 1 and not kwargs and callable(args[0]): # Called without args @@ -588,12 +591,12 @@ def _typed_rpc(func): inputs = [] definition = inspect.signature(func) for param_name, param_type in definition.parameters.items(): - if param_name == "self": + if param_name in ("self", "ctx"): continue inputs.append(param_type.annotation) if definition.return_annotation is inspect._empty: - raise Exception("_returns must be omitted when type annotations are used. Please annotate the return type") + raise ValueError("_returns must be omitted when type annotations are used. Please annotate the return type") new_func = rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) diff --git a/spyne/test/test_service.py b/spyne/test/test_service.py index ac09aa3d1..ef45e2648 100755 --- a/spyne/test/test_service.py +++ b/spyne/test/test_service.py @@ -35,7 +35,7 @@ from spyne.model.primitive import NATIVE_MAP from spyne.service import Service -from spyne.decorator import rpc, srpc +from spyne.decorator import rpc, srpc, typed_rpc from spyne.application import Application from spyne.auxproc.sync import SyncAuxProc from spyne.auxproc.thread import ThreadAuxProc @@ -505,6 +505,87 @@ def method(ctx): raise Exception("Must fail with: " "'SelfReference can't be used inside @rpc and its ilk'") + def test_typed_rpc_works_with_no_kwargs(self): + class someCallResponse(ComplexModel): + __namespace__ = 'tns' + s = String + + class SomeService(Service): + @typed_rpc + def someCall(ctx, x: someCallResponse) -> String: + return ['abc', 'def'] + + Application( + [SomeService], + 'tns', + in_protocol=Soap11(), + out_protocol=Soap11(cleanup_namespaces=True) + ) + + def test_typed_rpc_works_with_kwargs(self): + class someCallResponse(ComplexModel): + __namespace__ = 'tns' + s = String + + class SomeService(Service): + @typed_rpc(_is_async=True) + def someCall(ctx, x: someCallResponse) -> String: + return ['abc', 'def'] + + Application( + [SomeService], + 'tns', + in_protocol=Soap11(), + out_protocol=Soap11(cleanup_namespaces=True) + ) + + def test_typed_rpc_works_with_args_raises(self): + class someCallResponse(ComplexModel): + __namespace__ = 'tns' + s = String + + class SomeService(Service): + @typed_rpc(someCallResponse, _is_async=True) + def someCall(ctx, x: someCallResponse) -> String: + return ['abc', 'def'] + expected_message = "*params must be empty when type annotations are used" + try: + Application( + [SomeService], + 'tns', + in_protocol=Soap11(), + out_protocol=Soap11(cleanup_namespaces=True) + ) + except ValueError as e: + assert str(e) == expected_message + else: + raise Exception(f"Must fail with: ValueError('{expected_message}'") + + def test_typed_rpc_works_with_no_response_raises(self): + class someCallResponse(ComplexModel): + __namespace__ = 'tns' + s = String + + class SomeService(Service): + @typed_rpc(_is_async=True) + def someCall(ctx, x: someCallResponse): + return ['abc', 'def'] + + expected_message= "Missing type annotation for the nth argument" + try: + Application( + [SomeService], + 'tns', + in_protocol=Soap11(), + out_protocol=Soap11(cleanup_namespaces=True) + ) + except ValueError as e: + assert str(e) == expected_message + else: + raise Exception("Must fail with: ValueError('{expected_message}')") + + + if __name__ == '__main__': unittest.main() From d0648b27e6d5e71ee815cd8a8e2d7473254457e8 Mon Sep 17 00:00:00 2001 From: ghandic Date: Tue, 10 Jan 2023 21:59:24 +1100 Subject: [PATCH 4/8] Adding addtional info for typed_rpc errors to assit debugging --- spyne/decorator.py | 21 ++++++++++++------ spyne/test/test_service.py | 45 +++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/spyne/decorator.py b/spyne/decorator.py index 986ebea13..ec6d0de99 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -574,15 +574,15 @@ def mrpc(*params, **kparams): return rpc(*params, **kparams) -class TypedRPCException(Exception): - ... - def typed_rpc(*args, **kwargs): no_args = False if len(args) > 0 and inspect.isclass(args[0]): raise ValueError("*params must be empty when type annotations are used") + if "_returns" in kwargs: + raise ValueError("_returns must be omitted when type annotations are used. Please annotate the return type") + if len(args) == 1 and not kwargs and callable(args[0]): # Called without args func = args[0] @@ -590,15 +590,22 @@ def typed_rpc(*args, **kwargs): def _typed_rpc(func): inputs = [] definition = inspect.signature(func) + missing_type_annotations = [] for param_name, param_type in definition.parameters.items(): if param_name in ("self", "ctx"): continue + if param_type.annotation is inspect._empty: + missing_type_annotations.append(param_name) inputs.append(param_type.annotation) + + if missing_type_annotations: + caller = inspect.getframeinfo(inspect.stack()[2][0]) + raise TypeError(f"{caller.filename}:{caller.lineno} - Missing type annotation for the parameters: {missing_type_annotations}") - if definition.return_annotation is inspect._empty: - raise ValueError("_returns must be omitted when type annotations are used. Please annotate the return type") - - new_func = rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) + if definition.return_annotation is not inspect._empty: + new_func = rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) + else: + new_func = rpc(*inputs, **kwargs)(func) @wraps(new_func) def wrapper(*args, **kwargs): diff --git a/spyne/test/test_service.py b/spyne/test/test_service.py index ef45e2648..fcfc7689b 100755 --- a/spyne/test/test_service.py +++ b/spyne/test/test_service.py @@ -512,7 +512,7 @@ class someCallResponse(ComplexModel): class SomeService(Service): @typed_rpc - def someCall(ctx, x: someCallResponse) -> String: + def someCall(ctx, x: someCallResponse) -> Array(String): return ['abc', 'def'] Application( @@ -529,7 +529,7 @@ class someCallResponse(ComplexModel): class SomeService(Service): @typed_rpc(_is_async=True) - def someCall(ctx, x: someCallResponse) -> String: + def someCall(ctx, x: someCallResponse) -> Array(String): return ['abc', 'def'] Application( @@ -539,16 +539,33 @@ def someCall(ctx, x: someCallResponse) -> String: out_protocol=Soap11(cleanup_namespaces=True) ) - def test_typed_rpc_works_with_args_raises(self): + def test_typed_rpc_works_with_no_response_works(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String class SomeService(Service): - @typed_rpc(someCallResponse, _is_async=True) - def someCall(ctx, x: someCallResponse) -> String: + @typed_rpc(_is_async=True) + def someCall(ctx, x: someCallResponse): return ['abc', 'def'] - expected_message = "*params must be empty when type annotations are used" + + Application( + [SomeService], + 'tns', + in_protocol=Soap11(), + out_protocol=Soap11(cleanup_namespaces=True) + ) + + def test_typed_rpc_works_with__returns_kwarg_raises(self): + class someCallResponse(ComplexModel): + __namespace__ = 'tns' + s = String + + class SomeService(Service): + @typed_rpc(someCallResponse, _returns=Array(String)) + def someCall(ctx, x: someCallResponse) -> Array(String): + return ['abc', 'def'] + expected_message = "_returns must be omitted when type annotations are used. Please annotate the return type" try: Application( [SomeService], @@ -560,18 +577,17 @@ def someCall(ctx, x: someCallResponse) -> String: assert str(e) == expected_message else: raise Exception(f"Must fail with: ValueError('{expected_message}'") - - def test_typed_rpc_works_with_no_response_raises(self): + + def test_typed_rpc_works_with_missing_type_annotation_raises(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String class SomeService(Service): - @typed_rpc(_is_async=True) - def someCall(ctx, x: someCallResponse): + @typed_rpc(someCallResponse) + def someCall(ctx, x: someCallResponse, y) -> Array(String): return ['abc', 'def'] - - expected_message= "Missing type annotation for the nth argument" + expected_sub_message = "Missing type annotation for the parameters: ['b']" try: Application( [SomeService], @@ -580,10 +596,9 @@ def someCall(ctx, x: someCallResponse): out_protocol=Soap11(cleanup_namespaces=True) ) except ValueError as e: - assert str(e) == expected_message + assert expected_sub_message in str(e) else: - raise Exception("Must fail with: ValueError('{expected_message}')") - + raise Exception(f"Must fail with: ValueError('{expected_sub_message}'") From 1b3ff88b2e2f96b1ce4f93d355ea60c0d5299d0f Mon Sep 17 00:00:00 2001 From: ghandic Date: Wed, 11 Jan 2023 08:47:50 +1100 Subject: [PATCH 5/8] Tests passing --- spyne/decorator.py | 2 +- spyne/test/test_service.py | 53 ++++++++------------------------------ 2 files changed, 12 insertions(+), 43 deletions(-) diff --git a/spyne/decorator.py b/spyne/decorator.py index ec6d0de99..2653131df 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -600,7 +600,7 @@ def _typed_rpc(func): if missing_type_annotations: caller = inspect.getframeinfo(inspect.stack()[2][0]) - raise TypeError(f"{caller.filename}:{caller.lineno} - Missing type annotation for the parameters: {missing_type_annotations}") + raise ValueError(f"{caller.filename}:{caller.lineno} - Missing type annotation for the parameters: {missing_type_annotations}") if definition.return_annotation is not inspect._empty: new_func = rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) diff --git a/spyne/test/test_service.py b/spyne/test/test_service.py index fcfc7689b..d75c94345 100755 --- a/spyne/test/test_service.py +++ b/spyne/test/test_service.py @@ -514,13 +514,6 @@ class SomeService(Service): @typed_rpc def someCall(ctx, x: someCallResponse) -> Array(String): return ['abc', 'def'] - - Application( - [SomeService], - 'tns', - in_protocol=Soap11(), - out_protocol=Soap11(cleanup_namespaces=True) - ) def test_typed_rpc_works_with_kwargs(self): class someCallResponse(ComplexModel): @@ -532,12 +525,6 @@ class SomeService(Service): def someCall(ctx, x: someCallResponse) -> Array(String): return ['abc', 'def'] - Application( - [SomeService], - 'tns', - in_protocol=Soap11(), - out_protocol=Soap11(cleanup_namespaces=True) - ) def test_typed_rpc_works_with_no_response_works(self): class someCallResponse(ComplexModel): @@ -548,31 +535,18 @@ class SomeService(Service): @typed_rpc(_is_async=True) def someCall(ctx, x: someCallResponse): return ['abc', 'def'] - - Application( - [SomeService], - 'tns', - in_protocol=Soap11(), - out_protocol=Soap11(cleanup_namespaces=True) - ) def test_typed_rpc_works_with__returns_kwarg_raises(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String - - class SomeService(Service): - @typed_rpc(someCallResponse, _returns=Array(String)) - def someCall(ctx, x: someCallResponse) -> Array(String): - return ['abc', 'def'] + expected_message = "_returns must be omitted when type annotations are used. Please annotate the return type" try: - Application( - [SomeService], - 'tns', - in_protocol=Soap11(), - out_protocol=Soap11(cleanup_namespaces=True) - ) + class SomeService(Service): + @typed_rpc(_returns=Array(String)) + def someCall(ctx, x: someCallResponse) -> Array(String): + return ['abc', 'def'] except ValueError as e: assert str(e) == expected_message else: @@ -583,18 +557,13 @@ class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String - class SomeService(Service): - @typed_rpc(someCallResponse) - def someCall(ctx, x: someCallResponse, y) -> Array(String): - return ['abc', 'def'] - expected_sub_message = "Missing type annotation for the parameters: ['b']" + + expected_sub_message = "Missing type annotation for the parameters: ['y']" try: - Application( - [SomeService], - 'tns', - in_protocol=Soap11(), - out_protocol=Soap11(cleanup_namespaces=True) - ) + class SomeService(Service): + @typed_rpc(_is_async=True) + def someCall(ctx, x: someCallResponse, y) -> Array(String): + return ['abc', 'def'] except ValueError as e: assert expected_sub_message in str(e) else: From 31f4e11659096e8eda8f0ae0163ab63c6b4dbab2 Mon Sep 17 00:00:00 2001 From: ghandic Date: Sat, 21 Jan 2023 19:19:30 +1100 Subject: [PATCH 6/8] Making @rpc use type annotations by default --- spyne/const/__init__.py | 7 ++++++ spyne/decorator.py | 48 +++++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/spyne/const/__init__.py b/spyne/const/__init__.py index eb5ca9043..8f4f6122b 100644 --- a/spyne/const/__init__.py +++ b/spyne/const/__init__.py @@ -76,6 +76,13 @@ """Warn about duplicate faultcodes in all Fault subclasses globally. Only works when CODE class attribute is set for every Fault subclass.""" +READ_ANNOTATIONS = True +"""Whether to use the type annotations as a mechanism of constructing the rpc call +""" +READ_ANNOTATIONS_WARNING = ('Using types in @rpc will be depreciated in future releases, ' + 'please use python type annotations for both your request and response', + DeprecationWarning, + stacklevel=2) def add_request_suffix(string): """Concatenates REQUEST_SUFFIX to end of string""" diff --git a/spyne/decorator.py b/spyne/decorator.py index 2653131df..1f98b465d 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -28,12 +28,14 @@ from functools import wraps import inspect +import warnings import spyne.const.xml from copy import copy from inspect import isclass from spyne import MethodDescriptor +import spyne.const # Empty means empty input, bare output. Doesn't say anything about response # being empty @@ -300,7 +302,7 @@ def _get_event_managers(kparams): return _event_managers if _event_managers is not None else [] -def rpc(*params, **kparams): +def _rpc(*params, **kparams): """Method decorator to tag a method as a remote procedure call in a :class:`spyne.service.Service` subclass. @@ -566,53 +568,63 @@ def srpc(*params, **kparams): """ kparams["_no_ctx"] = True - return rpc(*params, **kparams) + return _rpc(*params, **kparams) def mrpc(*params, **kparams): kparams["_no_self"] = False - return rpc(*params, **kparams) + return _rpc(*params, **kparams) -def typed_rpc(*args, **kwargs): +def rpc(*args, **kwargs): no_args = False - - if len(args) > 0 and inspect.isclass(args[0]): - raise ValueError("*params must be empty when type annotations are used") - - if "_returns" in kwargs: - raise ValueError("_returns must be omitted when type annotations are used. Please annotate the return type") - if len(args) == 1 and not kwargs and callable(args[0]): # Called without args func = args[0] no_args = True + + if not spyne.const.READ_ANNOTATIONS: + warnings.warn(*spyne.const.READ_ANNOTATIONS_WARNING) + return _rpc + def _typed_rpc(func): inputs = [] definition = inspect.signature(func) missing_type_annotations = [] + + input_type_annotations = [_param.annotation for _param in definition.parameters.values() if _param.annotation is not inspect._empty] + is_typed = definition.return_annotation is not inspect._empty or input_type_annotations + + if is_typed and args and inspect.isclass(args[0]): + raise ValueError("*params must be empty when type annotations are used") + + if is_typed and "_returns" in kwargs: + raise ValueError("_returns must be omitted when type annotations are used. Please annotate the return type") + + if not is_typed: + warnings.warn(*spyne.const.READ_ANNOTATIONS_WARNING) + return _rpc(*args, *kwargs) + for param_name, param_type in definition.parameters.items(): if param_name in ("self", "ctx"): continue if param_type.annotation is inspect._empty: missing_type_annotations.append(param_name) inputs.append(param_type.annotation) - + if missing_type_annotations: caller = inspect.getframeinfo(inspect.stack()[2][0]) raise ValueError(f"{caller.filename}:{caller.lineno} - Missing type annotation for the parameters: {missing_type_annotations}") if definition.return_annotation is not inspect._empty: - new_func = rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) + new_func = _rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) else: - new_func = rpc(*inputs, **kwargs)(func) + new_func = _rpc(*inputs, **kwargs)(func) @wraps(new_func) def wrapper(*args, **kwargs): return new_func(*args, **kwargs) return wrapper - if no_args: - return _typed_rpc(func) - else: - return _typed_rpc \ No newline at end of file + + return _typed_rpc(func) if no_args else _typed_rpc \ No newline at end of file From c22d0ef77d2859e6467a39b1cea14db303baccfd Mon Sep 17 00:00:00 2001 From: ghandic Date: Sun, 22 Jan 2023 10:45:43 +1100 Subject: [PATCH 7/8] Updating tests to reflect change --- spyne/decorator.py | 12 ++++++------ spyne/test/test_service.py | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spyne/decorator.py b/spyne/decorator.py index 1f98b465d..9b0e4f442 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -587,21 +587,21 @@ def rpc(*args, **kwargs): warnings.warn(*spyne.const.READ_ANNOTATIONS_WARNING) return _rpc - def _typed_rpc(func): + def _annotated_rpc(func): inputs = [] definition = inspect.signature(func) missing_type_annotations = [] input_type_annotations = [_param.annotation for _param in definition.parameters.values() if _param.annotation is not inspect._empty] - is_typed = definition.return_annotation is not inspect._empty or input_type_annotations + is_annotated = definition.return_annotation is not inspect._empty or input_type_annotations - if is_typed and args and inspect.isclass(args[0]): + if is_annotated and args and inspect.isclass(args[0]): raise ValueError("*params must be empty when type annotations are used") - if is_typed and "_returns" in kwargs: + if is_annotated and "_returns" in kwargs: raise ValueError("_returns must be omitted when type annotations are used. Please annotate the return type") - if not is_typed: + if not is_annotated: warnings.warn(*spyne.const.READ_ANNOTATIONS_WARNING) return _rpc(*args, *kwargs) @@ -627,4 +627,4 @@ def wrapper(*args, **kwargs): return wrapper - return _typed_rpc(func) if no_args else _typed_rpc \ No newline at end of file + return _annotated_rpc(func) if no_args else _annotated_rpc \ No newline at end of file diff --git a/spyne/test/test_service.py b/spyne/test/test_service.py index d75c94345..158fa8ee3 100755 --- a/spyne/test/test_service.py +++ b/spyne/test/test_service.py @@ -35,7 +35,7 @@ from spyne.model.primitive import NATIVE_MAP from spyne.service import Service -from spyne.decorator import rpc, srpc, typed_rpc +from spyne.decorator import rpc, srpc from spyne.application import Application from spyne.auxproc.sync import SyncAuxProc from spyne.auxproc.thread import ThreadAuxProc @@ -505,38 +505,38 @@ def method(ctx): raise Exception("Must fail with: " "'SelfReference can't be used inside @rpc and its ilk'") - def test_typed_rpc_works_with_no_kwargs(self): + def test_annotated_rpc_works_with_no_kwargs(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String class SomeService(Service): - @typed_rpc + @rpc def someCall(ctx, x: someCallResponse) -> Array(String): return ['abc', 'def'] - def test_typed_rpc_works_with_kwargs(self): + def test_annotated_rpc_works_with_kwargs(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String class SomeService(Service): - @typed_rpc(_is_async=True) + @rpc(_is_async=True) def someCall(ctx, x: someCallResponse) -> Array(String): return ['abc', 'def'] - def test_typed_rpc_works_with_no_response_works(self): + def test_annotated_rpc_works_with_no_response_works(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String class SomeService(Service): - @typed_rpc(_is_async=True) + @rpc(_is_async=True) def someCall(ctx, x: someCallResponse): return ['abc', 'def'] - def test_typed_rpc_works_with__returns_kwarg_raises(self): + def test_annotated_rpc_works_with__returns_kwarg_raises(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String @@ -544,7 +544,7 @@ class someCallResponse(ComplexModel): expected_message = "_returns must be omitted when type annotations are used. Please annotate the return type" try: class SomeService(Service): - @typed_rpc(_returns=Array(String)) + @rpc(_returns=Array(String)) def someCall(ctx, x: someCallResponse) -> Array(String): return ['abc', 'def'] except ValueError as e: @@ -552,7 +552,7 @@ def someCall(ctx, x: someCallResponse) -> Array(String): else: raise Exception(f"Must fail with: ValueError('{expected_message}'") - def test_typed_rpc_works_with_missing_type_annotation_raises(self): + def test_annotated_rpc_works_with_missing_type_annotation_raises(self): class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String @@ -561,7 +561,7 @@ class someCallResponse(ComplexModel): expected_sub_message = "Missing type annotation for the parameters: ['y']" try: class SomeService(Service): - @typed_rpc(_is_async=True) + @rpc(_is_async=True) def someCall(ctx, x: someCallResponse, y) -> Array(String): return ['abc', 'def'] except ValueError as e: From 0165753d964177dc84ea6f51f0758ce7e281069a Mon Sep 17 00:00:00 2001 From: ghandic Date: Sat, 28 Jan 2023 13:43:02 +1100 Subject: [PATCH 8/8] manually making line length <=80 --- spyne/decorator.py | 27 +++++++++++++++++++++------ spyne/test/test_service.py | 9 +++++---- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/spyne/decorator.py b/spyne/decorator.py index 9b0e4f442..dc1ad6b00 100644 --- a/spyne/decorator.py +++ b/spyne/decorator.py @@ -592,14 +592,23 @@ def _annotated_rpc(func): definition = inspect.signature(func) missing_type_annotations = [] - input_type_annotations = [_param.annotation for _param in definition.parameters.values() if _param.annotation is not inspect._empty] - is_annotated = definition.return_annotation is not inspect._empty or input_type_annotations + input_type_annotations = [ + _param.annotation for _param in definition.parameters.values() + if _param.annotation is not inspect._empty + ] + is_annotated = definition.return_annotation is not inspect._empty \ + or input_type_annotations if is_annotated and args and inspect.isclass(args[0]): - raise ValueError("*params must be empty when type annotations are used") + raise ValueError( + "*params must be empty when type annotations are used" + ) if is_annotated and "_returns" in kwargs: - raise ValueError("_returns must be omitted when type annotations are used. Please annotate the return type") + raise ValueError( + "_returns must be omitted when type annotations are used. " + "Please annotate the return type" + ) if not is_annotated: warnings.warn(*spyne.const.READ_ANNOTATIONS_WARNING) @@ -614,10 +623,16 @@ def _annotated_rpc(func): if missing_type_annotations: caller = inspect.getframeinfo(inspect.stack()[2][0]) - raise ValueError(f"{caller.filename}:{caller.lineno} - Missing type annotation for the parameters: {missing_type_annotations}") + raise ValueError( + f"{caller.filename}:{caller.lineno} - " + "Missing type annotation for the parameters: " + f"{missing_type_annotations}" + ) if definition.return_annotation is not inspect._empty: - new_func = _rpc(*inputs, _returns=definition.return_annotation, **kwargs)(func) + new_func = _rpc( + *inputs, _returns=definition.return_annotation, **kwargs + )(func) else: new_func = _rpc(*inputs, **kwargs)(func) diff --git a/spyne/test/test_service.py b/spyne/test/test_service.py index 158fa8ee3..cdf1626cd 100755 --- a/spyne/test/test_service.py +++ b/spyne/test/test_service.py @@ -541,7 +541,8 @@ class someCallResponse(ComplexModel): __namespace__ = 'tns' s = String - expected_message = "_returns must be omitted when type annotations are used. Please annotate the return type" + expected_message = "_returns must be omitted when type annotations " \ + "are used. Please annotate the return type" try: class SomeService(Service): @rpc(_returns=Array(String)) @@ -558,16 +559,16 @@ class someCallResponse(ComplexModel): s = String - expected_sub_message = "Missing type annotation for the parameters: ['y']" + e_sub_message = "Missing type annotation for the parameters: ['y']" try: class SomeService(Service): @rpc(_is_async=True) def someCall(ctx, x: someCallResponse, y) -> Array(String): return ['abc', 'def'] except ValueError as e: - assert expected_sub_message in str(e) + assert e_sub_message in str(e) else: - raise Exception(f"Must fail with: ValueError('{expected_sub_message}'") + raise Exception(f"Must fail with: ValueError('{e_sub_message}'")