Skip to content

Resolve test_inspect bytecode parity gaps#7926

Merged
youknowone merged 2 commits into
RustPython:mainfrom
youknowone:bytecode-parity
May 19, 2026
Merged

Resolve test_inspect bytecode parity gaps#7926
youknowone merged 2 commits into
RustPython:mainfrom
youknowone:bytecode-parity

Conversation

@youknowone
Copy link
Copy Markdown
Member

@youknowone youknowone commented May 19, 2026

Summary by CodeRabbit

  • Refactor

    • Improved compiler scope classification for more accurate detection of function-like scopes.
    • Extended handling of special dunder identifiers to reduce incorrect name resolution.
    • Simplified class dunder initialization flow for more consistent name loading/storing.
  • Tests

    • Added coverage for nested class scope resolution (including nonlocal dunder behavior).
    • Added tests validating nested lambda qualname generation.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: f5a6ca9b-5d79-4340-8731-09c1fd9d2964

📥 Commits

Reviewing files that changed from the base of the PR and between ddecd7f and 13549d8.

📒 Files selected for processing (1)
  • crates/codegen/src/compile.rs

📝 Walkthrough

Walkthrough

Refactors compile-time scope classification to use explicit CompilerScope variants, maps specific class-related dunder identifiers to unknown symbol scope, consolidates class dunder emission via load_name/store_name helpers, adds a locals-plus name helper, and adds tests for nested nonlocal/qualname behavior.

Changes

Scope and Dunder Name Resolution Refactoring

Layer / File(s) Summary
Parent scope classification refactoring
crates/codegen/src/compile.rs
Make parent_scope mutable, capture parent symbol-table typ, and decide function-like parents by matching CompilerScope::Function, ::AsyncFunction, or ::Lambda.
Symbol scope resolution for specific dunder names
crates/codegen/src/compile.rs
Add explicit resolution branch mapping __name__, __module__, __qualname__, __firstlineno__, __doc__, __static_attributes__, __classdictcell__, and __classcell__ to SymbolScope::Unknown.
Class dunder initialization via helpers
crates/codegen/src/compile.rs
Replace manual LoadName/StoreName sequences with load_name(...) and store_name(...) for class dunders (__name__, __module__, __qualname__, __firstlineno__, __doc__, __static_attributes__, __classdictcell__, __classcell__).
locals-plus helper and tests for nested scope/qualname behavior
crates/codegen/src/compile.rs
Introduce localsplus_name(...) to map locals-plus indices to names; add tests verifying __firstlineno__ is captured as a freevar (emitted via StoreDeref) and nested lambda qualnames include chained .<locals> segments.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
A rabbit hops through scope and name,
Dunders tidy, no heuristics to blame.
Parents matched and qualnames spun,
Nested lambdas linked, one by one.
StoreDeref whispers: closure done.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly relates to the main objective of the pull request, which addresses bytecode parity gaps in test_inspect, as confirmed by the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[x] lib: cpython/Lib/inspect.py
[ ] test: cpython/Lib/test/test_inspect (TODO: 34)

dependencies:

  • inspect

dependent tests: (90 tests)

  • inspect: test_abc test_argparse test_asyncgen test_buffer test_builtin test_clinic test_code test_collections test_coroutines test_decimal test_enum test_functools test_generators test_grammar test_inspect test_monitoring test_ntpath test_operator test_patma test_posixpath test_pydoc test_signal test_sqlite3 test_traceback test_type_annotations test_types test_typing test_unittest test_yield_from test_zipimport test_zipimport_support test_zoneinfo
    • ast: test_ast test_compile test_compiler_codegen test_dis test_fstring test_future_stmt test_peepholer test_peg_generator test_site test_ssl test_type_comments test_ucn test_unparse
      • annotationlib: test_annotationlib test_reprlib test_type_params
      • dbm.dumb: test_dbm_dumb
      • pyclbr: test_pyclbr
      • traceback: test_asyncio test_code_module test_contextlib test_contextlib_async test_dictcomps test_exceptions test_http_cookiejar test_importlib test_iter test_listcomps test_pyexpat test_setcomps test_socket test_subprocess test_sys test_threadedtempfile test_threading test_unittest test_with
    • asyncio: test_asyncio test_logging test_os test_sys_settrace
    • bdb: test_bdb
    • cmd: test_cmd
    • dataclasses: test__colorize test_copy test_ctypes test_genericalias test_pprint test_regrtest
      • pprint: test_htmlparser test_sys_setprofile
    • importlib.metadata: test_importlib
    • pkgutil: test_pkgutil test_runpy
    • pydoc:
      • xmlrpc.server: test_docxmlrpc test_xmlrpc
    • rlcompleter: test_rlcompleter
    • trace: test_trace

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/codegen/src/compile.rs`:
- Around line 20926-20955: The assertion is too weak because it only checks for
any Instruction::StoreDeref without verifying the deref targets; update the
third assert to resolve the StoreDeref's target index (from unit.arg/OpArg as
used elsewhere) back into the closure capture name (using class_code.freevars or
the appropriate captured-names table) and assert that the resolved name is
exactly "__firstlineno__" (i.e. check the deref slot maps to "__firstlineno__"
rather than just any StoreDeref), keeping the existing error message format and
using the same symbols: class_code.instructions, Instruction::StoreDeref,
unit.arg/OpArg, and class_code.freevars to locate the code to change.
- Around line 3261-3262: The fallback that maps underscore-prefixed identifiers
to SymbolScope::Unknown is too broad—replace the single check
name.starts_with('_') with a whitelist of the compiler-generated synthetic
dunders introduced in this patch: e.g., match exact names via a constant set
(SYNTHETIC_NAMES.contains(name)) and/or prefix list
(SYNTHETIC_PREFIXES.iter().any(|p| name.starts_with(p))) and only map those to
SymbolScope::Unknown; keep the rest of identifiers unchanged. Update the code
around the existing branch that returns SymbolScope::Unknown (the expression
using name and SymbolScope::Unknown) to use this whitelist logic and add or
adjust tests to cover both synthetic dunders and normal user names.
- Line 6717: Replace the call to self.store_name("__classdictcell__") with an
unconditional class-local store like you do for "__classdict__" (emit
Instruction::StoreDeref or the equivalent direct store-deref bytecode) so the
name is always written into the class namespace regardless of global/nonlocal
declarations; likewise find the parallel location handling "__classcell__" and
change its self.store_name("__classcell__") to the same direct StoreDeref-style
emission. Locate these in compile.rs near the class-creation handling (the site
where "__classdict__" is already handled with Instruction::StoreDeref) and
ensure both special slots bypass store_name() and are stored unconditionally
into the class namespace.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 6fdca45e-5dbf-4384-91c9-e0ba1346cec8

📥 Commits

Reviewing files that changed from the base of the PR and between d8dee81 and ddecd7f.

⛔ Files ignored due to path filters (1)
  • Lib/test/test_inspect/test_inspect.py is excluded by !Lib/**
📒 Files selected for processing (1)
  • crates/codegen/src/compile.rs

Comment thread crates/codegen/src/compile.rs Outdated
namei: classdictcell
}
);
self.store_name("__classdictcell__")?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In CPython, does the compiler always store the synthetic classcellinto the class locals namespace, even when the class body declaresglobal classcellornonlocal classcell?

💡 Result:

No—CPython does not always end up storing the synthetic classcell into the class-body locals namespace when the class body declares global classcell or nonlocal classcell. In CPython, the zero-argument super() mechanism is implemented by injecting a hidden class / classcell cell during compilation, but CPython also standardizes behavior so that class and (in 3.6+) classcell do not show up in the class locals while the class body executes [1]. Additionally, the generator/symbol-table machinery for class/classcell interacts “strangely” with explicit nonlocal class in nested namespaces: the injection behaves like a closure/free variable and can be accessed via nonlocal from a nested class, without being treated as a normal local in the class-body locals mapping [1]. This directly implies that declaring nonlocal classcell (analogous to the class case discussed) prevents classcell from being stored like a regular local entry in the class locals namespace. So, when you explicitly declare global classcell or nonlocal classcell, CPython’s compiler/symbol-table handling will not necessarily (and in these cases generally does not) place the synthetic classcell into the class locals mapping the way it would for a normal class body without such declarations; instead the injected cell remains hidden/handled specially for LOAD_BUILD_CLASS and zero-argument super() semantics [1].

Citations:


🏁 Script executed:

# First, check the file size and read the relevant sections
wc -l crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 101


🏁 Script executed:

# Read around line 6717
sed -n '6710,6725p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 690


🏁 Script executed:

# Read around line 6731
sed -n '6724,6738p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 557


🏁 Script executed:

# Find the store_name method definition to understand its behavior
rg -n "fn store_name" crates/codegen/src/compile.rs | head -5

Repository: RustPython/RustPython

Length of output: 134


🏁 Script executed:

# Search for any special handling of __classcell__ or __classdictcell__
rg -n "__classcell__|__classdictcell__" crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 661


🏁 Script executed:

# Read the store_name implementation
sed -n '3146,3160p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 684


🏁 Script executed:

# Search for any method that might do unconditional class-local stores
rg -n "store_.*local|store_cell|STORE_DEREF|STORE_GLOBAL" crates/codegen/src/compile.rs | head -20

Repository: RustPython/RustPython

Length of output: 380


🏁 Script executed:

# Get broader context around line 6717 - understand the class body compilation
sed -n '6690,6745p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 1960


🏁 Script executed:

# Find compile_name implementation
rg -n "fn compile_name" crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 154


🏁 Script executed:

# Search for enum NameUsage to understand Store variant
rg -n "enum NameUsage" crates/codegen/src/compile.rs -A 10

Repository: RustPython/RustPython

Length of output: 386


🏁 Script executed:

# Look for direct instruction emitters that might bypass name resolution
rg -n "Instruction::Store|PseudoInstruction::Store" crates/codegen/src/compile.rs | head -20

Repository: RustPython/RustPython

Length of output: 1639


🏁 Script executed:

# Check how __static_attributes__ is handled (line 6707 shows it uses store_name too)
# Let's see if there's any special handling for class protocol names
rg -n "__staticattr|__firstlineno__|__module__|__qualname__" crates/codegen/src/compile.rs | head -20

Repository: RustPython/RustPython

Length of output: 1309


🏁 Script executed:

# Confirm that __classdict__ uses direct STORE_DEREF
sed -n '6648,6656p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 502


🏁 Script executed:

# Check what the exact instruction types are for these stores
rg -n "Instruction::(StoreDeref|StoreName|StoreGlobal|StoreFast)" crates/codegen/src/compile.rs | grep -E "6(6|7)[0-9]{2}:" | head -20

Repository: RustPython/RustPython

Length of output: 234


Use unconditional class-local stores for __classcell__ and __classdictcell__ instead of routing through store_name().

These are class-creation protocol slots that must always be stored to the class namespace, regardless of global or nonlocal declarations in the class body. Routing them through store_name() causes them to honor such declarations, converting them to STORE_DEREF or STORE_GLOBAL, which prevents the class namespace from receiving the cells that __build_class__ expects. Note that __classdict__ is already correctly handled with a direct Instruction::StoreDeref (line 6652); apply the same pattern to both locations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/codegen/src/compile.rs` at line 6717, Replace the call to
self.store_name("__classdictcell__") with an unconditional class-local store
like you do for "__classdict__" (emit Instruction::StoreDeref or the equivalent
direct store-deref bytecode) so the name is always written into the class
namespace regardless of global/nonlocal declarations; likewise find the parallel
location handling "__classcell__" and change its
self.store_name("__classcell__") to the same direct StoreDeref-style emission.
Locate these in compile.rs near the class-creation handling (the site where
"__classdict__" is already handled with Instruction::StoreDeref) and ensure both
special slots bypass store_name() and are stored unconditionally into the class
namespace.

Comment thread crates/codegen/src/compile.rs
@youknowone youknowone merged commit 62b081b into RustPython:main May 19, 2026
27 checks passed
@youknowone youknowone deleted the bytecode-parity branch May 19, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant