Skip to content

Commit 093825e

Browse files
EvanR-Devevanromangavin-aguiar
authored
Add thread_local_storage property to Context (#167)
* Add thread_local_storage property to Context * Remove return * Change threading.local type check * Add tls to tests * Format lines * Add function name back to MockContext --------- Co-authored-by: evanroman <[email protected]> Co-authored-by: gavin-aguiar <[email protected]>
1 parent 847f763 commit 093825e

File tree

5 files changed

+42
-7
lines changed

5 files changed

+42
-7
lines changed

azure/functions/_abc.py

+13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import abc
55
import datetime
66
import io
7+
import threading
78
import typing
89

910
from azure.functions._thirdparty.werkzeug.datastructures import Headers
@@ -102,6 +103,18 @@ def invocation_id(self) -> str:
102103
"""Function invocation ID."""
103104
pass
104105

106+
@property
107+
@abc.abstractmethod
108+
def thread_local_storage(self) -> typing.Type[threading.local]:
109+
"""Thread local storage.
110+
111+
:attribute str invocation_id:
112+
Invocation ID contained in local thread storage.
113+
Enables logging from user threads when set to
114+
the current context's invocation ID.
115+
"""
116+
pass
117+
105118
@property
106119
@abc.abstractmethod
107120
def function_name(self) -> str:

azure/functions/_http_asgi.py

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def to_asgi_http_scope(self):
5656
"azure_functions.function_directory": self.af_function_directory,
5757
"azure_functions.function_name": self.af_function_name,
5858
"azure_functions.invocation_id": self.af_invocation_id,
59+
"azure_functions.thread_local_storage":
60+
self.af_thread_local_storage,
5961
"azure_functions.trace_context": self.af_trace_context,
6062
"azure_functions.retry_context": self.af_retry_context
6163
}

azure/functions/_http_wsgi.py

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ def __init__(self,
5656
'function_directory', None)
5757
self.af_function_name = getattr(func_ctx, 'function_name', None)
5858
self.af_invocation_id = getattr(func_ctx, 'invocation_id', None)
59+
self.af_thread_local_storage = getattr(func_ctx,
60+
'thread_local_storage',
61+
None)
5962
self.af_trace_context = getattr(func_ctx, 'trace_context', None)
6063
self.af_retry_context = getattr(func_ctx, 'retry_context', None)
6164

@@ -83,6 +86,8 @@ def to_environ(self, errors_buffer: StringIO) -> Dict[str, Any]:
8386
'azure_functions.function_directory': self.af_function_directory,
8487
'azure_functions.function_name': self.af_function_name,
8588
'azure_functions.invocation_id': self.af_invocation_id,
89+
'azure_functions.thread_local_storage':
90+
self.af_thread_local_storage,
8691
'azure_functions.trace_context': self.af_trace_context,
8792
'azure_functions.retry_context': self.af_retry_context
8893
}

tests/test_http_asgi.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Licensed under the MIT License.
33

44
import asyncio
5+
import threading
56
import unittest
67

78
import azure.functions as func
@@ -111,14 +112,16 @@ def _generate_func_request(
111112
def _generate_func_context(
112113
self,
113114
invocation_id='123e4567-e89b-12d3-a456-426655440000',
115+
thread_local_storage=threading.local(),
114116
function_name='httptrigger',
115117
function_directory='/home/roger/wwwroot/httptrigger',
116118
trace_context=TraceContext,
117119
retry_context=RetryContext
118120
) -> func.Context:
119121
class MockContext(func.Context):
120-
def __init__(self, ii, fn, fd, tc, rc):
122+
def __init__(self, ii, tls, fn, fd, tc, rc):
121123
self._invocation_id = ii
124+
self._thread_local_storage = tls
122125
self._function_name = fn
123126
self._function_directory = fd
124127
self._trace_context = tc
@@ -128,6 +131,10 @@ def __init__(self, ii, fn, fd, tc, rc):
128131
def invocation_id(self):
129132
return self._invocation_id
130133

134+
@property
135+
def thread_local_storage(self):
136+
return self._thread_local_storage
137+
131138
@property
132139
def function_name(self):
133140
return self._function_name
@@ -144,8 +151,8 @@ def trace_context(self):
144151
def retry_context(self):
145152
return self._retry_context
146153

147-
return MockContext(invocation_id, function_name, function_directory,
148-
trace_context, retry_context)
154+
return MockContext(invocation_id, thread_local_storage, function_name,
155+
function_directory, trace_context, retry_context)
149156

150157
def test_middleware_calls_app(self):
151158
app = MockAsgiApplication()

tests/test_http_wsgi.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
3-
3+
import threading
44
import unittest
55
from io import StringIO, BytesIO
66

@@ -113,7 +113,8 @@ def test_request_parse_function_context(self):
113113
environ = WsgiRequest(func_request,
114114
func_context).to_environ(error_buffer)
115115
self.assertEqual(environ['azure_functions.invocation_id'],
116-
'123e4567-e89b-12d3-a456-426655440000')
116+
'123e4567-e89b-12d3-a456-426655440000'),
117+
self.assertIsNotNone(environ['azure_functions.thread_local_storage'])
117118
self.assertEqual(environ['azure_functions.function_name'],
118119
'httptrigger')
119120
self.assertEqual(environ['azure_functions.function_directory'],
@@ -236,14 +237,16 @@ def _generate_func_request(
236237
def _generate_func_context(
237238
self,
238239
invocation_id='123e4567-e89b-12d3-a456-426655440000',
240+
thread_local_storage=threading.local(),
239241
function_name='httptrigger',
240242
function_directory='/home/roger/wwwroot/httptrigger',
241243
trace_context=TraceContext,
242244
retry_context=RetryContext
243245
) -> func.Context:
244246
class MockContext(func.Context):
245-
def __init__(self, ii, fn, fd, tc, rc):
247+
def __init__(self, ii, tls, fn, fd, tc, rc):
246248
self._invocation_id = ii
249+
self._thread_local_storage = tls
247250
self._function_name = fn
248251
self._function_directory = fd
249252
self._trace_context = tc
@@ -253,6 +256,10 @@ def __init__(self, ii, fn, fd, tc, rc):
253256
def invocation_id(self):
254257
return self._invocation_id
255258

259+
@property
260+
def thread_local_storage(self):
261+
return self._thread_local_storage
262+
256263
@property
257264
def function_name(self):
258265
return self._function_name
@@ -269,7 +276,8 @@ def trace_context(self):
269276
def retry_context(self):
270277
return self._retry_context
271278

272-
return MockContext(invocation_id, function_name, function_directory,
279+
return MockContext(invocation_id, thread_local_storage,
280+
function_name, function_directory,
273281
trace_context, retry_context)
274282

275283
def _generate_wsgi_app(self,

0 commit comments

Comments
 (0)