Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8a7e0ff
Preserve original main VM through C API execution
youknowdot Oct 16, 2026
4da4895
Make unraisable reporting robust at hook boundaries
youknowdot Oct 16, 2026
3a4ad29
Separate interactive warning collection from compilation
youknowdot Oct 16, 2026
5649b19
Evaluate native stdin with shared VM record semantics
youknowdot Oct 16, 2026
7e1fe21
Refresh canonical REPL tests and retire fixed failures
youknowdot Oct 16, 2026
bddb59f
Keep the root compiler dependency for development targets
youknowdot Oct 16, 2026
371728d
Validate explicit conditional failure marker retirement
youknowdot Oct 16, 2026
0efd645
Refresh canonical stdin and exception audit tests
youknowdot Oct 16, 2026
19cf878
Synchronize runpy warning attribution and document remaining capabili…
youknowdot Oct 16, 2026
08e4c22
Propagate interrupted native stdin reads to signal dispatch
youknowdot Oct 16, 2026
daf4ce4
Reject statements after a compound statement in single mode
youknowone Oct 17, 2026
6a2ecdd
Remove passing expected failure from test_runpy
youknowone Oct 17, 2026
a4d5d4a
Restructure stdin execution after Modules/main.c and pythonrun.c
youknowone Oct 17, 2026
8a20dd6
Exit through Py_Exit from PyErr_Print and run the basic REPL via stdin
youknowone Oct 17, 2026
e2ac3c0
Display exceptions through PyErr_Display and track the main thread ru…
youknowone Oct 18, 2026
2a8fa66
Treat a parse error at the end of the source as incomplete input
youknowone Oct 18, 2026
6480222
Place syntax errors on tokens made up at the end of the input
youknowone Oct 18, 2026
934004f
Slice the source with get when checking an indented block error at EOF
youknowone Oct 18, 2026
05881b0
Rename the host exits after C exit() and fix wasi and Windows builds
youknowone Oct 18, 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
1 change: 1 addition & 0 deletions .cspell.dict/cpython.txt
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ pylifecycle
pymain
pymem
pyrepl
pyrun
pystate
pystrcmp
PYTHONTRACEMALLOC
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ winresource = "0.1"

[dependencies]
rustpython-capi = { workspace = true, optional = true }
rustpython-compiler = { workspace = true }
rustpython-pylib = { workspace = true, optional = true }
rustpython-stdlib = { workspace = true, optional = true, default-features = false, features = ["compiler"] }
rustpython-vm = { workspace = true, features = ["compiler", "gc"] }
Expand All @@ -62,6 +61,7 @@ rustyline = { workspace = true }
mimalloc = { version = "0.1", optional = true, default-features = false }

[dev-dependencies]
rustpython-compiler = { workspace = true }
criterion = { workspace = true }
pyo3 = { workspace = true, features = ["auto-initialize"] }
rustls-graviola = { workspace = true }
Expand Down
8 changes: 3 additions & 5 deletions Lib/runpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ def _run_code(code, run_globals, init_globals=None,
pkg_name = mod_spec.parent
run_globals.update(__name__ = mod_name,
__file__ = fname,
__cached__ = cached,
__doc__ = None,
__loader__ = loader,
__package__ = pkg_name,
Expand Down Expand Up @@ -185,7 +184,6 @@ def _run_module_as_main(mod_name, alter_argv=True):
At the very least, these variables in __main__ will be overwritten:
__name__
__file__
__cached__
__loader__
__package__
"""
Expand Down Expand Up @@ -254,7 +252,7 @@ def _get_main_module_details(error=ImportError):
sys.modules[main_name] = saved_main


def _get_code_from_file(fname):
def _get_code_from_file(fname, module):
# Check for a compiled file first
from pkgutil import read_code
code_path = os.path.abspath(fname)
Expand All @@ -263,7 +261,7 @@ def _get_code_from_file(fname):
if code is None:
# That didn't work, so try it as normal source code
with io.open_code(code_path) as f:
code = compile(f.read(), fname, 'exec')
code = compile(f.read(), fname, 'exec', module=module)
return code

def run_path(path_name, init_globals=None, run_name=None):
Expand All @@ -290,7 +288,7 @@ def run_path(path_name, init_globals=None, run_name=None):
if isinstance(importer, type(None)):
# Not a valid sys.path entry, so run the code directly
# execfile() doesn't help as we want to allow compiled files
code = _get_code_from_file(path_name)
code = _get_code_from_file(path_name, run_name)
return _run_module_code(code, init_globals, run_name,
pkg_name=pkg_name, script_name=path_name)
else:
Expand Down
94 changes: 94 additions & 0 deletions Lib/test/audit-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import contextlib
import os
import sys
import unittest.mock
from test.support import swap_item


class TestHook:
Expand Down Expand Up @@ -206,6 +208,16 @@ def rl(name):
else:
return None

try:
import _remote_debugging
except ImportError:
_remote_debugging = None

def rd(name):
if _remote_debugging:
return getattr(_remote_debugging, name, None)
return None

# Try a range of "open" functions.
# All of them should fail
with TestHook(raise_on_events={"open"}) as hook:
Expand All @@ -223,6 +235,8 @@ def rl(name):
(rl("append_history_file"), 0, None),
(rl("read_init_file"), testfn),
(rl("read_init_file"), None),
(rd("BinaryWriter"), testfn, 1000, 0),
(rd("BinaryReader"), testfn),
]:
if not fn:
continue
Expand Down Expand Up @@ -256,6 +270,8 @@ def rl(name):
("~/.history", "a") if rl("append_history_file") else None,
(testfn, "r") if readline else None,
("<readline_init_file>", "r") if readline else None,
(testfn, "wb") if rd("BinaryWriter") else None,
(testfn, "rb") if rd("BinaryReader") else None,
]
if i is not None
],
Expand Down Expand Up @@ -672,6 +688,84 @@ def hook(event, args):
assertEqual(event_script_path, tmp_file.name)
assertEqual(remote_event_script_path, tmp_file.name)

def test_import_module():
import importlib

with TestHook() as hook:
importlib.import_module("importlib") # already imported, won't get logged
importlib.import_module("email") # standard library module
importlib.import_module("pythoninfo") # random module
importlib.import_module(".audit_test_data.submodule", "test") # relative import
importlib.import_module("test.audit_test_data.submodule2") # absolute import
importlib.import_module("_testcapi") # extension module

actual = [a for e, a in hook.seen if e == "import"]
assertSequenceEqual(
[
("email", None, sys.path, sys.meta_path, sys.path_hooks),
("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks),
("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
("_testcapi", unittest.mock.ANY, None, None, None)
],
actual,
)

def test_builtin__import__():
import importlib # noqa: F401

with TestHook() as hook:
__import__("importlib")
__import__("email")
__import__("pythoninfo")
__import__("audit_test_data.submodule", level=1, globals={"__package__": "test"})
__import__("test.audit_test_data.submodule2")
__import__("_testcapi")

actual = [a for e, a in hook.seen if e == "import"]
assertSequenceEqual(
[
("email", None, sys.path, sys.meta_path, sys.path_hooks),
("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks),
("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
("_testcapi", unittest.mock.ANY, None, None, None)
],
actual,
)

def test_import_statement():
import importlib # noqa: F401
# Set __package__ so relative imports work
with swap_item(globals(), "__package__", "test"):
with TestHook() as hook:
import importlib # noqa: F401
import email # noqa: F401
import pythoninfo # noqa: F401
from .audit_test_data import submodule # noqa: F401
import test.audit_test_data.submodule2 # noqa: F401
import _testcapi # noqa: F401

actual = [a for e, a in hook.seen if e == "import"]
# Import statement ordering is different because the package is
# loaded first and then the submodule
assertSequenceEqual(
[
("email", None, sys.path, sys.meta_path, sys.path_hooks),
("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks),
("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks),
("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
("_testcapi", unittest.mock.ANY, None, None, None)
],
actual,
)

if __name__ == "__main__":
from test.support import suppress_msvcrt_asserts

Expand Down
Empty file.
Empty file.
Empty file.
13 changes: 12 additions & 1 deletion Lib/test/test_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ def test_posixsubprocess(self):
import_helper.import_module("_posixsubprocess")
self.do_test("test_posixsubprocess")

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_excepthook(self):
returncode, events, stderr = self.run_python("test_excepthook")
if not returncode:
Expand Down Expand Up @@ -345,5 +344,17 @@ def test_sys_remote_exec(self):
if returncode:
self.fail(stderr)

@unittest.expectedFailureIf(__import__("importlib.util", fromlist=["find_spec"]).find_spec("_testcapi") is None, "TODO: RUSTPYTHON; requires the absent _testcapi module for extension import audit events; recheck when discoverable")
def test_import_module(self):
self.do_test("test_import_module")

@unittest.expectedFailureIf(__import__("importlib.util", fromlist=["find_spec"]).find_spec("_testcapi") is None, "TODO: RUSTPYTHON; requires the absent _testcapi module for extension import audit events; recheck when discoverable")
def test_builtin__import__(self):
self.do_test("test_builtin__import__")

@unittest.expectedFailureIf(__import__("importlib.util", fromlist=["find_spec"]).find_spec("_testcapi") is None, "TODO: RUSTPYTHON; requires the absent _testcapi module for extension import audit events; recheck when discoverable")
def test_import_statement(self):
self.do_test("test_import_statement")

if __name__ == "__main__":
unittest.main()
Loading
Loading