Skip to content

sdk: tighten ReadableSpan.attributes to non-Optional Mapping (#4569)#5183

Open
WatchTree-19 wants to merge 1 commit into
open-telemetry:mainfrom
WatchTree-19:fix/readable-span-attributes-non-optional
Open

sdk: tighten ReadableSpan.attributes to non-Optional Mapping (#4569)#5183
WatchTree-19 wants to merge 1 commit into
open-telemetry:mainfrom
WatchTree-19:fix/readable-span-attributes-non-optional

Conversation

@WatchTree-19
Copy link
Copy Markdown

Description

ReadableSpan.attributes is annotated as types.Attributes, which resolves to Optional[Mapping[str, AttributeValue]]. The implementation always returns MappingProxyType(self._attributes or {}) - the or {} fallback ensures we never return None at runtime.

Pyright / Pylance flags Object of type "None" is not subscriptable on span.attributes["key"], forcing assert span.attributes is not None boilerplate at every call site.

This PR tightens the return annotation on ReadableSpan.attributes to the non-Optional Mapping[str, types.AttributeValue]. Implementation unchanged. Mapping is already imported in the file.

Scope:

  • ReadableSpan.attributes only. Inherited by Span and _Span, no further changes needed.
  • Event.attributes and Link.attributes return self._attributes directly (which can be None) and stay Optional.
  • types.Attributes global alias is unchanged for the same reason.

Per the comment thread on #4569 from @exekis and @tekumara, the implementation has never returned None from ReadableSpan.attributes, so this is type-system tightening rather than a behaviour change. Technically a breaking change for any caller explicitly handling None on the return; in practice there shouldn't be any.

Fixes #4569

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • patched file parses cleanly via python -m ast
  • existing call sites are unchanged, only the return-type annotation is narrower
  • have not run the project test suite locally; happy to chase a green CI here if anything trips

Does This PR Require a Contrib Repo Change?

  • Yes. - Link to PR:
  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

@WatchTree-19 WatchTree-19 requested a review from a team as a code owner May 7, 2026 14:57
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented May 7, 2026

CLA Signed
The committers listed above are authorized under a signed CLA.

WatchTree-19 added a commit to WatchTree-19/opentelemetry-python that referenced this pull request May 8, 2026
Copy link
Copy Markdown
Member

@MikeGoldsmith MikeGoldsmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @WatchTree-19 - left some feedback on things we need to tidy up

Comment thread CHANGELOG.md Outdated

## Unreleased

- `opentelemetry-sdk`: tighten `ReadableSpan.attributes` annotation to non-Optional `Mapping` so callers don't need `assert ... is not None` boilerplate; runtime guarantee was already in place via `MappingProxyType(self._attributes or {})`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you got some other changelog entries in here - please can you clean this up?

@property
def attributes(self) -> types.Attributes:
def attributes(self) -> Mapping[str, types.AttributeValue]:
# The implementation always returns a MappingProxyType, never None,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very verbose, can we tighten this up?


@property
def attributes(self) -> types.Attributes:
def attributes(self) -> Mapping[str, types.AttributeValue]:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do / should we update the API definition too?

@github-project-automation github-project-automation Bot moved this to Reviewed PRs that need fixes in Python PR digest May 11, 2026
WatchTree-19 added a commit to WatchTree-19/opentelemetry-python that referenced this pull request May 12, 2026
…try#5183)

Signed-off-by: WatchTree-19 <119982314+WatchTree-19@users.noreply.github.com>
@WatchTree-19
Copy link
Copy Markdown
Author

thanks @MikeGoldsmith. pushed a follow-up addressing all three:

  1. CHANGELOG.md: hadn't realised the repo had moved to towncrier since my first commit went up - sorry for the noise. reverted the CHANGELOG.md edit to upstream/main and added .changelog/5183.changed as a one-line fragment per CONTRIBUTING.md.

  2. docstring: tightened to a single line.

@property
def attributes(self) -> Mapping[str, types.AttributeValue]:
    # `or {}` keeps the return non-None; see #4569.
    return MappingProxyType(self._attributes or {})
  1. API definition: looked at opentelemetry-api/src/opentelemetry/trace/span.py. the abstract trace_api.Span class defines set_attributes and set_attribute but no attributes property - that's only on the SDK's ReadableSpan. so no parallel API change is needed; the narrower annotation lives entirely in the SDK layer. happy to add a @property to the abstract API with the same Mapping[str, types.AttributeValue] return type if you'd prefer the surface symmetrical, but that'd be an API addition rather than a fix.

let me know if there's anything else.

@emdneto
Copy link
Copy Markdown
Member

emdneto commented May 12, 2026

Thanks for the PR!

Just a heads-up: we no longer update CHANGELOG.md directly. The changelog is now generated from changelog fragments using Towncrier.

Please add the appropriate changelog fragment for this change instead of editing CHANGELOG.md manually. You can find the instructions and expected format in CONTRIBUTING.md.

@WatchTree-19
Copy link
Copy Markdown
Author

@emdneto already addressed in the follow-up - reverted the CHANGELOG.md edit and added .changelog/5183.changed per CONTRIBUTING.md. see the latest commit + comment above.

…n-telemetry#4569)

The implementation always returns a `MappingProxyType`, never `None`,
because `self._attributes or {}` falls back to an empty dict. Tightening
the return annotation lets callers index `span.attributes["key"]` without
type-checker complaints. Adds the towncrier fragment per CONTRIBUTING.

Signed-off-by: WatchTree-19 <119982314+WatchTree-19@users.noreply.github.com>
@WatchTree-19 WatchTree-19 force-pushed the fix/readable-span-attributes-non-optional branch from 6b96015 to 6e05d7d Compare May 17, 2026 12:43
@WatchTree-19
Copy link
Copy Markdown
Author

@MikeGoldsmith @emdneto gentle bump - rebased onto current main to clear the CHANGELOG.md conflict that came in with the towncrier migration. branch is now one commit (SDK annotation tightening + .changelog/5183.changed fragment), DCO signed. ready for another look when you have time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Reviewed PRs that need fixes

Development

Successfully merging this pull request may close these issues.

Object of type "None" is not subscriptable

3 participants