Skip to content

Commit 76c2645

Browse files
authored
Merge branch 'python:main' into gh-118234
2 parents 674ec21 + 0584533 commit 76c2645

File tree

9 files changed

+75
-42
lines changed

9 files changed

+75
-42
lines changed

Doc/whatsnew/3.14.rst

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,11 +1997,19 @@ Optimizations
19971997
asyncio
19981998
-------
19991999

2000-
* :mod:`asyncio` now uses double linked list implementation for native tasks
2001-
which speeds up execution by 10% on standard pyperformance benchmarks and
2002-
reduces memory usage.
2000+
* :mod:`asyncio` has a new per-thread double linked list implementation internally for
2001+
:class:`native tasks <asyncio.Task>` which speeds up execution by 10-20% on standard
2002+
pyperformance benchmarks and reduces memory usage.
2003+
This enables external introspection tools such as
2004+
:ref:`python -m asyncio pstree <whatsnew314-asyncio-introspection>`
2005+
to introspect the call graph of asyncio tasks running in all threads.
20032006
(Contributed by Kumar Aditya in :gh:`107803`.)
20042007

2008+
* :mod:`asyncio` has first class support for :term:`free-threading builds <free threading>`.
2009+
This enables parallel execution of multiple event loops across different threads and scales
2010+
linearly with the number of threads.
2011+
(Contributed by Kumar Aditya in :gh:`128002`.)
2012+
20052013
* :mod:`asyncio` has new utility functions for introspecting and printing
20062014
the program's call graph: :func:`asyncio.capture_call_graph` and
20072015
:func:`asyncio.print_call_graph`.
@@ -2083,7 +2091,6 @@ Deprecated
20832091
* :class:`asyncio.WindowsProactorEventLoopPolicy`
20842092
* :func:`asyncio.get_event_loop_policy`
20852093
* :func:`asyncio.set_event_loop_policy`
2086-
* :func:`asyncio.set_event_loop`
20872094

20882095
Users should use :func:`asyncio.run` or :class:`asyncio.Runner` with
20892096
*loop_factory* to use the desired event loop implementation.

Lib/_strptime.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def __init__(self, locale_time=None):
302302
# W is set below by using 'U'
303303
'y': r"(?P<y>\d\d)",
304304
'Y': r"(?P<Y>\d\d\d\d)",
305-
'z': r"(?P<z>[+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?|(?-i:Z))",
305+
'z': r"(?P<z>([+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?",
306306
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
307307
'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
308308
'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'),
@@ -548,27 +548,28 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
548548
iso_week = int(found_dict['V'])
549549
elif group_key == 'z':
550550
z = found_dict['z']
551-
if z == 'Z':
552-
gmtoff = 0
553-
else:
554-
if z[3] == ':':
555-
z = z[:3] + z[4:]
556-
if len(z) > 5:
557-
if z[5] != ':':
558-
msg = f"Inconsistent use of : in {found_dict['z']}"
559-
raise ValueError(msg)
560-
z = z[:5] + z[6:]
561-
hours = int(z[1:3])
562-
minutes = int(z[3:5])
563-
seconds = int(z[5:7] or 0)
564-
gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
565-
gmtoff_remainder = z[8:]
566-
# Pad to always return microseconds.
567-
gmtoff_remainder_padding = "0" * (6 - len(gmtoff_remainder))
568-
gmtoff_fraction = int(gmtoff_remainder + gmtoff_remainder_padding)
569-
if z.startswith("-"):
570-
gmtoff = -gmtoff
571-
gmtoff_fraction = -gmtoff_fraction
551+
if z:
552+
if z == 'Z':
553+
gmtoff = 0
554+
else:
555+
if z[3] == ':':
556+
z = z[:3] + z[4:]
557+
if len(z) > 5:
558+
if z[5] != ':':
559+
msg = f"Inconsistent use of : in {found_dict['z']}"
560+
raise ValueError(msg)
561+
z = z[:5] + z[6:]
562+
hours = int(z[1:3])
563+
minutes = int(z[3:5])
564+
seconds = int(z[5:7] or 0)
565+
gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
566+
gmtoff_remainder = z[8:]
567+
# Pad to always return microseconds.
568+
gmtoff_remainder_padding = "0" * (6 - len(gmtoff_remainder))
569+
gmtoff_fraction = int(gmtoff_remainder + gmtoff_remainder_padding)
570+
if z.startswith("-"):
571+
gmtoff = -gmtoff
572+
gmtoff_fraction = -gmtoff_fraction
572573
elif group_key == 'Z':
573574
# Since -1 is default value only need to worry about setting tz if
574575
# it can be something other than -1.

Lib/test/datetimetester.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2972,6 +2972,17 @@ def test_strptime_leap_year(self):
29722972
with self._assertNotWarns(DeprecationWarning):
29732973
self.theclass.strptime('02-29,2024', '%m-%d,%Y')
29742974

2975+
def test_strptime_z_empty(self):
2976+
for directive in ('z',):
2977+
string = '2025-04-25 11:42:47'
2978+
format = f'%Y-%m-%d %H:%M:%S%{directive}'
2979+
target = self.theclass(2025, 4, 25, 11, 42, 47)
2980+
with self.subTest(string=string,
2981+
format=format,
2982+
target=target):
2983+
result = self.theclass.strptime(string, format)
2984+
self.assertEqual(result, target)
2985+
29752986
def test_more_timetuple(self):
29762987
# This tests fields beyond those tested by the TestDate.test_timetuple.
29772988
t = self.theclass(2004, 12, 31, 6, 22, 33)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``%z`` directive in :func:`datetime.datetime.strptime` to allow for no provided
2+
offset as was documented.

PCbuild/regen.targets

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@
125125
<JITArgs Condition="$(Platform) == 'x64'">x86_64-pc-windows-msvc</JITArgs>
126126
<JITArgs Condition="$(Configuration) == 'Debug'">$(JITArgs) --debug</JITArgs>
127127
</PropertyGroup>
128-
<Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs)'
129-
WorkingDirectory="$(GeneratedJitStencilsDir)"/>
128+
<Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs) --output-dir "$(GeneratedJitStencilsDir)" --pyconfig-dir "$(PySourcePath)PC"'/>
130129
</Target>
131130
<Target Name="_CleanJIT" AfterTargets="Clean">
132131
<Delete Files="@(_JITOutputs)"/>

Tools/jit/_targets.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class _Target(typing.Generic[_S, _R]):
4747
debug: bool = False
4848
verbose: bool = False
4949
known_symbols: dict[str, int] = dataclasses.field(default_factory=dict)
50+
pyconfig_dir: pathlib.Path = pathlib.Path.cwd().resolve()
5051

5152
def _get_nop(self) -> bytes:
5253
if re.fullmatch(r"aarch64-.*", self.triple):
@@ -57,13 +58,13 @@ def _get_nop(self) -> bytes:
5758
raise ValueError(f"NOP not defined for {self.triple}")
5859
return nop
5960

60-
def _compute_digest(self, out: pathlib.Path) -> str:
61+
def _compute_digest(self) -> str:
6162
hasher = hashlib.sha256()
6263
hasher.update(self.triple.encode())
6364
hasher.update(self.debug.to_bytes())
6465
# These dependencies are also reflected in _JITSources in regen.targets:
6566
hasher.update(PYTHON_EXECUTOR_CASES_C_H.read_bytes())
66-
hasher.update((out / "pyconfig.h").read_bytes())
67+
hasher.update((self.pyconfig_dir / "pyconfig.h").read_bytes())
6768
for dirpath, _, filenames in sorted(os.walk(TOOLS_JIT)):
6869
for filename in filenames:
6970
hasher.update(pathlib.Path(dirpath, filename).read_bytes())
@@ -125,7 +126,7 @@ async def _compile(
125126
f"-D_JIT_OPCODE={opname}",
126127
"-D_PyJIT_ACTIVE",
127128
"-D_Py_JIT",
128-
"-I.",
129+
f"-I{self.pyconfig_dir}",
129130
f"-I{CPYTHON / 'Include'}",
130131
f"-I{CPYTHON / 'Include' / 'internal'}",
131132
f"-I{CPYTHON / 'Include' / 'internal' / 'mimalloc'}",
@@ -193,28 +194,27 @@ async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]:
193194

194195
def build(
195196
self,
196-
out: pathlib.Path,
197197
*,
198198
comment: str = "",
199199
force: bool = False,
200-
stencils_h: str = "jit_stencils.h",
200+
jit_stencils: pathlib.Path,
201201
) -> None:
202202
"""Build jit_stencils.h in the given directory."""
203+
jit_stencils.parent.mkdir(parents=True, exist_ok=True)
203204
if not self.stable:
204205
warning = f"JIT support for {self.triple} is still experimental!"
205206
request = "Please report any issues you encounter.".center(len(warning))
206207
outline = "=" * len(warning)
207208
print("\n".join(["", outline, warning, request, outline, ""]))
208-
digest = f"// {self._compute_digest(out)}\n"
209-
jit_stencils = out / stencils_h
209+
digest = f"// {self._compute_digest()}\n"
210210
if (
211211
not force
212212
and jit_stencils.exists()
213213
and jit_stencils.read_text().startswith(digest)
214214
):
215215
return
216216
stencil_groups = ASYNCIO_RUNNER.run(self._build_stencils())
217-
jit_stencils_new = out / "jit_stencils.h.new"
217+
jit_stencils_new = jit_stencils.parent / "jit_stencils.h.new"
218218
try:
219219
with jit_stencils_new.open("w") as file:
220220
file.write(digest)

Tools/jit/build.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import _targets
99

1010
if __name__ == "__main__":
11-
out = pathlib.Path.cwd().resolve()
1211
comment = f"$ {shlex.join([pathlib.Path(sys.executable).name] + sys.argv)}"
1312
parser = argparse.ArgumentParser(description=__doc__)
1413
parser.add_argument(
@@ -23,6 +22,20 @@
2322
parser.add_argument(
2423
"-f", "--force", action="store_true", help="force the entire JIT to be rebuilt"
2524
)
25+
parser.add_argument(
26+
"-o",
27+
"--output-dir",
28+
help="where to output generated files",
29+
required=True,
30+
type=lambda p: pathlib.Path(p).resolve(),
31+
)
32+
parser.add_argument(
33+
"-p",
34+
"--pyconfig-dir",
35+
help="where to find pyconfig.h",
36+
required=True,
37+
type=lambda p: pathlib.Path(p).resolve(),
38+
)
2639
parser.add_argument(
2740
"-v", "--verbose", action="store_true", help="echo commands as they are run"
2841
)
@@ -31,13 +44,13 @@
3144
target.debug = args.debug
3245
target.force = args.force
3346
target.verbose = args.verbose
47+
target.pyconfig_dir = args.pyconfig_dir
3448
target.build(
35-
out,
3649
comment=comment,
37-
stencils_h=f"jit_stencils-{target.triple}.h",
3850
force=args.force,
51+
jit_stencils=args.output_dir / f"jit_stencils-{target.triple}.h",
3952
)
40-
jit_stencils_h = out / "jit_stencils.h"
53+
jit_stencils_h = args.output_dir / "jit_stencils.h"
4154
lines = [f"// {comment}\n"]
4255
guard = "#if"
4356
for target in args.target:

configure

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2776,7 +2776,7 @@ AS_VAR_IF([jit_flags],
27762776
[],
27772777
[AS_VAR_APPEND([CFLAGS_NODIST], [" $jit_flags"])
27782778
AS_VAR_SET([REGEN_JIT_COMMAND],
2779-
["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host}"])
2779+
["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir ."])
27802780
AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"])
27812781
AS_VAR_IF([Py_DEBUG],
27822782
[true],

0 commit comments

Comments
 (0)