Skip to content

Commit ddf11d4

Browse files
Merge branch 'main' into gh-131357-some-extra-tests-for-empty-bytes
2 parents 6aa938b + 979d81a commit ddf11d4

File tree

15 files changed

+196
-114
lines changed

15 files changed

+196
-114
lines changed

.github/workflows/build.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ permissions:
1515
contents: read
1616

1717
concurrency:
18-
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}-reusable
18+
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#concurrency
19+
# 'group' must be a key uniquely representing a PR or push event.
20+
# github.workflow is the workflow name
21+
# github.actor is the user invoking the workflow
22+
# github.head_ref is the source branch of the PR or otherwise blank
23+
# github.run_id is a unique number for the current run
24+
group: ${{ github.workflow }}-${{ github.actor }}-${{ github.head_ref || github.run_id }}
1925
cancel-in-progress: true
2026

2127
env:

Include/internal/pycore_pyerrors.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ extern void _PyErr_Fetch(
9494
PyObject **value,
9595
PyObject **traceback);
9696

97-
extern PyObject* _PyErr_GetRaisedException(PyThreadState *tstate);
97+
PyAPI_FUNC(PyObject*) _PyErr_GetRaisedException(PyThreadState *tstate);
9898

9999
PyAPI_FUNC(int) _PyErr_ExceptionMatches(
100100
PyThreadState *tstate,
101101
PyObject *exc);
102102

103-
extern void _PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc);
103+
PyAPI_FUNC(void) _PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc);
104104

105105
extern void _PyErr_Restore(
106106
PyThreadState *tstate,

Lib/_pyrepl/_module_completer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818

1919
def make_default_module_completer() -> ModuleCompleter:
20-
# Inside pyrepl, __package__ is set to '_pyrepl'
21-
return ModuleCompleter(namespace={'__package__': '_pyrepl'})
20+
# Inside pyrepl, __package__ is set to None by default
21+
return ModuleCompleter(namespace={'__package__': None})
2222

2323

2424
class ModuleCompleter:

Lib/_pyrepl/main.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import errno
22
import os
33
import sys
4+
import types
45

56

67
CAN_USE_PYREPL: bool
@@ -29,12 +30,10 @@ def interactive_console(mainmodule=None, quiet=False, pythonstartup=False):
2930
print(FAIL_REASON, file=sys.stderr)
3031
return sys._baserepl()
3132

32-
if mainmodule:
33-
namespace = mainmodule.__dict__
34-
else:
35-
import __main__
36-
namespace = __main__.__dict__
37-
namespace.pop("__pyrepl_interactive_console", None)
33+
if not mainmodule:
34+
mainmodule = types.ModuleType("__main__")
35+
36+
namespace = mainmodule.__dict__
3837

3938
# sys._baserepl() above does this internally, we do it here
4039
startup_path = os.getenv("PYTHONSTARTUP")

Lib/_pyrepl/readline.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ def _setup(namespace: Mapping[str, Any]) -> None:
606606
# set up namespace in rlcompleter, which requires it to be a bona fide dict
607607
if not isinstance(namespace, dict):
608608
namespace = dict(namespace)
609+
_wrapper.config.module_completer = ModuleCompleter(namespace)
609610
_wrapper.config.readline_completer = RLCompleter(namespace).complete
610611

611612
# this is not really what readline.c does. Better than nothing I guess

Lib/ipaddress.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ def __eq__(self, other):
729729
return NotImplemented
730730

731731
def __hash__(self):
732-
return hash(int(self.network_address) ^ int(self.netmask))
732+
return hash((int(self.network_address), int(self.netmask)))
733733

734734
def __contains__(self, other):
735735
# always false if one is v4 and the other is v6.

Lib/test/support/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,12 +2929,6 @@ def make_clean_env() -> dict[str, str]:
29292929
return clean_env
29302930

29312931

2932-
def initialized_with_pyrepl():
2933-
"""Detect whether PyREPL was used during Python initialization."""
2934-
# If the main module has a __file__ attribute it's a Python module, which means PyREPL.
2935-
return hasattr(sys.modules["__main__"], "__file__")
2936-
2937-
29382932
WINDOWS_STATUS = {
29392933
0xC0000005: "STATUS_ACCESS_VIOLATION",
29402934
0xC00000FD: "STATUS_STACK_OVERFLOW",

Lib/test/test__interpreters.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,7 @@ def test_closure(self):
10541054
def script():
10551055
assert spam
10561056

1057-
with self.assertRaises(ValueError):
1057+
with self.assertRaises(TypeError):
10581058
_interpreters.run_func(self.id, script)
10591059

10601060
# XXX This hasn't been fixed yet.
@@ -1065,6 +1065,7 @@ def script():
10651065
with self.assertRaises(ValueError):
10661066
_interpreters.run_func(self.id, script)
10671067

1068+
@unittest.skip("we're not quite there yet")
10681069
def test_args(self):
10691070
with self.subTest('args'):
10701071
def script(a, b=0):

Lib/test/test_ipaddress.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,6 +2762,34 @@ def testV6HashIsNotConstant(self):
27622762
ipv6_address2 = ipaddress.IPv6Interface("2001:658:22a:cafe:200:0:0:2")
27632763
self.assertNotEqual(ipv6_address1.__hash__(), ipv6_address2.__hash__())
27642764

2765+
# issue 134062 Hash collisions in IPv4Network and IPv6Network
2766+
def testNetworkV4HashCollisions(self):
2767+
self.assertNotEqual(
2768+
ipaddress.IPv4Network("192.168.1.255/32").__hash__(),
2769+
ipaddress.IPv4Network("192.168.1.0/24").__hash__()
2770+
)
2771+
self.assertNotEqual(
2772+
ipaddress.IPv4Network("172.24.255.0/24").__hash__(),
2773+
ipaddress.IPv4Network("172.24.0.0/16").__hash__()
2774+
)
2775+
self.assertNotEqual(
2776+
ipaddress.IPv4Network("192.168.1.87/32").__hash__(),
2777+
ipaddress.IPv4Network("192.168.1.86/31").__hash__()
2778+
)
2779+
2780+
# issue 134062 Hash collisions in IPv4Network and IPv6Network
2781+
def testNetworkV6HashCollisions(self):
2782+
self.assertNotEqual(
2783+
ipaddress.IPv6Network("fe80::/64").__hash__(),
2784+
ipaddress.IPv6Network("fe80::ffff:ffff:ffff:0/112").__hash__()
2785+
)
2786+
self.assertNotEqual(
2787+
ipaddress.IPv4Network("10.0.0.0/8").__hash__(),
2788+
ipaddress.IPv6Network(
2789+
"ffff:ffff:ffff:ffff:ffff:ffff:aff:0/112"
2790+
).__hash__()
2791+
)
2792+
27652793

27662794
if __name__ == '__main__':
27672795
unittest.main()

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ def tearDown(self):
926926
def prepare_reader(self, events, namespace):
927927
console = FakeConsole(events)
928928
config = ReadlineConfig()
929+
config.module_completer = ModuleCompleter(namespace)
929930
config.readline_completer = rlcompleter.Completer(namespace).complete
930931
reader = ReadlineAlikeReader(console=console, config=config)
931932
return reader
@@ -1022,13 +1023,15 @@ def test_builtin_completion_top_level(self):
10221023

10231024
def test_relative_import_completions(self):
10241025
cases = (
1025-
("from .readl\t\n", "from .readline"),
1026-
("from . import readl\t\n", "from . import readline"),
1026+
(None, "from .readl\t\n", "from .readl"),
1027+
(None, "from . import readl\t\n", "from . import readl"),
1028+
("_pyrepl", "from .readl\t\n", "from .readline"),
1029+
("_pyrepl", "from . import readl\t\n", "from . import readline"),
10271030
)
1028-
for code, expected in cases:
1031+
for package, code, expected in cases:
10291032
with self.subTest(code=code):
10301033
events = code_to_events(code)
1031-
reader = self.prepare_reader(events, namespace={})
1034+
reader = self.prepare_reader(events, namespace={"__package__": package})
10321035
output = reader.readline()
10331036
self.assertEqual(output, expected)
10341037

@@ -1397,7 +1400,7 @@ def _assertMatchOK(
13971400
)
13981401

13991402
@force_not_colorized
1400-
def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False):
1403+
def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False, pythonstartup=False):
14011404
clean_env = make_clean_env()
14021405
clean_env["NO_COLOR"] = "1" # force_not_colorized doesn't touch subprocesses
14031406

@@ -1406,9 +1409,13 @@ def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False
14061409
blue.mkdir()
14071410
mod = blue / "calx.py"
14081411
mod.write_text("FOO = 42", encoding="utf-8")
1412+
startup = blue / "startup.py"
1413+
startup.write_text("BAR = 64", encoding="utf-8")
14091414
commands = [
14101415
"print(f'^{" + var + "=}')" for var in expectations
14111416
] + ["exit()"]
1417+
if pythonstartup:
1418+
clean_env["PYTHONSTARTUP"] = str(startup)
14121419
if as_file and as_module:
14131420
self.fail("as_file and as_module are mutually exclusive")
14141421
elif as_file:
@@ -1427,7 +1434,13 @@ def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False
14271434
skip=True,
14281435
)
14291436
else:
1430-
self.fail("Choose one of as_file or as_module")
1437+
output, exit_code = self.run_repl(
1438+
commands,
1439+
cmdline_args=[],
1440+
env=clean_env,
1441+
cwd=td,
1442+
skip=True,
1443+
)
14311444

14321445
self.assertEqual(exit_code, 0)
14331446
for var, expected in expectations.items():
@@ -1440,6 +1453,23 @@ def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False
14401453
self.assertNotIn("Exception", output)
14411454
self.assertNotIn("Traceback", output)
14421455

1456+
def test_globals_initialized_as_default(self):
1457+
expectations = {
1458+
"__name__": "'__main__'",
1459+
"__package__": "None",
1460+
# "__file__" is missing in -i, like in the basic REPL
1461+
}
1462+
self._run_repl_globals_test(expectations)
1463+
1464+
def test_globals_initialized_from_pythonstartup(self):
1465+
expectations = {
1466+
"BAR": "64",
1467+
"__name__": "'__main__'",
1468+
"__package__": "None",
1469+
# "__file__" is missing in -i, like in the basic REPL
1470+
}
1471+
self._run_repl_globals_test(expectations, pythonstartup=True)
1472+
14431473
def test_inspect_keeps_globals_from_inspected_file(self):
14441474
expectations = {
14451475
"FOO": "42",
@@ -1449,6 +1479,16 @@ def test_inspect_keeps_globals_from_inspected_file(self):
14491479
}
14501480
self._run_repl_globals_test(expectations, as_file=True)
14511481

1482+
def test_inspect_keeps_globals_from_inspected_file_with_pythonstartup(self):
1483+
expectations = {
1484+
"FOO": "42",
1485+
"BAR": "64",
1486+
"__name__": "'__main__'",
1487+
"__package__": "None",
1488+
# "__file__" is missing in -i, like in the basic REPL
1489+
}
1490+
self._run_repl_globals_test(expectations, as_file=True, pythonstartup=True)
1491+
14521492
def test_inspect_keeps_globals_from_inspected_module(self):
14531493
expectations = {
14541494
"FOO": "42",
@@ -1458,26 +1498,32 @@ def test_inspect_keeps_globals_from_inspected_module(self):
14581498
}
14591499
self._run_repl_globals_test(expectations, as_module=True)
14601500

1501+
def test_inspect_keeps_globals_from_inspected_module_with_pythonstartup(self):
1502+
expectations = {
1503+
"FOO": "42",
1504+
"BAR": "64",
1505+
"__name__": "'__main__'",
1506+
"__package__": "'blue'",
1507+
"__file__": re.compile(r"^'.*calx.py'$"),
1508+
}
1509+
self._run_repl_globals_test(expectations, as_module=True, pythonstartup=True)
1510+
14611511
@force_not_colorized
14621512
def test_python_basic_repl(self):
14631513
env = os.environ.copy()
1464-
commands = ("from test.support import initialized_with_pyrepl\n"
1465-
"initialized_with_pyrepl()\n"
1466-
"exit()\n")
1467-
1514+
pyrepl_commands = "clear\nexit()\n"
14681515
env.pop("PYTHON_BASIC_REPL", None)
1469-
output, exit_code = self.run_repl(commands, env=env, skip=True)
1516+
output, exit_code = self.run_repl(pyrepl_commands, env=env, skip=True)
14701517
self.assertEqual(exit_code, 0)
1471-
self.assertIn("True", output)
1472-
self.assertNotIn("False", output)
14731518
self.assertNotIn("Exception", output)
1519+
self.assertNotIn("NameError", output)
14741520
self.assertNotIn("Traceback", output)
14751521

1522+
basic_commands = "help\nexit()\n"
14761523
env["PYTHON_BASIC_REPL"] = "1"
1477-
output, exit_code = self.run_repl(commands, env=env)
1524+
output, exit_code = self.run_repl(basic_commands, env=env)
14781525
self.assertEqual(exit_code, 0)
1479-
self.assertIn("False", output)
1480-
self.assertNotIn("True", output)
1526+
self.assertIn("Type help() for interactive help", output)
14811527
self.assertNotIn("Exception", output)
14821528
self.assertNotIn("Traceback", output)
14831529

0 commit comments

Comments
 (0)