Skip to content
Merged
1 change: 1 addition & 0 deletions Doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
('c:type', '__int64'),
('c:type', 'unsigned __int64'),
('c:type', 'double'),
('c:type', '_Float16'),
# Standard C structures
('c:struct', 'in6_addr'),
('c:struct', 'in_addr'),
Expand Down
6 changes: 4 additions & 2 deletions Doc/library/struct.rst
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ platform-dependent.
+--------+--------------------------+--------------------+----------------+------------+
| ``N`` | :c:type:`size_t` | integer | | \(3) |
+--------+--------------------------+--------------------+----------------+------------+
| ``e`` | \(6) | float | 2 | \(4) |
| ``e`` | :c:expr:`_Float16` | float | 2 | \(4), \(6) |
+--------+--------------------------+--------------------+----------------+------------+
| ``f`` | :c:expr:`float` | float | 4 | \(4) |
+--------+--------------------------+--------------------+----------------+------------+
Expand Down Expand Up @@ -328,7 +328,9 @@ Notes:
revision of the `IEEE 754 standard <ieee 754 standard_>`_. It has a sign
bit, a 5-bit exponent and 11-bit precision (with 10 bits explicitly stored),
and can represent numbers between approximately ``6.1e-05`` and ``6.5e+04``
at full precision. This type is not widely supported by C compilers: on a
at full precision. This type is not widely supported by C compilers:
it's available as :c:expr:`_Float16` type, if the compiler supports the Annex H
of the C23 standard. On a
typical machine, an unsigned short can be used for storage, but not for math
operations. See the Wikipedia page on the `half-precision floating-point
format <half precision format_>`_ for more information.
Expand Down
11 changes: 1 addition & 10 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,16 +249,7 @@ static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) {

PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);

static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
uintptr_t here_addr = _Py_get_machine_stack_pointer();
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
assert(_tstate->c_stack_hard_limit != 0);
#if _Py_STACK_GROWS_DOWN
return here_addr <= _tstate->c_stack_soft_limit;
#else
return here_addr >= _tstate->c_stack_soft_limit;
#endif
}
PyAPI_FUNC(int) _Py_ReachedRecursionLimit(PyThreadState *tstate);

// Export for test_peg_generator
PyAPI_FUNC(int) _Py_ReachedRecursionLimitWithMargin(
Expand Down
3 changes: 2 additions & 1 deletion Lib/ensurepip/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
# policies recommend against bundling dependencies. For example, Fedora
# installs wheel packages in the /usr/share/python-wheels/ directory and don't
# install the ensurepip._bundled package.
if (_pkg_dir := sysconfig.get_config_var('WHEEL_PKG_DIR')) is not None:
_pkg_dir = sysconfig.get_config_var('WHEEL_PKG_DIR')
if _pkg_dir:
_WHEEL_PKG_DIR = Path(_pkg_dir).resolve()
else:
_WHEEL_PKG_DIR = None
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_ensurepip.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import unittest
import unittest.mock
from pathlib import Path
from test.support import import_helper

import ensurepip
import ensurepip._uninstall
Expand Down Expand Up @@ -36,6 +37,15 @@ def test_version_no_dir(self):
# when the bundled pip wheel is used, we get _PIP_VERSION
self.assertEqual(ensurepip._PIP_VERSION, ensurepip.version())

def test_wheel_pkg_dir_none(self):
# gh-146310: empty or None WHEEL_PKG_DIR should not search CWD
for value in ('', None):
with unittest.mock.patch('sysconfig.get_config_var',
return_value=value) as get_config_var:
module = import_helper.import_fresh_module('ensurepip')
self.assertIsNone(module._WHEEL_PKG_DIR)
get_config_var.assert_called_once_with('WHEEL_PKG_DIR')

def test_selected_wheel_path_no_dir(self):
pip_filename = f'pip-{ensurepip._PIP_VERSION}-py3-none-any.whl'
with unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', None):
Expand Down
23 changes: 19 additions & 4 deletions Lib/test/test_lazy_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,8 @@ def my_filter(name):
sys.set_lazy_imports_filter(my_filter)
self.assertIs(sys.get_lazy_imports_filter(), my_filter)

def test_lazy_modules_attribute_is_set(self):
"""sys.lazy_modules should be a set per PEP 810."""
def test_lazy_modules_attribute_is_dict(self):
"""sys.lazy_modules should be a dict per PEP 810."""
self.assertIsInstance(sys.lazy_modules, dict)

@support.requires_subprocess()
Expand Down Expand Up @@ -966,9 +966,24 @@ def test_module_added_to_lazy_modules_on_lazy_import(self):

def test_lazy_modules_is_per_interpreter(self):
"""Each interpreter should have independent sys.lazy_modules."""
# Basic test that sys.lazy_modules exists and is a set
# Basic test that sys.lazy_modules exists and is a dict
self.assertIsInstance(sys.lazy_modules, dict)

def test_lazy_module_without_children_is_tracked(self):
code = textwrap.dedent("""
import sys
lazy import json
assert "json" in sys.lazy_modules, (
f"expected 'json' in sys.lazy_modules, got {set(sys.lazy_modules)}"
)
assert sys.lazy_modules["json"] == set(), (
f"expected empty set for sys.lazy_modules['json'], "
f"got {sys.lazy_modules['json']!r}"
)
print("OK")
""")
assert_python_ok("-c", code)


@support.requires_subprocess()
class CommandLineAndEnvVarTests(unittest.TestCase):
Expand Down Expand Up @@ -1575,7 +1590,7 @@ def access_modules(idx):
self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}")
self.assertIn("OK", result.stdout)

def test_concurrent_lazy_modules_set_updates(self):
def test_concurrent_lazy_modules_dict_updates(self):
"""Multiple threads creating lazy imports should safely update sys.lazy_modules."""
code = textwrap.dedent("""
import sys
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed ``sys.lazy_modules`` to include lazy modules without submodules. Patch by Bartosz Sławecki.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Set frame pointers in ``aarch64-unknown-linux-gnu`` JIT code, allowing most native profilers and debuggers to unwind through them. Patch by Diego Russo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The :mod:`ensurepip` module no longer looks for ``pip-*.whl`` wheel packages
in the current directory.
13 changes: 13 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,19 @@ _PyEval_GetIter(_PyStackRef iterable, _PyStackRef *index_or_null, int yield_from
return PyStackRef_FromPyObjectSteal(iter_o);
}

Py_NO_INLINE int
_Py_ReachedRecursionLimit(PyThreadState *tstate) {
uintptr_t here_addr = _Py_get_machine_stack_pointer();
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
assert(_tstate->c_stack_hard_limit != 0);
#if _Py_STACK_GROWS_DOWN
return here_addr <= _tstate->c_stack_soft_limit;
#else
return here_addr >= _tstate->c_stack_soft_limit;
#endif
}


#if (defined(__GNUC__) && __GNUC__ >= 10 && !defined(__clang__)) && defined(__x86_64__)
/*
* gh-129987: The SLP autovectorizer can cause poor code generation for
Expand Down
6 changes: 6 additions & 0 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -4377,6 +4377,12 @@ register_lazy_on_parent(PyThreadState *tstate, PyObject *name,
Py_ssize_t dot = PyUnicode_FindChar(name, '.', 0,
PyUnicode_GET_LENGTH(name), -1);
if (dot < 0) {
PyObject *lazy_submodules = ensure_lazy_submodules(
(PyDictObject *)lazy_modules, name);
if (lazy_submodules == NULL) {
goto done;
}
Py_DECREF(lazy_submodules);
ret = 0;
goto done;
}
Expand Down
17 changes: 16 additions & 1 deletion Tools/jit/_optimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ class Optimizer:
label_prefix: str
symbol_prefix: str
re_global: re.Pattern[str]
frame_pointers: bool
# The first block in the linked list:
_root: _Block = dataclasses.field(init=False, default_factory=_Block)
_labels: dict[str, _Block] = dataclasses.field(init=False, default_factory=dict)
Expand Down Expand Up @@ -193,6 +194,7 @@ class Optimizer:
_re_small_const_1 = _RE_NEVER_MATCH
_re_small_const_2 = _RE_NEVER_MATCH
const_reloc = "<Not supported>"
_frame_pointer_modify: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH

def __post_init__(self) -> None:
# Split the code into a linked list of basic blocks. A basic block is an
Expand Down Expand Up @@ -553,6 +555,16 @@ def _small_const_2(self, inst: Instruction) -> tuple[str, Instruction | None]:
def _small_consts_match(self, inst1: Instruction, inst2: Instruction) -> bool:
raise NotImplementedError()

def _validate(self) -> None:
for block in self._blocks():
if not block.instructions:
continue
for inst in block.instructions:
if self.frame_pointers:
assert (
self._frame_pointer_modify.match(inst.text) is None
), "Frame pointer should not be modified"

def run(self) -> None:
"""Run this optimizer."""
self._insert_continue_label()
Expand All @@ -565,6 +577,7 @@ def run(self) -> None:
self._remove_unreachable()
self._fixup_external_labels()
self._fixup_constants()
self._validate()
self.path.write_text(self._body())


Expand Down Expand Up @@ -595,6 +608,7 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods
r"\s*(?P<instruction>ldr)\s+.*(?P<value>_JIT_OP(ARG|ERAND(0|1))_(16|32)).*"
)
const_reloc = "CUSTOM_AARCH64_CONST"
_frame_pointer_modify = re.compile(r"\s*stp\s+x29.*")

def _get_reg(self, inst: Instruction) -> str:
_, rest = inst.text.split(inst.name)
Expand Down Expand Up @@ -649,4 +663,5 @@ class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods
# https://www.felixcloutier.com/x86/jmp
_re_jump = re.compile(r"\s*jmp\s+(?P<target>[\w.]+)")
# https://www.felixcloutier.com/x86/ret
_re_return = re.compile(r"\s*ret\b")
_re_return = re.compile(r"\s*retq?\b")
_frame_pointer_modify = re.compile(r"\s*movq?\s+%(\w+),\s+%rbp.*")
21 changes: 13 additions & 8 deletions Tools/jit/_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,21 +176,24 @@ async def _compile(
f"{s}",
f"{c}",
]
is_shim = opname == "shim"
if self.frame_pointers:
frame_pointer = "all" if opname == "shim" else "reserved"
frame_pointer = "all" if is_shim else "reserved"
args_s += ["-Xclang", f"-mframe-pointer={frame_pointer}"]
args_s += self.args
# Allow user-provided CFLAGS to override any defaults
args_s += shlex.split(self.cflags)
await _llvm.run(
"clang", args_s, echo=self.verbose, llvm_version=self.llvm_version
)
self.optimizer(
s,
label_prefix=self.label_prefix,
symbol_prefix=self.symbol_prefix,
re_global=self.re_global,
).run()
if not is_shim:
self.optimizer(
s,
label_prefix=self.label_prefix,
symbol_prefix=self.symbol_prefix,
re_global=self.re_global,
frame_pointers=self.frame_pointers,
).run()
args_o = [f"--target={self.triple}", "-c", "-o", f"{o}", f"{s}"]
await _llvm.run(
"clang", args_o, echo=self.verbose, llvm_version=self.llvm_version
Expand Down Expand Up @@ -593,7 +596,9 @@ def get_target(host: str) -> _COFF32 | _COFF64 | _ELF | _MachO:
# -mno-outline-atomics: Keep intrinsics from being emitted.
args = ["-fpic", "-mno-outline-atomics", "-fno-plt"]
optimizer = _optimizers.OptimizerAArch64
target = _ELF(host, condition, args=args, optimizer=optimizer)
target = _ELF(
host, condition, args=args, optimizer=optimizer, frame_pointers=True
)
elif re.fullmatch(r"i686-pc-windows-msvc", host):
host = "i686-pc-windows-msvc"
condition = "defined(_M_IX86)"
Expand Down
Loading