55
66from . import bindings
77from . import protos
8+ from . import typing_inspect
89
910
1011class ParamTypeInfo (typing .NamedTuple ):
@@ -55,6 +56,7 @@ def add_function(self, function_id: str,
5556 func_name = metadata .name
5657 sig = inspect .signature (func )
5758 params = dict (sig .parameters )
59+ annotations = typing .get_type_hints (func )
5860
5961 input_types : typing .Dict [str , ParamTypeInfo ] = {}
6062 output_types : typing .Dict [str , ParamTypeInfo ] = {}
@@ -91,15 +93,16 @@ def add_function(self, function_id: str,
9193
9294 if 'context' in params and 'context' not in bound_params :
9395 requires_context = True
94- ctx_param = params .pop ('context' )
95- if ctx_param .annotation is not ctx_param .empty :
96- if (not isinstance (ctx_param .annotation , type ) or
97- not issubclass (ctx_param .annotation , azf .Context )):
96+ params .pop ('context' )
97+ if 'context' in annotations :
98+ ctx_anno = annotations .get ('context' )
99+ if (not isinstance (ctx_anno , type ) or
100+ not issubclass (ctx_anno , azf .Context )):
98101 raise FunctionLoadError (
99102 func_name ,
100103 f'the "context" parameter is expected to be of '
101104 f'type azure.functions.Context, got '
102- f'{ ctx_param . annotation !r} ' )
105+ f'{ ctx_anno !r} ' )
103106
104107 if set (params ) - set (bound_params ):
105108 raise FunctionLoadError (
@@ -116,16 +119,33 @@ def add_function(self, function_id: str,
116119 for param in params .values ():
117120 desc = bound_params [param .name ]
118121
119- param_has_anno = param .annotation is not param .empty
120- if param_has_anno and not isinstance (param .annotation , type ):
122+ param_has_anno = param .name in annotations
123+ param_anno = annotations .get (param .name )
124+
125+ is_param_out = (
126+ param_has_anno and
127+ (typing_inspect .is_generic_type (param_anno ) and
128+ typing_inspect .get_origin (param_anno ) == azf .Out ) or
129+ param_anno == azf .Out )
130+
131+ is_binding_out = desc .direction == protos .BindingInfo .out
132+
133+ if is_param_out :
134+ param_anno_args = typing_inspect .get_args (param_anno )
135+ if len (param_anno_args ) != 1 :
136+ raise FunctionLoadError (
137+ func_name ,
138+ f'binding { param .name } has invalid Out annotation '
139+ f'{ param_anno !r} ' )
140+ param_py_type = param_anno_args [0 ]
141+ else :
142+ param_py_type = param_anno
143+
144+ if param_has_anno and not isinstance (param_py_type , type ):
121145 raise FunctionLoadError (
122146 func_name ,
123147 f'binding { param .name } has invalid non-type annotation '
124- f'{ param .annotation !r} ' )
125-
126- is_param_out = (param_has_anno and
127- issubclass (param .annotation , azf .Out ))
128- is_binding_out = desc .direction == protos .BindingInfo .out
148+ f'{ param_anno !r} ' )
129149
130150 if is_binding_out and param_has_anno and not is_param_out :
131151 raise FunctionLoadError (
@@ -147,27 +167,18 @@ def add_function(self, function_id: str,
147167 func_name ,
148168 f'unknown type for { param .name } binding: "{ desc .type } "' )
149169
150- param_py_type = None
151170 if param_has_anno :
152171 if is_param_out :
153- assert issubclass (param .annotation , azf .Out )
154- param_py_type = param .annotation .__args__
155- if param_py_type :
156- param_py_type = param_py_type [0 ]
172+ checker = bindings .check_output_type_annotation
157173 else :
158- param_py_type = param .annotation
159- if param_py_type :
160- if is_param_out :
161- checker = bindings .check_output_type_annotation
162- else :
163- checker = bindings .check_input_type_annotation
164-
165- if not checker (param_bind_type , param_py_type ):
166- raise FunctionLoadError (
167- func_name ,
168- f'type of { param .name } binding in function.json '
169- f'"{ param_bind_type } " does not match its Python '
170- f'annotation "{ param_py_type .__name__ } "' )
174+ checker = bindings .check_input_type_annotation
175+
176+ if not checker (param_bind_type , param_py_type ):
177+ raise FunctionLoadError (
178+ func_name ,
179+ f'type of { param .name } binding in function.json '
180+ f'"{ param_bind_type } " does not match its Python '
181+ f'annotation "{ param_py_type .__name__ } "' )
171182
172183 param_type_info = ParamTypeInfo (param_bind_type , param_py_type )
173184 if is_binding_out :
@@ -176,19 +187,20 @@ def add_function(self, function_id: str,
176187 input_types [param .name ] = param_type_info
177188
178189 return_pytype = None
179- if ( return_binding_name is not None and
180- sig . return_annotation is not sig . empty ):
181- return_pytype = sig . return_annotation
182- if not isinstance ( return_pytype , type ):
190+ if return_binding_name is not None and 'return' in annotations :
191+ return_anno = annotations . get ( 'return' )
192+ if ( typing_inspect . is_generic_type ( return_anno ) and
193+ typing_inspect . get_origin ( return_anno ) == azf . Out ):
183194 raise FunctionLoadError (
184195 func_name ,
185- f'has invalid non-type return '
186- f'annotation { return_pytype !r} ' )
196+ f'return annotation should not be azure.functions.Out' )
187197
188- if issubclass (return_pytype , azf .Out ):
198+ return_pytype = return_anno
199+ if not isinstance (return_pytype , type ):
189200 raise FunctionLoadError (
190201 func_name ,
191- f'return annotation should not be azure.functions.Out' )
202+ f'has invalid non-type return '
203+ f'annotation { return_pytype !r} ' )
192204
193205 if not bindings .check_output_type_annotation (
194206 return_binding_name , return_pytype ):
0 commit comments