diff --git a/azure_functions_worker/functions.py b/azure_functions_worker/functions.py index 292fe4857..643753ba0 100644 --- a/azure_functions_worker/functions.py +++ b/azure_functions_worker/functions.py @@ -3,6 +3,7 @@ import inspect import operator import pathlib +import re import typing import uuid @@ -11,6 +12,7 @@ from ._thirdparty import typing_inspect from .constants import HTTP_TRIGGER from .protos import BindingInfo +from .logging import logger class ParamTypeInfo(typing.NamedTuple): @@ -130,6 +132,15 @@ def is_context_required(params, bound_params: dict, f'{ctx_anno!r}') return requires_context + @staticmethod + def validate_arg_name(params: dict): + pattern = r'(^\d|\b\w*__\w*|\w{129,})' + for arg_name in params: + if re.search(pattern, arg_name): + logger.warning("Argument name %s is invalid. Please " + "ensure it does not contain '__', start with a digit, " + "or exceed 128 characters.", arg_name) + @staticmethod def validate_function_params(params: dict, bound_params: dict, annotations: dict, func_name: str): @@ -389,6 +400,8 @@ def add_function(self, function_id: str, annotations, func_name) + self.validate_arg_name(params) + input_types, output_types, _ = self.validate_function_params( params, bound_params, annotations, func_name) diff --git a/tests/emulator_tests/blob_functions/blob_functions_stein/function_app.py b/tests/emulator_tests/blob_functions/blob_functions_stein/function_app.py index 24489b0e6..df77c1d44 100644 --- a/tests/emulator_tests/blob_functions/blob_functions_stein/function_app.py +++ b/tests/emulator_tests/blob_functions/blob_functions_stein/function_app.py @@ -12,29 +12,29 @@ @app.function_name(name="blob_trigger") -@app.blob_trigger(arg_name="file", +@app.blob_trigger(arg_name="file_snake", path="python-worker-tests/test-blob-trigger.txt", connection="AzureWebJobsStorage") @app.blob_output(arg_name="$return", path="python-worker-tests/test-blob-triggered.txt", connection="AzureWebJobsStorage") -def blob_trigger(file: func.InputStream) -> str: +def blob_trigger(file_snake: func.InputStream) -> str: return json.dumps({ - 'name': file.name, - 'length': file.length, - 'content': file.read().decode('utf-8') + 'name': file_snake.name, + 'length': file_snake.length, + 'content': file_snake.read().decode('utf-8') }) @app.function_name(name="get_blob_as_bytes") @app.route(route="get_blob_as_bytes") -@app.blob_input(arg_name="file", +@app.blob_input(arg_name="file_snake", path="python-worker-tests/test-bytes.txt", data_type="BINARY", connection="AzureWebJobsStorage") -def get_blob_as_bytes(req: func.HttpRequest, file: bytes) -> str: - assert isinstance(file, bytes) - return file.decode('utf-8') +def get_blob_as_bytes(req: func.HttpRequest, file_snake: bytes) -> str: + assert isinstance(file_snake, bytes) + return file_snake.decode('utf-8') @app.function_name(name="get_blob_as_bytes_return_http_response") @@ -189,13 +189,13 @@ def get_blob_triggered(req: func.HttpRequest, file: func.InputStream) -> str: @app.function_name(name="put_blob_as_bytes_return_http_response") -@app.blob_output(arg_name="file", +@app.blob_output(arg_name="file_snake", path="python-worker-tests/shmem-test-bytes-out.txt", data_type="BINARY", connection="AzureWebJobsStorage") @app.route(route="put_blob_as_bytes_return_http_response") def put_blob_as_bytes_return_http_response(req: func.HttpRequest, - file: func.Out[ + file_snake: func.Out[ bytes]) -> func.HttpResponse: """ Write a blob (bytes) and respond back (in HTTP response) with the number of @@ -214,7 +214,7 @@ def put_blob_as_bytes_return_http_response(req: func.HttpRequest, content = bytearray(random.getrandbits(8) for _ in range(content_size)) content_sha256 = hashlib.sha256(content).hexdigest() - file.set(content) + file_snake.set(content) response_dict = { 'content_size': content_size, diff --git a/tests/emulator_tests/blob_functions/blob_trigger/function.json b/tests/emulator_tests/blob_functions/blob_trigger/function.json index 85f59728d..71323bbf4 100644 --- a/tests/emulator_tests/blob_functions/blob_trigger/function.json +++ b/tests/emulator_tests/blob_functions/blob_trigger/function.json @@ -4,7 +4,7 @@ { "type": "blobTrigger", "direction": "in", - "name": "file", + "name": "file_snake", "connection": "AzureWebJobsStorage", "path": "python-worker-tests/test-blob-trigger.txt" }, diff --git a/tests/emulator_tests/blob_functions/blob_trigger/main.py b/tests/emulator_tests/blob_functions/blob_trigger/main.py index 5f162baaf..e46418995 100644 --- a/tests/emulator_tests/blob_functions/blob_trigger/main.py +++ b/tests/emulator_tests/blob_functions/blob_trigger/main.py @@ -5,9 +5,9 @@ import azure.functions as azf -def main(file: azf.InputStream) -> str: +def main(file_snake: azf.InputStream) -> str: return json.dumps({ - 'name': file.name, - 'length': file.length, - 'content': file.read().decode('utf-8') + 'name': file_snake.name, + 'length': file_snake.length, + 'content': file_snake.read().decode('utf-8') }) diff --git a/tests/emulator_tests/blob_functions/get_blob_as_bytes/function.json b/tests/emulator_tests/blob_functions/get_blob_as_bytes/function.json index 79caf12a9..bb8eb0f9f 100644 --- a/tests/emulator_tests/blob_functions/get_blob_as_bytes/function.json +++ b/tests/emulator_tests/blob_functions/get_blob_as_bytes/function.json @@ -10,7 +10,7 @@ { "type": "blob", "direction": "in", - "name": "file", + "name": "file_snake", "dataType": "binary", "connection": "AzureWebJobsStorage", "path": "python-worker-tests/test-bytes.txt" diff --git a/tests/emulator_tests/blob_functions/get_blob_as_bytes/main.py b/tests/emulator_tests/blob_functions/get_blob_as_bytes/main.py index 94a73d99e..d353232cc 100644 --- a/tests/emulator_tests/blob_functions/get_blob_as_bytes/main.py +++ b/tests/emulator_tests/blob_functions/get_blob_as_bytes/main.py @@ -3,6 +3,6 @@ import azure.functions as azf -def main(req: azf.HttpRequest, file: bytes) -> str: - assert isinstance(file, bytes) - return file.decode('utf-8') +def main(req: azf.HttpRequest, file_snake: bytes) -> str: + assert isinstance(file_snake, bytes) + return file_snake.decode('utf-8') diff --git a/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/function.json b/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/function.json index 4f8821813..c38e45fcf 100644 --- a/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/function.json +++ b/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/function.json @@ -10,7 +10,7 @@ { "type": "blob", "direction": "out", - "name": "file", + "name": "file_snake", "dataType": "binary", "connection": "AzureWebJobsStorage", "path": "python-worker-tests/shmem-test-bytes-out.txt" diff --git a/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/main.py b/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/main.py index 583258820..c54d4f8ab 100644 --- a/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/main.py +++ b/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/main.py @@ -8,7 +8,7 @@ import azure.functions as azf -def main(req: azf.HttpRequest, file: azf.Out[bytes]) -> azf.HttpResponse: +def main(req: azf.HttpRequest, file_snake: azf.Out[bytes]) -> azf.HttpResponse: """ Write a blob (bytes) and respond back (in HTTP response) with the number of bytes written and the MD5 digest of the content. @@ -26,7 +26,7 @@ def main(req: azf.HttpRequest, file: azf.Out[bytes]) -> azf.HttpResponse: content = bytearray(random.getrandbits(8) for _ in range(content_size)) content_sha256 = hashlib.sha256(content).hexdigest() - file.set(content) + file_snake.set(content) response_dict = { 'content_size': content_size, diff --git a/tests/emulator_tests/eventhub_functions/eventhub_functions_stein/function_app.py b/tests/emulator_tests/eventhub_functions/eventhub_functions_stein/function_app.py index 1481f7b55..897d8bc45 100644 --- a/tests/emulator_tests/eventhub_functions/eventhub_functions_stein/function_app.py +++ b/tests/emulator_tests/eventhub_functions/eventhub_functions_stein/function_app.py @@ -12,26 +12,26 @@ # An HttpTrigger to generating EventHub event from EventHub Output Binding @app.function_name(name="eventhub_output") @app.route(route="eventhub_output") -@app.event_hub_output(arg_name="event", +@app.event_hub_output(arg_name="event_snake", event_hub_name="python-worker-ci-eventhub-one", connection="AzureWebJobsEventHubConnectionString") -def eventhub_output(req: func.HttpRequest, event: func.Out[str]): - event.set(req.get_body().decode('utf-8')) +def eventhub_output(req: func.HttpRequest, event_snake: func.Out[str]): + event_snake.set(req.get_body().decode('utf-8')) return 'OK' # This is an actual EventHub trigger which will convert the event data # into a storage blob. @app.function_name(name="eventhub_trigger") -@app.event_hub_message_trigger(arg_name="event", +@app.event_hub_message_trigger(arg_name="event_snake", event_hub_name="python-worker-ci-eventhub-one", connection="AzureWebJobsEventHubConnectionString" ) @app.blob_output(arg_name="$return", path="python-worker-tests/test-eventhub-triggered.txt", connection="AzureWebJobsStorage") -def eventhub_trigger(event: func.EventHubEvent) -> bytes: - return event.get_body() +def eventhub_trigger(event_snake: func.EventHubEvent) -> bytes: + return event_snake.get_body() # Retrieve the event data from storage blob and return it as Http response diff --git a/tests/emulator_tests/eventhub_functions/eventhub_output/__init__.py b/tests/emulator_tests/eventhub_functions/eventhub_output/__init__.py index 9a41012b7..5adfba780 100644 --- a/tests/emulator_tests/eventhub_functions/eventhub_output/__init__.py +++ b/tests/emulator_tests/eventhub_functions/eventhub_output/__init__.py @@ -4,7 +4,7 @@ # An HttpTrigger to generating EventHub event from EventHub Output Binding -def main(req: func.HttpRequest, event: func.Out[str]): - event.set(req.get_body().decode('utf-8')) +def main(req: func.HttpRequest, event_snake: func.Out[str]): + event_snake.set(req.get_body().decode('utf-8')) return 'OK' diff --git a/tests/emulator_tests/eventhub_functions/eventhub_output/function.json b/tests/emulator_tests/eventhub_functions/eventhub_output/function.json index ec96c1617..19f599f2a 100644 --- a/tests/emulator_tests/eventhub_functions/eventhub_output/function.json +++ b/tests/emulator_tests/eventhub_functions/eventhub_output/function.json @@ -10,7 +10,7 @@ }, { "type": "eventHub", - "name": "event", + "name": "event_snake", "direction": "out", "eventHubName": "python-worker-ci-eventhub-one", "connection": "AzureWebJobsEventHubConnectionString" diff --git a/tests/emulator_tests/eventhub_functions/eventhub_trigger/__init__.py b/tests/emulator_tests/eventhub_functions/eventhub_trigger/__init__.py index bc177d499..bec13523b 100644 --- a/tests/emulator_tests/eventhub_functions/eventhub_trigger/__init__.py +++ b/tests/emulator_tests/eventhub_functions/eventhub_trigger/__init__.py @@ -5,5 +5,5 @@ # This is an actual EventHub trigger which will convert the event data # into a storage blob. -def main(event: func.EventHubEvent) -> bytes: - return event.get_body() +def main(event_snake: func.EventHubEvent) -> bytes: + return event_snake.get_body() diff --git a/tests/emulator_tests/eventhub_functions/eventhub_trigger/function.json b/tests/emulator_tests/eventhub_functions/eventhub_trigger/function.json index f8d15f4e6..fa2c1343c 100644 --- a/tests/emulator_tests/eventhub_functions/eventhub_trigger/function.json +++ b/tests/emulator_tests/eventhub_functions/eventhub_trigger/function.json @@ -4,7 +4,7 @@ "bindings": [ { "type": "eventHubTrigger", - "name": "event", + "name": "event_snake", "direction": "in", "eventHubName": "python-worker-ci-eventhub-one", "connection": "AzureWebJobsEventHubConnectionString" diff --git a/tests/emulator_tests/queue_functions/put_queue/function.json b/tests/emulator_tests/queue_functions/put_queue/function.json index b8e03f2be..d41ae1b6a 100644 --- a/tests/emulator_tests/queue_functions/put_queue/function.json +++ b/tests/emulator_tests/queue_functions/put_queue/function.json @@ -10,7 +10,7 @@ }, { "direction": "out", - "name": "msg", + "name": "msg_snake", "queueName": "testqueue", "connection": "AzureWebJobsStorage", "type": "queue" diff --git a/tests/emulator_tests/queue_functions/put_queue/main.py b/tests/emulator_tests/queue_functions/put_queue/main.py index fde178e41..f308bedd5 100644 --- a/tests/emulator_tests/queue_functions/put_queue/main.py +++ b/tests/emulator_tests/queue_functions/put_queue/main.py @@ -3,7 +3,7 @@ import azure.functions as azf -def main(req: azf.HttpRequest, msg: azf.Out[str]): - msg.set(req.get_body()) +def main(req: azf.HttpRequest, msg_snake: azf.Out[str]): + msg_snake.set(req.get_body()) return 'OK' diff --git a/tests/emulator_tests/queue_functions/queue_functions_stein/function_app.py b/tests/emulator_tests/queue_functions/queue_functions_stein/function_app.py index 087cf4592..0ebb3f6cd 100644 --- a/tests/emulator_tests/queue_functions/queue_functions_stein/function_app.py +++ b/tests/emulator_tests/queue_functions/queue_functions_stein/function_app.py @@ -49,11 +49,11 @@ def get_queue_untyped_blob_return(req: func.HttpRequest, @app.function_name(name="put_queue") @app.route(route="put_queue") -@app.queue_output(arg_name="msg", +@app.queue_output(arg_name="msg_snake", connection="AzureWebJobsStorage", queue_name="testqueue") -def put_queue(req: func.HttpRequest, msg: func.Out[str]): - msg.set(req.get_body()) +def put_queue(req: func.HttpRequest, msg_snake: func.Out[str]): + msg_snake.set(req.get_body()) return 'OK' @@ -110,24 +110,24 @@ def put_queue_untyped_return(req: func.HttpRequest, @app.function_name(name="queue_trigger") -@app.queue_trigger(arg_name="msg", +@app.queue_trigger(arg_name="msg_snake", queue_name="testqueue", connection="AzureWebJobsStorage") @app.blob_output(arg_name="$return", connection="AzureWebJobsStorage", path="python-worker-tests/test-queue-blob.txt") -def queue_trigger(msg: func.QueueMessage) -> str: +def queue_trigger(msg_snake: func.QueueMessage) -> str: result = json.dumps({ - 'id': msg.id, - 'body': msg.get_body().decode('utf-8'), - 'expiration_time': (msg.expiration_time.isoformat() - if msg.expiration_time else None), - 'insertion_time': (msg.insertion_time.isoformat() - if msg.insertion_time else None), - 'time_next_visible': (msg.time_next_visible.isoformat() - if msg.time_next_visible else None), - 'pop_receipt': msg.pop_receipt, - 'dequeue_count': msg.dequeue_count + 'id': msg_snake.id, + 'body': msg_snake.get_body().decode('utf-8'), + 'expiration_time': (msg_snake.expiration_time.isoformat() + if msg_snake.expiration_time else None), + 'insertion_time': (msg_snake.insertion_time.isoformat() + if msg_snake.insertion_time else None), + 'time_next_visible': (msg_snake.time_next_visible.isoformat() + if msg_snake.time_next_visible else None), + 'pop_receipt': msg_snake.pop_receipt, + 'dequeue_count': msg_snake.dequeue_count }) return result diff --git a/tests/emulator_tests/queue_functions/queue_trigger/function.json b/tests/emulator_tests/queue_functions/queue_trigger/function.json index 9c7f2b322..1565ba3e9 100644 --- a/tests/emulator_tests/queue_functions/queue_trigger/function.json +++ b/tests/emulator_tests/queue_functions/queue_trigger/function.json @@ -5,7 +5,7 @@ { "type": "queueTrigger", "direction": "in", - "name": "msg", + "name": "msg_snake", "queueName": "testqueue", "connection": "AzureWebJobsStorage", }, diff --git a/tests/emulator_tests/queue_functions/queue_trigger/main.py b/tests/emulator_tests/queue_functions/queue_trigger/main.py index 08a5d4e9d..288d3789b 100644 --- a/tests/emulator_tests/queue_functions/queue_trigger/main.py +++ b/tests/emulator_tests/queue_functions/queue_trigger/main.py @@ -5,18 +5,18 @@ import azure.functions as azf -def main(msg: azf.QueueMessage) -> str: +def main(msg_snake: azf.QueueMessage) -> str: result = json.dumps({ - 'id': msg.id, - 'body': msg.get_body().decode('utf-8'), - 'expiration_time': (msg.expiration_time.isoformat() - if msg.expiration_time else None), - 'insertion_time': (msg.insertion_time.isoformat() - if msg.insertion_time else None), - 'time_next_visible': (msg.time_next_visible.isoformat() - if msg.time_next_visible else None), - 'pop_receipt': msg.pop_receipt, - 'dequeue_count': msg.dequeue_count + 'id': msg_snake.id, + 'body': msg_snake.get_body().decode('utf-8'), + 'expiration_time': (msg_snake.expiration_time.isoformat() + if msg_snake.expiration_time else None), + 'insertion_time': (msg_snake.insertion_time.isoformat() + if msg_snake.insertion_time else None), + 'time_next_visible': (msg_snake.time_next_visible.isoformat() + if msg_snake.time_next_visible else None), + 'pop_receipt': msg_snake.pop_receipt, + 'dequeue_count': msg_snake.dequeue_count }) return result diff --git a/tests/emulator_tests/servicebus_functions/put_message/__init__.py b/tests/emulator_tests/servicebus_functions/put_message/__init__.py index 85ad99bf7..405fc86b1 100644 --- a/tests/emulator_tests/servicebus_functions/put_message/__init__.py +++ b/tests/emulator_tests/servicebus_functions/put_message/__init__.py @@ -3,7 +3,7 @@ import azure.functions as azf -def main(req: azf.HttpRequest, msg: azf.Out[str]): - msg.set(req.get_body().decode('utf-8')) +def main(req: azf.HttpRequest, msg_snake: azf.Out[str]): + msg_snake.set(req.get_body().decode('utf-8')) return 'OK' diff --git a/tests/emulator_tests/servicebus_functions/put_message/function.json b/tests/emulator_tests/servicebus_functions/put_message/function.json index 722f19541..dfa7b17fb 100644 --- a/tests/emulator_tests/servicebus_functions/put_message/function.json +++ b/tests/emulator_tests/servicebus_functions/put_message/function.json @@ -10,7 +10,7 @@ }, { "direction": "out", - "name": "msg", + "name": "msg_snake", "queueName": "testqueue", "connection": "AzureWebJobsServiceBusConnectionString", "type": "serviceBus" diff --git a/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/function_app.py b/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/function_app.py index 9e9d12246..9aee8b626 100644 --- a/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/function_app.py +++ b/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/function_app.py @@ -7,11 +7,11 @@ @app.route(route="put_message") @app.service_bus_queue_output( - arg_name="msg", + arg_name="msg_snake", connection="AzureWebJobsServiceBusConnectionString", queue_name="testqueue") -def put_message(req: func.HttpRequest, msg: func.Out[str]): - msg.set(req.get_body().decode('utf-8')) +def put_message(req: func.HttpRequest, msg_snake: func.Out[str]): + msg_snake.set(req.get_body().decode('utf-8')) return 'OK' @@ -26,48 +26,48 @@ def get_servicebus_triggered(req: func.HttpRequest, @app.service_bus_queue_trigger( - arg_name="msg", + arg_name="msg_snake", connection="AzureWebJobsServiceBusConnectionString", queue_name="testqueue") @app.blob_output(arg_name="$return", path="python-worker-tests/test-servicebus-triggered.txt", connection="AzureWebJobsStorage") -def servicebus_trigger(msg: func.ServiceBusMessage) -> str: +def servicebus_trigger(msg_snake: func.ServiceBusMessage) -> str: result = json.dumps({ - 'message_id': msg.message_id, - 'body': msg.get_body().decode('utf-8'), - 'content_type': msg.content_type, - 'delivery_count': msg.delivery_count, - 'expiration_time': (msg.expiration_time.isoformat() if - msg.expiration_time else None), - 'label': msg.label, - 'partition_key': msg.partition_key, - 'reply_to': msg.reply_to, - 'reply_to_session_id': msg.reply_to_session_id, - 'scheduled_enqueue_time': (msg.scheduled_enqueue_time.isoformat() if - msg.scheduled_enqueue_time else None), - 'session_id': msg.session_id, - 'time_to_live': msg.time_to_live, - 'to': msg.to, - 'user_properties': msg.user_properties, + 'message_id': msg_snake.message_id, + 'body': msg_snake.get_body().decode('utf-8'), + 'content_type': msg_snake.content_type, + 'delivery_count': msg_snake.delivery_count, + 'expiration_time': (msg_snake.expiration_time.isoformat() if + msg_snake.expiration_time else None), + 'label': msg_snake.label, + 'partition_key': msg_snake.partition_key, + 'reply_to': msg_snake.reply_to, + 'reply_to_session_id': msg_snake.reply_to_session_id, + 'scheduled_enqueue_time': (msg_snake.scheduled_enqueue_time.isoformat() if + msg_snake.scheduled_enqueue_time else None), + 'session_id': msg_snake.session_id, + 'time_to_live': msg_snake.time_to_live, + 'to': msg_snake.to, + 'user_properties': msg_snake.user_properties, - 'application_properties': msg.application_properties, - 'correlation_id': msg.correlation_id, - 'dead_letter_error_description': msg.dead_letter_error_description, - 'dead_letter_reason': msg.dead_letter_reason, - 'dead_letter_source': msg.dead_letter_source, - 'enqueued_sequence_number': msg.enqueued_sequence_number, - 'enqueued_time_utc': (msg.enqueued_time_utc.isoformat() if - msg.enqueued_time_utc else None), - 'expires_at_utc': (msg.expires_at_utc.isoformat() if - msg.expires_at_utc else None), - 'locked_until': (msg.locked_until.isoformat() if - msg.locked_until else None), - 'lock_token': msg.lock_token, - 'sequence_number': msg.sequence_number, - 'state': msg.state, - 'subject': msg.subject, - 'transaction_partition_key': msg.transaction_partition_key + 'application_properties': msg_snake.application_properties, + 'correlation_id': msg_snake.correlation_id, + 'dead_letter_error_description': msg_snake.dead_letter_error_description, + 'dead_letter_reason': msg_snake.dead_letter_reason, + 'dead_letter_source': msg_snake.dead_letter_source, + 'enqueued_sequence_number': msg_snake.enqueued_sequence_number, + 'enqueued_time_utc': (msg_snake.enqueued_time_utc.isoformat() if + msg_snake.enqueued_time_utc else None), + 'expires_at_utc': (msg_snake.expires_at_utc.isoformat() if + msg_snake.expires_at_utc else None), + 'locked_until': (msg_snake.locked_until.isoformat() if + msg_snake.locked_until else None), + 'lock_token': msg_snake.lock_token, + 'sequence_number': msg_snake.sequence_number, + 'state': msg_snake.state, + 'subject': msg_snake.subject, + 'transaction_partition_key': msg_snake.transaction_partition_key }) return result diff --git a/tests/emulator_tests/servicebus_functions/servicebus_trigger/__init__.py b/tests/emulator_tests/servicebus_functions/servicebus_trigger/__init__.py index 341779a4d..64efe6c3e 100644 --- a/tests/emulator_tests/servicebus_functions/servicebus_trigger/__init__.py +++ b/tests/emulator_tests/servicebus_functions/servicebus_trigger/__init__.py @@ -5,42 +5,42 @@ import azure.functions as azf -def main(msg: azf.ServiceBusMessage) -> str: +def main(msg_snake: azf.ServiceBusMessage) -> str: result = json.dumps({ - 'message_id': msg.message_id, - 'body': msg.get_body().decode('utf-8'), - 'content_type': msg.content_type, - 'delivery_count': msg.delivery_count, - 'expiration_time': (msg.expiration_time.isoformat() if - msg.expiration_time else None), - 'label': msg.label, - 'partition_key': msg.partition_key, - 'reply_to': msg.reply_to, - 'reply_to_session_id': msg.reply_to_session_id, - 'scheduled_enqueue_time': (msg.scheduled_enqueue_time.isoformat() if - msg.scheduled_enqueue_time else None), - 'session_id': msg.session_id, - 'time_to_live': msg.time_to_live, - 'to': msg.to, - 'user_properties': msg.user_properties, + 'message_id': msg_snake.message_id, + 'body': msg_snake.get_body().decode('utf-8'), + 'content_type': msg_snake.content_type, + 'delivery_count': msg_snake.delivery_count, + 'expiration_time': (msg_snake.expiration_time.isoformat() if + msg_snake.expiration_time else None), + 'label': msg_snake.label, + 'partition_key': msg_snake.partition_key, + 'reply_to': msg_snake.reply_to, + 'reply_to_session_id': msg_snake.reply_to_session_id, + 'scheduled_enqueue_time': (msg_snake.scheduled_enqueue_time.isoformat() if + msg_snake.scheduled_enqueue_time else None), + 'session_id': msg_snake.session_id, + 'time_to_live': msg_snake.time_to_live, + 'to': msg_snake.to, + 'user_properties': msg_snake.user_properties, - 'application_properties': msg.application_properties, - 'correlation_id': msg.correlation_id, - 'dead_letter_error_description': msg.dead_letter_error_description, - 'dead_letter_reason': msg.dead_letter_reason, - 'dead_letter_source': msg.dead_letter_source, - 'enqueued_sequence_number': msg.enqueued_sequence_number, - 'enqueued_time_utc': (msg.enqueued_time_utc.isoformat() if - msg.enqueued_time_utc else None), - 'expires_at_utc': (msg.expires_at_utc.isoformat() if - msg.expires_at_utc else None), - 'locked_until': (msg.locked_until.isoformat() if - msg.locked_until else None), - 'lock_token': msg.lock_token, - 'sequence_number': msg.sequence_number, - 'state': msg.state, - 'subject': msg.subject, - 'transaction_partition_key': msg.transaction_partition_key + 'application_properties': msg_snake.application_properties, + 'correlation_id': msg_snake.correlation_id, + 'dead_letter_error_description': msg_snake.dead_letter_error_description, + 'dead_letter_reason': msg_snake.dead_letter_reason, + 'dead_letter_source': msg_snake.dead_letter_source, + 'enqueued_sequence_number': msg_snake.enqueued_sequence_number, + 'enqueued_time_utc': (msg_snake.enqueued_time_utc.isoformat() if + msg_snake.enqueued_time_utc else None), + 'expires_at_utc': (msg_snake.expires_at_utc.isoformat() if + msg_snake.expires_at_utc else None), + 'locked_until': (msg_snake.locked_until.isoformat() if + msg_snake.locked_until else None), + 'lock_token': msg_snake.lock_token, + 'sequence_number': msg_snake.sequence_number, + 'state': msg_snake.state, + 'subject': msg_snake.subject, + 'transaction_partition_key': msg_snake.transaction_partition_key }) return result diff --git a/tests/emulator_tests/servicebus_functions/servicebus_trigger/function.json b/tests/emulator_tests/servicebus_functions/servicebus_trigger/function.json index b6fe4355e..364b4454f 100644 --- a/tests/emulator_tests/servicebus_functions/servicebus_trigger/function.json +++ b/tests/emulator_tests/servicebus_functions/servicebus_trigger/function.json @@ -5,7 +5,7 @@ { "type": "serviceBusTrigger", "direction": "in", - "name": "msg", + "name": "msg_snake", "queueName": "testqueue", "connection": "AzureWebJobsServiceBusConnectionString", }, diff --git a/tests/emulator_tests/table_functions/table_functions_stein/function_app.py b/tests/emulator_tests/table_functions/table_functions_stein/function_app.py index 5ebd10e07..bcd1c421c 100644 --- a/tests/emulator_tests/table_functions/table_functions_stein/function_app.py +++ b/tests/emulator_tests/table_functions/table_functions_stein/function_app.py @@ -10,13 +10,13 @@ @app.function_name(name="table_in_binding") @app.route(route="table_in_binding/{id}") -@app.table_input(arg_name="testEntity", +@app.table_input(arg_name="testEntity_snake", connection="AzureWebJobsStorage", table_name="BindingTestTable", row_key='{id}', partition_key="test") -def table_in_binding(req: func.HttpRequest, testEntity): - return func.HttpResponse(status_code=200, body=testEntity) +def table_in_binding(req: func.HttpRequest, testEntity_snake): + return func.HttpResponse(status_code=200, body=testEntity_snake) @app.function_name(name="table_out_binding") diff --git a/tests/emulator_tests/table_functions/table_in_binding/__init__.py b/tests/emulator_tests/table_functions/table_in_binding/__init__.py index a125e2bdb..49e64b25b 100644 --- a/tests/emulator_tests/table_functions/table_in_binding/__init__.py +++ b/tests/emulator_tests/table_functions/table_in_binding/__init__.py @@ -3,5 +3,5 @@ import azure.functions as func -def main(req: func.HttpRequest, testEntity): - return func.HttpResponse(status_code=200, body=testEntity) +def main(req: func.HttpRequest, testEntity_snake): + return func.HttpResponse(status_code=200, body=testEntity_snake) diff --git a/tests/emulator_tests/table_functions/table_in_binding/function.json b/tests/emulator_tests/table_functions/table_in_binding/function.json index d62461d0b..c1f1382e7 100644 --- a/tests/emulator_tests/table_functions/table_in_binding/function.json +++ b/tests/emulator_tests/table_functions/table_in_binding/function.json @@ -13,7 +13,7 @@ { "direction": "in", "type": "table", - "name": "testEntity", + "name": "testEntity_snake", "partitionKey": "test", "rowKey": "WillBePopulatedWithGuid", "tableName": "BindingTestTable", diff --git a/tests/endtoend/cosmosdb_functions/cosmosdb_functions_stein/function_app.py b/tests/endtoend/cosmosdb_functions/cosmosdb_functions_stein/function_app.py index c0ddcaad1..3775893f0 100644 --- a/tests/endtoend/cosmosdb_functions/cosmosdb_functions_stein/function_app.py +++ b/tests/endtoend/cosmosdb_functions/cosmosdb_functions_stein/function_app.py @@ -7,24 +7,24 @@ @app.route() @app.cosmos_db_input( - arg_name="docs", database_name="test", + arg_name="docs_snake", database_name="test", container_name="items", id="cosmosdb-input-test", connection="AzureWebJobsCosmosDBConnectionString") -def cosmosdb_input(req: func.HttpRequest, docs: func.DocumentList) -> str: - return func.HttpResponse(docs[0].to_json(), mimetype='application/json') +def cosmosdb_input(req: func.HttpRequest, docs_snake: func.DocumentList) -> str: + return func.HttpResponse(docs_snake[0].to_json(), mimetype='application/json') @app.cosmos_db_trigger( - arg_name="docs", database_name="test", + arg_name="docs_snake", database_name="test", container_name="items", lease_container_name="leases", connection="AzureWebJobsCosmosDBConnectionString", create_lease_container_if_not_exists=True) @app.blob_output(arg_name="$return", connection="AzureWebJobsStorage", path="python-worker-tests/test-cosmosdb-triggered.txt") -def cosmosdb_trigger(docs: func.DocumentList) -> str: - return docs[0].to_json() +def cosmosdb_trigger(docs_snake: func.DocumentList) -> str: + return docs_snake[0].to_json() @app.route() @@ -37,10 +37,10 @@ def get_cosmosdb_triggered(req: func.HttpRequest, @app.route() @app.cosmos_db_output( - arg_name="doc", database_name="test", + arg_name="doc_snake", database_name="test", container_name="items", create_if_not_exists=True, connection="AzureWebJobsCosmosDBConnectionString") -def put_document(req: func.HttpRequest, doc: func.Out[func.Document]): - doc.set(func.Document.from_json(req.get_body())) +def put_document(req: func.HttpRequest, doc_snake: func.Out[func.Document]): + doc_snake.set(func.Document.from_json(req.get_body())) return 'OK' diff --git a/tests/endtoend/cosmosdb_functions/cosmosdb_input/__init__.py b/tests/endtoend/cosmosdb_functions/cosmosdb_input/__init__.py index 313d63137..cb4fbeeeb 100644 --- a/tests/endtoend/cosmosdb_functions/cosmosdb_input/__init__.py +++ b/tests/endtoend/cosmosdb_functions/cosmosdb_input/__init__.py @@ -3,5 +3,5 @@ import azure.functions as func -def main(req: func.HttpRequest, docs: func.DocumentList) -> str: - return func.HttpResponse(docs[0].to_json(), mimetype='application/json') +def main(req: func.HttpRequest, docs_snake: func.DocumentList) -> str: + return func.HttpResponse(docs_snake[0].to_json(), mimetype='application/json') diff --git a/tests/endtoend/cosmosdb_functions/cosmosdb_input/function.json b/tests/endtoend/cosmosdb_functions/cosmosdb_input/function.json index 23608f043..8b9224ea2 100644 --- a/tests/endtoend/cosmosdb_functions/cosmosdb_input/function.json +++ b/tests/endtoend/cosmosdb_functions/cosmosdb_input/function.json @@ -11,7 +11,7 @@ { "direction": "in", "type": "cosmosDB", - "name": "docs", + "name": "docs_snake", "databaseName": "test", "containerName": "items", "id": "cosmosdb-input-test", diff --git a/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/__init__.py b/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/__init__.py index a8868aa79..0dcda31cb 100644 --- a/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/__init__.py +++ b/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/__init__.py @@ -3,5 +3,5 @@ import azure.functions as azf -def main(docs: azf.DocumentList) -> str: - return docs[0].to_json() +def main(docs_snake: azf.DocumentList) -> str: + return docs_snake[0].to_json() diff --git a/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/function.json b/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/function.json index 76a24c07d..796b44265 100644 --- a/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/function.json +++ b/tests/endtoend/cosmosdb_functions/cosmosdb_trigger/function.json @@ -5,7 +5,7 @@ { "direction": "in", "type": "cosmosDBTrigger", - "name": "docs", + "name": "docs_snake", "databaseName": "test", "containerName": "items", "id": "cosmosdb-trigger-test", diff --git a/tests/endtoend/cosmosdb_functions/put_document/__init__.py b/tests/endtoend/cosmosdb_functions/put_document/__init__.py index 5e481332e..e898f4a61 100644 --- a/tests/endtoend/cosmosdb_functions/put_document/__init__.py +++ b/tests/endtoend/cosmosdb_functions/put_document/__init__.py @@ -3,7 +3,7 @@ import azure.functions as func -def main(req: func.HttpRequest, doc: func.Out[func.Document]): - doc.set(func.Document.from_json(req.get_body())) +def main(req: func.HttpRequest, doc_snake: func.Out[func.Document]): + doc_snake.set(func.Document.from_json(req.get_body())) return 'OK' diff --git a/tests/endtoend/cosmosdb_functions/put_document/function.json b/tests/endtoend/cosmosdb_functions/put_document/function.json index b385fbfd5..2fcb41e71 100644 --- a/tests/endtoend/cosmosdb_functions/put_document/function.json +++ b/tests/endtoend/cosmosdb_functions/put_document/function.json @@ -11,7 +11,7 @@ { "direction": "out", "type": "cosmosDB", - "name": "doc", + "name": "doc_snake", "databaseName": "test", "containerName": "items", "leaseContainerName": "leases", diff --git a/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/function_app.py b/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/function_app.py index 94f05bf22..e1387f85d 100644 --- a/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/function_app.py +++ b/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/function_app.py @@ -9,30 +9,30 @@ @app.function_name(name="eventGridTrigger") -@app.event_grid_trigger(arg_name="event") +@app.event_grid_trigger(arg_name="event_snake") @app.blob_output(arg_name="$return", path="python-worker-tests/test-eventgrid-triggered.txt", connection="AzureWebJobsStorage") -def event_grid_trigger(event: func.EventGridEvent) -> str: +def event_grid_trigger(event_snake: func.EventGridEvent) -> str: logging.info("Event grid function is triggered!") return json.dumps({ - 'id': event.id, - 'data': event.get_json(), - 'topic': event.topic, - 'subject': event.subject, - 'event_type': event.event_type, + 'id': event_snake.id, + 'data': event_snake.get_json(), + 'topic': event_snake.topic, + 'subject': event_snake.subject, + 'event_type': event_snake.event_type, }) @app.function_name(name="eventgrid_output_binding") @app.route(route="eventgrid_output_binding") @app.event_grid_output( - arg_name="outputEvent", + arg_name="outputEvent_snake", topic_endpoint_uri="AzureWebJobsEventGridTopicUri", topic_key_setting="AzureWebJobsEventGridConnectionKey") def eventgrid_output_binding( req: func.HttpRequest, - outputEvent: func.Out[func.EventGridOutputEvent]) -> func.HttpResponse: + outputEvent_snake: func.Out[func.EventGridOutputEvent]) -> func.HttpResponse: test_uuid = req.params.get('test_uuid') data_to_event_grid = func.EventGridOutputEvent(id="test-id", data={ @@ -43,7 +43,7 @@ def eventgrid_output_binding( event_time=datetime.utcnow(), data_version="1.0") - outputEvent.set(data_to_event_grid) + outputEvent_snake.set(data_to_event_grid) r_value = "Sent event with subject: {}, id: {}, data: {}, event_type: {} " \ "to EventGrid!".format(data_to_event_grid.subject, data_to_event_grid.id, diff --git a/tests/endtoend/eventgrid_functions/eventgrid_output_binding/__init__.py b/tests/endtoend/eventgrid_functions/eventgrid_output_binding/__init__.py index 817e31396..88ca6d915 100644 --- a/tests/endtoend/eventgrid_functions/eventgrid_output_binding/__init__.py +++ b/tests/endtoend/eventgrid_functions/eventgrid_output_binding/__init__.py @@ -7,7 +7,7 @@ def main(req: func.HttpRequest, - outputEvent: func.Out[func.EventGridOutputEvent]) -> func.HttpResponse: + outputEvent_snake: func.Out[func.EventGridOutputEvent]) -> func.HttpResponse: test_uuid = req.params.get('test_uuid') data_to_event_grid = func.EventGridOutputEvent(id="test-id", data={ @@ -18,7 +18,7 @@ def main(req: func.HttpRequest, event_time=datetime.utcnow(), data_version="1.0") - outputEvent.set(data_to_event_grid) + outputEvent_snake.set(data_to_event_grid) r_value = "Sent event with subject: {}, id: {}, data: {}, event_type: {} " \ "to EventGrid!".format(data_to_event_grid.subject, data_to_event_grid.id, diff --git a/tests/endtoend/eventgrid_functions/eventgrid_output_binding/function.json b/tests/endtoend/eventgrid_functions/eventgrid_output_binding/function.json index 1c0343465..cbabbda62 100644 --- a/tests/endtoend/eventgrid_functions/eventgrid_output_binding/function.json +++ b/tests/endtoend/eventgrid_functions/eventgrid_output_binding/function.json @@ -10,7 +10,7 @@ }, { "type": "eventGrid", - "name": "outputEvent", + "name": "outputEvent_snake", "topicEndpointUri": "AzureWebJobsEventGridTopicUri", "topicKeySetting": "AzureWebJobsEventGridConnectionKey", "direction": "out" diff --git a/tests/endtoend/eventgrid_functions/eventgrid_trigger/__init__.py b/tests/endtoend/eventgrid_functions/eventgrid_trigger/__init__.py index b2b414623..5dfa72e43 100644 --- a/tests/endtoend/eventgrid_functions/eventgrid_trigger/__init__.py +++ b/tests/endtoend/eventgrid_functions/eventgrid_trigger/__init__.py @@ -5,11 +5,11 @@ import azure.functions as func -def main(event: func.EventGridEvent) -> str: +def main(event_snake: func.EventGridEvent) -> str: return json.dumps({ - 'id': event.id, - 'data': event.get_json(), - 'topic': event.topic, - 'subject': event.subject, - 'event_type': event.event_type, + 'id': event_snake.id, + 'data': event_snake.get_json(), + 'topic': event_snake.topic, + 'subject': event_snake.subject, + 'event_type': event_snake.event_type, }) diff --git a/tests/endtoend/eventgrid_functions/eventgrid_trigger/function.json b/tests/endtoend/eventgrid_functions/eventgrid_trigger/function.json index bf33c7072..72dc54a44 100644 --- a/tests/endtoend/eventgrid_functions/eventgrid_trigger/function.json +++ b/tests/endtoend/eventgrid_functions/eventgrid_trigger/function.json @@ -5,7 +5,7 @@ { "type": "eventGridTrigger", "direction": "in", - "name": "event" + "name": "event_snake" }, { "type": "blob", diff --git a/tests/endtoend/snake_case_functions/classic_snake_case/__init__.py b/tests/endtoend/snake_case_functions/classic_snake_case/__init__.py new file mode 100644 index 000000000..6a9938230 --- /dev/null +++ b/tests/endtoend/snake_case_functions/classic_snake_case/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(req_snake: func.HttpRequest) -> func.HttpResponse: + name = req_snake.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/classic_snake_case/function.json b/tests/endtoend/snake_case_functions/classic_snake_case/function.json new file mode 100644 index 000000000..58ad53314 --- /dev/null +++ b/tests/endtoend/snake_case_functions/classic_snake_case/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req_snake", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/double_underscore/__init__.py b/tests/endtoend/snake_case_functions/double_underscore/__init__.py new file mode 100644 index 000000000..d5104161a --- /dev/null +++ b/tests/endtoend/snake_case_functions/double_underscore/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(req__snake: func.HttpRequest) -> func.HttpResponse: + name = req__snake.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/double_underscore/function.json b/tests/endtoend/snake_case_functions/double_underscore/function.json new file mode 100644 index 000000000..d895aefbc --- /dev/null +++ b/tests/endtoend/snake_case_functions/double_underscore/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req__snake", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/double_underscore_prefix/__init__.py b/tests/endtoend/snake_case_functions/double_underscore_prefix/__init__.py new file mode 100644 index 000000000..0452c9a72 --- /dev/null +++ b/tests/endtoend/snake_case_functions/double_underscore_prefix/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(__req: func.HttpRequest) -> func.HttpResponse: + name = __req.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/double_underscore_prefix/function.json b/tests/endtoend/snake_case_functions/double_underscore_prefix/function.json new file mode 100644 index 000000000..9c4ea3f8c --- /dev/null +++ b/tests/endtoend/snake_case_functions/double_underscore_prefix/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "__req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/double_underscore_suffix/__init__.py b/tests/endtoend/snake_case_functions/double_underscore_suffix/__init__.py new file mode 100644 index 000000000..9c529729b --- /dev/null +++ b/tests/endtoend/snake_case_functions/double_underscore_suffix/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(req__: func.HttpRequest) -> func.HttpResponse: + name = req__.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/double_underscore_suffix/function.json b/tests/endtoend/snake_case_functions/double_underscore_suffix/function.json new file mode 100644 index 000000000..dbbeaab54 --- /dev/null +++ b/tests/endtoend/snake_case_functions/double_underscore_suffix/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req__", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/just_double_underscore/__init__.py b/tests/endtoend/snake_case_functions/just_double_underscore/__init__.py new file mode 100644 index 000000000..d2a6b3e30 --- /dev/null +++ b/tests/endtoend/snake_case_functions/just_double_underscore/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(__: func.HttpRequest) -> func.HttpResponse: + name = __.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/just_double_underscore/function.json b/tests/endtoend/snake_case_functions/just_double_underscore/function.json new file mode 100644 index 000000000..54b67af8b --- /dev/null +++ b/tests/endtoend/snake_case_functions/just_double_underscore/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "__", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/python_main_keyword/__init__.py b/tests/endtoend/snake_case_functions/python_main_keyword/__init__.py new file mode 100644 index 000000000..c72788afd --- /dev/null +++ b/tests/endtoend/snake_case_functions/python_main_keyword/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(__main__: func.HttpRequest) -> func.HttpResponse: + name = __main__.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/python_main_keyword/function.json b/tests/endtoend/snake_case_functions/python_main_keyword/function.json new file mode 100644 index 000000000..dc66ab087 --- /dev/null +++ b/tests/endtoend/snake_case_functions/python_main_keyword/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "__main__", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/sandwich/__init__.py b/tests/endtoend/snake_case_functions/sandwich/__init__.py new file mode 100644 index 000000000..5f7792463 --- /dev/null +++ b/tests/endtoend/snake_case_functions/sandwich/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(_req_: func.HttpRequest) -> func.HttpResponse: + name = _req_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/sandwich/function.json b/tests/endtoend/snake_case_functions/sandwich/function.json new file mode 100644 index 000000000..7f51aceb9 --- /dev/null +++ b/tests/endtoend/snake_case_functions/sandwich/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "_req_", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/single_underscore/__init__.py b/tests/endtoend/snake_case_functions/single_underscore/__init__.py new file mode 100644 index 000000000..804a9f42c --- /dev/null +++ b/tests/endtoend/snake_case_functions/single_underscore/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(_: func.HttpRequest) -> func.HttpResponse: + name = _.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/single_underscore/function.json b/tests/endtoend/snake_case_functions/single_underscore/function.json new file mode 100644 index 000000000..94f26ae3a --- /dev/null +++ b/tests/endtoend/snake_case_functions/single_underscore/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "_", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/ultimate_combo/__init__.py b/tests/endtoend/snake_case_functions/ultimate_combo/__init__.py new file mode 100644 index 000000000..c87691ad2 --- /dev/null +++ b/tests/endtoend/snake_case_functions/ultimate_combo/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(_req_snake_snake_snake_snake_: func.HttpRequest) -> func.HttpResponse: + name = _req_snake_snake_snake_snake_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/ultimate_combo/function.json b/tests/endtoend/snake_case_functions/ultimate_combo/function.json new file mode 100644 index 000000000..51cb69358 --- /dev/null +++ b/tests/endtoend/snake_case_functions/ultimate_combo/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "_req_snake_snake_snake_snake_", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/ultimate_combo2/__init__.py b/tests/endtoend/snake_case_functions/ultimate_combo2/__init__.py new file mode 100644 index 000000000..7e454e5c0 --- /dev/null +++ b/tests/endtoend/snake_case_functions/ultimate_combo2/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(__9req__snake__sna_ke________snake__sn0ke_: func.HttpRequest) -> func.HttpResponse: + name = __9req__snake__sna_ke________snake__sn0ke_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/ultimate_combo2/function.json b/tests/endtoend/snake_case_functions/ultimate_combo2/function.json new file mode 100644 index 000000000..3b3506716 --- /dev/null +++ b/tests/endtoend/snake_case_functions/ultimate_combo2/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "__9req__snake__sna_ke________snake__sn0ke_", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/underscore_prefix/__init__.py b/tests/endtoend/snake_case_functions/underscore_prefix/__init__.py new file mode 100644 index 000000000..adfec92d4 --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_prefix/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(_req: func.HttpRequest) -> func.HttpResponse: + name = _req.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/underscore_prefix/function.json b/tests/endtoend/snake_case_functions/underscore_prefix/function.json new file mode 100644 index 000000000..f2bb4fd06 --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_prefix/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "_req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/underscore_prefix_snake/__init__.py b/tests/endtoend/snake_case_functions/underscore_prefix_snake/__init__.py new file mode 100644 index 000000000..c949433ec --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_prefix_snake/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(_req_snake: func.HttpRequest) -> func.HttpResponse: + name = _req_snake.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/underscore_prefix_snake/function.json b/tests/endtoend/snake_case_functions/underscore_prefix_snake/function.json new file mode 100644 index 000000000..7aa61592f --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_prefix_snake/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "_req_snake", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/underscore_suffix/__init__.py b/tests/endtoend/snake_case_functions/underscore_suffix/__init__.py new file mode 100644 index 000000000..c59b69a4b --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_suffix/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(req_: func.HttpRequest) -> func.HttpResponse: + name = req_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/underscore_suffix/function.json b/tests/endtoend/snake_case_functions/underscore_suffix/function.json new file mode 100644 index 000000000..75301ae9a --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_suffix/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req_", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/underscore_suffix_snake/__init__.py b/tests/endtoend/snake_case_functions/underscore_suffix_snake/__init__.py new file mode 100644 index 000000000..c1600f747 --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_suffix_snake/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# flake8: noqa +import logging + +import azure.functions as func + + +def main(req_snake_: func.HttpRequest) -> func.HttpResponse: + name = req_snake_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/snake_case_functions/underscore_suffix_snake/function.json b/tests/endtoend/snake_case_functions/underscore_suffix_snake/function.json new file mode 100644 index 000000000..f7a5dc106 --- /dev/null +++ b/tests/endtoend/snake_case_functions/underscore_suffix_snake/function.json @@ -0,0 +1,20 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req_snake_", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/endtoend/snake_case_functions/valid_stein/function_app.py b/tests/endtoend/snake_case_functions/valid_stein/function_app.py new file mode 100644 index 000000000..3b6f68f30 --- /dev/null +++ b/tests/endtoend/snake_case_functions/valid_stein/function_app.py @@ -0,0 +1,94 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import azure.functions as func + +app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) + + +@app.route(route="classic_snake_case", trigger_arg_name="req_snake_snake_snake_snake") +def classic_snake_case(req_snake_snake_snake_snake: func.HttpRequest)\ + -> func.HttpResponse: + name = req_snake_snake_snake_snake.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="single_underscore", trigger_arg_name="_") +def single_underscore(_: func.HttpRequest) -> func.HttpResponse: + name = _.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="underscore_prefix", trigger_arg_name="_req") +def underscore_prefix(_req: func.HttpRequest) -> func.HttpResponse: + name = _req.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="underscore_prefix_snake", trigger_arg_name="_req_snake") +def underscore_prefix_snake(_req_snake: func.HttpRequest) -> func.HttpResponse: + name = _req_snake.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="underscore_suffix", trigger_arg_name="req_") +def underscore_suffix(req_: func.HttpRequest) -> func.HttpResponse: + name = req_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="underscore_suffix_snake", trigger_arg_name="req_snake_") +def underscore_suffix_snake(req_snake_: func.HttpRequest) -> func.HttpResponse: + name = req_snake_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="ultimate_combo", trigger_arg_name="_req_snake_snake_snake_snake_") +def ultimate_combo(_req_snake_snake_snake_snake_: func.HttpRequest)\ + -> func.HttpResponse: + name = _req_snake_snake_snake_snake_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="sandwich", trigger_arg_name="_req_") +def sandwich(_req_: func.HttpRequest)\ + -> func.HttpResponse: + name = _req_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="double_underscore", trigger_arg_name="req__snake") +def double_underscore(req__snake: func.HttpRequest) -> func.HttpResponse: + name = req__snake.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="double_underscore_prefix", trigger_arg_name="__req") +def classic_double_underscore(__req: func.HttpRequest) -> func.HttpResponse: + name = __req.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +def double_underscore_suffix(req__: func.HttpRequest) -> func.HttpResponse: + name = req__.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="jsut_double_underscore", trigger_arg_name="__") +def jsut_double_underscore(__: func.HttpRequest) -> func.HttpResponse: + name = __.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="python_main_keyword", trigger_arg_name="__main__") +def python_main_keyword(__main__: func.HttpRequest) -> func.HttpResponse: + name = __main__.params.get('name') + return func.HttpResponse(f"Hello, {name}.") + + +@app.route(route="ultimate_combo2", + trigger_arg_name="__9req__snake__sna_ke________snake__sn0ke_") +def ultimate_combo2( + __9req__snake__sna_ke________snake__sn0ke_: func.HttpRequest)\ + -> func.HttpResponse: + name = __9req__snake__sna_ke________snake__sn0ke_.params.get('name') + return func.HttpResponse(f"Hello, {name}.") diff --git a/tests/endtoend/sql_functions/sql_functions_stein/function_app.py b/tests/endtoend/sql_functions/sql_functions_stein/function_app.py index 07c78c385..d34b9f772 100644 --- a/tests/endtoend/sql_functions/sql_functions_stein/function_app.py +++ b/tests/endtoend/sql_functions/sql_functions_stein/function_app.py @@ -9,14 +9,14 @@ @app.route(route="sql_input/{productid}") -@app.sql_input(arg_name="products", +@app.sql_input(arg_name="products_snake", command_text="SELECT * FROM Products WHERE ProductId = @ProductId", command_type="Text", parameters="@ProductId={productid}", connection_string_setting="AzureWebJobsSqlConnectionString") -def sql_input(req: func.HttpRequest, products: func.SqlRowList) \ +def sql_input(req: func.HttpRequest, products_snake: func.SqlRowList) \ -> func.HttpResponse: - rows = list(map(lambda r: json.loads(r.to_json()), products)) + rows = list(map(lambda r: json.loads(r.to_json()), products_snake)) return func.HttpResponse( json.dumps(rows), @@ -42,13 +42,14 @@ def sql_input2(req: func.HttpRequest, products: func.SqlRowList) -> func.HttpRes @app.route(route="sql_output") -@app.sql_output(arg_name="r", +@app.sql_output(arg_name="r_snake", command_text="[dbo].[Products]", connection_string_setting="AzureWebJobsSqlConnectionString") -def sql_output(req: func.HttpRequest, r: func.Out[func.SqlRow]) -> func.HttpResponse: +def sql_output(req: func.HttpRequest, r_snake: func.Out[func.SqlRow])\ + -> func.HttpResponse: body = json.loads(req.get_body()) row = func.SqlRow.from_dict(body) - r.set(row) + r_snake.set(row) return func.HttpResponse( body=req.get_body(), @@ -57,13 +58,13 @@ def sql_output(req: func.HttpRequest, r: func.Out[func.SqlRow]) -> func.HttpResp ) -@app.sql_trigger(arg_name="changes", +@app.sql_trigger(arg_name="changes_snake", table_name="Products", connection_string_setting="AzureWebJobsSqlConnectionString") @app.sql_output(arg_name="r", command_text="[dbo].[Products2]", connection_string_setting="AzureWebJobsSqlConnectionString") -def sql_trigger(changes, r: func.Out[func.SqlRow]) -> str: - row = func.SqlRow.from_dict(json.loads(changes)[0]["Item"]) +def sql_trigger(changes_snake, r: func.Out[func.SqlRow]) -> str: + row = func.SqlRow.from_dict(json.loads(changes_snake)[0]["Item"]) r.set(row) return "OK" diff --git a/tests/endtoend/sql_functions/sql_input/__init__.py b/tests/endtoend/sql_functions/sql_input/__init__.py index 03c622492..7d14f3df9 100644 --- a/tests/endtoend/sql_functions/sql_input/__init__.py +++ b/tests/endtoend/sql_functions/sql_input/__init__.py @@ -6,8 +6,8 @@ import azure.functions as func -def main(req: func.HttpRequest, products: func.SqlRowList) -> func.HttpResponse: - rows = list(map(lambda r: json.loads(r.to_json()), products)) +def main(req: func.HttpRequest, products_snake: func.SqlRowList) -> func.HttpResponse: + rows = list(map(lambda r: json.loads(r.to_json()), products_snake)) return func.HttpResponse( json.dumps(rows), diff --git a/tests/endtoend/sql_functions/sql_input/function.json b/tests/endtoend/sql_functions/sql_input/function.json index 38ec00f6f..9780af5bf 100644 --- a/tests/endtoend/sql_functions/sql_input/function.json +++ b/tests/endtoend/sql_functions/sql_input/function.json @@ -17,7 +17,7 @@ "direction": "out" }, { - "name": "products", + "name": "products_snake", "type": "sql", "direction": "in", "commandText": "SELECT * FROM Products WHERE ProductId = @ProductId", diff --git a/tests/endtoend/sql_functions/sql_output/__init__.py b/tests/endtoend/sql_functions/sql_output/__init__.py index 42a21ff24..c0708abbd 100644 --- a/tests/endtoend/sql_functions/sql_output/__init__.py +++ b/tests/endtoend/sql_functions/sql_output/__init__.py @@ -6,10 +6,10 @@ import azure.functions as func -def main(req: func.HttpRequest, r: func.Out[func.SqlRow]) -> func.HttpResponse: +def main(req: func.HttpRequest, r_snake: func.Out[func.SqlRow]) -> func.HttpResponse: body = json.loads(req.get_body()) row = func.SqlRow.from_dict(body) - r.set(row) + r_snake.set(row) return func.HttpResponse( body=req.get_body(), diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json index 44ede8421..63fd02ebd 100644 --- a/tests/endtoend/sql_functions/sql_output/function.json +++ b/tests/endtoend/sql_functions/sql_output/function.json @@ -16,7 +16,7 @@ "direction": "out" }, { - "name": "r", + "name": "r_snake", "type": "sql", "direction": "out", "commandText": "[dbo].[Products]", diff --git a/tests/endtoend/sql_functions/sql_trigger/__init__.py b/tests/endtoend/sql_functions/sql_trigger/__init__.py index 56115c75d..9da3589d2 100644 --- a/tests/endtoend/sql_functions/sql_trigger/__init__.py +++ b/tests/endtoend/sql_functions/sql_trigger/__init__.py @@ -5,7 +5,7 @@ import azure.functions as func -def main(changes, r: func.Out[func.SqlRow]) -> str: +def main(changes, r_snake: func.Out[func.SqlRow]) -> str: row = func.SqlRow.from_dict(json.loads(changes)[0]["Item"]) - r.set(row) + r_snake.set(row) return "OK" diff --git a/tests/endtoend/sql_functions/sql_trigger/function.json b/tests/endtoend/sql_functions/sql_trigger/function.json index db68da83d..eb0cb5042 100644 --- a/tests/endtoend/sql_functions/sql_trigger/function.json +++ b/tests/endtoend/sql_functions/sql_trigger/function.json @@ -10,7 +10,7 @@ "connectionStringSetting": "AzureWebJobsSqlConnectionString" }, { - "name": "r", + "name": "r_snake", "type": "sql", "direction": "out", "commandText": "[dbo].[Products2]", diff --git a/tests/endtoend/test_snake_case_functions.py b/tests/endtoend/test_snake_case_functions.py new file mode 100644 index 000000000..92aaa916f --- /dev/null +++ b/tests/endtoend/test_snake_case_functions.py @@ -0,0 +1,166 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import os +from unittest.mock import patch + +from tests.utils import testutils + +REQUEST_TIMEOUT_SEC = 5 + + +class TestValidSnakeCaseFunctions(testutils.WebHostTestCase): + def setUp(self): + self._patch_environ = patch.dict('os.environ', os.environ.copy()) + self._patch_environ.start() + super().setUp() + + def tearDown(self): + super().tearDown() + self._patch_environ.stop() + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'snake_case_functions' + + @testutils.retryable_test(3, 5) + def test_classic_snake_case(self): + r = self.webhost.request('GET', 'classic_snake_case', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_single_underscore(self): + r = self.webhost.request('GET', 'single_underscore', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_underscore_prefix(self): + r = self.webhost.request('GET', 'underscore_prefix', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_underscore_suffix(self): + r = self.webhost.request('GET', 'underscore_suffix', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_ultimate_combo(self): + r = self.webhost.request('GET', 'ultimate_combo', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_underscore_prefix_snake(self): + r = self.webhost.request('GET', 'underscore_prefix_snake', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_underscore_suffix_snake(self): + r = self.webhost.request('GET', 'underscore_suffix_snake', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_double_underscore(self): + r = self.webhost.request('GET', 'double_underscore', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_double_underscore_prefix(self): + r = self.webhost.request('GET', 'double_underscore_prefix', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_double_underscore_suffix(self): + r = self.webhost.request('GET', 'double_underscore_suffix', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_just_double_underscore(self): + r = self.webhost.request('GET', 'just_double_underscore', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_python_main_keyword(self): + r = self.webhost.request('GET', 'python_main_keyword', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) + + @testutils.retryable_test(3, 5) + def test_ultimate_combo2(self): + r = self.webhost.request('GET', 'ultimate_combo2', + params={'name': 'query'}, + timeout=REQUEST_TIMEOUT_SEC) + self.assertTrue(r.ok) + self.assertEqual( + r.content, + b'Hello, query.' + ) diff --git a/tests/endtoend/timer_functions/timer_func/__init__.py b/tests/endtoend/timer_functions/timer_func/__init__.py index 5cdd1a102..90737ce8c 100644 --- a/tests/endtoend/timer_functions/timer_func/__init__.py +++ b/tests/endtoend/timer_functions/timer_func/__init__.py @@ -6,5 +6,5 @@ import azure.functions as func -def main(mytimer: func.TimerRequest) -> None: +def main(mytimer_snake: func.TimerRequest) -> None: logging.info("This timer trigger function executed successfully") diff --git a/tests/endtoend/timer_functions/timer_func/function.json b/tests/endtoend/timer_functions/timer_func/function.json index dba900a90..91c052097 100644 --- a/tests/endtoend/timer_functions/timer_func/function.json +++ b/tests/endtoend/timer_functions/timer_func/function.json @@ -2,7 +2,7 @@ "scriptFile": "__init__.py", "bindings": [ { - "name": "mytimer", + "name": "mytimer_snake", "type": "timerTrigger", "direction": "in", "schedule": "*/1 * * * * *", diff --git a/tests/endtoend/timer_functions/timer_functions_stein/function_app.py b/tests/endtoend/timer_functions/timer_functions_stein/function_app.py index 25937d316..67214024c 100644 --- a/tests/endtoend/timer_functions/timer_functions_stein/function_app.py +++ b/tests/endtoend/timer_functions/timer_functions_stein/function_app.py @@ -9,8 +9,8 @@ @app.function_name(name="mytimer") -@app.schedule(schedule="*/1 * * * * *", arg_name="mytimer", +@app.schedule(schedule="*/1 * * * * *", arg_name="mytimer_snake", run_on_startup=False, use_monitor=False) -def mytimer(mytimer: func.TimerRequest) -> None: +def mytimer(mytimer_snake: func.TimerRequest) -> None: logging.info("This timer trigger function executed successfully") diff --git a/tests/endtoend/warmup_functions/warmup/__init__.py b/tests/endtoend/warmup_functions/warmup/__init__.py index 0d186eab6..9e85acff8 100644 --- a/tests/endtoend/warmup_functions/warmup/__init__.py +++ b/tests/endtoend/warmup_functions/warmup/__init__.py @@ -4,5 +4,5 @@ import logging -def main(warmupContext) -> None: +def main(warmupContext_snake) -> None: logging.info('Function App instance is warm') diff --git a/tests/endtoend/warmup_functions/warmup/function.json b/tests/endtoend/warmup_functions/warmup/function.json index 04c3f9d07..3a895c2e2 100644 --- a/tests/endtoend/warmup_functions/warmup/function.json +++ b/tests/endtoend/warmup_functions/warmup/function.json @@ -3,7 +3,7 @@ { "type": "warmupTrigger", "direction": "in", - "name": "warmupContext" + "name": "warmupContext_snake" } ] } \ No newline at end of file diff --git a/tests/endtoend/warmup_functions/warmup_functions_stein/function_app.py b/tests/endtoend/warmup_functions/warmup_functions_stein/function_app.py index 83968cc4d..dab616b63 100644 --- a/tests/endtoend/warmup_functions/warmup_functions_stein/function_app.py +++ b/tests/endtoend/warmup_functions/warmup_functions_stein/function_app.py @@ -8,6 +8,6 @@ app = func.FunctionApp() -@app.warm_up_trigger('warmup') -def warmup(warmup) -> None: +@app.warm_up_trigger('warmup_snake') +def warmup(warmup_snake) -> None: logging.info('Function App instance is warm') diff --git a/tests/unittests/broken_functions/double_underscore_arg_name/function.json b/tests/unittests/broken_functions/double_underscore_arg_name/function.json new file mode 100644 index 000000000..ed9f6a050 --- /dev/null +++ b/tests/unittests/broken_functions/double_underscore_arg_name/function.json @@ -0,0 +1,15 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "httpTrigger", + "direction": "in", + "name": "__req" + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/tests/unittests/broken_functions/double_underscore_arg_name/main.py b/tests/unittests/broken_functions/double_underscore_arg_name/main.py new file mode 100644 index 000000000..e8cc92376 --- /dev/null +++ b/tests/unittests/broken_functions/double_underscore_arg_name/main.py @@ -0,0 +1,8 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import azure.functions as azf + + +def main(__req: azf.HttpRequest): + return azf.HttpResponse('

Hello World™

', + mimetype='text/html') diff --git a/tests/unittests/test_broken_functions.py b/tests/unittests/test_broken_functions.py index 508122c92..b0d4917e9 100644 --- a/tests/unittests/test_broken_functions.py +++ b/tests/unittests/test_broken_functions.py @@ -297,3 +297,24 @@ async def test_import_module_troubleshooting_url(self): self.assertRegex( r.response.result.exception.message, r'.*ModuleNotFoundError') + + async def test_double_underscore_arg_name(self): + async with testutils.start_mockhost( + script_root=self.broken_funcs_dir) as host: + await host.init_worker() + func_id, r = await host.load_function('double_underscore_arg_name') + + self.assertEqual(r.response.function_id, func_id) + # Indexing does not fail here + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + + message_present = False + warning_log = ("Argument name __req is invalid. " + "Please ensure it does not contain " + "\'__\', start with a digit, or exceed 128 characters.") + for log in r.logs: + if warning_log in log.message: + message_present = True + break + self.assertTrue(message_present)