Skip to content

Commit 7cee871

Browse files
authored
Python Worker Extension Interface (worker) (#815)
1 parent 9324efc commit 7cee871

File tree

13 files changed

+1262
-18
lines changed

13 files changed

+1262
-18
lines changed

azure_functions_worker/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
3+
4+
__version__ = '1.1.10'
Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,44 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4+
from typing import Dict
5+
46

57
class TraceContext:
8+
"""Check https://www.w3.org/TR/trace-context/ for more information"""
69

710
def __init__(self, trace_parent: str,
8-
trace_state: str, attributes: dict) -> None:
11+
trace_state: str, attributes: Dict[str, str]) -> None:
912
self.__trace_parent = trace_parent
1013
self.__trace_state = trace_state
1114
self.__attributes = attributes
1215

1316
@property
1417
def Tracestate(self) -> str:
18+
"""Get trace state from trace-context (deprecated)."""
1519
return self.__trace_state
1620

1721
@property
1822
def Traceparent(self) -> str:
23+
"""Get trace parent from trace-context (deprecated)."""
24+
return self.__trace_parent
25+
26+
@property
27+
def Attributes(self) -> Dict[str, str]:
28+
"""Get trace-context attributes (deprecated)."""
29+
return self.__attributes
30+
31+
@property
32+
def trace_state(self) -> str:
33+
"""Get trace state from trace-context"""
34+
return self.__trace_state
35+
36+
@property
37+
def trace_parent(self) -> str:
38+
"""Get trace parent from trace-context"""
1939
return self.__trace_parent
2040

2141
@property
22-
def Attributes(self) -> str:
42+
def attributes(self) -> Dict[str, str]:
43+
"""Get trace-context attributes"""
2344
return self.__attributes

azure_functions_worker/constants.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4-
# Prefixes
5-
CONSOLE_LOG_PREFIX = "LanguageWorkerConsoleLog"
6-
74
# Capabilities
85
RAW_HTTP_BODY_BYTES = "RawHttpBodyBytes"
96
TYPED_DATA_COLLECTION = "TypedDataCollection"
@@ -26,6 +23,7 @@
2623
PYTHON_ROLLBACK_CWD_PATH = "PYTHON_ROLLBACK_CWD_PATH"
2724
PYTHON_THREADPOOL_THREAD_COUNT = "PYTHON_THREADPOOL_THREAD_COUNT"
2825
PYTHON_ISOLATE_WORKER_DEPENDENCIES = "PYTHON_ISOLATE_WORKER_DEPENDENCIES"
26+
PYTHON_ENABLE_WORKER_EXTENSIONS = "PYTHON_ENABLE_WORKER_EXTENSIONS"
2927
FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED = \
3028
"FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED"
3129
"""
@@ -40,6 +38,8 @@
4038
PYTHON_THREADPOOL_THREAD_COUNT_MAX = 32
4139
PYTHON_ISOLATE_WORKER_DEPENDENCIES_DEFAULT = False
4240
PYTHON_ISOLATE_WORKER_DEPENDENCIES_DEFAULT_39 = True
41+
PYTHON_ENABLE_WORKER_EXTENSIONS_DEFAULT = False
42+
PYTHON_ENABLE_WORKER_EXTENSIONS_DEFAULT_39 = True
4343

4444
# External Site URLs
4545
MODULE_NOT_FOUND_TS_URL = "https://aka.ms/functions-modulenotfound"

azure_functions_worker/dispatcher.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818

1919
import grpc
2020

21+
from . import __version__
2122
from . import bindings
2223
from . import constants
2324
from . import functions
2425
from . import loader
2526
from . import protos
26-
from .constants import (CONSOLE_LOG_PREFIX, PYTHON_THREADPOOL_THREAD_COUNT,
27+
from .constants import (PYTHON_THREADPOOL_THREAD_COUNT,
2728
PYTHON_THREADPOOL_THREAD_COUNT_DEFAULT,
2829
PYTHON_THREADPOOL_THREAD_COUNT_MAX,
2930
PYTHON_THREADPOOL_THREAD_COUNT_MIN)
3031
from .logging import disable_console_logging, enable_console_logging
31-
from .logging import error_logger, is_system_log_category, logger
32+
from .logging import (logger, error_logger, is_system_log_category,
33+
CONSOLE_LOG_PREFIX)
34+
from .extension import ExtensionManager
3235
from .utils.common import get_app_setting
3336
from .utils.tracing import marshall_exception_trace
3437
from .utils.dependency import DependencyManager
@@ -255,8 +258,9 @@ async def _dispatch_grpc_request(self, request):
255258
self._grpc_resp_queue.put_nowait(resp)
256259

257260
async def _handle__worker_init_request(self, req):
258-
logger.info('Received WorkerInitRequest, request ID %s',
259-
self.request_id)
261+
logger.info('Received WorkerInitRequest, '
262+
'python version %s, worker version %s, request ID %s',
263+
sys.version, __version__, self.request_id)
260264

261265
capabilities = {
262266
constants.RAW_HTTP_BODY_BYTES: _TRUE,
@@ -304,6 +308,11 @@ async def _handle__function_load_request(self, req):
304308
self._functions.add_function(
305309
function_id, func, func_request.metadata)
306310

311+
ExtensionManager.function_load_extension(
312+
function_name,
313+
func_request.metadata.directory
314+
)
315+
307316
logger.info('Successfully processed FunctionLoadRequest, '
308317
f'request ID: {self.request_id}, '
309318
f'function ID: {function_id},'
@@ -373,20 +382,24 @@ async def _handle__invocation_request(self, req):
373382
pytype=pb_type_info.pytype,
374383
shmem_mgr=self._shmem_mgr)
375384

385+
fi_context = bindings.Context(
386+
fi.name, fi.directory, invocation_id, trace_context)
376387
if fi.requires_context:
377-
args['context'] = bindings.Context(
378-
fi.name, fi.directory, invocation_id, trace_context)
388+
args['context'] = fi_context
379389

380390
if fi.output_types:
381391
for name in fi.output_types:
382392
args[name] = bindings.Out()
383393

384394
if fi.is_async:
385-
call_result = await fi.func(**args)
395+
call_result = await self._run_async_func(
396+
fi_context, fi.func, args
397+
)
386398
else:
387399
call_result = await self._loop.run_in_executor(
388400
self._sync_call_tp,
389-
self.__run_sync_func, invocation_id, fi.func, args)
401+
self._run_sync_func,
402+
invocation_id, fi_context, fi.func, args)
390403
if call_result is not None and not fi.has_return:
391404
raise RuntimeError(f'function {fi.name!r} without a $return '
392405
'binding returned a non-None value')
@@ -582,15 +595,21 @@ def _create_sync_call_tp(
582595
max_workers=max_worker
583596
)
584597

585-
def __run_sync_func(self, invocation_id, func, params):
598+
def _run_sync_func(self, invocation_id, context, func, params):
586599
# This helper exists because we need to access the current
587600
# invocation_id from ThreadPoolExecutor's threads.
588601
_invocation_id_local.v = invocation_id
589602
try:
590-
return func(**params)
603+
return ExtensionManager.get_sync_invocation_wrapper(context,
604+
func)(params)
591605
finally:
592606
_invocation_id_local.v = None
593607

608+
async def _run_async_func(self, context, func, params):
609+
return await ExtensionManager.get_async_invocation_wrapper(
610+
context, func, params
611+
)
612+
594613
def __poll_grpc(self):
595614
options = []
596615
if self._grpc_max_msg_len:

0 commit comments

Comments
 (0)