Skip to content

Skip __class__ lookup in object_isinstance for standard getattro#7303

Merged
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:worktree-isinstance-opt
Mar 2, 2026
Merged

Skip __class__ lookup in object_isinstance for standard getattro#7303
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:worktree-isinstance-opt

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Mar 1, 2026

In object_isinstance(), when is_subtype() returns false, the class attribute lookup via get_attribute_opt is redundant for objects using standard getattribute, since class is a data descriptor on object that always returns obj.class().

Summary by CodeRabbit

  • Performance Improvements
    • Optimized isinstance checks by streamlining attribute lookup paths for standard attribute access scenarios.

In object_isinstance(), when is_subtype() returns false, the __class__
attribute lookup via get_attribute_opt is redundant for objects using
standard __getattribute__, since __class__ is a data descriptor on
object that always returns obj.class().
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 1, 2026

📝 Walkthrough

Walkthrough

The change optimizes attribute lookup behavior in Python's isinstance and issubclass operations by introducing a helper method that detects standard __getattribute__ usage, allowing short-circuit evaluation of __class__ lookups when possible, while also adding PyBaseObject to module imports.

Changes

Cohort / File(s) Summary
Attribute Lookup Optimization
crates/vm/src/protocol/object.rs
Added has_standard_getattro() private helper method to detect standard __getattribute__ path usage. Refactored isinstance and issubclass logic to conditionally guard __class__ attribute lookup based on this check. Updated imports to include PyBaseObject in builtins.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Fix isinstance #5869: Modifies the same vm/src/protocol/object.rs file to refactor __class__ and special-method lookup behavior in isinstance/issubclass operations with similar optimization goals.

Poem

🐰 A rabbit hops through protocol lands,
With has_standard_getattro in paws so grand,
Short-circuits and lookups now dance in place,
__class__ finds its swifter race! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main optimization: skipping unnecessary class lookup in object_isinstance when standard getattro is used.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@youknowone youknowone marked this pull request as ready for review March 2, 2026 01:06
Copy link
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/vm/src/protocol/object.rs (1)

568-598: ⚠️ Potential issue | 🟠 Major

Skip __class__ lookup optimization is semantically incorrect and diverges from CPython.

RustPython's optimization to skip __class__ lookup when has_standard_getattro() is true (lines 571 and 590) contradicts CPython's behavior. In CPython's object_isinstance, attribute lookup via PyObject_GetOptionalAttr is always performed, regardless of whether __getattribute__ is standard or custom. This matters because a subclass can define __class__ as a custom descriptor/property at the class level, and the standard __getattribute__ will still invoke the descriptor protocol, returning the custom value. By skipping the lookup when getattro is standard, RustPython assumes __class__ always resolves to self.class(), which is incorrect when custom __class__ descriptors exist.

To align with CPython, remove the !self.has_standard_getattro() condition and always perform the __class__ lookup via vm.get_attribute_opt(), as shown in the provided diff.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/protocol/object.rs` around lines 568 - 598, The current
optimization skips looking up __class__ when has_standard_getattro() is true,
which diverges from CPython; update the logic in the object_isinstance path that
currently branches on has_standard_getattro() to always call
vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__)) and use its
result to determine i_cls (falling back to self.class() only if lookup fails),
remove the conditional gating on has_standard_getattro(), and ensure the
subsequent code still converts i_cls via PyTypeRef::try_from_object and calls
i_cls.abstract_issubclass(cls, vm) as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@crates/vm/src/protocol/object.rs`:
- Around line 568-598: The current optimization skips looking up __class__ when
has_standard_getattro() is true, which diverges from CPython; update the logic
in the object_isinstance path that currently branches on has_standard_getattro()
to always call vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))
and use its result to determine i_cls (falling back to self.class() only if
lookup fails), remove the conditional gating on has_standard_getattro(), and
ensure the subsequent code still converts i_cls via PyTypeRef::try_from_object
and calls i_cls.abstract_issubclass(cls, vm) as before.

ℹ️ Review info

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0b5a5a and 3b49a33.

📒 Files selected for processing (1)
  • crates/vm/src/protocol/object.rs

@youknowone youknowone merged commit 69fc75b into RustPython:main Mar 2, 2026
13 checks passed
@youknowone youknowone deleted the worktree-isinstance-opt branch March 2, 2026 01:18
@coderabbitai coderabbitai bot mentioned this pull request Mar 2, 2026
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