CPython raises ProgrammingError with "Binding N is a named parameter"
when a query uses named placeholders (, , ) but
the caller passes a sequence instead of a mapping.
RustPython's bind_parameters_sequence() did not check whether each
binding slot is a named parameter, so no error was raised.
Fix: call sqlite3_bind_parameter_name() for each slot in
bind_parameters_sequence(). If the first byte of the returned name is
not '?' (i.e. it is a named placeholder), raise ProgrammingError before
attempting to bind.
Assisted-by: GitHub Copilot:claude-sonnet-4-6
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
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
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
* 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
Part of #8245 to reduce the amount of work needed to review. I
simplified the embedded nul errors by forwarding to the implementations
in `vm::exceptions`.
* 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
* 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
* 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
* 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: 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
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")]'.
* Store StopIteration.value in a struct field like SystemExit
* Unmark test_pydoc's test_member_descriptor
* Use expect instead of unwrap in new_stop_iteration
Dual-Root Mismatch in RustPython:
Unlike CPython, RustPython manages the standard library
in two separate locations in the workspace:
- Workspace 'Lib/': Contains the test suite ('Lib/test/...') and
standard library files.
- crates/pylib/Lib/: A helper directory used for packaging.
'crates/pylib/Lib' is a symlink pointing to the real workspace '../../Lib/'.
Runtime Directory Discrepancy:
- 'sys.path' contains 'crates/pylib/Lib' (the path to the symlink).
- Thus, when 'test.support' is imported, its '__file__' is resolved relative
to the symlink: '/opt/RustPython/crates/pylib/Lib/test/support/__init__.py'.
- Consequently, 'STDLIB_DIR' resolves to the symlink root:
'/opt/RustPython/crates/pylib/Lib'.
- However, since 'test_program.py' is executed directly from the workspace,
its 'pkg_dir' is: '/opt/RustPython/Lib/test/test_unittest/testmock'.
- When 'os.path.relpath(pkg_dir, top_dir)' is called:
---
os.path.relpath(
"/opt/RustPython/Lib/test/test_unittest/testmock",
"/opt/RustPython/crates/pylib/Lib"
)
# Result: "../../../Lib/test/test_unittest/testmock"
---
Because the relative path starts with '..', the assertion fails.
Assisted-by: Gemini
* weakref: reuse callback-less proxy objects like CPython
Make PyWeakProxy a #[repr(transparent)] view sharing PyWeak's payload
(manual PyPayload with shared PAYLOAD_TYPE_ID + class check), so a proxy
is the weakref-list node itself and add()'s reuse applies to it
directly. Port CPython's insert_weakref/get_basic_refs slot discipline:
genericref at head, generic proxy right after it, with callback/class
re-verificationbefore reuse. Constructor moves to a slot_new override
since reuse returns an existing object. Drops the hidden __weakproxy
marker subclass and the proxy branch in PyWeakref_GetRef.
Assisted-by: Claude Code:claude-opus-4-8
* weakref: always validate class in basic-proxy slot lookup
* weakref: rustfmt find_generic_proxy_ptr
* weakref: unify reuse check shape
stdlib/_symtable.rs previously provided the public Symbol API. When
7f1fc3602 imported Lib/symtable.py, responsibility for the public Symbol
implementation moved to Python, but the native implementation remained.
Remove it along with the unused identifiers getter.
Assisted-by: Codex:gpt-5.6-sol
When `ordered_attributes` is set, report attributes to the
StartElementHandler as a flat `[name, value, ...]` list matching
CPython's my_StartElementHandler, instead of always passing a dict.
`xml.etree.ElementTree` and `xml.dom.minidom` (via expatbuilder) both
enable this flag and rely on the flat-list contract, so parsing any
element with attributes previously failed: the dict made `_start` do
integer indexing, raising KeyError which was silently swallowed by the
handler invoker, leaving the tree empty ("missing toplevel element").
Reference (CPython 3.14):
https://github.com/python/cpython/blob/3.14/Modules/pyexpat.c#L439-L443
Remove `@unittest.expectedFailure` from tests that now pass
(23 in test_xml_etree, 15 in test_minidom, 1 in test_regrtest).
Assisted-by: Claude:claude-opus-4-8
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* vm: replace fetch_update() with try_update()
fetch_update() in atomic namespace is in the deprecation stage.
* host_env: replace libc::RLIM_NLIMITS with const RLIM_NLIMITS
Define a new const, RLIM_NLIMITS instead of libc::RLIM_NLIMITS in order
to clean up the deprecated const and to prevent breaking API where the const
is still used.
* Validate TextIOWrapper chunk size
Match CPython for non-positive, oversized, and index-compatible values.
Assisted-by: Codex:GPT-5
* Use explicit AssertionError in io snippet
Assisted-by: Codex:GPT-5
* Report original type in chunk size overflow
Assisted-by: Codex:GPT-5
* Chain io snippet assertion error
Assisted-by: Codex:GPT-5
* Match CPython chunk size setter behavior
Assisted-by: Codex:GPT-5
* Format io snippet assertion
Assisted-by: Codex:GPT-5.6
* Match CPython type name truncation
Assisted-by: Codex:GPT-5
* Format non-ASCII chunk size test
Assisted-by: Codex:GPT-5
Casting a u32 to a u16 would truncate the value. It's safer to use
Wtf::encode_wide which already handles converting a CodePoint to a u16.
I removed vec_into_bytes because it was only used in two places. The
function was unsound for T but sound in the way RustPython used it (POD
to POD, less strict alignment).
AI disclosure: I linted this code with AI. I found this issue by
accident while working on another patch in which I introduced a similar
mistake. AI caught that mistake, so I linted the original code to
cross-check it. I wrote the code myself in both instances.
Assisted-by: Codex:gpt-5.4