_socket, sys, posix, mapdict, faulthandler, _warnings: parity fixes - #871
Conversation
…ribute `socket_idna_converter` looked up `encode` on the host object and called it; `idna_converter` reaches the codec through `space.encode_unicode_object` (`objspace.py encode_unicode_object` -> `unicodeobject.py encode_object`). The non-ASCII branch now calls `type_methods::encode_object` with the `idna` codec, which is the same helper `str.encode`, `bytes(str, ...)` and `bytearray(str, ...)` share. Encoding returns the bytes directly, so the non-bytes result check and the `take_call_error` fallback are gone. Assisted-by: Claude
`bytecode_only_trace` and `run_trace_func` returned early when `self.space` was null. `space` is the interpreter owner rather than a tracing flag, and it is PY_NULL in this runtime, so the test suppressed every line event while call and return events still reached `_trace`. Both hooks now guard only on a null frame. Assisted-by: Claude
`handler.py:174` declares `@unwrap_spec(all_threads=int, chain=int)` with defaults `all_threads=1, chain=0`, so both arguments go through `gateway_int_w` and a non-integer raises. The module took them by truthiness instead. `enable` also takes `file=None, all_threads=True` per `handler.py:141-145`. Assisted-by: Claude
`load_attr_slowpath` and `store_attr_slowpath` held the immortal-node cache entry's code-owned lock across the slow path. That extends the lock over arbitrary descriptor calls, unlike the single GIL critical section upstream, and admits instance-A -> code -> instance-B / instance-B -> code lock cycles. The entry is copied under the lock, which is then released before the instance is touched. Assisted-by: Claude
…xtras `stat_result`, `uname_result`, `statvfs_result` and `times_result` carry `__name__` "stat_result" with repr "os.stat_result". Following `app_posix.py:20-37`, slots 7..10 hold the hidden integer timestamps and the float `st_atime`/`st_mtime`/`st_ctime` are named-only extras that are never indexable; `app_posix.py:38-69` orders the remaining named-only extras. Assisted-by: Claude
`_warnings.warn`'s `source` is positional-or-keyword; only `skip_file_prefixes` is keyword-only. The `#[kwonly]` marker moved off `source`. pyre-macros: a signature carrying kw-only or **kwargs markers is bound by the gateway before the wrapper runs, so the scope it hands over holds every positional and kw-only slot and `args.len()` is no longer the caller's positional count. `BuiltinCode.funcrun_obj` likewise runs the activation straight after `args.parse_obj(...)`, so the wrapper's positional-count preamble is skipped for those signatures. Assisted-by: Claude
WalkthroughThe PR adds functional standard input, propagates launcher flags into ChangesRuntime state and standard I/O
Process and platform APIs
Free-threaded attribute caches
Interpreter execution guards
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Launcher
participant RuntimeFlags
participant SysModule
participant StandardStreams
participant BuiltinsInput
Launcher->>RuntimeFlags: parse -X, -W, -u, -b, and environment settings
RuntimeFlags->>SysModule: publish argv, warnings, xoptions, and encoding state
SysModule->>StandardStreams: create streams using live encoding and buffering policy
BuiltinsInput->>StandardStreams: flush prompt streams and read stdin.readline
StandardStreams-->>BuiltinsInput: return input line or EOF
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 Codex parity reviewStatic analysis of this diff vs the local RPython/PyPy sources (commit ba42511). Files in the reviewed diff1. Regressions to PyPy parity introduced by this patchNone. 2. Other mismatches introduced by this patch
3. Pre-existing mismatches (already present before this patch)
4. Structural adaptations
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pyre/pyre-interpreter/src/module/faulthandler/handler.rs`:
- Around line 62-82: Update faulthandler.enable and the
faulthandler_signal_handler path so the validated descriptor from
faulthandler_extract_fd is stored in a global atomic before enabling fatal
handlers, and have the signal handler read that value instead of hardcoding fd
2. Preserve the existing enable success/error behavior and ensure the descriptor
is wired to enable_fatal_handlers for file-specific dumps.
In `@pyre/pyre-interpreter/src/module/sys/vm.rs`:
- Around line 1981-2011: Update stdio_stdin_readline to apply universal-newline
translation to the bytes returned by buffer.readline before decoding, converting
CRLF and standalone CR line endings to LF. Preserve the existing argument
validation, bytes validation, encoding/error selection, and final decoded-string
return behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: d21160e5-954e-4039-95c7-cc088a06f9cd
📒 Files selected for processing (13)
pyre/extra_tests/snippets/stdlib_warnings.pypyre/pyre-interpreter/src/builtins.rspyre/pyre-interpreter/src/executioncontext.rspyre/pyre-interpreter/src/importing.rspyre/pyre-interpreter/src/module/_io/textio.rspyre/pyre-interpreter/src/module/_socket/interp_socket.rspyre/pyre-interpreter/src/module/_warnings/mod.rspyre/pyre-interpreter/src/module/faulthandler/handler.rspyre/pyre-interpreter/src/module/posix/interp_posix.rspyre/pyre-interpreter/src/module/sys/vm.rspyre/pyre-interpreter/src/objspace/std/mapdict.rspyre/pyre-macros/src/lib.rspyre/pyrex/src/lib.rs
| // `handler.py:141-145 enable` — file=None, all_threads=True. | ||
| let _fd = | ||
| faulthandler_extract_fd(args.first().copied().unwrap_or(pyre_object::PY_NULL))?; | ||
| #[cfg(all(unix, feature = "host_env"))] | ||
| { | ||
| let ok = rustpython_host_env::faulthandler::enable_fatal_handlers( | ||
| faulthandler_signal_handler, | ||
| libc::SA_NODEFER | libc::SA_ONSTACK, | ||
| ); | ||
| if ok { | ||
| FAULTHANDLER_ENABLED.store(true, std::sync::atomic::Ordering::Relaxed); | ||
| return Ok(pyre_object::w_none()); | ||
| } | ||
| return Err(crate::PyError::runtime_error( | ||
| "faulthandler.enable: sigaction failed", | ||
| )); | ||
| } | ||
| return Err(crate::PyError::runtime_error( | ||
| "faulthandler.enable: sigaction failed", | ||
| )); | ||
| } | ||
| #[cfg(not(all(unix, feature = "host_env")))] | ||
| Err(crate::PyError::not_implemented( | ||
| "faulthandler.enable requires host_env feature", | ||
| )) | ||
| #[cfg(not(all(unix, feature = "host_env")))] | ||
| Err(crate::PyError::not_implemented( | ||
| "faulthandler.enable requires host_env feature", | ||
| )) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
enable() validates file but never wires its fd to the actual crash handler — dumps always go to fd 2.
_fd is computed via faulthandler_extract_fd(...) purely to validate the file argument, then discarded; enable_fatal_handlers only takes the handler function pointer and sigaction flags, and faulthandler_signal_handler (lines 21-29) hardcodes write_fd(2, ...). So faulthandler.enable(file=some_other_fd) silently writes fatal-error dumps to stderr instead of the requested fd — contradicting both CPython/PyPy semantics and this review cohort's own description ("faulthandler.enable and register wire validated file descriptors ... to host signal handlers"), which is true for register() (lines 196-202 pass fd through) but not for enable().
Since the signal handler is a fixed extern "C" fn and can't capture state, route the validated fd through a global (mirroring the existing FAULTHANDLER_ENABLED atomic) that the handler reads at crash time.
🛠️ Suggested fix
static FAULTHANDLER_ENABLED: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
+static FAULTHANDLER_FD: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(2);
extern "C" fn faulthandler_signal_handler(signum: libc::c_int) {
let name =
rustpython_host_env::faulthandler::fatal_signal_name(signum).unwrap_or("unknown signal");
let msg = format!("Fatal Python error: {name}\n");
- rustpython_host_env::faulthandler::write_fd(2, msg.as_bytes());
+ let fd = FAULTHANDLER_FD.load(std::sync::atomic::Ordering::Relaxed);
+ rustpython_host_env::faulthandler::write_fd(fd, msg.as_bytes());
rustpython_host_env::faulthandler::signal_default_and_raise(signum);
}- let _fd =
+ let fd =
faulthandler_extract_fd(args.first().copied().unwrap_or(pyre_object::PY_NULL))?;
#[cfg(all(unix, feature = "host_env"))]
{
+ FAULTHANDLER_FD.store(fd, std::sync::atomic::Ordering::Relaxed);
let ok = rustpython_host_env::faulthandler::enable_fatal_handlers(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // `handler.py:141-145 enable` — file=None, all_threads=True. | |
| let _fd = | |
| faulthandler_extract_fd(args.first().copied().unwrap_or(pyre_object::PY_NULL))?; | |
| #[cfg(all(unix, feature = "host_env"))] | |
| { | |
| let ok = rustpython_host_env::faulthandler::enable_fatal_handlers( | |
| faulthandler_signal_handler, | |
| libc::SA_NODEFER | libc::SA_ONSTACK, | |
| ); | |
| if ok { | |
| FAULTHANDLER_ENABLED.store(true, std::sync::atomic::Ordering::Relaxed); | |
| return Ok(pyre_object::w_none()); | |
| } | |
| return Err(crate::PyError::runtime_error( | |
| "faulthandler.enable: sigaction failed", | |
| )); | |
| } | |
| return Err(crate::PyError::runtime_error( | |
| "faulthandler.enable: sigaction failed", | |
| )); | |
| } | |
| #[cfg(not(all(unix, feature = "host_env")))] | |
| Err(crate::PyError::not_implemented( | |
| "faulthandler.enable requires host_env feature", | |
| )) | |
| #[cfg(not(all(unix, feature = "host_env")))] | |
| Err(crate::PyError::not_implemented( | |
| "faulthandler.enable requires host_env feature", | |
| )) | |
| static FAULTHANDLER_ENABLED: std::sync::atomic::AtomicBool = | |
| std::sync::atomic::AtomicBool::new(false); | |
| static FAULTHANDLER_FD: std::sync::atomic::AtomicI32 = | |
| std::sync::atomic::AtomicI32::new(2); | |
| extern "C" fn faulthandler_signal_handler(signum: libc::c_int) { | |
| let name = | |
| rustpython_host_env::faulthandler::fatal_signal_name(signum).unwrap_or("unknown signal"); | |
| let msg = format!("Fatal Python error: {name}\n"); | |
| let fd = FAULTHANDLER_FD.load(std::sync::atomic::Ordering::Relaxed); | |
| rustpython_host_env::faulthandler::write_fd(fd, msg.as_bytes()); | |
| rustpython_host_env::faulthandler::signal_default_and_raise(signum); | |
| } | |
| let fd = | |
| faulthandler_extract_fd(args.first().copied().unwrap_or(pyre_object::PY_NULL))?; | |
| #[cfg(all(unix, feature = "host_env"))] | |
| { | |
| FAULTHANDLER_FD.store(fd, std::sync::atomic::Ordering::Relaxed); | |
| let ok = rustpython_host_env::faulthandler::enable_fatal_handlers( | |
| faulthandler_signal_handler, | |
| libc::SA_NODEFER | libc::SA_ONSTACK, | |
| ); | |
| if ok { | |
| FAULTHANDLER_ENABLED.store(true, std::sync::atomic::Ordering::Relaxed); | |
| return Ok(pyre_object::w_none()); | |
| } | |
| return Err(crate::PyError::runtime_error( | |
| "faulthandler.enable: sigaction failed", | |
| )); | |
| } | |
| #[cfg(not(all(unix, feature = "host_env")))] | |
| Err(crate::PyError::not_implemented( | |
| "faulthandler.enable requires host_env feature", | |
| )) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pyre/pyre-interpreter/src/module/faulthandler/handler.rs` around lines 62 -
82, Update faulthandler.enable and the faulthandler_signal_handler path so the
validated descriptor from faulthandler_extract_fd is stored in a global atomic
before enabling fatal handlers, and have the signal handler read that value
instead of hardcoding fd 2. Preserve the existing enable success/error behavior
and ensure the descriptor is wired to enable_fatal_handlers for file-specific
dumps.
| fn stdio_stdin_readline(args: &[PyObjectRef]) -> crate::PyResult { | ||
| if args.len() > 1 { | ||
| return Err(crate::PyError::type_error(format!( | ||
| "readline() takes at most one argument ({} given)", | ||
| args.len() | ||
| ))); | ||
| } | ||
| let sys = crate::importing::get_sys_module("sys") | ||
| .ok_or_else(|| crate::PyError::runtime_error("lost sys.stdin"))?; | ||
| let stdin = crate::baseobjspace::getattr_str(sys, "stdin")?; | ||
| let buffer = crate::baseobjspace::getattr_str(stdin, "buffer")?; | ||
| let bytes = crate::baseobjspace::call_method(buffer, "readline", args); | ||
| if bytes.is_null() { | ||
| return Err(crate::call::take_call_error() | ||
| .unwrap_or_else(|| crate::PyError::runtime_error("readline failed"))); | ||
| } | ||
| if !unsafe { pyre_object::is_bytes(bytes) } { | ||
| return Err(crate::PyError::type_error( | ||
| "underlying readline() should have returned a bytes-like object", | ||
| )); | ||
| } | ||
| let _roots = pyre_object::gc_roots::push_roots(); | ||
| pyre_object::gc_roots::pin_root(bytes); | ||
| let bytes_slot = pyre_object::gc_roots::shadow_stack_len() - 1; | ||
| let (encoding, errors) = live_stdio_encoding_errors("stdin", "strict"); | ||
| crate::typedef::bytes_method_decode(&[ | ||
| pyre_object::gc_roots::shadow_stack_get(bytes_slot), | ||
| w_str_new(&encoding), | ||
| w_str_new(&errors), | ||
| ]) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## Locate files"
fd -a 'vm.rs|builtins.rs' . | sed 's#^\./##'
echo "## Outline relevant files"
ast-grep outline pyre/pyre-interpreter/src/module/sys/vm.rs --view expanded 2>/dev/null | sed -n '1,220p' || true
echo "## vm.rs around stdio_stdin_readline"
sed -n '1940,2030p' pyre/pyre-interpreter/src/module/sys/vm.rs
echo "## vm.rs around line 2116"
sed -n '2080,2140p' pyre/pyre-interpreter/src/module/sys/vm.rs
echo "## relevant builtins input usages"
rg -n "builtin_input|input\(|remove trailing|strip|trailing" pyre/pyre-interpreter/src module tests -g '*.rs' | sed -n '1,220p'
echo "## sys stdin allocation references"
rg -n "readuniversal|readtranslate|allocate_stdio|sys\.stdin|stdin|TextIOWrapper|Buffered|BufferedReader" pyre/pyre-interpreter/src/module pyre -g '*.rs' | sed -n '1,260p'Repository: youknowone/pyre
Length of output: 33421
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## vm.rs stdin allocation lines 2013-2085"
sed-2140p pyre/pyre-interpreter/src/module/sys/vm.rs -n '2013,2085'
echo "## builtin_input"
sed-2960p pyre/pyre-interpreter/src/builtins.rs -n '2897,2950'
echo "## stdio encoding / allocation config references"
rg -n "readuniversal|readtranslate|TextIOWrapper|decode\(\)|decode.*\(|sys\.stdin|sys.stdin|allocate_stdio|create_stdio|make_std_stream|stdio_stdin_readline" pyre/pyre-interpreter/src pyre -g '*.rs' -g '*.py' | sed -n '1,220p'
echo "## Read/write TextIOWrapper/stdio sources in repo"
git ls-files | rg -i 'TextIOWrapper|textio|stdio|buffer|file|io|interp_(stdio|io)|stream' || trueRepository: youknowone/pyre
Length of output: 242
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## vm.rs stdin allocation lines 2013-2085"
sed -n '2013,2085p' pyre/pyre-interpreter/src/module/sys/vm.rs
echo "## builtin_input"
sed -n '2897,2950p' pyre/pyre-interpreter/src/builtins.rs
echo "## stdio encoding / allocation config references"
rg -n "readuniversal|readtranslate|TextIOWrapper|decode\(\)|decode.*\(|sys\.stdin|sys.stdin|allocate_stdio|create_stdio|make_std_stream|stdio_stdin_readline" pyre/pyre-interpreter/src pyre -g '*.rs' -g '*.py' | sed -n '1,220p'
echo "## Read/write TextIOWrapper/stdio sources in repo"
git ls-files | rg -i 'TextIOWrapper|textio|stdio|buffer|file|io|interp_(stdio|io)|stream' || trueRepository: youknowone/pyre
Length of output: 50371
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## locate allocate_stdio definitions"
rg -n "allocate_stdio|alloc_stdio|readuniversal|readtranslate|TextIOWrapper" pyre/pyre-interpreter/src pyre/pyre-js/src pyre/pyre-wasm/src pyre/extras pyrex/pyre -g '*.rs' -g '*.py' --max-count 100
echo "## locate _io module definition area"
fd -a '_io' pyre/pyre-interpreter/src pyre/pyre-js/src pyre/pyre-wasm/src pyrex/pyre -t d | sed -n '1,40p'
echo "## read Rust allocate_stdio implementation around hits"
for f in $(rg -l "allocate_stdio" pyre/pyre-interpreter/src pyre/pyre-js/src pyre/pyre-wasm/src pyrex/pyre -g '*.rs'); do
echo "### $f"
rg -n -C 20 "fn allocate_stdio|allocate_stdio\\(" "$f" | sed -n '1,260p'
done
echo "## read CPython/Pypo relevant io.py/config snippets if present"
rg -n "readuniversal|readtranslate|stdlib_newline|universal|newline" lib-python/3/io.py lib_pypy/_io pypy/module/_io lib-python/3/test/test_io.py lib-python/3/test/test_textio.py -g '*.py' | sed -n '1,220p'Repository: youknowone/pyre
Length of output: 6461
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## allocate_stdio implementation"
sed -n '803,845p' pyre/pyre-interpreter/src/module/_io/textio.rs
echo "## readuniversal/readtranslate decoding path"
sed -n '494,525p' pyre/pyre-interpreter/src/module/_io/textio.rs
sed -n '645,665p' pyre/pyre-interpreter/src/module/_io/textio.rs
echo "## TextIOWrapper method registration and decode helper"
sed -n '114,160p' pyre/pyre-interpreter/src/module/_io/textio.rs
rg -n "decode_reader|decode_bytes|readtranslate|readuniversal" pyre/pyre-interpreter/src/module/_io/textio.rs | sed -n '1,180p'Repository: youknowone/pyre
Length of output: 5843
🌐 Web query:
CPython io.TextIOWrapper readline CRLF universal newlines translate \r\n to \n
💡 Result:
In CPython's io.TextIOWrapper, the readline method handles CRLF and other line endings based on the newline parameter passed to the constructor [1][2]. When the newline parameter is set to None (the default behavior for open), universal newlines mode is enabled [1][3]. In this mode, the TextIOWrapper automatically recognizes all common line endings—'\n' (LF), '\r' (CR), and '\r\n' (CRLF)—and translates them into a single '\n' (LF) character before returning the line to the caller [1][4]. The internal state machine for readline specifically handles the translation [5]. When it encounters a carriage return ('\r'), it peeks at the next character to determine if it is a line feed ('\n') [5]. If a CRLF sequence is detected, it is consumed and translated as a single '\n' [1][5]. If the newline parameter is set to a specific value (such as '', '\n', '\r', or '\r\n'), the behavior changes [1][2]: 1. If newline is '', universal newlines mode remains enabled, but the line endings are not translated; they are returned to the caller exactly as they appear in the source [1][4]. 2. If newline is set to a specific string (e.g., '\r\n'), only that specific sequence is recognized as a line terminator, and it is returned to the caller untranslated [1][2]. The newline translation applies to all reading methods, including read, readline, and readlines [6]. Additionally, when universal newlines mode is enabled (newline=None), the TextIOWrapper object maintains a newlines attribute that tracks the types of line endings encountered in the stream (e.g., None, '\n', '\r', '\r\n', or a tuple of these) [6][2][5].
Citations:
- 1: https://docs.python.org/3/library/io.html
- 2: https://github.com/python/cpython/blob/master/Modules/_io/textio.c
- 3: https://docs.python.org/3.10/library/io.html
- 4: https://peps.pythonlang.net/pep-3116/
- 5: https://gopy.tamnd.com/docs/annotations/modules/module_textiowrapper_detail
- 6: https://peps.python.org/pep-0278/
Preserve universal-newline translation in stdio_stdin_readline.
sys.stdin.readline() currently calls buffer.readline(), which returns decoded raw bytes split on \n. For CRLF input, this decoded line retains \r; input() then only strips a trailing '\n', so the carriage return is returned to the caller. Since the stdin TextIOWrapper is configured with readuniversal: true, readtranslate: true, apply the same CRLF/CR to LF translation here before returning the string.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pyre/pyre-interpreter/src/module/sys/vm.rs` around lines 1981 - 2011, Update
stdio_stdin_readline to apply universal-newline translation to the bytes
returned by buffer.readline before decoding, converting CRLF and standalone CR
line endings to LF. Preserve the existing argument validation, bytes validation,
encoding/error selection, and final decoded-string return behavior.
…e std streams The launcher parsed `-b`, `-u`, `-X`, `-W` and PYTHONIOENCODING without handing them on. `set_runtime_flags` now also takes `bytes_warning`, `unbuffered`, the raw `-X` and `-W` strings and the stdio encoding. `app_main.py` keeps the `-X` strings in `options['_xoptions']` as a list until sys initialization builds the public dict, so they are stored as lists here too; `sys._xoptions`, `sys.warnoptions` and `sys.orig_argv` are built from them. _io: `allocate_stdio` takes the buffer object plus `line_buffering` and `write_through` instead of defaulting them to none/false. builtins: `input` was a stub returning "". It resolves sys.stdin, sys.stdout and sys.stderr, flushes stderr, writes and flushes the prompt through stdout, then reads one line from stdin (`app_io.py:input`), so redirected streams are used; end of input raises EOFError. Assisted-by: Claude
Seven commits on top of
main, each a separate parity fix._socket: encode an IDNA hostname through the codec, not an encode attribute_socketexecutioncontext: drop the space-null guard from the trace hooksfaulthandler: coerce all_threads and chain as integersfaulthandlermapdict: release the code-owned cache lock before touching an instanceposix: give the result structseqs their dotted names and named-only extrasposix_warnings: take warn's source positionally_warnings,pyre-macrossys, _io, builtins: carry the launcher's option state into sys and the std streamssys,_io,builtinsAuthorship
Only the
_socketcommit was written and verified here. The other six are theoutput of a
codexsession running in the same worktree; they were leftuncommitted and are split into themed commits and given commit messages citing
the upstream lines they follow, but their code has not been reviewed
line-by-line here.
_socket— what the change rests onsocket_idna_converterlooked upencodeon the host object and called it.interp_socket.py:108-129 idna_converterreaches the codec throughspace.encode_unicode_object, which isobjspace.py:786->unicodeobject.py:1683 encode_object;type_methods::encode_objectis thecounterpart already shared by
str.encode,bytes(str, ...)andbytearray(str, ...).A review suggestion motivating this said the codec route avoids invoking a
strsubclass'sencodeoverride. Both oracles refute that:encodings/idna.pyCodec.encodeitself callsinput.encode('ascii'), so the override runs eitherway.
The change is made on the structural ground above, not that one. Observable
behaviour on the normal paths:
Verification
ba42511c81, HEAD pinned across every stage:cargo test --all --no-default-features --features dynasmtest result: ok, 0 failedpyre/check.pycargo test -p pyre-sandbox --test e2e_interact -- --ignoredThe one
check.pyfailure issynth/ast_compile_roundtripon all threebackends, reported as
BASEFAIL/cpython/pypy output mismatch. The twooracles disagree with each other, so no baseline is established:
That benchmark arrived with
a510d58a73(#856) and is untouched by thisbranch, so the failure is not attributable to it.
The first
cargo test --allrun aborted inpyre-object functional::range_obj_tests::iter_routes_increment_overflow_to_longrange(
handle_alloc_error-> SIGABRT). No commit here touchespyre-object; thetest passes alone, the crate's 279 tests pass together, and the same command at
the same commit passes on re-run, with sibling worktrees running concurrent
builds during the failing run.
🤖 Generated with Claude Code
Follow-up in this branch
The first push failed
sandbox build + e2e (ubuntu-24.04): both e2e tests diedin the guest with
make_std_streamopened the binary layer for fd 0/1/2 and called.expectonthe result, so a descriptor the sandbox controller does not expose aborted
interpreter startup.
app_main.py:465-470 create_stdiotakes that failure asexcept OSErrorrather than aborting. Itsreturn Noneis not the rightmapping here — these streams keep instance-override methods that reach the
descriptor without the buffer, and the e2e guests assert on
printoutput — sothe open failure now leaves the buffer absent and the stream in place.
Reproduced locally (2 failed), fixed, re-run green (2 passed), and squashed
into the
sys, _io, builtinscommit.