Skip to content

Commit 86f7183

Browse files
authored
Merge branch 'main' into fix-149738
2 parents 5489c45 + 27ebd9a commit 86f7183

40 files changed

Lines changed: 2836 additions & 2456 deletions

Doc/library/site.rst

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,79 @@ Module contents
356356
This function used to be called unconditionally.
357357

358358

359-
.. function:: addsitedir(sitedir, known_paths=None, *, defer_processing_start_files=False)
359+
.. function:: makepath(*paths)
360+
361+
Join *paths* with :func:`os.path.join`, attempt to make the result
362+
absolute with :func:`os.path.abspath`, and return a 2-tuple containing
363+
the absolute path and its case-normalized form as produced by
364+
:func:`os.path.normcase`. If :func:`os.path.abspath` raises
365+
:exc:`OSError`, the joined path is used unchanged for the
366+
case-normalization step.
367+
368+
The second element of the returned tuple is the form used throughout the
369+
:mod:`!site` module to compare paths on case-insensitive file systems, and
370+
is what populates the ``known_paths`` sets that prevent duplicate
371+
:data:`sys.path` entries in various APIs within this module.
372+
373+
374+
.. class:: StartupState(known_paths=None)
375+
376+
Instances of this class accumulate interpreter startup configuration data
377+
from one or more site directories. They are the preferred interface for
378+
batching the processing of :file:`.pth` and :file:`.start` files across
379+
multiple site directories, so that every :data:`sys.path` extension is
380+
visible before any startup code runs.
381+
382+
The optional *known_paths* argument is a set of case-normalized paths
383+
(which can be produced by :func:`makepath`) used to prevent duplicate
384+
:data:`sys.path` entries. When ``None`` (the default), the set is built
385+
from the current :data:`sys.path`. :func:`main` implicitly uses an
386+
instance of this class.
387+
388+
Typical use:
389+
390+
.. code-block:: python
391+
392+
state = site.StartupState()
393+
for sitedir in site_dirs:
394+
state.addsitedir(sitedir)
395+
state.process()
396+
397+
.. versionadded:: 3.15
398+
399+
.. method:: addsitedir(sitedir)
400+
401+
Read the :file:`.pth` and :file:`.start` files in *sitedir* and
402+
record their :data:`sys.path` extensions, deprecated :file:`.pth`
403+
``import`` lines, and :file:`.start` entry points on this state.
404+
The recorded data is not applied until :meth:`process` is called.
405+
406+
.. method:: addusersitepackages()
407+
408+
Add the per-user site-packages directory, if enabled and if it exists.
409+
The directory's startup data is accumulated for later processing by
410+
:meth:`process`.
411+
412+
.. method:: addsitepackages(prefixes=None)
413+
414+
Add global site-packages directories, computed from *prefixes* or from
415+
the global :data:`PREFIXES` when *prefixes* is ``None``. Each
416+
directory's startup data is accumulated for later processing by
417+
:meth:`process`.
418+
419+
.. method:: process()
420+
421+
Apply the accumulated state by first adding the path extensions to
422+
:data:`sys.path`, then executing the :file:`.start` file entry points
423+
and :file:`.pth` file ``import`` lines (:ref:`deprecated
424+
<site-pth-files>`).
425+
426+
This method is not idempotent and must not be called more than once
427+
on the same instance. Doing so will apply the accumulated state
428+
more than once, re-running entry points and ``import`` lines.
429+
430+
431+
.. function:: addsitedir(sitedir, known_paths=None)
360432

361433
Add a directory to sys.path and parse the :file:`.pth` and :file:`.start`
362434
files found in that directory. Typically used in :mod:`sitecustomize` or
@@ -366,17 +438,15 @@ Module contents
366438
used to prevent duplicate :data:`sys.path` entries. When ``None`` (the
367439
default), the set is built from the current :data:`sys.path`.
368440

369-
While :file:`.pth` and :file:`.start` files are always parsed, set
370-
*defer_processing_start_files* to ``True`` to prevent processing the
371-
startup data found in those files, so that you can process them explicitly
372-
(this is typically used by the :func:`main` function).
441+
For batched processing across multiple site directories, build a
442+
:class:`StartupState` explicitly and call :meth:`StartupState.addsitedir`
443+
on it; that defers :file:`.pth` and :file:`.start` processing until a
444+
single :meth:`StartupState.process` call, ensuring every :data:`sys.path`
445+
extension is visible before any startup code runs.
373446

374447
.. versionchanged:: 3.15
375448

376449
Also processes :file:`.start` files. See :ref:`site-start-files`.
377-
All :file:`.pth` and :file:`.start` files are now read and
378-
accumulated before any path extensions, ``import`` line execution,
379-
or entry point invocations take place.
380450

381451

382452
.. function:: getsitepackages()
@@ -447,4 +517,3 @@ value greater than 2 if there is an error.
447517
* :pep:`370` -- Per user site-packages directory
448518
* :pep:`829` -- Startup entry points and the deprecation of import lines in ``.pth`` files
449519
* :ref:`sys-path-init` -- The initialization of :data:`sys.path`.
450-

Doc/reference/import.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ Importing ``parent.one`` will implicitly execute ``parent/__init__.py`` and
122122
``parent.three`` will execute ``parent/two/__init__.py`` and
123123
``parent/three/__init__.py`` respectively.
124124

125+
A subdirectory inside a regular package that does not contain an
126+
``__init__.py`` file is treated as an implicit
127+
:ref:`namespace package <reference-namespace-package>` (a "namespace
128+
subpackage") rooted in that parent. See :pep:`420` for the underlying
129+
specification.
130+
125131

126132
.. _reference-namespace-package:
127133

@@ -153,6 +159,12 @@ physically located next to ``parent/two``. In this case, Python will create a
153159
namespace package for the top-level ``parent`` package whenever it or one of
154160
its subpackages is imported.
155161

162+
Namespace packages may also be nested inside a regular package. When the
163+
import system searches a regular package's ``__path__`` and encounters a
164+
subdirectory that does not contain an ``__init__.py`` file, that
165+
subdirectory becomes a :term:`portion` contributing to a namespace
166+
subpackage of the enclosing regular package.
167+
156168
See also :pep:`420` for the namespace package specification.
157169

158170

Doc/whatsnew/3.15.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,18 @@ matching :file:`.start` file is found, ``import`` lines in :file:`.pth` files
478478
are ignored. There is no change to :data:`sys.path` extension lines in
479479
:file:`.pth` files.
480480

481-
(Contributed by Barry Warsaw in :gh:`148641`.)
481+
The :mod:`site` module also provides :class:`site.StartupState` to batch
482+
startup processing for multiple site directories, ensuring all static path
483+
extensions are applied before any startup code is executed. :func:`site.main`
484+
uses an instance of this class implicitly to batch process all startup
485+
configuration files during normal interpreter startup. Callers needing the
486+
same batching behavior can build a :class:`~site.StartupState` directly and
487+
drive it with :meth:`~site.StartupState.addsitedir`,
488+
:meth:`~site.StartupState.addusersitepackages`, and
489+
:meth:`~site.StartupState.addsitepackages`, then call
490+
:meth:`~site.StartupState.process` once at the end of the batch.
491+
492+
(Contributed by Barry Warsaw in :gh:`148641` and :gh:`150228`.)
482493

483494

484495
.. _whatsnew315-abi3t:

Include/cpython/pystats.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ typedef struct _optimization_stats {
163163
uint64_t jit_code_size;
164164
uint64_t jit_trampoline_size;
165165
uint64_t jit_data_size;
166+
uint64_t jit_got_size;
166167
uint64_t jit_padding_size;
167168
uint64_t jit_freed_memory_size;
168169
uint64_t trace_total_memory_hist[_Py_UOP_HIST_SIZE];

Include/internal/pycore_interpframe.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ _PyFrame_GetLocalsArray(_PyInterpreterFrame *frame)
230230
static inline _PyStackRef*
231231
_PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
232232
{
233+
#ifndef _Py_JIT
233234
assert(frame->stackpointer != NULL);
235+
#endif
234236
_PyStackRef *sp = frame->stackpointer;
235237
#ifndef NDEBUG
236238
frame->stackpointer = NULL;
@@ -241,7 +243,10 @@ _PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
241243
static inline void
242244
_PyFrame_SetStackPointer(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
243245
{
246+
/* Avoid bloating the JIT code */
247+
#ifndef _Py_JIT
244248
assert(frame->stackpointer == NULL);
249+
#endif
245250
frame->stackpointer = stack_pointer;
246251
}
247252

Include/internal/pycore_jit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ _Py_CODEUNIT *_PyJIT_Entry(
3131
int _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size_t length);
3232
void _PyJIT_Free(_PyExecutorObject *executor);
3333
PyAPI_FUNC(int) _PyJIT_AddressInJitCode(PyInterpreterState *interp, uintptr_t addr);
34+
PyAPI_FUNC(void) _Py_jit_assert_within_stack_bounds(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int lineno);
35+
PyAPI_FUNC(int) _Py_jit_assertion_failure(int line);
3436

3537
#endif // _Py_JIT
3638

Include/internal/pycore_uop.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,7 @@ typedef struct _PyUOpInstruction{
3838

3939
// Fitness is the target length of the trace we translate initially. The uop
4040
// buffer has a small amount of extra space for entry/loop-closing overhead.
41-
#if defined(Py_DEBUG) && defined(_Py_JIT)
42-
// With asserts, the stencils are a lot larger
43-
#define FITNESS_INITIAL 1000
44-
#else
4541
#define FITNESS_INITIAL 2500
46-
#endif
4742

4843
#define UOP_TRACE_BUFFER_OVERHEAD 10
4944
#define UOP_MAX_TRACE_LENGTH (FITNESS_INITIAL + UOP_TRACE_BUFFER_OVERHEAD)

Lib/dis.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def _try_compile(source, name):
8484
return compile(source, name, 'exec')
8585

8686
def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
87-
show_offsets=False, show_positions=False):
87+
show_offsets=False, show_positions=False, show_jit=False):
8888
"""Disassemble classes, methods, functions, and other compiled objects.
8989
9090
With no argument, disassemble the last traceback.
@@ -95,7 +95,8 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
9595
"""
9696
if x is None:
9797
distb(file=file, show_caches=show_caches, adaptive=adaptive,
98-
show_offsets=show_offsets, show_positions=show_positions)
98+
show_offsets=show_offsets, show_positions=show_positions,
99+
show_jit=show_jit)
99100
return
100101
# Extract functions from methods.
101102
if hasattr(x, '__func__'):
@@ -116,12 +117,14 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
116117
if isinstance(x1, _have_code):
117118
print("Disassembly of %s:" % name, file=file)
118119
try:
119-
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
120+
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
121+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
120122
except TypeError as msg:
121123
print("Sorry:", msg, file=file)
122124
print(file=file)
123125
elif hasattr(x, 'co_code'): # Code object
124-
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
126+
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
127+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
125128
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
126129
labels_map = _make_labels_map(x)
127130
label_width = 4 + len(str(len(labels_map)))
@@ -132,12 +135,13 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
132135
arg_resolver = ArgResolver(labels_map=labels_map)
133136
_disassemble_bytes(x, arg_resolver=arg_resolver, formatter=formatter)
134137
elif isinstance(x, str): # Source code
135-
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
138+
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
139+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
136140
else:
137141
raise TypeError("don't know how to disassemble %s objects" %
138142
type(x).__name__)
139143

140-
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
144+
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
141145
"""Disassemble a traceback (default: last traceback)."""
142146
if tb is None:
143147
try:
@@ -148,7 +152,8 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets
148152
except AttributeError:
149153
raise RuntimeError("no last traceback to disassemble") from None
150154
while tb.tb_next: tb = tb.tb_next
151-
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
155+
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive,
156+
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
152157

153158
# The inspect module interrogates this dictionary to build its
154159
# list of CO_* constants. It is also used by pretty_flags to
@@ -216,14 +221,14 @@ def _deoptop(op):
216221
name = _all_opname[op]
217222
return _all_opmap[deoptmap[name]] if name in deoptmap else op
218223

219-
def _get_code_array(co, adaptive):
224+
def _get_code_array(co, adaptive, show_jit):
220225
if adaptive:
221226
code = co._co_code_adaptive
222227
res = []
223228
found = False
224229
for i in range(0, len(code), 2):
225230
op, arg = code[i], code[i+1]
226-
if op == ENTER_EXECUTOR:
231+
if op == ENTER_EXECUTOR and not show_jit:
227232
try:
228233
ex = get_executor(co, i)
229234
except (ValueError, RuntimeError):
@@ -656,7 +661,7 @@ def get_argval_argrepr(self, op, arg, offset):
656661
argrepr = 'not in' if argval else 'in'
657662
return argval, argrepr
658663

659-
def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
664+
def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False, show_jit=False):
660665
"""Iterator for the opcodes in methods, functions or code
661666
662667
Generates a series of Instruction named tuples giving the details of
@@ -679,7 +684,7 @@ def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
679684
names=co.co_names,
680685
varname_from_oparg=co._varname_from_oparg,
681686
labels_map=_make_labels_map(original_code))
682-
return _get_instructions_bytes(_get_code_array(co, adaptive),
687+
return _get_instructions_bytes(_get_code_array(co, adaptive, show_jit),
683688
linestarts=linestarts,
684689
line_offset=line_offset,
685690
co_positions=co.co_positions(),
@@ -792,6 +797,8 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N
792797
positions = Positions(*next(co_positions, ()))
793798
deop = _deoptop(op)
794799
op = code[offset]
800+
if op == ENTER_EXECUTOR:
801+
arg = code[offset+1]
795802

796803
if arg_resolver:
797804
argval, argrepr = arg_resolver.get_argval_argrepr(op, arg, offset)
@@ -820,7 +827,7 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N
820827

821828

822829
def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
823-
show_offsets=False, show_positions=False):
830+
show_offsets=False, show_positions=False, show_jit=False):
824831
"""Disassemble a code object."""
825832
linestarts = dict(findlinestarts(co))
826833
exception_entries = _parse_exception_table(co)
@@ -840,12 +847,12 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
840847
names=co.co_names,
841848
varname_from_oparg=co._varname_from_oparg,
842849
labels_map=labels_map)
843-
_disassemble_bytes(_get_code_array(co, adaptive), lasti, linestarts,
850+
_disassemble_bytes(_get_code_array(co, adaptive, show_jit), lasti, linestarts,
844851
exception_entries=exception_entries, co_positions=co.co_positions(),
845852
original_code=co.co_code, arg_resolver=arg_resolver, formatter=formatter)
846853

847-
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
848-
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
854+
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
855+
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
849856
if depth is None or depth > 0:
850857
if depth is not None:
851858
depth = depth - 1
@@ -855,7 +862,8 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap
855862
print("Disassembly of %r:" % (x,), file=file)
856863
_disassemble_recursive(
857864
x, file=file, depth=depth, show_caches=show_caches,
858-
adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions
865+
adaptive=adaptive, show_offsets=show_offsets,
866+
show_positions=show_positions, show_jit=show_jit
859867
)
860868

861869

@@ -1054,7 +1062,7 @@ class Bytecode:
10541062
10551063
Iterating over this yields the bytecode operations as Instruction instances.
10561064
"""
1057-
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
1065+
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
10581066
self.codeobj = co = _get_code_object(x)
10591067
if first_line is None:
10601068
self.first_line = co.co_firstlineno
@@ -1070,6 +1078,7 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False
10701078
self.adaptive = adaptive
10711079
self.show_offsets = show_offsets
10721080
self.show_positions = show_positions
1081+
self.show_jit = show_jit
10731082

10741083
def __iter__(self):
10751084
co = self.codeobj
@@ -1079,7 +1088,7 @@ def __iter__(self):
10791088
names=co.co_names,
10801089
varname_from_oparg=co._varname_from_oparg,
10811090
labels_map=labels_map)
1082-
return _get_instructions_bytes(_get_code_array(co, self.adaptive),
1091+
return _get_instructions_bytes(_get_code_array(co, self.adaptive, self.show_jit),
10831092
linestarts=self._linestarts,
10841093
line_offset=self._line_offset,
10851094
co_positions=co.co_positions(),
@@ -1111,7 +1120,7 @@ def dis(self):
11111120
else:
11121121
offset = -1
11131122
with io.StringIO() as output:
1114-
code = _get_code_array(co, self.adaptive)
1123+
code = _get_code_array(co, self.adaptive, self.show_jit)
11151124
offset_width = len(str(max(len(code) - 2, 9999))) if self.show_offsets else 0
11161125
if self.show_positions:
11171126
lineno_width = _get_positions_width(co)

0 commit comments

Comments
 (0)