@@ -791,12 +791,12 @@ async def _test_start_operation_without_service_definition(
791
791
792
792
793
793
@nexusrpc .service
794
- class MyServiceWithOperationsWithoutTypeAnnotations ( MyService ) :
794
+ class MyServiceWithOperationsWithoutTypeAnnotations :
795
795
workflow_run_operation_without_type_annotations : nexusrpc .Operation [Input , Output ]
796
796
sync_operation_without_type_annotations : nexusrpc .Operation [Input , Output ]
797
797
798
798
799
- class MyServiceHandlerWithOperationsWithoutTypeAnnotations ( MyServiceHandler ) :
799
+ class MyServiceHandlerWithOperationsWithoutTypeAnnotations :
800
800
@sync_operation
801
801
async def sync_operation_without_type_annotations (self , ctx , input ):
802
802
# Despite the lack of type annotations, the input type from the op definition in
@@ -820,13 +820,7 @@ class SyncHandlerHappyPathWithoutTypeAnnotations(_TestCase):
820
820
expected = SuccessfulResponse (
821
821
status_code = 200 ,
822
822
body_json = {
823
- "value" : "from start method on MyServiceHandler without type annotations: Input(value='hello')"
824
- },
825
- )
826
- expected_without_service_definition = SuccessfulResponse (
827
- status_code = 200 ,
828
- body_json = {
829
- "value" : "from start method on MyServiceHandler without type annotations: {'value': 'hello'}"
823
+ "value" : "from start method on MyServiceHandlerWithOperationsWithoutTypeAnnotations without type annotations: Input(value='hello')"
830
824
},
831
825
)
832
826
@@ -839,6 +833,60 @@ class AsyncHandlerHappyPathWithoutTypeAnnotations(_TestCase):
839
833
)
840
834
841
835
836
+ # Attempting to use the service_handler decorator on a class containing an operation
837
+ # without type annotations is a validation error (test coverage in nexusrpc)
838
+ @pytest .mark .parametrize (
839
+ "test_case" ,
840
+ [
841
+ SyncHandlerHappyPathWithoutTypeAnnotations ,
842
+ AsyncHandlerHappyPathWithoutTypeAnnotations ,
843
+ ],
844
+ )
845
+ async def test_start_operation_without_type_annotations (
846
+ test_case : Type [_TestCase ], env : WorkflowEnvironment
847
+ ):
848
+ if test_case .skip :
849
+ pytest .skip (test_case .skip )
850
+ task_queue = str (uuid .uuid4 ())
851
+ endpoint = (await create_nexus_endpoint (task_queue , env .client )).endpoint .id
852
+ service_client = ServiceClient (
853
+ server_address = server_address (env ),
854
+ endpoint = endpoint ,
855
+ service = MyServiceWithOperationsWithoutTypeAnnotations .__name__ ,
856
+ )
857
+
858
+ with pytest .WarningsRecorder () as warnings :
859
+ decorator = service_handler (
860
+ service = MyServiceWithOperationsWithoutTypeAnnotations
861
+ )
862
+ user_service_handler = decorator (
863
+ MyServiceHandlerWithOperationsWithoutTypeAnnotations
864
+ )()
865
+
866
+ async with Worker (
867
+ env .client ,
868
+ task_queue = task_queue ,
869
+ nexus_service_handlers = [user_service_handler ],
870
+ nexus_task_executor = concurrent .futures .ThreadPoolExecutor (),
871
+ ):
872
+ response = await service_client .start_operation (
873
+ test_case .operation ,
874
+ dataclass_as_dict (test_case .input ),
875
+ test_case .headers ,
876
+ )
877
+ test_case .check_response (response , with_service_definition = True )
878
+
879
+ assert not any (warnings ), [w .message for w in warnings ]
880
+
881
+
882
+ def test_operation_without_type_annotations_without_service_definition_raises_validation_error ():
883
+ with pytest .raises (
884
+ ValueError ,
885
+ match = r"has no input type.+has no output type" ,
886
+ ):
887
+ service_handler (MyServiceHandlerWithOperationsWithoutTypeAnnotations )
888
+
889
+
842
890
async def test_logger_uses_operation_context (env : WorkflowEnvironment , caplog : Any ):
843
891
task_queue = str (uuid .uuid4 ())
844
892
service_name = MyService .__name__
0 commit comments