forked from python-interpreters/python314
Compare commits
2 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
| 990b2813e1 | |||
|
7a848baf56
|
@@ -4,11 +4,11 @@
|
|||||||
Lib/test/test_xml_etree.py | 10 ++++++++++
|
Lib/test/test_xml_etree.py | 10 ++++++++++
|
||||||
3 files changed, 17 insertions(+)
|
3 files changed, 17 insertions(+)
|
||||||
|
|
||||||
Index: Python-3.14.0/Lib/test/test_pyexpat.py
|
Index: Python-3.14.2/Lib/test/test_pyexpat.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0.orig/Lib/test/test_pyexpat.py 2025-10-08 11:27:29.989583305 +0200
|
--- Python-3.14.2.orig/Lib/test/test_pyexpat.py 2025-12-11 18:10:58.057084164 +0100
|
||||||
+++ Python-3.14.0/Lib/test/test_pyexpat.py 2025-10-08 11:28:09.418914658 +0200
|
+++ Python-3.14.2/Lib/test/test_pyexpat.py 2025-12-11 18:12:07.480767746 +0100
|
||||||
@@ -827,6 +827,10 @@
|
@@ -848,6 +848,10 @@
|
||||||
self.assertEqual(started, ['doc'])
|
self.assertEqual(started, ['doc'])
|
||||||
|
|
||||||
def test_reparse_deferral_disabled(self):
|
def test_reparse_deferral_disabled(self):
|
||||||
@@ -19,10 +19,10 @@ Index: Python-3.14.0/Lib/test/test_pyexpat.py
|
|||||||
started = []
|
started = []
|
||||||
|
|
||||||
def start_element(name, _):
|
def start_element(name, _):
|
||||||
Index: Python-3.14.0/Lib/test/test_sax.py
|
Index: Python-3.14.2/Lib/test/test_sax.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0.orig/Lib/test/test_sax.py 2025-10-08 11:27:30.053760879 +0200
|
--- Python-3.14.2.orig/Lib/test/test_sax.py 2025-12-11 18:10:58.116419305 +0100
|
||||||
+++ Python-3.14.0/Lib/test/test_sax.py 2025-10-08 11:28:09.419532320 +0200
|
+++ Python-3.14.2/Lib/test/test_sax.py 2025-12-11 18:12:07.481506046 +0100
|
||||||
@@ -1241,6 +1241,9 @@
|
@@ -1241,6 +1241,9 @@
|
||||||
|
|
||||||
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
|
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
|
||||||
@@ -33,10 +33,10 @@ Index: Python-3.14.0/Lib/test/test_sax.py
|
|||||||
def test_flush_reparse_deferral_disabled(self):
|
def test_flush_reparse_deferral_disabled(self):
|
||||||
result = BytesIO()
|
result = BytesIO()
|
||||||
xmlgen = XMLGenerator(result)
|
xmlgen = XMLGenerator(result)
|
||||||
Index: Python-3.14.0/Lib/test/test_xml_etree.py
|
Index: Python-3.14.2/Lib/test/test_xml_etree.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0.orig/Lib/test/test_xml_etree.py 2025-10-08 11:27:30.502943506 +0200
|
--- Python-3.14.2.orig/Lib/test/test_xml_etree.py 2025-12-11 18:10:58.548740381 +0100
|
||||||
+++ Python-3.14.0/Lib/test/test_xml_etree.py 2025-10-08 11:28:09.420206077 +0200
|
+++ Python-3.14.2/Lib/test/test_xml_etree.py 2025-12-11 18:12:07.481767744 +0100
|
||||||
@@ -138,6 +138,11 @@
|
@@ -138,6 +138,11 @@
|
||||||
return mock.patch.object(cls, "__eq__", autospec=True, wraps=eq)
|
return mock.patch.object(cls, "__eq__", autospec=True, wraps=eq)
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ Index: Python-3.14.0/Lib/test/test_xml_etree.py
|
|||||||
def checkwarnings(*filters, quiet=False):
|
def checkwarnings(*filters, quiet=False):
|
||||||
def decorator(test):
|
def decorator(test):
|
||||||
def newtest(*args, **kwargs):
|
def newtest(*args, **kwargs):
|
||||||
@@ -1547,9 +1552,11 @@
|
@@ -1573,9 +1578,11 @@
|
||||||
self.assert_event_tags(parser, [('end', 'root')])
|
self.assert_event_tags(parser, [('end', 'root')])
|
||||||
self.assertIsNone(parser.close())
|
self.assertIsNone(parser.close())
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ Index: Python-3.14.0/Lib/test/test_xml_etree.py
|
|||||||
def test_simple_xml_chunk_5(self):
|
def test_simple_xml_chunk_5(self):
|
||||||
self.test_simple_xml(chunk_size=5, flush=True)
|
self.test_simple_xml(chunk_size=5, flush=True)
|
||||||
|
|
||||||
@@ -1774,6 +1781,9 @@
|
@@ -1802,6 +1809,9 @@
|
||||||
|
|
||||||
self.assert_event_tags(parser, [('end', 'doc')])
|
self.assert_event_tags(parser, [('end', 'doc')])
|
||||||
|
|
||||||
|
|||||||
@@ -1,374 +0,0 @@
|
|||||||
From 5c0bf5295a6a38ee7540e447bcdc4889d131e261 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Serhiy Storchaka <storchaka@gmail.com>
|
|
||||||
Date: Fri, 31 Oct 2025 15:49:51 +0200
|
|
||||||
Subject: [PATCH] [3.14] gh-136065: Fix quadratic complexity in
|
|
||||||
os.path.expandvars() (GH-134952) (cherry picked from commit
|
|
||||||
f029e8db626ddc6e3a3beea4eff511a71aaceb5c)
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=UTF-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
|
||||||
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
|
|
||||||
---
|
|
||||||
Lib/ntpath.py | 126 ++++++------------
|
|
||||||
Lib/posixpath.py | 43 +++---
|
|
||||||
Lib/test/test_genericpath.py | 21 ++-
|
|
||||||
Lib/test/test_ntpath.py | 22 ++-
|
|
||||||
...-05-30-22-33-27.gh-issue-136065.bu337o.rst | 1 +
|
|
||||||
5 files changed, 96 insertions(+), 117 deletions(-)
|
|
||||||
create mode 100644 Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
|
|
||||||
|
|
||||||
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
|
|
||||||
index 9cdc16480f9afe..01f060e70beed9 100644
|
|
||||||
--- a/Lib/ntpath.py
|
|
||||||
+++ b/Lib/ntpath.py
|
|
||||||
@@ -400,17 +400,23 @@ def expanduser(path):
|
|
||||||
# XXX With COMMAND.COM you can use any characters in a variable name,
|
|
||||||
# XXX except '^|<>='.
|
|
||||||
|
|
||||||
+_varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)"
|
|
||||||
+_varsub = None
|
|
||||||
+_varsubb = None
|
|
||||||
+
|
|
||||||
def expandvars(path):
|
|
||||||
"""Expand shell variables of the forms $var, ${var} and %var%.
|
|
||||||
|
|
||||||
Unknown variables are left unchanged."""
|
|
||||||
path = os.fspath(path)
|
|
||||||
+ global _varsub, _varsubb
|
|
||||||
if isinstance(path, bytes):
|
|
||||||
if b'$' not in path and b'%' not in path:
|
|
||||||
return path
|
|
||||||
- import string
|
|
||||||
- varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
|
|
||||||
- quote = b'\''
|
|
||||||
+ if not _varsubb:
|
|
||||||
+ import re
|
|
||||||
+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
|
|
||||||
+ sub = _varsubb
|
|
||||||
percent = b'%'
|
|
||||||
brace = b'{'
|
|
||||||
rbrace = b'}'
|
|
||||||
@@ -419,94 +425,44 @@ def expandvars(path):
|
|
||||||
else:
|
|
||||||
if '$' not in path and '%' not in path:
|
|
||||||
return path
|
|
||||||
- import string
|
|
||||||
- varchars = string.ascii_letters + string.digits + '_-'
|
|
||||||
- quote = '\''
|
|
||||||
+ if not _varsub:
|
|
||||||
+ import re
|
|
||||||
+ _varsub = re.compile(_varpattern, re.ASCII).sub
|
|
||||||
+ sub = _varsub
|
|
||||||
percent = '%'
|
|
||||||
brace = '{'
|
|
||||||
rbrace = '}'
|
|
||||||
dollar = '$'
|
|
||||||
environ = os.environ
|
|
||||||
- res = path[:0]
|
|
||||||
- index = 0
|
|
||||||
- pathlen = len(path)
|
|
||||||
- while index < pathlen:
|
|
||||||
- c = path[index:index+1]
|
|
||||||
- if c == quote: # no expansion within single quotes
|
|
||||||
- path = path[index + 1:]
|
|
||||||
- pathlen = len(path)
|
|
||||||
- try:
|
|
||||||
- index = path.index(c)
|
|
||||||
- res += c + path[:index + 1]
|
|
||||||
- except ValueError:
|
|
||||||
- res += c + path
|
|
||||||
- index = pathlen - 1
|
|
||||||
- elif c == percent: # variable or '%'
|
|
||||||
- if path[index + 1:index + 2] == percent:
|
|
||||||
- res += c
|
|
||||||
- index += 1
|
|
||||||
- else:
|
|
||||||
- path = path[index+1:]
|
|
||||||
- pathlen = len(path)
|
|
||||||
- try:
|
|
||||||
- index = path.index(percent)
|
|
||||||
- except ValueError:
|
|
||||||
- res += percent + path
|
|
||||||
- index = pathlen - 1
|
|
||||||
- else:
|
|
||||||
- var = path[:index]
|
|
||||||
- try:
|
|
||||||
- if environ is None:
|
|
||||||
- value = os.fsencode(os.environ[os.fsdecode(var)])
|
|
||||||
- else:
|
|
||||||
- value = environ[var]
|
|
||||||
- except KeyError:
|
|
||||||
- value = percent + var + percent
|
|
||||||
- res += value
|
|
||||||
- elif c == dollar: # variable or '$$'
|
|
||||||
- if path[index + 1:index + 2] == dollar:
|
|
||||||
- res += c
|
|
||||||
- index += 1
|
|
||||||
- elif path[index + 1:index + 2] == brace:
|
|
||||||
- path = path[index+2:]
|
|
||||||
- pathlen = len(path)
|
|
||||||
- try:
|
|
||||||
- index = path.index(rbrace)
|
|
||||||
- except ValueError:
|
|
||||||
- res += dollar + brace + path
|
|
||||||
- index = pathlen - 1
|
|
||||||
- else:
|
|
||||||
- var = path[:index]
|
|
||||||
- try:
|
|
||||||
- if environ is None:
|
|
||||||
- value = os.fsencode(os.environ[os.fsdecode(var)])
|
|
||||||
- else:
|
|
||||||
- value = environ[var]
|
|
||||||
- except KeyError:
|
|
||||||
- value = dollar + brace + var + rbrace
|
|
||||||
- res += value
|
|
||||||
- else:
|
|
||||||
- var = path[:0]
|
|
||||||
- index += 1
|
|
||||||
- c = path[index:index + 1]
|
|
||||||
- while c and c in varchars:
|
|
||||||
- var += c
|
|
||||||
- index += 1
|
|
||||||
- c = path[index:index + 1]
|
|
||||||
- try:
|
|
||||||
- if environ is None:
|
|
||||||
- value = os.fsencode(os.environ[os.fsdecode(var)])
|
|
||||||
- else:
|
|
||||||
- value = environ[var]
|
|
||||||
- except KeyError:
|
|
||||||
- value = dollar + var
|
|
||||||
- res += value
|
|
||||||
- if c:
|
|
||||||
- index -= 1
|
|
||||||
+
|
|
||||||
+ def repl(m):
|
|
||||||
+ lastindex = m.lastindex
|
|
||||||
+ if lastindex is None:
|
|
||||||
+ return m[0]
|
|
||||||
+ name = m[lastindex]
|
|
||||||
+ if lastindex == 1:
|
|
||||||
+ if name == percent:
|
|
||||||
+ return name
|
|
||||||
+ if not name.endswith(percent):
|
|
||||||
+ return m[0]
|
|
||||||
+ name = name[:-1]
|
|
||||||
else:
|
|
||||||
- res += c
|
|
||||||
- index += 1
|
|
||||||
- return res
|
|
||||||
+ if name == dollar:
|
|
||||||
+ return name
|
|
||||||
+ if name.startswith(brace):
|
|
||||||
+ if not name.endswith(rbrace):
|
|
||||||
+ return m[0]
|
|
||||||
+ name = name[1:-1]
|
|
||||||
+
|
|
||||||
+ try:
|
|
||||||
+ if environ is None:
|
|
||||||
+ return os.fsencode(os.environ[os.fsdecode(name)])
|
|
||||||
+ else:
|
|
||||||
+ return environ[name]
|
|
||||||
+ except KeyError:
|
|
||||||
+ return m[0]
|
|
||||||
+
|
|
||||||
+ return sub(repl, path)
|
|
||||||
|
|
||||||
|
|
||||||
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
|
|
||||||
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
|
|
||||||
index d38f3bd5872bcd..ad86cc06c017a0 100644
|
|
||||||
--- a/Lib/posixpath.py
|
|
||||||
+++ b/Lib/posixpath.py
|
|
||||||
@@ -284,42 +284,41 @@ def expanduser(path):
|
|
||||||
# This expands the forms $variable and ${variable} only.
|
|
||||||
# Non-existent variables are left unchanged.
|
|
||||||
|
|
||||||
-_varprog = None
|
|
||||||
-_varprogb = None
|
|
||||||
+_varpattern = r'\$(\w+|\{[^}]*\}?)'
|
|
||||||
+_varsub = None
|
|
||||||
+_varsubb = None
|
|
||||||
|
|
||||||
def expandvars(path):
|
|
||||||
"""Expand shell variables of form $var and ${var}. Unknown variables
|
|
||||||
are left unchanged."""
|
|
||||||
path = os.fspath(path)
|
|
||||||
- global _varprog, _varprogb
|
|
||||||
+ global _varsub, _varsubb
|
|
||||||
if isinstance(path, bytes):
|
|
||||||
if b'$' not in path:
|
|
||||||
return path
|
|
||||||
- if not _varprogb:
|
|
||||||
+ if not _varsubb:
|
|
||||||
import re
|
|
||||||
- _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
|
|
||||||
- search = _varprogb.search
|
|
||||||
+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
|
|
||||||
+ sub = _varsubb
|
|
||||||
start = b'{'
|
|
||||||
end = b'}'
|
|
||||||
environ = getattr(os, 'environb', None)
|
|
||||||
else:
|
|
||||||
if '$' not in path:
|
|
||||||
return path
|
|
||||||
- if not _varprog:
|
|
||||||
+ if not _varsub:
|
|
||||||
import re
|
|
||||||
- _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
|
|
||||||
- search = _varprog.search
|
|
||||||
+ _varsub = re.compile(_varpattern, re.ASCII).sub
|
|
||||||
+ sub = _varsub
|
|
||||||
start = '{'
|
|
||||||
end = '}'
|
|
||||||
environ = os.environ
|
|
||||||
- i = 0
|
|
||||||
- while True:
|
|
||||||
- m = search(path, i)
|
|
||||||
- if not m:
|
|
||||||
- break
|
|
||||||
- i, j = m.span(0)
|
|
||||||
- name = m.group(1)
|
|
||||||
- if name.startswith(start) and name.endswith(end):
|
|
||||||
+
|
|
||||||
+ def repl(m):
|
|
||||||
+ name = m[1]
|
|
||||||
+ if name.startswith(start):
|
|
||||||
+ if not name.endswith(end):
|
|
||||||
+ return m[0]
|
|
||||||
name = name[1:-1]
|
|
||||||
try:
|
|
||||||
if environ is None:
|
|
||||||
@@ -327,13 +326,11 @@ def expandvars(path):
|
|
||||||
else:
|
|
||||||
value = environ[name]
|
|
||||||
except KeyError:
|
|
||||||
- i = j
|
|
||||||
+ return m[0]
|
|
||||||
else:
|
|
||||||
- tail = path[j:]
|
|
||||||
- path = path[:i] + value
|
|
||||||
- i = len(path)
|
|
||||||
- path += tail
|
|
||||||
- return path
|
|
||||||
+ return value
|
|
||||||
+
|
|
||||||
+ return sub(repl, path)
|
|
||||||
|
|
||||||
|
|
||||||
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
|
|
||||||
diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py
|
|
||||||
index df07af01fc7540..1a44cedcd360b1 100644
|
|
||||||
--- a/Lib/test/test_genericpath.py
|
|
||||||
+++ b/Lib/test/test_genericpath.py
|
|
||||||
@@ -7,9 +7,9 @@
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import warnings
|
|
||||||
-from test.support import (
|
|
||||||
- is_apple, is_emscripten, os_helper, warnings_helper
|
|
||||||
-)
|
|
||||||
+from test import support
|
|
||||||
+from test.support import os_helper
|
|
||||||
+from test.support import warnings_helper
|
|
||||||
from test.support.script_helper import assert_python_ok
|
|
||||||
from test.support.os_helper import FakePath
|
|
||||||
|
|
||||||
@@ -445,6 +445,19 @@ def check(value, expected):
|
|
||||||
os.fsencode('$bar%s bar' % nonascii))
|
|
||||||
check(b'$spam}bar', os.fsencode('%s}bar' % nonascii))
|
|
||||||
|
|
||||||
+ @support.requires_resource('cpu')
|
|
||||||
+ def test_expandvars_large(self):
|
|
||||||
+ expandvars = self.pathmodule.expandvars
|
|
||||||
+ with os_helper.EnvironmentVarGuard() as env:
|
|
||||||
+ env.clear()
|
|
||||||
+ env["A"] = "B"
|
|
||||||
+ n = 100_000
|
|
||||||
+ self.assertEqual(expandvars('$A'*n), 'B'*n)
|
|
||||||
+ self.assertEqual(expandvars('${A}'*n), 'B'*n)
|
|
||||||
+ self.assertEqual(expandvars('$A!'*n), 'B!'*n)
|
|
||||||
+ self.assertEqual(expandvars('${A}A'*n), 'BA'*n)
|
|
||||||
+ self.assertEqual(expandvars('${'*10*n), '${'*10*n)
|
|
||||||
+
|
|
||||||
def test_abspath(self):
|
|
||||||
self.assertIn("foo", self.pathmodule.abspath("foo"))
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
@@ -502,7 +515,7 @@ def test_nonascii_abspath(self):
|
|
||||||
# directory (when the bytes name is used).
|
|
||||||
and sys.platform not in {
|
|
||||||
"win32", "emscripten", "wasi"
|
|
||||||
- } and not is_apple
|
|
||||||
+ } and not support.is_apple
|
|
||||||
):
|
|
||||||
name = os_helper.TESTFN_UNDECODABLE
|
|
||||||
elif os_helper.TESTFN_NONASCII:
|
|
||||||
diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
|
|
||||||
index 03bfccf260b25d..9270f3257068d6 100644
|
|
||||||
--- a/Lib/test/test_ntpath.py
|
|
||||||
+++ b/Lib/test/test_ntpath.py
|
|
||||||
@@ -7,8 +7,7 @@
|
|
||||||
import unittest
|
|
||||||
import warnings
|
|
||||||
from test import support
|
|
||||||
-from test.support import cpython_only, os_helper
|
|
||||||
-from test.support import TestFailed
|
|
||||||
+from test.support import os_helper
|
|
||||||
from ntpath import ALLOW_MISSING
|
|
||||||
from test.support.os_helper import FakePath
|
|
||||||
from test import test_genericpath
|
|
||||||
@@ -59,7 +58,7 @@ def tester(fn, wantResult):
|
|
||||||
fn = fn.replace("\\", "\\\\")
|
|
||||||
gotResult = eval(fn)
|
|
||||||
if wantResult != gotResult and _norm(wantResult) != _norm(gotResult):
|
|
||||||
- raise TestFailed("%s should return: %s but returned: %s" \
|
|
||||||
+ raise support.TestFailed("%s should return: %s but returned: %s" \
|
|
||||||
%(str(fn), str(wantResult), str(gotResult)))
|
|
||||||
|
|
||||||
# then with bytes
|
|
||||||
@@ -75,7 +74,7 @@ def tester(fn, wantResult):
|
|
||||||
warnings.simplefilter("ignore", DeprecationWarning)
|
|
||||||
gotResult = eval(fn)
|
|
||||||
if _norm(wantResult) != _norm(gotResult):
|
|
||||||
- raise TestFailed("%s should return: %s but returned: %s" \
|
|
||||||
+ raise support.TestFailed("%s should return: %s but returned: %s" \
|
|
||||||
%(str(fn), str(wantResult), repr(gotResult)))
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1022,6 +1021,19 @@ def check(value, expected):
|
|
||||||
check('%spam%bar', '%sbar' % nonascii)
|
|
||||||
check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
|
|
||||||
|
|
||||||
+ @support.requires_resource('cpu')
|
|
||||||
+ def test_expandvars_large(self):
|
|
||||||
+ expandvars = ntpath.expandvars
|
|
||||||
+ with os_helper.EnvironmentVarGuard() as env:
|
|
||||||
+ env.clear()
|
|
||||||
+ env["A"] = "B"
|
|
||||||
+ n = 100_000
|
|
||||||
+ self.assertEqual(expandvars('%A%'*n), 'B'*n)
|
|
||||||
+ self.assertEqual(expandvars('%A%A'*n), 'BA'*n)
|
|
||||||
+ self.assertEqual(expandvars("''"*n + '%%'), "''"*n + '%')
|
|
||||||
+ self.assertEqual(expandvars("%%"*n), "%"*n)
|
|
||||||
+ self.assertEqual(expandvars("$$"*n), "$"*n)
|
|
||||||
+
|
|
||||||
def test_expanduser(self):
|
|
||||||
tester('ntpath.expanduser("test")', 'test')
|
|
||||||
|
|
||||||
@@ -1439,7 +1451,7 @@ def test_con_device(self):
|
|
||||||
self.assertTrue(os.path.exists(r"\\.\CON"))
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform != 'win32', "Fast paths are only for win32")
|
|
||||||
- @cpython_only
|
|
||||||
+ @support.cpython_only
|
|
||||||
def test_fast_paths_in_use(self):
|
|
||||||
# There are fast paths of these functions implemented in posixmodule.c.
|
|
||||||
# Confirm that they are being used, and not the Python fallbacks in
|
|
||||||
diff --git a/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst b/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
|
|
||||||
new file mode 100644
|
|
||||||
index 00000000000000..1d152bb5318380
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
|
|
||||||
@@ -0,0 +1 @@
|
|
||||||
+Fix quadratic complexity in :func:`os.path.expandvars`.
|
|
||||||
@@ -1,306 +0,0 @@
|
|||||||
From 5454f861e2b3c96fa1e6430dc952544670955f69 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Serhiy Storchaka <storchaka@gmail.com>
|
|
||||||
Date: Tue, 7 Oct 2025 20:15:26 +0300
|
|
||||||
Subject: [PATCH] gh-139700: Check consistency of the zip64 end of central
|
|
||||||
directory record (GH-139702)
|
|
||||||
|
|
||||||
Support records with "zip64 extensible data" if there are no bytes
|
|
||||||
prepended to the ZIP file.
|
|
||||||
(cherry picked from commit 162997bb70e067668c039700141770687bc8f267)
|
|
||||||
|
|
||||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
|
||||||
---
|
|
||||||
Lib/test/test_zipfile/test_core.py | 82 +++++++++-
|
|
||||||
Lib/zipfile/__init__.py | 51 +++---
|
|
||||||
Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst | 3
|
|
||||||
3 files changed, 113 insertions(+), 23 deletions(-)
|
|
||||||
create mode 100644 Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst
|
|
||||||
|
|
||||||
Index: Python-3.14.0/Lib/test/test_zipfile/test_core.py
|
|
||||||
===================================================================
|
|
||||||
--- Python-3.14.0.orig/Lib/test/test_zipfile/test_core.py 2025-11-12 01:03:54.947094045 +0100
|
|
||||||
+++ Python-3.14.0/Lib/test/test_zipfile/test_core.py 2025-11-12 01:04:11.766432381 +0100
|
|
||||||
@@ -898,6 +898,8 @@
|
|
||||||
self, file_size_64_set=False, file_size_extra=False,
|
|
||||||
compress_size_64_set=False, compress_size_extra=False,
|
|
||||||
header_offset_64_set=False, header_offset_extra=False,
|
|
||||||
+ extensible_data=b'',
|
|
||||||
+ end_of_central_dir_size=None, offset_to_end_of_central_dir=None,
|
|
||||||
):
|
|
||||||
"""Generate bytes sequence for a zip with (incomplete) zip64 data.
|
|
||||||
|
|
||||||
@@ -951,6 +953,12 @@
|
|
||||||
|
|
||||||
central_dir_size = struct.pack('<Q', 58 + 8 * len(central_zip64_fields))
|
|
||||||
offset_to_central_dir = struct.pack('<Q', 50 + 8 * len(local_zip64_fields))
|
|
||||||
+ if end_of_central_dir_size is None:
|
|
||||||
+ end_of_central_dir_size = 44 + len(extensible_data)
|
|
||||||
+ if offset_to_end_of_central_dir is None:
|
|
||||||
+ offset_to_end_of_central_dir = (108
|
|
||||||
+ + 8 * len(local_zip64_fields)
|
|
||||||
+ + 8 * len(central_zip64_fields))
|
|
||||||
|
|
||||||
local_extra_length = struct.pack("<H", 4 + 8 * len(local_zip64_fields))
|
|
||||||
central_extra_length = struct.pack("<H", 4 + 8 * len(central_zip64_fields))
|
|
||||||
@@ -979,14 +987,17 @@
|
|
||||||
+ filename
|
|
||||||
+ central_extra
|
|
||||||
# Zip64 end of central directory
|
|
||||||
- + b"PK\x06\x06,\x00\x00\x00\x00\x00\x00\x00-\x00-"
|
|
||||||
- + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
|
|
||||||
+ + b"PK\x06\x06"
|
|
||||||
+ + struct.pack('<Q', end_of_central_dir_size)
|
|
||||||
+ + b"-\x00-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
|
|
||||||
+ b"\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
|
|
||||||
+ central_dir_size
|
|
||||||
+ offset_to_central_dir
|
|
||||||
+ + extensible_data
|
|
||||||
# Zip64 end of central directory locator
|
|
||||||
- + b"PK\x06\x07\x00\x00\x00\x00l\x00\x00\x00\x00\x00\x00\x00\x01"
|
|
||||||
- + b"\x00\x00\x00"
|
|
||||||
+ + b"PK\x06\x07\x00\x00\x00\x00"
|
|
||||||
+ + struct.pack('<Q', offset_to_end_of_central_dir)
|
|
||||||
+ + b"\x01\x00\x00\x00"
|
|
||||||
# end of central directory
|
|
||||||
+ b"PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00:\x00\x00\x002\x00"
|
|
||||||
+ b"\x00\x00\x00\x00"
|
|
||||||
@@ -1017,6 +1028,7 @@
|
|
||||||
with self.assertRaises(zipfile.BadZipFile) as e:
|
|
||||||
zipfile.ZipFile(io.BytesIO(missing_file_size_extra))
|
|
||||||
self.assertIn('file size', str(e.exception).lower())
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_file_size_extra)))
|
|
||||||
|
|
||||||
# zip64 file size present, zip64 compress size present, one field in
|
|
||||||
# extra, expecting two, equals missing compress size.
|
|
||||||
@@ -1028,6 +1040,7 @@
|
|
||||||
with self.assertRaises(zipfile.BadZipFile) as e:
|
|
||||||
zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
|
|
||||||
self.assertIn('compress size', str(e.exception).lower())
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_compress_size_extra)))
|
|
||||||
|
|
||||||
# zip64 compress size present, no fields in extra, expecting one,
|
|
||||||
# equals missing compress size.
|
|
||||||
@@ -1037,6 +1050,7 @@
|
|
||||||
with self.assertRaises(zipfile.BadZipFile) as e:
|
|
||||||
zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
|
|
||||||
self.assertIn('compress size', str(e.exception).lower())
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_compress_size_extra)))
|
|
||||||
|
|
||||||
# zip64 file size present, zip64 compress size present, zip64 header
|
|
||||||
# offset present, two fields in extra, expecting three, equals missing
|
|
||||||
@@ -1051,6 +1065,7 @@
|
|
||||||
with self.assertRaises(zipfile.BadZipFile) as e:
|
|
||||||
zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
|
|
||||||
self.assertIn('header offset', str(e.exception).lower())
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra)))
|
|
||||||
|
|
||||||
# zip64 compress size present, zip64 header offset present, one field
|
|
||||||
# in extra, expecting two, equals missing header offset
|
|
||||||
@@ -1063,6 +1078,7 @@
|
|
||||||
with self.assertRaises(zipfile.BadZipFile) as e:
|
|
||||||
zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
|
|
||||||
self.assertIn('header offset', str(e.exception).lower())
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra)))
|
|
||||||
|
|
||||||
# zip64 file size present, zip64 header offset present, one field in
|
|
||||||
# extra, expecting two, equals missing header offset
|
|
||||||
@@ -1075,6 +1091,7 @@
|
|
||||||
with self.assertRaises(zipfile.BadZipFile) as e:
|
|
||||||
zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
|
|
||||||
self.assertIn('header offset', str(e.exception).lower())
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra)))
|
|
||||||
|
|
||||||
# zip64 header offset present, no fields in extra, expecting one,
|
|
||||||
# equals missing header offset
|
|
||||||
@@ -1086,6 +1103,63 @@
|
|
||||||
with self.assertRaises(zipfile.BadZipFile) as e:
|
|
||||||
zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
|
|
||||||
self.assertIn('header offset', str(e.exception).lower())
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra)))
|
|
||||||
+
|
|
||||||
+ def test_bad_zip64_end_of_central_dir(self):
|
|
||||||
+ zipdata = self.make_zip64_file(end_of_central_dir_size=0)
|
|
||||||
+ with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*record'):
|
|
||||||
+ zipfile.ZipFile(io.BytesIO(zipdata))
|
|
||||||
+ self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata)))
|
|
||||||
+
|
|
||||||
+ zipdata = self.make_zip64_file(end_of_central_dir_size=100)
|
|
||||||
+ with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*record'):
|
|
||||||
+ zipfile.ZipFile(io.BytesIO(zipdata))
|
|
||||||
+ self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata)))
|
|
||||||
+
|
|
||||||
+ zipdata = self.make_zip64_file(offset_to_end_of_central_dir=0)
|
|
||||||
+ with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*record'):
|
|
||||||
+ zipfile.ZipFile(io.BytesIO(zipdata))
|
|
||||||
+ self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata)))
|
|
||||||
+
|
|
||||||
+ zipdata = self.make_zip64_file(offset_to_end_of_central_dir=1000)
|
|
||||||
+ with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*locator'):
|
|
||||||
+ zipfile.ZipFile(io.BytesIO(zipdata))
|
|
||||||
+ self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata)))
|
|
||||||
+
|
|
||||||
+ def test_zip64_end_of_central_dir_record_not_found(self):
|
|
||||||
+ zipdata = self.make_zip64_file()
|
|
||||||
+ zipdata = zipdata.replace(b"PK\x06\x06", b'\x00'*4)
|
|
||||||
+ with self.assertRaisesRegex(zipfile.BadZipFile, 'record not found'):
|
|
||||||
+ zipfile.ZipFile(io.BytesIO(zipdata))
|
|
||||||
+ self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata)))
|
|
||||||
+
|
|
||||||
+ zipdata = self.make_zip64_file(
|
|
||||||
+ extensible_data=b'\xca\xfe\x04\x00\x00\x00data')
|
|
||||||
+ zipdata = zipdata.replace(b"PK\x06\x06", b'\x00'*4)
|
|
||||||
+ with self.assertRaisesRegex(zipfile.BadZipFile, 'record not found'):
|
|
||||||
+ zipfile.ZipFile(io.BytesIO(zipdata))
|
|
||||||
+ self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata)))
|
|
||||||
+
|
|
||||||
+ def test_zip64_extensible_data(self):
|
|
||||||
+ # These values are what is set in the make_zip64_file method.
|
|
||||||
+ expected_file_size = 8
|
|
||||||
+ expected_compress_size = 8
|
|
||||||
+ expected_header_offset = 0
|
|
||||||
+ expected_content = b"test1234"
|
|
||||||
+
|
|
||||||
+ zipdata = self.make_zip64_file(
|
|
||||||
+ extensible_data=b'\xca\xfe\x04\x00\x00\x00data')
|
|
||||||
+ with zipfile.ZipFile(io.BytesIO(zipdata)) as zf:
|
|
||||||
+ zinfo = zf.infolist()[0]
|
|
||||||
+ self.assertEqual(zinfo.file_size, expected_file_size)
|
|
||||||
+ self.assertEqual(zinfo.compress_size, expected_compress_size)
|
|
||||||
+ self.assertEqual(zinfo.header_offset, expected_header_offset)
|
|
||||||
+ self.assertEqual(zf.read(zinfo), expected_content)
|
|
||||||
+ self.assertTrue(zipfile.is_zipfile(io.BytesIO(zipdata)))
|
|
||||||
+
|
|
||||||
+ with self.assertRaisesRegex(zipfile.BadZipFile, 'record not found'):
|
|
||||||
+ zipfile.ZipFile(io.BytesIO(b'prepended' + zipdata))
|
|
||||||
+ self.assertFalse(zipfile.is_zipfile(io.BytesIO(b'prepended' + zipdata)))
|
|
||||||
|
|
||||||
def test_generated_valid_zip64_extra(self):
|
|
||||||
# These values are what is set in the make_zip64_file method.
|
|
||||||
Index: Python-3.14.0/Lib/zipfile/__init__.py
|
|
||||||
===================================================================
|
|
||||||
--- Python-3.14.0.orig/Lib/zipfile/__init__.py 2025-11-12 01:03:55.239347604 +0100
|
|
||||||
+++ Python-3.14.0/Lib/zipfile/__init__.py 2025-11-12 01:04:11.767105446 +0100
|
|
||||||
@@ -265,7 +265,7 @@
|
|
||||||
else:
|
|
||||||
with open(filename, "rb") as fp:
|
|
||||||
result = _check_zipfile(fp)
|
|
||||||
- except OSError:
|
|
||||||
+ except (OSError, BadZipFile):
|
|
||||||
pass
|
|
||||||
return result
|
|
||||||
|
|
||||||
@@ -275,9 +275,6 @@
|
|
||||||
|
|
||||||
# "concat" is zero, unless zip was concatenated to another file
|
|
||||||
concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
|
|
||||||
- if endrec[_ECD_SIGNATURE] == stringEndArchive64:
|
|
||||||
- # If Zip64 extension structures are present, account for them
|
|
||||||
- concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator)
|
|
||||||
|
|
||||||
if debug > 2:
|
|
||||||
inferred = concat + offset_cd
|
|
||||||
@@ -289,16 +286,15 @@
|
|
||||||
"""
|
|
||||||
Read the ZIP64 end-of-archive records and use that to update endrec
|
|
||||||
"""
|
|
||||||
- try:
|
|
||||||
- fpin.seek(offset - sizeEndCentDir64Locator, 2)
|
|
||||||
- except OSError:
|
|
||||||
- # If the seek fails, the file is not large enough to contain a ZIP64
|
|
||||||
+ offset -= sizeEndCentDir64Locator
|
|
||||||
+ if offset < 0:
|
|
||||||
+ # The file is not large enough to contain a ZIP64
|
|
||||||
# end-of-archive record, so just return the end record we were given.
|
|
||||||
return endrec
|
|
||||||
-
|
|
||||||
+ fpin.seek(offset)
|
|
||||||
data = fpin.read(sizeEndCentDir64Locator)
|
|
||||||
if len(data) != sizeEndCentDir64Locator:
|
|
||||||
- return endrec
|
|
||||||
+ raise OSError("Unknown I/O error")
|
|
||||||
sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
|
|
||||||
if sig != stringEndArchive64Locator:
|
|
||||||
return endrec
|
|
||||||
@@ -306,16 +302,33 @@
|
|
||||||
if diskno != 0 or disks > 1:
|
|
||||||
raise BadZipFile("zipfiles that span multiple disks are not supported")
|
|
||||||
|
|
||||||
- # Assume no 'zip64 extensible data'
|
|
||||||
- fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2)
|
|
||||||
+ offset -= sizeEndCentDir64
|
|
||||||
+ if reloff > offset:
|
|
||||||
+ raise BadZipFile("Corrupt zip64 end of central directory locator")
|
|
||||||
+ # First, check the assumption that there is no prepended data.
|
|
||||||
+ fpin.seek(reloff)
|
|
||||||
+ extrasz = offset - reloff
|
|
||||||
data = fpin.read(sizeEndCentDir64)
|
|
||||||
if len(data) != sizeEndCentDir64:
|
|
||||||
- return endrec
|
|
||||||
+ raise OSError("Unknown I/O error")
|
|
||||||
+ if not data.startswith(stringEndArchive64) and reloff != offset:
|
|
||||||
+ # Since we already have seen the Zip64 EOCD Locator, it's
|
|
||||||
+ # possible we got here because there is prepended data.
|
|
||||||
+ # Assume no 'zip64 extensible data'
|
|
||||||
+ fpin.seek(offset)
|
|
||||||
+ extrasz = 0
|
|
||||||
+ data = fpin.read(sizeEndCentDir64)
|
|
||||||
+ if len(data) != sizeEndCentDir64:
|
|
||||||
+ raise OSError("Unknown I/O error")
|
|
||||||
+ if not data.startswith(stringEndArchive64):
|
|
||||||
+ raise BadZipFile("Zip64 end of central directory record not found")
|
|
||||||
+
|
|
||||||
sig, sz, create_version, read_version, disk_num, disk_dir, \
|
|
||||||
dircount, dircount2, dirsize, diroffset = \
|
|
||||||
struct.unpack(structEndArchive64, data)
|
|
||||||
- if sig != stringEndArchive64:
|
|
||||||
- return endrec
|
|
||||||
+ if (diroffset + dirsize != reloff or
|
|
||||||
+ sz + 12 != sizeEndCentDir64 + extrasz):
|
|
||||||
+ raise BadZipFile("Corrupt zip64 end of central directory record")
|
|
||||||
|
|
||||||
# Update the original endrec using data from the ZIP64 record
|
|
||||||
endrec[_ECD_SIGNATURE] = sig
|
|
||||||
@@ -325,6 +338,7 @@
|
|
||||||
endrec[_ECD_ENTRIES_TOTAL] = dircount2
|
|
||||||
endrec[_ECD_SIZE] = dirsize
|
|
||||||
endrec[_ECD_OFFSET] = diroffset
|
|
||||||
+ endrec[_ECD_LOCATION] = offset - extrasz
|
|
||||||
return endrec
|
|
||||||
|
|
||||||
|
|
||||||
@@ -358,7 +372,7 @@
|
|
||||||
endrec.append(filesize - sizeEndCentDir)
|
|
||||||
|
|
||||||
# Try to read the "Zip64 end of central directory" structure
|
|
||||||
- return _EndRecData64(fpin, -sizeEndCentDir, endrec)
|
|
||||||
+ return _EndRecData64(fpin, filesize - sizeEndCentDir, endrec)
|
|
||||||
|
|
||||||
# Either this is not a ZIP file, or it is a ZIP file with an archive
|
|
||||||
# comment. Search the end of the file for the "end of central directory"
|
|
||||||
@@ -382,8 +396,7 @@
|
|
||||||
endrec.append(maxCommentStart + start)
|
|
||||||
|
|
||||||
# Try to read the "Zip64 end of central directory" structure
|
|
||||||
- return _EndRecData64(fpin, maxCommentStart + start - filesize,
|
|
||||||
- endrec)
|
|
||||||
+ return _EndRecData64(fpin, maxCommentStart + start, endrec)
|
|
||||||
|
|
||||||
# Unable to find a valid end of central directory structure
|
|
||||||
return None
|
|
||||||
@@ -2142,7 +2155,7 @@
|
|
||||||
" would require ZIP64 extensions")
|
|
||||||
zip64endrec = struct.pack(
|
|
||||||
structEndArchive64, stringEndArchive64,
|
|
||||||
- 44, 45, 45, 0, 0, centDirCount, centDirCount,
|
|
||||||
+ sizeEndCentDir64 - 12, 45, 45, 0, 0, centDirCount, centDirCount,
|
|
||||||
centDirSize, centDirOffset)
|
|
||||||
self.fp.write(zip64endrec)
|
|
||||||
|
|
||||||
Index: Python-3.14.0/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst
|
|
||||||
===================================================================
|
|
||||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
||||||
+++ Python-3.14.0/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst 2025-11-12 01:04:11.767493557 +0100
|
|
||||||
@@ -0,0 +1,3 @@
|
|
||||||
+Check consistency of the zip64 end of central directory record. Support
|
|
||||||
+records with "zip64 extensible data" if there are no bytes prepended to the
|
|
||||||
+ZIP file.
|
|
||||||
BIN
Python-3.14.0.tar.xz
LFS
BIN
Python-3.14.0.tar.xz
LFS
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
Python-3.14.2.tar.xz
LFS
Normal file
BIN
Python-3.14.2.tar.xz
LFS
Normal file
Binary file not shown.
1
Python-3.14.2.tar.xz.sigstore
Normal file
1
Python-3.14.2.tar.xz.sigstore
Normal file
File diff suppressed because one or more lines are too long
@@ -13,11 +13,11 @@ Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
|||||||
5 files changed, 37 insertions(+), 9 deletions(-)
|
5 files changed, 37 insertions(+), 9 deletions(-)
|
||||||
create mode 100644 Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst
|
create mode 100644 Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst
|
||||||
|
|
||||||
Index: Python-3.14.0rc1/Doc/library/ensurepip.rst
|
Index: Python-3.14.2/Doc/library/ensurepip.rst
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0rc1.orig/Doc/library/ensurepip.rst 2025-07-22 18:42:44.000000000 +0200
|
--- Python-3.14.2.orig/Doc/library/ensurepip.rst 2025-12-11 18:11:54.214805989 +0100
|
||||||
+++ Python-3.14.0rc1/Doc/library/ensurepip.rst 2025-07-23 10:10:31.690342385 +0200
|
+++ Python-3.14.2/Doc/library/ensurepip.rst 2025-12-11 18:12:02.573781892 +0100
|
||||||
@@ -61,7 +61,11 @@
|
@@ -65,7 +65,11 @@
|
||||||
By default, ``pip`` is installed into the current virtual environment
|
By default, ``pip`` is installed into the current virtual environment
|
||||||
(if one is active) or into the system site packages (if there is no
|
(if one is active) or into the system site packages (if there is no
|
||||||
active virtual environment). The installation location can be controlled
|
active virtual environment). The installation location can be controlled
|
||||||
@@ -30,7 +30,7 @@ Index: Python-3.14.0rc1/Doc/library/ensurepip.rst
|
|||||||
|
|
||||||
.. option:: --root <dir>
|
.. option:: --root <dir>
|
||||||
|
|
||||||
@@ -102,7 +106,7 @@
|
@@ -106,7 +110,7 @@
|
||||||
Returns a string specifying the available version of pip that will be
|
Returns a string specifying the available version of pip that will be
|
||||||
installed when bootstrapping an environment.
|
installed when bootstrapping an environment.
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ Index: Python-3.14.0rc1/Doc/library/ensurepip.rst
|
|||||||
altinstall=False, default_pip=False, \
|
altinstall=False, default_pip=False, \
|
||||||
verbosity=0)
|
verbosity=0)
|
||||||
|
|
||||||
@@ -112,6 +116,8 @@
|
@@ -116,6 +120,8 @@
|
||||||
If *root* is ``None``, then installation uses the default install location
|
If *root* is ``None``, then installation uses the default install location
|
||||||
for the current environment.
|
for the current environment.
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ Index: Python-3.14.0rc1/Doc/library/ensurepip.rst
|
|||||||
*upgrade* indicates whether or not to upgrade an existing installation
|
*upgrade* indicates whether or not to upgrade an existing installation
|
||||||
of an earlier version of ``pip`` to the available version.
|
of an earlier version of ``pip`` to the available version.
|
||||||
|
|
||||||
@@ -132,6 +138,8 @@
|
@@ -136,6 +142,8 @@
|
||||||
*verbosity* controls the level of output to :data:`sys.stdout` from the
|
*verbosity* controls the level of output to :data:`sys.stdout` from the
|
||||||
bootstrapping operation.
|
bootstrapping operation.
|
||||||
|
|
||||||
@@ -57,10 +57,10 @@ Index: Python-3.14.0rc1/Doc/library/ensurepip.rst
|
|||||||
.. audit-event:: ensurepip.bootstrap root ensurepip.bootstrap
|
.. audit-event:: ensurepip.bootstrap root ensurepip.bootstrap
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Index: Python-3.14.0rc1/Lib/ensurepip/__init__.py
|
Index: Python-3.14.2/Lib/ensurepip/__init__.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0rc1.orig/Lib/ensurepip/__init__.py 2025-07-23 10:10:18.541751619 +0200
|
--- Python-3.14.2.orig/Lib/ensurepip/__init__.py 2025-12-11 18:11:54.214805989 +0100
|
||||||
+++ Python-3.14.0rc1/Lib/ensurepip/__init__.py 2025-07-23 10:10:31.690818287 +0200
|
+++ Python-3.14.2/Lib/ensurepip/__init__.py 2025-12-11 18:12:02.574381834 +0100
|
||||||
@@ -106,27 +106,27 @@
|
@@ -106,27 +106,27 @@
|
||||||
os.environ['PIP_CONFIG_FILE'] = os.devnull
|
os.environ['PIP_CONFIG_FILE'] = os.devnull
|
||||||
|
|
||||||
@@ -123,10 +123,10 @@ Index: Python-3.14.0rc1/Lib/ensurepip/__init__.py
|
|||||||
upgrade=args.upgrade,
|
upgrade=args.upgrade,
|
||||||
user=args.user,
|
user=args.user,
|
||||||
verbosity=args.verbosity,
|
verbosity=args.verbosity,
|
||||||
Index: Python-3.14.0rc1/Lib/test/test_ensurepip.py
|
Index: Python-3.14.2/Lib/test/test_ensurepip.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0rc1.orig/Lib/test/test_ensurepip.py 2025-07-23 10:10:19.969641992 +0200
|
--- Python-3.14.2.orig/Lib/test/test_ensurepip.py 2025-12-11 18:11:54.214805989 +0100
|
||||||
+++ Python-3.14.0rc1/Lib/test/test_ensurepip.py 2025-07-23 10:10:31.691217643 +0200
|
+++ Python-3.14.2/Lib/test/test_ensurepip.py 2025-12-11 18:12:02.574698987 +0100
|
||||||
@@ -100,6 +100,17 @@
|
@@ -100,6 +100,17 @@
|
||||||
unittest.mock.ANY,
|
unittest.mock.ANY,
|
||||||
)
|
)
|
||||||
@@ -145,11 +145,11 @@ Index: Python-3.14.0rc1/Lib/test/test_ensurepip.py
|
|||||||
def test_bootstrapping_with_user(self):
|
def test_bootstrapping_with_user(self):
|
||||||
ensurepip.bootstrap(user=True)
|
ensurepip.bootstrap(user=True)
|
||||||
|
|
||||||
Index: Python-3.14.0rc1/Makefile.pre.in
|
Index: Python-3.14.2/Makefile.pre.in
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0rc1.orig/Makefile.pre.in 2025-07-23 10:10:27.325708066 +0200
|
--- Python-3.14.2.orig/Makefile.pre.in 2025-12-11 18:11:58.776792838 +0100
|
||||||
+++ Python-3.14.0rc1/Makefile.pre.in 2025-07-23 10:10:31.691716104 +0200
|
+++ Python-3.14.2/Makefile.pre.in 2025-12-11 18:12:02.575189138 +0100
|
||||||
@@ -2371,7 +2371,7 @@
|
@@ -2375,7 +2375,7 @@
|
||||||
install|*) ensurepip="" ;; \
|
install|*) ensurepip="" ;; \
|
||||||
esac; \
|
esac; \
|
||||||
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
||||||
@@ -158,7 +158,7 @@ Index: Python-3.14.0rc1/Makefile.pre.in
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: altinstall
|
.PHONY: altinstall
|
||||||
@@ -2382,7 +2382,7 @@
|
@@ -2386,7 +2386,7 @@
|
||||||
install|*) ensurepip="--altinstall" ;; \
|
install|*) ensurepip="--altinstall" ;; \
|
||||||
esac; \
|
esac; \
|
||||||
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
||||||
@@ -167,9 +167,9 @@ Index: Python-3.14.0rc1/Makefile.pre.in
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: commoninstall
|
.PHONY: commoninstall
|
||||||
Index: Python-3.14.0rc1/Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst
|
Index: Python-3.14.2/Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst
|
||||||
===================================================================
|
===================================================================
|
||||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
+++ Python-3.14.0rc1/Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst 2025-07-23 10:10:31.692253536 +0200
|
+++ Python-3.14.2/Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst 2025-12-11 18:12:02.575697168 +0100
|
||||||
@@ -0,0 +1 @@
|
@@ -0,0 +1 @@
|
||||||
+A directory prefix can now be specified when using :mod:`ensurepip`.
|
+A directory prefix can now be specified when using :mod:`ensurepip`.
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
Lib/test/test_compile.py | 5 +++++
|
Lib/test/test_compile.py | 5 +++++
|
||||||
1 file changed, 5 insertions(+)
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
Index: Python-3.14.0/Lib/test/test_compile.py
|
Index: Python-3.14.2/Lib/test/test_compile.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0.orig/Lib/test/test_compile.py 2025-11-06 23:19:11.681015028 +0100
|
--- Python-3.14.2.orig/Lib/test/test_compile.py 2025-12-11 18:10:57.011786647 +0100
|
||||||
+++ Python-3.14.0/Lib/test/test_compile.py 2025-11-06 23:22:47.971267371 +0100
|
+++ Python-3.14.2/Lib/test/test_compile.py 2025-12-11 18:12:09.732814688 +0100
|
||||||
@@ -24,6 +24,9 @@
|
@@ -24,6 +24,9 @@
|
||||||
from test.support.bytecode_helper import instructions_with_positions
|
from test.support.bytecode_helper import instructions_with_positions
|
||||||
from test.support.os_helper import FakePath
|
from test.support.os_helper import FakePath
|
||||||
@@ -24,7 +24,7 @@ Index: Python-3.14.0/Lib/test/test_compile.py
|
|||||||
@support.skip_emscripten_stack_overflow()
|
@support.skip_emscripten_stack_overflow()
|
||||||
def test_extended_arg(self):
|
def test_extended_arg(self):
|
||||||
repeat = 100
|
repeat = 100
|
||||||
@@ -709,6 +713,7 @@
|
@@ -724,6 +728,7 @@
|
||||||
|
|
||||||
@support.cpython_only
|
@support.cpython_only
|
||||||
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
|
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
|
||||||
|
|||||||
212
gh138498-llvm-version-config.patch
Normal file
212
gh138498-llvm-version-config.patch
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
Index: Python-3.14.0/Tools/jit/README.md
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.14.0.orig/Tools/jit/README.md
|
||||||
|
+++ Python-3.14.0/Tools/jit/README.md
|
||||||
|
@@ -9,7 +9,7 @@ Python 3.11 or newer is required to buil
|
||||||
|
|
||||||
|
The JIT compiler does not require end users to install any third-party dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are *not* required to build the rest of CPython using LLVM, or even the same version of LLVM (in fact, this is uncommon).
|
||||||
|
|
||||||
|
-LLVM version 19 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code.
|
||||||
|
+LLVM version 19 is the officially supported version. You can modify if needed using the `LLVM_VERSION` env var during configure. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code.
|
||||||
|
|
||||||
|
It's easy to install all of the required tools:
|
||||||
|
|
||||||
|
Index: Python-3.14.0/Tools/jit/_llvm.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.14.0.orig/Tools/jit/_llvm.py
|
||||||
|
+++ Python-3.14.0/Tools/jit/_llvm.py
|
||||||
|
@@ -10,8 +10,8 @@ import typing
|
||||||
|
|
||||||
|
import _targets
|
||||||
|
|
||||||
|
-_LLVM_VERSION = 19
|
||||||
|
-_LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+")
|
||||||
|
+
|
||||||
|
+_LLVM_VERSION = "19"
|
||||||
|
_EXTERNALS_LLVM_TAG = "llvm-19.1.7.0"
|
||||||
|
|
||||||
|
_P = typing.ParamSpec("_P")
|
||||||
|
@@ -56,53 +56,66 @@ async def _run(tool: str, args: typing.I
|
||||||
|
|
||||||
|
|
||||||
|
@_async_cache
|
||||||
|
-async def _check_tool_version(name: str, *, echo: bool = False) -> bool:
|
||||||
|
+async def _check_tool_version(
|
||||||
|
+ name: str, llvm_version: str, *, echo: bool = False
|
||||||
|
+) -> bool:
|
||||||
|
output = await _run(name, ["--version"], echo=echo)
|
||||||
|
- return bool(output and _LLVM_VERSION_PATTERN.search(output))
|
||||||
|
+ _llvm_version_pattern = re.compile(rf"version\s+{llvm_version}\.\d+\.\d+\S*\s+")
|
||||||
|
+ return bool(output and _llvm_version_pattern.search(output))
|
||||||
|
|
||||||
|
|
||||||
|
@_async_cache
|
||||||
|
-async def _get_brew_llvm_prefix(*, echo: bool = False) -> str | None:
|
||||||
|
- output = await _run("brew", ["--prefix", f"llvm@{_LLVM_VERSION}"], echo=echo)
|
||||||
|
+async def _get_brew_llvm_prefix(llvm_version: str, *, echo: bool = False) -> str | None:
|
||||||
|
+ output = await _run("brew", ["--prefix", f"llvm@{llvm_version}"], echo=echo)
|
||||||
|
return output and output.removesuffix("\n")
|
||||||
|
|
||||||
|
|
||||||
|
@_async_cache
|
||||||
|
-async def _find_tool(tool: str, *, echo: bool = False) -> str | None:
|
||||||
|
+async def _find_tool(tool: str, llvm_version: str, *, echo: bool = False) -> str | None:
|
||||||
|
# Unversioned executables:
|
||||||
|
path = tool
|
||||||
|
- if await _check_tool_version(path, echo=echo):
|
||||||
|
+ if await _check_tool_version(path, llvm_version, echo=echo):
|
||||||
|
return path
|
||||||
|
# Versioned executables:
|
||||||
|
- path = f"{tool}-{_LLVM_VERSION}"
|
||||||
|
- if await _check_tool_version(path, echo=echo):
|
||||||
|
+ path = f"{tool}-{llvm_version}"
|
||||||
|
+ if await _check_tool_version(path, llvm_version, echo=echo):
|
||||||
|
return path
|
||||||
|
# PCbuild externals:
|
||||||
|
externals = os.environ.get("EXTERNALS_DIR", _targets.EXTERNALS)
|
||||||
|
path = os.path.join(externals, _EXTERNALS_LLVM_TAG, "bin", tool)
|
||||||
|
- if await _check_tool_version(path, echo=echo):
|
||||||
|
+ if await _check_tool_version(path, llvm_version, echo=echo):
|
||||||
|
return path
|
||||||
|
# Homebrew-installed executables:
|
||||||
|
- prefix = await _get_brew_llvm_prefix(echo=echo)
|
||||||
|
+ prefix = await _get_brew_llvm_prefix(llvm_version, echo=echo)
|
||||||
|
if prefix is not None:
|
||||||
|
path = os.path.join(prefix, "bin", tool)
|
||||||
|
- if await _check_tool_version(path, echo=echo):
|
||||||
|
+ if await _check_tool_version(path, llvm_version, echo=echo):
|
||||||
|
return path
|
||||||
|
# Nothing found:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def maybe_run(
|
||||||
|
- tool: str, args: typing.Iterable[str], echo: bool = False
|
||||||
|
+ tool: str,
|
||||||
|
+ args: typing.Iterable[str],
|
||||||
|
+ echo: bool = False,
|
||||||
|
+ llvm_version: str = _LLVM_VERSION,
|
||||||
|
) -> str | None:
|
||||||
|
"""Run an LLVM tool if it can be found. Otherwise, return None."""
|
||||||
|
- path = await _find_tool(tool, echo=echo)
|
||||||
|
+
|
||||||
|
+ path = await _find_tool(tool, llvm_version, echo=echo)
|
||||||
|
return path and await _run(path, args, echo=echo)
|
||||||
|
|
||||||
|
|
||||||
|
-async def run(tool: str, args: typing.Iterable[str], echo: bool = False) -> str:
|
||||||
|
+async def run(
|
||||||
|
+ tool: str,
|
||||||
|
+ args: typing.Iterable[str],
|
||||||
|
+ echo: bool = False,
|
||||||
|
+ llvm_version: str = _LLVM_VERSION,
|
||||||
|
+) -> str:
|
||||||
|
"""Run an LLVM tool if it can be found. Otherwise, raise RuntimeError."""
|
||||||
|
- output = await maybe_run(tool, args, echo=echo)
|
||||||
|
+
|
||||||
|
+ output = await maybe_run(tool, args, echo=echo, llvm_version=llvm_version)
|
||||||
|
if output is None:
|
||||||
|
- raise RuntimeError(f"Can't find {tool}-{_LLVM_VERSION}!")
|
||||||
|
+ raise RuntimeError(f"Can't find {tool}-{llvm_version}!")
|
||||||
|
return output
|
||||||
|
Index: Python-3.14.0/Tools/jit/_targets.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.14.0.orig/Tools/jit/_targets.py
|
||||||
|
+++ Python-3.14.0/Tools/jit/_targets.py
|
||||||
|
@@ -48,6 +48,7 @@ class _Target(typing.Generic[_S, _R]):
|
||||||
|
debug: bool = False
|
||||||
|
verbose: bool = False
|
||||||
|
cflags: str = ""
|
||||||
|
+ llvm_version: str = _llvm._LLVM_VERSION
|
||||||
|
known_symbols: dict[str, int] = dataclasses.field(default_factory=dict)
|
||||||
|
pyconfig_dir: pathlib.Path = pathlib.Path.cwd().resolve()
|
||||||
|
|
||||||
|
@@ -79,7 +80,9 @@ class _Target(typing.Generic[_S, _R]):
|
||||||
|
async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup:
|
||||||
|
group = _stencils.StencilGroup()
|
||||||
|
args = ["--disassemble", "--reloc", f"{path}"]
|
||||||
|
- output = await _llvm.maybe_run("llvm-objdump", args, echo=self.verbose)
|
||||||
|
+ output = await _llvm.maybe_run(
|
||||||
|
+ "llvm-objdump", args, echo=self.verbose, llvm_version=self.llvm_version
|
||||||
|
+ )
|
||||||
|
if output is not None:
|
||||||
|
# Make sure that full paths don't leak out (for reproducibility):
|
||||||
|
long, short = str(path), str(path.name)
|
||||||
|
@@ -97,7 +100,9 @@ class _Target(typing.Generic[_S, _R]):
|
||||||
|
"--sections",
|
||||||
|
f"{path}",
|
||||||
|
]
|
||||||
|
- output = await _llvm.run("llvm-readobj", args, echo=self.verbose)
|
||||||
|
+ output = await _llvm.run(
|
||||||
|
+ "llvm-readobj", args, echo=self.verbose, llvm_version=self.llvm_version
|
||||||
|
+ )
|
||||||
|
# --elf-output-style=JSON is only *slightly* broken on Mach-O...
|
||||||
|
output = output.replace("PrivateExtern\n", "\n")
|
||||||
|
output = output.replace("Extern\n", "\n")
|
||||||
|
@@ -164,7 +169,9 @@ class _Target(typing.Generic[_S, _R]):
|
||||||
|
# Allow user-provided CFLAGS to override any defaults
|
||||||
|
*shlex.split(self.cflags),
|
||||||
|
]
|
||||||
|
- await _llvm.run("clang", args, echo=self.verbose)
|
||||||
|
+ await _llvm.run(
|
||||||
|
+ "clang", args, echo=self.verbose, llvm_version=self.llvm_version
|
||||||
|
+ )
|
||||||
|
return await self._parse(o)
|
||||||
|
|
||||||
|
async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]:
|
||||||
|
@@ -212,6 +219,8 @@ class _Target(typing.Generic[_S, _R]):
|
||||||
|
if not self.stable:
|
||||||
|
warning = f"JIT support for {self.triple} is still experimental!"
|
||||||
|
request = "Please report any issues you encounter.".center(len(warning))
|
||||||
|
+ if self.llvm_version != _llvm._LLVM_VERSION:
|
||||||
|
+ request = f"Warning! Building with an LLVM version other than {_llvm._LLVM_VERSION} is not supported."
|
||||||
|
outline = "=" * len(warning)
|
||||||
|
print("\n".join(["", outline, warning, request, outline, ""]))
|
||||||
|
digest = f"// {self._compute_digest()}\n"
|
||||||
|
Index: Python-3.14.0/Tools/jit/build.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.14.0.orig/Tools/jit/build.py
|
||||||
|
+++ Python-3.14.0/Tools/jit/build.py
|
||||||
|
@@ -42,6 +42,7 @@ if __name__ == "__main__":
|
||||||
|
parser.add_argument(
|
||||||
|
"--cflags", help="additional flags to pass to the compiler", default=""
|
||||||
|
)
|
||||||
|
+ parser.add_argument("--llvm-version", help="LLVM version to use")
|
||||||
|
args = parser.parse_args()
|
||||||
|
for target in args.target:
|
||||||
|
target.debug = args.debug
|
||||||
|
@@ -49,6 +50,8 @@ if __name__ == "__main__":
|
||||||
|
target.verbose = args.verbose
|
||||||
|
target.cflags = args.cflags
|
||||||
|
target.pyconfig_dir = args.pyconfig_dir
|
||||||
|
+ if args.llvm_version:
|
||||||
|
+ target.llvm_version = args.llvm_version
|
||||||
|
target.build(
|
||||||
|
comment=comment,
|
||||||
|
force=args.force,
|
||||||
|
Index: Python-3.14.0/configure
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.14.0.orig/configure
|
||||||
|
+++ Python-3.14.0/configure
|
||||||
|
@@ -10866,7 +10866,7 @@ then :
|
||||||
|
|
||||||
|
else case e in #(
|
||||||
|
e) as_fn_append CFLAGS_NODIST " $jit_flags"
|
||||||
|
- REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\""
|
||||||
|
+ REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\" --llvm-version=\"$LLVM_VERSION\""
|
||||||
|
JIT_STENCILS_H="jit_stencils.h"
|
||||||
|
if test "x$Py_DEBUG" = xtrue
|
||||||
|
then :
|
||||||
|
Index: Python-3.14.0/configure.ac
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.14.0.orig/configure.ac
|
||||||
|
+++ Python-3.14.0/configure.ac
|
||||||
|
@@ -2779,7 +2779,7 @@ AS_VAR_IF([jit_flags],
|
||||||
|
[],
|
||||||
|
[AS_VAR_APPEND([CFLAGS_NODIST], [" $jit_flags"])
|
||||||
|
AS_VAR_SET([REGEN_JIT_COMMAND],
|
||||||
|
- ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\""])
|
||||||
|
+ ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\" --llvm-version=\"$LLVM_VERSION\""])
|
||||||
|
AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"])
|
||||||
|
AS_VAR_IF([Py_DEBUG],
|
||||||
|
[true],
|
||||||
@@ -4,33 +4,151 @@ Date: Tue, 23 Sep 2025 10:20:16 +0200
|
|||||||
Subject: [PATCH 1/2] gh-139257: Support docutils >= 0.22
|
Subject: [PATCH 1/2] gh-139257: Support docutils >= 0.22
|
||||||
|
|
||||||
---
|
---
|
||||||
Doc/tools/extensions/pyspecific.py | 12 +++++++++++-
|
Doc/tools/extensions/pyspecific.py | 68 +++++++++++++++++++++++++------------
|
||||||
1 file changed, 11 insertions(+), 1 deletion(-)
|
1 file changed, 46 insertions(+), 22 deletions(-)
|
||||||
|
|
||||||
Index: Python-3.14.0/Doc/tools/extensions/pyspecific.py
|
Index: Python-3.14.2/Doc/tools/extensions/pyspecific.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0.orig/Doc/tools/extensions/pyspecific.py
|
--- Python-3.14.2.orig/Doc/tools/extensions/pyspecific.py 2025-12-05 17:49:16.000000000 +0100
|
||||||
+++ Python-3.14.0/Doc/tools/extensions/pyspecific.py
|
+++ Python-3.14.2/Doc/tools/extensions/pyspecific.py 2025-12-11 18:15:44.936875242 +0100
|
||||||
@@ -25,11 +25,21 @@ from sphinx.util.docutils import SphinxD
|
@@ -1,12 +1,12 @@
|
||||||
SOURCE_URI = 'https://github.com/python/cpython/tree/3.14/%s'
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
- pyspecific.py
|
||||||
|
- ~~~~~~~~~~~~~
|
||||||
|
+pyspecific.py
|
||||||
|
+~~~~~~~~~~~~~
|
||||||
|
|
||||||
# monkey-patch reST parser to disable alphabetic and roman enumerated lists
|
- Sphinx extension with Python doc-specific markup.
|
||||||
|
+Sphinx extension with Python doc-specific markup.
|
||||||
|
|
||||||
|
- :copyright: 2008-2014 by Georg Brandl.
|
||||||
|
- :license: Python license.
|
||||||
|
+:copyright: 2008-2014 by Georg Brandl.
|
||||||
|
+:license: Python license.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
@@ -15,6 +15,7 @@
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from docutils.parsers.rst import directives
|
||||||
|
+from docutils.parsers.rst.states import Body
|
||||||
|
from docutils.utils import unescape
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.domains.python import PyFunction, PyMethod, PyModule
|
||||||
|
@@ -22,30 +23,48 @@
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
|
|
||||||
|
# Used in conf.py and updated here by python/release-tools/run_release.py
|
||||||
|
-SOURCE_URI = 'https://github.com/python/cpython/tree/3.14/%s'
|
||||||
|
+SOURCE_URI = "https://github.com/python/cpython/tree/3.14/%s"
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+# monkey-patch reST parser to disable alphabetic and roman enumerated lists
|
||||||
+def _disable_alphabetic_and_roman(text):
|
+def _disable_alphabetic_and_roman(text):
|
||||||
+ try:
|
+ try:
|
||||||
+ # docutils >= 0.22
|
+ # docutils >= 0.22
|
||||||
+ from docutils.parsers.rst.states import InvalidRomanNumeralError
|
+ from docutils.parsers.rst.states import InvalidRomanNumeralError
|
||||||
|
+
|
||||||
+ raise InvalidRomanNumeralError(text)
|
+ raise InvalidRomanNumeralError(text)
|
||||||
+ except ImportError:
|
+ except ImportError:
|
||||||
+ # docutils < 0.22
|
+ # docutils < 0.22
|
||||||
+ return None
|
+ return None
|
||||||
+
|
+
|
||||||
+
|
+
|
||||||
from docutils.parsers.rst.states import Body
|
+Body.enum.converters["loweralpha"] = Body.enum.converters["upperalpha"] = (
|
||||||
Body.enum.converters['loweralpha'] = \
|
+ Body.enum.converters["lowerroman"]
|
||||||
Body.enum.converters['upperalpha'] = \
|
+) = Body.enum.converters["upperroman"] = _disable_alphabetic_and_roman
|
||||||
Body.enum.converters['lowerroman'] = \
|
+
|
||||||
- Body.enum.converters['upperroman'] = lambda x: None
|
|
||||||
+ Body.enum.converters['upperroman'] = _disable_alphabetic_and_roman
|
|
||||||
|
|
||||||
|
|
||||||
class PyAwaitableMixin(object):
|
class PyAwaitableMixin(object):
|
||||||
|
def handle_signature(self, sig, signode):
|
||||||
|
ret = super(PyAwaitableMixin, self).handle_signature(sig, signode)
|
||||||
|
- signode.insert(0, addnodes.desc_annotation('awaitable ', 'awaitable '))
|
||||||
|
+ signode.insert(0, addnodes.desc_annotation("awaitable ", "awaitable "))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class PyAwaitableFunction(PyAwaitableMixin, PyFunction):
|
||||||
|
def run(self):
|
||||||
|
- self.name = 'py:function'
|
||||||
|
+ self.name = "py:function"
|
||||||
|
return PyFunction.run(self)
|
||||||
|
|
||||||
|
|
||||||
|
class PyAwaitableMethod(PyAwaitableMixin, PyMethod):
|
||||||
|
def run(self):
|
||||||
|
- self.name = 'py:method'
|
||||||
|
+ self.name = "py:method"
|
||||||
|
return PyMethod.run(self)
|
||||||
|
|
||||||
|
|
||||||
|
# Support for documenting Opcodes
|
||||||
|
|
||||||
|
-opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')
|
||||||
|
+opcode_sig_re = re.compile(r"(\w+(?:\+\d)?)(?:\s*\((.*)\))?")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_opcode_signature(env, sig, signode):
|
||||||
|
@@ -64,7 +83,7 @@
|
||||||
|
|
||||||
|
# Support for documenting pdb commands
|
||||||
|
|
||||||
|
-pdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)')
|
||||||
|
+pdbcmd_sig_re = re.compile(r"([a-z()!]+)\s*(.*)")
|
||||||
|
|
||||||
|
# later...
|
||||||
|
# pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+ | # identifiers
|
||||||
|
@@ -80,16 +99,16 @@
|
||||||
|
if m is None:
|
||||||
|
raise ValueError
|
||||||
|
name, args = m.groups()
|
||||||
|
- fullname = name.replace('(', '').replace(')', '')
|
||||||
|
+ fullname = name.replace("(", "").replace(")", "")
|
||||||
|
signode += addnodes.desc_name(name, name)
|
||||||
|
if args:
|
||||||
|
- signode += addnodes.desc_addname(' '+args, ' '+args)
|
||||||
|
+ signode += addnodes.desc_addname(" " + args, " " + args)
|
||||||
|
return fullname
|
||||||
|
|
||||||
|
|
||||||
|
def parse_monitoring_event(env, sig, signode):
|
||||||
|
"""Transform a monitoring event signature into RST nodes."""
|
||||||
|
- signode += addnodes.desc_addname('sys.monitoring.events.', 'sys.monitoring.events.')
|
||||||
|
+ signode += addnodes.desc_addname("sys.monitoring.events.", "sys.monitoring.events.")
|
||||||
|
signode += addnodes.desc_name(sig, sig)
|
||||||
|
return sig
|
||||||
|
|
||||||
|
@@ -102,7 +121,7 @@
|
||||||
|
As such, we link this to ``env-check-consistency``, even though it has
|
||||||
|
nothing to do with the environment consistency check.
|
||||||
|
"""
|
||||||
|
- if app.builder.name != 'gettext':
|
||||||
|
+ if app.builder.name != "gettext":
|
||||||
|
return
|
||||||
|
|
||||||
|
# allow translating deprecated index entries
|
||||||
|
@@ -119,10 +138,15 @@
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
- app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
|
||||||
|
- app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)
|
||||||
|
- app.add_object_type('monitoring-event', 'monitoring-event', '%s (monitoring event)', parse_monitoring_event)
|
||||||
|
- app.add_directive_to_domain('py', 'awaitablefunction', PyAwaitableFunction)
|
||||||
|
- app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod)
|
||||||
|
- app.connect('env-check-consistency', patch_pairindextypes)
|
||||||
|
- return {'version': '1.0', 'parallel_read_safe': True}
|
||||||
|
+ app.add_object_type("opcode", "opcode", "%s (opcode)", parse_opcode_signature)
|
||||||
|
+ app.add_object_type("pdbcommand", "pdbcmd", "%s (pdb command)", parse_pdb_command)
|
||||||
|
+ app.add_object_type(
|
||||||
|
+ "monitoring-event",
|
||||||
|
+ "monitoring-event",
|
||||||
|
+ "%s (monitoring event)",
|
||||||
|
+ parse_monitoring_event,
|
||||||
|
+ )
|
||||||
|
+ app.add_directive_to_domain("py", "awaitablefunction", PyAwaitableFunction)
|
||||||
|
+ app.add_directive_to_domain("py", "awaitablemethod", PyAwaitableMethod)
|
||||||
|
+ app.connect("env-check-consistency", patch_pairindextypes)
|
||||||
|
+ return {"version": "1.0", "parallel_read_safe": True}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
Makefile.pre.in | 5 +++++
|
Makefile.pre.in | 5 +++++
|
||||||
1 file changed, 5 insertions(+)
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
Index: Python-3.14.0/Makefile.pre.in
|
Index: Python-3.14.2/Makefile.pre.in
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.14.0.orig/Makefile.pre.in 2025-10-07 11:34:52.000000000 +0200
|
--- Python-3.14.2.orig/Makefile.pre.in 2025-12-11 18:11:54.290805770 +0100
|
||||||
+++ Python-3.14.0/Makefile.pre.in 2025-11-06 23:19:25.434995608 +0100
|
+++ Python-3.14.2/Makefile.pre.in 2025-12-11 18:11:58.776792838 +0100
|
||||||
@@ -1910,6 +1910,11 @@
|
@@ -1914,6 +1914,11 @@
|
||||||
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \
|
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \
|
||||||
-o $@ $(srcdir)/Modules/getbuildinfo.c
|
-o $@ $(srcdir)/Modules/getbuildinfo.c
|
||||||
|
|
||||||
|
|||||||
4
python314-base.rpmlintrc
Normal file
4
python314-base.rpmlintrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
addFilter("pem-certificate.*/usr/lib.*/python.*/test/*.pem")
|
||||||
|
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/tests/*.c")
|
||||||
|
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/*.cpp")
|
||||||
|
addFilter("python-bytecode-inconsistent-mtime.*/usr/lib.*/python.*/*.pyc")
|
||||||
@@ -1,3 +1,619 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu Dec 12 07:00:00 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
|
||||||
|
|
||||||
|
- Use LLVM21 to build python314, add patch
|
||||||
|
gh138498-llvm-version-config.patch
|
||||||
|
bsc#1254826, gh#python/cpython#138498
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu Dec 11 17:37:09 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||||
|
|
||||||
|
* Update to 3.14.2:
|
||||||
|
- Security
|
||||||
|
- gh-142145: Remove quadratic behavior in xml.minidom node ID
|
||||||
|
cache clearing.
|
||||||
|
- gh-119452: Fix a potential memory denial of service in the
|
||||||
|
http.server module. When a malicious user is connected to the
|
||||||
|
CGI server on Windows, it could cause an arbitrary amount of
|
||||||
|
memory to be allocated. This could have led to symptoms
|
||||||
|
including a MemoryError, swapping, out of memory (OOM) killed
|
||||||
|
processes or containers, or even system crashes.
|
||||||
|
- Library
|
||||||
|
- gh-140797: Revert changes to the undocumented re.Scanner
|
||||||
|
class. Capturing groups are still allowed for backward
|
||||||
|
compatibility, although using them can lead to incorrect
|
||||||
|
result. They will be forbidden in future Python versions.
|
||||||
|
- gh-142206: The resource tracker in the multiprocessing module
|
||||||
|
now uses the original communication protocol, as in Python
|
||||||
|
3.14.0 and below, by default. This avoids issues with
|
||||||
|
upgrading Python while it is running. (Note that such
|
||||||
|
‘in-place’ upgrades are not tested.) The tracker remains
|
||||||
|
compatible with subprocesses that use new protocol (that is,
|
||||||
|
subprocesses using Python 3.13.10, 3.14.1 and 3.15).
|
||||||
|
- gh-142214: Fix two regressions in dataclasses in Python
|
||||||
|
3.14.1 related to annotations. An exception is no longer
|
||||||
|
raised if slots=True is used and the __init__ method does not
|
||||||
|
have an __annotate__ attribute (likely because init=False was
|
||||||
|
used). An exception is no longer raised if annotations are
|
||||||
|
requested on the __init__ method and one of the fields is not
|
||||||
|
present in the class annotations. This can occur in certain
|
||||||
|
dynamic scenarios. Patch by Jelle Zijlstra.
|
||||||
|
- Core and Builtins
|
||||||
|
- gh-142218: Fix crash when inserting into a split table
|
||||||
|
dictionary with a non str key that matches an existing key.
|
||||||
|
- gh-116738: Fix cmath data race when initializing
|
||||||
|
trigonometric tables with subinterpreters.
|
||||||
|
* Update to 3.14.1:
|
||||||
|
- Tools/Demos
|
||||||
|
- gh-141692: Each slice of an iOS XCframework now contains
|
||||||
|
a lib folder that contains a symlink to the libpython dylib.
|
||||||
|
This allows binary modules to be compiled for iOS using
|
||||||
|
dynamic libreary linking, rather than Framework linking.
|
||||||
|
- gh-141442: The iOS testbed now correctly handles test
|
||||||
|
arguments that contain spaces.
|
||||||
|
- gh-140702: The iOS testbed app will now expose the
|
||||||
|
GITHUB_ACTIONS environment variable to iOS apps being tested.
|
||||||
|
- gh-137484: Have Tools/wasm/wasi put the build Python into
|
||||||
|
a directory named after the build triple instead of “build”.
|
||||||
|
- gh-137248: Add a --logdir option to Tools/wasm/wasi for
|
||||||
|
specifying where to write log files.
|
||||||
|
- gh-137243: Have Tools/wasm/wasi detect a WASI SDK install in
|
||||||
|
/opt when it was directly extracted from a release tarball.
|
||||||
|
- Tests
|
||||||
|
- gh-140482: Preserve and restore the state of stty echo as
|
||||||
|
part of the test environment.
|
||||||
|
- gh-140082: Update python -m test to set FORCE_COLOR=1 when
|
||||||
|
being run with color enabled so that unittest which is run by
|
||||||
|
it with redirected output will output in color.
|
||||||
|
- gh-139208: Fix regrtest --fast-ci --verbose: don’t ignore the
|
||||||
|
--verbose option anymore. Patch by Victor Stinner.
|
||||||
|
- gh-136442: Use exitcode 1 instead of 5 if
|
||||||
|
unittest.TestCase.setUpClass() raises an exception
|
||||||
|
- Security
|
||||||
|
- gh-139700: Check consistency of the zip64 end of central
|
||||||
|
directory record. Support records with “zip64 extensible
|
||||||
|
data” if there are no bytes prepended to the ZIP file.
|
||||||
|
- gh-139283: sqlite3: correctly handle maximum number of rows
|
||||||
|
to fetch in Cursor.fetchmany and reject negative values for
|
||||||
|
Cursor.arraysize. Patch by Bénédikt Tran. (CVE-2025-8291,
|
||||||
|
bsc#1251305)
|
||||||
|
- gh-137836: Add support of the “plaintext” element, RAWTEXT
|
||||||
|
elements “xmp”, “iframe”, “noembed” and “noframes”, and
|
||||||
|
optionally RAWTEXT element “noscript” in
|
||||||
|
html.parser.HTMLParser.
|
||||||
|
- gh-136063: email.message: ensure linear complexity for legacy
|
||||||
|
HTTP parameters parsing. Patch by Bénédikt Tran.
|
||||||
|
- gh-136065: Fix quadratic complexity in os.path.expandvars()
|
||||||
|
(CVE-2025-6075, bsc#1252974)
|
||||||
|
- gh-119451: Fix a potential memory denial of service in the
|
||||||
|
http.client module. When connecting to a malicious server, it
|
||||||
|
could cause an arbitrary amount of memory to be allocated.
|
||||||
|
This could have led to symptoms including a MemoryError,
|
||||||
|
swapping, out of memory (OOM) killed processes or containers,
|
||||||
|
or even system crashes (CVE-2025-13836, bsc#1254400)
|
||||||
|
- gh-119342: Fix a potential memory denial of service in the
|
||||||
|
plistlib module. When reading a Plist file received from
|
||||||
|
untrusted source, it could cause an arbitrary amount of
|
||||||
|
memory to be allocated. This could have led to symptoms
|
||||||
|
including a MemoryError, swapping, out of memory (OOM) killed
|
||||||
|
processes or containers, or even system crashes
|
||||||
|
(CVE-2025-13837, bsc#1254401).
|
||||||
|
- Library
|
||||||
|
- gh-74389: When the stdin being used by a subprocess.Popen
|
||||||
|
instance is closed, this is now ignored in
|
||||||
|
subprocess.Popen.communicate() instead of leaving the class
|
||||||
|
in an inconsistent state.
|
||||||
|
- gh-87512: Fix subprocess.Popen.communicate() timeout handling
|
||||||
|
on Windows when writing large input. Previously, the timeout
|
||||||
|
was ignored during stdin writing, causing the method to block
|
||||||
|
indefinitely if the child process did not consume input
|
||||||
|
quickly. The stdin write is now performed in a background
|
||||||
|
thread, allowing the timeout to be properly enforced.
|
||||||
|
- gh-141473: When subprocess.Popen.communicate() was called
|
||||||
|
with input and a timeout and is called for a second time
|
||||||
|
after a TimeoutExpired exception before the process has died,
|
||||||
|
it should no longer hang.
|
||||||
|
- gh-59000: Fix pdb breakpoint resolution for class methods
|
||||||
|
when the module defining the class is not imported.
|
||||||
|
- gh-141570: Support file-like object raising OSError from
|
||||||
|
fileno() in color detection (_colorize.can_colorize()). This
|
||||||
|
can occur when sys.stdout is redirected.
|
||||||
|
- gh-141659: Fix bad file descriptor errors from
|
||||||
|
_posixsubprocess on AIX.
|
||||||
|
- gh-141600: Fix musl version detection on Void Linux.
|
||||||
|
- gh-141497: ipaddress: ensure that the methods
|
||||||
|
IPv4Network.hosts() and IPv6Network.hosts() always return an
|
||||||
|
iterator.
|
||||||
|
- gh-140938: The statistics.stdev() and statistics.pstdev()
|
||||||
|
functions now raise a ValueError when the input contains an
|
||||||
|
infinity or a NaN.
|
||||||
|
- gh-124111: Updated Tcl threading configuration in _tkinter to
|
||||||
|
assume that threads are always available in Tcl 9 and later.
|
||||||
|
- gh-137109: The os.fork and related forking APIs will no
|
||||||
|
longer warn in the common case where Linux or macOS platform
|
||||||
|
APIs return the number of threads in a process and find the
|
||||||
|
answer to be 1 even when a os.register_at_fork()
|
||||||
|
after_in_parent= callback (re)starts a thread.
|
||||||
|
- gh-141314: Fix assertion failure in io.TextIOWrapper.tell()
|
||||||
|
when reading files with standalone carriage return (\r) line
|
||||||
|
endings.
|
||||||
|
- gh-141311: Fix assertion failure in io.BytesIO.readinto() and
|
||||||
|
undefined behavior arising when read position is above
|
||||||
|
capcity in io.BytesIO.
|
||||||
|
- gh-141141: Fix a thread safety issue with base64.b85decode().
|
||||||
|
Contributed by Benel Tayar.
|
||||||
|
- gh-137969: Fix annotationlib.ForwardRef.evaluate() returning
|
||||||
|
ForwardRef objects which don’t update with new globals.
|
||||||
|
- gh-140911: collections: Ensure that the methods
|
||||||
|
UserString.rindex() and UserString.index() accept
|
||||||
|
collections.UserString instances as the sub argument.
|
||||||
|
- gh-140797: The undocumented re.Scanner class now forbids
|
||||||
|
regular expressions containing capturing groups in its
|
||||||
|
lexicon patterns. Patterns using capturing groups could
|
||||||
|
previously lead to crashes with segmentation fault. Use
|
||||||
|
non-capturing groups (?:…) instead.
|
||||||
|
- gh-125115: Refactor the pdb parsing issue so positional
|
||||||
|
arguments can pass through intuitively.
|
||||||
|
- gh-140815: faulthandler now detects if a frame or a code
|
||||||
|
object is invalid or freed. Patch by Victor Stinner.
|
||||||
|
- gh-100218: Correctly set errno when socket.if_nametoindex()
|
||||||
|
or socket.if_indextoname() raise an OSError. Patch by
|
||||||
|
Bénédikt Tran.
|
||||||
|
- gh-140875: Fix handling of unclosed character references
|
||||||
|
(named and numerical) followed by the end of file in
|
||||||
|
html.parser.HTMLParser with convert_charrefs=False.
|
||||||
|
- gh-140734: multiprocessing: fix off-by-one error when
|
||||||
|
checking the length of a temporary socket file path. Patch by
|
||||||
|
Bénédikt Tran.
|
||||||
|
- gh-140874: Bump the version of pip bundled in ensurepip to
|
||||||
|
version 25.3
|
||||||
|
- gh-140691: In urllib.request, when opening a FTP URL fails
|
||||||
|
because a data connection cannot be made, the control
|
||||||
|
connection’s socket is now closed to avoid a ResourceWarning.
|
||||||
|
- gh-103847: Fix hang when cancelling process created by
|
||||||
|
asyncio.create_subprocess_exec() or
|
||||||
|
asyncio.create_subprocess_shell(). Patch by Kumar Aditya.
|
||||||
|
- gh-120057: Add os.reload_environ() to os.__all__.
|
||||||
|
- gh-140228: Avoid making unnecessary filesystem calls for
|
||||||
|
frozen modules in linecache when the global module cache is
|
||||||
|
not present.
|
||||||
|
- gh-140590: Fix arguments checking for the
|
||||||
|
functools.partial.__setstate__() that may lead to internal
|
||||||
|
state corruption and crash. Patch by Sergey Miryanov.
|
||||||
|
- gh-125434: Display thread name in faulthandler on Windows.
|
||||||
|
Patch by Victor Stinner.
|
||||||
|
- gh-140634: Fix a reference counting bug in
|
||||||
|
os.sched_param.__reduce__().
|
||||||
|
- gh-140633: Ignore AttributeError when setting a module’s
|
||||||
|
__file__ attribute when loading an extension module packaged
|
||||||
|
as Apple Framework.
|
||||||
|
- gh-140593: xml.parsers.expat: Fix a memory leak that could
|
||||||
|
affect users with ElementDeclHandler() set to a custom
|
||||||
|
element declaration handler. Patch by Sebastian Pipping.
|
||||||
|
- gh-140607: Inside io.RawIOBase.read(), validate that the
|
||||||
|
count of bytes returned by io.RawIOBase.readinto() is valid
|
||||||
|
(inside the provided buffer).
|
||||||
|
- gh-138162: Fix logging.LoggerAdapter with merge_extra=True
|
||||||
|
and without the extra argument.
|
||||||
|
- gh-138774: ast.unparse() now generates full source code when
|
||||||
|
handling ast.Interpolation nodes that do not have a specified
|
||||||
|
source.
|
||||||
|
- gh-140474: Fix memory leak in array.array when creating
|
||||||
|
arrays from an empty str and the u type code.
|
||||||
|
- gh-137530: dataclasses Fix annotations for generated __init__
|
||||||
|
methods by replacing the annotations that were in-line in the
|
||||||
|
generated source code with __annotate__ functions attached to
|
||||||
|
the methods.
|
||||||
|
- gh-140348: Fix regression in Python 3.14.0 where using the
|
||||||
|
| operator on a typing.Union object combined with an object
|
||||||
|
that is not a type would raise an error.
|
||||||
|
- gh-140272: Fix memory leak in the clear() method of the
|
||||||
|
dbm.gnu database.
|
||||||
|
- gh-140041: Fix import of ctypes on Android and Cygwin when
|
||||||
|
ABI flags are present.
|
||||||
|
- gh-140120: Fixed a memory leak in hmac when it was using the
|
||||||
|
hacl-star backend. Discovered by @ashm-dev using
|
||||||
|
AddressSanitizer.
|
||||||
|
- gh-139905: Add suggestion to error message for typing.Generic
|
||||||
|
subclasses when cls.__parameters__ is missing due to a parent
|
||||||
|
class failing to call super().__init_subclass__() in its
|
||||||
|
__init_subclass__.
|
||||||
|
- gh-139894: Fix incorrect sharing of current task with the
|
||||||
|
child process while forking in asyncio. Patch by Kumar
|
||||||
|
Aditya.
|
||||||
|
- gh-139845: Fix to not print KeyboardInterrupt twice in
|
||||||
|
default asyncio REPL.
|
||||||
|
- gh-139783: Fix inspect.getsourcelines() for the case when
|
||||||
|
a decorator is followed by a comment or an empty line.
|
||||||
|
- gh-139809: Prevent premature colorization of subparser prog
|
||||||
|
in argparse.ArgumentParser.add_subparsers() to respect color
|
||||||
|
environment variable changes after parser creation.
|
||||||
|
- gh-139736: Fix excessive indentation in the default argparse
|
||||||
|
HelpFormatter. Patch by Alexander Edland.
|
||||||
|
- gh-70765: http.server: fix default handling of HTTP/0.9
|
||||||
|
requests in BaseHTTPRequestHandler. Previously,
|
||||||
|
BaseHTTPRequestHandler.parse_request() incorrectly waited for
|
||||||
|
headers in the request although those are not supported in
|
||||||
|
HTTP/0.9. Patch by Bénédikt Tran.
|
||||||
|
- gh-63161: Fix tokenize.detect_encoding(). Support non-UTF-8
|
||||||
|
shebang and comments if non-UTF-8 encoding is specified.
|
||||||
|
Detect decoding error for non-UTF-8 encoding. Detect null
|
||||||
|
bytes in source code.
|
||||||
|
- gh-139391: Fix an issue when, on non-Windows platforms, it
|
||||||
|
was not possible to gracefully exit a python -m asyncio
|
||||||
|
process suspended by Ctrl+Z and later resumed by fg other
|
||||||
|
than with kill.
|
||||||
|
- gh-101828: Fix 'shift_jisx0213', 'shift_jis_2004',
|
||||||
|
'euc_jisx0213' and 'euc_jis_2004' codecs truncating null
|
||||||
|
chars as they were treated as part of multi-character
|
||||||
|
sequences.
|
||||||
|
- gh-139289: Do a real lazy-import on rlcompleter in pdb and
|
||||||
|
restore the existing completer after importing rlcompleter.
|
||||||
|
- gh-139246: fix: paste zero-width in default repl width is
|
||||||
|
wrong.
|
||||||
|
- gh-90949: Add SetAllocTrackerActivationThreshold() and
|
||||||
|
SetAllocTrackerMaximumAmplification() to xmlparser objects to
|
||||||
|
prevent use of disproportional amounts of dynamic memory from
|
||||||
|
within an Expat parser. Patch by Bénédikt Tran.
|
||||||
|
- gh-139210: Fix use-after-free when reporting unknown event in
|
||||||
|
xml.etree.ElementTree.iterparse(). Patch by Ken Jin.
|
||||||
|
- gh-138860: Lazy import rlcompleter in pdb to avoid deadlock
|
||||||
|
in subprocess.
|
||||||
|
- gh-112729: Fix crash when calling
|
||||||
|
concurrent.interpreters.create() when the process is out of
|
||||||
|
memory.
|
||||||
|
- gh-135729: Fix unraisable exception during finalization when
|
||||||
|
using concurrent.interpreters in the REPL.
|
||||||
|
- gh-139076: Fix a bug in the pydoc module that was hiding
|
||||||
|
functions in a Python module if they were implemented in an
|
||||||
|
extension module and the module did not have __all__.
|
||||||
|
- gh-139065: Fix trailing space before a wrapped long word if
|
||||||
|
the line length is exactly width in textwrap.
|
||||||
|
- gh-139001: Fix race condition in pathlib.Path on the internal
|
||||||
|
_raw_paths field.
|
||||||
|
- gh-138813: multiprocessing.BaseProcess defaults kwargs to
|
||||||
|
None instead of a shared dictionary.
|
||||||
|
- gh-138993: Dedent credits text.
|
||||||
|
- gh-138891: Fix SyntaxError when inspect.get_annotations(f,
|
||||||
|
eval_str=True) is called on a function annotated with a PEP
|
||||||
|
646 star_expression
|
||||||
|
- gh-130567: Fix possible crash in locale.strxfrm() due to
|
||||||
|
a platform bug on macOS.
|
||||||
|
- gh-138859: Fix generic type parameterization raising
|
||||||
|
a TypeError when omitting a ParamSpec that has a default
|
||||||
|
which is not a list of types.
|
||||||
|
- gh-138764: Prevent annotationlib.call_annotate_function()
|
||||||
|
from calling __annotate__ functions that don’t support
|
||||||
|
VALUE_WITH_FAKE_GLOBALS in a fake globals namespace with
|
||||||
|
empty globals. Make FORWARDREF and STRING annotations fall
|
||||||
|
back to using VALUE annotations in the case that neither
|
||||||
|
their own format, nor VALUE_WITH_FAKE_GLOBALS are supported.
|
||||||
|
- gh-138775: Use of python -m with base64 has been fixed to
|
||||||
|
detect input from a terminal so that it properly notices EOF.
|
||||||
|
- gh-138779: Support device numbers larger than 2**63-1 for the
|
||||||
|
st_rdev field of the os.stat_result structure.
|
||||||
|
- gh-137706: Fix the partial evaluation of annotations that use
|
||||||
|
typing.Annotated[T, x] where T is a forward reference.
|
||||||
|
- gh-88375: Fix normalization of the robots.txt rules and URLs
|
||||||
|
in the urllib.robotparser module. No longer ignore trailing
|
||||||
|
?. Distinguish raw special characters ?, = and & from the
|
||||||
|
percent-encoded ones.
|
||||||
|
- gh-111788: Fix parsing errors in the urllib.robotparser
|
||||||
|
module. Don’t fail trying to parse weird paths. Don’t fail
|
||||||
|
trying to decode non-UTF-8 robots.txt files.
|
||||||
|
- gh-98896: Fix a failure in multiprocessing resource_tracker
|
||||||
|
when SharedMemory names contain colons. Patch by Rani
|
||||||
|
Pinchuk.
|
||||||
|
- gh-138425: Fix partial evaluation of annotationlib.ForwardRef
|
||||||
|
objects which rely on names defined as globals.
|
||||||
|
- gh-138432: zoneinfo.reset_tzpath() will now convert any
|
||||||
|
os.PathLike objects it receives into strings before adding
|
||||||
|
them to TZPATH. It will raise TypeError if anything other
|
||||||
|
than a string is found after this conversion. If given an
|
||||||
|
os.PathLike object that represents a relative path, it will
|
||||||
|
now raise ValueError instead of TypeError, and present a more
|
||||||
|
informative error message.
|
||||||
|
- gh-138008: Fix segmentation faults in the ctypes module due
|
||||||
|
to invalid argtypes. Patch by Dung Nguyen.
|
||||||
|
- gh-60462: Fix locale.strxfrm() on Solaris (and possibly other
|
||||||
|
platforms).
|
||||||
|
- gh-138239: The REPL now highlights type as a soft keyword in
|
||||||
|
type statements.
|
||||||
|
- gh-138204: Forbid expansion of shared anonymous memory maps
|
||||||
|
on Linux, which caused a bus error.
|
||||||
|
- gh-138010: Fix an issue where defining a class with an
|
||||||
|
@warnings.deprecated-decorated base class may not invoke the
|
||||||
|
correct __init_subclass__() method in cases involving
|
||||||
|
multiple inheritance. Patch by Brian Schubert.
|
||||||
|
- gh-138151: In annotationlib, improve evaluation of forward
|
||||||
|
references to nonlocal variables that are not yet defined
|
||||||
|
when the annotations are initially evaluated.
|
||||||
|
- gh-137317: inspect.signature() now correctly handles classes
|
||||||
|
that use a descriptor on a wrapped __init__() or __new__()
|
||||||
|
method. Contributed by Yongyu Yan.
|
||||||
|
- gh-137754: Fix import of the zoneinfo module if the
|
||||||
|
C implementation of the datetime module is not available.
|
||||||
|
- gh-137490: Handle ECANCELED in the same way as EINTR in
|
||||||
|
signal.sigwaitinfo() on NetBSD.
|
||||||
|
- gh-137477: Fix inspect.getblock(), inspect.getsourcelines()
|
||||||
|
and inspect.getsource() for generator expressions.
|
||||||
|
- gh-137044: Return large limit values as positive integers
|
||||||
|
instead of negative integers in resource.getrlimit(). Accept
|
||||||
|
large values and reject negative values (except
|
||||||
|
RLIM_INFINITY) for limits in resource.setrlimit().
|
||||||
|
- gh-75989: tarfile.TarFile.extractall() and
|
||||||
|
tarfile.TarFile.extract() now overwrite symlinks when
|
||||||
|
extracting hardlinks. (Contributed by Alexander Enrique
|
||||||
|
Urieles Nieto in gh-75989.)
|
||||||
|
- gh-137017: Fix threading.Thread.is_alive to remain True until
|
||||||
|
the underlying OS thread is fully cleaned up. This avoids
|
||||||
|
false negatives in edge cases involving thread monitoring or
|
||||||
|
premature threading.Thread.is_alive calls.
|
||||||
|
- gh-137273: Fix debug assertion failure in locale.setlocale()
|
||||||
|
on Windows.
|
||||||
|
- gh-137239: heapq: Update heapq.__all__ with *_max functions.
|
||||||
|
- gh-81325: tarfile.TarFile now accepts a path-like when
|
||||||
|
working on a tar archive. (Contributed by Alexander Enrique
|
||||||
|
Urieles Nieto in gh-81325.)
|
||||||
|
- gh-137185: Fix a potential async-signal-safety issue in
|
||||||
|
faulthandler when printing C stack traces.
|
||||||
|
- gh-136914: Fix retrieval of doctest.DocTest.lineno for
|
||||||
|
objects decorated with functools.cache() or
|
||||||
|
functools.cached_property.
|
||||||
|
- gh-136912: hmac.digest() now properly handles large keys and
|
||||||
|
messages by falling back to the pure Python implementation
|
||||||
|
when necessary. Patch by Bénédikt Tran.
|
||||||
|
- gh-83424: Allows creating a ctypes.CDLL without name when
|
||||||
|
passing a handle as an argument.
|
||||||
|
- gh-136234: Fix asyncio.WriteTransport.writelines() to be
|
||||||
|
robust to connection failure, by using the same behavior as
|
||||||
|
write().
|
||||||
|
- gh-136507: Fix mimetypes CLI to handle multiple file
|
||||||
|
parameters.
|
||||||
|
- gh-136057: Fixed the bug in pdb and bdb where next and step
|
||||||
|
can’t go over the line if a loop exists in the line.
|
||||||
|
- gh-135386: Fix opening a dbm.sqlite3 database for reading
|
||||||
|
from read-only file or directory.
|
||||||
|
- gh-135444: Fix asyncio.DatagramTransport.sendto() to account
|
||||||
|
for datagram header size when data cannot be sent.
|
||||||
|
- gh-126631: Fix multiprocessing forkserver bug which prevented
|
||||||
|
__main__ from being preloaded.
|
||||||
|
- gh-135307: email: Fix exception in set_content() when
|
||||||
|
encoding text and max_line_length is set to 0 or None
|
||||||
|
(unlimited).
|
||||||
|
- gh-134453: Fixed subprocess.Popen.communicate() input=
|
||||||
|
handling of memoryview instances that were non-byte shaped on
|
||||||
|
POSIX platforms. Those are now properly cast to a byte shaped
|
||||||
|
view instead of truncating the input. Windows platforms did
|
||||||
|
not have this bug.
|
||||||
|
- gh-134698: Fix a crash when calling methods of ssl.SSLContext
|
||||||
|
or ssl.SSLSocket across multiple threads.
|
||||||
|
- gh-125996: Fix thread safety of collections.OrderedDict.
|
||||||
|
Patch by Kumar Aditya.
|
||||||
|
- gh-133789: Fix unpickling of pathlib objects that were
|
||||||
|
pickled in Python 3.13.
|
||||||
|
- gh-127081: Fix libc thread safety issues with dbm by
|
||||||
|
performing stateful operations in critical sections.
|
||||||
|
- gh-132551: Make io.BytesIO safe in free-threaded build.
|
||||||
|
- gh-131788: Make ResourceTracker.send from multiprocessing
|
||||||
|
re-entrant safe
|
||||||
|
- gh-118981: Fix potential hang in
|
||||||
|
multiprocessing.popen_spawn_posix that can happen when the
|
||||||
|
child proc dies early by closing the child fds right away.
|
||||||
|
- gh-102431: Clarify constraints for “logical” arguments in
|
||||||
|
methods of decimal.Context.
|
||||||
|
- gh-78319: UTF8 support for the IMAP APPEND command has been
|
||||||
|
made RFC compliant. bpo-38735: Fix failure when importing
|
||||||
|
a module from the root directory on unix-like platforms with
|
||||||
|
sys.pycache_prefix set. bpo-41839: Allow negative priority
|
||||||
|
values from os.sched_get_priority_min() and
|
||||||
|
os.sched_get_priority_max() functions.
|
||||||
|
- IDLE
|
||||||
|
- gh-96491: Deduplicate version number in IDLE shell title bar
|
||||||
|
after saving to a file.
|
||||||
|
- gh-139742: Colorize t-string prefixes for template strings in
|
||||||
|
IDLE, as done for f-string prefixes.
|
||||||
|
- Documentation
|
||||||
|
- gh-141994: xml.sax.handler: Make Documentation of
|
||||||
|
xml.sax.handler.feature_external_ges warn of opening up to
|
||||||
|
external entity attacks. Patch by Sebastian Pipping.
|
||||||
|
- gh-140578: Remove outdated sencence in the documentation for
|
||||||
|
multiprocessing, that implied that
|
||||||
|
concurrent.futures.ThreadPoolExecutor did not exist.
|
||||||
|
- Core and Builtins
|
||||||
|
- gh-142048: Fix quadratically increasing garbage collection
|
||||||
|
delays in free-threaded build.
|
||||||
|
- gh-116738: Fix thread safety issue with re scanner objects in
|
||||||
|
free-threaded builds.
|
||||||
|
- gh-141930: When importing a module, use Python’s regular file
|
||||||
|
object to ensure that writes to .pyc files are complete or an
|
||||||
|
appropriate error is raised.
|
||||||
|
- gh-120158: Fix inconsistent state when enabling or disabling
|
||||||
|
monitoring events too many times.
|
||||||
|
- gh-139653: Only raise a RecursionError or trigger a fatal
|
||||||
|
error if the stack pointer is both below the limit pointer
|
||||||
|
and above the stack base. If outside of these bounds assume
|
||||||
|
that it is OK. This prevents false positives when user-space
|
||||||
|
threads swap stacks.
|
||||||
|
- gh-139103: Improve multithreaded scaling of dataclasses on
|
||||||
|
the free-threaded build.
|
||||||
|
- gh-141579: Fix sys.activate_stack_trampoline() to properly
|
||||||
|
support the perf_jit backend. Patch by Pablo Galindo.
|
||||||
|
- gh-114203: Skip locking if object is already locked by
|
||||||
|
two-mutex critical section.
|
||||||
|
- gh-141528: Suggest using
|
||||||
|
concurrent.interpreters.Interpreter.close() instead of the
|
||||||
|
private _interpreters.destroy function when warning about
|
||||||
|
remaining subinterpreters. Patch by Sergey Miryanov.
|
||||||
|
- gh-141312: Fix the assertion failure in the __setstate__
|
||||||
|
method of the range iterator when a non-integer argument is
|
||||||
|
passed. Patch by Sergey Miryanov.
|
||||||
|
- gh-116738: Make csv module thread-safe on the free threaded
|
||||||
|
build.
|
||||||
|
- gh-140939: Fix memory leak when bytearray or bytes is
|
||||||
|
formated with the %*b format with a large width that results
|
||||||
|
in a MemoryError.
|
||||||
|
- gh-140260: Fix struct data race in endian table
|
||||||
|
initialization with subinterpreters. Patch by Shamil
|
||||||
|
Abdulaev.
|
||||||
|
- gh-140530: Fix a reference leak when raise exc from cause
|
||||||
|
fails. Patch by Bénédikt Tran.
|
||||||
|
- gh-140373: Correctly emit PY_UNWIND event when generator
|
||||||
|
object is closed. Patch by Mikhail Efimov.
|
||||||
|
- gh-140576: Fixed crash in tokenize.generate_tokens() in case
|
||||||
|
of specific incorrect input. Patch by Mikhail Efimov.
|
||||||
|
- gh-140551: Fixed crash in dict if dict.clear() is called at
|
||||||
|
the lookup stage. Patch by Mikhail Efimov and Inada Naoki.
|
||||||
|
- gh-140517: Fixed a reference leak when iterating over the
|
||||||
|
result of map() with strict=True when the input iterables
|
||||||
|
have different lengths. Patch by Mikhail Efimov.
|
||||||
|
- gh-140471: Fix potential buffer overflow in ast.AST node
|
||||||
|
initialization when encountering malformed _fields containing
|
||||||
|
non-str.
|
||||||
|
- gh-140431: Fix a crash in Python’s garbage collector due to
|
||||||
|
partially initialized coroutine objects when coroutine origin
|
||||||
|
tracking depth is enabled
|
||||||
|
(sys.set_coroutine_origin_tracking_depth()).
|
||||||
|
- gh-140398: Fix memory leaks in readline functions
|
||||||
|
read_init_file(), read_history_file(), write_history_file(),
|
||||||
|
and append_history_file() when PySys_Audit() fails.
|
||||||
|
- gh-140406: Fix memory leak when an object’s __hash__() method
|
||||||
|
returns an object that isn’t an int.
|
||||||
|
- gh-140358: Restore elapsed time and unreachable object count
|
||||||
|
in GC debug output. These were inadvertently removed during
|
||||||
|
a refactor of gc.c. The debug log now again reports elapsed
|
||||||
|
collection time and the number of unreachable objects.
|
||||||
|
Contributed by Pål Grønås Drange.
|
||||||
|
- gh-140306: Fix memory leaks in cross-interpreter channel
|
||||||
|
operations and shared namespace handling.
|
||||||
|
- gh-140301: Fix memory leak of PyConfig in subinterpreters.
|
||||||
|
- gh-140257: Fix data race between interpreter_clear() and
|
||||||
|
take_gil() on eval_breaker during finalization with daemon
|
||||||
|
threads.
|
||||||
|
- gh-139951: Fixes a regression in GC performance for a growing
|
||||||
|
heap composed mostly of small tuples. Counts number of
|
||||||
|
actually tracked objects, instead of trackable objects. This
|
||||||
|
ensures that untracking tuples has the desired effect of
|
||||||
|
reducing GC overhead. Does not track most untrackable tuples
|
||||||
|
during creation. This prevents large numbers of small tuples
|
||||||
|
causing excessive GCs.
|
||||||
|
- gh-140104: Fix a bug with exception handling in the JIT.
|
||||||
|
Patch by Ken Jin. Bug reported by Daniel Diniz.
|
||||||
|
- gh-140061: Fixing the checking of whether an object is
|
||||||
|
uniquely referenced to ensure free-threaded compatibility.
|
||||||
|
Patch by Sergey Miryanov.
|
||||||
|
- gh-140067: Fix memory leak in sub-interpreter creation.
|
||||||
|
- gh-140000: Fix potential memory leak when a reference cycle
|
||||||
|
exists between an instance of typing.TypeAliasType,
|
||||||
|
typing.TypeVar, typing.ParamSpec, or typing.TypeVarTuple and
|
||||||
|
its __name__ attribute. Patch by Mikhail Efimov.
|
||||||
|
- gh-139914: Restore support for HP PA-RISC, which has an
|
||||||
|
upwards-growing stack.
|
||||||
|
- gh-139988: Fix a memory leak when failing to create a Union
|
||||||
|
type. Patch by Bénédikt Tran.
|
||||||
|
- gh-139748: Fix reference leaks in error branches of functions
|
||||||
|
accepting path strings or bytes such as compile() and
|
||||||
|
os.system(). Patch by Bénédikt Tran.
|
||||||
|
- gh-139516: Fix lambda colon erroneously start format spec in
|
||||||
|
f-string in tokenizer.
|
||||||
|
- gh-139640: ast.parse() no longer emits syntax warnings for
|
||||||
|
return/break/continue in finally (see PEP 765) – they are
|
||||||
|
only emitted during compilation.
|
||||||
|
- gh-139640: Fix swallowing some syntax warnings in different
|
||||||
|
modules if they accidentally have the same message and are
|
||||||
|
emitted from the same line. Fix duplicated warnings in the
|
||||||
|
finally block.
|
||||||
|
- gh-63161: Support non-UTF-8 shebang and comments in Python
|
||||||
|
source files if non-UTF-8 encoding is specified. Detect
|
||||||
|
decoding error in comments for default (UTF-8) encoding. Show
|
||||||
|
the line and position of decoding error for default encoding
|
||||||
|
in a traceback. Show the line containing the coding cookie
|
||||||
|
when it conflicts with the BOM in a traceback.
|
||||||
|
- gh-116738: Make mmap thread-safe on the free threaded build.
|
||||||
|
- gh-138558: Fix handling of unusual t-string annotations in
|
||||||
|
annotationlib. Patch by Dave Peck.
|
||||||
|
- gh-134466: Don’t run PyREPL in a degraded environment where
|
||||||
|
setting termios attributes is not allowed.
|
||||||
|
- gh-138944: Fix SyntaxError message when invalid syntax
|
||||||
|
appears on the same line as a valid import ... as ... or from
|
||||||
|
... import ... as ... statement. Patch by Brian Schubert.
|
||||||
|
- gh-105487: Remove non-existent __copy__(), __deepcopy__(),
|
||||||
|
and __bases__ from the __dir__() entries of
|
||||||
|
types.GenericAlias.
|
||||||
|
- gh-69605: Fix some standard library submodules missing from
|
||||||
|
the REPL auto-completion of imports.
|
||||||
|
- gh-116738: Make cProfile thread-safe on the free threaded
|
||||||
|
build.
|
||||||
|
- gh-138004: On Solaris/Illumos platforms, thread names are now
|
||||||
|
encoded as ASCII to avoid errors on systems (e.g.
|
||||||
|
OpenIndiana) that don’t support non-ASCII names.
|
||||||
|
- gh-137433: Fix a potential deadlock in the free threading
|
||||||
|
build when daemon threads enable or disable profiling or
|
||||||
|
tracing while the main thread is shutting down the
|
||||||
|
interpreter.
|
||||||
|
- gh-137400: Fix a crash in the free threading build when
|
||||||
|
disabling profiling or tracing across all threads with
|
||||||
|
PyEval_SetProfileAllThreads() or PyEval_SetTraceAllThreads()
|
||||||
|
or their Python equivalents threading.settrace_all_threads()
|
||||||
|
and threading.setprofile_all_threads().
|
||||||
|
- gh-58124: Fix name of the Python encoding in Unicode errors
|
||||||
|
of the code page codec: use “cp65000” and “cp65001” instead
|
||||||
|
of “CP_UTF7” and “CP_UTF8” which are not valid Python code
|
||||||
|
names. Patch by Victor Stinner.
|
||||||
|
- gh-132657: Improve performance of frozenset by removing locks
|
||||||
|
in the free-threading build.
|
||||||
|
- gh-133400: Fixed Ctrl+D (^D) behavior in _pyrepl module to
|
||||||
|
match old pre-3.13 REPL behavior.
|
||||||
|
- gh-128640: Fix a crash when using threads inside of
|
||||||
|
a subinterpreter.
|
||||||
|
- C API
|
||||||
|
- gh-137422: Fix free threading race condition in
|
||||||
|
PyImport_AddModuleRef(). It was previously possible for two
|
||||||
|
calls to the function return two different objects, only one
|
||||||
|
of which was stored in sys.modules.
|
||||||
|
- gh-140042: Removed the sqlite3_shutdown call that could cause
|
||||||
|
closing connections for sqlite when used with multiple sub
|
||||||
|
interpreters.
|
||||||
|
- gh-141042: Make qNaN in PyFloat_Pack2() and PyFloat_Pack4(),
|
||||||
|
if while conversion to a narrower precision floating-point
|
||||||
|
format — the remaining after truncation payload will be zero.
|
||||||
|
Patch by Sergey B Kirpichev.
|
||||||
|
- gh-140487: Fix Py_RETURN_NOTIMPLEMENTED in limited C API 3.11
|
||||||
|
and older: don’t treat Py_NotImplemented as immortal. Patch
|
||||||
|
by Victor Stinner.
|
||||||
|
- gh-140153: Fix Py_REFCNT() definition on limited C API
|
||||||
|
3.11-3.13. Patch by Victor Stinner.
|
||||||
|
- gh-139653: Add PyUnstable_ThreadState_SetStackProtection()
|
||||||
|
and PyUnstable_ThreadState_ResetStackProtection() functions
|
||||||
|
to set the stack protection base address and stack protection
|
||||||
|
size of a Python thread state. Patch by Victor Stinner.
|
||||||
|
- Build
|
||||||
|
- gh-141808: Do not generate the jit stencils twice in case of
|
||||||
|
PGO builds on Windows.
|
||||||
|
- gh-141784: Fix _remote_debugging_module.c compilation on
|
||||||
|
32-bit Linux. Include Python.h before system headers to make
|
||||||
|
sure that _remote_debugging_module.c uses the same types
|
||||||
|
(ABI) than Python. Patch by Victor Stinner.
|
||||||
|
- gh-140768: Warn when the WASI SDK version doesn’t match
|
||||||
|
what’s supported.
|
||||||
|
- gh-140513: Generate a clear compilation error when
|
||||||
|
_Py_TAIL_CALL_INTERP is enabled but either preserve_none or
|
||||||
|
musttail is not supported.
|
||||||
|
- gh-140189: iOS builds were added to CI.
|
||||||
|
- gh-138489: When cross-compiling for WASI by build_wasm or
|
||||||
|
build_emscripten, the build-details.json step is now included
|
||||||
|
in the build process, just like with native builds. This
|
||||||
|
fixes the libinstall task which requires the
|
||||||
|
build-details.json file during the process.
|
||||||
|
- gh-137618: PYTHON_FOR_REGEN now requires Python 3.10 to
|
||||||
|
Python 3.15. Patch by Adam Turner.
|
||||||
|
- gh-123681: Check the strftime() behavior at runtime instead
|
||||||
|
of at the compile time to support cross-compiling. Remove the
|
||||||
|
internal macro _Py_NORMALIZE_CENTURY.
|
||||||
|
- Remove upstreamed patches:
|
||||||
|
- CVE-2025-6075-expandvars-perf-degrad.patch
|
||||||
|
- CVE-2025-8291-consistency-zip64.patch
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Thu Nov 13 17:13:03 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
Thu Nov 13 17:13:03 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||||
|
|
||||||
|
|||||||
4
python314.rpmlintrc
Normal file
4
python314.rpmlintrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
addFilter("pem-certificate.*/usr/lib.*/python.*/test/*.pem")
|
||||||
|
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/tests/*.c")
|
||||||
|
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/*.cpp")
|
||||||
|
addFilter("python-bytecode-inconsistent-mtime.*/usr/lib.*/python.*/*.pyc")
|
||||||
@@ -54,6 +54,8 @@
|
|||||||
%bcond_with GIL
|
%bcond_with GIL
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%define llvm_version 21
|
||||||
|
|
||||||
%if 0%{?do_profiling} && !0%{?want_reproducible_builds}
|
%if 0%{?do_profiling} && !0%{?want_reproducible_builds}
|
||||||
%bcond_without profileopt
|
%bcond_without profileopt
|
||||||
%else
|
%else
|
||||||
@@ -124,7 +126,7 @@
|
|||||||
# %%define tarversion %%{version}
|
# %%define tarversion %%{version}
|
||||||
# %%endif
|
# %%endif
|
||||||
# We don't process beta signs well
|
# We don't process beta signs well
|
||||||
%define folderversion 3.14.0
|
%define folderversion 3.14.2
|
||||||
%define sitedir %{_libdir}/python%{python_version}
|
%define sitedir %{_libdir}/python%{python_version}
|
||||||
# three possible ABI kinds: m - pymalloc, d - debug build; see PEP 3149
|
# three possible ABI kinds: m - pymalloc, d - debug build; see PEP 3149
|
||||||
%define abi_kind %{nil}
|
%define abi_kind %{nil}
|
||||||
@@ -162,7 +164,7 @@
|
|||||||
# _md5.cpython-38m-x86_64-linux-gnu.so
|
# _md5.cpython-38m-x86_64-linux-gnu.so
|
||||||
%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so
|
%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so
|
||||||
Name: %{python_pkg_name}%{psuffix}
|
Name: %{python_pkg_name}%{psuffix}
|
||||||
Version: 3.14.0
|
Version: 3.14.2
|
||||||
%define tarversion %{version}
|
%define tarversion %{version}
|
||||||
%define tarname Python-%{tarversion}
|
%define tarname Python-%{tarversion}
|
||||||
Release: 0
|
Release: 0
|
||||||
@@ -209,6 +211,8 @@ Patch07: bpo-31046_ensurepip_honours_prefix.patch
|
|||||||
# PATCH-FIX-SLE skip-test_pyobject_freed_is_freed.patch mcepl@suse.com
|
# PATCH-FIX-SLE skip-test_pyobject_freed_is_freed.patch mcepl@suse.com
|
||||||
# skip a test failing on SLE-15
|
# skip a test failing on SLE-15
|
||||||
Patch09: skip-test_pyobject_freed_is_freed.patch
|
Patch09: skip-test_pyobject_freed_is_freed.patch
|
||||||
|
# PATCH-FIX-UPSTREAM gh138498-llvm-version-config gh#python/cpython#138498 daniel.garcia@suse.com
|
||||||
|
Patch10: gh138498-llvm-version-config.patch
|
||||||
# PATCH-FIX-OPENSUSE CVE-2023-52425-libexpat-2.6.0-backport-15.6.patch
|
# PATCH-FIX-OPENSUSE CVE-2023-52425-libexpat-2.6.0-backport-15.6.patch
|
||||||
# This problem on libexpat is patched on 15.6 without version
|
# This problem on libexpat is patched on 15.6 without version
|
||||||
# update, this patch changes the tests to match the libexpat provided
|
# update, this patch changes the tests to match the libexpat provided
|
||||||
@@ -224,12 +228,6 @@ Patch41: bsc1243155-sphinx-non-determinism.patch
|
|||||||
Patch44: gh138131-exclude-pycache-from-digest.patch
|
Patch44: gh138131-exclude-pycache-from-digest.patch
|
||||||
# PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch gh#python/cpython#139257 daniel.garcia@suse.com
|
# PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch gh#python/cpython#139257 daniel.garcia@suse.com
|
||||||
Patch45: gh139257-Support-docutils-0.22.patch
|
Patch45: gh139257-Support-docutils-0.22.patch
|
||||||
# PATCH-FIX-UPSTREAM CVE-2025-8291-consistency-zip64.patch bsc#1251305 mcepl@suse.com
|
|
||||||
# Check consistency of the zip64 end of central directory record
|
|
||||||
Patch46: CVE-2025-8291-consistency-zip64.patch
|
|
||||||
# PATCH-FIX-UPSTREAM CVE-2025-6075-expandvars-perf-degrad.patch bsc#1252974 mcepl@suse.com
|
|
||||||
# Avoid potential quadratic complexity vulnerabilities in path modules
|
|
||||||
Patch47: CVE-2025-6075-expandvars-perf-degrad.patch
|
|
||||||
#### Python 3.14 DEVELOPMENT PATCHES
|
#### Python 3.14 DEVELOPMENT PATCHES
|
||||||
BuildRequires: autoconf-archive
|
BuildRequires: autoconf-archive
|
||||||
BuildRequires: automake
|
BuildRequires: automake
|
||||||
@@ -275,7 +273,7 @@ BuildRequires: python3-python-docs-theme >= 2022.1
|
|||||||
|
|
||||||
%if %{with experimental_jit}
|
%if %{with experimental_jit}
|
||||||
# needed for experimental_jit
|
# needed for experimental_jit
|
||||||
BuildRequires: clang19 llvm19
|
BuildRequires: clang%{llvm_version} llvm%{llvm_version}
|
||||||
BuildRequires: llvm
|
BuildRequires: llvm
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
@@ -581,6 +579,7 @@ sed -e 's/-fprofile-correction//' -i Makefile.pre.in
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
export CFLAGS="%{optflags} -IVendor/"
|
export CFLAGS="%{optflags} -IVendor/"
|
||||||
|
export LLVM_VERSION=%{llvm_version}
|
||||||
|
|
||||||
%configure \
|
%configure \
|
||||||
--with-platlibdir=%{_lib} \
|
--with-platlibdir=%{_lib} \
|
||||||
|
|||||||
Reference in New Issue
Block a user