Skip to content
Merged
31 changes: 9 additions & 22 deletions Doc/library/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ defined:
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'B'`` | unsigned char | int | 1 | |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'u'`` | wchar_t | Unicode character | 2 | \(1) |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'w'`` | Py_UCS4 | Unicode character | 4 | \(2) |
| ``'w'`` | Py_UCS4 | Unicode character | 4 | \(1) |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'h'`` | signed short | int | 2 | |
+-----------+--------------------+-------------------+-----------------------+-------+
Expand All @@ -42,35 +40,24 @@ defined:
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'Q'`` | unsigned long long | int | 8 | |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'e'`` | _Float16 | float | 2 | \(3) |
| ``'e'`` | _Float16 | float | 2 | \(2) |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'f'`` | float | float | 4 | |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'d'`` | double | float | 8 | |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'Zf'`` | float complex | complex | 8 | \(4) |
| ``'Zf'`` | float complex | complex | 8 | \(3) |
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'Zd'`` | double complex | complex | 16 | \(4) |
| ``'Zd'`` | double complex | complex | 16 | \(3) |
+-----------+--------------------+-------------------+-----------------------+-------+


Notes:

(1)
It can be 16 bits or 32 bits depending on the platform.

.. versionchanged:: 3.9
``array('u')`` now uses :c:type:`wchar_t` as C type instead of deprecated
``Py_UNICODE``. This change doesn't affect its behavior because
``Py_UNICODE`` is alias of :c:type:`wchar_t` since Python 3.3.

.. deprecated-removed:: 3.3 3.16
Please migrate to ``'w'`` typecode.

(2)
.. versionadded:: 3.13

(3)
(2)
The IEEE 754 binary16 "half precision" type was introduced in the 2008
revision of the `IEEE 754 standard <ieee 754 standard_>`_.
This type is not widely supported by C compilers. It's available
Expand All @@ -79,7 +66,7 @@ Notes:

.. versionadded:: 3.15

(4)
(3)
Complex types (``Zf`` and ``Zd``) are available unconditionally,
regardless on support for complex types (the Annex G of the C11 standard)
by the C compiler.
Expand Down Expand Up @@ -220,7 +207,7 @@ The module defines the following type:
.. method:: fromunicode(ustr, /)

Extends this array with data from the given Unicode string.
The array must have type code ``'u'`` or ``'w'``; otherwise a :exc:`ValueError` is raised.
The array must have type code ``'w'``; otherwise a :exc:`ValueError` is raised.
Use ``array.frombytes(unicodestring.encode(enc))`` to append Unicode data to an
array of some other type.

Expand Down Expand Up @@ -288,15 +275,15 @@ The module defines the following type:

.. method:: tounicode()

Convert the array to a Unicode string. The array must have a type ``'u'`` or ``'w'``;
Convert the array to a Unicode string. The array must have a type ``'w'``;
otherwise a :exc:`ValueError` is raised. Use ``array.tobytes().decode(enc)`` to
obtain a Unicode string from an array of some other type.


The string representation of array objects has the form
``array(typecode, initializer)``.
The *initializer* is omitted if the array is empty, otherwise it is
a Unicode string if the *typecode* is ``'u'`` or ``'w'``, otherwise it is
a Unicode string if the *typecode* is ``'w'``, otherwise it is
a list of numbers.
The string representation is guaranteed to be able to be converted back to an
array with the same type and value using :func:`eval`, so long as the
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ annotationlib
Use :meth:`annotationlib.ForwardRef.evaluate`
or :func:`typing.evaluate_forward_ref` instead.

array
-----

* The ``'u'`` format code (:c:type:`wchar_t`) which has been deprecated in
documentation since Python 3.3 and at runtime since Python 3.13.
Use ``'w'`` format code instead (:c:type:`Py_UCS4`, always 4 bytes).

asyncio
-------

Expand Down
1 change: 1 addition & 0 deletions Include/cpython/pystats.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ typedef struct _optimization_stats {
uint64_t jit_code_size;
uint64_t jit_trampoline_size;
uint64_t jit_data_size;
uint64_t jit_got_size;
uint64_t jit_padding_size;
uint64_t jit_freed_memory_size;
uint64_t trace_total_memory_hist[_Py_UOP_HIST_SIZE];
Expand Down
5 changes: 5 additions & 0 deletions Include/internal/pycore_interpframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ _PyFrame_GetLocalsArray(_PyInterpreterFrame *frame)
static inline _PyStackRef*
_PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
{
#ifndef _Py_JIT
assert(frame->stackpointer != NULL);
#endif
_PyStackRef *sp = frame->stackpointer;
#ifndef NDEBUG
frame->stackpointer = NULL;
Expand All @@ -241,7 +243,10 @@ _PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
static inline void
_PyFrame_SetStackPointer(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
{
/* Avoid bloating the JIT code */
#ifndef _Py_JIT
assert(frame->stackpointer == NULL);
#endif
frame->stackpointer = stack_pointer;
}

Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_jit.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ _Py_CODEUNIT *_PyJIT_Entry(
int _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction *trace, size_t length);
void _PyJIT_Free(_PyExecutorObject *executor);
PyAPI_FUNC(int) _PyJIT_AddressInJitCode(PyInterpreterState *interp, uintptr_t addr);
PyAPI_FUNC(void) _Py_jit_assert_within_stack_bounds(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int lineno);
PyAPI_FUNC(int) _Py_jit_assertion_failure(int line);

#endif // _Py_JIT

Expand Down
5 changes: 0 additions & 5 deletions Include/internal/pycore_uop.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,7 @@ typedef struct _PyUOpInstruction{

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

#define UOP_TRACE_BUFFER_OVERHEAD 10
#define UOP_MAX_TRACE_LENGTH (FITNESS_INITIAL + UOP_TRACE_BUFFER_OVERHEAD)
Expand Down
47 changes: 28 additions & 19 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def _try_compile(source, name):
return compile(source, name, 'exec')

def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
show_offsets=False, show_positions=False):
show_offsets=False, show_positions=False, show_jit=False):
"""Disassemble classes, methods, functions, and other compiled objects.

With no argument, disassemble the last traceback.
Expand All @@ -95,7 +95,8 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
"""
if x is None:
distb(file=file, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions)
show_offsets=show_offsets, show_positions=show_positions,
show_jit=show_jit)
return
# Extract functions from methods.
if hasattr(x, '__func__'):
Expand All @@ -116,12 +117,14 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
if isinstance(x1, _have_code):
print("Disassembly of %s:" % name, file=file)
try:
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
except TypeError as msg:
print("Sorry:", msg, file=file)
print(file=file)
elif hasattr(x, 'co_code'): # Code object
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
labels_map = _make_labels_map(x)
label_width = 4 + len(str(len(labels_map)))
Expand All @@ -132,12 +135,13 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
arg_resolver = ArgResolver(labels_map=labels_map)
_disassemble_bytes(x, arg_resolver=arg_resolver, formatter=formatter)
elif isinstance(x, str): # Source code
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
else:
raise TypeError("don't know how to disassemble %s objects" %
type(x).__name__)

def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
"""Disassemble a traceback (default: last traceback)."""
if tb is None:
try:
Expand All @@ -148,7 +152,8 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets
except AttributeError:
raise RuntimeError("no last traceback to disassemble") from None
while tb.tb_next: tb = tb.tb_next
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)
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, show_jit=show_jit)

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

def _get_code_array(co, adaptive):
def _get_code_array(co, adaptive, show_jit):
if adaptive:
code = co._co_code_adaptive
res = []
found = False
for i in range(0, len(code), 2):
op, arg = code[i], code[i+1]
if op == ENTER_EXECUTOR:
if op == ENTER_EXECUTOR and not show_jit:
try:
ex = get_executor(co, i)
except (ValueError, RuntimeError):
Expand Down Expand Up @@ -656,7 +661,7 @@ def get_argval_argrepr(self, op, arg, offset):
argrepr = 'not in' if argval else 'in'
return argval, argrepr

def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False, show_jit=False):
"""Iterator for the opcodes in methods, functions or code

Generates a series of Instruction named tuples giving the details of
Expand All @@ -679,7 +684,7 @@ def get_instructions(x, *, first_line=None, show_caches=None, adaptive=False):
names=co.co_names,
varname_from_oparg=co._varname_from_oparg,
labels_map=_make_labels_map(original_code))
return _get_instructions_bytes(_get_code_array(co, adaptive),
return _get_instructions_bytes(_get_code_array(co, adaptive, show_jit),
linestarts=linestarts,
line_offset=line_offset,
co_positions=co.co_positions(),
Expand Down Expand Up @@ -792,6 +797,8 @@ def _get_instructions_bytes(code, linestarts=None, line_offset=0, co_positions=N
positions = Positions(*next(co_positions, ()))
deop = _deoptop(op)
op = code[offset]
if op == ENTER_EXECUTOR:
arg = code[offset+1]

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


def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
show_offsets=False, show_positions=False):
show_offsets=False, show_positions=False, show_jit=False):
"""Disassemble a code object."""
linestarts = dict(findlinestarts(co))
exception_entries = _parse_exception_table(co)
Expand All @@ -840,12 +847,12 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
names=co.co_names,
varname_from_oparg=co._varname_from_oparg,
labels_map=labels_map)
_disassemble_bytes(_get_code_array(co, adaptive), lasti, linestarts,
_disassemble_bytes(_get_code_array(co, adaptive, show_jit), lasti, linestarts,
exception_entries=exception_entries, co_positions=co.co_positions(),
original_code=co.co_code, arg_resolver=arg_resolver, formatter=formatter)

def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions)
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions, show_jit=show_jit)
if depth is None or depth > 0:
if depth is not None:
depth = depth - 1
Expand All @@ -855,7 +862,8 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap
print("Disassembly of %r:" % (x,), file=file)
_disassemble_recursive(
x, file=file, depth=depth, show_caches=show_caches,
adaptive=adaptive, show_offsets=show_offsets, show_positions=show_positions
adaptive=adaptive, show_offsets=show_offsets,
show_positions=show_positions, show_jit=show_jit
)


Expand Down Expand Up @@ -1054,7 +1062,7 @@ class Bytecode:

Iterating over this yields the bytecode operations as Instruction instances.
"""
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False):
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False, show_jit=False):
self.codeobj = co = _get_code_object(x)
if first_line is None:
self.first_line = co.co_firstlineno
Expand All @@ -1070,6 +1078,7 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False
self.adaptive = adaptive
self.show_offsets = show_offsets
self.show_positions = show_positions
self.show_jit = show_jit

def __iter__(self):
co = self.codeobj
Expand All @@ -1079,7 +1088,7 @@ def __iter__(self):
names=co.co_names,
varname_from_oparg=co._varname_from_oparg,
labels_map=labels_map)
return _get_instructions_bytes(_get_code_array(co, self.adaptive),
return _get_instructions_bytes(_get_code_array(co, self.adaptive, self.show_jit),
linestarts=self._linestarts,
line_offset=self._line_offset,
co_positions=co.co_positions(),
Expand Down Expand Up @@ -1111,7 +1120,7 @@ def dis(self):
else:
offset = -1
with io.StringIO() as output:
code = _get_code_array(co, self.adaptive)
code = _get_code_array(co, self.adaptive, self.show_jit)
offset_width = len(str(max(len(code) - 2, 9999))) if self.show_offsets else 0
if self.show_positions:
lineno_width = _get_positions_width(co)
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3318,3 +3318,18 @@ def control_characters_c0() -> list[str]:
_ROOT_IN_POSIX = hasattr(os, 'geteuid') and os.geteuid() == 0
requires_root_user = unittest.skipUnless(_ROOT_IN_POSIX, "test needs root privilege")
requires_non_root_user = unittest.skipIf(_ROOT_IN_POSIX, "test needs non-root account")


STATUS_DLL_INIT_FAILED = 0xC0000142
def skip_on_low_desktop_heap_memory_subprocess(returncode):
if sys.platform not in ('win32', 'cygwin'):
return
# On Windows, STATUS_DLL_INIT_FAILED is a generic error code that could
# come from any of the DLLs being loaded when a new Python process is
# created. In practice, it's likely a memory allocation failure in the
# desktop heap memory which caused the DLL init failure, especially on
# process created with CREATE_NEW_CONSOLE creation flag. See the article:
# https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/desktop-heap-limitation-out-of-memory
if returncode == STATUS_DLL_INIT_FAILED:
raise unittest.SkipTest('gh-150436: DLL init failed, likely because '
'of low desktop heap memory')
Loading
Loading