Skip to content

Commit d5ab569

Browse files
authored
Merge branch 'main' into 3.14-zstd-python-code
2 parents f51c758 + 2bbcaed commit d5ab569

29 files changed

+1087
-342
lines changed

Doc/library/annotationlib.rst

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ Classes
132132

133133
Values are real annotation values (as per :attr:`Format.VALUE` format)
134134
for defined values, and :class:`ForwardRef` proxies for undefined
135-
values. Real objects may contain references to, :class:`ForwardRef`
135+
values. Real objects may contain references to :class:`ForwardRef`
136136
proxy objects.
137137

138138
.. attribute:: STRING
@@ -172,14 +172,21 @@ Classes
172172
:class:`~ForwardRef`. The string may not be exactly equivalent
173173
to the original source.
174174

175-
.. method:: evaluate(*, owner=None, globals=None, locals=None, type_params=None)
175+
.. method:: evaluate(*, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE)
176176

177177
Evaluate the forward reference, returning its value.
178178

179-
This may throw an exception, such as :exc:`NameError`, if the forward
179+
If the *format* argument is :attr:`~Format.VALUE` (the default),
180+
this method may throw an exception, such as :exc:`NameError`, if the forward
180181
reference refers to a name that cannot be resolved. The arguments to this
181182
method can be used to provide bindings for names that would otherwise
182-
be undefined.
183+
be undefined. If the *format* argument is :attr:`~Format.FORWARDREF`,
184+
the method will never throw an exception, but may return a :class:`~ForwardRef`
185+
instance. For example, if the forward reference object contains the code
186+
``list[undefined]``, where ``undefined`` is a name that is not defined,
187+
evaluating it with the :attr:`~Format.FORWARDREF` format will return
188+
``list[ForwardRef('undefined')]``. If the *format* argument is
189+
:attr:`~Format.STRING`, the method will return :attr:`~ForwardRef.__forward_arg__`.
183190

184191
The *owner* parameter provides the preferred mechanism for passing scope
185192
information to this method. The owner of a :class:`~ForwardRef` is the

Doc/library/subprocess.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,24 @@ handling consistency are valid for these functions.
15251525
Notes
15261526
-----
15271527

1528+
.. _subprocess-timeout-behavior:
1529+
1530+
Timeout Behavior
1531+
^^^^^^^^^^^^^^^^
1532+
1533+
When using the ``timeout`` parameter in functions like :func:`run`,
1534+
:meth:`Popen.wait`, or :meth:`Popen.communicate`,
1535+
users should be aware of the following behaviors:
1536+
1537+
1. **Process Creation Delay**: The initial process creation itself cannot be interrupted
1538+
on many platform APIs. This means that even when specifying a timeout, you are not
1539+
guaranteed to see a timeout exception until at least after however long process
1540+
creation takes.
1541+
1542+
2. **Extremely Small Timeout Values**: Setting very small timeout values (such as a few
1543+
milliseconds) may result in almost immediate :exc:`TimeoutExpired` exceptions because
1544+
process creation and system scheduling inherently require time.
1545+
15281546
.. _converting-argument-sequence:
15291547

15301548
Converting an argument sequence to a string on Windows

Lib/annotationlib.py

Lines changed: 131 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,28 @@ def __init__(
9292
def __init_subclass__(cls, /, *args, **kwds):
9393
raise TypeError("Cannot subclass ForwardRef")
9494

95-
def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
95+
def evaluate(
96+
self,
97+
*,
98+
globals=None,
99+
locals=None,
100+
type_params=None,
101+
owner=None,
102+
format=Format.VALUE,
103+
):
96104
"""Evaluate the forward reference and return the value.
97105
98106
If the forward reference cannot be evaluated, raise an exception.
99107
"""
108+
match format:
109+
case Format.STRING:
110+
return self.__forward_arg__
111+
case Format.VALUE:
112+
is_forwardref_format = False
113+
case Format.FORWARDREF:
114+
is_forwardref_format = True
115+
case _:
116+
raise NotImplementedError(format)
100117
if self.__cell__ is not None:
101118
try:
102119
return self.__cell__.cell_contents
@@ -159,17 +176,36 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
159176
arg = self.__forward_arg__
160177
if arg.isidentifier() and not keyword.iskeyword(arg):
161178
if arg in locals:
162-
value = locals[arg]
179+
return locals[arg]
163180
elif arg in globals:
164-
value = globals[arg]
181+
return globals[arg]
165182
elif hasattr(builtins, arg):
166183
return getattr(builtins, arg)
184+
elif is_forwardref_format:
185+
return self
167186
else:
168187
raise NameError(arg)
169188
else:
170189
code = self.__forward_code__
171-
value = eval(code, globals=globals, locals=locals)
172-
return value
190+
try:
191+
return eval(code, globals=globals, locals=locals)
192+
except Exception:
193+
if not is_forwardref_format:
194+
raise
195+
new_locals = _StringifierDict(
196+
{**builtins.__dict__, **locals},
197+
globals=globals,
198+
owner=owner,
199+
is_class=self.__forward_is_class__,
200+
format=format,
201+
)
202+
try:
203+
result = eval(code, globals=globals, locals=new_locals)
204+
except Exception:
205+
return self
206+
else:
207+
new_locals.transmogrify()
208+
return result
173209

174210
def _evaluate(self, globalns, localns, type_params=_sentinel, *, recursive_guard):
175211
import typing
@@ -546,6 +582,14 @@ def __missing__(self, key):
546582
self.stringifiers.append(fwdref)
547583
return fwdref
548584

585+
def transmogrify(self):
586+
for obj in self.stringifiers:
587+
obj.__class__ = ForwardRef
588+
obj.__stringifier_dict__ = None # not needed for ForwardRef
589+
if isinstance(obj.__ast_node__, str):
590+
obj.__arg__ = obj.__ast_node__
591+
obj.__ast_node__ = None
592+
549593
def create_unique_name(self):
550594
name = f"__annotationlib_name_{self.next_id}__"
551595
self.next_id += 1
@@ -595,19 +639,10 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
595639
# convert each of those into a string to get an approximation of the
596640
# original source.
597641
globals = _StringifierDict({}, format=format)
598-
if annotate.__closure__:
599-
freevars = annotate.__code__.co_freevars
600-
new_closure = []
601-
for i, cell in enumerate(annotate.__closure__):
602-
if i < len(freevars):
603-
name = freevars[i]
604-
else:
605-
name = "__cell__"
606-
fwdref = _Stringifier(name, stringifier_dict=globals)
607-
new_closure.append(types.CellType(fwdref))
608-
closure = tuple(new_closure)
609-
else:
610-
closure = None
642+
is_class = isinstance(owner, type)
643+
closure = _build_closure(
644+
annotate, owner, is_class, globals, allow_evaluation=False
645+
)
611646
func = types.FunctionType(
612647
annotate.__code__,
613648
globals,
@@ -649,32 +684,36 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
649684
is_class=is_class,
650685
format=format,
651686
)
652-
if annotate.__closure__:
653-
freevars = annotate.__code__.co_freevars
654-
new_closure = []
655-
for i, cell in enumerate(annotate.__closure__):
656-
try:
657-
cell.cell_contents
658-
except ValueError:
659-
if i < len(freevars):
660-
name = freevars[i]
661-
else:
662-
name = "__cell__"
663-
fwdref = _Stringifier(
664-
name,
665-
cell=cell,
666-
owner=owner,
667-
globals=annotate.__globals__,
668-
is_class=is_class,
669-
stringifier_dict=globals,
670-
)
671-
globals.stringifiers.append(fwdref)
672-
new_closure.append(types.CellType(fwdref))
673-
else:
674-
new_closure.append(cell)
675-
closure = tuple(new_closure)
687+
closure = _build_closure(
688+
annotate, owner, is_class, globals, allow_evaluation=True
689+
)
690+
func = types.FunctionType(
691+
annotate.__code__,
692+
globals,
693+
closure=closure,
694+
argdefs=annotate.__defaults__,
695+
kwdefaults=annotate.__kwdefaults__,
696+
)
697+
try:
698+
result = func(Format.VALUE_WITH_FAKE_GLOBALS)
699+
except Exception:
700+
pass
676701
else:
677-
closure = None
702+
globals.transmogrify()
703+
return result
704+
705+
# Try again, but do not provide any globals. This allows us to return
706+
# a value in certain cases where an exception gets raised during evaluation.
707+
globals = _StringifierDict(
708+
{},
709+
globals=annotate.__globals__,
710+
owner=owner,
711+
is_class=is_class,
712+
format=format,
713+
)
714+
closure = _build_closure(
715+
annotate, owner, is_class, globals, allow_evaluation=False
716+
)
678717
func = types.FunctionType(
679718
annotate.__code__,
680719
globals,
@@ -683,13 +722,21 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
683722
kwdefaults=annotate.__kwdefaults__,
684723
)
685724
result = func(Format.VALUE_WITH_FAKE_GLOBALS)
686-
for obj in globals.stringifiers:
687-
obj.__class__ = ForwardRef
688-
obj.__stringifier_dict__ = None # not needed for ForwardRef
689-
if isinstance(obj.__ast_node__, str):
690-
obj.__arg__ = obj.__ast_node__
691-
obj.__ast_node__ = None
692-
return result
725+
globals.transmogrify()
726+
if _is_evaluate:
727+
if isinstance(result, ForwardRef):
728+
return result.evaluate(format=Format.FORWARDREF)
729+
else:
730+
return result
731+
else:
732+
return {
733+
key: (
734+
val.evaluate(format=Format.FORWARDREF)
735+
if isinstance(val, ForwardRef)
736+
else val
737+
)
738+
for key, val in result.items()
739+
}
693740
elif format == Format.VALUE:
694741
# Should be impossible because __annotate__ functions must not raise
695742
# NotImplementedError for this format.
@@ -698,6 +745,39 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
698745
raise ValueError(f"Invalid format: {format!r}")
699746

700747

748+
def _build_closure(annotate, owner, is_class, stringifier_dict, *, allow_evaluation):
749+
if not annotate.__closure__:
750+
return None
751+
freevars = annotate.__code__.co_freevars
752+
new_closure = []
753+
for i, cell in enumerate(annotate.__closure__):
754+
if i < len(freevars):
755+
name = freevars[i]
756+
else:
757+
name = "__cell__"
758+
new_cell = None
759+
if allow_evaluation:
760+
try:
761+
cell.cell_contents
762+
except ValueError:
763+
pass
764+
else:
765+
new_cell = cell
766+
if new_cell is None:
767+
fwdref = _Stringifier(
768+
name,
769+
cell=cell,
770+
owner=owner,
771+
globals=annotate.__globals__,
772+
is_class=is_class,
773+
stringifier_dict=stringifier_dict,
774+
)
775+
stringifier_dict.stringifiers.append(fwdref)
776+
new_cell = types.CellType(fwdref)
777+
new_closure.append(new_cell)
778+
return tuple(new_closure)
779+
780+
701781
def _stringify_single(anno):
702782
if anno is ...:
703783
return "..."
@@ -809,7 +889,7 @@ def get_annotations(
809889
# But if we didn't get it, we use __annotations__ instead.
810890
ann = _get_dunder_annotations(obj)
811891
if ann is not None:
812-
return annotations_to_string(ann)
892+
return annotations_to_string(ann)
813893
case Format.VALUE_WITH_FAKE_GLOBALS:
814894
raise ValueError("The VALUE_WITH_FAKE_GLOBALS format is for internal use only")
815895
case _:

Lib/ast.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ def unparse(ast_obj):
626626
return unparser.visit(ast_obj)
627627

628628

629-
def main():
629+
def main(args=None):
630630
import argparse
631631
import sys
632632

@@ -643,7 +643,7 @@ def main():
643643
'column offsets')
644644
parser.add_argument('-i', '--indent', type=int, default=3,
645645
help='indentation of nodes (number of spaces)')
646-
args = parser.parse_args()
646+
args = parser.parse_args(args)
647647

648648
if args.infile == '-':
649649
name = '<stdin>'

Lib/asyncio/tools.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from itertools import count
66
from enum import Enum
77
import sys
8-
from _remotedebugging import get_all_awaited_by
8+
from _remote_debugging import get_all_awaited_by
99

1010

1111
class NodeType(Enum):
@@ -27,6 +27,7 @@ def _index(result):
2727
for tid, tname, awaited in tasks:
2828
id2name[tid] = tname
2929
for stack, parent_id in awaited:
30+
stack = [elem[0] if isinstance(elem, tuple) else elem for elem in stack]
3031
awaits.append((parent_id, stack, tid))
3132
return id2name, awaits
3233

@@ -151,6 +152,7 @@ def build_task_table(result):
151152
]
152153
)
153154
for stack, awaiter_id in awaited:
155+
stack = [elem[0] if isinstance(elem, tuple) else elem for elem in stack]
154156
coroutine_chain = " -> ".join(stack)
155157
awaiter_name = id2name.get(awaiter_id, "Unknown")
156158
table.append(

Lib/subprocess.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,8 +1235,11 @@ def communicate(self, input=None, timeout=None):
12351235

12361236
finally:
12371237
self._communication_started = True
1238-
1239-
sts = self.wait(timeout=self._remaining_time(endtime))
1238+
try:
1239+
sts = self.wait(timeout=self._remaining_time(endtime))
1240+
except TimeoutExpired as exc:
1241+
exc.timeout = timeout
1242+
raise
12401243

12411244
return (stdout, stderr)
12421245

@@ -2145,8 +2148,11 @@ def _communicate(self, input, endtime, orig_timeout):
21452148
selector.unregister(key.fileobj)
21462149
key.fileobj.close()
21472150
self._fileobj2output[key.fileobj].append(data)
2148-
2149-
self.wait(timeout=self._remaining_time(endtime))
2151+
try:
2152+
self.wait(timeout=self._remaining_time(endtime))
2153+
except TimeoutExpired as exc:
2154+
exc.timeout = orig_timeout
2155+
raise
21502156

21512157
# All data exchanged. Translate lists into strings.
21522158
if stdout is not None:

0 commit comments

Comments
 (0)