* Target Python 3.15 using the final upstream reference Update active version and comparison configuration to Python 3.15.0. Preserve the alpha implementation status, actual bytecode format, and existing libraries pending separate migration. Align generated library-upgrade workflow pins without changing permissions or actions. Assisted-by: OpenAI Codex:model identifier unavailable * Adapt essential snippet contracts to Python 3.15 final Preserve existing coverage while following verified final behavior for script metadata, builtin signatures and loading, AST fixture construction, memoryview diagnostics, mmap search bounds, and struct format errors. The unchanged final comparator exposes the earlier expectations; focused native baseline failures establish the required implementation changes. Assisted-by: OpenAI Codex:model identifier unavailable * Keep final-reference regression paths and remove expired sysconfig argument Preserve reverse multiplication syntax and native exact overflow diagnostics. Accept the two source-proven CPython 3.15 specialization messages only in affected reverse str/bytes/tuple cases. Use an actually out-of-range Windows datetime errno fixture. Remove the sysconfig check_home argument at its existing canonical 3.15 removal test boundary. Assisted-by: OpenAI Codex:model identifier unavailable * Keep README wording unchanged in Python 3.15 transition Assisted-by: OpenAI Codex:model identifier unavailable * Match Python 3.15 repetition specialization and keep universal tests Use exact sequence-type guards and preserve generic fallback diagnostics. Validate both cold and warmed expressions without implementation-specific assertions. Reject stale CPython source checkouts and cross-version library caches. Assisted-by: OpenAI Codex:model identifier unavailable * Limit in-place specialization additions to exact sequence repetition Assisted-by: OpenAI Codex:model identifier unavailable * Keep subclass regression scoped to repetition dispatch Assisted-by: OpenAI Codex:model identifier unavailable * Provision final CPython 3.15 with pinned uv builds Assisted-by: OpenAI Codex:model identifier unavailable * Enforce Python 3.15 AST constructor requirements Reject retired missing-field and arbitrary-keyword forms while preserving defaults and explicit placeholders. Sync only matching final CPython constructor test hunks. Assisted-by: OpenAI Codex:model identifier unavailable * Keep seeded Python ahead of its Windows DLL directory Assisted-by: OpenAI Codex:model identifier unavailable * Keep opcode metadata tied to the implemented bytecode format The interpreter target and reference tests use Python 3.15.0, but the current Rust opcode enum retains the 3.14 bytecode format. Generate metadata from its matching source rather than importing unsupported instruction families. Assisted-by: OpenAI Codex:model identifier unavailable * Align CSV, logarithm and decimal formatting with Python 3.15 Propagate dialect lookup errors except AttributeError, retry integer logarithms after float overflow, and update the decimal conversion diagnostic. Exercise the final reference contracts without implementation-specific branches. Assisted-by: OpenAI Codex:model identifier unavailable * Bind the logarithm index result before borrowing its integer Assisted-by: OpenAI Codex:model identifier unavailable * Align required canonical test expectations with Python 3.15 Take only demonstrated contract changes from CPython v3.15.0, d220409a13b77de7a6c30abe16ea90db01528edc: removed module __cached__, Struct format exception classes, and decimal-format diagnostic. Preserve unrelated tests and expectations. Assisted-by: OpenAI Codex:model identifier unavailable * Track pending frozendict pickle support during the 3.15 transition Keep the two existing frozendict pickle tests running as expected failures until #8951 supplies the builtin. Unexpected success will signal removal of these temporary markers. Assisted-by: OpenAI Codex:model identifier unavailable * Support final Python 3.15 Windows negative timestamps Use FILETIME for pre-epoch conversions and preserve precise Windows timestamp errors. Assisted-by: OpenAI Codex:model identifier unavailable * Derive historical Windows timezone offsets from wall time FILETIME reports historical DST as unknown, so calculate the actual offset rather than selecting standard-time bias. Assisted-by: OpenAI Codex:model identifier unavailable * Identify decimal formatting arguments in Python 3.15 errors Track consumed positional arguments, including star width and precision, and preserve mapping key representations in decimal conversion diagnostics. Assisted-by: OpenAI Codex:model identifier unavailable * Reuse mapping format keys for lookup and error context Assisted-by: OpenAI Codex:model identifier unavailable * Borrow mapping diagnostic keys as Python objects Assisted-by: OpenAI Codex:model identifier unavailable * Avoid empty components in the reference Python library path Assisted-by: OpenAI Codex:model identifier unavailable * Isolate RustPython bytecode caches during the 3.15 transition Assisted-by: OpenAI Codex:model identifier unavailable * Sync final Windows timestamp test expectations Import only the final CPython platform-limit guards and mktime roundtrip expectation required by the existing Windows CI suite. Assisted-by: OpenAI Codex:model identifier unavailable * Restore official Python setup for 3.15 final Use the existing pinned setup-python action now that its official catalog supplies CPython 3.15.0. Remove the temporary standalone provisioning action and its sparse-checkout entry while retaining interpreter outputs and requested versions. Assisted-by: OpenAI Codex:model identifier unavailable * Align merged runtime contracts with Python 3.15 final Restrict mappingproxy ordering, use final codec registry normalization, and prefer index conversion before float conversion for sleep. Retain universal Python assertions and import the matching final codec/time canonical test hunks plus the non-ASCII codec-name deprecation warning. Upstream source: CPython d220409a13b77de7a6c30abe16ea90db01528edc. The existing codec normalization implementation and bytecode contract remain unchanged. Assisted-by: OpenAI Codex:model identifier unavailable * Limit Windows launcher version check to its interpreter Exclude only the CPython py.exe version-identity case from RustPython CI. The installed launcher reports its own final version, while RustPython correctly remains alpha. Preserve the other 45 launcher cases and all RustPython version assertions without modifying Lib. Assisted-by: OpenAI Codex:model identifier unavailable --------- Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
12 KiB
AI Agent Instructions for RustPython
This document provides guidelines for AI coding agents (GitHub Copilot, Claude Code, Gemini, etc.) contributing to the RustPython project.
Project Overview
RustPython is a Python 3 interpreter written in Rust, targeting Python 3.15 with compatibility work in progress. The project aims to provide:
- A complete Python-3 environment entirely in Rust (not CPython bindings)
- A clean implementation without compatibility hacks
- Cross-platform support, including WebAssembly compilation
- The ability to embed Python scripting in Rust applications
Repository Structure
See the "Code organization" section in CONTRIBUTING.md for the current directory layout.
AI Agent Rules
CRITICAL: AI Policy
- Follow RustPython's AI Policy for every AI-assisted contribution.
- Disclose AI assistance in commit messages with an
Assisted-by: AGENT_NAME:MODEL_VERSIONtrailer. Use one trailer per AI tool, and never useCo-authored-byfor an AI assistant.
CRITICAL: Git Operations
- NEVER create pull requests directly without explicit user permission
- NEVER push commits to remote without explicit user permission
- Always ask the user before performing any git operations that affect the remote repository
- Commits can be created locally when requested, but pushing and PR creation require explicit approval
CRITICAL: Commit Hooks and Validation
- Install the repository's pre-commit hook with
prek install(orpre-commit install) after cloning the repository. - Every commit must run the configured pre-commit hook. NEVER bypass it with
--no-verify. Automated workflows that use a normalgit commit, such asscripts/update_lib quick, should be allowed to create local commits through the hook. - If a hook auto-fixes files (e.g.
ruff-format,rustfmt), re-stage the fixes and retry the commit. Do not amend or force a failing commit through. - Before completing a task, run the tests appropriate for the change. Test commands are documented in the Testing section below. At minimum run
cargo test --workspace --exclude rustpython_wasm --exclude rustpython-venvlauncher --exclude rustpython-capi, then runcargo testfromcrates/capi; if the change touchesextra_tests/snippets/runpytest -vthere too, and if it touchesLib/or interpreter behavior, run the relevantcargo run --release -- -m test <module>modules.
Important Development Notes
Running Python Code
When testing Python code, always use RustPython instead of the standard python command:
# Use this instead of python script.py
cargo run -- script.py
# For interactive REPL
cargo run
# With specific features
cargo run --features jit
# Release mode (recommended for better performance)
cargo run --release -- script.py
Comparing with CPython
When you need to compare behavior with CPython or run test suites:
# Use python command to explicitly run CPython
python my_test_script.py
# Run RustPython
cargo run -- my_test_script.py
Working with the Lib Directory
The Lib/ directory contains Python standard library files copied from the CPython repository. Important notes:
- These files should be edited very conservatively
- Modifications should be minimal and only to work around RustPython limitations
- Tests in
Lib/testoften use one of the following markers:- Add a
# TODO: RUSTPYTHONcomment when modifications are made unittest.skip("TODO: RustPython <reason>")unittest.expectedFailurewith# TODO: RUSTPYTHON <reason>comment
- Add a
Choosing the right marker
When marking a test that fails on RustPython, prefer one of the following forms:
@unittest.expectedFailure # TODO: RUSTPYTHON; <reason>
# or
@unittest.expectedFailureIf(<condition>, "TODO: RUSTPYTHON; <reason>")
If the test would crash the interpreter (segfault, Rust panic, abort, infinite loop), use skip instead so the rest of the suite can still run:
@unittest.skip("TODO: RUSTPYTHON; <reason>")
# or
@unittest.skipIf(<condition>, "TODO: RUSTPYTHON; <reason>")
When to use which:
- Prefer
expectedFailure/expectedFailureIfby default. The test body still runs, so if RustPython is later fixed, the unexpected pass surfaces immediately and the decorator can be removed. Use the conditional*Ifform when the failure is environment-specific (e.g., a platform or build flag). - Use
skip/skipIfonly when running the test would take down the test process — segfaults, Rust panics, aborts, or hangs that block subsequent tests. Skipping keeps the suite usable;expectedFailurecannot help here, because the test body still executes.
To find WIP entries that are partly modified and may need follow-up:
grep -d recurse 'TODO: RUSTPYTHON' Lib/test/
Clean Build
When you modify bytecode instructions, a full clean is required:
rm -r target/debug/build/rustpython-* && find . | grep -E "\.pyc$" | xargs rm -r
Testing
# Run Rust unit tests
cargo test --workspace --exclude rustpython_wasm --exclude rustpython-venvlauncher --exclude rustpython-capi
# Run C-API tests from their directory so their separate Cargo config applies
(cd crates/capi && cargo test)
# Run Clippy with the same C-API separation
cargo clippy --workspace --all-targets --exclude rustpython_wasm --exclude rustpython-venvlauncher --exclude rustpython-capi
(cd crates/capi && cargo clippy --all-targets)
# Run Python snippets tests (debug mode recommended for faster compilation)
cargo run -- extra_tests/snippets/builtin_bytes.py
# Run all Python snippets tests with pytest
cd extra_tests
pytest -v
# Run the Python test module (release mode recommended for better performance)
cargo run --release -- -m test ${TEST_MODULE}
cargo run --release -- -m test test_unicode # to test test_unicode.py
# Run the Python test module with specific function
cargo run --release -- -m test test_unicode -k test_unicode_escape
Note: For extra_tests/snippets tests, use debug mode (cargo run) as compilation is faster. For unittest (-m test), use release mode (cargo run --release) for better runtime performance.
Determining What to Implement
Run ./scripts/whats_left.py to get a list of unimplemented methods, which is helpful when looking for contribution opportunities.
Coding Guidelines
Rust Code
- Follow the default rustfmt code style (
cargo fmtto format) - IMPORTANT: Always run clippy to lint code (
cargo clippy) before completing tasks. Fix any warnings or lints that are introduced by your 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
Comments
- Do not delete or rewrite existing comments unless they are factually wrong or directly contradict the new code.
- Do not add decorative section separators (e.g.
// -----------,// ===,/* *** */). Use///doc-comments or short//comments only when they add value. - A
///doc comment on a Python-exposed item (#[pyattr],#[pyclass],#[pyfunction],#[pymethod],#[pyclassmethod],#[pystaticmethod],#[pygetset],#[pymember],#[pyslot],#[pyexception],#[pymodule],#[pystruct_sequence]) is that item's docstring and overrides the stored docs inrustpython-doc(crates/doc). Those stored docs are used only when the item has no///comment. Developer notes on these items must be//comments.
Avoid Duplicate Code in Branches
When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once.
Bad:
let result = if condition {
let msg = format!("message A: {x}");
some_function(msg, shared_arg)
} else {
let msg = format!("message B");
some_function(msg, shared_arg)
};
Good:
let msg = if condition {
format!("message A: {x}")
} else {
format!("message B")
};
let result = some_function(msg, shared_arg);
Python Code
- IMPORTANT: In most cases, Python code should not be edited. Bug fixes should be made through Rust code modifications only
- Follow PEP 8 style for custom Python code
- Use ruff for linting Python code
- Minimize modifications to CPython standard library files
Integration Between Rust and Python
The project provides several mechanisms for integration:
pymodulemacro for creating Python modules in Rustpyclassmacro for implementing Python classes in Rustpyfunctionmacro for exposing Rust functions to PythonPyObjectRefand other types for working with Python objects in Rust
Common Patterns
Implementing a Python Module in Rust
#[pymodule]
mod mymodule {
use rustpython_vm::prelude::*;
#[pyfunction]
fn my_function(value: i32) -> i32 {
value * 2
}
#[pyattr]
#[pyclass(name = "MyClass")]
#[derive(Debug, PyPayload)]
struct MyClass {
value: usize,
}
#[pyclass]
impl MyClass {
#[pymethod]
fn get_value(&self) -> usize {
self.value
}
}
}
Adding a Python Module to the Interpreter
vm.add_native_module(
"my_module_name".to_owned(),
Box::new(my_module::make_module),
);
Building for Different Targets
WebAssembly
# Build for WASM
cargo build --target wasm32-wasip1 --no-default-features --features freeze-stdlib,stdlib --release
JIT Support
# Enable JIT support
cargo run --features jit
Linux Build and Debug on macOS
See the "Testing on Linux from macOS" section in CONTRIBUTING.md.
Building venvlauncher (Windows)
IMPORTANT: All 4 venvlauncher binaries use the same source code. Do NOT add multiple [[bin]] entries to Cargo.toml. Build once and copy with different names.
Test Code Modification Rules
CRITICAL: Test code modification restrictions
- NEVER comment out or delete any test code lines except for removing
@unittest.expectedFailuredecorators and upper TODO comments - NEVER modify test assertions, test logic, or test data
- When a test cannot pass due to missing language features, keep it as expectedFailure and document the reason
- The only acceptable modifications to test files are:
- Removing
@unittest.expectedFailuredecorators and the upper TODO comments when tests actually pass - Adding
@unittest.expectedFailuredecorators when tests cannot be fixed
- Removing
Examples of FORBIDDEN modifications:
- Commenting out test lines
- Changing test assertions
- Modifying test data or expected results
- Removing test logic
Correct approach when tests fail due to unsupported syntax:
- Keep the test as
@unittest.expectedFailure - Document that it requires PEP 695 support
- Focus on tests that can be fixed through Rust code changes only
CI Workflows
If you modify any file under .github/workflows/, the change must pass a zizmor scan in CI.
Documentation
- Check the architecture document for a high-level overview
- Read the development guide for detailed setup instructions
- Generate documentation with
cargo doc --no-deps --all - Online documentation is available at docs.rs/rustpython
- How to update test files — guide for syncing test cases from upstream CPython into the
Lib/directory