Skip to content

Add thread_local_storage property to Context #167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions azure/functions/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import abc
import datetime
import io
import threading
import typing

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

@property
@abc.abstractmethod
def thread_local_storage(self) -> typing.Type[threading.local]:
"""Thread local storage.

:attribute str invocation_id:
Invocation ID contained in local thread storage.
Enables logging from user threads when set to
the current context's invocation ID.
"""
pass

@property
@abc.abstractmethod
def function_name(self) -> str:
Expand Down
2 changes: 2 additions & 0 deletions azure/functions/_http_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def to_asgi_http_scope(self):
"azure_functions.function_directory": self.af_function_directory,
"azure_functions.function_name": self.af_function_name,
"azure_functions.invocation_id": self.af_invocation_id,
"azure_functions.thread_local_storage":
self.af_thread_local_storage,
"azure_functions.trace_context": self.af_trace_context,
"azure_functions.retry_context": self.af_retry_context
}
Expand Down
5 changes: 5 additions & 0 deletions azure/functions/_http_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def __init__(self,
'function_directory', None)
self.af_function_name = getattr(func_ctx, 'function_name', None)
self.af_invocation_id = getattr(func_ctx, 'invocation_id', None)
self.af_thread_local_storage = getattr(func_ctx,
'thread_local_storage',
None)
self.af_trace_context = getattr(func_ctx, 'trace_context', None)
self.af_retry_context = getattr(func_ctx, 'retry_context', None)

Expand Down Expand Up @@ -83,6 +86,8 @@ def to_environ(self, errors_buffer: StringIO) -> Dict[str, Any]:
'azure_functions.function_directory': self.af_function_directory,
'azure_functions.function_name': self.af_function_name,
'azure_functions.invocation_id': self.af_invocation_id,
'azure_functions.thread_local_storage':
self.af_thread_local_storage,
'azure_functions.trace_context': self.af_trace_context,
'azure_functions.retry_context': self.af_retry_context
}
Expand Down
13 changes: 10 additions & 3 deletions tests/test_http_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Licensed under the MIT License.

import asyncio
import threading
import unittest

import azure.functions as func
Expand Down Expand Up @@ -111,14 +112,16 @@ def _generate_func_request(
def _generate_func_context(
self,
invocation_id='123e4567-e89b-12d3-a456-426655440000',
thread_local_storage=threading.local(),
function_name='httptrigger',
function_directory='/home/roger/wwwroot/httptrigger',
trace_context=TraceContext,
retry_context=RetryContext
) -> func.Context:
class MockContext(func.Context):
def __init__(self, ii, fn, fd, tc, rc):
def __init__(self, ii, tls, fn, fd, tc, rc):
self._invocation_id = ii
self._thread_local_storage = tls
self._function_name = fn
self._function_directory = fd
self._trace_context = tc
Expand All @@ -128,6 +131,10 @@ def __init__(self, ii, fn, fd, tc, rc):
def invocation_id(self):
return self._invocation_id

@property
def thread_local_storage(self):
return self._thread_local_storage

@property
def function_name(self):
return self._function_name
Expand All @@ -144,8 +151,8 @@ def trace_context(self):
def retry_context(self):
return self._retry_context

return MockContext(invocation_id, function_name, function_directory,
trace_context, retry_context)
return MockContext(invocation_id, thread_local_storage, function_name,
function_directory, trace_context, retry_context)

def test_middleware_calls_app(self):
app = MockAsgiApplication()
Expand Down
16 changes: 12 additions & 4 deletions tests/test_http_wsgi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import threading
import unittest
from io import StringIO, BytesIO

Expand Down Expand Up @@ -113,7 +113,8 @@ def test_request_parse_function_context(self):
environ = WsgiRequest(func_request,
func_context).to_environ(error_buffer)
self.assertEqual(environ['azure_functions.invocation_id'],
'123e4567-e89b-12d3-a456-426655440000')
'123e4567-e89b-12d3-a456-426655440000'),
self.assertIsNotNone(environ['azure_functions.thread_local_storage'])
self.assertEqual(environ['azure_functions.function_name'],
'httptrigger')
self.assertEqual(environ['azure_functions.function_directory'],
Expand Down Expand Up @@ -236,14 +237,16 @@ def _generate_func_request(
def _generate_func_context(
self,
invocation_id='123e4567-e89b-12d3-a456-426655440000',
thread_local_storage=threading.local(),
function_name='httptrigger',
function_directory='/home/roger/wwwroot/httptrigger',
trace_context=TraceContext,
retry_context=RetryContext
) -> func.Context:
class MockContext(func.Context):
def __init__(self, ii, fn, fd, tc, rc):
def __init__(self, ii, tls, fn, fd, tc, rc):
self._invocation_id = ii
self._thread_local_storage = tls
self._function_name = fn
self._function_directory = fd
self._trace_context = tc
Expand All @@ -253,6 +256,10 @@ def __init__(self, ii, fn, fd, tc, rc):
def invocation_id(self):
return self._invocation_id

@property
def thread_local_storage(self):
return self._thread_local_storage

@property
def function_name(self):
return self._function_name
Expand All @@ -269,7 +276,8 @@ def trace_context(self):
def retry_context(self):
return self._retry_context

return MockContext(invocation_id, function_name, function_directory,
return MockContext(invocation_id, thread_local_storage,
function_name, function_directory,
trace_context, retry_context)

def _generate_wsgi_app(self,
Expand Down