Commit Graph

277 Commits

Author SHA1 Message Date
Jeong, YunWon
1385c4e472 rename ssl-vendor (#7989) 2026-05-28 09:37:44 +03:00
Joshua Megnauth
7d54ba502e Fix ssl-vendor (OpenSSL) (#7985)
Closes: #7893

Fix 1:
`foreign-types-shared` needs to match `openssl`'s version. Bumping it is
a SemVer violation because the latest versions of the crate aren't
backwards compatible with older versions.
See: rust-openssl/rust-openssl#2461

Fix 2:
The second fix is to align the `openssl` module with the latest
`host_env` and `ssl` changes.
2026-05-28 12:16:08 +09:00
Joshua Megnauth
30ae48b24b Support custom rustls providers (#7657)
* Modularize rustls as work towards providers

`rustls`'s architecture is very clean and trait-driven. There are many
providers for `rustls` including the built-in `aws-lc-rs` and `ring` as
well as backends for `boringssl`, `graviola`, `openssl`, `mbedtls`, etc.

This commit removes the hard dependency on `aws-lc-rs` and adds support
for `ring`. It works towards #7059 as well.

* Clean up rustls features

* Remove ring as an explicit feature
* ssl-rustls is the default and implies aws-lc

* Support custom rustls crypto providers

The new feature, `ssl-rustls-no-provider`, enables custom rustls
providers. By default, `aws-lc-rs` is enabled which matches the old
behavior and keeps backward compatibility.

I wrote a new type that abstracts what we need from crypto providers.
CryptoExt encapsulates the ticketer as well as cipher suites and KX
groups. I wrote fallbacks to help select a reasonable default if a
provider is missing features (they all seem to support the same things
though).

I also wrote an example to show how to actually use custom providers.

* Fix duplicate VERIFY_X509 constants and unused imports in ssl module

Remove duplicate VERIFY_X509_STRICT/VERIFY_X509_PARTIAL_CHAIN definitions
from compat.rs (already defined in _ssl module with #[pyattr]).
Remove unused imports: ClientConnection, ServerConnection.

* no-provider as default

* Fix CI failures: openssl build, wasm target, cargo-shear

- Update openssl.rs to use renamed sock_wait/SockWaitKind and add vm parameter
- Add skip_ssl for wasm32-wasip2 target (aws-lc-sys cannot build for wasm)
- Remove unused workspace dependency aws-lc-rs
- Fix foreign-types-shared version to match openssl's dependency (0.1)
- Restore Cargo.lock from upstream/main

---------

Co-authored-by: Jeong, YunWon <jeong@youknowone.org>
2026-05-28 11:07:09 +09:00
Jeong, YunWon
1a959cf7f3 Align codegen passes and opcode metadata with CPython (#7987)
* Share marshal ref table between code object and its internals

read_marshal_bytes, _str, _str_vec, _name_tuple, and _const_tuple now
take a shared ref table and resolve TYPE_REF / register FLAG_REF
entries. deserialize_code is split into a public wrapper and an inner
function that receives the ref table; deserialize_value_depth opens a
fresh inner ref space when it hits Type::Code, mirroring CPython's
behaviour of putting the code object itself at ref slot 0. Nested code
objects inside const tuples reuse the surrounding code's ref space via
the new read_const_value helper.

* Align PYC magic number, FORMAT_VERSION, and header check with CPython 3.14

PYC_MAGIC_NUMBER changes from 2994 to 3627, matching CPython 3.14's
pyc_magic_number_token (0x0a0d0e2b). marshal FORMAT_VERSION drops from
5 to 4 (the encoder/marshal.version value; the decoder already accepts
both). check_pyc_magic_number_bytes now compares all four magic bytes
instead of the first two.

* Accept CPython-tagged .pyc as read-only bytecode source

SourceFileLoader.get_code now also looks for .pyc files using
_RP_FALLBACK_CACHE_TAGS (currently ('cpython-314',)) in addition to
sys.implementation.cache_tag. The matched .pyc is only used for
reading; recompilation still writes to the RustPython-tagged path, so
CPython's .pyc is never overwritten. Source-stat / hash / timestamp
validation logic is unchanged.

* Apply rustfmt to marshal helpers

* Marshal PySlice from format version 4 instead of 5

CPython's marshal supports TYPE_SLICE from format version 4 onwards
and that is the default version. Rejecting slice dumps below version
5 made marshal.dumps(slice(...)) fail with the default version and
broke test.test_marshal.SliceTestCase.test_slice.

* Revert "Accept CPython-tagged .pyc as read-only bytecode source"

Lib/importlib/_bootstrap_external.py is CPython's own code copied
verbatim; local patches here defeat compatibility tracking. The
cpython-XX cache_tag fallback needs to live on the RustPython side
(Rust code or sys.implementation.cache_tag policy), not as edits to
the imported standard library.

This reverts commit 1fc426d0fb5fcdb50d35cad13bbb43e8f6ce1c7f.

* Set marshal FORMAT_VERSION to 5 to match CPython 3.14.5

Py_MARSHAL_VERSION is 5 in CPython 3.14.5 (Include/marshal.h:16) and
TYPE_SLICE serialization rejects version < 5 (Python/marshal.c:720).
Restore the same threshold and constant so marshal.version and the
slice-marshal gate match CPython.

* Thread marshal recursion depth through nested code objects

Code objects embedded in const-tuples reset the depth budget on each
recursion, so a hostile or pathological marshal stream of code-in-tuple-
in-code can blow the stack despite MAX_MARSHAL_STACK_DEPTH. Pass the
current depth through deserialize_code_inner and read_marshal_const_tuple
and decrement at each code-object/tuple boundary.

Also route dict keys through deserialize_value_after_header so TYPE_CODE
keys decode instead of failing with BadType.

* align compiler to CPython

* Align codegen with CPython compile.c

Rename CFG helpers and accessors to the names used in CPython's
compile.c (basicblock_next_instr, basicblock_last_instr,
basicblock_append_instructions, bb_has_fallthrough, is_jump,
make_cfg_traversal_stack, mark_warm/mark_cold, etc.). Drop the unused
boolop-folding gate, mark_cpython_cfg_label_block helper, and
ComprehensionLoopControl::iter_range field.

Track an is_coroutine flag on SymbolTable, set in async def, await, and
async comprehensions, and propagate it through non-generator
comprehensions per symtable_handle_comprehension().

Mark SetupCleanup/SetupFinally/SetupWith as has_arg pseudo-ops, mark
ForIter as a terminator, and add has_arg/has_const on AnyInstruction.
Fix Instruction::stack_effect_jump to delegate to the opcode's
stack_effect_jump rather than stack_effect.

* Align codegen IR with CPython CFG structures

* Match CPython CFG annotation offset arithmetic

* Propagate CPython CFG label translation errors

* Align CPython exception target labeling flow

* Propagate CPython CFG traversal stack allocation errors

* Match CPython optimize_load_fast allocation flow

* Propagate CPython basicblock allocation errors

* Propagate CPython redundant NOP cleanup errors

* Propagate CPython unused const cleanup errors

* Propagate CPython const folding errors

* Propagate CPython swaptimize allocation errors

* Match CPython list-to-tuple fold allocation

* Skip const folding on CPython allocation failures

* Skip subscript folding on CPython allocation failures

* Propagate CPython assembler allocation errors

* Propagate CPython localsplus allocation errors

* Propagate CPython localsplus setup allocation errors

* Propagate CPython jump label map allocation errors

* Propagate CPython instruction sequence allocation errors

* Propagate CPython instruction label allocation errors

* Guard CPython codegen block allocation

* Guard CPython label shadow allocation

* Propagate CPython label shadow allocation errors

* Align CPython c-array allocation updates

* Align CPython ref stack growth

* Match CPython CFG builder debug check

* Avoid Rust-only CFG append clone

* Reuse CPython cleared block slots

* Use CPython block append in copy_basicblock

* Match CPython cfg builder creation order

* Clear label map after CPython apply pass

* Match CPython cfg builder allocation check

* Propagate CPython c-array size errors

* Drop Rust-only label uniqueness check

* Drop Rust-only label shadow debug checks

* Propagate CFG block index overflow

* Propagate CFG label oparg overflow

* Model CPython basicblock instruction storage

* Drop Rust-only recorded CFG precheck

* Model CPython instruction sequence storage

* Propagate instruction sequence offset overflow

* Model CPython instruction sequence labels

* Match CPython jump offset arithmetic

* Match CPython exception table arithmetic

* Match CPython label index arithmetic

* Match CPython instruction offset casts

* Match CPython jump offset indexing

* Match CPython oparg locals casts

* Match CPython localsplus offset arithmetic

* Match CPython cell prefix indexing

* Match CPython C array growth arithmetic

* Match CPython label map allocation arithmetic

* Match CPython label map size tracking

* Use CPython label map size in sequence passes

* Assert CPython label map clearing invariants

* Match CPython label oparg assignment

* Match CPython compiler direct arithmetic

* Match CPython load fast local casts

* Match CPython load fast depth assert

* Match CPython resume depth flagging

* Match CPython stack depth arithmetic

* Drop Rust-only stack overflow error

* Return CPython stack depth directly

* Match CPython C array growth errors

* Match CPython instruction insert asserts

* Match CPython unreachable pseudo jump

* Match CPython CFG size guard

* Match CPython superinstruction assert

* Match CPython redundant jump assert

* Match CPython stackdepth errors

* Match CPython jump offset flow

* Match CPython assembler buffer defaults

* Match CPython bytecode emit growth

* Match CPython assembler entry growth

* Match CPython assembler growth overflow check

* Match CPython remove_unreachable structure

* Match CPython static swap flow

* Inline CPython code unit preprocessing

* Match CPython C array growth checks

* Match CPython label map size guard

* Match CPython load-fast flow

* Simplify CPython CFG condition flow

* Align exception fallthrough propagation

* Match CPython pseudo target table

* Match CPython annotations CFG assert

* Match CPython inverted op assert

* Match CPython many-locals guard

* Reject deopt opcodes in CFG stack effects

* Match CPython invalid stack effect error

* Test CPython deopt stack effect guard

* Match CPython load-fast extended-arg assert

* Match CPython instruction allocation asserts

* Match CPython basicblock last-instr asserts

* Match CPython opcode range asserts

* Assert CPython fallthrough line propagation invariant

* Assert CPython CFG target offset sign

* Assert CPython exception fallthrough invariant

* Assert CPython exception stack bounds

* Assert CPython traversal stack allocation

* Match CPython label-map allocation in shadow

* Mirror CPython label-map sentinel fill

* Match CPython CFG builder allocation asserts

* Match CPython exception stack structure

* Match CPython ref stack structure

* Match CPython CFG traversal stack structure

* Mirror CPython CFG traversal stack pointer

* Use CPython fixed exception handler stack

* Mirror CPython ref stack capacity field

* Match CPython swap optimizer scratch stack

* Align static swap helpers with CPython blocks

* Align swaptimize signature with CPython

* Match CPython redundant pair pass result

* Match CPython inline pass result

* Match CPython redundant NOP pass results

* Fix bytecode metadata after upstream rebase

* Match CPython opcode stack metadata

* Add CPython identifiers to cspell dictionary

Add CNOTAB, LNOTAB, ialloc, ioffset, iused, nblocks, ncellsused,
ncellvars, nextop, noffsets, nvars, swaptimize, untargeted to
.cspell.dict/cpython.txt for the new CFG/assembler code in
crates/codegen/src/ir.rs.

* Fix CI failures from bytecode-parity work

- clippy: drop redundant `test_` prefix on three test functions and
  remove an unnecessary `u32` cast in basicblock_clear_reuses_cpython_spare_slots_in_offset_order
- insta: regenerate nested_double_async_with snapshot to match the new
  CFG output that drops unreferenced labels after the redundant-NOP pass
- regrtest: drop `@expectedFailure` markers from test_func_args,
  test_meth_args (test_compile), test_disassemble_with,
  test_disassemble_try_finally (test_dis), and test_except_star
  (test_monitoring) which now pass

* Resync generated opcode metadata

Empty conf.toml since WithExceptStart and Setup{Cleanup,Finally,With}
stack effects already match CPython, so the TODO override entries are
stale and only cause CI hook diffs.

Regenerate opcode_metadata.rs and drop the matching SetupCleanup/
SetupFinally/SetupWith assertions on PseudoOpcode::has_arg(); their
`HAS_ARG` flag comes from pseudo definitions in bytecodes.c that the
upstream analyzer does not propagate through PseudoInstruction.properties,
so the generated has_arg() excludes them. has_target() still covers
these block-push pseudos via is_block_push().

* Drop is_block_push has_arg invariant

The CPython invariant `assert(OPCODE_HAS_ARG(op) || !IS_BLOCK_PUSH(op))`
relies on SETUP_{FINALLY,CLEANUP,WITH} carrying `HAS_ARG_FLAG` in
CPython's metadata. The autogen tool reads pseudo-opcode properties from
target instructions and does not propagate the pseudo's own
HAS_ARG flag, so PseudoOpcode::has_arg() omits these three opcodes.
Drop the debug_assert that fired inside py_freeze proc-macro expansion.

* Auto-generate has_eval_break and route AnyInstruction has_arg/has_const via macro

Add fn_has_eval_break to generate_rs_opcode_metadata.py using CPython's
Properties.eval_breaker, removing the hand-written matches! body for
Opcode::has_eval_break and PseudoOpcode::has_eval_break.

Forward has_arg/has_const from Instruction and PseudoInstruction to
their opcode, so AnyInstruction can use either_real_pseudo! like the
other has_* accessors instead of an open-coded match.
2026-05-28 09:19:11 +09:00
Chanho Lee
1cb24c5ebb Reject non-ASCII digits in JSON numbers (#7982) 2026-05-27 16:40:52 +09:00
Jiseok CHOI
ce79cd4853 sqlite3: fix Blob.__setitem__ value range validation (#7981)
* sqlite3: fix Blob.__setitem__ value range validation

Previously, assigning an out-of-range integer (negative or > 255) or an
integer too large for i64 (e.g. 2**65) to a Blob index raised OverflowError
instead of ValueError.

Mirror CPython's ass_subscript_index logic:
- Convert the value via to_i64(), treating any overflow as -1
- Validate the result is in [0, 255], raising ValueError("byte must be in range(0, 256)") otherwise
- Separate deletion error messages: "item deletion" for index, "slice deletion" for slice

* sqlite3: fix Blob.__setitem__ negative-step slice write

In the step != 1 branch of Blob.ass_subscript, the loop used
  i_in_temp += step as usize
where step is isize. For negative steps (e.g. step = -2),
  (-2isize) as usize = 18446744073709551614
causing an out-of-bounds panic whenever slice_len >= 2.

Fix: use SaturatedSliceIter (already used by the read path) to iterate
over the correct absolute blob indices, then map each index back to a
temp buffer offset via abs_idx - range_start.

Also fix a Clippy lint: replace
  val < 0 || val > 255
with the idiomatic
  !(0..=255).contains(&val)

Add a regression test in extra_tests/snippets/stdlib_sqlite.py that
exercises blob[9:0:-2] (negative step, slice_len=5).

* fix: guard blob negative-step snippet from CPython 3.11 bug

* style: add blank line after import sys in stdlib_sqlite snippet (ruff)

* Update extra_tests/snippets/stdlib_sqlite.py

---------

Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
2026-05-27 16:40:05 +09:00
Shahar Naveh
9701c46d86 Clippy rules (test related) (#7968) 2026-05-25 22:03:18 +09:00
Shahar Naveh
b69644196a Upgrade pkcs8 to 0.11 (#7977) 2026-05-25 21:20:13 +09:00
fanninpm
bc3d00e879 Replace ahash with rapidhash (#7954)
* Add `rapidhash` to list of dependencies

* Use `rapidhash::quality::RandomState` in `codegen` crate

* Use `rapidhash::quality::RandomState` in `stdlib` crate

* Use `rapidhash::quality::RandomState` in `vm` crate

* Remove `ahash` from lists of dependencies
2026-05-25 13:53:54 +09:00
Shahar Naveh
2fabf38d8f Impl sys.audithook (#7960)
* Update tests

* Add basic audit support

* Add audit for `time.sleep`

* Add some for `socket`

* Some syslog

* Some sys related audits

* some marshal

* monitoring callback

* Mark failing tests

* clippy

* Clippy

* clippy

* mark failing test

* mark more

* Update `test_sys_setprofile.py` to 3.14.5

* Mark failing tests
2026-05-24 19:56:35 +09:00
Jeong, YunWon
d3272e752b Align codegen metadata with CPython (#7952) 2026-05-23 20:16:03 +09:00
Ivan Mironov
cf3b6397b2 Fix panic in select.select() when too many FDs specified (#7948)
Also:

* Add regression test into existing `extra_tests/snippets/stdlib_select.py`
* Stop calculating nfds on Windows as it is ignored there

Panic:

	thread 'main' (189598) panicked at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.186/src/unix/linux_like/mod.rs:1777:9:
	index out of bounds: the len is 16 but the index is 16
	note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

	thread 'main' (189598) panicked at library/core/src/panicking.rs:225:5:
	panic in a function that cannot unwind
	stack backtrace:
	   0:     0xaaab763a0b88 - <<std[1934960bf7f41d0a]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[f1abae5f1257fe69]::fmt::Display>::fmt
	   1:     0xaaab75590ff0 - core[f1abae5f1257fe69]::fmt::write
	   2:     0xaaab763a94fc - <std[1934960bf7f41d0a]::sys::stdio::unix::Stderr as std[1934960bf7f41d0a]::io::Write>::write_fmt
	   3:     0xaaab7638b714 - std[1934960bf7f41d0a]::panicking::default_hook::{closure#0}
	   4:     0xaaab7639b288 - std[1934960bf7f41d0a]::panicking::default_hook
	   5:     0xaaab7639b478 - std[1934960bf7f41d0a]::panicking::panic_with_hook
	   6:     0xaaab7638b7ec - std[1934960bf7f41d0a]::panicking::panic_handler::{closure#0}
	   7:     0xaaab76382654 - std[1934960bf7f41d0a]::sys::backtrace::__rust_end_short_backtrace::<std[1934960bf7f41d0a]::panicking::panic_handler::{closure#0}, !>
	   8:     0xaaab7638c504 - __rustc[b7425922bef61dcf]::rust_begin_unwind
	   9:     0xaaab754f778c - core[f1abae5f1257fe69]::panicking::panic_nounwind_fmt
	  10:     0xaaab754f7714 - core[f1abae5f1257fe69]::panicking::panic_nounwind
	  11:     0xaaab754f786c - core[f1abae5f1257fe69]::panicking::panic_cannot_unwind
	  12:     0xaaab75a6283c - rustpython_vm::function::builtin::<impl rustpython_vm::function::builtin::sealed::PyNativeFnInternal<(rustpython_vm::function::builtin::OwnedParam<T1>,rustpython_vm::function::builtin::OwnedParam<T2>,rustpython_vm::function::builtin::OwnedParam<T3>,rustpython_vm::function::builtin::OwnedParam<T4>),R,rustpython_vm::vm::VirtualMachine> for F>::call_::h2471c8e242c9b51d
	  13:     0xaaab75db1e68 - rustpython_vm::types::slot::Callable::slot_call::hd1c1ad0ad14f306b
	  14:     0xaaab762c0a50 - rustpython_vm::protocol::callable::PyCallable::invoke::h9f6d571fca351ca6
	  15:     0xaaab75c550e8 - rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args::hed1f4a61aba2dced
	  16:     0xaaab762e7c24 - rustpython_vm::frame::ExecutingFrame::execute_call::h0ad3490dd74ed1e3
	  17:     0xaaab762fed40 - rustpython_vm::frame::ExecutingFrame::run::hcf90f0950fc26812
	  18:     0xaaab761e6768 - rustpython_vm::vm::VirtualMachine::with_frame::hd49ba6fcdf2422e2
	  19:     0xaaab75c45398 - rustpython_vm::builtins::function::<impl rustpython_vm::object::core::Py<rustpython_vm::builtins::function::PyFunction>>::invoke_with_locals::h42de3d2316941ce2
	  20:     0xaaab76132a80 - rustpython_vm::builtins::function::vectorcall_function::h7331cb67b334e867
	  21:     0xaaab763369d8 - rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::vectorcall::h9019c5d16685c89a
	  22:     0xaaab762f4b54 - rustpython_vm::frame::ExecutingFrame::execute_call_vectorcall::h120134e11a58c946
	  23:     0xaaab76302a7c - rustpython_vm::frame::ExecutingFrame::run::hcf90f0950fc26812
	  24:     0xaaab761e6768 - rustpython_vm::vm::VirtualMachine::with_frame::hd49ba6fcdf2422e2
	  25:     0xaaab761e7f24 - rustpython_vm::vm::VirtualMachine::run_code_obj::h354618be6e5cc553
	  26:     0xaaab761e2d18 - rustpython_vm::vm::python_run::file_run::<impl rustpython_vm::vm::VirtualMachine>::run_any_file::h783d3127fbc0b523
	  27:     0xaaab757d700c - rustpython::run_rustpython::h354efb8d817cefbf
	  28:     0xaaab757c79e0 - std::thread::local::LocalKey<T>::with::hc9728e249843a926
	  29:     0xaaab757db860 - rustpython_vm::vm::interpreter::Interpreter::run::h42ac1fe9ed2287a2
	  30:     0xaaab757d7b30 - rustpython::run::hf14a209db5b4289c
	  31:     0xaaab757e2eb4 - rustpython::main::h1b59d8e13276ac48
	  32:     0xaaab757e2eec - std::sys::backtrace::__rust_begin_short_backtrace::h47e4b1f073f2155c
	  33:     0xaaab757e2ed4 - std::rt::lang_start::{{closure}}::h663a6c3dc7d80101
	  34:     0xaaab76399fd4 - std[1934960bf7f41d0a]::rt::lang_start_internal
	  35:     0xaaab757e2f44 - main
	  36:     0xfffed057655c - __libc_start_call_main
	  37:     0xfffed057663c - __libc_start_main@@GLIBC_2.34
	  38:     0xaaab755526f0 - _start
	  39:                0x0 - <unknown>
	thread caused non-unwinding panic. aborting.
	Aborted                    (core dumped) cargo run --release -- extra_tests/snippets/stdlib_select.py
2026-05-22 20:10:24 +09:00
Ivan Mironov
2a163609cd Rustls integration improvements (#7946)
* Do not call `import socket` on each send()/recv() when using rustls

Use method references cached during socket creation.

* Implement reading of at most one TLS record from socket

Previous algorithm didn't take into account that recv() may return less
data than requested even for blocking sockets.

* Remove special handling of rustls "buffer full" errors

First of all, existing code does not really work and this leads to an
infinite loop: https://github.com/RustPython/RustPython/issues/7891

Second, this should never happen when rustls used properly (wrt
wants_read() and wants_write()) and thus all such errors are
implementation bugs that must be properly fixed.

* Replace own TlsConnection with rustls::Connection

* Fix waiting on a socket

1) Ensure that socket_wait() called from TLS glue code allows threads
2) Ensure that socket_wait() called from TLS glue code properly handles
   EINTR on *nix
3) Ensure that select() or poll() error conditions are checked
4) Use poll() on *nix so socket descriptor values are not limited

* Remove dead code from rustls glue

* Do not present rustls errors as OSError(0, "Success")

* Remove infinite loop "detection" from rustls glue

TLS handshake cannot be infinite. Any infinite loop here is a serious
bug in implementation and should be fixed properly.

This code triggers in some cases (very short reads) with misleading
`ssl_error.SSLWantReadError: The operation did not complete (read)`.

* Add test for 1-byte max recv in TLS client

* Add regression test for https://github.com/RustPython/RustPython/issues/7891

* Fix constants in rustls glue code

* Deduplicate verify flags / record-size constants
* Larger "max encrypted TLS record length"
2026-05-22 20:04:15 +09:00
Jeong, YunWon
ae3804fb21 more hostenv isolation (#7886)
* Convert host_env Windows path/argv params from raw *const u16 to &WideCStr

* Migrate remaining winapi raw u16 pointer signatures to typed references

* Migrate winreg pub unsafe fn string parameters to typed references

* Add ToPyException impls for host_env error types (PyPy wrap_oserror analog)

* Add CheckLibcResult helper and apply to socket/fcntl/shm/posix_wasi

* Add Win32 BOOL/HANDLE check helpers; apply check helpers across host_env

* Apply Win32/libc check helpers to overlapped/testconsole/os.rs

* Apply Win32 check helpers to winapi.rs (partial)

* Apply Win32 check helpers across more winapi.rs functions

* Apply Win32 check helpers to nt.rs (partial)

* Add CheckWin32Sentinel helper; apply to nt.rs INVALID_HANDLE_VALUE/INVALID_FILE_ATTRIBUTES patterns

* Add OwnedHandle / HandleToOwned helper; apply to mmap create_named_mapping leak path

* Use OwnedHandle RAII in nt::pipe to eliminate manual cleanup on error path

* Use OwnedHandle in nt::chmod_follow; hoist HandleToOwned import

* Drop rustix dependency from vm crate

Remove unused IntoPyException impl for rustix::io::Errno and the
rustix entry in crates/vm/Cargo.toml. rustix is now only depended on
by host_env.

* Fix CI failures: cross-platform regressions

- winapi.rs: pass None to create_event_w; the recent Option<&WideCStr>
  migration left one call site still passing a raw null pointer.
- exceptions.rs: gate ToPyException for LockfError with
  cfg(any(unix, target_os = "wasi")), matching host_env::fcntl's own
  cfg. The previous cfg let it compile on wasm32-unknown-unknown where
  host_env::fcntl does not exist.
- io_unsupported.rs: derive Eq on FileMode alongside PartialEq to
  satisfy clippy::derive_partial_eq_without_eq.

* Fix CI failures: cfg gates and unused imports

- exceptions.rs: gate ToPyException for LockfError with
  cfg(all(unix, not(target_os = "redox"))) to match the type's own
  cfg in host_env/src/fcntl.rs (LockfError is not built on wasi).
- signal.rs: CheckLibcResult is only used in unix-gated functions;
  split import so it is not pulled in for windows.
- mmap.rs: remove CheckWin32Handle from imports; no longer used after
  switching to HandleToOwned-based RAII.
- overlapped.rs: remove INVALID_HANDLE_VALUE from connect_pipe import;
  the call now uses .check_valid().

* Fix CI failures: rustfmt and windows unused import

- signal.rs: reorder cfg-gated imports per rustfmt.
- socket.rs: gate ToPyException import to cfg(all(unix, not(target_os = "redox")));
  it is only used inside sendmsg which has the same gate, so it was unused
  on windows.

* Push remaining libc/extern callsites from vm into host_env

Add host_env wrappers and replace the corresponding vm call sites:
- host_env::errno::strerror_string for libc::strerror
- host_env::io::write_stderr_raw for libc::write(STDERR_FILENO,...)
- host_env::locale::localeconv_data reused from vm::format
- host_env::os::abort for the inline abort extern
- host_env::os::urandom wraps getrandom; getrandom moves from vm to host_env
- host_env::posix::lchmod for the macOS/BSD lchmod extern
- host_env::posix::fcopyfile for the macOS fcopyfile extern
- host_env::nt::wputenv for the Windows _wputenv extern

vm/format.rs's get_locale_info now uses host_env on both unix and windows
instead of the unix-only libc::localeconv path.

* Move time tz state and winsound FFI into host_env

- host_env::time::tz: wraps the libc tzset/timezone/daylight/tzname
  globals on non-msvc, non-wasm32 targets. vm::stdlib::time now reads
  these via the typed wrappers instead of declaring its own externs.
- host_env::winsound (windows): exposes PlaySoundW (via a typed
  PlaySoundSource enum), Beep, and MessageBeep. vm::stdlib::winsound
  drops its inline FFI block and routes through host_env.

* Migrate unsetenv to host_env::nt::wputenv; rustfmt

- vm::stdlib::os::unsetenv had a second _wputenv call site that still
  referenced the removed inline extern. Route it through
  host_env::nt::wputenv like putenv.
- rustfmt fixups in exceptions.rs (boolean chain layout) and the two
  winsound files.

* Address PR review comments

- host_env::winapi::create_process: assert that the command_line buffer
  is NUL-terminated and that the env block ends with a double-NUL,
  matching the Win32 CreateProcessW contract.
- stdlib::overlapped CreateEvent: replace WideCString::from_str_truncate
  with the fallible from_str(), so embedded NULs in the event name
  surface as ValueError instead of being silently truncated.
- vm::exceptions::ReadlinkError::NotSymbolicLink now maps to OSError
  (matches Win32 ERROR_NOT_A_REPARSE_POINT semantics) rather than
  ValueError.
- winreg::ConnectRegistry: route the non-zero return through the
  existing os_error_from_windows_code helper so the resulting exception
  carries the real winerror/message instead of a generic OSError.

* Fix CI failures and address review follow-ups

CI failures:
- rustfmt cleanup in exceptions.rs after the ReadlinkError change.
- vm/stdlib/os.rs: drop unused ToWideString import that the wputenv
  migration left behind.
- vm/stdlib/winsound.rs: replace explicit `&*buf` with `&buf` to
  satisfy clippy::explicit_auto_deref.
- Lib/test/test_format.py, Lib/test/test_types.py: drop the now-stale
  expectedFailureIfWindows decorators on the locale-format tests; the
  Windows path now reads real `localeconv` data via host_env so these
  tests pass.

Review follow-ups:
- host_env::winapi::create_process: switch the new buffer terminator
  checks from `assert!` to fallible validators returning
  `io::ErrorKind::InvalidInput`, so bad inputs stay recoverable at
  the API boundary.
- host_env::winsound::play_sound: reject `Memory(_)` together with
  `SND_ASYNC` (lifetime-unsafe) and `SND_MEMORY` without a
  `Memory(_)` source. Expand `PlaySoundError` into a variant enum.
- vm::stdlib::_winapi::CreateProcess: route the Win32 path/argv strings
  through `as_wtf8().to_wide_cstring()` like the rest of the Windows
  API surface; `expect_str()` could panic on Python strings containing
  lone surrogates.
2026-05-20 16:16:14 +09:00
Jeong, YunWon
d8dee81157 Bytecode parity (#7885)
* Align codegen CFG cleanup with CPython

* Align codegen bytecode with CPython 3.14

* Remove remove_jump_target_line_nops

The pass dropped a same-line NOP at the start of any jump-targeted block
whose body advanced to a later line, but CPython's basicblock_remove_redundant_nops
only consults the previous/next instruction inside the same block. The
extra pass deleted the else-body line trace anchor exercised by
test_nested_double_async_with.

Ignore test_conditional_break_finally_does_not_keep_break_cleanup_nop;
the break NOP lands in a separate block from the inlined finally body,
so same-line successor elision does not apply here.

* Keep except_handler blocks reachable in eliminate_unreachable_blocks

After convert_pseudo_ops lowers SETUP_FINALLY to a plain NOP, the only
remaining link from the try body to the except_handler block was the
per-instruction except_handler annotation. When earlier passes had
already removed every NOP that carried that annotation (e.g. an empty
try body with `pass`), the handler block became unreachable from the
entry block and its instructions were cleared, dropping the handler
entirely. Seed reachability with blocks already marked except_handler
so handler dispatch survives independent of the in-block annotation.

Also drop two expectedFailure markers in test_patma whose match-tracing
expectations now pass.

* Align bytecode CFG cleanup with CPython
2026-05-19 19:24:45 +09:00
Joshua Megnauth
ab72b292bd rustix/windows-sys for page size; drop maplit (#7876)
The `page_size` crate is a simple libc wrapper for Unix and uses
`winapi-rs` for Windows. `windows-sys` and `windows-rs` are the modern
alternatives for the unmaintained `winapi-rs`. Both crates are
maintained by Microsoft - they're official. Getting the page size is a
simple call for both Unix and Windows.

Besides Unix and Windows, I also added the page size for wasm32 which
the `page_size` crate did not support. `wasm32`'s page size is a
constant that is defined by the spec, so I hard coded it without adding
additional dependencies.

Finally, I dropped `maplit` which is seven years old and only used in
one place. Calling `collect()` with a single item iterator is idiomatic
as well as better in this case because Rust can optimize it. `maplit`
called `HashMap::insert` which over allocates to amortize future allocs.
2026-05-17 20:47:13 +09:00
Shahar Naveh
54589bf255 Remove some #[allow(clippy::*)] (#7878)
* Remove some `#[allow(clippy::*)]`

* Fix after merge
2026-05-17 01:56:47 +09:00
Jeong, YunWon
8253253fc7 more hostenv impl (#7604) 2026-05-16 19:01:40 +09:00
Shahar Naveh
ddfcb25957 Clippy nursery lints (#7875) 2026-05-15 10:38:36 +00:00
Shahar Naveh
460b1d39ed Clippy warn uninlined_format_args & redundant_else (#7873) 2026-05-15 16:52:06 +09:00
Shahar Naveh
e8711edd2d Clippy warn on unnecessary wraps (#7869) 2026-05-13 23:14:33 +09:00
Shahar Naveh
e36cd994e8 Update socket.py to 3.14.5 (#7866)
* Update `socket.py` to 3.14.5

* Don't have socket.sethostname on android

* Mark failing test
2026-05-13 19:52:56 +09:00
Shahar Naveh
5ef91c22de Unchecked code fixes (#7786)
* Clippy lints fixes

* Update crates/stdlib/src/tkinter.rs

Co-authored-by: fanninpm <27117322+fanninpm@users.noreply.github.com>

* Update crates/stdlib/src/openssl.rs

Co-authored-by: fanninpm <27117322+fanninpm@users.noreply.github.com>

* Update crates/stdlib/src/ssl/compat.rs

Co-authored-by: fanninpm <27117322+fanninpm@users.noreply.github.com>

* Update crates/stdlib/src/ssl/compat.rs

Co-authored-by: fanninpm <27117322+fanninpm@users.noreply.github.com>

* Revert "Update crates/stdlib/src/ssl/compat.rs"

This reverts commit b34228ff37.

---------

Co-authored-by: fanninpm <27117322+fanninpm@users.noreply.github.com>
2026-05-13 09:45:15 +09:00
Shahar Naveh
9d77b25858 Add more pedantic clippy rules (#7830) 2026-05-12 20:57:37 +09:00
Shahar Naveh
d09179a7ee Code nits in contextvars.rs (#7835) 2026-05-12 20:57:07 +09:00
Shahar Naveh
d6abdc4b79 Fixed: unused manifest key: dependencies.libz-sys.package (#7792)
* Fixed: `unused manifest key: dependencies.libz-sys.package`

* Use `libz-rs-sys` instead

* Adjust rust code
2026-05-12 00:54:44 +09:00
Shahar Naveh
fc00fc22d2 Code nits in csv.rs (#7834)
* Code nits in `csv.rs`

* clippy
2026-05-12 00:53:58 +09:00
Shahar Naveh
4a46e84eb9 Add map_unwrap_or clippy rule (#7829) 2026-05-12 00:51:23 +09:00
Shahar Naveh
8f19dff19d Update test_resource.py to 3.14.4 (#7808)
* Update `test_resource.py` to 3.14.4

* Mark failing tests

* Skip crashing macos test
2026-05-12 00:38:16 +09:00
Shahar Naveh
320355f633 Autogen instructions & opcodes (#7797) 2026-05-10 22:13:38 +09:00
Shahar Naveh
67630ffbfe Update test_context.py to 3.14.4 (#7804)
* Update `test_context.py` to 3.14.4

* Mark & fix some tests

* clippy
2026-05-10 00:28:30 +09:00
Shahar Naveh
02a2b19839 Remove unused rust impl for formatting dis output (#7660)
* Remove unused rust impl for formatting dis output

* remove `examples/dis.rs`

* Added tests

* Update lock

* Try to set snapshot dir

* Remove verbose flag

* Regenerate snapshots after #7711

* Revert "Bump insta from 1.46.3 to 1.47.2 (#7706)"

This reverts commit e6d9ea6bfe.

* Debug info

* Show diff as well

* Show debug faster

* CI: true env

* Recert CI

* Add `CI: true` in ci emv

* Reapply "Bump insta from 1.46.3 to 1.47.2 (#7706)"

This reverts commit 693ca8cbe4d7885a81162a9be31e8bb567db885a.

* simplify macro

* trim on function side

* Force insta workspace root

* fix merge
2026-05-05 08:09:35 +00:00
Noa
02932384d6 Use cfg_select in a bunch more places (#7740) 2026-05-04 20:26:16 +00:00
Shahar Naveh
ee5e9d0001 Enable some pedantic clippy lints (#7764) 2026-05-04 10:25:37 +09:00
Shahar Naveh
eb99a8ecf1 Warn on unreachable_pub (#7762) 2026-05-04 10:18:24 +09:00
fanninpm
acc167fc44 Move dependencies to workspace for stdlib crate (#7747)
* Add dependencies from `stdlib` crate to workspace

* Declare dependencies as workspace = true
2026-05-04 09:50:21 +09:00
Shahar Naveh
926d69b50a Add some clippy lints (#7755) 2026-05-02 11:14:23 +09:00
Shahar Naveh
3f718f9942 Align _opcode.has_* functions with 3.14.4 (#7711)
* Align `_opcode.has*` with 3.14.4

* Unmark passing tests

* Unmark more passing tests
2026-04-28 12:15:23 +00:00
dependabot[bot]
59382f385a Bump rustls from 0.23.38 to 0.23.39 (#7705)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.38 to 0.23.39.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.38...v/0.23.39)

---
updated-dependencies:
- dependency-name: rustls
  dependency-version: 0.23.39
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-28 21:11:00 +09:00
Shahar Naveh
5648a3346f Upgrade der to 0.8 (#7695)
* Update `der` to 0.8, move to workspace dependencies

* Add `pem` feature
2026-04-27 21:37:42 +09:00
dependabot[bot]
049d44b1e0 Bump uuid from 1.23.0 to 1.23.1 (#7692)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.23.0 to 1.23.1.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.23.0...v1.23.1)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.23.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-27 12:37:57 +09:00
dependabot[bot]
f6b6b18b62 Bump foreign-types-shared from 0.1.1 to 0.3.1 (#7691)
Bumps [foreign-types-shared](https://github.com/sfackler/foreign-types) from 0.1.1 to 0.3.1.
- [Release notes](https://github.com/sfackler/foreign-types/releases)
- [Commits](https://github.com/sfackler/foreign-types/commits/foreign-types-shared-v0.3.1)

---
updated-dependencies:
- dependency-name: foreign-types-shared
  dependency-version: 0.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-27 12:37:48 +09:00
Changjoon
625e5bf012 Report invalid \uXXXX escape position at the u character (#7676)
* Report invalid \uXXXX escape position at the u character

CPython's json decoder reports the position of the `u` specifier
when a \uXXXX escape fails to parse, but RustPython was reporting
the preceding `\`. For surrogate-pair cases (\uXXXX\uYYYY) the
second call was passing char_offset + next_char_i + 1, which
lands on the first hex digit of the first escape -- unrelated to
the actual failure site.

Pass next_char_i (position of the primary `u`) to the primary
decode_unicode call, and capture the second `u`'s char index from
the next_tuple peek to pass to the surrogate-pair decode_unicode
call.

Verified: 13 targeted probes across invalid-hex, short, and pair
cases now all match CPython positions. test.test_json 214 tests
pass with no regressions.

* Add regression test for invalid \uXXXX escape position

* Use raise AssertionError instead of assert False (B011)
2026-04-26 12:33:34 +09:00
dependabot[bot]
956267c49e Bump aws-lc-rs from 1.16.2 to 1.16.3 (#7684)
Bumps [aws-lc-rs](https://github.com/aws/aws-lc-rs) from 1.16.2 to 1.16.3.
- [Release notes](https://github.com/aws/aws-lc-rs/releases)
- [Commits](https://github.com/aws/aws-lc-rs/compare/v1.16.2...v1.16.3)

---
updated-dependencies:
- dependency-name: aws-lc-rs
  dependency-version: 1.16.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-26 12:32:10 +09:00
Changjoon
be43bb6dbf Match CPython error type for non-ASCII struct format arguments (#7681)
* Match CPython error type for non-ASCII struct format arguments

Struct() raised the wrong exception type when the format argument
contained non-ASCII characters:
- str input with non-ASCII char: RustPython raised UnicodeDecodeError
  with an empty message; CPython raises UnicodeEncodeError as if
  format.encode('ascii') had been called directly.
- bytes input with non-ASCII byte: same wrong UnicodeDecodeError;
  CPython passes the bytes through to the format parser, which then
  errors with struct.error("bad char in struct format").

Restructure IntoStructFormatBytes::try_from_object to:
- raise UnicodeEncodeError("ascii", s, start, start+1, "ordinal not
  in range(128)") for non-ASCII str, with start computed as the
  first non-ASCII code point position (matching CPython's natural
  encoding-error format);
- raise struct.error("bad char in struct format") for non-ASCII bytes,
  produced via the existing new_struct_error helper.

Probed byte-identical with CPython 3.14.4 for both cases. Full
test.test_struct (43 tests) passes with no regressions. Sanity-tested
all standard format/pack/unpack/calcsize call shapes remain unchanged.

* Add regression test for non-ASCII format string error types

* Use raise AssertionError instead of assert False (B011)
2026-04-26 12:31:54 +09:00
Shahar Naveh
f0e23aacc2 Opcode descs not hardcoded (#7682)
* Add `::iterator()` for oparg enums

* adjust `get_special_method_names`

* Adjust intristic function 2 oparg

* Adjust intristic function 1 oparg

* Adjust binary operator oparg
2026-04-25 21:57:02 +09:00
dependabot[bot]
7df0801db3 Bump rustls from 0.23.37 to 0.23.38 (#7678)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.37 to 0.23.38.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.37...v/0.23.38)

---
updated-dependencies:
- dependency-name: rustls
  dependency-version: 0.23.38
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-25 21:53:11 +09:00
Changjoon
fb1218d6ba Accept surrogates in _json.JsonScanner decode path (#7675)
The _json decoder had two failure modes when a Python str value would
contain a lone surrogate (legal per the Python 3 str model):

1. Boundary UnicodeEncodeError: JsonScanner::Callable::call rejected
   any input str with surrogates via try_into_utf8 before scanning
   began.
2. Silent U+FFFD corruption: call_scan_once and parse_object's key
   path called .to_string() on scanstring's Wtf8Buf output, which
   routes through Wtf8::Display (lossy). Array values and dict keys
   decoded from JSON \uXXXX escapes silently became U+FFFD.

Switch JsonScanner's five PyUtf8StrRef signatures to PyStrRef, drop
the entry-point try_into_utf8 call, and feed Wtf8Buf directly to
new_str instead of going through .to_string(). Key memoization now
uses HashMap<Wtf8Buf, PyStrRef> so surrogate-bearing keys survive
interning. parse_number takes &[u8] since JSON numbers are ASCII.

Extends the WTF-8 refactor pattern established in #7673 to the
decoder. machinery::scanstring already returns Wtf8Buf and is
unchanged.

Unmasks test_single_surrogate_decode. 214 tests in test.test_json
pass with no regressions. Decoder output verified byte-identical to
CPython 3.13.4 over 10,000 random fuzz cases (JSON docs containing
random surrogate escapes at root/list/dict positions, compared via
json.dumps(..., ensure_ascii=True, sort_keys=True)).
2026-04-25 05:16:12 +09:00
Changjoon
2e5c2be7fa Accept surrogates in _json.encode_basestring{,_ascii} (#7673)
encode_basestring/encode_basestring_ascii took PyUtf8StrRef, so
json.dumps(str_with_lone_surrogate) raised UnicodeEncodeError at the
Python/Rust boundary before write_json_string ran. CPython's encoder
emits \uXXXX under ensure_ascii=True and passes raw WTF-8 otherwise.

Switch to PyStrRef + s.as_wtf8(), matching scanstring in the same file.
Rewrite write_json_string to accept &Wtf8 and iterate
code_point_indices, emitting \uXXXX for surrogates in ascii mode and
passing their bytes through otherwise. Stop escaping 0x7F in the
ensure_ascii=False path (matches py_encode_basestring). Return Wtf8Buf
via the checked from_bytes so invariant breaks panic instead of UB.

Fuzzing also exposed two pre-existing ESCAPE_CHARS typos: 0x0B was
"\u000" and 0x1B was "\u001" (both missing trailing 'b'). Fixed here.

Verified byte-identical with CPython 3.13.4 over 16 manual + 10,000
random fuzz cases. Full test.test_json: 214 tests, 0 failures, 0
unexpected successes. Unmasks test_ascii_non_printable_encode and
test_single_surrogate_encode. Decoder path is a follow-up.
2026-04-25 00:08:14 +09:00
Roman
7bb2fb0755 Fix pyclass memory layout to prevent silent UB in inherited getter dispatch (#7663) 2026-04-24 23:30:23 +09:00