Skip to content

Commit 6477195

Browse files
committed
Added WSGIRequest to _VALID_ANY_VALUE_TYPES
1 parent 1d56f29 commit 6477195

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

opentelemetry-api/src/opentelemetry/attributes/__init__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from collections import OrderedDict
1818
from collections.abc import MutableMapping
1919
from typing import Mapping, Optional, Sequence, Tuple, Union
20-
20+
from django.core.handlers.wsgi import WSGIRequest
2121
from opentelemetry.util import types
2222

2323
# bytes are accepted as a user supplied value for attributes but
@@ -33,6 +33,7 @@
3333
str,
3434
Sequence,
3535
Mapping,
36+
WSGIRequest,
3637
)
3738

3839

@@ -121,11 +122,22 @@ def _clean_attribute(
121122
def _clean_extended_attribute_value(
122123
value: types.AnyValue, max_len: Optional[int]
123124
) -> types.AnyValue:
125+
# pylint: disable=too-many-branches
124126
# for primitive types just return the value and eventually shorten the string length
125127
if value is None or isinstance(value, _VALID_ATTR_VALUE_TYPES):
126128
if max_len is not None and isinstance(value, str):
127129
value = value[:max_len]
128130
return value
131+
132+
if isinstance(value, WSGIRequest):
133+
wsgi_data = {
134+
"method": getattr(value, "method", None),
135+
"path": getattr(value, "path", None),
136+
"path_info": getattr(value, "path_info", None),
137+
"content_type": getattr(value, "content_type", None),
138+
"user": str(getattr(value, "user", None)) if hasattr(value, "user") else None,
139+
}
140+
return {k: v for k, v in wsgi_data.items() if v is not None}
129141

130142
if isinstance(value, Mapping):
131143
cleaned_dict: dict[str, types.AnyValue] = {}

opentelemetry-api/tests/attributes/test_attributes.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,70 @@ def test_mapping(self):
181181
self.assertEqual(
182182
_clean_extended_attribute("headers", mapping, None), expected
183183
)
184-
184+
185+
def test_wsgi_request_attribute(self):
186+
# Test WSGIRequest type validation
187+
try:
188+
from django.core.handlers.wsgi import WSGIRequest
189+
from django.conf import settings
190+
import io
191+
192+
# Configure Django settings if not already configured
193+
if not settings.configured:
194+
settings.configure(
195+
DEBUG=True,
196+
SECRET_KEY='test-secret-key',
197+
USE_TZ=True,
198+
ROOT_URLCONF=[],
199+
MIDDLEWARE=[],
200+
)
201+
202+
# Create a minimal WSGI environ dict
203+
environ = {
204+
'REQUEST_METHOD': 'GET',
205+
'PATH_INFO': '/test',
206+
'QUERY_STRING': '',
207+
'CONTENT_TYPE': '',
208+
'CONTENT_LENGTH': '',
209+
'HTTP_HOST': 'testserver',
210+
'wsgi.version': (1, 0),
211+
'wsgi.url_scheme': 'http',
212+
'wsgi.input': io.StringIO(),
213+
'wsgi.errors': io.StringIO(),
214+
'wsgi.multithread': False,
215+
'wsgi.multiprocess': False,
216+
'wsgi.run_once': False,
217+
'SERVER_NAME': 'testserver',
218+
'SERVER_PORT': '80',
219+
}
220+
221+
# Create a WSGIRequest object
222+
wsgi_request = WSGIRequest(environ)
223+
224+
# Test that WSGIRequest gets cleaned to a dictionary format
225+
expected_cleaned = {
226+
'method': 'GET',
227+
'path': '/test',
228+
'path_info': '/test',
229+
'content_type': ''
230+
}
231+
232+
# Test WSGIRequest cleaning directly
233+
from opentelemetry.attributes import _clean_extended_attribute
234+
cleaned_value = _clean_extended_attribute("request", wsgi_request, None)
235+
self.assertEqual(cleaned_value, expected_cleaned)
236+
237+
# Test WSGIRequest in sequences - should be cleaned to dict
238+
cleaned_sequence = _clean_extended_attribute("requests", [wsgi_request], None)
239+
self.assertEqual(cleaned_sequence, (expected_cleaned,))
240+
241+
# Test WSGIRequest in mappings - should be cleaned to dict
242+
cleaned_mapping = _clean_extended_attribute("data", {"request": wsgi_request}, None)
243+
self.assertEqual(cleaned_mapping, {"request": expected_cleaned})
244+
245+
except ImportError:
246+
# Skip test if django is not available
247+
self.skipTest("Django not available")
185248

186249
class TestBoundedAttributes(unittest.TestCase):
187250
# pylint: disable=consider-using-dict-items

0 commit comments

Comments
 (0)