Skip to content

Allow heap getset creation #5854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 28, 2025
Merged

Conversation

youknowone
Copy link
Member

@youknowone youknowone commented Jun 27, 2025

Summary by CodeRabbit

  • New Features
    • Improved internal handling of property descriptors, ensuring more robust and static binding for class attributes.
  • Bug Fixes
    • Enhanced safety in attribute management to prevent potential issues with prematurely freed references.
  • Documentation
    • Removed module-level documentation and usage examples from thread-local functionality.
  • Style
    • Updated method signatures to enforce positional-only parameters in thread-local object initialization.
  • Refactor
    • Streamlined descriptor creation for class and property attributes for improved maintainability.

Copy link

coderabbitai bot commented Jun 27, 2025

Walkthrough

The changes primarily refactor how property and getset descriptors are created and managed, switching from dynamic to static getset descriptors across multiple modules. Internal safety and pointer management are improved for class references in getset descriptors. In Python's _threading_local.py, the docstring is removed, the _patch context manager is simplified, and the local class's __new__ method signature is updated for positional-only arguments.

Changes

File(s) Change Summary
stdlib/src/pyexpat.rs Macro updated to use new_static_getset instead of new_getset for property creation.
vm/src/builtins/getset.rs PyGetSet struct's class field changed to PointerSlot; constructor and accessors updated for pointer safety.
vm/src/builtins/property.rs property type's __doc__ attribute registration updated to use new_static_getset.
vm/src/builtins/type.rs Unused import removed; PointerSlot marked as Sync/Send; trait impls updated; __dict__ uses new_static_getset.
vm/src/class.rs extend_class now sets __dict__ using new_static_getset instead of new_getset.
vm/src/vm/context.rs new_getset renamed to new_static_getset (with 'static class ref); new unsafe new_getset method added for non-'static ref.
Lib/_threading_local.py Module docstring removed; _patch no longer restores old __dict__; local.__new__ enforces positional-only for cls.

Sequence Diagram(s)

sequenceDiagram
    participant Context
    participant PyType
    participant PyGetSet

    Context->>PyType: Provide static reference for class
    Context->>PyGetSet: Create static getset descriptor (new_static_getset)
    PyGetSet-->>PyType: Store class reference via PointerSlot
    Note right of PyGetSet: Access class via PointerSlot safety methods
Loading

Suggested reviewers

  • arihant2math

Poem

In the warren, code hops anew,
Static getsets now in view!
Pointer slots keep classes tight,
Thread-local docs hop out of sight.
With signatures neat and context clear,
This bunny cheers, "Refactor here!"
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11dd424 and f57cddf.

📒 Files selected for processing (7)
  • Lib/_threading_local.py (1 hunks)
  • stdlib/src/pyexpat.rs (1 hunks)
  • vm/src/builtins/getset.rs (4 hunks)
  • vm/src/builtins/property.rs (1 hunks)
  • vm/src/builtins/type.rs (4 hunks)
  • vm/src/class.rs (1 hunks)
  • vm/src/vm/context.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • stdlib/src/pyexpat.rs
  • vm/src/class.rs
  • Lib/_threading_local.py
  • vm/src/builtins/property.rs
  • vm/src/builtins/getset.rs
  • vm/src/builtins/type.rs
  • vm/src/vm/context.rs
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run rust tests (macos-latest)
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac26be7 and 6cb9500.

📒 Files selected for processing (6)
  • stdlib/src/pyexpat.rs (1 hunks)
  • vm/src/builtins/getset.rs (4 hunks)
  • vm/src/builtins/property.rs (1 hunks)
  • vm/src/builtins/type.rs (4 hunks)
  • vm/src/class.rs (1 hunks)
  • vm/src/vm/context.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
vm/src/builtins/type.rs (4)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-26T12:52:11.109Z
Learning: Minimize modifications to CPython standard library files in the Lib/ directory; bug fixes should be made through Rust code modifications whenever possible.
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-26T12:52:11.109Z
Learning: When implementing a Python module in Rust for RustPython, use the #[pymodule] attribute and expose functions and classes using #[pyfunction], #[pyclass], and #[pymethod] as appropriate.
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-26T12:52:11.109Z
Learning: Use the macro system (such as 'pyclass', 'pymodule', 'pyfunction') when implementing Python functionality in Rust for RustPython.
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-26T12:52:11.109Z
Learning: Always use 'cargo fmt' to format Rust code and follow the default rustfmt code style when contributing to RustPython.
vm/src/builtins/getset.rs (2)
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-26T12:52:11.109Z
Learning: Use the macro system (such as 'pyclass', 'pymodule', 'pyfunction') when implementing Python functionality in Rust for RustPython.
Learnt from: CR
PR: RustPython/RustPython#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-26T12:52:11.109Z
Learning: When implementing a Python module in Rust for RustPython, use the #[pymodule] attribute and expose functions and classes using #[pyfunction], #[pyclass], and #[pymethod] as appropriate.
🧬 Code Graph Analysis (1)
vm/src/builtins/getset.rs (1)
vm/src/builtins/type.rs (5)
  • from (90-92)
  • class (146-148)
  • borrow_static (76-78)
  • slot_name (426-431)
  • name (433-438)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run rust tests (macos-latest)
🔇 Additional comments (11)
vm/src/vm/context.rs (2)

573-587: LGTM!

The renaming to new_static_getset clearly indicates that this method requires a static lifetime for the class parameter, which improves API clarity.


589-607: Clarify the safety invariants for the unsafe cast.

The safety comment mentions this can be broken "if used unconventionally" but doesn't specify what constitutes unconventional use. Consider documenting:

  • The specific invariants that must be upheld (e.g., the class must outlive all its getset descriptors)
  • Examples of what would constitute unsafe usage
  • Why this unsafe approach is preferred over alternative safe designs
vm/src/builtins/property.rs (1)

389-394: LGTM!

Correct usage of new_static_getset since context.types.property_type is a static type reference.

vm/src/class.rs (1)

88-93: LGTM!

Appropriate use of new_static_getset as the class parameter has a 'static lifetime in the extend_class method signature.

stdlib/src/pyexpat.rs (1)

21-21: LGTM!

The macro correctly uses new_static_getset as it's called from extend_class_with_fields where the class has a 'static lifetime.

vm/src/builtins/getset.rs (3)

16-16: Good use of PointerSlot for lifetime management.

The change from &'static Py<PyType> to PointerSlot<Py<PyType>> provides a safer abstraction for managing the class reference lifetime, supporting both static and heap-allocated types.


142-146: LGTM!

Correct usage of unsafe { self.class.borrow_static() } to access the class reference through PointerSlot.


152-152: LGTM!

Consistent and correct usage of the unsafe borrow pattern for PointerSlot.

vm/src/builtins/type.rs (3)

34-34: LGTM: Import cleanup is consistent with trait implementation changes.

Removing the unused fmt import aligns with the switch to fully qualified paths in the Display/Debug implementations below.


132-142: LGTM: Good practice using fully qualified paths.

The switch from imported fmt to fully qualified std::fmt paths improves code clarity and is consistent with the import cleanup above.


999-1020: Verify the safety of unsafe lifetime casting in getset creation.

This restructuring introduces unsafe lifetime manipulation by passing a non-'static reference to new_getset. According to the AI summary, new_getset now uses unsafe casting to convert non-'static references to 'static.

Key concerns:

  1. The safety contract of this pattern isn't documented in the code
  2. Risk of use-after-free if the type object is dropped while getsets still reference it
  3. The unsafe block obscures the specific safety invariants being relied upon

Verify that the lifetime safety guarantees are properly maintained:

#!/bin/bash
# Verify the new_getset implementation and its safety contract
ast-grep --pattern 'fn new_getset($$$) {
  $$$
}'

# Check for documentation of safety invariants
rg -A 10 -B 5 "new_getset.*unsafe|unsafe.*new_getset"

# Look for related unsafe casting patterns
rg -A 5 "unsafe.*'static|'static.*unsafe"

Please document the safety invariants that ensure the type object outlives any getset descriptors created from it.

Comment on lines +72 to +73
unsafe impl<T> Sync for PointerSlot<T> {}
unsafe impl<T> Send for PointerSlot<T> {}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Overly broad unsafe trait implementations pose safety risks.

These blanket unsafe impl Sync/Send for PointerSlot<T> implementations are concerning because:

  1. They apply to any type T without safety constraints
  2. NonNull<T> alone doesn't guarantee the pointed-to data is thread-safe
  3. This could enable unsound code if PointerSlot is used with non-thread-safe data

Consider constraining these implementations:

-unsafe impl<T> Sync for PointerSlot<T> {}
-unsafe impl<T> Send for PointerSlot<T> {}
+unsafe impl<T: Sync> Sync for PointerSlot<T> {}
+unsafe impl<T: Send> Send for PointerSlot<T> {}

Or if the use case is specifically for static/immutable type data:

-unsafe impl<T> Sync for PointerSlot<T> {}
-unsafe impl<T> Send for PointerSlot<T> {}
+unsafe impl<T: Sync + Send + 'static> Sync for PointerSlot<T> {}
+unsafe impl<T: Sync + Send + 'static> Send for PointerSlot<T> {}
🤖 Prompt for AI Agents
In vm/src/builtins/type.rs at lines 72-73, the unsafe impls of Sync and Send for
PointerSlot<T> are too broad and unsafe because they apply to all T without
constraints. To fix this, add appropriate trait bounds to ensure T is Sync and
Send before implementing these traits for PointerSlot<T>. This will restrict the
implementations to only thread-safe types and prevent unsound behavior.

Copy link

@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: 0

🔭 Outside diff range comments (1)
Lib/_threading_local.py (1)

68-79: Critical: Context manager may not properly restore __dict__ state.

Based on the AI summary, the _patch context manager no longer saves and restores the old __dict__ attribute. This is a significant behavioral change that could cause issues:

  1. State persistence: The thread-local dictionary remains set on the object after the context exits, which may not be the intended behavior.
  2. Memory leaks: Without proper cleanup, references to thread-local dictionaries may persist longer than expected.
  3. Concurrency issues: Other threads accessing the same object may see unexpected state.

If this change was intentional to work around RustPython limitations, it should be clearly documented. Otherwise, consider restoring the original behavior:

 @contextmanager
 def _patch(self):
     impl = object.__getattribute__(self, '_local__impl')
     try:
         dct = impl.get_dict()
     except KeyError:
         dct = impl.create_dict()
         args, kw = impl.localargs
         self.__init__(*args, **kw)
+    old_dict = getattr(self, '__dict__', None)
     with impl.locallock:
         object.__setattr__(self, '__dict__', dct)
-        yield
+        try:
+            yield
+        finally:
+            if old_dict is not None:
+                object.__setattr__(self, '__dict__', old_dict)
🧹 Nitpick comments (2)
extra_tests/snippets/builtin_type.py (2)

602-602: Fix class definition formatting.

The class definition violates PEP 8 formatting guidelines as flagged by static analysis.

Apply this diff to fix the formatting:

-class A(type): pass
+
+
+class A(type):
+    pass

604-604: LGTM! Test validates metaclass behavior.

This assertion correctly validates that subclasses of type don't have __dict__ in their __dict__, which aligns with the conditional insertion logic mentioned in the PR summary.

Consider adding a blank line after this assertion to improve readability and comply with PEP 8:

 assert '__dict__' not in A.__dict__
+
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cb9500 and 3574d2c.

📒 Files selected for processing (9)
  • Lib/_threading_local.py (1 hunks)
  • extra_tests/snippets/builtin_type.py (1 hunks)
  • stdlib/src/pyexpat.rs (1 hunks)
  • vm/src/builtins/getset.rs (4 hunks)
  • vm/src/builtins/property.rs (1 hunks)
  • vm/src/builtins/type.rs (6 hunks)
  • vm/src/class.rs (1 hunks)
  • vm/src/stdlib/thread.rs (1 hunks)
  • vm/src/vm/context.rs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • vm/src/stdlib/thread.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • vm/src/class.rs
  • vm/src/builtins/getset.rs
  • stdlib/src/pyexpat.rs
  • vm/src/builtins/property.rs
  • vm/src/builtins/type.rs
  • vm/src/vm/context.rs
🧰 Additional context used
📓 Path-based instructions (1)
`Lib/**/*`: Files in the Lib/ directory (Python standard library copied from CPy...

Lib/**/*: Files in the Lib/ directory (Python standard library copied from CPython) should be edited very conservatively; modifications should be minimal and only to work around RustPython limitations.

📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • Lib/_threading_local.py
🪛 Flake8 (7.2.0)
extra_tests/snippets/builtin_type.py

[error] 602-602: expected 2 blank lines, found 1

(E302)


[error] 602-602: multiple statements on one line (colon)

(E701)


[error] 604-604: expected 2 blank lines after class or function definition, found 1

(E305)

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run rust tests (windows-latest)
🔇 Additional comments (3)
extra_tests/snippets/builtin_type.py (1)

599-600: LGTM! Test assertions validate internal descriptor behavior.

These assertions correctly test the __objclass__ attribute of the __dict__ descriptor and verify its type information, which aligns with the refactoring of getset descriptors mentioned in the PR objectives.

Lib/_threading_local.py (2)

1-7: Inconsistency: AI summary claims docstring was removed but it's still present.

The AI summary states that "module-level docstring and extensive inline documentation...have been completely removed," but the docstring is clearly present in the provided code. This indicates either:

  1. The summary is inaccurate, or
  2. The changes were partially reverted

Please clarify whether the docstring removal was intentional and if so, why it appears to have been reverted.

Likely an incorrect or invalid review comment.


85-85: Verify that positional-only parameter change is necessary for RustPython compatibility.

The addition of / to enforce positional-only parameters for cls is a behavior change from the original CPython implementation. Since this is a standard library file that should be edited conservatively, ensure this change is necessary to work around a specific RustPython limitation.

#!/bin/bash
# Description: Search for related changes or comments about positional-only parameters in RustPython codebase
# Expected: Evidence of RustPython-specific requirements for this change

# Search for related changes in threading or local implementations
rg -A 5 -B 5 "positional.*only|__new__.*/" --type rust
rg -A 5 -B 5 "threading.*local" --type rust

@youknowone youknowone force-pushed the heap-getset branch 2 times, most recently from 11dd424 to d8f64fb Compare June 28, 2025 15:10
@youknowone youknowone merged commit 07a04ac into RustPython:main Jun 28, 2025
12 checks passed
@youknowone youknowone deleted the heap-getset branch June 28, 2025 18:03
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