Repository navigation
Conversation
The function is used in 'feature = ssl'. However, the current codebase defines it without a conditional compilation expression and always be compiled. This commit removes the unused warning by adding a conditional compilation expression, '#[cfg(feature = "ssl")]'.
…Python#8313) Assisted-by: GitHub Copilot:claude-sonnet-4-6
* csv: quote embedded CR/LF under QUOTE_MINIMAL with custom lineterminator QUOTE_MINIMAL rows were serialized by csv-core, which only quotes bytes registered for the configured terminator, so fields containing '\r' or '\n' were left unquoted when the lineterminator was not CR/LF (e.g. '!' or '\0'). Route QUOTE_MINIMAL through a hand-written path that uses field_needs_quotes, which always treats '\r'/'\n' as needing quotes. A single empty field is quoted and an empty row emits only the terminator, matching CPython. QUOTE_ALL / QUOTE_NONNUMERIC still use csv-core. Assisted-by: Claude Code:claude-opus-4-8 * csv: unmark now-passing writer tests test_write_iterable and test_write_empty_fields pass with the QUOTE_MINIMAL writer fix (empty rows no longer emit an empty field). Assisted-by: Claude Code:claude-opus-4-8
Assisted-by: Codex:gpt-5.5 Signed-off-by: Jiwoo Ahn <[email protected]>
* ctypes: preserve result when errcheck returns args * ctypes: cover Windows pip truststore * capi: implement PyEval_SaveThread * capi: merge saved thread state into PyThreadState
* csv: reject reentrant reader advancement Snapshot the reader generation before calling the input iterator. If a nested call completes a record and advances the same reader, reject the outer call with csv.Error instead of continuing with stale parser state. Increment the generation on each successful record path, matching CPython's handling of the existing reentrant-reader regression. Enable that test by removing its expectedFailure marker. Assisted-by: Codex:gpt-5.6-sol * csv: invalidate outer reads after reentry Advance the reader generation immediately after the reentrancy check, before validating or parsing the returned input item. This invalidates the outer call even when a nested read later fails validation or parsing. Remove the per-return generation updates now that the common path advances it exactly once. Assisted-by: Codex:gpt-5.6-sol * csv: generation type conversion(usize to u64) On 32-bit targets (like WebAssembly, which RustPython supports), usize is 32-bit. Csv with over 32-bit rows can raise panic on these platform. type conversion from usize to u64.
* csv: handle empty fields with skipinitialspace skipinitialspace preprocessed records by trimming both ends of every split field. Fields containing only spaces could therefore produce an invalid slice, while trailing spaces were removed even though CPython preserves them. Trim only field prefixes in the csv-core path and make the remaining all-space trimming helper return an empty slice safely. QUOTE_NOTNULL and QUOTE_STRINGS add another distinction: an unquoted empty field becomes None, but a quoted empty field remains an empty string. Both forms have identical decoded bytes, so extend the existing QUOTE_NONE custom reader into one shared path that records whether each field started quoted. Use that metadata only for the null conversion while retaining QUOTE_NONE escape behavior. Unmark Test_Csv.test_read_skipinitialspace now that its standard, QUOTE_NOTNULL, and QUOTE_STRINGS cases pass. This keeps the existing one-item reader lifecycle and does not add the larger strict or multiline parser state machine. Assisted-by: Codex:gpt-5-sol * csv: make skipinitialspace quote-aware The csv-core preprocessing path split each raw iterator item on every delimiter before trimming field prefixes. Delimiters inside quoted fields were therefore treated as separators, so spaces after them could be removed before csv-core parsed the record. Extract the quote, escape, delimiter, and field-start transitions from read_quote_record() into a small per-item scanner. Reuse its events in the skipinitialspace preprocessor and discard only InitialSpace events while copying all other source bytes unchanged. This keeps the custom quote-mode reader and preprocessing logic aligned without introducing persistent multiline or strict parser state. Remove the delimiter cache that was only needed by the old split-and-join path and add a regression for spaces following a delimiter inside a quoted field. Assisted-by: Codex:gpt-5-sol
…RustPython#8331) Bumps [https://github.com/rbubley/mirrors-prettier](https://github.com/rbubley/mirrors-prettier) from v3.9.4 to 3.9.5. - [Commits](rbubley/mirrors-prettier@v3.9.4...v3.9.5) --- updated-dependencies: - dependency-name: https://github.com/rbubley/mirrors-prettier dependency-version: 3.9.5 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [https://github.com/astral-sh/ruff-pre-commit](https://github.com/astral-sh/ruff-pre-commit) from v0.15.20 to 0.15.21. - [Release notes](https://github.com/astral-sh/ruff-pre-commit/releases) - [Commits](astral-sh/ruff-pre-commit@v0.15.20...v0.15.21) --- updated-dependencies: - dependency-name: https://github.com/astral-sh/ruff-pre-commit dependency-version: 0.15.21 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…n#8330) * bz2: return streaming output from BZ2Compressor.compress() Return output produced during compression instead of waiting for flush(). Constraint: Preserve encoder state without flushing the BZ2 stream Confidence: high Scope-risk: narrow Directive: Do not flush merely to retrieve partial output Tested: test_bz2; stdlib_bz2.py; prek run --all-files Assisted-by: Codex:gpt-5.6-sol * bz2: remove redundant streaming-output snippet
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@48b55a0...8207627) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [which](https://github.com/harryfei/which-rs) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/harryfei/which-rs/releases) - [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md) - [Commits](harryfei/which-rs@8.0.4...8.0.5) --- updated-dependencies: - dependency-name: which dependency-version: 8.0.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.82.9 to 2.83.2. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](taiki-e/install-action@4684b84...43aecc8) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.83.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.13.0 to 2.13.1. - [Release notes](https://github.com/bitflags/bitflags/releases) - [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md) - [Commits](bitflags/bitflags@2.13.0...2.13.1) --- updated-dependencies: - dependency-name: bitflags dependency-version: 2.13.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.118 to 2.0.119. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](dtolnay/syn@2.0.118...2.0.119) --- updated-dependencies: - dependency-name: syn dependency-version: 2.0.119 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add more unicode functions Review Add unicode decode functions Add more functions * Remove * Add helper
* bytes: support keyword arguments in hex() * bytes: unmark test_memoryview_hex_separator
Signed-off-by: cuishuang <[email protected]>
Assisted-by: Codex:gpt-5
…_ATTR specializations (RustPython#8350) * Use dict keys-version stamps and entry-index hints in attr specializations Add a keys-version stamp to Dict: a globally unique u32 assigned lazily and reset on any key-set change (new key, deletion, clear). Value-only updates keep the stamp. - LoadAttrMethodWithValues / LoadAttrNondescriptorWithValues: cache the instance dict's stamp in the pointer cache and skip the shadow probe while the stamp matches. - LoadAttrWithHint: cache the entry index at specialization time; a hit is an identity check on the entry key instead of a hash probe, with self-refresh on miss. - StoreAttrWithHint / StoreAttrInstanceValue: replace values through the cached entry index via set_item_with_hint. Fixes youknowone#42 Assisted-by: Claude * Share keys-version stamps between dicts with identical layouts Derive the keys-version stamp from a shape — the ordered interned-key sequence of a hole-free dict — instead of always allocating a unique stamp. Dicts with identical layouts (instances of the same class built by the same __init__) now carry equal stamps, so a LOAD_ATTR cache entry populated by one instance skips the shadow probe for every instance sharing the layout. Shapes are held in a fixed-size lock-free table keyed by interned key addresses; dicts with holes, non-interned keys, or more than 32 keys fall back to dict-unique stamps. Assisted-by: Claude * Address clippy and review feedback for keys-version stamps - Use BuildHasher::hash_one for shape hashing (clippy manual_hash_one) - Fix set_item_with_hint doc: a refreshed hint is returned on any hint miss, not only when the hinted slot was vacant - Route both holey-dict instances through a single LOAD_ATTR cache site in the snippet test Assisted-by: Claude * Restore try_read_cached_descriptor doc comment to its function The doc block and #[inline] were left attached to store_attr_dict_hinted when it was inserted above try_read_cached_descriptor. Assisted-by: Claude * Specialize LoadAttrWithHint even when the entry index exceeds u16 hint_for_key returns None both for an absent key and for a present key whose entry index does not fit in u16, so very large instance dicts stopped specializing entirely. Use get_item_opt_refresh_hint to decide presence, degrading an unrepresentable hint to 0: the handler then keeps taking its full-probe fallback path. Assisted-by: Claude
Part of RustPython#8245 to reduce the amount of work needed to review. I simplified the embedded nul errors by forwarding to the implementations in `vm::exceptions`.
* Keep StringIO text operations CPython-compatible Constraint: email.feedparser depends on StringIO(newline='') recognizing CR, LF, and CRLF boundaries. Rejected: an email-specific parser workaround | StringIO is the shared root cause. Confidence: high Scope-risk: moderate Directive: Keep internal storage byte-based; convert only at StringIO API boundaries. Tested: prek run --all-files; test_memoryio; test_email; full workspace verification by contributor Assisted-by: Codex:gpt-5.6-sol * Run restored StringIO conformance tests Constraint: upstream StringIO tests already cover the repaired behavior. Rejected: new RustPython-only regression tests | existing CPython coverage is sufficient. Confidence: high Scope-risk: narrow Directive: Remove expected-failure markers only while the inherited tests pass. Tested: prek run --all-files; test_memoryio; test_email Assisted-by: Codex:gpt-5.6-sol * Keep StringIO newline handling consistent Constraint: StringIO must apply its newline mode consistently when constructing, writing, and reading text.\nRejected: a readline-only fix | it leaves CR and CRLF modes internally inconsistent.\nConfidence: high\nScope-risk: moderate\nDirective: Keep StringIO buffer contents valid WTF-8 before using unchecked views.\nTested: prek run --all-files; cargo clippy -p rustpython-vm -- -D warnings; test_memoryio; test_shlex; test_email; workspace excluding rustpython-capi; full workspace manually verified by contributor\nNot-tested: local rustpython-capi workspace test crashes with a pre-existing macOS SIGSEGV\nAssisted-by: Codex:gpt-5.6-sol
…thon#8362) When a Connection is explicitly closed via con.close(), subsequent operations (cursor(), commit(), rollback(), create_function(), etc.) should raise ProgrammingError with 'Cannot operate on a closed database.' to match CPython behaviour. Previously, _db_lock() always returned 'Base Connection.__init__ not called.' when self.db was None, without distinguishing between a connection that was never initialised (subclass before __init__) and one that was initialised and then explicitly closed. Fix: inspect the initialized atomic flag — if True but db is None, the connection was closed; if False, it was never initialised. Assisted-by: GitHub Copilot:claude-sonnet-4-6
…stPython#8364) Row(cursor, data) raised ValueError when cursor.description was None. CPython allows this case and returns an empty key list. - Use empty tuple when description is None instead of raising - Include the key name in the IndexError when a string key is not found Assisted-by: GitHub Copilot:claude-sonnet-4-6
* Fix AST identifier interning Ensure parsed name and function identifier fields use interned strings, matching CPython behavior. Assisted-by: Codex:gpt-5.6-sol * Apply PEP 8 import ordering
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
Assisted-by: Codex:gpt-5.6-sol
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.13 to 1.1.18. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](juliangruber/brace-expansion@v1.1.13...v1.1.18) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.18 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* test_weakref * test_inspect * socket * test_copy * pickle * test_ast * string * zstd * test_py_compile * test_os * mark failing test * mark failing * fix markers
…RustPython#8594) `missing_comma_expression_error` returned its span as `(index, next + 1)`, where `next` is the byte index at which the second atom starts. The `+ 1` is a fixed one-byte width, so the reported `end_offset` matched CPython only when that atom was a single ASCII character: (a b) end 5, CPython 5 (a bb) end 5, CPython 6 (a bbb) end 5, CPython 7 (a β) end 4, CPython 5 `adjacent_atom_end` already measures an atom -- identifiers including non-ASCII ones, string literals and numbers -- and is what the first atom is measured with three lines above. Use it for the second one too. Assisted-by: Claude
* test_memoryio * test_ordered_dict * more * urllib * test_compileall * urllib test * more * test_descr * test_enumerate
…#8556) * lock: add an opt-in rwlock that detaches before it blocks A thread blocked acquiring a lock reaches no safepoint, so stop-the-world cannot stop it, and the lock it waits for is routinely one a thread the requester already suspended is holding. `RawDetachingRwLock` wraps the raw rwlock and hands the wait for a contended acquire to a hook that leaves the interpreter first; an acquire that takes the lock on its first try does not reach the hook. The vm installs the hook during interpreter init and implements it with `allow_threads`. The wait ends with the lock acquired while detached, so re-attaching can park the thread holding it. That is only safe where nothing reachable from a stop-the-world section takes the same lock, so it is opt-in per lock: `PyDetachingRwLock` is a separate type from `PyRwLock`, and `Traverse` is not implemented for it, so a payload holding one cannot derive `Traverse`. `PyByteArray::inner` takes it. `BorrowedValue`/`BorrowedValueMut` gain the matching mapped-guard variants. `a_thread_blocked_on_a_lock_does_not_stall_stop_the_world` blocks an interpreter thread on a `PyDetachingRwLock` and asserts stop-the-world still completes, running the stop on its own thread with a timeout so a stop that never completes fails rather than hangs. Without the hook installed it fails on the 10 s timeout; with it, it passes in 0.07 s. Assisted-by: Claude * lock: stop detaching where the lock may already be held `upgrade` runs with the upgradable lock held, and `lock_shared_recursive` may be the re-entrant take of a lock the calling thread holds; detaching there parks a thread holding the lock, which is what this type documents it must not do. They forward to the wrapped lock instead. `lock_upgradable` starts from holding nothing, but nothing takes an upgradable read of one of these, so it forwards too. `lock_shared` and `lock_exclusive` still detach. Also narrow two claims the comments overstated. Not implementing `Traverse` enforces the opt-in rule only against collections, not against the other things that stop the world. And the requester exemption the hook relies on is wider than `_PyEval_StopTheWorld` gives, so it is a local invariant. Assisted-by: Claude * os.readinto: read aside when the fd can wait `readinto` held the destination's write lock for the whole call, including the `read(2)` inside `allow_threads`. On a pipe, socket or terminal that read returns only when the other end writes, so a thread reaching the same object waited on a lock for an unbounded time, reaching no safepoint while it did. Take the fd that answers without waiting directly, as before, and otherwise read into scratch and take the lock only for the copy. This is what `FileIO.readinto`, `socket.recv_into` and `socket.recvfrom_into` already do; `os.readinto` was the site left over. The EINTR retry moves to `read_into_slice`, unchanged. Assisted-by: Claude * fcntl: detach around the calls that wait Every call in this module ran with the thread attached, so a thread inside one reached no safepoint until it returned. `flock(LOCK_EX)` and `lockf(F_LOCK)` return when whoever holds the lock gives it up, which may be never, and an ioctl on a terminal or socket answers when the device is ready to; the world could not be stopped for that long. `fcntl_fcntl_impl`, `fcntl_ioctl_impl`, `fcntl_flock_impl` and `fcntl_lockf_impl` all release around the call. `ioctl` with `mutate_flag` additionally held the target's write lock for the whole call, so a thread reaching the same object waited on a lock for as long as the device took. Its bytes now go in and come back through a buffer of our own, as `fcntl_ioctl_impl` copies through one of its own for anything up to IOCTL_BUFSZ. The export the argument holds is what keeps the length from changing in between. test_fcntl and test_ioctl pass. Assisted-by: Claude * openssl: read aside instead of holding the caller's buffer locked `SSLSocket.read` wrote straight into the destination buffer, holding the lock that reaching its bytes takes for the whole call. That read returns when the peer writes, which may be never, so a thread touching the same object waited on that lock for as long as the peer took, reaching no safepoint while it did. Read into a buffer of our own and take the destination's lock only for the copy. The rustls backend already reads this way, and `_ssl__SSLSocket_read_impl` works from a `Py_buffer` whose critical section ended before the read. `test_ssl` on this backend fails the same 16 tests before and after. Assisted-by: Claude * vm: add the inverse of allow_threads for callbacks A call that detaches hands the thread to stop-the-world, which counts it as parked. A callback that reaches Python from inside such a call would then run on a thread the requester believes is stopped, and nothing in the vm could stop it: `attach_thread` and `detach_thread` are private to `thread.rs`, and `allow_threads` only goes the one way. `attach_for_callback` attaches for the duration of the closure and returns the thread to where it was, the way `PyGILState_Ensure` and `PyGILState_Release` bracket `_servername_callback`. It tests for "not ATTACHED" rather than for DETACHED, so a thread a stop-the-world has already moved to SUSPENDED routes through `attach_thread` and parks there until the world starts again. `a_callback_inside_a_detached_call_waits_for_the_world` stops the world with a thread detached, then turns that thread loose at a callback and asserts it does not run until the world starts. With the transition disabled it fails. Assisted-by: Claude * openssl: attach around the callbacks that run Python `_servername_callback` and `_msg_callback` reach the interpreter from inside an SSL call -- they take a reference to the Python callback, build arguments and call it. That call is about to detach, and running Python from a detached thread runs it on a thread a stop-the-world requester counts as parked. `_servername_callback` opens with `PyGILState_Ensure()` for the same reason. Both now rejoin the interpreter for the duration of the callback and give the thread back afterwards. The reference to the callback moves inside that section, since taking it is itself an interpreter operation; the check for whether a callback is set at all stays outside, so a socket with none set never reaches the interpreter. No behavior change yet: nothing detaches around these calls, so `attach_for_callback` finds the thread already attached and just runs. Assisted-by: Claude * openssl: detach around the SSL calls that wait On a socket with a timeout the wait lands in `select` -> `sock_wait`, which already detaches. On a blocking socket there is no such return: `SSL_read` blocks in `recv(2)` through `impl Read for &PySocket`, with the thread attached and the connection's write lock held, so the world could not be stopped for as long as the peer stayed silent. `SSL_do_handshake`, `SSL_read_ex`, `SSL_write_ex` and `SSL_shutdown` all run between `Py_BEGIN_ALLOW_THREADS` and `Py_END_ALLOW_THREADS`; these now do too, in both socket and BIO mode as there. The connection lock stays held across the call and so becomes a detaching lock: a thread reaching the same socket gives up its interpreter rather than wait for it attached. `connection` is `#[pytraverse(skip)]`, so a collection does not walk into it and never takes that lock -- which is the rule for opting in, though the skip is what supplies it here rather than the missing `Traverse`. A server that completes a handshake and then says nothing used to deadlock the whole process: the collector suspended the main thread at a safepoint and then waited forever for the reader, so even the test's own timeout could not fire. It now collects in 3 ms. Verified separately that a Python `_msg_callback` still runs from inside the handshake -- 920 invocations across 40 handshakes with a collector looping, five runs clean. test_ssl on this backend fails the same 8 tests before and after, by name, and openssl.rs draws no clippy warning it did not draw before. Assisted-by: Claude * lock: assert the opt-in rule against every stop-the-world section Not implementing `Traverse` for `PyDetachingRwLock` states the rule to a collection: a payload holding one cannot derive `Traverse`, so a collection cannot walk into it. It says nothing to the other sections that stop the world -- fork, traceback dumps, frame enumeration -- and `#[pytraverse(skip)]` steps around it besides, which is how `_SSLSocket.connection` holds one. For those the rule was a comment. `set_world_stopped` records, on the one thread still running inside a stopped world, that it is that thread; `lock_shared` and `lock_exclusive` assert it is not set. A section that took one of these could block on a lock a parked thread holds and only that section can release, which is the deadlock the rule exists to prevent. Debug builds only; release builds track nothing. Nothing in the tree trips it: 180 rounds of collect, `sys._current_frames`, `faulthandler.dump_traceback` and 18 forks with four threads churning bytearrays, plus test_gc/test_bytes/test_threading/test_memoryview/test_buffer on a debug build, all clean. `taking_one_while_stopping_the_world_is_caught` takes one with the flag set and asserts the panic, so the guard is not dead code. Assisted-by: Claude * clippy: drop std_instead_of_core suppressions 1.98 no longer reports `std::io` items for the lint, so the six `expect` attributes for it are unfulfilled. Also drops `from_iter_instead_of_collect` from the workspace lint table, which 1.98 removed. Assisted-by: Claude * lock: spell out why the detaching rwlock exists States the motivation as the three-thread cycle it avoids: how a stop reaches a DETACHED thread but not an ATTACHED one, why a thread blocked on a lock reaches no safepoint, and why the waiter rather than the holder gives way. Puts it on `RawDetachingRwLock`, which is public and so rendered; the module doc is private and keeps only the hook description. Assisted-by: Claude * fcntl: raise the error the call returned The six detached calls discarded the `io::Error` and read `errno` again after `allow_threads`, which re-attaches in between and can park on the way. `lockf` already converts the returned error; these now do too. Assisted-by: Claude * openssl: allocate the read buffer through the vm Without a `buffer` argument `read_len` is whatever non-negative size the caller passed, so `vec![0u8; read_len]` aborts on allocation failure. `new_zeroed_bytes` raises `MemoryError` instead, as the two other reads in this file already do. Assisted-by: Claude * lock: leave no attached blocking acquire on a detaching lock `lock_upgradable` starts from holding nothing, so it detaches like `lock_shared`. A recursive read cannot: it may be the re-entrant take of a lock this thread holds, so detaching there parks a thread holding it, and staying attached stalls stop-the-world. `RawRwLockRecursive` is no longer implemented, which removes `read_recursive` from these locks; nothing took one. The assertion test no longer replaces the panic hook, which is process-wide and was suppressing panic output from whatever else ran beside it. Assisted-by: Claude * vm: wait for the actual block in the stop-the-world test The worker signalled before `read()` and the test slept 50ms, so a stop could complete with nothing blocked on the lock and the test would pass having checked nothing. It now publishes its thread id from inside the interpreter and the test waits for that slot to reach DETACHED, bounded so an acquire that never detaches fails rather than hangs. Assisted-by: Claude
`malachite-bigint` 0.11.0 released while the workspace pinned 0.10.0. `pymath` requires `malachite-bigint = "0"`, so a fresh resolve picks 0.11 for it and leaves the workspace on 0.10, putting two incompatible copies of `BigInt` in one graph. `crates/stdlib/src/math.rs` then fails to compile, which is what the example projects do -- they carry no lockfile and resolve fresh on every run. `malachite-q` and `malachite-base` move with it; bumping `malachite-bigint` alone splits `malachite-nz` the same way. No source change is needed. Assisted-by: Claude
* `typing.py` * tempfile * tarfile * imaplib
Replace `PyType::attributes` (`PyRwLock<PyAttributes>`) with a `TypeNamespace` enum. A type created while an interpreter is running holds a `PyDict`; the types `Context::genesis` and `PyType::new_static` build keep the interned-key `IndexMap`, since hashing a string needs a VM. `type.__new__` binds `__classdictcell__` to the type's own namespace rather than the namespace dict passed to it, so an annotation scope reads attribute changes made after the class body ran. Removes the `expectedFailure` on `test_type_params.TypeParamsClassScopeTest.test_modified_later`. `PyGetSet` holds its class as a `PyRef<PyType>` instead of a non-owning `PointerSlot`, and traverses it, because a namespace can outlive the type it belongs to. `PointerSlot` and the `unsafe` on `Context::new_getset` are gone. `subs_parameters` raises `TypeError` when `__typing_subst__` returns a non-tuple in an unpack position, using the new `PyType::fully_qualified_name` for the message. Removes the `expectedFailure` on `test_typing.GenericTests.test_return_non_tuple_while_unpacking`. Assisted-by: Claude
…es found by differential testing (RustPython#8600) * Fix %-formatting of bytes precision, width and %c errors `format_bytes` subtracted the byte length from the field width without saturating, so `b'%1s' % (b'abcde',)` aborted with a capacity overflow. A precision written as a bare dot now truncates to nothing, and `%r`/`%a` on bytes now honour the precision and width like the other conversions. `%c` with a bytes, bytearray or str argument that is not one unit long now names the argument by its length instead of by its type. Assisted-by: Claude Code:claude-opus-5 * Fix format spec handling of grouping, bool, 'c' and complex A grouping option next to an unrecognised presentation type now reports "Cannot specify ',' with 'a'." instead of falling through to the type check. `format(True, spec)` now validates the spec the way an int does. The 'c' type now pads and aligns its character, and an integer outside the C long range raises OverflowError rather than reporting a code point out of range. A complex formatted with the 'n'/'g' types or with a precision and no type now goes through the general format without forcing a fraction. Assisted-by: Claude Code:claude-opus-5 * Round floats at a negative ndigits from the exact decimal digits Dividing by a power of ten rounded twice: once when the quotient was stored and again at the tie, so e.g. `round(2.5e2, -2)` came out as 300.0. The digits now come from the exact decimal expansion of the value. Assisted-by: Claude Code:claude-opus-5 * Report the requested base in int literal errors and reject a bare sign An invalid literal parsed with base 0 named the base that the prefix turned out to mean rather than 0. A string of nothing but a sign was taken for the old octal form and returned 0 instead of raising. Assisted-by: Claude Code:claude-opus-5 * Split, strip and expand tabs over the Py_UNICODE_ISSPACE set `str.split`, `rsplit`, `strip`, `lstrip` and `rstrip` with no separator used Rust's whitespace predicate, which differs from the 29 code points `Py_UNICODE_ISSPACE` names: U+001C-U+001F and U+0085 were skipped and U+200B was treated as a separator. `expandtabs` went through `str`, so it dropped the lone surrogates of a WTF-8 string; it now works on `Wtf8` throughout. Indexing a str out of range now says "string index out of range". Assisted-by: Claude Code:claude-opus-5 * Read Unicode numeric values as exact rationals `DerivedNumericValues.txt` field 1 carries a rounded decimal, so `unicodedata.numeric('⅐')` and its neighbours came back short of the value CPython reports. The generator now reads the rational in field 3, and takes the 3.2 values from field 8 of `UnicodeData-3.2.0.txt`. Assisted-by: Claude Code:claude-opus-5 * Return a mappingproxy from Pattern.groupindex `pattern_groupindex` wraps the group index in a dict proxy, and hands back a plain dict only when the pattern has no named group. Assisted-by: Claude Code:claude-opus-5 * Fix the utf-16/32/7 codecs' error handling The utf-32 decoder dropped a code point above U+10FFFF instead of calling the error handler, and both the utf-16 and utf-32 handlers were told the encoding was "utf-16"/"utf-32" whichever byte order the data was in. The encoders now take a bytes replacement from the handler as it stands, rejecting one that does not fill whole code units, rather than iterating over it as text; a str replacement from the utf-16 encoder now splits an astral code point into a surrogate pair. The utf-7 decoder names its encoding "utf7", as its codec does. Assisted-by: Claude Code:claude-opus-5 * Rework complex multiplication, division and power Division now divides by whichever part of the divisor is larger, the way `_Py_c_quot` does, and both it and multiplication recover the infinities that the plain formulas turn into nan, following C11 Annex G. An operand that is a real number is now multiplied and divided component-wise, which keeps the sign of a zero and the other part of an infinity; a real numerator has its own quotient so that its zero keeps a sign too. An integer exponent no greater than 100 in magnitude is now reached by repeated multiplication rather than through the polar form, so e.g. `(1+2j) ** 2` is exact. A zero exponent gives 1, and an exponent with no imaginary part no longer reaches for a logarithm that turns an infinite magnitude into a nan. A result that came out infinite raises OverflowError. `complex() arg is a malformed string` is replaced by `could not convert string to complex: ...` when the string carries a misplaced underscore, `complex division by zero` by `division by zero`, and the message for zero raised to a negative or complex power no longer names the base. Assisted-by: Claude Code:claude-opus-5 * Intern the string constants that read as names A code object's string constants were interned by length, so a short string that is not a name was interned and a long name was not. They are now picked out the way `all_name_chars` does. The one-character latin-1 strings are interned as well, so that every route to one of them lands on the same object. Assisted-by: Claude Code:claude-opus-5 * Honour the marshal version and separate its EOF errors Versions below 2 now write a float or complex as its digits under TYPE_FLOAT/TYPE_COMPLEX, versions from 4 on write a tuple of fewer than 256 items under TYPE_SMALL_TUPLE and a long ascii string under TYPE_ASCII, and a string is marked interned only when it is. `marshal.loads` now tells apart the three ways its data can run out: "EOF read where object expected" where an object was to start, "EOF read where not expected" where a single byte was, and "marshal data too short" where a run of bytes was cut short. Assisted-by: Claude Code:claude-opus-5
…p_name` does (RustPython#8608) * memoryview: walk the view and the buffer wrapper for the collector Neither `memoryview` nor the `_buffer_wrapper` a `__buffer__` produces was traversed, so the object a view looks at was never reported and a cycle running through one was classified as reachable: class Src: def __buffer__(self, flags): return memoryview(b'abcdef') o = Src(); o.keep = memoryview(o) # o is never freed Both now report what they hold, as `memory_traverse` and `mbuf_traverse` do. Assisted-by: Claude * Name the type in an unhashable-type message the way `tp_name` does The message took the short name, so a type defined in a module lost the module: `hash(array.array('B', b'a'))` said `unhashable type: 'array'` where the name is `array.array`, and `collections.deque` said `'deque'`. `slot_name()` is what `tp_name` holds, and it leaves a class defined in Python reported by its own name as before. Assisted-by: Claude
…n#8609) `setgroups_if_needed` was compiled out on macOS, so `extra_groups` was accepted and then silently ignored there. nix gates `unistd::setgroups` off `apple_targets`, `redox` and `haiku`; calling `libc::setgroups` and checking the return with `nix::Error::result` is the shape the adjacent `setregid_if_needed` and `setreuid_if_needed` already use, and libc declares the function everywhere except redox. `u32` and `libc::gid_t` have the same layout, so the caller's slice is passed as-is instead of being collected into a fresh `Vec` after fork and before exec. `ngroups` is `c_int` on the BSDs and `size_t` on linux, hence `as _`. Assisted-by: Claude
* Add _interpreters and _interpchannels modules
Apply the patch baseline for the low-level subinterpreter modules:
- crates/vm/src/stdlib/_interpreters.rs: create/destroy/list_all/get_main/
get_current/is_running/exec/call/run_string/run_func/set___main___attrs/
is_shareable/whence and the Interpreter*Error exceptions
- crates/vm/src/stdlib/_interpchannels.rs: channel create/destroy/send/recv/
list_all/list_interpreters/release/close and the Channel*Error exceptions
- crates/vm/src/vm/crossinterp.rs: cross-interpreter data protocol
- crates/vm/src/vm/{interpreter,mod,runtime,vm_new}.rs: interpreter registry
and lifecycle plumbing the modules need
- Lib/concurrent/interpreters/: high-level PEP 734 package
Assisted-by: Claude:claude-opus-5
* Correct _interpreters and _interpchannels against CPython 3.14
Argument matching:
- Add `function::ArgSpec`, a `PyArg_ParseTupleAndKeywords` equivalent that
applies the `|`, `$` and `:` markers of a format string to a `kwlist`, and
route every module function of both modules through it. This enforces
keyword-only parameters, rejects unexpected keywords, accepts keyword forms
that were previously positional-only (`whence(id=)`, `get_config(id=)`,
`set___main___attrs(id=, updates=)`, `capture_exception(exc=)`,
`_register_end_types(send=, recv=)`), and reproduces the arity messages.
- `O!` slots (`shared`, `updates`, `call`'s `args`/`kwargs`) now reject None,
and `_PyArg_BadArgument` renders None as "None" rather than "NoneType".
- `new_config` takes at most one positional `str`; `list_all` of
`_interpchannels` is argument-less.
- Convert arguments in `kwlist` order, so an argument's own error is raised
ahead of the checks that follow it.
_interpreters:
- Add the `CrossInterpreterBufferView` type and build the received memoryview
on it, keeping the sending interpreter's exporter out of the destination.
- Build `excinfo.errdisplay` from `traceback.TracebackException`.
Feature flags:
- `os.fork` checks finalization before `allow_fork`, matching `os_fork_impl`.
- `_thread.start_new_thread` and `start_joinable_thread` reject an interpreter
without `allow_threads`.
Lib/concurrent/interpreters/_crossinterp.py: restore the stripped docstrings
and comments.
Assisted-by: Claude:claude-opus-5
* Correct the stateless checks and error shapes of _interpreters against CPython 3.14
verify_stateless_function follows _PyFunction_VerifyStateless: it rejects
non-dict builtins, a non-empty __defaults__, __kwdefaults__ or __closure__,
and code whose LOAD_GLOBAL names are held by the function's globals or
absent from its builtins.
code_returns_only_none follows _PyCode_ReturnsOnlyNone: generator,
coroutine and async-generator code is rejected up front, the instruction
walk skips inline caches and maps specialized and instrumented opcodes
back, and the LOAD_CONST preceding each RETURN_VALUE is compared against
the index of None in co_consts.
ExcInfo::capture keeps an empty exception message instead of dropping it,
and builds `formatted` as `module.qualname: msg`, leaving out the builtins
and __main__ modules.
_interpreters.call packs the callable through _PyFunction_GetXIData before
pickle and re-raises the stateless-check failure when both fail. A func,
args or kwargs that cannot be rebuilt in the target, and a result without
cross-interpreter data, now raise NotShareableError with the snapshot as
the cause rather than being returned as excinfo.
_interpreters.capture_exception() without an argument returns None.
pickle_loads and _PyFunction_GetXIData attach the underlying failure as
__cause__.
channel_send converts the object to cross-interpreter data after the
channel's `closing` check.
ChannelID rich comparison returns the result of comparing its id with the
other number instead of coercing that result to bool.
PyMemoryView_FromObjectAndFlags raises "memoryview: a bytes-like object is
required, not 'X'" for an object that is not a buffer.
Assisted-by: Claude:claude-opus-5
* Correct cross-interpreter data conversion and channel argument parsing
Bound shareable ints by isize, raising OverflowError("try sending as
bytes") outside that range and keeping it as the NotShareableError cause.
Report an unshareable object by its repr, and attach a failing conversion
as __cause__ of the NotShareableError raised for the object it was called
for, so each level of a nested tuple appears in the chain.
Guard each tuple item conversion with with_recursion("while sharing a
tuple").
Gate the memoryview getdata function on `_interpreters` having been
imported, and route channel_send_buffer through a memoryview so it uses
that function and the resolved fallback.
Create the cross-interpreter exception types from the module_exec of
either module that raises them, instead of only `_interpreters`.
parse_cid raises OverflowError("int too big to convert"); int_arg
reproduces the `i` converter's three overflow messages and is also used
while parsing channel_send arguments.
Add BASETYPE to _queue.Empty, and gate nt.execv/nt.execve on allow_exec.
Assisted-by: Claude:claude-opus-5
* Correct interpreter ID parsing and the channel_send defaults lookup
_PyInterpreterState_ObjectToID accepts any object with __index__, raises
OverflowError("int too big to convert") outside the int64 range, and
reports a negative ID with the repr of the original object.
channelsmod_send reads the channel's default unboundop and fallback only
when one of the two arguments is negative, so an explicit pair is
validated before the channel is looked up.
Assisted-by: Claude:claude-opus-5
* Add the _interpqueues module and concurrent.interpreters._queues
_interpqueues holds a process-wide queue table and exposes create,
destroy, list_all, put, get, bind, release, get_maxsize,
get_queue_defaults, is_full, get_count and _register_heap_types, raising
QueueError and QueueNotFoundError. QueueEmpty and QueueFull, which
subclass queue.Empty and queue.Full, are registered per interpreter by
concurrent.interpreters._queues.
Cross-interpreter data carries a queue as a refcounted queue ID that
binds when the data is captured and releases when it is dropped, and
is_shareable reports a registered Queue as shareable.
Queue and channel items owned by an interpreter are now cleared from
Interpreter::finalize, after module finalization, rather than from
destroy_owned_interpreter, so values sent by an atexit callback also
become unbound.
is_shareable gates memoryview on the same registration flag as the
getdata lookup.
Assisted-by: Claude:claude-opus-5
* Add the high-level concurrent.interpreters API
Interpreter, ExecutionFailed and the queue aliases, copied verbatim from
CPython 3.14.
Assisted-by: Claude:claude-opus-5
* Destroy leftover subinterpreters when the main interpreter finalizes
Interpreter::finalize now runs finalize_subinterpreters between the atexit
handlers and the finalizing flag: when the main interpreter still owns
subinterpreters it emits the RuntimeWarning "remaining subinterpreters;
close them with Interpreter.close()" and then finalizes each of them.
runtime::owned_interpreter_ids lists the runtime-owned interpreters.
Assisted-by: Claude:claude-opus-5
* Absolutize the script path in run_file
_Py_abspath and _PyPathConfig_ComputeSysPath0 arrive as abs_path and
script_sys_path0. run_file hands the absolutized path to get_importer
and to the runner, so __file__ and co_filename no longer stay relative,
and inserts the symlink-resolved script directory as sys.path[0]
instead of the argument's parent.
test_cmd_line_script.test_script_abspath no longer expects a failure.
Assisted-by: Claude:claude-opus-5
* Name types in error messages the way tp_name does
The object, number, sequence and mapping protocols, the
unsupported-operand helpers, and the type-specific messages fed from
them now read PyType::slot_name() where they read PyType::name(),
so a type declared with a module keeps it.
Message texts corrected along the way:
- PySequence_Size, PyMapping_Size, PySequence_GetItem and
PySequence_SetItem/DelItem split their merged wording in two, and
PyObject_SetItem/DelItem say "object".
- PyObject_GenericSetAttr reports a read-only attribute and the
missing __dict__.
- check_class drops the type name it appended.
- __int__ and __index__ drop the class prefix; __float__ keeps it.
- float(), complex(), round(), next(), str subscripting and list
subscripting take their own wording.
- str.join and bytes.join number the item they turn down and name what
they wanted, and report a non-iterable as "can only join an
iterable".
test_class.testObjectAttributeAccessErrorMessages,
string_tests.test_subscript and the test_descrtut doctest no longer
expect a failure.
Assisted-by: Claude:claude-opus-5
* Gate script_code's source compilation on the compiler feature
crossinterp::script_code called vm.compile unconditionally, which only
exists with the rustpython-compiler feature. Builds without it, such as
example_projects/barebone, now raise a TypeError instead of failing to
compile.
Assisted-by: Claude:claude-opus-5
* Skip test_pyinit_function_raises_exception
The test imports _testsinglephase in its body without a decorator
guarding it, and now that _interpreters exists requires_subinterpreters
no longer skips it.
Assisted-by: Claude:claude-opus-5
Alias criterion to codspeed-criterion-compat so the existing benchmark suites can be measured by CodSpeed with CPU simulation on every pull request, and add the workflow running them. Co-authored-by: CodSpeed Bot <[email protected]>
) * 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
* Implement PEP 667 frame locals semantics Assisted-by: Codex:GPT-5 * Fix C API test CString import Assisted-by: Codex:GPT-5 * Address PEP 667 review findings Assisted-by: Codex:GPT-5
Assisted-by: Codex:gpt-5.6-sol
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
…ite (RustPython#8621) * Leave a byte that starts no UTF-8 sequence at the end of a console write `find_last_utf8_boundary` read every byte at or above 0xc0 as the start of a sequence, giving 0xf8..=0xff an expected length of 4 and cutting the tail back to before it. `_find_last_utf8_boundary` stops the four-byte range at 0xf8 and returns the full length for anything above it, leaving the byte for MultiByteToWideChar to answer with U+FFFD. `write_console_utf8` therefore reported fewer bytes written than it was given whenever one of those bytes fell in the last three, and for a buffer that began with one it cut the length to 0, converted nothing and returned Ok(0) -- a write that never advances. The scan also ran a fourth iteration the C loop does not; the arms happen to answer the same there, but the bound is now the same 3. * Drop the expectedFailure on test_winconsoleio's test_write The marker went stale with the boundary fix in the parent commit. The test writes `b'\xff'*10` and `data + b'\xff'` to CONOUT$ and asserts the returned count, which is the case `find_last_utf8_boundary` was answering wrong, so regrtest now reports it as an unexpected success.
…c__` (RustPython#8614) * Strip the signature prefix from builtin __doc__ A function's signature and its documentation share one string, separated by `\n--\n\n`, as in CPython. __text_signature__ read the front of it, but __doc__ returned the whole thing: >>> len.__doc__ 'len(obj, /)\n--\n\nReturn the number of items in a container.' # was 'Return the number of items in a container.' # now 44 of the 46 builtin functions leaked the prefix. get_doc_from_internal_doc already stripped it but was reachable only from PyType.__doc__. Split it the way CPython does, into doc_without_signature (_PyType_DocWithoutSignature) and a wrapper mapping an empty result to None (_PyType_GetDocFromInternalDoc), and call it from PyNativeFunction and PyMethodDescriptor as well. Assisted-by: Claude Code:claude-opus-5 * Terminate a generated __text_signature__ that carries no documentation A signature and its documentation share one string, separated by `\n--\n\n`, and readers find the signature by searching for that marker. A #[pyfunction] with no doc comment stored the bare signature, which no reader could parse: >>> time.clock_getres.__doc__ 'clock_getres(clk_id, /)' >>> time.clock_getres.__text_signature__ None Emit the marker in that case too, as Argument Clinic does for an undocumented function. 136 of the 915 functions reachable from the importable modules were affected. __doc__ now reports None for them, matching CPython, because nothing follows the marker. Assisted-by: Claude Code:claude-opus-5 * Report a __text_signature__ for every method A #[pymethod] built its doc only when the method carried a doc comment, so one without a comment dropped the generated signature too: >>> list.append.__text_signature__ None Build it from either part, as #[pyfunction] does. All 1101 method descriptors reachable from the builtin types now report a signature, up from 1, and 713 of the 1030 that CPython also describes match it exactly. A method that takes its receiver as an ordinary first argument instead of `&self` reported that argument by name, so binding the method could not drop it: >>> inspect.signature([].__dir__) (obj, /) # was () # now Mark it $self, or $type for a classmethod, as CPython does. 310 methods were affected. test_unbound_builtin_method_noargs and test_bound_builtin_method_noargs pass now. Assisted-by: Claude Code:claude-opus-5 * Drop the expectedFailure on test_simple_completion rlcompleter closes the parenthesis for a callable whose signature takes no parameters, and leaves it open when the signature cannot be read: def _callable_postfix(self, val, word): if callable(val): word += "(" try: if not inspect.signature(val).parameters: word += ")" except ValueError: pass os.getpid reports `()` since the generated signature carries the `--` terminator, so completing `os.getpid` yields `os.getpid()` and the test passes. Assisted-by: Claude Code:claude-opus-5 * Name every #[pymethod] receiver zelf A method that needs an owned handle cannot take `&self`, so it takes the receiver as its first argument and names it `zelf`, `self` being a Rust keyword. 24 of the 163 such methods named it something else: `instance` (11), `obj` (4), `exc` (3), `_self` (3), `_instance` (2). Rename them. The generated signature is unaffected either way, since the macro marks the receiver by position rather than by name. Assisted-by: Claude Code:claude-opus-5 * Drop the unreachable zelf name check in func_sig The receiver is now marked by position, so the name it carries no longer matters, and no path reaches this check with an argument named zelf: - a #[pymethod] or #[pyclassmethod] without a `&self` receiver has its first argument replaced by the marker before the name is read - a `&self` receiver is handled as syn::FnArg::Receiver - a #[pymethod(raw)] is passed to static_raw_func, whose PyNativeFn bound fixes its signature to (&VirtualMachine, FuncArgs) - no #[pystaticmethod] or #[pyfunction] names an argument zelf - #[pymember], #[pygetset] and #[pyslot] never reach func_sig Every generated signature is byte for byte identical without it. Assisted-by: Claude Code:claude-opus-5 * Keep the argument bundle when marking the receiver A #[pyclassmethod] that takes FuncArgs receives the class inside the bundle along with every other argument, but the marker was spent on the bundle and the arguments went unreported: >>> object.__subclasshook__.__text_signature__ '($type, /)' # was, claims it takes nothing '($type, *args, **kwargs)' # now Report both, the shape CPython uses for __new__. Two methods take their arguments this way, __subclasshook__ on object and on type. Assisted-by: Claude Code:claude-opus-5
* common: add PyPy CJK codec engines
Assisted-by: Codex:GPT-5
* common: mark CJK mapping tables generated
Assisted-by: Codex:GPT-5
* common: add reusable byte and text engines
Assisted-by: Codex:GPT-5
* common: record opt-in engine dependencies
Assisted-by: Codex:GPT-5
* stdlib: add `_multibytecodec` and the six `_codecs_*` modules
Wire the CJK codec engines in `rustpython-common` up to the VM. `_multibytecodec`
holds `MultibyteCodec`, `MultibyteIncrementalEncoder`, `MultibyteIncrementalDecoder`,
`MultibyteStreamReader` and `MultibyteStreamWriter`, the encode/decode drivers and
the error-handler protocol; each `_codecs_*` module exposes `getcodec` for the
encodings of its region.
`rustpython-stdlib` now enables the `cjk-codecs` feature of `rustpython-common`.
Add `cjk::decode_reset`, which the decoder `reset()` methods need. It differs from
building a fresh decoder state: ISO 2022 keeps its G1..G3 designations and the
escape-throughout flag, and resets only G0 and the shifted flag.
Drop the markers from the CJK codec tests and from the `test_codecs`, `test_io`,
`test_email`, `test_zipfile` and `test_traceback` cases that needed a CJK codec.
`test_codecs.BasicUnicodeTest.{test_basics,test_decoder_state}` stay expected
failures for an unrelated reason: `codecs.charmap_encode` rejects a `None` mapping.
Assisted-by: Claude Code:claude-opus-5
* stdlib: use the byte and text engines from `rustpython-common`
`binascii`, `_json` and `_socket`'s address converters now call the engines in
`rustpython-common` instead of carrying their own copies, and `rustpython-stdlib`
enables the `binascii`, `inet` and `json` features for them. That leaves
`base64` and `crc32fast` with no caller in `rustpython-stdlib`, and
`crates/stdlib/src/json/machinery.rs`, the last of the json_in_type-derived code,
goes away with its last caller.
The address converters change behaviour. `inet_aton` reads the lenient forms
(`127.1`, `16909060`, `0x7f.1`, `0177.0.0.1`), `inet_pton` rejects an octet with a
redundant leading zero and a group of five hex digits, `inet_ntop` writes the
IPv4-compatible form as `::c000:201`, and an address family with no converter
raises `EAFNOSUPPORT` instead of a bare `OSError`.
Give the JSON engine the closing-quote scan and the allocation-free `\uXXXX`
escape it needs to carry `_json`'s workload. Against the code it replaces,
`json.dumps` runs 2.3x faster with `ensure_ascii=True` and 1.7x without it, and
`json.loads` 1.2x.
`binascii.a2b_base64` now reports the messages `binascii.c` does: no
`error decoding base64: ` prefix, and the data-character count in parentheses.
Assisted-by: Claude Code:claude-opus-5
* test: enable the Windows cp932 codec case
The shared CJK engine now satisfies the Windows code-page adapter, so keeping this test marked as an expected failure turns its success into a CI failure.
Assisted-by: Codex:GPT-5
Assisted-by: Codex:GPT-5
Assisted-by: Codex:gpt-5.6-sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One of the checkboxes below must be checked before this draft is submitted upstream.
Summary
Expand Miri coverage around the unsafe allocation and reference-counting paths used by Python object lifecycles. The new focused fixtures exercise:
The test helpers build the minimum bootstrap type hierarchy needed to reach these paths and are kept inside the existing
core.rstest module. This PR does not change runtime behavior; it adds executable safety coverage for invariants that were previously exercised mainly through the full interpreter.Testing
Validation after rebasing onto the current
upstream/main:cargo fmt --all -- --checkcargo check --locked -p rustpython-vm --testscargo test --locked -p rustpython-vm -- miri_test(6 passed)MIRIFLAGS='-Zmiri-ignore-leaks -Zmiri-permissive-provenance' cargo +nightly miri test -p rustpython-vm miri_test(6 passed in 220.63s)AI assistance
Codex (
gpt-5.6-sol) assisted extensively with selecting unsafe lifecycle invariants, implementing the fixtures, adapting them to the current bootstrap type hierarchy, running automated and Miri validation, reviewing the diff, and drafting this PR description. Before submission upstream, the contributor must personally review the final diff and complete the hands-on verification required by the project AI policy.