Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions Apple/iOS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ Once you have a built an XCframework, you can test that framework by running:

$ python Apple test iOS

This test will attempt to find an "SE-class" simulator (i.e., an iPhone SE, or
iPhone 16e, or similar), and run the test suite on the most recent version of
iOS that is available. You can specify a simulator using the `--simulator`
command line argument, providing the name of the simulator (e.g., `--simulator
'iPhone 16 Pro'`). You can also use this argument to control the OS version used
for testing; `--simulator 'iPhone 16 Pro,OS=18.2'` would attempt to run the
tests on an iPhone 16 Pro running iOS 18.2.

If the test runner is executed on GitHub Actions, the `GITHUB_ACTIONS`
environment variable will be exposed to the iOS process at runtime.

### Testing a single-architecture framework

The `Apple/testbed` folder that contains an Xcode project that is able to run
Expand Down
3 changes: 3 additions & 0 deletions Apple/testbed/TestbedTests/TestbedTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ - (void)testPython {
setenv("NO_COLOR", "1", true);
setenv("PYTHON_COLORS", "0", true);

if (getenv("GITHUB_ACTIONS")) {
NSLog(@"Running in a GitHub Actions environment");
}
// Arguments to pass into the test suite runner.
// argv[0] must identify the process; any subsequent arg
// will be handled as if it were an argument to `python -m test`
Expand Down
9 changes: 9 additions & 0 deletions Apple/testbed/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import argparse
import json
import os
import re
import shutil
import subprocess
Expand Down Expand Up @@ -78,13 +79,21 @@ def xcode_test(location: Path, platform: str, simulator: str, verbose: bool):
check=True,
)

# Any environment variable prefixed with TEST_RUNNER_ is exposed into the
# test runner environment. There are some variables (like those identifying
# CI platforms) that can be useful to have access to.
test_env = os.environ.copy()
if "GITHUB_ACTIONS" in os.environ:
test_env["TEST_RUNNER_GITHUB_ACTIONS"] = os.environ["GITHUB_ACTIONS"]

print("Running test project...")
# Test execution *can't* be run -quiet; verbose mode
# is how we see the output of the test output.
process = subprocess.Popen(
["xcodebuild", "test-without-building"] + args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env=test_env,
)
while line := (process.stdout.readline()).decode(*DECODE_ARGS):
# Strip the timestamp/process prefix from each log line
Expand Down
5 changes: 4 additions & 1 deletion Lib/linecache.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,12 @@ def updatecache(filename, module_globals=None):
if _source_unavailable(filename):
return []

if filename.startswith('<frozen ') and module_globals is not None:
if filename.startswith('<frozen '):
# This is a frozen module, so we need to use the filename
# from the module globals.
if module_globals is None:
return []

fullname = module_globals.get('__file__')
if fullname is None:
return []
Expand Down
8 changes: 7 additions & 1 deletion Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3577,7 +3577,13 @@ def main():
parser.error("argument -m: not allowed with argument --pid")
try:
attach(opts.pid, opts.commands)
except PermissionError as e:
except RuntimeError:
print(
f"Cannot attach to pid {opts.pid}, please make sure that the process exists "
"and is using the same Python version."
)
sys.exit(1)
except PermissionError:
exit_with_permission_help_text()
return
elif opts.module:
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"BrokenIter",
"in_systemd_nspawn_sync_suppressed",
"run_no_yield_async_fn", "run_yielding_async_fn", "async_yield",
"reset_code",
"reset_code", "on_github_actions"
]


Expand Down Expand Up @@ -1369,6 +1369,7 @@ def reset_code(f: types.FunctionType) -> types.FunctionType:
f.__code__ = f.__code__.replace()
return f

on_github_actions = "GITHUB_ACTIONS" in os.environ

#=======================================================================
# Check for the presence of docstrings.
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -1601,6 +1601,26 @@ def __hash__(self):
with self.assertRaises(KeyError):
d.get(key2)

def test_clear_at_lookup(self):
class X:
def __hash__(self):
return 1
def __eq__(self, other):
nonlocal d
d.clear()

d = {}
for _ in range(10):
d[X()] = None

self.assertEqual(len(d), 1)

d = {}
for _ in range(10):
d.setdefault(X(), None)

self.assertEqual(len(d), 1)


class CAPITest(unittest.TestCase):

Expand Down
12 changes: 12 additions & 0 deletions Lib/test/test_remote_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1590,5 +1590,17 @@ def test_attach_to_process_with_colors(self):
self.assertNotIn("while x == 1", output["client"]["stdout"])
self.assertIn("while x == 1", re.sub("\x1b[^m]*m", "", output["client"]["stdout"]))

def test_attach_to_non_existent_process(self):
with force_color(False):
result = subprocess.run([sys.executable, "-m", "pdb", "-p", "999999"], text=True, capture_output=True)
self.assertNotEqual(result.returncode, 0)
if sys.platform == "darwin":
# On MacOS, attaching to a non-existent process gives PermissionError
error = "The specified process cannot be attached to due to insufficient permissions"
else:
error = "Cannot attach to pid 999999, please make sure that the process exists"
self.assertIn(error, result.stdout)


if __name__ == "__main__":
unittest.main()
4 changes: 4 additions & 0 deletions Lib/test/test_socketserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,16 @@ def test_ForkingUDPServer(self):
self.dgram_examine)

@requires_unix_sockets
@unittest.skipIf(test.support.is_apple_mobile and test.support.on_github_actions,
"gh-140702: Test fails regularly on iOS simulator on GitHub Actions")
def test_UnixDatagramServer(self):
self.run_server(socketserver.UnixDatagramServer,
socketserver.DatagramRequestHandler,
self.dgram_examine)

@requires_unix_sockets
@unittest.skipIf(test.support.is_apple_mobile and test.support.on_github_actions,
"gh-140702: Test fails regularly on iOS simulator on GitHub Actions")
def test_ThreadingUnixDatagramServer(self):
self.run_server(socketserver.ThreadingUnixDatagramServer,
socketserver.DatagramRequestHandler,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed crash in :class:`dict` if :meth:`dict.clear` is called at the lookup
stage. Patch by Mikhail Efimov and Inada Naoki.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Print clearer error message when using ``pdb`` to attach to a non-existing process.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Avoid making unnecessary filesystem calls for frozen modules in :mod:`linecache` when the global module cache is not present.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The iOS testbed app will now expose the ``GITHUB_ACTIONS`` environment
variable to iOS apps being tested.
82 changes: 32 additions & 50 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,14 @@ static inline int
insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
Py_hash_t hash, PyObject *key, PyObject *value)
{
// gh-140551: If dict was cleared in _Py_dict_lookup,
// we have to resize one more time to force general key kind.
if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) {
if (insertion_resize(mp, 0) < 0)
return -1;
assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
}

if (mp->ma_keys->dk_usable <= 0) {
/* Need to resize. */
if (insertion_resize(mp, 1) < 0) {
Expand Down Expand Up @@ -1871,38 +1879,31 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
PyObject *key, Py_hash_t hash, PyObject *value)
{
PyObject *old_value;
Py_ssize_t ix;

ASSERT_DICT_LOCKED(mp);

if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) {
if (insertion_resize(mp, 0) < 0)
goto Fail;
assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
}

if (_PyDict_HasSplitTable(mp)) {
Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash);
if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) {
ix = insert_split_key(mp->ma_keys, key, hash);
if (ix != DKIX_EMPTY) {
insert_split_value(interp, mp, key, value, ix);
Py_DECREF(key);
Py_DECREF(value);
return 0;
}

/* No space in shared keys. Resize and continue below. */
if (insertion_resize(mp, 1) < 0) {
// No space in shared keys. Go to insert_combined_dict() below.
}
else {
ix = _Py_dict_lookup(mp, key, hash, &old_value);
if (ix == DKIX_ERROR)
goto Fail;
}
}

Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value);
if (ix == DKIX_ERROR)
goto Fail;

if (ix == DKIX_EMPTY) {
assert(!_PyDict_HasSplitTable(mp));
/* Insert into new slot. */
assert(old_value == NULL);
// insert_combined_dict() will convert from non DICT_KEYS_GENERAL table
// into DICT_KEYS_GENERAL table if key is not Unicode.
// We don't convert it before _Py_dict_lookup because non-Unicode key
// may change generic table into Unicode table.
if (insert_combined_dict(interp, mp, hash, key, value) < 0) {
goto Fail;
}
Expand Down Expand Up @@ -4374,6 +4375,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
PyDictObject *mp = (PyDictObject *)d;
PyObject *value;
Py_hash_t hash;
Py_ssize_t ix;
PyInterpreterState *interp = _PyInterpreterState_GET();

ASSERT_DICT_LOCKED(d);
Expand Down Expand Up @@ -4409,17 +4411,8 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
return 0;
}

if (!PyUnicode_CheckExact(key) && DK_IS_UNICODE(mp->ma_keys)) {
if (insertion_resize(mp, 0) < 0) {
if (result) {
*result = NULL;
}
return -1;
}
}

if (_PyDict_HasSplitTable(mp)) {
Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash);
if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) {
ix = insert_split_key(mp->ma_keys, key, hash);
if (ix != DKIX_EMPTY) {
PyObject *value = mp->ma_values->values[ix];
int already_present = value != NULL;
Expand All @@ -4432,27 +4425,22 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
}
return already_present;
}

/* No space in shared keys. Resize and continue below. */
if (insertion_resize(mp, 1) < 0) {
goto error;
}
// No space in shared keys. Go to insert_combined_dict() below.
}

assert(!_PyDict_HasSplitTable(mp));

Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &value);
if (ix == DKIX_ERROR) {
if (result) {
*result = NULL;
else {
ix = _Py_dict_lookup(mp, key, hash, &value);
if (ix == DKIX_ERROR) {
if (result) {
*result = NULL;
}
return -1;
}
return -1;
}

if (ix == DKIX_EMPTY) {
assert(!_PyDict_HasSplitTable(mp));
value = default_value;

// See comment to this function in insertdict.
if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) {
Py_DECREF(key);
Py_DECREF(value);
Expand All @@ -4477,12 +4465,6 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
*result = incref_result ? Py_NewRef(value) : value;
}
return 1;

error:
if (result) {
*result = NULL;
}
return -1;
}

int
Expand Down
Loading