* Add a native _zoneinfo module
ZoneInfo reads TZif tuples from zoneinfo._common.load_data. The stdlib
module implements utcoffset, dst, tzname, fromutc, and the zone cache.
whats_left compares that class. Cache tests no longer expect failure.
Assisted-by: Grok:grok-4.7
* Fix ZoneInfo layout and keep its cache on the module
utcoffset, dst, and tzname take the object. Zone data is boxed so the
payload lines up with tzinfo on 32-bit. The strong, weak, and timedelta
caches live on each interpreter's _zoneinfo module.
Assisted-by: Grok:grok-4.7
* zlib: clear unconsumed_tail when a bounded decompress reaches stream end
When a max_length-bounded Decompress.decompress() call reached the end
of the stream, the bytes after the stream went to unused_data, but
unconsumed_tail kept the previous call's tail, including input the call
had already consumed. A later flush() then moved those stale bytes into
unused_data.
Empty unconsumed_tail at end of stream, as CPython's
save_unconsumed_input() does since gh-158169. CPython releases before
that change report the bytes after the stream in unconsumed_tail, so
the snippet accepts either value when it runs under CPython.
Fixes#8906
Assisted-by: Claude Code:claude-opus-5-5
* zlib: bind the unconsumed input with an if expression
Choose between the empty slice and the rest of the input in one
expression instead of reassigning a mutable binding.
Assisted-by: Claude Code:claude-opus-5-5
* zlib: accept both valid tails in the snippet on every implementation
The snippet no longer branches on sys.implementation. RustPython and
CPython after gh-158169 give an empty tail; CPython before it gives the
bytes after the stream. The stale tail of gh-8906 fails either way.
Assisted-by: Claude Code:claude-opus-5-5
* Bound AST nesting so deep source raises instead of crashing
Deeply nested source exhausted the native stack and killed the process
with SIGSEGV, which no Python-level `except` can catch.
The parser grows its own stack when it runs low on one, so it returns
trees deeper than the passes after it can walk. Those passes recurse
without a limit of their own, and the symbol table, which does apply
one, runs last. `compile(..., PyCF_ONLY_AST)` returns before reaching
it at all.
Apply that same limit to the tree right after it is parsed, and count
bracket nesting before parsing rather than after, as CPython's
tokenizer does. Checking afterwards built the tree first, and one
nested that deep exhausts the stack when it is dropped.
Fixes#7655
Assisted-by: Claude Code:Opus 5
* Return Result from too_deeply_nested_error
Lets the four call sites use `?` instead of matching on `Some` and
returning the error by hand, which drops 11 lines. `Result` is already
`#[must_use]`, so the attribute on the function goes away with it.
Assisted-by: Claude Code:Opus 5
* Return Result from pre_parse_source_error too
Same shape as the previous commit, for the other check this PR adds, so
the two are consistent with each other.
Assisted-by: Claude Code:Opus 5
* Drop exceeds_max_nesting for the check beside it
The escape-warning pass needs the bracket check but had no `SourceFile` to
build a `CompileError` from, so it got a `bool` wrapper of its own. Building
the `SourceFile` there lets it call `pre_parse_source_error` instead, which
is shaped like the other checks in that file, and costs nothing measurable.
Assisted-by: Claude Code:Opus 5
* fix(gc): traverse annotate name and qualname fields in PyFunction
* style: format extra_tests/snippets/stdlib_gc.py
Assisted-by: Antigravity:Gemini-3.8-Flash
* fix(contextvars): isolate context stack per interpreter and thread on VirtualMachine
* style: format and sort imports in extra_tests/snippets/stdlib_contextvars.py
Assisted-by: Antigravity:Gemini-3.8-Flash
* Raise MemoryError instead of aborting on impossible allocations
Assisted-by: Claude Code:claude-opus-5-5
* use CPython 3.14's integer digit limit for left shifts
* size the pow memory check by what malachite allocates
* Make sequence allocations fallible and reuse deallocation buffers
Assisted-by: Codex:GPT-6
---------
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
* Accept `__index__` objects for integer arguments
CPython converts these nine arguments with `__index__`, RustPython
required an exact `int`. Uses the existing `ArgSize`/`ArgPrimitiveIndex`
converters at each call site rather than widening the blanket `int`
conversion, so argument paths CPython keeps strict (ctypes pointers,
thread handles) are unaffected, and the range check happens while the
argument is bound rather than in the method body.
Also widens `maxsplit` before adding one. `sys.maxsize` was already
reachable there with a plain `int` and overflowed in a debug build.
Assisted-by: Claude Code:claude-opus-5
* Add snippet for `__index__` argument conversion
Expectations verified against CPython 3.14.
Assisted-by: Claude Code:claude-opus-5
* Call __index__ in primitive integer conversion; add ArgStrictInt
The TryFromObject impls for isize, usize and the fixed-width integer
types now take an int (or subclass) as-is and otherwise call __index__,
instead of accepting only int instances.
ArgStrictInt<T> keeps the old int-only conversion. The ctypes address
and handle arguments (from_address, PyObj_FromPtr, dlsym, dlclose,
call_function, call_cdeclfunction, non-Windows FreeLibrary) use it.
_thread._make_thread_handle checks for an int itself and raises
"ident must be an integer".
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Grok:grok-4.7
* Accept __index__ where arguments were bound as int objects
chr, bin, math.factorial, zlib.crc32/adler32, binascii.crc32/crc_hqx,
the start/stop of sequence index(), the select event mask and a
__len__ result took an int object directly and rejected other objects
with __index__. They now go through __index__.
math.ldexp still accepts only an int for its exponent and raises
"Expected an int as second argument to ldexp." otherwise.
Extend protocol_index_argument.py with the newly accepted calls, an int
subclass whose __index__ is not called, and the sites that still
reject __index__.
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Grok:grok-4.7
* Replace ArgPrimitiveIndex and ArgSize with PySsize/PySize aliases
The primitive integer conversions call __index__ themselves, so
ArgPrimitiveIndex<T> and its alias ArgSize behaved the same as T. They
are removed. Arguments that used ArgSize or ArgPrimitiveIndex<isize>
now take PySsize (= isize), ArgPrimitiveIndex<usize> takes PySize
(= usize), and the other ArgPrimitiveIndex<T> take T. Those with a
literal fallback take it as a `default`; those where a missing argument
is a distinct state stay OptionalArg.
list.insert is a #[pymethod] again instead of going through a wrapper.
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Grok:grok-4.7
---------
Co-authored-by: Roshan Ramani <roshanramani.dev@gmail.com>
Accept F/D through the existing buffer packing implementation and restore
its complex round-trip test. Fix exact __complex__ results warning as
subclasses so valid conversions also work with warnings treated as errors.
Codex assisted with implementation, regression coverage, and review.
Tested: CPython 3.14.7 comparison; struct/memoryview/complex/cmath; workspace and C-API tests; Clippy; pytest; prek
Assisted-by: Codex:gpt-6-astra
* Speed up exec/eval and audited builtins when no audit hooks are set
Assisted-by: Claude Code:claude-opus-5-5
* move audit hooks from `VirtualMachine` to `PyGlobalState`
* addaudithook only appends once the lock
* sync with main
Use sequence concatenation for str and clear inherited concat slots when Python subclasses override __add__. Cover direct calls, reflected dispatch, and dynamic overrides against CPython.
Codex assisted with investigation, implementation, tests, validation, and the PR description.
Assisted-by: Codex:gpt-6
Allow the four required arguments to be passed by position or keyword, matching CPython and fixing pytest Node.warn calls. Add regression coverage for mixed calls, warning instances, and invalid argument binding.
Codex implemented the fix and regression tests, ran validation, and drafted this commit and the PR description. The contributor manually verified the warning reproducer.
Assisted-by: Codex:GPT-6
* Compose native text signatures at compile time from FromArgs
FromArgs gains a PARAMS const describing its Python parameters.
The derive builds the whole internal doc, `name(sig)\n--\n\ndoc`,
in a const context from those params, and `__text_signature__` /
`__doc__` parse it from the doc. The macro-time string path and the
runtime sig_parts composition are removed.
Signatures follow clinic conventions: `$module` for module functions,
Rust literal defaults rendered as Python reprs, other defaults as
`<unrepresentable>`, and a new `#[pyarg(py_default = "...")]` for the
few fields whose Rust default differs from the Python one. The
`#[pyarg]` keys are documented on the FromArgs derive.
Module functions keep their module object and return it from
`__self__`. PyNativeFunction now traverses its references.
os.stat, os.mkdir, os.listdir, os.open, dict.fromkeys, round, sorted,
int.to_bytes and bytes.hex take arguments matching their signatures.
Assisted-by: Grok:grok-4.7
Assisted-by: Claude:claude-opus-5-5
* Update builtin_signature snippet and fix mkdir on Windows
round and sum now report their FromArgs parameters, so the snippet
asserts their signatures instead of their absence. sum's start is a
PyObjectRef defaulting to int 0 with py_default "0". mkdir reads
dir_fd only on platforms that use it.
Assisted-by: Claude:claude-opus-5-5
* Remove expectedFailure from two signature tests
test_cmp_to_signature and test_autospec_on_bound_builtin_function
now pass. The TestCmpToKeyC override existed only for the marker.
Assisted-by: Claude:claude-opus-5-5
Return the kernel thread id (gettid / pthread_threadid_np /
GetCurrentThreadId). threading.get_native_id and Thread.native_id
follow once the function exists.
Assisted-by: Grok:4.6
get_find_range clamped end up to start, so an inverted range such as
(2, 1) became the empty-but-valid range (2, 2). A real subsequence then
found nothing there, which looked right, but the empty subsequence
matched and both methods reported a hit where CPython reports -1.
rfind also returned the start of the range for an empty subsequence,
because its body was copied from find and the returned edge was never
adjusted. An empty subsequence matches at the edge the scan begins
from, so a reverse scan must report the end.
Dropping the clamp lets an inverted range stay inverted and be rejected
explicitly. find and rfind now share find_inner, which keeps the two
directions from drifting apart again, and it checks the mmap is open
before inspecting the subsequence, so find(b"") on a closed mmap raises
ValueError instead of returning a position.
m = mmap.mmap(-1, 3); m[:] = b"abc"
m.rfind(b"") # was 0, now 3
m.rfind(b"", 1, 2) # was 1, now 2
m.find(b"", 2, 1) # was 2, now -1
m.rfind(b"", 2, 1) # was 2, now -1
Verified on macOS arm64 against CPython 3.14.7 with a 19-case table
covering both methods over empty and non-empty subsequences, clamped,
negative and inverted ranges: 8 mismatches before, none after. The
added snippet assertions fail on the unpatched build and pass on
CPython 3.14.7 as well. test_mmap, cargo fmt --check and
cargo clippy -p rustpython-stdlib -D warnings are clean.
Fixes#8722
Assisted-by: Claude Code:claude-opus-5