Skip to content

Conversation

@77AXEL
Copy link
Contributor

@77AXEL 77AXEL commented Dec 28, 2025

Improve InterpreterConfig with convenience methods (with_debug, add_path, add_paths), better documentation with working examples, refactored stdlib setup into focused functions, and comprehensive unit tests while maintaining 100% backward compatibility.

Summary by CodeRabbit

  • New Features

    • Builder-style config: set debug level, add single or multiple module search paths, and opt into stdlib initialization.
    • Public init_stdlib function to initialize the standard library with support for frozen or dynamic modes.
  • API Changes

    • Native module registration now accepts a simple function pointer form for easier integration.
  • Tests

    • Added tests covering config behaviors when stdlib is enabled.
  • Documentation

    • Expanded examples and usage guidance for the new config methods and stdlib handling.

✏️ Tip: You can customize this high-level summary in your review settings.

Improve InterpreterConfig with convenience methods (with_debug, add_path, add_paths), better documentation with working examples, refactored stdlib setup into focused functions, and comprehensive unit tests while maintaining 100% backward compatibility.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 28, 2025

📝 Walkthrough

Walkthrough

InterpreterConfig gains builder-style methods (with_debug, add_path, add_paths, init_stdlib). add_native_module now accepts a function pointer and is boxed internally. Stdlib initialization moved into helper functions that choose frozen vs dynamic setup based on feature flags. Tests and docs updated accordingly.

Changes

Cohort / File(s) Summary
Core Interpreter API
src/interpreter.rs
add_native_module signature changed from Box<dyn FnOnce(&VirtualMachine) -> PyRef<PyModule>> to fn(&VirtualMachine) -> PyRef<PyModule> and is boxed internally when registered; other public item adjustments.
Builder & Init Methods
src/interpreter.rs
New builder methods on InterpreterConfig: with_debug(level: u8), add_path(path: impl Into<String>), add_paths<I, S>(paths: I), plus init_stdlib() (cfg variants for feature enabled/disabled).
Stdlib Setup Helpers
src/interpreter.rs
New free/helper functions: pub fn init_stdlib(vm: &mut VirtualMachine) (cfg feature), setup_frozen_stdlib(vm) (cfg freeze-stdlib), setup_dynamic_stdlib(vm) and collect_stdlib_paths() (cfg dynamic) to centralize stdlib loading logic.
Docs & Tests
src/interpreter.rs
Expanded doc comments and examples; added cfg-gated tests (when stdlib dynamic) covering default config, with_debug, add_path, and add_paths.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client Code
    participant Config as InterpreterConfig
    participant VM as VirtualMachine
    participant Stdlib as Stdlib Setup

    Client->>Config: build config (with_debug, add_path(s))
    Client->>Config: call init_stdlib()
    Config->>VM: start VM with config
    VM->>VM: call init_stdlib(vm)

    alt feature: "freeze-stdlib"
        VM->>Stdlib: setup_frozen_stdlib(vm)
        Stdlib-->>VM: register frozen modules, set _stdlib_dir
    else feature: NOT "freeze-stdlib"
        VM->>Stdlib: collect_stdlib_paths()
        Stdlib-->>VM: return additional paths
        VM->>Stdlib: setup_dynamic_stdlib(vm)
        Stdlib-->>VM: extend module_search_paths
    end

    VM-->>Client: VM ready (native modules & stdlib initialized)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I’m a rabbit who builds the VM bright, 🐇
Adding debug and paths in moonlight,
Frozen or roaming, stdlib in place,
Native modules boxed with gentle grace,
I hop off, satisfied, out of sight.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title states 'Refine documentation in interpreter.rs' but the PR introduces significant API changes including new public methods, modified signatures, and refactored initialization logic that go far beyond documentation refinement. Update the title to accurately reflect the main changes, such as 'Add convenience methods to InterpreterConfig and refactor stdlib initialization' or 'Enhance InterpreterConfig API with builder methods and stdlib setup refactoring'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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.

@github-actions
Copy link
Contributor

Code has been automatically formatted

The code in this PR has been formatted using cargo fmt --all.
Please pull the latest changes before pushing again:

git pull origin main

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/interpreter.rs (2)

130-135: Reconsider using eprintln! in library code.

Libraries typically shouldn't print to stderr without user control. Since this is gated by a compile-time feature, consider these alternatives:

  1. Use compile_error! to fail compilation if stdlib is expected but the feature isn't enabled
  2. Document this behavior clearly and let users handle it
  3. Use a logging crate with appropriate log levels

The current approach will print the warning every time init_stdlib() is called, which could spam output in applications that create multiple interpreters.


261-301: Consider expanding test coverage.

The unit tests provide good coverage of the configuration builder methods. Consider adding integration tests that:

  • Verify interpreter creation with various configurations
  • Test stdlib initialization in both frozen and dynamic modes
  • Validate native module registration

These would provide more confidence that the refactored initialization logic works correctly end-to-end.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 476b75d and 2cae714.

📒 Files selected for processing (1)
  • src/interpreter.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • src/interpreter.rs
🧬 Code graph analysis (1)
src/interpreter.rs (1)
src/settings.rs (2)
  • split_paths (345-351)
  • split_paths (358-365)
🔇 Additional comments (4)
src/interpreter.rs (4)

5-49: Documentation examples look good overall.

The examples are comprehensive and well-structured. The use of no_run is appropriate since these require runtime context. The todo!() placeholder in the example at line 39 is acceptable for documentation purposes as it compiles and makes it clear the user needs to implement their own logic.


137-186: Well-implemented convenience methods.

The new builder-style methods (with_debug, add_path, add_paths) are well-designed with proper lazy initialization of settings via get_or_insert_with. The generic implementation of add_paths provides good flexibility for various input types.


106-114: This review comment contains a factual error. The add_native_module method was introduced with the fn(&VirtualMachine) -> PyRef<PyModule> signature in commit 3737f2a and has never been changed from a Box<dyn FnOnce> signature. This is not a breaking change—it is the original design. The method has always accepted function pointers only, not closures with captures, so no functionality was removed.

Likely an incorrect or invalid review comment.


207-221: The assert! and unwrap() pattern should be replaced with proper error handling.

While setup_frozen_stdlib is guaranteed to execute only once per VM instance (the init_hooks are executed exactly once during Interpreter::with_init via a FnOnce closure), the use of assert! at line 213 and .unwrap() at line 220 is still poor error handling. Replace these with explicit error handling or return a Result type to gracefully handle cases where _stdlib_dir already exists or the attribute setting fails.

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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/interpreter.rs (1)

106-114: Breaking change: restricting make_module to function pointers.

The signature change from Box<dyn FnOnce(&VirtualMachine) -> PyRef<PyModule>> to fn(&VirtualMachine) -> PyRef<PyModule> is more restrictive and breaks backward compatibility. Function pointers cannot capture variables from their environment, so existing code that passes closures with captures will fail to compile.

This contradicts the PR's claim of "100% backward compatibility."

Example of code that would break:

let module_name = "custom".to_string();
config.add_native_module(
    "mymod".to_owned(),
    |vm| { 
        // Error: closure captures `module_name`, but fn pointer required
        vm.ctx.new_module(&module_name, /* ... */)
    }
)

Consider reverting to Box<dyn FnOnce(&VirtualMachine) -> PyRef<PyModule>> or providing a migration path with deprecation warnings.

🧹 Nitpick comments (3)
src/interpreter.rs (3)

125-135: Consider warning only once or document the repeated warning behavior.

The eprintln! will execute every time init_stdlib() is called on the builder chain. While this alerts users to the missing stdlib feature, it could print multiple warnings if called multiple times (though typically called once). Consider documenting this behavior or using a once_cell to warn only on first call.


262-302: Consider expanding test coverage for new functionality.

The tests appropriately cover the builder methods (with_debug, add_path, add_paths) but are limited to verifying configuration state. Consider adding tests for:

  • init_stdlib initialization (if testable in unit test context)
  • add_native_module registration
  • Actual interpreter creation with various configurations
  • Helper functions (setup_frozen_stdlib, setup_dynamic_stdlib, collect_stdlib_paths)

Note: Some of these may require integration tests rather than unit tests.


227-238: Consider adding a descriptive error message for initialization clarity.

Line 230 uses .unwrap() on PyRc::get_mut(&mut vm.state), which is safe during initialization due to exclusive access via &mut VirtualMachine. To improve debuggability, use .expect() instead:

let state = PyRc::get_mut(&mut vm.state)
    .expect("vm.state must have exclusive access during initialization");

This provides better context in the unlikely event of a panic and documents the initialization assumption.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cae714 and 7e7428b.

📒 Files selected for processing (1)
  • src/interpreter.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • src/interpreter.rs
🧠 Learnings (2)
📚 Learning: 2025-09-28T22:22:55.921Z
Learnt from: reactive-firewall
Repo: RustPython/RustPython PR: 6176
File: .github/workflows/Check_Tests.yml:133-141
Timestamp: 2025-09-28T22:22:55.921Z
Learning: In the RustPython project's CI-5974-Test-RustPython-Integration action, the override-rustpython-path input is marked as required but has runtime fallback logic that defaults to RUSTPYTHONPATH environment variable or "Lib" if neither is provided, making explicit specification unnecessary in most cases.

Applied to files:

  • src/interpreter.rs
📚 Learning: 2025-06-28T16:31:03.991Z
Learnt from: arihant2math
Repo: RustPython/RustPython PR: 5790
File: build.rs:2-2
Timestamp: 2025-06-28T16:31:03.991Z
Learning: In Cargo build scripts (build.rs), the environment variable CARGO_CFG_TARGET_OS is guaranteed to exist and is automatically set by Cargo during the build process, making unwrap() safe to use when accessing this variable.

Applied to files:

  • src/interpreter.rs
🧬 Code graph analysis (1)
src/interpreter.rs (1)
src/settings.rs (2)
  • split_paths (345-351)
  • split_paths (358-365)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Check Rust code with clippy
🔇 Additional comments (3)
src/interpreter.rs (3)

5-49: Well-structured documentation with clear examples.

The expanded documentation provides clear usage patterns for basic configuration, settings override, and native module registration. The use of no_run is appropriate, and the inline comments (lines 24-25) help clarify path purposes.


146-149: LGTM! Well-implemented convenience methods.

The new builder methods (with_debug, add_path, add_paths) follow a consistent pattern using get_or_insert_with(Default::default) to lazily initialize settings. The generic bounds on add_paths provide good flexibility while maintaining type safety.

Also applies to: 161-167, 178-186


244-260: LGTM! Path handling properly addresses previous review.

The use of unwrap_or_else with a descriptive error message (lines 251-252) properly addresses the previous review comment and matches the pattern used in settings.rs. This provides clear context for debugging non-Unicode path failures.

Based on learnings, the approach follows established patterns in the codebase.

Comment on lines +207 to +221
fn setup_frozen_stdlib(vm: &mut VirtualMachine) {
vm.add_frozen(rustpython_pylib::FROZEN_STDLIB);

// FIXME: Remove this hack once sys._stdlib_dir is properly implemented
// or _frozen_importlib doesn't depend on it anymore.
// The assert ensures _stdlib_dir doesn't already exist before we set it
assert!(vm.sys_module.get_attr("_stdlib_dir", vm).is_err());
vm.sys_module
.set_attr(
"_stdlib_dir",
vm.new_pyobj(rustpython_pylib::LIB_PATH.to_owned()),
vm,
)
.unwrap();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for all _stdlib_dir references
rg -n "_stdlib_dir" --type=rust -C3

Repository: RustPython/RustPython

Length of output: 897


🏁 Script executed:

# Look for where setup_frozen_stdlib is called to understand initialization order
rg -n "setup_frozen_stdlib" --type=rust -B2 -A2

Repository: RustPython/RustPython

Length of output: 624


🏁 Script executed:

# Check the VirtualMachine initialization and sys_module setup
rg -n "sys_module" src/interpreter.rs -B1 -A1 | head -50

Repository: RustPython/RustPython

Length of output: 259


🏁 Script executed:

# Look at the full context around line 197 to see initialization order
sed -n '180,210p' src/interpreter.rs

Repository: RustPython/RustPython

Length of output: 1046


🏁 Script executed:

# Check if sys_module is created before or after setup_frozen_stdlib
rg -n "sys_module.*=" src/interpreter.rs | head -20

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Look for any module initialization that might touch _stdlib_dir
rg -n "def.*_stdlib_dir|_stdlib_dir.*=" --type=python

Repository: RustPython/RustPython

Length of output: 95


🏁 Script executed:

# Search for where sys module is initially created/initialized
rg -n "sys_module" src/interpreter.rs | head -30

Repository: RustPython/RustPython

Length of output: 155


🏁 Script executed:

# Look for VirtualMachine::new to understand initialization order
ast-grep --pattern 'impl VirtualMachine {
  $$$
  pub fn new($_) {
    $$$
  }
  $$$
}'

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Search for any code path that could set _stdlib_dir before setup_frozen_stdlib
rg -n "set_attr.*_stdlib_dir|_stdlib_dir" src/

Repository: RustPython/RustPython

Length of output: 401


🏁 Script executed:

# Look for VirtualMachine creation and sys_module initialization
rg -n "sys_module\s*=" src/interpreter.rs -B2 -A2

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Find where init_stdlib is called to understand the full initialization sequence
rg -n "init_stdlib" --type=rust -B3 -A3

Repository: RustPython/RustPython

Length of output: 5257


🏁 Script executed:

# Check if there's any Python-level code or module that might set _stdlib_dir
fd --type f -e py | xargs rg "_stdlib_dir" 2>/dev/null || echo "No Python files found with _stdlib_dir"

Repository: RustPython/RustPython

Length of output: 103


🏁 Script executed:

# Look for VirtualMachine::new and sys module initialization
rg -n "fn new\(" src/interpreter.rs crates/vm/src/vm/mod.rs -A 50 | grep -A 50 "impl.*VirtualMachine" | head -100

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Search in vm crate for sys_module creation
rg -n "sys_module\s*:" crates/vm/src/ -B2 -A2

Repository: RustPython/RustPython

Length of output: 720


🏁 Script executed:

# Look at the actual flow: when is sys module initialized relative to init_stdlib
grep -n "pub fn interpreter\|init_stdlib\|init_hook" src/interpreter.rs | head -20

Repository: RustPython/RustPython

Length of output: 628


🏁 Script executed:

# Look at the Interpreter creation flow to see when sys_module is created
rg -n "pub fn interpreter" src/interpreter.rs -A 20

Repository: RustPython/RustPython

Length of output: 814


🏁 Script executed:

# Find where VirtualMachine is constructed
rg -n "VirtualMachine\s*{" crates/vm/src/vm/mod.rs -B5 -A20 | head -80

Repository: RustPython/RustPython

Length of output: 164


🏁 Script executed:

# Look for sys module initialization in new()
rg -n "sys_module.*PyModule\|fn new\(" crates/vm/src/vm/mod.rs -A 30 | head -100

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Find Interpreter::with_init to see when sys_module is created
rg -n "with_init\|pub fn with_init" crates/vm/src/ --type=rust -A 30 | head -100

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Look for sys module initialization/creation
rg -n "PyModule::new\(.*sys\|sys.*PyModule" crates/vm/src/ --type=rust -B2 -A2

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Check if sys module is set up before or after hooks are called
rg -n "pub fn with_init" crates/vm/src/ -A 50

Repository: RustPython/RustPython

Length of output: 3824


🏁 Script executed:

# Look at VirtualMachine::new to see when sys_module is created
rg -n "impl VirtualMachine" crates/vm/src/vm/mod.rs -A 100 | grep -A 50 "pub fn new"

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Find the VirtualMachine::new implementation
sed -n '500,700p' crates/vm/src/vm/mod.rs | head -150

Repository: RustPython/RustPython

Length of output: 5630


🏁 Script executed:

# Look for the initialize method to see what it does
rg -n "pub fn initialize\(" crates/vm/src/vm/mod.rs -A 30

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Find VirtualMachine::new
rg -n "fn new\(" crates/vm/src/vm/mod.rs -A 40

Repository: RustPython/RustPython

Length of output: 1734


🏁 Script executed:

# Find the initialize method
rg -n "fn initialize\(" crates/vm/src/vm/mod.rs -A 50

Repository: RustPython/RustPython

Length of output: 2578


🏁 Script executed:

# Look at stdlib::sys::init_module to see if it sets _stdlib_dir
rg -n "pub fn init_module\|_stdlib_dir" crates/stdlib/src/sys.rs -B2 -A2

Repository: RustPython/RustPython

Length of output: 129


🏁 Script executed:

# Check what attributes stdlib::sys::init_module actually sets
rg -n "def __module_def\|init_module" crates/stdlib/src/sys.rs -A 80 | head -150

Repository: RustPython/RustPython

Length of output: 129


🏁 Script executed:

# Find where sys module is defined
fd "sys.rs" --type f

Repository: RustPython/RustPython

Length of output: 92


🏁 Script executed:

# Search for sys module definition
rg -n "stdlib.*sys\|pub fn init_module.*sys" --type=rust | head -20

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Look at initialization order more carefully
sed -n '280,320p' crates/vm/src/vm/mod.rs

Repository: RustPython/RustPython

Length of output: 1877


🏁 Script executed:

# Check stdlib::sys::init_module to see what it does
rg -n "pub fn init_module" crates/vm/src/stdlib/sys.rs -A 100

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Search for _stdlib_dir in sys.rs
rg -n "_stdlib_dir" crates/vm/src/stdlib/sys.rs

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Look at the full sys.rs file to understand initialization
head -100 crates/vm/src/stdlib/sys.rs

Repository: RustPython/RustPython

Length of output: 3318


🏁 Script executed:

# Check if init_module sets _stdlib_dir or any similar attribute
cat crates/vm/src/stdlib/sys.rs | head -200

Repository: RustPython/RustPython

Length of output: 6292


🏁 Script executed:

# Look at the full sys module to see if _stdlib_dir is anywhere
wc -l crates/vm/src/stdlib/sys.rs

Repository: RustPython/RustPython

Length of output: 97


🏁 Script executed:

# Search the entire file for _stdlib_dir
grep -n "_stdlib_dir" crates/vm/src/stdlib/sys.rs

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Get a sense of the file structure
tail -100 crates/vm/src/stdlib/sys.rs

Repository: RustPython/RustPython

Length of output: 2953


🏁 Script executed:

# Check if any other code sets _stdlib_dir before setup_frozen_stdlib
rg -n "set_attr.*_stdlib_dir|_stdlib_dir.*set" --type=rust

Repository: RustPython/RustPython

Length of output: 164


🏁 Script executed:

# Check the initialization sequence in detail - get the full setup_frozen_stdlib and init_stdlib flow
sed -n '190,225p' src/interpreter.rs

Repository: RustPython/RustPython

Length of output: 1265


🏁 Script executed:

# Verify the timing: check if any init_hook before setup_frozen_stdlib could set _stdlib_dir
rg -n "init_hook\|setup_frozen_stdlib\|init_stdlib" src/interpreter.rs | head -30

Repository: RustPython/RustPython

Length of output: 47


The assert! on _stdlib_dir is safe and protected by initialization order.

The sys module is created as an empty struct in VirtualMachine::new(), and setup_frozen_stdlib() runs as an init hook before stdlib::sys::init_module() is called in vm.initialize(). Since no code path sets _stdlib_dir before this function, the attribute will not exist at the time of the assertion.

However, the current assert! panics without context. Consider replacing it with a more informative check:

if vm.sys_module.get_attr("_stdlib_dir", vm).is_ok() {
    panic!("sys._stdlib_dir already exists; stdlib initialization called in unexpected order");
}

This provides better debugging information if the initialization invariant is ever violated.

🤖 Prompt for AI Agents
In src/interpreter.rs around lines 207 to 221, replace the bare assert! checking
that sys._stdlib_dir does not already exist with an explicit conditional that
checks vm.sys_module.get_attr("_stdlib_dir", vm).is_ok() and, if true, panic
with a clear message like "sys._stdlib_dir already exists; stdlib initialization
called in unexpected order" so the code fails with informative context rather
than a plain panic; keep the subsequent set_attr call and unwrap as-is.

Copy link
Member

@youknowone youknowone left a comment

Choose a reason for hiding this comment

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

Thank you for contributing! I like the new functions split out.

I will read the change more later, but I have question about adding no_run to every doctests. Do you have any motivation to do that?

/// ```
/// let interpreter = rustpython::InterpreterConfig::new()
/// # Basic Usage
/// ```no_run
Copy link
Member

Choose a reason for hiding this comment

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

Is there benefits to add no_run here?
no_run means the code can be outdated easy without doctest

Copy link
Contributor Author

@77AXEL 77AXEL Dec 29, 2025

Choose a reason for hiding this comment

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

The original code used compile_fail, which indicates the example is expected to fail compilation. However the code is valid and should compile successfully it just can't run in a doc test environment so I changed it to no_run for clarity.

Copy link
Member

Choose a reason for hiding this comment

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

Only one test had compile_fail, and the others were possible to be run on doc test environment.

/// }))
/// .add_native_module(
/// "your_module_name".to_owned(),
/// make_custom_module,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// make_custom_module,
/// your_module::make_module,

keeping make_module is important. it is literally the function name created by #[pymodule]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the feedback! I've updated the documentation examples to consistently use make_module following the RustPython convention. The changes have been pushed.

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.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/interpreter.rs (1)

203-221: The bare assert! lacks debugging context.

While the assertion is safe due to initialization order guarantees, it provides no context when it fails. As suggested in previous review comments, consider replacing it with an explicit check and informative panic message:

if vm.sys_module.get_attr("_stdlib_dir", vm).is_ok() {
    panic!("sys._stdlib_dir already exists; stdlib initialization called in unexpected order");
}

This makes debugging easier if the initialization invariant is ever violated.

🧹 Nitpick comments (1)
src/interpreter.rs (1)

116-135: LGTM! Clear feature-gated behavior.

The feature-gated variants of init_stdlib correctly handle both compile-time configurations. The runtime warning in the no-op variant provides helpful feedback to developers.

Optional: Consider compile-time warning instead

For production binaries, you might prefer a compile-time warning instead of a runtime eprintln!:

 #[cfg(not(feature = "stdlib"))]
 pub fn init_stdlib(self) -> Self {
-    eprintln!(
-        "Warning: stdlib feature is not enabled. Standard library will not be available."
-    );
     self
 }

This prevents the message from appearing every time init_stdlib() is called in configurations where it's intentionally disabled.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e7428b and c4d502a.

📒 Files selected for processing (1)
  • src/interpreter.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • src/interpreter.rs
🧠 Learnings (4)
📚 Learning: 2025-09-28T22:22:55.921Z
Learnt from: reactive-firewall
Repo: RustPython/RustPython PR: 6176
File: .github/workflows/Check_Tests.yml:133-141
Timestamp: 2025-09-28T22:22:55.921Z
Learning: In the RustPython project's CI-5974-Test-RustPython-Integration action, the override-rustpython-path input is marked as required but has runtime fallback logic that defaults to RUSTPYTHONPATH environment variable or "Lib" if neither is provided, making explicit specification unnecessary in most cases.

Applied to files:

  • src/interpreter.rs
📚 Learning: 2025-06-28T16:31:03.991Z
Learnt from: arihant2math
Repo: RustPython/RustPython PR: 5790
File: build.rs:2-2
Timestamp: 2025-06-28T16:31:03.991Z
Learning: In Cargo build scripts (build.rs), the environment variable CARGO_CFG_TARGET_OS is guaranteed to exist and is automatically set by Cargo during the build process, making unwrap() safe to use when accessing this variable.

Applied to files:

  • src/interpreter.rs
📚 Learning: 2025-12-27T14:03:49.011Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T14:03:49.011Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust

Applied to files:

  • src/interpreter.rs
📚 Learning: 2025-12-27T14:03:49.011Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T14:03:49.011Z
Learning: Applies to **/*test*.py : When a test cannot pass due to missing language features, keep it as expectedFailure and document the reason; do not comment it out

Applied to files:

  • src/interpreter.rs
🧬 Code graph analysis (1)
src/interpreter.rs (1)
src/settings.rs (2)
  • split_paths (345-351)
  • split_paths (358-365)
🔇 Additional comments (6)
src/interpreter.rs (6)

5-49: LGTM! Excellent documentation improvements.

The expanded documentation provides clear, practical examples covering the main use cases. The consistent use of make_module naming aligns with RustPython conventions, and the no_run attribute is appropriately applied for examples that require a full runtime environment.


137-186: LGTM! Well-designed builder methods.

The convenience methods follow idiomatic Rust builder patterns with appropriate lazy initialization of settings. The generic bounds on add_paths provide good ergonomics while maintaining type safety.


189-201: LGTM! Clean stdlib initialization orchestration.

The refactored init_stdlib function clearly delegates to feature-specific implementations. The separation of concerns between frozen and dynamic stdlib handling improves maintainability.


223-260: LGTM! Proper handling of dynamic stdlib paths.

The dynamic stdlib setup correctly:

  • Collects paths from BUILDTIME_RUSTPYTHONPATH or defaults
  • Provides informative panic messages for invalid Unicode (line 252)
  • Inserts stdlib paths at the beginning to prioritize them over user paths

262-302: LGTM! Comprehensive test coverage.

The test suite appropriately covers the new builder methods:

  • Default configuration state
  • Debug level setting
  • Single and sequential path additions
  • Batch path additions

The tests verify expected behavior without unnecessary complexity.


106-114: LGTM! API simplification improves ergonomics.

The signature change from Box<dyn FnOnce...> to fn(...) simplifies the public API by accepting function pointers directly and boxing them internally. This reduces boilerplate for callers while maintaining the same functionality.

Comment on lines +37 to +40
/// fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
/// // Your module implementation
/// # todo!()
/// }
Copy link
Member

Choose a reason for hiding this comment

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

This is still misleading about making a module.

Suggested change
/// fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
/// // Your module implementation
/// # todo!()
/// }
/// #[pymodule]
/// mod your_module {}

/// }))
/// .add_native_module(
/// "your_module_name".to_owned(),
/// make_module, // ← use make_module
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// make_module, // ← use make_module
/// your_module::make_module

/// Build the interpreter with the current configuration
///
/// # Panics
/// May panic if initialization hooks encounter fatal errors
Copy link
Member

Choose a reason for hiding this comment

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

hmm, this is a caveat of vm init, but probably helpful


/// Add a custom initialization hook
///
/// Hooks are executed in the order they are added during interpreter creation
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// Hooks are executed in the order they are added during interpreter creation
/// This function is the only path in the entire interpreter through which a mutable reference to the VirtualMachine can be obtained.
/// Hooks are executed in the order they are added during interpreter creation

/// When the `stdlib` feature is not enabled, this method does nothing
/// and prints a warning. Enable the `stdlib` feature to use the standard library.
#[cfg(not(feature = "stdlib"))]
pub fn init_stdlib(self) -> Self {
Copy link
Member

Choose a reason for hiding this comment

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

Is this useful? Users get compile error when no feature set. But after changes, it became runtime error.

Comment on lines +266 to +271
#[test]
fn test_default_config() {
let config = InterpreterConfig::new();
assert!(config.settings.is_none());
assert!(config.init_hooks.is_empty());
}
Copy link
Member

Choose a reason for hiding this comment

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

I really appreciate the effort to improve RustPython.
If you used AI assistance here, could you please double-check that the changes match your intent? I’m not sure some changes are actually reviewed by you.

I’m not saying that this patch is without value or that it’s doing the wrong thing. I generally agree with the direction of this patch, but I would appreciate a version that has been reviewed a bit more carefully.

@77AXEL 77AXEL marked this pull request as draft December 29, 2025 21:14
@77AXEL 77AXEL closed this by deleting the head repository Dec 29, 2025
@youknowone
Copy link
Member

Could you open it again? we can finish it.

@youknowone youknowone mentioned this pull request Dec 31, 2025
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.

2 participants