Skip to content

Commit c0f4051

Browse files
committed
Merge branch 'main' into capi-number-tests
2 parents 9e14905 + 1c7ed7e commit c0f4051

File tree

20 files changed

+366
-197
lines changed

20 files changed

+366
-197
lines changed

Doc/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ help:
2929
@echo " venv to create a venv with necessary tools"
3030
@echo " html to make standalone HTML files"
3131
@echo " htmlview to open the index page built by the html target in your browser"
32+
@echo " htmllive to rebuild and reload HTML files in your browser"
3233
@echo " htmlhelp to make HTML files and a HTML help project"
3334
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
3435
@echo " text to make plain text files"
@@ -139,6 +140,11 @@ pydoc-topics: build
139140
htmlview: html
140141
$(PYTHON) -c "import os, webbrowser; webbrowser.open('file://' + os.path.realpath('build/html/index.html'))"
141142

143+
.PHONY: htmllive
144+
htmllive: SPHINXBUILD = $(VENVDIR)/bin/sphinx-autobuild
145+
htmllive: SPHINXOPTS = --re-ignore="/venv/"
146+
htmllive: html
147+
142148
.PHONY: clean
143149
clean: clean-venv
144150
-rm -rf build/*

Doc/reference/expressions.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,10 +1781,11 @@ Or, when processing a file stream in chunks:
17811781
while chunk := file.read(9000):
17821782
process(chunk)
17831783
1784-
Assignment expressions must be surrounded by parentheses when used
1785-
as sub-expressions in slicing, conditional, lambda,
1786-
keyword-argument, and comprehension-if expressions
1787-
and in ``assert`` and ``with`` statements.
1784+
Assignment expressions must be surrounded by parentheses when
1785+
used as expression statements and when used as sub-expressions in
1786+
slicing, conditional, lambda,
1787+
keyword-argument, and comprehension-if expressions and
1788+
in ``assert``, ``with``, and ``assignment`` statements.
17881789
In all other places where they can be used, parentheses are not required,
17891790
including in ``if`` and ``while`` statements.
17901791

Doc/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ sphinx==6.2.1
1111

1212
blurb
1313

14+
sphinx-autobuild
1415
sphinxext-opengraph==0.7.5
1516

1617
# The theme used by the documentation is stored separately, so we need

Lib/dis.py

Lines changed: 84 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,84 @@ class Instruction(_Instruction):
330330
covered by this instruction
331331
"""
332332

333+
@staticmethod
334+
def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg):
335+
get_name = None if names is None else names.__getitem__
336+
argval = None
337+
argrepr = ''
338+
deop = _deoptop(op)
339+
if arg is not None:
340+
# Set argval to the dereferenced value of the argument when
341+
# available, and argrepr to the string representation of argval.
342+
# _disassemble_bytes needs the string repr of the
343+
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
344+
argval = arg
345+
if deop in hasconst:
346+
argval, argrepr = _get_const_info(deop, arg, co_consts)
347+
elif deop in hasname:
348+
if deop == LOAD_GLOBAL:
349+
argval, argrepr = _get_name_info(arg//2, get_name)
350+
if (arg & 1) and argrepr:
351+
argrepr = f"{argrepr} + NULL"
352+
elif deop == LOAD_ATTR:
353+
argval, argrepr = _get_name_info(arg//2, get_name)
354+
if (arg & 1) and argrepr:
355+
argrepr = f"{argrepr} + NULL|self"
356+
elif deop == LOAD_SUPER_ATTR:
357+
argval, argrepr = _get_name_info(arg//4, get_name)
358+
if (arg & 1) and argrepr:
359+
argrepr = f"{argrepr} + NULL|self"
360+
else:
361+
argval, argrepr = _get_name_info(arg, get_name)
362+
elif deop in hasjabs:
363+
argval = arg*2
364+
argrepr = "to " + repr(argval)
365+
elif deop in hasjrel:
366+
signed_arg = -arg if _is_backward_jump(deop) else arg
367+
argval = offset + 2 + signed_arg*2
368+
caches = _get_cache_size(_all_opname[deop])
369+
argval += 2 * caches
370+
argrepr = "to " + repr(argval)
371+
elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
372+
arg1 = arg >> 4
373+
arg2 = arg & 15
374+
val1, argrepr1 = _get_name_info(arg1, varname_from_oparg)
375+
val2, argrepr2 = _get_name_info(arg2, varname_from_oparg)
376+
argrepr = argrepr1 + ", " + argrepr2
377+
argval = val1, val2
378+
elif deop in haslocal or deop in hasfree:
379+
argval, argrepr = _get_name_info(arg, varname_from_oparg)
380+
elif deop in hascompare:
381+
argval = cmp_op[arg >> 5]
382+
argrepr = argval
383+
if arg & 16:
384+
argrepr = f"bool({argrepr})"
385+
elif deop == CONVERT_VALUE:
386+
argval = (None, str, repr, ascii)[arg]
387+
argrepr = ('', 'str', 'repr', 'ascii')[arg]
388+
elif deop == SET_FUNCTION_ATTRIBUTE:
389+
argrepr = ', '.join(s for i, s in enumerate(FUNCTION_ATTR_FLAGS)
390+
if arg & (1<<i))
391+
elif deop == BINARY_OP:
392+
_, argrepr = _nb_ops[arg]
393+
elif deop == CALL_INTRINSIC_1:
394+
argrepr = _intrinsic_1_descs[arg]
395+
elif deop == CALL_INTRINSIC_2:
396+
argrepr = _intrinsic_2_descs[arg]
397+
return argval, argrepr
398+
399+
400+
@classmethod
401+
def _create(cls, op, arg, offset, start_offset, starts_line, line_number,
402+
is_jump_target, positions,
403+
co_consts=None, varname_from_oparg=None, names=None):
404+
argval, argrepr = cls._get_argval_argrepr(
405+
op, arg, offset,
406+
co_consts, names, varname_from_oparg)
407+
return Instruction(_all_opname[op], op, arg, argval, argrepr,
408+
offset, start_offset, starts_line, line_number,
409+
is_jump_target, positions)
410+
333411
@property
334412
def oparg(self):
335413
"""Alias for Instruction.arg."""
@@ -544,73 +622,15 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
544622
else:
545623
line_number = None
546624
is_jump_target = offset in labels
547-
argval = None
548-
argrepr = ''
549625
positions = Positions(*next(co_positions, ()))
550626
deop = _deoptop(op)
551-
caches = _get_cache_size(_all_opname[deop])
552627
op = code[offset]
553-
if arg is not None:
554-
# Set argval to the dereferenced value of the argument when
555-
# available, and argrepr to the string representation of argval.
556-
# _disassemble_bytes needs the string repr of the
557-
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
558-
argval = arg
559-
if deop in hasconst:
560-
argval, argrepr = _get_const_info(deop, arg, co_consts)
561-
elif deop in hasname:
562-
if deop == LOAD_GLOBAL:
563-
argval, argrepr = _get_name_info(arg//2, get_name)
564-
if (arg & 1) and argrepr:
565-
argrepr = f"{argrepr} + NULL"
566-
elif deop == LOAD_ATTR:
567-
argval, argrepr = _get_name_info(arg//2, get_name)
568-
if (arg & 1) and argrepr:
569-
argrepr = f"{argrepr} + NULL|self"
570-
elif deop == LOAD_SUPER_ATTR:
571-
argval, argrepr = _get_name_info(arg//4, get_name)
572-
if (arg & 1) and argrepr:
573-
argrepr = f"{argrepr} + NULL|self"
574-
else:
575-
argval, argrepr = _get_name_info(arg, get_name)
576-
elif deop in hasjabs:
577-
argval = arg*2
578-
argrepr = "to " + repr(argval)
579-
elif deop in hasjrel:
580-
signed_arg = -arg if _is_backward_jump(deop) else arg
581-
argval = offset + 2 + signed_arg*2
582-
argval += 2 * caches
583-
argrepr = "to " + repr(argval)
584-
elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
585-
arg1 = arg >> 4
586-
arg2 = arg & 15
587-
val1, argrepr1 = _get_name_info(arg1, varname_from_oparg)
588-
val2, argrepr2 = _get_name_info(arg2, varname_from_oparg)
589-
argrepr = argrepr1 + ", " + argrepr2
590-
argval = val1, val2
591-
elif deop in haslocal or deop in hasfree:
592-
argval, argrepr = _get_name_info(arg, varname_from_oparg)
593-
elif deop in hascompare:
594-
argval = cmp_op[arg >> 5]
595-
argrepr = argval
596-
if arg & 16:
597-
argrepr = f"bool({argrepr})"
598-
elif deop == CONVERT_VALUE:
599-
argval = (None, str, repr, ascii)[arg]
600-
argrepr = ('', 'str', 'repr', 'ascii')[arg]
601-
elif deop == SET_FUNCTION_ATTRIBUTE:
602-
argrepr = ', '.join(s for i, s in enumerate(FUNCTION_ATTR_FLAGS)
603-
if arg & (1<<i))
604-
elif deop == BINARY_OP:
605-
_, argrepr = _nb_ops[arg]
606-
elif deop == CALL_INTRINSIC_1:
607-
argrepr = _intrinsic_1_descs[arg]
608-
elif deop == CALL_INTRINSIC_2:
609-
argrepr = _intrinsic_2_descs[arg]
610-
yield Instruction(_all_opname[op], op,
611-
arg, argval, argrepr,
612-
offset, start_offset, starts_line, line_number,
613-
is_jump_target, positions)
628+
629+
yield Instruction._create(op, arg, offset, start_offset, starts_line, line_number,
630+
is_jump_target, positions, co_consts=co_consts,
631+
varname_from_oparg=varname_from_oparg, names=names)
632+
633+
caches = _get_cache_size(_all_opname[deop])
614634
if not caches:
615635
continue
616636
if not show_caches:

Lib/pathlib.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -647,9 +647,11 @@ def relative_to(self, other, /, *_deprecated, walk_up=False):
647647
"scheduled for removal in Python {remove}")
648648
warnings._deprecated("pathlib.PurePath.relative_to(*args)", msg,
649649
remove=(3, 14))
650-
other = self.with_segments(other, *_deprecated)
650+
other = self.with_segments(other, *_deprecated)
651+
elif not isinstance(other, PurePath):
652+
other = self.with_segments(other)
651653
for step, path in enumerate([other] + list(other.parents)):
652-
if self.is_relative_to(path):
654+
if path == self or path in self.parents:
653655
break
654656
elif not walk_up:
655657
raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}")
@@ -658,7 +660,7 @@ def relative_to(self, other, /, *_deprecated, walk_up=False):
658660
else:
659661
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
660662
parts = ['..'] * step + self._tail[len(path._tail):]
661-
return self.with_segments(*parts)
663+
return self._from_parsed_parts('', '', parts)
662664

663665
def is_relative_to(self, other, /, *_deprecated):
664666
"""Return True if the path is relative to another path or False.
@@ -669,7 +671,9 @@ def is_relative_to(self, other, /, *_deprecated):
669671
"scheduled for removal in Python {remove}")
670672
warnings._deprecated("pathlib.PurePath.is_relative_to(*args)",
671673
msg, remove=(3, 14))
672-
other = self.with_segments(other, *_deprecated)
674+
other = self.with_segments(other, *_deprecated)
675+
elif not isinstance(other, PurePath):
676+
other = self.with_segments(other)
673677
return other == self or other in self.parents
674678

675679
@property

Lib/test/support/pty_helper.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""
2+
Helper to run a script in a pseudo-terminal.
3+
"""
4+
import os
5+
import selectors
6+
import subprocess
7+
import sys
8+
from contextlib import ExitStack
9+
from errno import EIO
10+
11+
from test.support.import_helper import import_module
12+
13+
def run_pty(script, input=b"dummy input\r", env=None):
14+
pty = import_module('pty')
15+
output = bytearray()
16+
[master, slave] = pty.openpty()
17+
args = (sys.executable, '-c', script)
18+
proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env)
19+
os.close(slave)
20+
with ExitStack() as cleanup:
21+
cleanup.enter_context(proc)
22+
def terminate(proc):
23+
try:
24+
proc.terminate()
25+
except ProcessLookupError:
26+
# Workaround for Open/Net BSD bug (Issue 16762)
27+
pass
28+
cleanup.callback(terminate, proc)
29+
cleanup.callback(os.close, master)
30+
# Avoid using DefaultSelector and PollSelector. Kqueue() does not
31+
# work with pseudo-terminals on OS X < 10.9 (Issue 20365) and Open
32+
# BSD (Issue 20667). Poll() does not work with OS X 10.6 or 10.4
33+
# either (Issue 20472). Hopefully the file descriptor is low enough
34+
# to use with select().
35+
sel = cleanup.enter_context(selectors.SelectSelector())
36+
sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE)
37+
os.set_blocking(master, False)
38+
while True:
39+
for [_, events] in sel.select():
40+
if events & selectors.EVENT_READ:
41+
try:
42+
chunk = os.read(master, 0x10000)
43+
except OSError as err:
44+
# Linux raises EIO when slave is closed (Issue 5380)
45+
if err.errno != EIO:
46+
raise
47+
chunk = b""
48+
if not chunk:
49+
return output
50+
output.extend(chunk)
51+
if events & selectors.EVENT_WRITE:
52+
try:
53+
input = input[os.write(master, input):]
54+
except OSError as err:
55+
# Apparently EIO means the slave was closed
56+
if err.errno != EIO:
57+
raise
58+
input = b"" # Stop writing
59+
if not input:
60+
sel.modify(master, selectors.EVENT_READ)

Lib/test/test_dis.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,23 @@ def test_jump_target(self):
20012001
positions=None)
20022002
self.assertEqual(10 + 2 + 1*2 + 100*2, instruction.jump_target)
20032003

2004+
def test_argval_argrepr(self):
2005+
f = dis.Instruction._get_argval_argrepr
2006+
2007+
offset = 42
2008+
co_consts = (0, 1, 2, 3)
2009+
names = {1: 'a', 2: 'b'}
2010+
varname_from_oparg = lambda i : names[i]
2011+
args = (offset, co_consts, names, varname_from_oparg)
2012+
self.assertEqual(f(opcode.opmap["POP_TOP"], None, *args), (None, ''))
2013+
self.assertEqual(f(opcode.opmap["LOAD_CONST"], 1, *args), (1, '1'))
2014+
self.assertEqual(f(opcode.opmap["LOAD_GLOBAL"], 2, *args), ('a', 'a'))
2015+
self.assertEqual(f(opcode.opmap["JUMP_BACKWARD"], 11, *args), (24, 'to 24'))
2016+
self.assertEqual(f(opcode.opmap["COMPARE_OP"], 3, *args), ('<', '<'))
2017+
self.assertEqual(f(opcode.opmap["SET_FUNCTION_ATTRIBUTE"], 2, *args), (2, 'kwdefaults'))
2018+
self.assertEqual(f(opcode.opmap["BINARY_OP"], 3, *args), (3, '<<'))
2019+
self.assertEqual(f(opcode.opmap["CALL_INTRINSIC_1"], 2, *args), (2, 'INTRINSIC_IMPORT_STAR'))
2020+
20042021
def test_start_offset(self):
20052022
# When no extended args are present,
20062023
# start_offset should be equal to offset

Lib/test/test_inspect/test_inspect.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4733,19 +4733,15 @@ def test_builtins_have_signatures(self):
47334733
# These have unrepresentable parameter default values of NULL
47344734
needs_null = {"anext"}
47354735
no_signature |= needs_null
4736-
# These need PEP 457 groups or a signature change to accept None
4737-
needs_semantic_update = {"round"}
4738-
no_signature |= needs_semantic_update
47394736
# These need *args support in Argument Clinic
4740-
needs_varargs = {"breakpoint", "min", "max", "print",
4741-
"__build_class__"}
4737+
needs_varargs = {"min", "max", "__build_class__"}
47424738
no_signature |= needs_varargs
4743-
# These simply weren't covered in the initial AC conversion
4744-
# for builtin callables
4745-
not_converted_yet = {"open", "__import__"}
4746-
no_signature |= not_converted_yet
47474739
# These builtin types are expected to provide introspection info
4748-
types_with_signatures = set()
4740+
types_with_signatures = {
4741+
'bool', 'classmethod', 'complex', 'enumerate', 'filter', 'float',
4742+
'frozenset', 'list', 'map', 'memoryview', 'object', 'property',
4743+
'reversed', 'set', 'staticmethod', 'tuple', 'zip'
4744+
}
47494745
# Check the signatures we expect to be there
47504746
ns = vars(builtins)
47514747
for name, obj in sorted(ns.items()):
@@ -4758,15 +4754,19 @@ def test_builtins_have_signatures(self):
47584754
if (name in no_signature):
47594755
# Not yet converted
47604756
continue
4757+
if name in {'classmethod', 'staticmethod'}:
4758+
# Bug gh-112006: inspect.unwrap() does not work with types
4759+
# with the __wrapped__ data descriptor.
4760+
continue
47614761
with self.subTest(builtin=name):
47624762
self.assertIsNotNone(inspect.signature(obj))
47634763
# Check callables that haven't been converted don't claim a signature
47644764
# This ensures this test will start failing as more signatures are
47654765
# added, so the affected items can be moved into the scope of the
47664766
# regression test above
4767-
for name in no_signature:
4767+
for name in no_signature - needs_null:
47684768
with self.subTest(builtin=name):
4769-
self.assertIsNone(obj.__text_signature__)
4769+
self.assertIsNone(ns[name].__text_signature__)
47704770

47714771
def test_python_function_override_signature(self):
47724772
def func(*args, **kwargs):

0 commit comments

Comments
 (0)