* Document only workspace crates in the cargo doc job
cargo doc ran rustdoc on every dependency as well; in run #91 that was
284 documented crates for a check of this workspace's own docs.
Assisted-by: Claude Code
Claude-Session: https://claude.ai/code/session_01JpHqXiC3mDShqfsPxvr9ey
* Share one target directory between the example projects
Each example project is its own workspace and built into its own target
directory, so the second build recompiled the dependencies the first had
just built. Of the 359 units the frozen_stdlib build needs, 261 are
identical to units of the barebone build.
Assisted-by: Claude Code
Claude-Session: https://claude.ai/code/session_01JpHqXiC3mDShqfsPxvr9ey
* Check both macOS cross targets on one runner
macOS runners have the smallest concurrent-job limit of the hosted
runners. The aarch64-apple-ios and x86_64-apple-darwin checks now run in
one job, with one cargo invocation per feature set that passes both
targets, so they also share the host-side build scripts and proc macros.
The matrix entry lists the targets separated by a space, because the
target is part of the cache key and actions/cache rejects commas.
This renames the two "cargo check (macos-latest, ...)" checks to one.
Assisted-by: Claude Code
Claude-Session: https://claude.ai/code/session_01JpHqXiC3mDShqfsPxvr9ey
---------
Co-authored-by: Claude <noreply@anthropic.com>
Stop automatically labeling OSCCA participants' pull requests with
z-ca-2026 and assigning them, and drop the matching zizmor ignore entry.
Assisted-by: Claude Code:claude-opus-5-5
* Add script to run upstream pyperformance against RustPython
pyperformance can't be installed as-is on RustPython since pyperf hard-depends
on psutil, a C extension RustPython cannot build or load (no CPython C-API /
extension-module support). Since pyperf already disables psutil usage on
interpreters reporting Py_GIL_DISABLED=1 (which RustPython does, having no
GIL), a functionality-free pure-Python psutil stub is enough to satisfy pip's
dependency resolution and unblock any pure-Python benchmark.
scripts/pyperformance/run_all.py drives pyperformance (under a host CPython
venv) with RustPython as the --python target, installs the stub via
PIP_FIND_LINKS + --inherit-environ, and catalogs pass/fail/timeout per
benchmark with an automatic failure classifier (C-extension build blocked vs.
a genuine exception raised by the benchmark itself).
Assisted-by: Claude Code:claude-sonnet-5
* pyperformance: support targeting any Python and comparing catalogs
Generalize run_all.py from a RustPython-only runner to a --python/--label
pair, so the same script can catalog a real CPython (with --no-psutil-stub,
since it has a genuine psutil) alongside RustPython. Each label gets its own
results/<label>/ subdirectory.
Add compare.py, which loads two labels' catalogs and writes a per-benchmark
Markdown table with both targets' status/mean and the candidate/baseline
time ratio, plus the median slowdown across benchmarks both targets passed.
Assisted-by: Claude Code:claude-sonnet-5
* ci: compare base, head and CPython with pyperformance on one runner
A ratio between two pyperformance runs measured on different machines can
move further than the change under review did, so a PR's numbers are only
worth reading when base, head and CPython were measured back to back on the
same runner. This adds a job that does exactly that -- two release builds
kept side by side with the stdlib of their own commit, then three runs with
nothing in between -- and posts the table as a PR comment.
`pr_diff.py` renders the three catalogs `run_all.py` writes: per benchmark
the CPython time, both RustPython times, both ratios against CPython, and
the head/base change, with anything under 3% shown as `~` because a shared
runner is not quiet enough to say more.
The comment is handed to a `workflow_run` companion the same way
`codspeed-comment.yaml` does it, since a `pull_request` run from a fork gets
a read-only token however the workflow is configured.
The job is opt-in behind a `run:pyperformance` label: it runs 28 benchmarks
three times over and costs well over an hour, which most PRs do not need.
`run_all.py` now forwards RUSTPYTHONPATH through pyperf, which a binary
copied away from its checkout needs in order to find the stdlib at all; the
two names go in one comma-separated `--inherit-environ`, because repeating
the flag keeps only the last.
Assisted-by: Claude Code:claude-fable-5-1
* ci: comment from the pyperformance run itself when the PR allows it
The `workflow_run` companion only starts firing once it is on the default
branch, so a pull request that introduces it -- this one -- would upload the
table and never show it. A pull request opened from this repo carries a
token that may write comments, so post it from the run that measured it and
leave the companion to the forks that genuinely cannot.
Both paths upsert on the same marker, so a fork PR that reaches the
companion after this step was skipped still lands one comment.
Assisted-by: Claude Code:claude-fable-5-1
* ci: address zizmor findings and fall back to head vs. CPython
- Pin `actions/github-script` to the commit the rest of the repo pins it to;
the hash carried over from an in-flight branch pointed elsewhere than the
`v9.0.0` comment claimed (ref-version-mismatch).
- Read the PR number, the CPython path and version, and the two commit shas
from `env` rather than expanding them into the shell (template-injection).
- Record the `workflow_run` trigger in `.github/zizmor.yml` next to the
existing `pull_request_target` entry, with the same justification: the
workflow reads one artifact and writes a comment, and never checks out or
runs pull request code (dangerous-triggers).
Building or measuring the base commit is now non-fatal, and `pr_diff.py`
renders head against CPython alone when the base column has nothing in it --
a base that will not build should still leave a usable table rather than
failing the job. The time and ratio columns are also named apart, since
`| base | head | base | head |` gave no clue which pair was which.
Assisted-by: Claude Code:claude-fable-5-1
* ci: derive pyperformance comment target from trusted workflow_run metadata
pr_number.txt came from the artifact a `pull_request` run uploads, which a
fork could tamper with to make this privileged `workflow_run` comment on an
arbitrary PR/issue. Resolve the PR from context.payload.workflow_run instead
(falling back to a commit-associated open PR lookup for workflow_dispatch
runs), and escape any HTML comments already present in diff.md so they can't
be mistaken for the marker.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* ci: grant actions:read to the pyperformance comment workflow
download-artifact needs actions:read to fetch another run's artifact by
run-id. Specifying a permissions block sets every unlisted scope to none, so
without this the cross-run download silently failed for fork PRs -- the
scenario this workflow exists for -- continue-on-error masked it, and the
comment step found no diff.md and returned without posting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: kill the whole process group on a benchmark timeout
subprocess.run's own timeout only terminates the direct pyperformance
process; pyperf re-execs the target interpreter as a child, which can
survive the timeout and keep burning CPU into the next benchmark's
measurement. Run each benchmark in its own session/process group instead,
and on timeout SIGKILL the whole group via os.killpg.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: compute the head-vs-base cell from head_vs_base directly
The cell was derived from base_vs_cpython and head_vs_cpython, both of which
are None when a benchmark has no CPython time -- so it rendered "-" even
though base and head both had valid times and head_vs_base was already
computed. Use that precomputed ratio instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: invalidate the resumed catalog when the target executable changes
catalog.json records results without any fingerprint of target_python, and
resume skips benchmarks by name alone, so a local rebuild (or swapping the
target) under the same --label could silently reuse stale measurements.
Store a sha256 of the executable's contents alongside the catalog and, when
it no longer matches, ignore the existing catalog and re-run everything.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* ci: fall back to github.sha for the head checkout on workflow_dispatch
github.event.pull_request.head.sha is empty on a workflow_dispatch run (no
pull_request payload), so `git checkout --quiet "" --` failed with "fatal:
invalid reference" on this non-optional step, aborting every manual run
before it could measure or render results.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: resolve --out/--cache-dir to absolute paths
Every benchmark subprocess runs with cwd=work_dir (under --cache-dir), so a
relative --out or --cache-dir passed on the command line resolved against
that directory instead of the one run_all.py was invoked from, breaking
catalog output, the pyperformance binary path, and PIP_FIND_LINKS. Both
default to absolute paths already; resolve them up front so a relative
override behaves the same way.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: persist catalog invalidation before the new fingerprint
The fingerprint file was rewritten before the invalidated catalog was ever
written back to disk. An interruption between the two (e.g. a crash during
the first benchmark of the re-run) left a fingerprint matching the new
executable pointing at a catalog.json still full of results measured
against the old one, so the next run would trust it as fresh. Write the
reset (empty) catalog first, then record the fingerprint.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: show benchmarks that fail on both base and head
Rows were dropped whenever neither side had a mean, which also hid a
benchmark that failed or timed out identically on base and head -- e.g. a
C-extension gap unrelated to the PR under review. Only skip a benchmark
never attempted on either commit; keep rows with a fail/timeout status so
they still show up in the table.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: write catalog.json atomically
write_catalog runs after every single benchmark, and Path.write_text
truncates the file before writing the replacement. An interruption
mid-write left catalog.json both missing every earlier result and invalid
JSON, so the next run's resume logic failed outright at json.loads(). Write
to a temporary sibling file and rename it into place instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: fix passed count and median population in the summary table
"Benchmarks passed" counted len(base_ratios)/len(head_ratios), which require
a *CPython* ratio too -- a CPython-only failure hid an otherwise-passing
benchmark from both sides' counts. Count actual "ok" status per side
instead.
The two medians were also computed over independently-filtered
populations: whichever benchmarks had a valid ratio on that side alone. A
benchmark that regressed to failure on only one side dropped out of only
that side's median, which could shift it well beyond what any surviving
benchmark's timing actually changed by. Restrict both medians to
benchmarks with a valid ratio on both sides, and label the population size.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: include RUSTPYTHONPATH's stdlib in the executable fingerprint
The catalog-invalidation fingerprint hashed only the executable's bytes, but
a RustPython binary copied away from its checkout finds its stdlib through
RUSTPYTHONPATH -- pyperf re-execs the target interpreter into a venv and
forwards that variable so it can still find Lib/. Changing that path, or
editing the files under it, without rebuilding the binary left resume
trusting results measured against a now-different stdlib. Fold its contents
into the fingerprint too.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: require an explicit ok/fail status for pass/fail transitions
only_head/only_base flagged a benchmark as newly passing or no longer
passing based only on whether the other side had a mean, so a benchmark
never attempted on one side (e.g. a partial --benchmarks run) was reported
as a regression or fix it never was. Require the other side's status to be
an actual fail/timeout, not just missing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: don't gate the head/base geometric mean on a CPython ratio
The summary panel -- pass counts and the head/base geometric mean, neither
of which need a CPython comparison -- only rendered when at least one
benchmark also had a valid ratio against CPython on both sides. A build
with a perfectly good head-vs-base comparison but no common CPython result
lost the whole panel, including the one aggregate that didn't depend on
CPython at all.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: normalize RUSTPYTHONPATH and extract main() into named steps
Fix: a relative RUSTPYTHONPATH was fingerprinted relative to this script's
invocation directory but resolved by pyperf's subprocess relative to
work_dir (its cwd), so a relative value could be hashed against the wrong
directory entirely, or fail to resolve at runtime. Resolve it to an
absolute path up front, alongside --out and --cache-dir.
Refactor: main() had accumulated argparse setup, executable resolution, and
the fingerprint/resume block inline, each wrapped in an explanatory comment
-- a sign the function was doing too much rather than that it needed more
narration. Extracted parse_args(), resolve_target_python(), and
load_resumable_catalog() so main() reads as a short sequence of named
steps and each comment sits next to the one function it actually explains.
No behavior change; verified with unit tests against the extracted
functions (fingerprint reacting to RUSTPYTHONPATH, resume picking up a
prior catalog, invalidation on an executable change).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: split pr_diff.py's main() and fix a status-transition gap
Fix: only_head was still gated on have_base, which only asks whether *any*
base benchmark produced a timing -- so a benchmark that flipped from
failing to passing went unreported whenever every other base benchmark
also happened to fail. Status transitions don't need that gate; they
already require an explicit ok/fail/timeout status on both sides.
Refactor: main() had grown into one long function mixing argument parsing,
catalog loading, row computation, and three separate rendering concerns.
Split it into parse_args(), load_base_catalog(), build_rows(),
render_intro(), render_summary_panel(), render_table(), and
find_transitions(), each documenting the one thing it decides. No behavior
change beyond the fix above; re-verified against every scenario exercised
in the last several fixes (dual-fail rows, geo mean without a CPython
ratio, unattempted benchmarks, and the have_base gap) plus the existing
manual smoke tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* pyperformance: don't resolve an empty RUSTPYTHONPATH to the cwd
The normalization only checked "RUSTPYTHONPATH" in os.environ, so a set-but-
empty value (RUSTPYTHONPATH="") resolved to the current directory via
Path("").resolve(). executable_fingerprint() would then recursively hash
every file under wherever run_all.py was invoked from -- including its own
results directory while writing to it -- causing spurious catalog
invalidation and a lot of pointless hashing. Check for a nonempty value
instead, matching the truthiness check already used everywhere else this
variable is read.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
* docs(pyperformance): distinguish the host runner from the benchmarked target
A reviewer read "pyperformance cannot be installed as-is on RustPython" and
reasonably asked why RustPython would need to support pyperformance's CLI at
all, since it's normally run as a command under any Python (see
https://github.com/RustPython/RustPython/pull/8691#discussion_r3997957942).
It doesn't: the pyperformance CLI (the host process) always runs under a
real CPython regardless of --python. What actually needs psutil/pyperf
importable is the *benchmark* subprocess -- pyperf re-execs the --python
target and that subprocess itself does `import pyperf`. Spell out that
host/worker split up front instead of leading with "cannot be installed".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy8J7LpmdYjesTDonNoYkv
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Add dependency-free CPU-bound workloads adapted from pyperformance 1.14.0 to cover interpreter, standard-library serialization, regex, numeric, and garbage-collection paths under the existing Criterion and CodSpeed harnesses. Keep inputs deterministic and scale expensive cases for simulation while checking that benchmark execution does not leak interpreter state.
Assisted-by: Codex:gpt-5.6-sol
* Fix tuple subclass preservation in type bases
Assisted-by: Codex:gpt-5.6-sol
* Refine type bases representation
Store validated base classes in the original typed tuple, while retaining a bootstrap vector only until a Python tuple can be materialized.
Assisted-by: gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
* Bootstrap tuple type with type hierarchy
Initialize tuple and the canonical empty tuple alongside type and object so every type stores an actual typed bases tuple from construction. Remove the bootstrap bases representation and keep raw initialization Miri-safe.
Assisted-by: Codex:gpt-5.6-sol
* uuid: expose stable hardware node
Cache native MAC address detection and advertise an extractable node only when a hardware address is available. This lets uuid.getnode() use the native UUID path while preserving its existing fallbacks.
Assisted-by: Codex:gpt-5.6-sol
* Prefer universally administered MAC addresses
Match uuid.getnode()'s documented interface-selection rule by scanning available addresses before falling back to a locally administered one. Ignore zero addresses and cover the selection behavior with Rust unit tests.
Assisted-by: Codex:gpt-5.6-sol
* Allow automated commits through pre-commit hooks
* Fix agent C-API test instructions
Exclude rustpython-capi from root workspace test commands and run its tests from the crate directory so its Cargo configuration applies. Document the project AI policy and required commit trailer.
Assisted-by: Codex:gpt-5
Raise OverflowError when a finite f64 value overflows while being converted for the struct f format. Remove the expected-failure marker from the corresponding regression test.
Assisted-by: Codex:gpt-5
Apply the maintainer review suggestion from RustPython/RustPython#8270. Codex inspected the review context, made the one-line whitespace-only edit, and ran validation.
Assisted-by: Codex:gpt-5
* Add GetDescriptor for PyBoundMethod (return self)
CPython's method_descr_get always returns the bound method unchanged.
This preserves the original binding when __get__ is called on an
already-bound method (e.g. a.meth.__get__(b, B) still returns a).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add constructor validation for PyBoundMethod
Reject non-callable functions and None instances, matching CPython's
method_new which checks PyCallable_Check(func) and instance != Py_None.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix PyBoundMethod __reduce__ to propagate errors
Previously swallowed errors from get_attr with .ok(), silently
returning None. Now propagates errors matching CPython's method_reduce.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate direct __repr__ definitions to Representable trait
Move legacy #[pymethod] __repr__ to impl Representable for:
- PySSLContext, PySSLSocket (ssl.rs)
- BufferedReader, BufferedWriter, BufferedRandom (_io.rs)
Enable __repr__ guard in derive to prevent future direct definitions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Migrate direct __del__ definition to Destructor trait for PySocket
Move #[pymethod] __del__ to impl Destructor for PySocket.
Preserves ResourceWarning emission and close behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Remove redundant __iter__ pymethods from PyFuture and PyTask
Both types already have impl Iterable, so the direct #[pymethod]
__iter__ definitions were duplicates. Enable __iter__/__next__
guards in derive to prevent future direct definitions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enable derive guards for __get__/__set__/__delete__ descriptor methods
All concrete types already use GetDescriptor/SetDescriptor traits.
Activate the compile-time guard to prevent future direct definitions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enable derive guards for AsMapping/AsSequence slot methods
Remove redundant #[pymethod(name = "__len__")] from PyStr
(already provided via AsMapping/AsSequence trait impls).
Enable compile-time guards for __len__, __contains__, __getitem__,
__setitem__, __delitem__ to prevent future direct definitions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Preserve imaginary zero signs when adding real values to complex numbers
* Refactor complex_add with match expression
* Correct complex real subtract op
* Remove unnecessary vm arugment
* Add compile_bool_op_inner and optimize nested opposite-operator BoolOps to avoid redundant __bool__ calls
When a nested BoolOp has the opposite operator (e.g., `And` inside `Or`),
the inner BoolOp's short-circuit exits are redirected to skip the outer
BoolOp's redundant truth test. This avoids calling `__bool__()` twice on
the same value (e.g., `Test() and False or False` previously called
`Test().__bool__()` twice instead of once).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add snapshot test for nested BoolOp bytecode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add runtime test for redundant __bool__ check (issue #3567)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Apply clippy and rustfmt
* Apply ruff format
* Refactor compile_bool_op: extract emit_short_circuit_test and unify with compile_bool_op_inner
Reduce code duplication by:
- Extracting the repeated Copy + conditional jump pattern into emit_short_circuit_test
- Merging compile_bool_op and compile_bool_op_inner into a single
compile_bool_op_with_target with an optional short_circuit_target parameter
- Keeping compile_bool_op as a thin wrapper for the public interface
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Relocate redundant __bool__ check test snippet
* Update extra_tests/snippets/syntax_short_circuit_bool.py
* Fix assertion in syntax_short_circuit_bool
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
The encoding errors occur in setUp() which causes ERRORs, not FAILUREs.
@unittest.expectedFailure only catches test failures, not setUp errors.
Using class-level @unittest.skip properly handles this case.
* Simplify upgrade-pylib.md by removing manual copy steps handled by quick command
* Let upgrade-pylib run auto-mark instead marking manually
* Let upgrade-pylib a separate commit from updating module
* Correct commands' permissions
* Add open PR check to upgrade-pylib-next command
* Fix dependency pattern example in upgrade-pylib-next command
* Let upgrade-pylib command to invetigate failing tests
* Add pre-commit review to invetigate-test-failure command
* Use deps command to get dependent tests in upgrade-pylib workflow
This pull request updates `types` module to v3.14.2. While doing it, it
fixes also async-related feature. This pull request's base is generated
by #6827.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Generators that act as iterable coroutines are now recognized as
awaitable, improving async behavior.
* Yield-from and await interactions now handle coroutine-iterable
sources more consistently.
* **Bug Fixes**
* Reduces spurious TypeError cases when awaiting or yielding from
wrapped coroutine-like generators.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* Use empty list as default in ClassDef.{bases,keywords}
* Unmark resolved tests
* Use empty list as default in FunctionDef.type_params
* Use empty list as default in ClassDef.type_params
Change benchmark interpreter from without_stdlib to with_init to load
Rust-optimized native modules like _json, ensuring benchmarks measure
actual optimized performance rather than pure Python fallbacks.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Generate a metadata file alongside criterion benchmark data containing
the commit hash, ref name, and timestamp for tracking purposes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Insert unittest import line if it doesn't exist
* Complement upgrade-pylib Claude Code command
* Ensure test module having import statement at least one
* Parse JSON in Rust
* Reuse key when decoding JSON
* Unmark resolved test
* Parse null/true/false directly in call_scan_once
Parse JSON constants (null, true, false) directly in Rust within
call_scan_once() instead of falling back to Python scan_once.
This reduces Python-Rust boundary crossings for array/object values.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Parse numbers directly in call_scan_once
Parse JSON numbers starting with digits (0-9) directly in Rust within
call_scan_once() by reusing the existing parse_number() method.
This reduces Python-Rust boundary crossings for array/object values.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Parse NaN/Infinity/-Infinity in call_scan_once
Parse special JSON constants (NaN, Infinity, -Infinity) and negative
numbers directly in Rust within call_scan_once(). This handles:
- 'N' -> NaN via parse_constant callback
- 'I' -> Infinity via parse_constant callback
- '-' -> -Infinity or negative numbers via parse_constant/parse_number
This reduces Python-Rust boundary crossings for array/object values.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Correct wrong index access
* Leave more flame span
* Refactor json scanstring with byte index
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Use shared PYTHON_VERSION in cron-ci benchmark job
* Correct extra-tests/jsontests.py script
When the `extra_tests/jsontests.py` script was added, it was presumably during
the time when Python versions 3.5 to 3.8 were in use. At that time,
`test.libregrtest.runtest` was a valid path, but from CPython version 3.11
onwards, the path changed to `test.libregrtest.findtests`.
* Use ssl-rustls feature instead of ssl in cron-ci workflow
Replace the ssl feature with ssl-rustls in both the CARGO_ARGS
environment variable and the cargo-llvm-cov test command to fix
the cron-ci workflow.
Since 1a783fc9ec, it is disallowed
to use ssl manually.
* Replace inspect.getargspec with inspect.getfullargspec in custom_text_test_runner
inspect.getargspec() was removed in Python 3.11, causing jsontests.py to
fail with "TypeError: 'NoneType' object is not iterable" when running on
RustPython (which targets Python 3.13).
The get_function_args() function was silently catching the AttributeError
and returning None, which then caused the error in store_class_fields()
when trying to iterate over None.
> https://docs.python.org/3/whatsnew/3.11.html
> The getargspec() function, deprecated since Python 3.0; use
> inspect.signature() or inspect.getfullargspec() instead.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Exclude rustpython-venvlauncher in cron-ci workflow
Since rustpython-venvlauncher is Windows-only,
it disables the project in the cron-ci workflow.
* Apply suggestion from @fanninpm
Co-authored-by: fanninpm <luxverdans@sbcglobal.net>
* Trigger cron-ci workflow in pull_request
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
Co-authored-by: fanninpm <luxverdans@sbcglobal.net>
* Replace architecture overview image with mermaid graph
* Update architecture.md with correct paths and links
- Fix file paths to reflect crates/ directory structure
- Update GitHub permalink references to current commit
- Remove trailing slashes from package names
- Use archive.org for broken desosa.nl link
- Change master to main branch reference
* Update architecture documentation for ruff_python_parser
RustPython now uses ruff_python_parser from astral-sh/ruff instead of
the separate RustPython/Parser project. Update architecture documentation
to reflect this change.