mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
* Add no_memory_error for allocation failures Assisted-by: Claude Code:claude-fable-5-1 * Use no_memory_error at allocation failure sites Assisted-by: Claude Code:claude-fable-5-1 * Raise MemoryError instead of aborting on sequence repeat allocation failure Vec::with_capacity and Vec::reserve in SequenceExt::mul and SequenceMutExt::imul call handle_alloc_error on failure, which aborts the process under panic = "abort". `[1, 2, 3] * 10**17` passes the MAX_MEMORY_SIZE guard (2.4e18 bytes < isize::MAX) and aborted at the allocation instead of raising MemoryError. Use try_reserve_exact and map failures to vm.no_memory_error(). This also removes the n != 2 special case in imul: with the reserve done unconditionally, the copy loop runs zero times for n == 2 and the final append uses the reserved capacity. Assisted-by: Claude Code:claude-fable-5-1 * Recycle MemoryError instances through a dedicated payload freelist no_memory_error allocated the exception object on every raise, so under real memory exhaustion the raise itself could abort. Keep dead MemoryError husks in a process-wide reserve and revive them on the next raise without touching the allocator. The freelist is scoped to MemoryError at the type level. PyMemoryError already exists as a transparent wrapper payload, but instances never used it: the default exception slot_new delegates to the base class, so everything was constructed as PyBaseException. Route no_memory_error through a PyMemoryError-typed construction instead; the freelist hooks live on PyMemoryError's own PyPayload impl, the dealloc glue is monomorphized per payload type, and the existing exact-type push gate admits exactly MemoryError husks. The #[pyexception] macro always generates the PyPayload impl, which left no way to attach freelist configuration; a payload = "manual" attribute now skips that generation so a type can write its own impl, mirroring the generated PAYLOAD_TYPE_ID chain and class-based downcast validation that keep the payload flavors interchangeable. Exceptions are the first HAS_DICT freelist type, so their husks carry an ObjExt prefix that PyInner::dealloc would normally drop; clear its instance dict and member slots before pushing so no stale attributes survive into a revived instance. The reserve is a process-wide Mutex rather than a thread_local so a single prewarm at interpreter init covers every thread; MemoryError is a cold path, so the lock is uncontended. Python-level MemoryError() still constructs a PyBaseException payload through the delegated slot_new and simply does not participate, matching CPython, where only the PyErr_NoMemory path uses the preallocated reserve. Assisted-by: Claude Code:claude-fable-5-1 * Clear MemoryError husks before they enter the freelist Assisted-by: Claude Code:claude-fable-5-1 * Use no_memory_error for the bz2 allocation failure The shared bz2 engine maps BZ_MEM_ERROR to Bz2Error::Mem; report it through no_memory_error like the other allocation-failure sites, matching PyErr_NoMemory in CPython's _bz2module. Assisted-by: Claude Code:claude-fable-5-1 * Change MemoryError husks Vec to a fixed-size array Assisted-by: Claude Code:claude-fable-5-1 * Increase MemoryError freelist size from 4 to 16 * Address review: keep repeat and husk clearing allocation-safe Assisted-by: Claude Code:claude-fable-5-1
Test snippets
This directory contains two sets of test snippets which can be run in Python.
The snippets/ directory contains functional tests, and the benchmarks/
directory contains snippets for use in benchmarking RustPython's performance.
Setup
Our testing depends on pytest, which you can install using pip.
Running
Simply run pytest -v in this directory, and the tests should run (and hopefully
pass). If it hangs for a long time, that's because it's building RustPython in
release mode, which should take less time than it would to run every test
snippet with RustPython compiled in debug mode.