Skip to content

Commit b2e31ec

Browse files
hallvictoriaVictoria Hall
and
Victoria Hall
authored
fix: enforcing unique function names (#216)
* unique function names * added blueprint tests * changing test function names * lint * lint * fixed test_decorator tests * fixed function_app tests * configurable fx name for wsgi / asgi * lint * last test * dapr test fx names * feedback * missed refs * incorrect check * missed reg * type hint --------- Co-authored-by: Victoria Hall <[email protected]>
1 parent de37877 commit b2e31ec

File tree

4 files changed

+526
-142
lines changed

4 files changed

+526
-142
lines changed

azure/functions/decorators/function_app.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ def __str__(self):
204204

205205

206206
class FunctionBuilder(object):
207+
function_bindings: dict = {}
208+
207209
def __init__(self, func, function_script_file):
208210
self._function = Function(func, function_script_file)
209211

@@ -232,6 +234,12 @@ def _validate_function(self,
232234
"""
233235
Validates the function information before building the function.
234236
237+
Functions with the same function name are not supported and should
238+
fail indexing. If a function name is not defined, the default is the
239+
method name. This also means that two functions with the same
240+
method name will also fail indexing.
241+
https://github.com/Azure/azure-functions-python-worker/issues/1489
242+
235243
:param auth_level: Http auth level that will be set if http
236244
trigger function auth level is None.
237245
"""
@@ -262,6 +270,16 @@ def _validate_function(self,
262270
parse_singular_param_to_enum(auth_level, AuthLevel))
263271
self._function._is_http_function = True
264272

273+
# This dict contains the function name and its bindings for all
274+
# functions in an app. If a previous function has the same name,
275+
# indexing will fail here.
276+
if self.function_bindings.get(function_name, None):
277+
raise ValueError(
278+
f"Function {function_name} does not have a unique"
279+
f" function name. Please change @app.function_name() or"
280+
f" the function method name to be unique.")
281+
self.function_bindings[function_name] = bindings
282+
265283
def build(self, auth_level: Optional[AuthLevel] = None) -> Function:
266284
"""
267285
Validates and builds the function object.
@@ -3333,11 +3351,13 @@ class ExternalHttpFunctionApp(
33333351
@abc.abstractmethod
33343352
def _add_http_app(self,
33353353
http_middleware: Union[
3336-
AsgiMiddleware, WsgiMiddleware]) -> None:
3354+
AsgiMiddleware, WsgiMiddleware],
3355+
function_name: str = 'http_app_func') -> None:
33373356
"""Add a Wsgi or Asgi app integrated http function.
33383357
33393358
:param http_middleware: :class:`WsgiMiddleware`
33403359
or class:`AsgiMiddleware` instance.
3360+
:param function_name: name for the function
33413361
33423362
:return: None
33433363
"""
@@ -3346,17 +3366,18 @@ def _add_http_app(self,
33463366

33473367
class AsgiFunctionApp(ExternalHttpFunctionApp):
33483368
def __init__(self, app,
3349-
http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION):
3369+
http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION,
3370+
function_name: str = 'http_app_func'):
33503371
"""Constructor of :class:`AsgiFunctionApp` object.
33513372
33523373
:param app: asgi app object.
33533374
:param http_auth_level: Determines what keys, if any, need to be
3354-
present
3355-
on the request in order to invoke the function.
3375+
present on the request in order to invoke the function.
3376+
:param function_name: function name
33563377
"""
33573378
super().__init__(auth_level=http_auth_level)
33583379
self.middleware = AsgiMiddleware(app)
3359-
self._add_http_app(self.middleware)
3380+
self._add_http_app(self.middleware, function_name)
33603381
self.startup_task_done = False
33613382

33623383
def __del__(self):
@@ -3365,7 +3386,8 @@ def __del__(self):
33653386

33663387
def _add_http_app(self,
33673388
http_middleware: Union[
3368-
AsgiMiddleware, WsgiMiddleware]) -> None:
3389+
AsgiMiddleware, WsgiMiddleware],
3390+
function_name: str = 'http_app_func') -> None:
33693391
"""Add an Asgi app integrated http function.
33703392
33713393
:param http_middleware: :class:`WsgiMiddleware`
@@ -3379,6 +3401,7 @@ def _add_http_app(self,
33793401

33803402
asgi_middleware: AsgiMiddleware = http_middleware
33813403

3404+
@self.function_name(name=function_name)
33823405
@self.http_type(http_type='asgi')
33833406
@self.route(methods=(method for method in HttpMethod),
33843407
auth_level=self.auth_level,
@@ -3395,21 +3418,25 @@ async def http_app_func(req: HttpRequest, context: Context):
33953418

33963419
class WsgiFunctionApp(ExternalHttpFunctionApp):
33973420
def __init__(self, app,
3398-
http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION):
3421+
http_auth_level: Union[AuthLevel, str] = AuthLevel.FUNCTION,
3422+
function_name: str = 'http_app_func'):
33993423
"""Constructor of :class:`WsgiFunctionApp` object.
34003424
34013425
:param app: wsgi app object.
3426+
:param function_name: function name
34023427
"""
34033428
super().__init__(auth_level=http_auth_level)
3404-
self._add_http_app(WsgiMiddleware(app))
3429+
self._add_http_app(WsgiMiddleware(app), function_name)
34053430

34063431
def _add_http_app(self,
34073432
http_middleware: Union[
3408-
AsgiMiddleware, WsgiMiddleware]) -> None:
3433+
AsgiMiddleware, WsgiMiddleware],
3434+
function_name: str = 'http_app_func') -> None:
34093435
"""Add a Wsgi app integrated http function.
34103436
34113437
:param http_middleware: :class:`WsgiMiddleware`
34123438
or class:`AsgiMiddleware` instance.
3439+
:param function_name: name for the function
34133440
34143441
:return: None
34153442
"""
@@ -3419,6 +3446,7 @@ def _add_http_app(self,
34193446

34203447
wsgi_middleware: WsgiMiddleware = http_middleware
34213448

3449+
@self.function_name(function_name)
34223450
@self.http_type(http_type='wsgi')
34233451
@self.route(methods=(method for method in HttpMethod),
34243452
auth_level=self.auth_level,

tests/decorators/test_dapr.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def test_dapr_service_invocation_trigger_default_args(self):
2424

2525
@app.dapr_service_invocation_trigger(arg_name="req",
2626
method_name="dummy_method_name")
27-
def dummy():
27+
def test_dapr_service_invocation_trigger_default_args():
2828
pass
2929

3030
func = self._get_user_function(app)
@@ -50,7 +50,7 @@ def test_dapr_binding_trigger_default_args(self):
5050

5151
@app.dapr_binding_trigger(arg_name="req",
5252
binding_name="dummy_binding_name")
53-
def dummy():
53+
def test_dapr_binding_trigger_default_args():
5454
pass
5555

5656
func = self._get_user_function(app)
@@ -73,7 +73,7 @@ def test_dapr_topic_trigger_default_args(self):
7373
pub_sub_name="dummy_pub_sub_name",
7474
topic="dummy_topic",
7575
route="/dummy_route")
76-
def dummy():
76+
def test_dapr_topic_trigger_default_args():
7777
pass
7878

7979
func = self._get_user_function(app)
@@ -99,7 +99,7 @@ def test_dapr_state_input_binding(self):
9999
@app.dapr_state_input(arg_name="in",
100100
state_store="dummy_state_store",
101101
key="dummy_key")
102-
def dummy():
102+
def test_dapr_state_input_binding():
103103
pass
104104

105105
func = self._get_user_function(app)
@@ -125,7 +125,7 @@ def test_dapr_secret_input_binding(self):
125125
secret_store_name="dummy_secret_store_name",
126126
key="dummy_key",
127127
metadata="dummy_metadata")
128-
def dummy():
128+
def test_dapr_secret_input_binding():
129129
pass
130130

131131
func = self._get_user_function(app)
@@ -151,7 +151,7 @@ def test_dapr_state_output_binding(self):
151151
@app.dapr_state_output(arg_name="out",
152152
state_store="dummy_state_store",
153153
key="dummy_key")
154-
def dummy():
154+
def test_dapr_state_output_binding():
155155
pass
156156

157157
func = self._get_user_function(app)
@@ -177,7 +177,7 @@ def test_dapr_invoke_output_binding(self):
177177
app_id="dummy_app_id",
178178
method_name="dummy_method_name",
179179
http_verb="dummy_http_verb")
180-
def dummy():
180+
def test_dapr_invoke_output_binding():
181181
pass
182182

183183
func = self._get_user_function(app)
@@ -203,7 +203,7 @@ def test_dapr_publish_output_binding(self):
203203
@app.dapr_publish_output(arg_name="out",
204204
pub_sub_name="dummy_pub_sub_name",
205205
topic="dummy_topic")
206-
def dummy():
206+
def test_dapr_publish_output_binding():
207207
pass
208208

209209
func = self._get_user_function(app)
@@ -228,7 +228,7 @@ def test_dapr_binding_output_binding(self):
228228
@app.dapr_binding_output(arg_name="out",
229229
binding_name="dummy_binding_name",
230230
operation="dummy_operation")
231-
def dummy():
231+
def test_dapr_binding_output_binding():
232232
pass
233233

234234
func = self._get_user_function(app)

0 commit comments

Comments
 (0)