Skip to content
Open
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- `opentelemetry-api`: Convert objects of any type other than AnyValue which fixes invalid type at WSGI request attributes
([#4808](https://github.com/open-telemetry/opentelemetry-python/pull/4808))
- docs: Added sqlcommenter example
([#4734](https://github.com/open-telemetry/opentelemetry-python/pull/4734))
- build: bump ruff to 0.14.1
Expand Down Expand Up @@ -1719,7 +1721,7 @@ can cause a deadlock to occur over `logging._lock` in some cases ([#4636](https:
- Add reset for the global configuration object, for testing purposes
([#636](https://github.com/open-telemetry/opentelemetry-python/pull/636))
- Add support for programmatic instrumentation
([#579](https://github.com/open-telemetry/opentelemetry-python/pull/569))
([#569](https://github.com/open-telemetry/opentelemetry-python/pull/569))

### Changed

Expand Down
18 changes: 12 additions & 6 deletions opentelemetry-api/src/opentelemetry/attributes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def _clean_attribute(
return None


def _clean_extended_attribute_value(
def _clean_extended_attribute_value( # pylint: disable=too-many-branches
value: types.AnyValue, max_len: Optional[int]
) -> types.AnyValue:
# for primitive types just return the value and eventually shorten the string length
Expand Down Expand Up @@ -180,11 +180,14 @@ def _clean_extended_attribute_value(
# Freeze mutable sequences defensively
return tuple(cleaned_seq)

raise TypeError(
f"Invalid type {type(value).__name__} for attribute value. "
f"Expected one of {[valid_type.__name__ for valid_type in _VALID_ANY_VALUE_TYPES]} or a "
"sequence of those types",
)
try:
return str(value)
except Exception:
raise TypeError(
f"Invalid type {type(value).__name__} for attribute value. "
f"Expected one of {[valid_type.__name__ for valid_type in _VALID_ANY_VALUE_TYPES]} or a "
"sequence of those types",
)


def _clean_extended_attribute(
Expand Down Expand Up @@ -279,6 +282,9 @@ def __setitem__(self, key: str, value: types.AnyValue) -> None:
return

if self._extended_attributes:
# Convert types other than AnyValue to strings before cleaning
if not isinstance(value, _VALID_ANY_VALUE_TYPES):
value = str(value)
value = _clean_extended_attribute(
key, value, self.max_value_len
)
Expand Down
19 changes: 19 additions & 0 deletions opentelemetry-api/tests/attributes/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,22 @@ def test_extended_attributes(self):
bdict["key"] = "value"

clean_extended_attribute_mock.assert_called_once()

def test_wsgi_request_conversion_to_string(self):
"""Test that WSGI request objects are converted to strings before calling _clean_extended_attribute."""

class DummyWSGIRequest:
def __str__(self):
return "<DummyWSGIRequest method=GET path=/example/>"

bdict = BoundedAttributes(extended_attributes=True, immutable=False)
wsgi_request = DummyWSGIRequest()

with unittest.mock.patch(
"opentelemetry.attributes._clean_extended_attribute",
return_value="stringified_request",
):
bdict["request"] = wsgi_request

# Verify that the request stored in the bounded dict matches the cleaned value
self.assertEqual(bdict["request"], "stringified_request")