Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
be972f0
interp, jit: PEP 529 filesystem encoding and OSError.winerror on Windows
youknowone Aug 5, 2026
ed6ccdc
parity_tests: name the os implementation module by platform
youknowone Aug 5, 2026
f4d555e
posix, _winapi: carry the Win32 error code into OSError.winerror
youknowone Aug 5, 2026
7f779ed
posix: the Windows descriptor calls, the wide path calls, and fstat
youknowone Aug 6, 2026
a8a2e18
posix: serve the os calls registered as noop placeholders
youknowone Aug 6, 2026
6a6b87a
posix: os.system, waitpid, times, the drive listings, and a terminal …
youknowone Aug 6, 2026
baea4ba
_winapi: the process-launch calls, so subprocess spawns on Windows
youknowone Aug 6, 2026
4868669
posix: name only the argument when dir_fd is unavailable
youknowone Aug 6, 2026
2a86b7f
parity_tests: assert the remaining timeout, not the whole of it
youknowone Aug 6, 2026
7115b92
posix: get_inheritable reads the descriptor flag through host_env
youknowone Aug 6, 2026
a911164
builtins: tell reads its lseek through the C runtime call wrapper
youknowone Aug 6, 2026
c8ff3fd
_winapi: read the process environment through the mapping protocol
youknowone Aug 6, 2026
d7ced19
posix: the arguments truncate, link, symlink and startfile were dropping
youknowone Aug 6, 2026
e5242e6
parity_tests: OSError's member descriptors name the class they hold
youknowone Aug 6, 2026
df95fd1
posix: utime names the file it could not reach
youknowone Aug 6, 2026
0135625
check.py, parity_tests: take a 3.14 oracle or none
youknowone Aug 6, 2026
ede7a44
Merge branch 'main' into win-fs-encoding-winerror
youknowone Aug 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions lib-python/3/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,12 @@ def _get_handles(self, stdin, stdout, stderr):
c2pread, c2pwrite = -1, -1
errread, errwrite = -1, -1

# A handle this method made a pipe of is closed as soon as its
# inheritable duplicate replaces it. Leaving that to the `Handle`
# wrapper's `__del__` waits on the collector, and until it runs the
# parent still holds the write end of the pipe it is reading to
# end-of-file — which never arrives.
ispread = False
with self._on_error_fd_closer() as err_close_fds:
if stdin is None:
p2cread = _winapi.GetStdHandle(_winapi.STD_INPUT_HANDLE)
Expand All @@ -1366,50 +1372,60 @@ def _get_handles(self, stdin, stdout, stderr):
p2cread = Handle(p2cread)
err_close_fds.append(p2cread)
_winapi.CloseHandle(_)
ispread = True
elif stdin == PIPE:
p2cread, p2cwrite = _winapi.CreatePipe(None, 0)
p2cread, p2cwrite = Handle(p2cread), Handle(p2cwrite)
err_close_fds.extend((p2cread, p2cwrite))
ispread = True
elif stdin == DEVNULL:
p2cread = msvcrt.get_osfhandle(self._get_devnull())
elif isinstance(stdin, int):
p2cread = msvcrt.get_osfhandle(stdin)
else:
# Assuming file-like object
p2cread = msvcrt.get_osfhandle(stdin.fileno())
p2cread = self._make_inheritable(p2cread)
p2cread = self._make_inheritable(p2cread, ispread)

ispwrite = False
if stdout is None:
c2pwrite = _winapi.GetStdHandle(_winapi.STD_OUTPUT_HANDLE)
if c2pwrite is None:
_, c2pwrite = _winapi.CreatePipe(None, 0)
c2pwrite = Handle(c2pwrite)
err_close_fds.append(c2pwrite)
_winapi.CloseHandle(_)
ispwrite = True
elif stdout == PIPE:
c2pread, c2pwrite = _winapi.CreatePipe(None, 0)
c2pread, c2pwrite = Handle(c2pread), Handle(c2pwrite)
err_close_fds.extend((c2pread, c2pwrite))
ispwrite = True
elif stdout == DEVNULL:
c2pwrite = msvcrt.get_osfhandle(self._get_devnull())
elif isinstance(stdout, int):
c2pwrite = msvcrt.get_osfhandle(stdout)
else:
# Assuming file-like object
c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
c2pwrite = self._make_inheritable(c2pwrite)
c2pwrite = self._make_inheritable(c2pwrite, ispwrite)

# `stderr == STDOUT` names the duplicate `c2pwrite` already
# holds, which stays open for as long as that one does.
ispwrite = False
if stderr is None:
errwrite = _winapi.GetStdHandle(_winapi.STD_ERROR_HANDLE)
if errwrite is None:
_, errwrite = _winapi.CreatePipe(None, 0)
errwrite = Handle(errwrite)
err_close_fds.append(errwrite)
_winapi.CloseHandle(_)
ispwrite = True
elif stderr == PIPE:
errread, errwrite = _winapi.CreatePipe(None, 0)
errread, errwrite = Handle(errread), Handle(errwrite)
err_close_fds.extend((errread, errwrite))
ispwrite = True
elif stderr == STDOUT:
errwrite = c2pwrite
elif stderr == DEVNULL:
Expand All @@ -1419,19 +1435,23 @@ def _get_handles(self, stdin, stdout, stderr):
else:
# Assuming file-like object
errwrite = msvcrt.get_osfhandle(stderr.fileno())
errwrite = self._make_inheritable(errwrite)
errwrite = self._make_inheritable(errwrite, ispwrite)

return (p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite)


def _make_inheritable(self, handle):
def _make_inheritable(self, handle, close=False):
"""Return a duplicate of handle, which is inheritable"""
h = _winapi.DuplicateHandle(
_winapi.GetCurrentProcess(), handle,
_winapi.GetCurrentProcess(), 0, 1,
_winapi.DUPLICATE_SAME_ACCESS)
# The handle the duplicate replaces is closed here when it was
# `_get_handles`' own — an end of a pipe it just made.
if close:
handle.Close()
return Handle(h)


Expand Down
71 changes: 63 additions & 8 deletions pyre/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,69 @@

EXE = ".exe" if sys.platform == "win32" else ""
# pyre's compat target is CPython 3.14; its native modules (`_sre.MAGIC`) and the
# vendored `lib-python/3` are coupled to that version. Prefer a version-matched
# oracle so a stale `python3` on PATH (an older system CPython) does not diverge
# from pypy on version-sensitive error text and trip a spurious cpython-vs-pypy
# baseline mismatch.
PYTHON3 = os.environ.get("PYRE_CHECK_PYTHON3") or next(
(cand for cand in ("python3.14", "python3", "python") if shutil.which(cand)),
"python3",
)
# vendored `lib-python/3` are coupled to that version. Take a version-matched
# oracle or none at all: a stale `python3` on PATH (an older system CPython)
# diverges from pypy on version-sensitive error text and trips a spurious
# cpython-vs-pypy baseline mismatch, and its timings gate the ratios.
CPYTHON_TARGET = (3, 14)


def _probe_interpreter(command):
"""What the interpreter reports as its version and its own path.

`None` when the command did not run at all, which on Windows is what a
`python3.14` naming an extensionless shim rather than an executable does —
`shutil.which` finds it and `CreateProcess` cannot start it.
"""
probe = "import sys; print(sys.version_info[0], sys.version_info[1]); print(sys.executable)"
try:
proc = subprocess.run(
[command, "-c", probe], capture_output=True, text=True, timeout=30,
)
except (OSError, subprocess.SubprocessError):
return None
if proc.returncode != 0:
return None
lines = (proc.stdout or "").splitlines()
if len(lines) < 2:
return None
try:
major, minor = lines[0].split()
except ValueError:
return None
return (int(major), int(minor)), lines[1].strip() or command


def _resolve_python3():
"""The oracle interpreter, as the absolute path it reports for itself.

A bare name would be resolved against the PATH of whichever environment
spawns it, and the timed runs hand the child a curated one.
"""
named = os.environ.get("PYRE_CHECK_PYTHON3")
candidates = [named] if named else ["python3.14", "python3", "python"]
rejected = []
for candidate in candidates:
if named is None and shutil.which(candidate) is None:
continue
probed = _probe_interpreter(candidate)
if probed is None:
rejected.append(f" {candidate}: did not run")
continue
version, executable = probed
if version == CPYTHON_TARGET:
return executable
rejected.append(" %s: %d.%d" % (candidate, *version))
Comment on lines +41 to +79

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require the CPython implementation before accepting the oracle.

Both resolvers accept any interpreter whose sys.version_info is (3, 14). A PyPy, GraalPy, or other implementation with that version can pass and become the parity baseline. This invalidates the comparison that these scripts report as CPython parity.

  • pyre/check.py#L41-L79: Include sys.implementation.name in the probe output. Reject candidates unless it equals "cpython" and the version equals (3, 14).
  • pyre/extra_tests/parity_tests/run.py#L126-L179: Apply the same implementation check before _cpython() returns the executable path.

As per PR objectives, this layer must require a matching CPython 3.14 interpreter.

🧰 Tools
🪛 ast-grep (0.45.0)

[error] 42-44: Command coming from incoming request
Context: subprocess.run(
[command, "-c", probe], capture_output=True, text=True, timeout=30,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🪛 Ruff (0.16.1)

[error] 43-43: subprocess call: check for execution of untrusted input

(S603)


[warning] 43-43: subprocess.run without explicit check argument

Add explicit check=False

(PLW1510)


[warning] 60-60: Missing return type annotation for private function _resolve_python3

(ANN202)


[warning] 79-79: Use format specifiers instead of percent format

(UP031)

📍 Affects 2 files
  • pyre/check.py#L41-L79 (this comment)
  • pyre/extra_tests/parity_tests/run.py#L126-L179
🤖 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/check.py` around lines 41 - 79, Require CPython specifically when
resolving the oracle interpreter: in pyre/check.py lines 41-79, extend
_probe_interpreter to report sys.implementation.name and make _resolve_python3
accept candidates only when the implementation is "cpython" and the version is
CPYTHON_TARGET; apply the same implementation check in
pyre/extra_tests/parity_tests/run.py lines 126-179 before _cpython() returns an
executable path.

wanted = "%d.%d" % CPYTHON_TARGET
raise SystemExit(
f"no CPython {wanted} to measure against — pyre targets it, and an "
f"older one disagrees on version-sensitive behaviour and timings.\n"
+ ("\n".join(rejected) or " (no candidate on PATH)")
+ "\nName one with PYRE_CHECK_PYTHON3."
)


PYTHON3 = _resolve_python3()
PYPY3 = os.environ.get("PYRE_CHECK_PYPY3") or (
"pypy3" if shutil.which("pypy3") else "pypy"
)
Expand Down
56 changes: 41 additions & 15 deletions pyre/extra_tests/parity_tests/compile_filename_boundary.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,22 @@
and this suite has to pass under CPython too.
"""

import sys

SOURCE = "x = 1"

# The filename the filesystem encoding carries but plain UTF-8 text cannot, and
# the bytes that spell it. Windows encodes with `surrogatepass` (PEP 529), so
# the lone surrogate is spelled by its own three UTF-8 bytes and a byte that
# begins no sequence has no spelling at all; every other platform uses
# `surrogateescape`, where that same byte is what the surrogate stands for.
if sys.platform == "win32":
NAME_BYTES, NAME_TEXT = b"\xed\xb3\xbf.py", "\udcff.py"
UNSPELLABLE = b"\xff.py"
else:
NAME_BYTES, NAME_TEXT = b"\xff.py", "\udcff.py"
UNSPELLABLE = None


class Path:
def __init__(self, name):
Expand All @@ -33,14 +47,23 @@ def __fspath__(self):
# bytes decode with the filesystem encoding. The ASCII case matters on its own:
# it needs no surrogate to expose a filename that never reaches the code object.
assert compile(SOURCE, b"ascii.py", "exec").co_filename == "ascii.py"
assert compile(SOURCE, b"\xff.py", "exec").co_filename == "\udcff.py"
assert compile(SOURCE, NAME_BYTES, "exec").co_filename == NAME_TEXT

# A str the encoding can only spell as a surrogate encodes back to the same
# bytes instead of raising UnicodeEncodeError.
assert compile(SOURCE, NAME_TEXT, "exec").co_filename == NAME_TEXT

# A str already carrying a surrogate escape encodes back to that same byte
# instead of raising UnicodeEncodeError.
assert compile(SOURCE, "\udcff.py", "exec").co_filename == "\udcff.py"
# Bytes the encoding cannot spell are reported, not renamed.
if UNSPELLABLE is not None:
try:
compile(SOURCE, UNSPELLABLE, "exec")
except UnicodeDecodeError:
pass
else:
raise AssertionError("compile() invented a spelling for %r" % (UNSPELLABLE,))

# `__fspath__` is honoured, and may answer with either str or bytes.
assert compile(SOURCE, Path(b"\xff.py"), "exec").co_filename == "\udcff.py"
assert compile(SOURCE, Path(NAME_BYTES), "exec").co_filename == NAME_TEXT
assert compile(SOURCE, Path("spelled.py"), "exec").co_filename == "spelled.py"

# An object that is neither a path nor a string is a TypeError, not a filename
Expand Down Expand Up @@ -75,9 +98,9 @@ def __fspath__(self):
# The filename a SyntaxError reports is the same one the successful compile
# would have recorded.
try:
compile("(", b"\xff.py", "exec")
compile("(", NAME_BYTES, "exec")
except SyntaxError as exc:
assert exc.filename == "\udcff.py", ascii(exc.filename)
assert exc.filename == NAME_TEXT, ascii(exc.filename)
else:
raise AssertionError("compile() accepted an unterminated '('")

Expand All @@ -87,26 +110,29 @@ def __fspath__(self):
code = compile(SOURCE, "before.py", "exec")
assert 'file "before.py"' in repr(code), repr(code)

replaced = code.replace(co_filename="\udcff.py")
assert replaced.co_filename == "\udcff.py"
assert 'file "\udcff.py"' in repr(replaced), ascii(repr(replaced))
replaced = code.replace(co_filename=NAME_TEXT)
assert replaced.co_filename == NAME_TEXT
assert 'file "%s"' % NAME_TEXT in repr(replaced), ascii(repr(replaced))

# `pycode.py:570-572` reports the zero sentinel as line -1.
assert 'line -1>' in repr(code.replace(co_firstlineno=0)), ascii(
repr(code.replace(co_firstlineno=0))
)

# and a filename that never had a UTF-8 spelling reaches repr() the same way.
from_bytes = compile(SOURCE, b"\xff.py", "exec")
assert 'file "\udcff.py"' in repr(from_bytes), ascii(repr(from_bytes))
# and a filename that has no plain-text spelling reaches repr() the same way.
from_bytes = compile(SOURCE, NAME_BYTES, "exec")
assert 'file "%s"' % NAME_TEXT in repr(from_bytes), ascii(repr(from_bytes))


# Every reader of a code object agrees on the filename, so a frame built from
# one points at the real file rather than at "<string>".
namespace = {}
exec(compile("def f():\n import sys\n return sys._getframe()\n", b"\xff.py", "exec"), namespace)
exec(
compile("def f():\n import sys\n return sys._getframe()\n", NAME_BYTES, "exec"),
namespace,
)
frame = namespace["f"]()
assert frame.f_code.co_filename == "\udcff.py", ascii(frame.f_code.co_filename)
assert frame.f_code.co_filename == NAME_TEXT, ascii(frame.f_code.co_filename)
assert "�" not in repr(frame), ascii(repr(frame))

print("OK")
25 changes: 15 additions & 10 deletions pyre/extra_tests/parity_tests/dir_entry_uninstantiable_python314.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
`acceptable_as_base_class = False` (:487); `:172-180` does the same for
`W_ScandirIterator.typedef`. `:463-465 descr_reduce_ex` refuses to pickle an
entry, spelling the type with `%T` (`error.py:592-593` -> the qualified typedef
name `'posix.DirEntry'`, `:469`).
name `'posix.DirEntry'`, `:469`). The module holding these types is the one
`os` is implemented by, which Windows spells `nt`.

Pickle protocols 0 and 1 are deliberately NOT asserted: they never reach
`reduce_newobj`, they land in `copyreg._reduce_ex`, and that leg already
Expand All @@ -17,46 +18,50 @@
import copy
import os
import pickle
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
MODULE = "nt" if sys.platform == "win32" else "posix"

assert os.DirEntry.__module__ == "posix", os.DirEntry.__module__
assert os.DirEntry.__module__ == MODULE, os.DirEntry.__module__
assert os.DirEntry.__name__ == "DirEntry", os.DirEntry.__name__
assert os.DirEntry.__qualname__ == "DirEntry", os.DirEntry.__qualname__
assert repr(os.DirEntry) == "<class 'posix.DirEntry'>", repr(os.DirEntry)
assert repr(os.DirEntry) == "<class '%s.DirEntry'>" % MODULE, repr(os.DirEntry)

try:
os.DirEntry()
except TypeError as exc:
assert str(exc) == "cannot create 'posix.DirEntry' instances", str(exc)
assert str(exc) == "cannot create '%s.DirEntry' instances" % MODULE, str(exc)
else:
raise AssertionError("os.DirEntry() must not construct an entry")

try:
class _Sub(os.DirEntry):
pass
except TypeError as exc:
assert str(exc) == "type 'posix.DirEntry' is not an acceptable base type", str(exc)
expected = "type '%s.DirEntry' is not an acceptable base type" % MODULE
assert str(exc) == expected, str(exc)
else:
raise AssertionError("os.DirEntry must not be an acceptable base type")

it = os.scandir(HERE)
scandir_iterator = type(it)
assert scandir_iterator.__module__ == "posix", scandir_iterator.__module__
assert scandir_iterator.__module__ == MODULE, scandir_iterator.__module__
assert scandir_iterator.__name__ == "ScandirIterator", scandir_iterator.__name__

try:
scandir_iterator()
except TypeError as exc:
assert str(exc) == "cannot create 'posix.ScandirIterator' instances", str(exc)
assert str(exc) == "cannot create '%s.ScandirIterator' instances" % MODULE, str(exc)
else:
raise AssertionError("ScandirIterator() must not construct an iterator")

try:
class _SubIter(scandir_iterator):
pass
except TypeError as exc:
assert str(exc) == "type 'posix.ScandirIterator' is not an acceptable base type", str(exc)
expected = "type '%s.ScandirIterator' is not an acceptable base type" % MODULE
assert str(exc) == expected, str(exc)
Comment on lines +21 to +64

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
f=pyre/pyre-interpreter/src/module/posix/interp_posix.rs
rg -n 'make_builtin_type\("(posix|nt)\.' "$f"
# Check whether any layer rewrites a builtin type's module name for Windows.
rg -n -C4 'cfg!\(windows\)|target_os = "windows"' pyre/pyre-interpreter/src/typedef.rs | head -50

Repository: youknowone/pyre

Length of output: 761


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant typedef constructors and nearby Windows/platform-aware examples.
f=pyre/pyre-interpreter/src/module/posix/interp_posix.rs
sed -n '2920,3085p' "$f" | cat -n

# Check the test behavior expectations and module detection.
g=pyre/extra_tests/parity_tests/dir_entry_uninstantiable_python314.py
sed -n '1,90p' "$g" | cat -n

# Find all references to os.DirEntry / ScandirIterator in extra_tests and module definitions.
rg -n "DirEntry|ScandirIterator|make_builtin_type\\(\"posix\\.|make_builtin_type\\(\"nt\\." pyre/extra_tests pyre/pyre-interpreter/src | head -200

Repository: youknowone/pyre

Length of output: 19799


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find any layer that chooses nt vs posix module prefixes for os types.
rg -n -C6 'os_module|os_scandir|scandir|nt|make_builtin_type\("nt|make_builtin_type\("posix|target_os = "windows|cfg!\(windows\)' pyre/pyre-interpreter/src/module | head -300

# Find os module files and their scandir-related type definitions.
fd -e rs -x sh -c 'echo "--- $1"; rg -n -C5 "scandir|DirEntry|ScandirIterator|os_module|module_ns_store|make_builtin_type" "$1"' sh pyre/pyre-interpreter/src/module

Repository: youknowone/pyre

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read focused os module files and the typedef name handling implementation.
fd -e rs '^(os|nt|typedef)\.rs$' pyre/pyre-interpreter/src/module pyre
for f in $(fd -e rs '^(os|nt)\.rs$' pyre/pyre-interpreter/src/module); do
  echo "--- $f"
  wc -l "$f"
  rg -n -C5 'DirEntry|ScandirIterator|scandir|os_module|nt|module_ns_store|register_module|target_os = "windows|cfg!\(windows\)' "$f" || true
done

echo "--- typedef.rs relevant lines"
rg -n -C6 'fn make_builtin_type|__module__|w_type_set_name|type_get_name|PySys_SetConfig|module_path' pyre/pyre-interpreter/src/typedef.rs

Repository: youknowone/pyre

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# List exact os/nt-related files.
git ls-files pyre/pyre-interpreter/src/module | grep -E '(^|/)(os|nt)\.rs$|/module/(os|nt)/' || true

# Search focused file list for relevant registry code.
while IFS= read -r f; do
  echo "--- $f"
  wc -l "$f"
  rg -n -C6 'DirEntry|ScandirIterator|scandir|os_module|nt|register_module|module_ns_store|make_builtin_type|target_os = "windows|cfg!\(windows"' "$f" || true
done < <(git ls-files pyre/pyre-interpreter/src/module | grep -E '(^|/)(os|nt)\.rs$|/module/(os|nt)/')

# Read typedef name handling around the module derivation.
f=pyre/pyre-interpreter/src/typedef.rs
sed -n '2268,2293p' "$f" | cat -n
sed -n '10888,10931p' "$f" | cat -n

Repository: youknowone/pyre

Length of output: 206


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- module files"
git ls-files pyre/pyre-interpreter/src/module | grep -E '(^|/)(os|nt)\.rs$|module/(os|nt)/' || true

echo "--- focused search for scandir/os types"
for f in pyre/pyre-interpreter/src/module/os.rs pyre/pyre-interpreter/src/module/nt.rs $(git ls-files pyre/pyre-interpreter/src/module | grep -E '(^|/)(os|nt)\.rs$'); do
  if [ -f "$f" ]; then
    echo "--- $f"
    wc -l "$f"
    rg -n -C6 'DirEntry|ScandirIterator|scandir|os_module|nt|register_module|module_ns_store|make_builtin_type|target_os = "windows|cfg!\(windows"' "$f" || true
  fi
done

echo "--- os crate references"
rg -n -C4 'os_module|os_scandir|scandir_fn|DirEntry|ScandirIterator|pyre::module::os|module::os' pyre/pyre-interpreter/src pyre | head -250

Repository: youknowone/pyre

Length of output: 26249


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- build config files mentioning posix/windows/os"
git ls-files | grep -E '(^|/)(Cargo.toml|Cargo.lock|pyproject.toml|oxc_build|build\.rs|Makefile|\.gclient|platform|windows)\.rs$' | head -200 || true
rg -n -C4 'target_arch.*wasm|windows|nt|os\scmodule|os_module|positive|posix|Platform|sys.platform|python314|platforms=' Cargo.toml configure.py pyproject.toml . | head -300 || true

echo "--- search for times_result_seq_type platform handling (mentioned in original comment)"
rg -n -C8 'times_result_seq_type|make_time|ScandirIterator|DirEntry|cfg!\(windows\)|target_os = "windows"' pyre/pyre-interpreter/src pyre/extra_tests/parity_tests | head -300

echo "--- read-only verifier: model name/module derivation and platform tests"
python3 - <<'PY'
from pathlib import Path
import re

f = Path("pyre/pyre-interpreter/src/module/posix/interp_posix.rs")
t = Path("pyre/pyre-interpreter/src/typedef.rs")
test = Path("pyre/extra_tests/parity_tests/dir_entry_uninstantiable_python314.py")

for sym in ["DirEntry", "ScandirIterator"]:
    src = f.read_text()
    for n, line in enumerate(src.splitlines(), 1):
        if f'make_builtin_type("{sym}"' in line:
            print(f"{f}:{n}: {line.strip()}")

print("typedef module derivation:")
text = t.read_text()
for n, line in enumerate(text.splitlines(), 1):
    if "rsplit_once" in line and "{" in line:
        start = n - 8
        end = n + 10
        print(f"typedef.rs:{start}-{end}")
        for i in range(max(1,start), end+1):
            print(f"  {i}: {text.splitlines()[i-1]}")

print("test module expectation:")
lines = test.read_text()
for n, line in enumerate(lines.splitlines(), 1):
    if "MODULE = " in line or "=='%s." in line:
        print(f"{test}:{n}: {line.strip()}")

print("scan for any Windows module-name rewrite around these symbols:")
root = Path("pyre/pyre-interpreter/src")
matches=[]
for p in root.rglob("*.rs"):
    s=p.read_text(errors="ignore")
    if "DirEntry" in s or "ScandirIterator" in s or "times_result_seq_type" in s:
        for n,line in enumerate(s.splitlines(),1):
            if any(sg in line for sg in ["DirEntry", "ScandirIterator", "times_result_seq_type", 'make_builtin_type("nt', 'make_builtin_type("posix']):
                matches.append((str(p),n,line.strip()))
for p,n,line in matches:
    print(f"{p}:{n}: {line}")
PY

Repository: youknowone/pyre

Length of output: 50373


Make the scandir-related typedef names platform-aware.

make_builtin_type("posix.DirEntry") and make_builtin_type("posix.ScandirIterator") register those types as posix.* even when Windows expects nt.*. Use the same cfg!(windows) branch used for times_result_seq_type, e.g. "nt.DirEntry"/"nt.ScandirIterator", so the test's MODULE = "nt" if sys.platform == "win32" else "posix" assertions pass on Windows.
[functional_correct]

🧰 Tools
🪛 Ruff (0.16.1)

[warning] 29-29: Use format specifiers instead of percent format

Replace with format specifiers

(UP031)


[warning] 34-34: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)


[warning] 34-34: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)


[warning] 34-34: Use format specifiers instead of percent format

Replace with format specifiers

(UP031)


[warning] 36-36: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 42-42: Use format specifiers instead of percent format

Replace with format specifiers

(UP031)


[warning] 43-43: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)


[warning] 43-43: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)


[warning] 45-45: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 55-55: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)


[warning] 55-55: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)


[warning] 55-55: Use format specifiers instead of percent format

Replace with format specifiers

(UP031)


[warning] 57-57: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 63-63: Use format specifiers instead of percent format

Replace with format specifiers

(UP031)


[warning] 64-64: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)


[warning] 64-64: Found assertion on exception exc in except block, use pytest.raises() instead

(PT017)

🤖 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/extra_tests/parity_tests/dir_entry_uninstantiable_python314.py` around
lines 21 - 64, Update the scandir-related builtin type registrations passed to
make_builtin_type so DirEntry and ScandirIterator use "nt.*" on Windows and
"posix.*" otherwise, matching the cfg!(windows) branching used for
times_result_seq_type. Preserve the existing type behavior while making their
module names platform-aware for the assertions in this test.

else:
raise AssertionError("ScandirIterator must not be an acceptable base type")

Expand All @@ -72,15 +77,15 @@ class _SubIter(scandir_iterator):
try:
pickle.dumps(entry, protocol)
except TypeError as exc:
assert str(exc) == "cannot pickle 'posix.DirEntry' object", (protocol, str(exc))
assert str(exc) == "cannot pickle '%s.DirEntry' object" % MODULE, (protocol, str(exc))
else:
raise AssertionError("a DirEntry must refuse to be pickled")

for clone in (copy.copy, copy.deepcopy):
try:
clone(entry)
except TypeError as exc:
assert str(exc) == "cannot pickle 'posix.DirEntry' object", (clone, str(exc))
assert str(exc) == "cannot pickle '%s.DirEntry' object" % MODULE, (clone, str(exc))
else:
raise AssertionError("a DirEntry must refuse to be copied")

Expand Down
Loading
Loading