Skip to content

Python: fix(core): point @experimental warnings at user code, not stdlib internals#5996

Open
eavanvalkenburg wants to merge 2 commits into
microsoft:mainfrom
eavanvalkenburg:fix/experimental-warning-stacklevel
Open

Python: fix(core): point @experimental warnings at user code, not stdlib internals#5996
eavanvalkenburg wants to merge 2 commits into
microsoft:mainfrom
eavanvalkenburg:fix/experimental-warning-stacklevel

Conversation

@eavanvalkenburg
Copy link
Copy Markdown
Member

Motivation and Context

When an @experimental ABC is subclassed (e.g. MemoryStore, SkillResource), the emitted ExperimentalWarning was attributed to internal stdlib frames instead of the user's code. On modern CPython this surfaced as:

/.../python3.13/abc.py:106: ExperimentalWarning: [HARNESS] MemoryStore is experimental ...
  cls = super().__new__(mcls, name, bases, namespace, **kwargs)

The cause: the wrappers installed by @experimental called warnings.warn with a fixed stacklevel=3, but ABCMeta inserts an extra abc.__new__ frame for ABC-driven class creation, so the warning landed inside abc.py (or <frozen abc>:106) rather than the user's class Sub(...) line.

Raised as a follow-up to a review comment on #5958.

Description

  • Replace the fixed stacklevel with a frame walker (_resolve_user_frame) that starts at inspect.currentframe() and skips frames belonging to this module plus abc/functools/typing/contextlib and their submodules. The skip list is matched on frame.f_globals['__name__'] rather than __file__, because frozen stdlib modules report <frozen abc> as the filename.
  • Emit via warnings.warn_explicit at the resolved (filename, lineno, module) so recorded warnings point at user code; fall back to warnings.warn(stacklevel=2) only if no user frame is found.
  • Install a single-line warnings.formatwarning specifically for FeatureStageWarning so the output is file:line: ExperimentalWarning: [FEATURE_ID] Name is experimental ... with no secondary source-snippet line (the class/function name is already in the message). Other warning categories delegate to the stdlib default formatter unchanged.
  • Added a regression test that subclasses an @experimental ABC inside warnings.catch_warnings and asserts the recorded filename equals the test file.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

…rnals

Previously the wrappers installed by @experimental called warnings.warn
with a fixed stacklevel=3. ABCMeta inserts an extra abc.__new__ frame
when an experimental ABC is subclassed, so the warning landed inside
abc.py (or <frozen abc>:106 on modern CPython) instead of the user's
class Sub(...) line.

Resolve the user frame by walking inspect.currentframe(), skipping
frames whose module name is abc/functools/typing/contextlib (or
submodules), then emit via warnings.warn_explicit so the recorded
filename/lineno point at user code. Falls back to warnings.warn with
stacklevel=2 if no user frame is found. Module-name matching is used
because frozen stdlib modules report '<frozen abc>' as their filename.

Also install a one-line warnings.formatwarning specifically for
FeatureStageWarning so 'file:line: ExperimentalWarning: [ID] Name ...'
prints without the secondary source-snippet line. Other categories
delegate to the stdlib default formatter unchanged.

Added a regression test that subclasses an @experimental ABC inside
warnings.catch_warnings and asserts the recorded filename equals the
test file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 21, 2026 08:58
@github-actions github-actions Bot changed the title fix(core): point @experimental warnings at user code, not stdlib internals Python: fix(core): point @experimental warnings at user code, not stdlib internals May 21, 2026
@moonbox3
Copy link
Copy Markdown
Contributor

moonbox3 commented May 21, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _feature_stage.py1631093%104, 150, 205, 216, 236, 274, 285, 291, 322, 350
TOTAL35332410888% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
7011 30 💤 0 ❌ 0 🔥 1m 52s ⏱️

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adjusts Python feature-stage warning emission so @experimental warnings are attributed to the user’s callsite (not stdlib/internal frames), including the ABC subclassing case that previously surfaced as <frozen abc>:....

Changes:

  • Replaces fixed stacklevel warning emission with a stack frame walker that resolves a “user” frame and emits via warnings.warn_explicit.
  • Installs a custom warnings.formatwarning implementation to render FeatureStageWarning categories as single-line warnings.
  • Adds a regression test covering subclassing an @experimental ABC and asserting the warning filename points at the test/user file.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
python/packages/core/agent_framework/_feature_stage.py Resolves user frames for warnings, switches to warn_explicit, and installs a single-line formatter for feature-stage warnings.
python/packages/core/tests/core/test_feature_stage.py Adds regression coverage for ABC subclass warning attribution.

Comment thread python/packages/core/agent_framework/_feature_stage.py
Comment thread python/packages/core/agent_framework/_feature_stage.py Outdated
Comment thread python/packages/core/tests/core/test_feature_stage.py Outdated
- Make _install_feature_stage_formatter idempotent: tag the installed
  formatter with a marker attribute and short-circuit re-installation,
  so re-imports/reloads don't wrap the formatter on top of itself.
  Also expose the previous formatter via __wrapped__ for restoration.
- Avoid leaking frame references in _resolve_user_frame: capture data
  into plain locals inside try and del frame/candidate in finally,
  per CPython's guidance on inspect.currentframe usage.
- Drop redundant _WARNED_FEATURES.clear() in the new ABC subclass test
  (the autouse fixture already handles it).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants