forked from pool/python313
Compare commits
141 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
b020ec1b9b
|
|||
|
6807e0fac4
|
|||
|
a8f3f2707f
|
|||
|
02c7c3ac57
|
|||
|
1b4b152007
|
|||
|
6823a127f7
|
|||
| 8490c35b5e | |||
| 216aee44d2 | |||
| 5c7e077e05 | |||
| 6ccfd57cb6 | |||
| f26b5dd668 | |||
| 97f2e50954 | |||
| d782ad00ca | |||
| b40f1d6405 | |||
| 45ae9e0091 | |||
| 0f5697e310 | |||
| 3dc0e87868 | |||
| f819c56b57 | |||
| 6ca12749fe | |||
| af83d0ea02 | |||
| 588cd5ec7f | |||
| 4a974dadae | |||
| 9a64481749 | |||
| c0f5d18c1e | |||
| 0c1f23a3d6 | |||
| 00d0af4ebb | |||
| 5ef0b0ecff | |||
| f1f4736355 | |||
| 8fc89fce82 | |||
| e51fa4e692 | |||
| 5584dde572 | |||
| da11e6e10a | |||
| b30cd19ff8 | |||
| a7efa91dcd | |||
| cb554c7d4c | |||
| 6fe6053eb5 | |||
| 92106b1aea | |||
| b58f975be7 | |||
| cf3b0e517c | |||
| f3df88065e | |||
| c7e438c2e0 | |||
| 7d8817d9bb | |||
| eb2298e3f2 | |||
| 308dfaef9b | |||
| f9c64528f8 | |||
| c2d30804e6 | |||
| 3386fc12ed | |||
| 70558652fc | |||
| e8c68d65d4 | |||
| 6072bbdbcd | |||
| f5a88d357f | |||
| 96acb778b3 | |||
| 6d5d3f96b0 | |||
| b8ba5ea90c | |||
| 820434f8e4 | |||
| 8d20edb449 | |||
| 487ae82f04 | |||
| 55fb9cd905 | |||
| 64bae1f84b | |||
| d8af743464 | |||
| c1d8c54913 | |||
| 82f875fb14 | |||
| 201e349852 | |||
| bb17c93a2a | |||
| a90f4e560b | |||
| 55167f91bd | |||
| 0f47302d79 | |||
| b91bbdde1b | |||
| 24d06dc05c | |||
| 384d0f4194 | |||
| 3837884001 | |||
| 9e2287fa69 | |||
| a23dbf9cdf | |||
| 9624a1ae7e | |||
| 208ac0bda6 | |||
| 415df5f3cd | |||
| 1a9b96ab31 | |||
| 88b70a09e9 | |||
| 3467717953 | |||
| 1ea8708b8d | |||
| 3bce06d06a | |||
| 1e079c98aa | |||
| 347e286045 | |||
| 279fe75cee | |||
| d6f4df3c91 | |||
| d4d0782687 | |||
| 7d140c532a | |||
| 875a6f6235 | |||
| c596c85ff5 | |||
| 5c3c7cecd2 | |||
| dfcfb5ce90 | |||
| 528339bd34 | |||
| 1faa76760c | |||
| d4f884437e | |||
| ecf4d377f8 | |||
| d6003ec835 | |||
| b7339fbd1a | |||
| adc199414a | |||
| 64423e0ba5 | |||
| 183fa1a4f9 | |||
| 99d319aa5b | |||
| c9f290cdec | |||
| 6daf155ac4 | |||
| be126e03ea | |||
| d67c636211 | |||
| 9fd773a946 | |||
| 73ac4a887b | |||
| 994d248383 | |||
| b8a809b1cc | |||
| eb20745074 | |||
| d5a98f8796 | |||
| d24d58c01e | |||
| 9875af21c3 | |||
| 26d0509456 | |||
| e84b2a9ea2 | |||
| b7221c02d8 | |||
| 3029e09e6c | |||
| ed950ec431 | |||
| 5ddcd862f2 | |||
| 1474d9e3e7 | |||
| 6a96a3b53f | |||
| a96d28f6cd | |||
| 9d3910e32a | |||
| d566f66214 | |||
| 30eeed452e | |||
| d30e6ca376 | |||
| fa823e120f | |||
| 3827c5d408 | |||
| 6afb8e217a | |||
| 2325ab9130 | |||
| 8eb4d86563 | |||
| 34a67fa7c5 | |||
| 648323dfb5 | |||
| 18edb4412d | |||
| bb1b0a85b2 | |||
| 31416b1907 | |||
| da884a6e9b | |||
| 6912c8cc4e | |||
| b81fd3c63c | |||
| 727c999e70 | |||
| d6957de319 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -21,3 +21,4 @@
|
|||||||
*.xz filter=lfs diff=lfs merge=lfs -text
|
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
*.zst filter=lfs diff=lfs merge=lfs -text
|
*.zst filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.changes merge=merge-changes
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1 +1,6 @@
|
|||||||
.osc
|
.osc
|
||||||
|
*.obscpio
|
||||||
|
*.osc
|
||||||
|
_build.*
|
||||||
|
.pbuild
|
||||||
|
python313-*-build/
|
||||||
|
|||||||
373
CVE-2025-6075-expandvars-perf-degrad.patch
Normal file
373
CVE-2025-6075-expandvars-perf-degrad.patch
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
From 4fc21099da844f85b799d3c4c8b1b5936faa4cdc Mon Sep 17 00:00:00 2001
|
||||||
|
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
Date: Fri, 31 Oct 2025 15:49:51 +0200
|
||||||
|
Subject: [PATCH 1/2] [3.13] 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 +
|
||||||
|
Misc/NEWS.d/next/Security/2025-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
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Lib/ntpath.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Lib/ntpath.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Lib/ntpath.py 2025-11-14 01:47:08.155405483 +0100
|
||||||
|
@@ -400,17 +400,23 @@
|
||||||
|
# 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 @@
|
||||||
|
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.
|
||||||
|
Index: Python-3.13.9/Lib/posixpath.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Lib/posixpath.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Lib/posixpath.py 2025-11-14 01:47:08.155728503 +0100
|
||||||
|
@@ -284,42 +284,41 @@
|
||||||
|
# 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 @@
|
||||||
|
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.
|
||||||
|
Index: Python-3.13.9/Lib/test/test_genericpath.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Lib/test/test_genericpath.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Lib/test/test_genericpath.py 2025-11-14 01:47:08.157575687 +0100
|
||||||
|
@@ -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, is_emscripten
|
||||||
|
+from test.support import warnings_helper
|
||||||
|
from test.support.script_helper import assert_python_ok
|
||||||
|
from test.support.os_helper import FakePath
|
||||||
|
|
||||||
|
@@ -446,6 +446,19 @@
|
||||||
|
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():
|
||||||
|
@@ -503,7 +516,7 @@
|
||||||
|
# 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:
|
||||||
|
Index: Python-3.13.9/Lib/test/test_ntpath.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Lib/test/test_ntpath.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Lib/test/test_ntpath.py 2025-11-14 01:47:08.156225429 +0100
|
||||||
|
@@ -8,8 +8,7 @@
|
||||||
|
import warnings
|
||||||
|
from ntpath import ALLOW_MISSING
|
||||||
|
from test import support
|
||||||
|
-from test.support import cpython_only, os_helper
|
||||||
|
-from test.support import TestFailed, is_emscripten
|
||||||
|
+from test.support import os_helper, is_emscripten
|
||||||
|
from test.support.os_helper import FakePath
|
||||||
|
from test import test_genericpath
|
||||||
|
from tempfile import TemporaryFile
|
||||||
|
@@ -59,7 +58,7 @@
|
||||||
|
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 @@
|
||||||
|
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 @@
|
||||||
|
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')
|
||||||
|
|
||||||
|
@@ -1440,7 +1452,7 @@
|
||||||
|
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
|
||||||
|
Index: Python-3.13.9/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ Python-3.13.9/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst 2025-11-14 01:47:08.156533642 +0100
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Fix quadratic complexity in :func:`os.path.expandvars`.
|
||||||
307
CVE-2025-8291-consistency-zip64.patch
Normal file
307
CVE-2025-8291-consistency-zip64.patch
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
From 1f2e4ec73cf7ece0a8c0a7a85cb73ec9ec0ef85a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
Date: Tue, 7 Oct 2025 20:15:26 +0300
|
||||||
|
Subject: [PATCH] [3.13] 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 +++++++-----
|
||||||
|
...-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
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py
|
||||||
|
index 41ec6a437ba917..2212d9c91dc899 100644
|
||||||
|
--- a/Lib/test/test_zipfile/test_core.py
|
||||||
|
+++ b/Lib/test/test_zipfile/test_core.py
|
||||||
|
@@ -884,6 +884,8 @@ def make_zip64_file(
|
||||||
|
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.
|
||||||
|
|
||||||
|
@@ -937,6 +939,12 @@ def make_zip64_file(
|
||||||
|
|
||||||
|
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))
|
||||||
|
@@ -965,14 +973,17 @@ def make_zip64_file(
|
||||||
|
+ 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"
|
||||||
|
@@ -1003,6 +1014,7 @@ def test_bad_zip64_extra(self):
|
||||||
|
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.
|
||||||
|
@@ -1014,6 +1026,7 @@ def test_bad_zip64_extra(self):
|
||||||
|
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.
|
||||||
|
@@ -1023,6 +1036,7 @@ def test_bad_zip64_extra(self):
|
||||||
|
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
|
||||||
|
@@ -1037,6 +1051,7 @@ def test_bad_zip64_extra(self):
|
||||||
|
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
|
||||||
|
@@ -1049,6 +1064,7 @@ def test_bad_zip64_extra(self):
|
||||||
|
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
|
||||||
|
@@ -1061,6 +1077,7 @@ def test_bad_zip64_extra(self):
|
||||||
|
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
|
||||||
|
@@ -1072,6 +1089,63 @@ def test_bad_zip64_extra(self):
|
||||||
|
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.
|
||||||
|
diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py
|
||||||
|
index 05f387a950ba0a..c01f13729e1c99 100644
|
||||||
|
--- a/Lib/zipfile/__init__.py
|
||||||
|
+++ b/Lib/zipfile/__init__.py
|
||||||
|
@@ -245,7 +245,7 @@ def is_zipfile(filename):
|
||||||
|
else:
|
||||||
|
with open(filename, "rb") as fp:
|
||||||
|
result = _check_zipfile(fp)
|
||||||
|
- except OSError:
|
||||||
|
+ except (OSError, BadZipFile):
|
||||||
|
pass
|
||||||
|
return result
|
||||||
|
|
||||||
|
@@ -253,16 +253,15 @@ def _EndRecData64(fpin, offset, endrec):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
@@ -270,16 +269,33 @@ def _EndRecData64(fpin, offset, endrec):
|
||||||
|
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
|
||||||
|
@@ -289,6 +305,7 @@ def _EndRecData64(fpin, offset, endrec):
|
||||||
|
endrec[_ECD_ENTRIES_TOTAL] = dircount2
|
||||||
|
endrec[_ECD_SIZE] = dirsize
|
||||||
|
endrec[_ECD_OFFSET] = diroffset
|
||||||
|
+ endrec[_ECD_LOCATION] = offset - extrasz
|
||||||
|
return endrec
|
||||||
|
|
||||||
|
|
||||||
|
@@ -322,7 +339,7 @@ def _EndRecData(fpin):
|
||||||
|
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"
|
||||||
|
@@ -346,8 +363,7 @@ def _EndRecData(fpin):
|
||||||
|
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
|
||||||
|
@@ -1458,9 +1474,6 @@ def _RealGetContents(self):
|
||||||
|
|
||||||
|
# "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 self.debug > 2:
|
||||||
|
inferred = concat + offset_cd
|
||||||
|
@@ -2082,7 +2095,7 @@ def _write_end_record(self):
|
||||||
|
" 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)
|
||||||
|
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst b/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000000000..a8e7a1f1878c6b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2025-10-07-19-31-34.gh-issue-139700.vNHU1O.rst
|
||||||
|
@@ -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.
|
||||||
@@ -24,32 +24,115 @@ Co-authored-by: Miro Hrončok <miro@hroncok.cz>
|
|||||||
Co-authored-by: Michal Cyprian <m.cyprian@gmail.com>
|
Co-authored-by: Michal Cyprian <m.cyprian@gmail.com>
|
||||||
Co-authored-by: Lumír Balhar <frenzy.madness@gmail.com>
|
Co-authored-by: Lumír Balhar <frenzy.madness@gmail.com>
|
||||||
---
|
---
|
||||||
Lib/site.py | 9 ++++++++-
|
Lib/sysconfig/__init__.py | 57 +++++++++++++++++++++++++++++++++++++++++----
|
||||||
Lib/test/test_sysconfig.py | 17 +++++++++++++++--
|
Lib/test/test_sysconfig.py | 17 +++++++++++--
|
||||||
2 files changed, 23 insertions(+), 3 deletions(-)
|
2 files changed, 67 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
--- a/Lib/site.py
|
Index: Python-3.13.9/Lib/sysconfig/__init__.py
|
||||||
+++ b/Lib/site.py
|
===================================================================
|
||||||
@@ -406,8 +406,15 @@ def getsitepackages(prefixes=None):
|
--- Python-3.13.9.orig/Lib/sysconfig/__init__.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
return sitepackages
|
+++ Python-3.13.9/Lib/sysconfig/__init__.py 2025-11-04 17:41:28.521141323 +0100
|
||||||
|
@@ -106,6 +106,11 @@
|
||||||
|
else:
|
||||||
|
_INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv']
|
||||||
|
|
||||||
def addsitepackages(known_paths, prefixes=None):
|
+# For a brief period of time in the Fedora 36 life cycle,
|
||||||
- """Add site-packages to sys.path"""
|
+# this installation scheme existed and was documented in the release notes.
|
||||||
+ """Add site-packages to sys.path
|
+# For backwards compatibility, we keep it here (at least on 3.10 and 3.11).
|
||||||
|
+_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix']
|
||||||
+
|
+
|
||||||
+ '/usr/local' is included in PREFIXES if RPM build is not detected
|
def _get_implementation():
|
||||||
+ to make packages installed into this location visible.
|
return 'Python'
|
||||||
|
|
||||||
|
@@ -167,13 +172,28 @@
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
+# This is used by distutils.command.install in the stdlib
|
||||||
|
+# as well as pypa/distutils (e.g. bundled in setuptools).
|
||||||
|
+# The self.prefix value is set to sys.prefix + /local/
|
||||||
|
+# if neither RPM build nor virtual environment is
|
||||||
|
+# detected to make distutils install packages
|
||||||
|
+# into the separate location.
|
||||||
|
+# https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+if (not (hasattr(sys, 'real_prefix') or
|
||||||
|
+ sys.prefix != sys.base_prefix) and
|
||||||
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
|
+ _prefix_addition = '/local'
|
||||||
+
|
+
|
||||||
+ """
|
+
|
||||||
_trace("Processing global site-packages")
|
_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
|
||||||
+ if ENABLE_USER_SITE and 'RPM_BUILD_ROOT' not in os.environ:
|
'scripts', 'data')
|
||||||
+ PREFIXES.insert(0, "/usr/local")
|
|
||||||
for sitedir in getsitepackages(prefixes):
|
_PY_VERSION = sys.version.split()[0]
|
||||||
if os.path.isdir(sitedir):
|
_PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}'
|
||||||
addsitedir(sitedir, known_paths)
|
_PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}'
|
||||||
--- a/Lib/test/test_sysconfig.py
|
+_PREFIX = os.path.normpath(sys.prefix)
|
||||||
+++ b/Lib/test/test_sysconfig.py
|
_BASE_PREFIX = os.path.normpath(sys.base_prefix)
|
||||||
@@ -121,8 +121,19 @@ class TestSysConfig(unittest.TestCase):
|
+_EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
|
||||||
|
_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
|
||||||
|
# Mutex guarding initialization of _CONFIG_VARS.
|
||||||
|
_CONFIG_VARS_LOCK = threading.RLock()
|
||||||
|
@@ -266,11 +286,40 @@
|
||||||
|
target_dict[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
+_CONFIG_VARS_LOCAL = None
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _config_vars_local():
|
||||||
|
+ # This function returns the config vars with prefixes amended to /usr/local
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ global _CONFIG_VARS_LOCAL
|
||||||
|
+ if _CONFIG_VARS_LOCAL is None:
|
||||||
|
+ _CONFIG_VARS_LOCAL = dict(get_config_vars())
|
||||||
|
+ _CONFIG_VARS_LOCAL['base'] = '/usr/local'
|
||||||
|
+ _CONFIG_VARS_LOCAL['platbase'] = '/usr/local'
|
||||||
|
+ return _CONFIG_VARS_LOCAL
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def _expand_vars(scheme, vars):
|
||||||
|
res = {}
|
||||||
|
if vars is None:
|
||||||
|
vars = {}
|
||||||
|
- _extend_dict(vars, get_config_vars())
|
||||||
|
+
|
||||||
|
+ # when we are not in a virtual environment or an RPM build
|
||||||
|
+ # we change '/usr' to '/usr/local'
|
||||||
|
+ # to avoid surprises, we explicitly check for the /usr/ prefix
|
||||||
|
+ # Python virtual environments have different prefixes
|
||||||
|
+ # we only do this for posix_prefix, not to mangle the venv scheme
|
||||||
|
+ # posix_prefix is used by sudo pip install
|
||||||
|
+ # we only change the defaults here, so explicit --prefix will take precedence
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ if (scheme == 'posix_prefix' and
|
||||||
|
+ _PREFIX == '/usr' and
|
||||||
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
|
+ _extend_dict(vars, _config_vars_local())
|
||||||
|
+ else:
|
||||||
|
+ _extend_dict(vars, get_config_vars())
|
||||||
|
+
|
||||||
|
if os.name == 'nt':
|
||||||
|
# On Windows we want to substitute 'lib' for schemes rather
|
||||||
|
# than the native value (without modifying vars, in case it
|
||||||
|
@@ -471,10 +520,8 @@
|
||||||
|
# Normalized versions of prefix and exec_prefix are handy to have;
|
||||||
|
# in fact, these are the standard versions used most places in the
|
||||||
|
# Distutils.
|
||||||
|
- _PREFIX = os.path.normpath(sys.prefix)
|
||||||
|
- _EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
|
||||||
|
- _CONFIG_VARS['prefix'] = _PREFIX # FIXME: This gets overwriten by _init_posix.
|
||||||
|
- _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX # FIXME: This gets overwriten by _init_posix.
|
||||||
|
+ _CONFIG_VARS['prefix'] = _PREFIX
|
||||||
|
+ _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
|
||||||
|
_CONFIG_VARS['py_version'] = _PY_VERSION
|
||||||
|
_CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
|
||||||
|
_CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT
|
||||||
|
Index: Python-3.13.9/Lib/test/test_sysconfig.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Lib/test/test_sysconfig.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Lib/test/test_sysconfig.py 2025-11-04 17:41:28.521386489 +0100
|
||||||
|
@@ -130,8 +130,19 @@
|
||||||
for scheme in _INSTALL_SCHEMES:
|
for scheme in _INSTALL_SCHEMES:
|
||||||
for name in _INSTALL_SCHEMES[scheme]:
|
for name in _INSTALL_SCHEMES[scheme]:
|
||||||
expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
|
expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
|
||||||
@@ -70,7 +153,7 @@ Co-authored-by: Lumír Balhar <frenzy.madness@gmail.com>
|
|||||||
os.path.normpath(expected),
|
os.path.normpath(expected),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -377,7 +388,7 @@ class TestSysConfig(unittest.TestCase):
|
@@ -393,7 +404,7 @@
|
||||||
self.assertTrue(os.path.isfile(config_h), config_h)
|
self.assertTrue(os.path.isfile(config_h), config_h)
|
||||||
|
|
||||||
def test_get_scheme_names(self):
|
def test_get_scheme_names(self):
|
||||||
@@ -79,7 +162,7 @@ Co-authored-by: Lumír Balhar <frenzy.madness@gmail.com>
|
|||||||
if HAS_USER_BASE:
|
if HAS_USER_BASE:
|
||||||
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
|
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
|
||||||
self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
|
self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
|
||||||
@@ -389,6 +400,8 @@ class TestSysConfig(unittest.TestCase):
|
@@ -405,6 +416,8 @@
|
||||||
cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
|
cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
|
||||||
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:3be094ad08b11dc2a065463524239c78dc9f2b342b01dcd4e1e606dbbc5c78a5
|
|
||||||
size 20841504
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
|
|
||||||
iQKTBAABCgB9FiEEcWlgX2LHUTVtBUomqCHmgOX6YwUFAmZ9d85fFIAAAAAALgAo
|
|
||||||
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDcx
|
|
||||||
Njk2MDVGNjJDNzUxMzU2RDA1NEEyNkE4MjFFNjgwRTVGQTYzMDUACgkQqCHmgOX6
|
|
||||||
YwWXBA//TeNtOZTRpGM4pWnY9GBOvXH+WFyT5k5GQM6AE5ITYhLPkz+Refj3kwPV
|
|
||||||
uF32neYXIJIO3v+vyfFTYyj5jNS5gvGRvjPXe2K5p0K9cv5HpTncob9p8FnTJQRY
|
|
||||||
hqXpzs2q8gjU9YN54wz3SqAFE1ensB2qHlq7EJUABbB+HNWk929TXh+cQwSybRT1
|
|
||||||
XMzdKHc4IO3WCVPKIsMngqglUrj7FhlEgx/C8hmu81zpLVkCzxDXLuEDt7nnHoCG
|
|
||||||
HHcmF8B7kGK6py8KD7n/RZgriXli37tyFHJ0wwCtzwyko73khWVayYyJ53s4Pa5H
|
|
||||||
C6A9eNJMBxAQU/M2uPed+io5xZ8IvzSPs4vJCS8YL+NHTQGubexo5QIIHTP5b7ta
|
|
||||||
JW6s3TDHA8g/b3rGum76ZelJ4dsUJ8TPxNl4fORtsltVLJwol1FLGzb7vK7r/ZTM
|
|
||||||
NBnYFjNjEfe4uWHwsZoLdZzH90Z8bKsUXWz/h4EMjUL3NLgORSE8vbPhpjscmEB/
|
|
||||||
x+DM9IJHQat/s8ijtELuOx9SLpUuFBAmzVkmWVWvlPjFrQ5nw5pWLRzADpEV7/IU
|
|
||||||
XOucXhV4hrc8fJ57dGv0mtpP37DbF/cPMdOC0kA9X4icr1sLwN6MVmQJcFoBcNge
|
|
||||||
LvKvNW8lXxWbdOj0+fIymIXoH+bzJUp8g8l79Hp6QYBM5EOxfso=
|
|
||||||
=KNpM
|
|
||||||
-----END PGP SIGNATURE-----
|
|
||||||
BIN
Python-3.13.9.tar.xz
LFS
Normal file
BIN
Python-3.13.9.tar.xz
LFS
Normal file
Binary file not shown.
1
Python-3.13.9.tar.xz.sigstore
Normal file
1
Python-3.13.9.tar.xz.sigstore
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,6 @@
|
|||||||
<multibuild>
|
<multibuild>
|
||||||
<package>base</package>
|
<package>base</package>
|
||||||
<package>doc</package>
|
<package>doc</package>
|
||||||
|
<package>nogil</package>
|
||||||
|
<package>nogil-base</package>
|
||||||
</multibuild>
|
</multibuild>
|
||||||
|
|||||||
BIN
bluez-devel-vendor.tar.xz
LFS
BIN
bluez-devel-vendor.tar.xz
LFS
Binary file not shown.
@@ -5,28 +5,30 @@ Subject: [PATCH] bpo-31046: ensurepip does not honour the value of $(prefix)
|
|||||||
|
|
||||||
Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
||||||
---
|
---
|
||||||
Doc/library/ensurepip.rst | 9 +++--
|
Doc/library/ensurepip.rst | 12 +++++-
|
||||||
Lib/ensurepip/__init__.py | 18 +++++++---
|
Lib/ensurepip/__init__.py | 18 +++++++---
|
||||||
Lib/test/test_ensurepip.py | 11 ++++++
|
Lib/test/test_ensurepip.py | 11 ++++++
|
||||||
Makefile.pre.in | 4 +-
|
Makefile.pre.in | 4 +-
|
||||||
Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst | 1
|
Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst | 1
|
||||||
5 files changed, 34 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
|
||||||
|
|
||||||
--- a/Doc/library/ensurepip.rst
|
--- a/Doc/library/ensurepip.rst
|
||||||
+++ b/Doc/library/ensurepip.rst
|
+++ b/Doc/library/ensurepip.rst
|
||||||
@@ -59,8 +59,9 @@ is at least as recent as the one availab
|
@@ -61,7 +61,11 @@ is at least as recent as the one availab
|
||||||
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
|
||||||
-through two additional command line options:
|
-through two additional command line options:
|
||||||
+through some additional command line options:
|
+through some additional command line options:
|
||||||
|
+
|
||||||
|
+.. option:: --prefix <dir>
|
||||||
|
+
|
||||||
|
+ Installs ``pip`` using the given directory prefix.
|
||||||
|
|
||||||
+* ``--prefix <dir>``: Installs ``pip`` using the given directory prefix.
|
.. option:: --root <dir>
|
||||||
* :samp:`--root {dir}`: Installs ``pip`` relative to the given root directory
|
|
||||||
rather than the root of the currently active virtual environment (if any)
|
@@ -102,7 +106,7 @@ Module API
|
||||||
or the default root for the current Python installation.
|
|
||||||
@@ -92,7 +93,7 @@ Module API
|
|
||||||
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.
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
|||||||
altinstall=False, default_pip=False, \
|
altinstall=False, default_pip=False, \
|
||||||
verbosity=0)
|
verbosity=0)
|
||||||
|
|
||||||
@@ -102,6 +103,8 @@ Module API
|
@@ -112,6 +116,8 @@ Module API
|
||||||
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.
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
|||||||
*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.
|
||||||
|
|
||||||
@@ -122,6 +125,8 @@ Module API
|
@@ -132,6 +138,8 @@ Module API
|
||||||
*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.
|
||||||
|
|
||||||
@@ -139,7 +141,7 @@ Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
|||||||
|
|
||||||
--- a/Makefile.pre.in
|
--- a/Makefile.pre.in
|
||||||
+++ b/Makefile.pre.in
|
+++ b/Makefile.pre.in
|
||||||
@@ -2142,7 +2142,7 @@ install: @FRAMEWORKINSTALLFIRST@ @INSTAL
|
@@ -2145,7 +2145,7 @@ install: @FRAMEWORKINSTALLFIRST@ @INSTAL
|
||||||
install|*) ensurepip="" ;; \
|
install|*) ensurepip="" ;; \
|
||||||
esac; \
|
esac; \
|
||||||
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
||||||
@@ -148,7 +150,7 @@ Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: altinstall
|
.PHONY: altinstall
|
||||||
@@ -2153,7 +2153,7 @@ altinstall: commoninstall
|
@@ -2156,7 +2156,7 @@ altinstall: commoninstall
|
||||||
install|*) ensurepip="--altinstall" ;; \
|
install|*) ensurepip="--altinstall" ;; \
|
||||||
esac; \
|
esac; \
|
||||||
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
||||||
|
|||||||
45
bsc1243155-sphinx-non-determinism.patch
Normal file
45
bsc1243155-sphinx-non-determinism.patch
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
From 906a590df191f66f4f0c4a70e3edb6fd82c156ef Mon Sep 17 00:00:00 2001
|
||||||
|
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
|
||||||
|
Date: Tue, 1 Jul 2025 12:13:28 +0200
|
||||||
|
Subject: [PATCH] Doc: Generate ids for audit_events using docname
|
||||||
|
|
||||||
|
This patch generates ids for audit_events using the docname so the id is
|
||||||
|
not global but depend on the source file. This make the doc build
|
||||||
|
reproducible with multiple cores because it doesn't which file is parsed
|
||||||
|
first, the id for audit_events will always be consistent independently
|
||||||
|
of what file is parsed first.
|
||||||
|
|
||||||
|
https://github.com/python/cpython/issues/130979
|
||||||
|
---
|
||||||
|
Doc/tools/extensions/audit_events.py | 11 ++++++++---
|
||||||
|
1 file changed, 8 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
Index: Python-3.13.5/Doc/tools/extensions/audit_events.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.5.orig/Doc/tools/extensions/audit_events.py 2025-07-02 15:51:58.388560540 +0200
|
||||||
|
+++ Python-3.13.5/Doc/tools/extensions/audit_events.py 2025-07-02 15:51:58.411254070 +0200
|
||||||
|
@@ -72,8 +72,13 @@
|
||||||
|
logger.warning(msg)
|
||||||
|
return
|
||||||
|
|
||||||
|
- def id_for(self, name) -> str:
|
||||||
|
- source_count = len(self.sources.get(name, set()))
|
||||||
|
+ def _source_count(self, name, docname) -> int:
|
||||||
|
+ """Count the event name in the same source"""
|
||||||
|
+ sources = self.sources.get(name, set())
|
||||||
|
+ return len([s for s, t in sources if s == docname])
|
||||||
|
+
|
||||||
|
+ def id_for(self, name, docname) -> str:
|
||||||
|
+ source_count = self._source_count(name, docname)
|
||||||
|
name_clean = re.sub(r"\W", "_", name)
|
||||||
|
return f"audit_event_{name_clean}_{source_count}"
|
||||||
|
|
||||||
|
@@ -148,7 +153,7 @@
|
||||||
|
except (IndexError, TypeError):
|
||||||
|
target = None
|
||||||
|
if not target:
|
||||||
|
- target = self.env.audit_events.id_for(name)
|
||||||
|
+ target = self.env.audit_events.id_for(name, self.env.docname)
|
||||||
|
ids.append(target)
|
||||||
|
self.env.audit_events.add_event(name, args, (self.env.docname, target))
|
||||||
|
|
||||||
918
doc-py38-to-py36.patch
Normal file
918
doc-py38-to-py36.patch
Normal file
@@ -0,0 +1,918 @@
|
|||||||
|
---
|
||||||
|
Doc/Makefile | 8 +--
|
||||||
|
Doc/c-api/arg.rst | 1
|
||||||
|
Doc/c-api/typeobj.rst | 8 +--
|
||||||
|
Doc/conf.py | 29 ++++++++++---
|
||||||
|
Doc/howto/free-threading-python.rst | 2
|
||||||
|
Doc/library/doctest.rst | 1
|
||||||
|
Doc/library/email.compat32-message.rst | 1
|
||||||
|
Doc/library/xml.etree.elementtree.rst | 1
|
||||||
|
Doc/Makefile | 8 +--
|
||||||
|
Doc/c-api/arg.rst | 1
|
||||||
|
Doc/c-api/typeobj.rst | 8 +--
|
||||||
|
Doc/conf.py | 29 ++++++++++---
|
||||||
|
Doc/library/doctest.rst | 1
|
||||||
|
Doc/library/email.compat32-message.rst | 1
|
||||||
|
Doc/library/xml.etree.elementtree.rst | 1
|
||||||
|
Doc/tools/check-warnings.py | 5 +-
|
||||||
|
Doc/tools/extensions/audit_events.py | 56 ++++++++++++++------------
|
||||||
|
Doc/tools/extensions/availability.py | 15 +++---
|
||||||
|
Doc/tools/extensions/c_annotations.py | 53 ++++++++++++++++--------
|
||||||
|
Doc/tools/extensions/changes.py | 8 +--
|
||||||
|
Doc/tools/extensions/glossary_search.py | 20 ++++++---
|
||||||
|
Doc/tools/extensions/implementation_detail.py | 22 +++-------
|
||||||
|
Doc/tools/extensions/issue_role.py | 16 ++-----
|
||||||
|
Doc/tools/extensions/misc_news.py | 14 ++----
|
||||||
|
Doc/tools/extensions/patchlevel.py | 9 ++--
|
||||||
|
Doc/tools/extensions/pydoc_topics.py | 22 +++++-----
|
||||||
|
18 files changed, 159 insertions(+), 130 deletions(-)
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Doc/Makefile
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/Makefile 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/Makefile 2025-11-20 01:09:35.814292408 +0100
|
||||||
|
@@ -14,15 +14,15 @@
|
||||||
|
SOURCES =
|
||||||
|
DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py)
|
||||||
|
REQUIREMENTS = requirements.txt
|
||||||
|
-SPHINXERRORHANDLING = --fail-on-warning
|
||||||
|
+SPHINXERRORHANDLING =
|
||||||
|
|
||||||
|
# Internal variables.
|
||||||
|
PAPEROPT_a4 = --define latex_elements.papersize=a4paper
|
||||||
|
PAPEROPT_letter = --define latex_elements.papersize=letterpaper
|
||||||
|
|
||||||
|
-ALLSPHINXOPTS = --builder $(BUILDER) \
|
||||||
|
- --doctree-dir build/doctrees \
|
||||||
|
- --jobs $(JOBS) \
|
||||||
|
+ALLSPHINXOPTS = -b $(BUILDER) \
|
||||||
|
+ -d build/doctrees \
|
||||||
|
+ -j $(JOBS) \
|
||||||
|
$(PAPEROPT_$(PAPER)) \
|
||||||
|
$(SPHINXOPTS) $(SPHINXERRORHANDLING) \
|
||||||
|
. build/$(BUILDER) $(SOURCES)
|
||||||
|
Index: Python-3.13.9/Doc/c-api/arg.rst
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/c-api/arg.rst 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/c-api/arg.rst 2025-11-20 01:07:59.902914275 +0100
|
||||||
|
@@ -334,7 +334,6 @@
|
||||||
|
should raise an exception and leave the content of *address* unmodified.
|
||||||
|
|
||||||
|
.. c:macro:: Py_CLEANUP_SUPPORTED
|
||||||
|
- :no-typesetting:
|
||||||
|
|
||||||
|
If the *converter* returns :c:macro:`!Py_CLEANUP_SUPPORTED`, it may get called a
|
||||||
|
second time if the argument parsing eventually fails, giving the converter a
|
||||||
|
Index: Python-3.13.9/Doc/c-api/typeobj.rst
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/c-api/typeobj.rst 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/c-api/typeobj.rst 2025-11-20 01:07:59.903382829 +0100
|
||||||
|
@@ -610,7 +610,7 @@
|
||||||
|
Functions like :c:func:`PyObject_NewVar` will take the value of N as an
|
||||||
|
argument, and store in the instance's :c:member:`~PyVarObject.ob_size` field.
|
||||||
|
Note that the :c:member:`~PyVarObject.ob_size` field may later be used for
|
||||||
|
- other purposes. For example, :py:type:`int` instances use the bits of
|
||||||
|
+ other purposes. For example, :py:obj:`int` instances use the bits of
|
||||||
|
:c:member:`~PyVarObject.ob_size` in an implementation-defined
|
||||||
|
way; the underlying storage and its size should be accessed using
|
||||||
|
:c:func:`PyLong_Export`.
|
||||||
|
@@ -622,9 +622,9 @@
|
||||||
|
|
||||||
|
Also, the presence of an :c:member:`~PyVarObject.ob_size` field in the
|
||||||
|
instance layout doesn't mean that the instance structure is variable-length.
|
||||||
|
- For example, the :py:type:`list` type has fixed-length instances, yet those
|
||||||
|
+ For example, the :py:obj:`list` type has fixed-length instances, yet those
|
||||||
|
instances have a :c:member:`~PyVarObject.ob_size` field.
|
||||||
|
- (As with :py:type:`int`, avoid reading lists' :c:member:`!ob_size` directly.
|
||||||
|
+ (As with :py:obj:`int`, avoid reading lists' :c:member:`!ob_size` directly.
|
||||||
|
Call :c:func:`PyList_Size` instead.)
|
||||||
|
|
||||||
|
The :c:member:`!tp_basicsize` includes size needed for data of the type's
|
||||||
|
@@ -637,7 +637,7 @@
|
||||||
|
In other words, :c:member:`!tp_basicsize` must be greater than or equal
|
||||||
|
to the base's :c:member:`!tp_basicsize`.
|
||||||
|
|
||||||
|
- Since every type is a subtype of :py:type:`object`, this struct must
|
||||||
|
+ Since every type is a subtype of :py:obj:`object`, this struct must
|
||||||
|
include :c:type:`PyObject` or :c:type:`PyVarObject` (depending on
|
||||||
|
whether :c:member:`~PyVarObject.ob_size` should be included). These are
|
||||||
|
usually defined by the macro :c:macro:`PyObject_HEAD` or
|
||||||
|
Index: Python-3.13.9/Doc/conf.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/conf.py 2025-11-20 01:07:14.944126757 +0100
|
||||||
|
+++ Python-3.13.9/Doc/conf.py 2025-11-20 01:07:59.903974303 +0100
|
||||||
|
@@ -11,6 +11,8 @@
|
||||||
|
from importlib import import_module
|
||||||
|
from importlib.util import find_spec
|
||||||
|
|
||||||
|
+from sphinx import version_info
|
||||||
|
+
|
||||||
|
# Make our custom extensions available to Sphinx
|
||||||
|
sys.path.append(os.path.abspath('tools/extensions'))
|
||||||
|
sys.path.append(os.path.abspath('includes'))
|
||||||
|
@@ -57,11 +59,11 @@
|
||||||
|
import _tkinter
|
||||||
|
except ImportError:
|
||||||
|
_tkinter = None
|
||||||
|
-# Treat warnings as errors, done here to prevent warnings in Sphinx code from
|
||||||
|
-# causing spurious CPython test failures.
|
||||||
|
-import warnings
|
||||||
|
-warnings.simplefilter('error')
|
||||||
|
-del warnings
|
||||||
|
+# # Treat warnings as errors, done here to prevent warnings in Sphinx code from
|
||||||
|
+# # causing spurious CPython test failures.
|
||||||
|
+# import warnings
|
||||||
|
+# warnings.simplefilter('error')
|
||||||
|
+# del warnings
|
||||||
|
'''
|
||||||
|
|
||||||
|
manpages_url = 'https://manpages.debian.org/{path}'
|
||||||
|
@@ -92,7 +94,7 @@
|
||||||
|
|
||||||
|
# Minimum version of sphinx required
|
||||||
|
# Keep this version in sync with ``Doc/requirements.txt``.
|
||||||
|
-needs_sphinx = '8.2.0'
|
||||||
|
+needs_sphinx = '4.2.0'
|
||||||
|
|
||||||
|
# Create table of contents entries for domain objects (e.g. functions, classes,
|
||||||
|
# attributes, etc.). Default is True.
|
||||||
|
@@ -257,6 +259,9 @@
|
||||||
|
# Avoid a warning with Sphinx >= 4.0
|
||||||
|
root_doc = 'contents'
|
||||||
|
|
||||||
|
+# Compatibility on old Sphinx
|
||||||
|
+suppress_warnings = ['pygments.ParserNotFound']
|
||||||
|
+
|
||||||
|
# Allow translation of index directives
|
||||||
|
gettext_additional_targets = [
|
||||||
|
'index',
|
||||||
|
@@ -296,7 +301,7 @@
|
||||||
|
# (See .readthedocs.yml and https://docs.readthedocs.io/en/stable/reference/environment-variables.html)
|
||||||
|
is_deployment_preview = os.getenv("READTHEDOCS_VERSION_TYPE") == "external"
|
||||||
|
repository_url = os.getenv("READTHEDOCS_GIT_CLONE_URL", "")
|
||||||
|
-repository_url = repository_url.removesuffix(".git")
|
||||||
|
+repository_url = repository_url[:-len(".git")]
|
||||||
|
html_context = {
|
||||||
|
"is_deployment_preview": is_deployment_preview,
|
||||||
|
"repository_url": repository_url or None,
|
||||||
|
@@ -607,6 +612,16 @@
|
||||||
|
}
|
||||||
|
extlinks_detect_hardcoded_links = True
|
||||||
|
|
||||||
|
+if version_info[:2] < (8, 1):
|
||||||
|
+ # Sphinx 8.1 has in-built CVE and CWE roles.
|
||||||
|
+ extlinks.update({
|
||||||
|
+ "cve": (
|
||||||
|
+ "https://www.cve.org/CVERecord?id=CVE-%s",
|
||||||
|
+ "CVE-%s",
|
||||||
|
+ ),
|
||||||
|
+ "cwe": ("https://cwe.mitre.org/data/definitions/%s.html", "CWE-%s"),
|
||||||
|
+ })
|
||||||
|
+
|
||||||
|
# Options for c_annotations extension
|
||||||
|
# -----------------------------------
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Doc/library/doctest.rst
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/library/doctest.rst 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/library/doctest.rst 2025-11-20 01:07:59.904511686 +0100
|
||||||
|
@@ -310,7 +310,6 @@
|
||||||
|
.. currentmodule:: None
|
||||||
|
|
||||||
|
.. attribute:: module.__test__
|
||||||
|
- :no-typesetting:
|
||||||
|
|
||||||
|
.. currentmodule:: doctest
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Doc/library/email.compat32-message.rst
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/library/email.compat32-message.rst 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/library/email.compat32-message.rst 2025-11-20 01:07:59.905009154 +0100
|
||||||
|
@@ -7,7 +7,6 @@
|
||||||
|
:synopsis: The base class representing email messages in a fashion
|
||||||
|
backward compatible with Python 3.2
|
||||||
|
:noindex:
|
||||||
|
- :no-index:
|
||||||
|
|
||||||
|
|
||||||
|
The :class:`Message` class is very similar to the
|
||||||
|
Index: Python-3.13.9/Doc/library/xml.etree.elementtree.rst
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/library/xml.etree.elementtree.rst 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/library/xml.etree.elementtree.rst 2025-11-20 01:07:59.905273001 +0100
|
||||||
|
@@ -873,7 +873,6 @@
|
||||||
|
|
||||||
|
.. module:: xml.etree.ElementTree
|
||||||
|
:noindex:
|
||||||
|
- :no-index:
|
||||||
|
|
||||||
|
.. class:: Element(tag, attrib={}, **extra)
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Doc/tools/check-warnings.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/check-warnings.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/check-warnings.py 2025-11-20 01:07:59.905613002 +0100
|
||||||
|
@@ -228,7 +228,8 @@
|
||||||
|
print(filename)
|
||||||
|
for warning in warnings:
|
||||||
|
if filename in warning:
|
||||||
|
- if match := WARNING_PATTERN.fullmatch(warning):
|
||||||
|
+ match = WARNING_PATTERN.fullmatch(warning)
|
||||||
|
+ if match:
|
||||||
|
print(" {line}: {msg}".format_map(match))
|
||||||
|
return -1
|
||||||
|
return 0
|
||||||
|
@@ -316,7 +317,7 @@
|
||||||
|
|
||||||
|
cwd = str(Path.cwd()) + os.path.sep
|
||||||
|
files_with_nits = {
|
||||||
|
- warning.removeprefix(cwd).split(":")[0]
|
||||||
|
+ (warning[len(cwd):].split(":")[0] if warning.startswith(cwd) else warning.split(":")[0])
|
||||||
|
for warning in warnings
|
||||||
|
if "Doc/" in warning
|
||||||
|
}
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/audit_events.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/audit_events.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/audit_events.py 2025-11-20 01:08:35.819222654 +0100
|
||||||
|
@@ -1,9 +1,6 @@
|
||||||
|
"""Support for documenting audit events."""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
import re
|
||||||
|
-from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from sphinx.errors import NoUri
|
||||||
|
@@ -12,12 +9,19 @@
|
||||||
|
from sphinx.util import logging
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
|
|
||||||
|
-if TYPE_CHECKING:
|
||||||
|
- from collections.abc import Iterator, Set
|
||||||
|
+from typing import Any, Iterator, List, Set, Tuple
|
||||||
|
+
|
||||||
|
+from sphinx.application import Sphinx
|
||||||
|
+from sphinx.builders import Builder
|
||||||
|
+from sphinx.environment import BuildEnvironment
|
||||||
|
+
|
||||||
|
+# --- The Monkey Patch ---
|
||||||
|
+def findall_patch(self, *args, **kwargs):
|
||||||
|
+ """A backwards-compatible findall method that calls traverse."""
|
||||||
|
+ return self.traverse(*args, **kwargs)
|
||||||
|
|
||||||
|
- from sphinx.application import Sphinx
|
||||||
|
- from sphinx.builders import Builder
|
||||||
|
- from sphinx.environment import BuildEnvironment
|
||||||
|
+if not hasattr(nodes.Node, 'findall'):
|
||||||
|
+ nodes.Node.findall = findall_patch
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@@ -32,16 +36,16 @@
|
||||||
|
|
||||||
|
class AuditEvents:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
- self.events: dict[str, list[str]] = {}
|
||||||
|
- self.sources: dict[str, set[tuple[str, str]]] = {}
|
||||||
|
+ self.events: dict[str, List[str]] = {}
|
||||||
|
+ self.sources: dict[str, Set[Tuple[str, str]]] = {}
|
||||||
|
|
||||||
|
- def __iter__(self) -> Iterator[tuple[str, list[str], tuple[str, str]]]:
|
||||||
|
+ def __iter__(self) -> Iterator[Any]:
|
||||||
|
for name, args in self.events.items():
|
||||||
|
for source in self.sources[name]:
|
||||||
|
yield name, args, source
|
||||||
|
|
||||||
|
def add_event(
|
||||||
|
- self, name, args: list[str], source: tuple[str, str]
|
||||||
|
+ self, name, args: List[str], source: Tuple[str, str]
|
||||||
|
) -> None:
|
||||||
|
if name in self.events:
|
||||||
|
self._check_args_match(name, args)
|
||||||
|
@@ -49,7 +53,7 @@
|
||||||
|
self.events[name] = args
|
||||||
|
self.sources.setdefault(name, set()).add(source)
|
||||||
|
|
||||||
|
- def _check_args_match(self, name: str, args: list[str]) -> None:
|
||||||
|
+ def _check_args_match(self, name: str, args: List[str]) -> None:
|
||||||
|
current_args = self.events[name]
|
||||||
|
msg = (
|
||||||
|
f"Mismatched arguments for audit-event {name}: "
|
||||||
|
@@ -60,7 +64,7 @@
|
||||||
|
if len(current_args) != len(args):
|
||||||
|
logger.warning(msg)
|
||||||
|
return
|
||||||
|
- for a1, a2 in zip(current_args, args, strict=False):
|
||||||
|
+ for a1, a2 in zip(current_args, args):
|
||||||
|
if a1 == a2:
|
||||||
|
continue
|
||||||
|
if any(a1 in s and a2 in s for s in _SYNONYMS):
|
||||||
|
@@ -73,7 +77,7 @@
|
||||||
|
name_clean = re.sub(r"\W", "_", name)
|
||||||
|
return f"audit_event_{name_clean}_{source_count}"
|
||||||
|
|
||||||
|
- def rows(self) -> Iterator[tuple[str, list[str], Set[tuple[str, str]]]]:
|
||||||
|
+ def rows(self) -> Iterator[Any]:
|
||||||
|
for name in sorted(self.events.keys()):
|
||||||
|
yield name, self.events[name], self.sources[name]
|
||||||
|
|
||||||
|
@@ -97,7 +101,7 @@
|
||||||
|
def audit_events_merge(
|
||||||
|
app: Sphinx,
|
||||||
|
env: BuildEnvironment,
|
||||||
|
- docnames: list[str],
|
||||||
|
+ docnames: List[str],
|
||||||
|
other: BuildEnvironment,
|
||||||
|
) -> None:
|
||||||
|
"""In Sphinx parallel builds, this merges audit_events from subprocesses."""
|
||||||
|
@@ -126,14 +130,16 @@
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
- def run(self) -> list[nodes.paragraph]:
|
||||||
|
+ def run(self) -> List[nodes.paragraph]:
|
||||||
|
+ def _no_walrus_op(args):
|
||||||
|
+ for arg in args.strip("'\"").split(","):
|
||||||
|
+ aarg = arg.strip()
|
||||||
|
+ if aarg:
|
||||||
|
+ yield aarg
|
||||||
|
+
|
||||||
|
name = self.arguments[0]
|
||||||
|
if len(self.arguments) >= 2 and self.arguments[1]:
|
||||||
|
- args = [
|
||||||
|
- arg
|
||||||
|
- for argument in self.arguments[1].strip("'\"").split(",")
|
||||||
|
- if (arg := argument.strip())
|
||||||
|
- ]
|
||||||
|
+ args = list(_no_walrus_op(self.arguments[1]))
|
||||||
|
else:
|
||||||
|
args = []
|
||||||
|
ids = []
|
||||||
|
@@ -169,7 +175,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
class AuditEventListDirective(SphinxDirective):
|
||||||
|
- def run(self) -> list[audit_event_list]:
|
||||||
|
+ def run(self) -> List[audit_event_list]:
|
||||||
|
return [audit_event_list()]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -217,8 +223,8 @@
|
||||||
|
builder: Builder,
|
||||||
|
docname: str,
|
||||||
|
name: str,
|
||||||
|
- args: list[str],
|
||||||
|
- sources: Set[tuple[str, str]],
|
||||||
|
+ args: List[str],
|
||||||
|
+ sources: Set[Tuple[str, str]],
|
||||||
|
) -> nodes.row:
|
||||||
|
row = nodes.row()
|
||||||
|
name_node = nodes.paragraph("", nodes.Text(name))
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/availability.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/availability.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/availability.py 2025-11-20 01:07:59.906156697 +0100
|
||||||
|
@@ -1,8 +1,6 @@
|
||||||
|
"""Support for documenting platform availability"""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
-from typing import TYPE_CHECKING
|
||||||
|
+from typing import Dict, List, TYPE_CHECKING, Union
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from sphinx import addnodes
|
||||||
|
@@ -55,7 +53,7 @@
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
|
||||||
|
- def run(self) -> list[nodes.container]:
|
||||||
|
+ def run(self) -> List[nodes.container]:
|
||||||
|
title = sphinx_gettext("Availability")
|
||||||
|
refnode = addnodes.pending_xref(
|
||||||
|
title,
|
||||||
|
@@ -79,7 +77,7 @@
|
||||||
|
|
||||||
|
return [cnode]
|
||||||
|
|
||||||
|
- def parse_platforms(self) -> dict[str, str | bool]:
|
||||||
|
+ def parse_platforms(self) -> Dict[str, Union[str, bool]]:
|
||||||
|
"""Parse platform information from arguments
|
||||||
|
|
||||||
|
Arguments is a comma-separated string of platforms. A platform may
|
||||||
|
@@ -98,12 +96,13 @@
|
||||||
|
platform, _, version = arg.partition(" >= ")
|
||||||
|
if platform.startswith("not "):
|
||||||
|
version = False
|
||||||
|
- platform = platform.removeprefix("not ")
|
||||||
|
+ platform = platform[len("not "):]
|
||||||
|
elif not version:
|
||||||
|
version = True
|
||||||
|
platforms[platform] = version
|
||||||
|
|
||||||
|
- if unknown := set(platforms).difference(KNOWN_PLATFORMS):
|
||||||
|
+ unknown = set(platforms).difference(KNOWN_PLATFORMS)
|
||||||
|
+ if unknown:
|
||||||
|
logger.warning(
|
||||||
|
"Unknown platform%s or syntax '%s' in '.. availability:: %s', "
|
||||||
|
"see %s:KNOWN_PLATFORMS for a set of known platforms.",
|
||||||
|
@@ -116,7 +115,7 @@
|
||||||
|
return platforms
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app):
|
||||||
|
app.add_directive("availability", Availability)
|
||||||
|
|
||||||
|
return {
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/c_annotations.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/c_annotations.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/c_annotations.py 2025-11-20 01:07:59.906354780 +0100
|
||||||
|
@@ -9,22 +9,26 @@
|
||||||
|
* Set ``stable_abi_file`` to the path to stable ABI list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
import csv
|
||||||
|
import dataclasses
|
||||||
|
from pathlib import Path
|
||||||
|
-from typing import TYPE_CHECKING
|
||||||
|
+from typing import Any, Dict, List, TYPE_CHECKING, Union
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from docutils.statemachine import StringList
|
||||||
|
-from sphinx import addnodes
|
||||||
|
+from sphinx import addnodes, version_info
|
||||||
|
from sphinx.locale import _ as sphinx_gettext
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
|
|
||||||
|
-if TYPE_CHECKING:
|
||||||
|
- from sphinx.application import Sphinx
|
||||||
|
- from sphinx.util.typing import ExtensionMetadata
|
||||||
|
+from sphinx.application import Sphinx
|
||||||
|
+
|
||||||
|
+# --- The Monkey Patch ---
|
||||||
|
+def findall_patch(self, *args, **kwargs):
|
||||||
|
+ """A backwards-compatible findall method that calls traverse."""
|
||||||
|
+ return self.traverse(*args, **kwargs)
|
||||||
|
+
|
||||||
|
+if not hasattr(nodes.Node, 'findall'):
|
||||||
|
+ nodes.Node.findall = findall_patch
|
||||||
|
|
||||||
|
ROLE_TO_OBJECT_TYPE = {
|
||||||
|
"func": "function",
|
||||||
|
@@ -35,20 +39,20 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-@dataclasses.dataclass(slots=True)
|
||||||
|
+@dataclasses.dataclass()
|
||||||
|
class RefCountEntry:
|
||||||
|
# Name of the function.
|
||||||
|
name: str
|
||||||
|
# List of (argument name, type, refcount effect) tuples.
|
||||||
|
# (Currently not used. If it was, a dataclass might work better.)
|
||||||
|
- args: list = dataclasses.field(default_factory=list)
|
||||||
|
+ args: List = dataclasses.field(default_factory=list)
|
||||||
|
# Return type of the function.
|
||||||
|
result_type: str = ""
|
||||||
|
# Reference count effect for the return value.
|
||||||
|
- result_refs: int | None = None
|
||||||
|
+ result_refs: Union[int, None] = None
|
||||||
|
|
||||||
|
|
||||||
|
-@dataclasses.dataclass(frozen=True, slots=True)
|
||||||
|
+@dataclasses.dataclass(frozen=True)
|
||||||
|
class StableABIEntry:
|
||||||
|
# Role of the object.
|
||||||
|
# Source: Each [item_kind] in stable_abi.toml is mapped to a C Domain role.
|
||||||
|
@@ -67,7 +71,7 @@
|
||||||
|
struct_abi_kind: str
|
||||||
|
|
||||||
|
|
||||||
|
-def read_refcount_data(refcount_filename: Path) -> dict[str, RefCountEntry]:
|
||||||
|
+def read_refcount_data(refcount_filename: Path) -> Dict[str, RefCountEntry]:
|
||||||
|
refcount_data = {}
|
||||||
|
refcounts = refcount_filename.read_text(encoding="utf8")
|
||||||
|
for line in refcounts.splitlines():
|
||||||
|
@@ -103,7 +107,7 @@
|
||||||
|
return refcount_data
|
||||||
|
|
||||||
|
|
||||||
|
-def read_stable_abi_data(stable_abi_file: Path) -> dict[str, StableABIEntry]:
|
||||||
|
+def read_stable_abi_data(stable_abi_file: Path) -> Dict[str, StableABIEntry]:
|
||||||
|
stable_abi_data = {}
|
||||||
|
with open(stable_abi_file, encoding="utf8") as fp:
|
||||||
|
for record in csv.DictReader(fp):
|
||||||
|
@@ -123,11 +127,14 @@
|
||||||
|
continue
|
||||||
|
if not par[0].get("ids", None):
|
||||||
|
continue
|
||||||
|
- name = par[0]["ids"][0].removeprefix("c.")
|
||||||
|
+ name = par[0]["ids"][0]
|
||||||
|
+ if name.startswith("c."):
|
||||||
|
+ name = name[len("c."):]
|
||||||
|
objtype = par["objtype"]
|
||||||
|
|
||||||
|
# Stable ABI annotation.
|
||||||
|
- if record := stable_abi_data.get(name):
|
||||||
|
+ record = stable_abi_data.get(name)
|
||||||
|
+ if record:
|
||||||
|
if ROLE_TO_OBJECT_TYPE[record.role] != objtype:
|
||||||
|
msg = (
|
||||||
|
f"Object type mismatch in limited API annotation for {name}: "
|
||||||
|
@@ -234,7 +241,7 @@
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def _return_value_annotation(result_refs: int | None) -> nodes.emphasis:
|
||||||
|
+def _return_value_annotation(result_refs: Union[int, None]) -> nodes.emphasis:
|
||||||
|
classes = ["refcount"]
|
||||||
|
if result_refs is None:
|
||||||
|
rc = sphinx_gettext("Return value: Always NULL.")
|
||||||
|
@@ -254,7 +261,7 @@
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
|
||||||
|
- def run(self) -> list[nodes.Node]:
|
||||||
|
+ def run(self) -> List[nodes.Node]:
|
||||||
|
state = self.env.domaindata["c_annotations"]
|
||||||
|
content = [
|
||||||
|
f"* :c:{record.role}:`{record.name}`"
|
||||||
|
@@ -277,13 +284,23 @@
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app: Sphinx) -> Any:
|
||||||
|
app.add_config_value("refcount_file", "", "env", types={str})
|
||||||
|
app.add_config_value("stable_abi_file", "", "env", types={str})
|
||||||
|
app.add_directive("limited-api-list", LimitedAPIList)
|
||||||
|
app.connect("builder-inited", init_annotations)
|
||||||
|
app.connect("doctree-read", add_annotations)
|
||||||
|
|
||||||
|
+ if version_info[:2] < (7, 2):
|
||||||
|
+ from docutils.parsers.rst import directives
|
||||||
|
+ from sphinx.domains.c import CObject
|
||||||
|
+
|
||||||
|
+ # monkey-patch C object...
|
||||||
|
+ CObject.option_spec.update({
|
||||||
|
+ "no-index-entry": directives.flag,
|
||||||
|
+ "no-contents-entry": directives.flag,
|
||||||
|
+ })
|
||||||
|
+
|
||||||
|
return {
|
||||||
|
"version": "1.0",
|
||||||
|
"parallel_read_safe": True,
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/changes.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/changes.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/changes.py 2025-11-20 01:07:59.906539198 +0100
|
||||||
|
@@ -1,7 +1,5 @@
|
||||||
|
"""Support for documenting version of changes, additions, deprecations."""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from sphinx.domains.changeset import (
|
||||||
|
@@ -25,7 +23,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
class PyVersionChange(VersionChange):
|
||||||
|
- def run(self) -> list[Node]:
|
||||||
|
+ def run(self) -> "list[Node]":
|
||||||
|
# Replace the 'next' special token with the current development version
|
||||||
|
self.arguments[0] = expand_version_arg(
|
||||||
|
self.arguments[0], self.config.release
|
||||||
|
@@ -43,7 +41,7 @@
|
||||||
|
"Deprecated since version %s, removed in version %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
- def run(self) -> list[Node]:
|
||||||
|
+ def run(self) -> "list[Node]":
|
||||||
|
# Replace the first two arguments (deprecated version and removed version)
|
||||||
|
# with a single tuple of both versions.
|
||||||
|
version_deprecated = expand_version_arg(
|
||||||
|
@@ -73,7 +71,7 @@
|
||||||
|
versionlabel_classes[self.name] = ""
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app: "Sphinx") -> "ExtensionMetadata":
|
||||||
|
# Override Sphinx's directives with support for 'next'
|
||||||
|
app.add_directive("versionadded", PyVersionChange, override=True)
|
||||||
|
app.add_directive("versionchanged", PyVersionChange, override=True)
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/glossary_search.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/glossary_search.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/glossary_search.py 2025-11-20 01:07:59.906696224 +0100
|
||||||
|
@@ -1,21 +1,27 @@
|
||||||
|
"""Feature search results for glossary items prominently."""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
-from typing import TYPE_CHECKING
|
||||||
|
+from typing import Any, TYPE_CHECKING
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from sphinx.addnodes import glossary
|
||||||
|
from sphinx.util import logging
|
||||||
|
|
||||||
|
-if TYPE_CHECKING:
|
||||||
|
- from sphinx.application import Sphinx
|
||||||
|
- from sphinx.util.typing import ExtensionMetadata
|
||||||
|
+from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
+from docutils import nodes
|
||||||
|
+from sphinx import addnodes
|
||||||
|
+
|
||||||
|
+# --- The Monkey Patch ---
|
||||||
|
+def findall_patch(self, *args, **kwargs):
|
||||||
|
+ """A backwards-compatible findall method that calls traverse."""
|
||||||
|
+ return self.traverse(*args, **kwargs)
|
||||||
|
+
|
||||||
|
+if not hasattr(nodes.Node, 'findall'):
|
||||||
|
+ nodes.Node.findall = findall_patch
|
||||||
|
|
||||||
|
def process_glossary_nodes(
|
||||||
|
app: Sphinx,
|
||||||
|
@@ -52,7 +58,7 @@
|
||||||
|
dest.write_text(json.dumps(app.env.glossary_terms), encoding='utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app: Sphinx) -> Any:
|
||||||
|
app.connect('doctree-resolved', process_glossary_nodes)
|
||||||
|
app.connect('build-finished', write_glossary_json)
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/implementation_detail.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/implementation_detail.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/implementation_detail.py 2025-11-20 01:07:59.906853200 +0100
|
||||||
|
@@ -1,17 +1,10 @@
|
||||||
|
"""Support for marking up implementation details."""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
-from typing import TYPE_CHECKING
|
||||||
|
-
|
||||||
|
from docutils import nodes
|
||||||
|
from sphinx.locale import _ as sphinx_gettext
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
|
|
||||||
|
-if TYPE_CHECKING:
|
||||||
|
- from sphinx.application import Sphinx
|
||||||
|
- from sphinx.util.typing import ExtensionMetadata
|
||||||
|
-
|
||||||
|
+from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
class ImplementationDetail(SphinxDirective):
|
||||||
|
has_content = True
|
||||||
|
@@ -21,23 +14,24 @@
|
||||||
|
label_text = sphinx_gettext("CPython implementation detail:")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
- self.assert_has_content()
|
||||||
|
- content_nodes = self.parse_content_to_nodes()
|
||||||
|
+ container_node = nodes.container()
|
||||||
|
+ container_node.document = self.state.document # Ensure node has document context
|
||||||
|
+ self.state.nested_parse(self.content, self.content_offset, container_node)
|
||||||
|
+ parsed_nodes = container_node.children
|
||||||
|
|
||||||
|
# insert our prefix at the start of the first paragraph
|
||||||
|
- first_node = content_nodes[0]
|
||||||
|
+ first_node = parsed_nodes[0]
|
||||||
|
first_node[:0] = [
|
||||||
|
nodes.strong(self.label_text, self.label_text),
|
||||||
|
nodes.Text(" "),
|
||||||
|
]
|
||||||
|
|
||||||
|
- # create a new compound container node
|
||||||
|
- cnode = nodes.compound("", *content_nodes, classes=["impl-detail"])
|
||||||
|
+ cnode = nodes.compound("", *parsed_nodes, classes=["impl-detail"])
|
||||||
|
self.set_source_info(cnode)
|
||||||
|
return [cnode]
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app: Sphinx):
|
||||||
|
app.add_directive("impl-detail", ImplementationDetail)
|
||||||
|
|
||||||
|
return {
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/issue_role.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/issue_role.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/issue_role.py 2025-11-20 01:07:59.907010386 +0100
|
||||||
|
@@ -1,22 +1,18 @@
|
||||||
|
"""Support for referencing issues in the tracker."""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
-from typing import TYPE_CHECKING
|
||||||
|
+from typing import TYPE_CHECKING, List, Tuple
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from sphinx.util.docutils import SphinxRole
|
||||||
|
|
||||||
|
-if TYPE_CHECKING:
|
||||||
|
- from docutils.nodes import Element
|
||||||
|
- from sphinx.application import Sphinx
|
||||||
|
- from sphinx.util.typing import ExtensionMetadata
|
||||||
|
+from docutils.nodes import Element
|
||||||
|
+from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
|
||||||
|
class BPOIssue(SphinxRole):
|
||||||
|
ISSUE_URI = "https://bugs.python.org/issue?@action=redirect&bpo={0}"
|
||||||
|
|
||||||
|
- def run(self) -> tuple[list[Element], list[nodes.system_message]]:
|
||||||
|
+ def run(self) -> Tuple[List[Element], List[nodes.system_message]]:
|
||||||
|
issue = self.text
|
||||||
|
|
||||||
|
# sanity check: there are no bpo issues within these two values
|
||||||
|
@@ -38,7 +34,7 @@
|
||||||
|
class GitHubIssue(SphinxRole):
|
||||||
|
ISSUE_URI = "https://github.com/python/cpython/issues/{0}"
|
||||||
|
|
||||||
|
- def run(self) -> tuple[list[Element], list[nodes.system_message]]:
|
||||||
|
+ def run(self) -> Tuple[List[Element], List[nodes.system_message]]:
|
||||||
|
issue = self.text
|
||||||
|
|
||||||
|
# sanity check: all GitHub issues have ID >= 32426
|
||||||
|
@@ -58,7 +54,7 @@
|
||||||
|
return [refnode], []
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app: Sphinx) -> "ExtensionMetadata":
|
||||||
|
app.add_role("issue", BPOIssue())
|
||||||
|
app.add_role("gh", GitHubIssue())
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/misc_news.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/misc_news.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/misc_news.py 2025-11-20 01:07:59.907170899 +0100
|
||||||
|
@@ -1,7 +1,5 @@
|
||||||
|
"""Support for including Misc/NEWS."""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
@@ -24,13 +22,13 @@
|
||||||
|
+++++++++++
|
||||||
|
"""
|
||||||
|
|
||||||
|
-bpo_issue_re: Final[re.Pattern[str]] = re.compile(
|
||||||
|
+bpo_issue_re: "Final[re.Pattern[str]]" = re.compile(
|
||||||
|
"(?:issue #|bpo-)([0-9]+)", re.ASCII
|
||||||
|
)
|
||||||
|
-gh_issue_re: Final[re.Pattern[str]] = re.compile(
|
||||||
|
+gh_issue_re: "Final[re.Pattern[str]]" = re.compile(
|
||||||
|
"gh-(?:issue-)?([0-9]+)", re.ASCII | re.IGNORECASE
|
||||||
|
)
|
||||||
|
-whatsnew_re: Final[re.Pattern[str]] = re.compile(
|
||||||
|
+whatsnew_re: "Final[re.Pattern[str]]" = re.compile(
|
||||||
|
r"^what's new in (.*?)\??$", re.ASCII | re.IGNORECASE | re.MULTILINE
|
||||||
|
)
|
||||||
|
|
||||||
|
@@ -42,7 +40,7 @@
|
||||||
|
final_argument_whitespace = False
|
||||||
|
option_spec = {}
|
||||||
|
|
||||||
|
- def run(self) -> list[Node]:
|
||||||
|
+ def run(self) -> "list[Node]":
|
||||||
|
# Get content of NEWS file
|
||||||
|
source, _ = self.get_source_info()
|
||||||
|
news_file = Path(source).resolve().parent / self.arguments[0]
|
||||||
|
@@ -54,7 +52,7 @@
|
||||||
|
return [nodes.strong(text, text)]
|
||||||
|
|
||||||
|
# remove first 3 lines as they are the main heading
|
||||||
|
- news_text = news_text.removeprefix(BLURB_HEADER)
|
||||||
|
+ news_text = news_text[len(BLURB_HEADER):] if news_text.startswith(BLURB_HEADER) else news_text
|
||||||
|
|
||||||
|
news_text = bpo_issue_re.sub(r":issue:`\1`", news_text)
|
||||||
|
# Fallback handling for GitHub issues
|
||||||
|
@@ -65,7 +63,7 @@
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app: "Sphinx") -> "ExtensionMetadata":
|
||||||
|
app.add_directive("miscnews", MiscNews)
|
||||||
|
|
||||||
|
return {
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/patchlevel.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/patchlevel.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/patchlevel.py 2025-11-20 01:07:59.907494228 +0100
|
||||||
|
@@ -3,7 +3,7 @@
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
-from typing import Literal, NamedTuple
|
||||||
|
+from typing import NamedTuple, Tuple
|
||||||
|
|
||||||
|
CPYTHON_ROOT = Path(
|
||||||
|
__file__, # cpython/Doc/tools/extensions/patchlevel.py
|
||||||
|
@@ -26,7 +26,7 @@
|
||||||
|
major: int #: Major release number
|
||||||
|
minor: int #: Minor release number
|
||||||
|
micro: int #: Patch release number
|
||||||
|
- releaselevel: Literal["alpha", "beta", "candidate", "final"]
|
||||||
|
+ releaselevel: str
|
||||||
|
serial: int #: Serial release number
|
||||||
|
|
||||||
|
|
||||||
|
@@ -37,7 +37,8 @@
|
||||||
|
defines = {}
|
||||||
|
patchlevel_h = PATCHLEVEL_H.read_text(encoding="utf-8")
|
||||||
|
for line in patchlevel_h.splitlines():
|
||||||
|
- if (m := pat.match(line)) is not None:
|
||||||
|
+ m = pat.match(line)
|
||||||
|
+ if m is not None:
|
||||||
|
name, value = m.groups()
|
||||||
|
defines[name] = value
|
||||||
|
|
||||||
|
@@ -50,7 +51,7 @@
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def format_version_info(info: version_info) -> tuple[str, str]:
|
||||||
|
+def format_version_info(info: version_info) -> Tuple[str, str]:
|
||||||
|
version = f"{info.major}.{info.minor}"
|
||||||
|
release = f"{info.major}.{info.minor}.{info.micro}"
|
||||||
|
if info.releaselevel != "final":
|
||||||
|
Index: Python-3.13.9/Doc/tools/extensions/pydoc_topics.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Doc/tools/extensions/pydoc_topics.py 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Doc/tools/extensions/pydoc_topics.py 2025-11-20 01:07:59.907684617 +0100
|
||||||
|
@@ -1,21 +1,23 @@
|
||||||
|
"""Support for building "topic help" for pydoc."""
|
||||||
|
|
||||||
|
-from __future__ import annotations
|
||||||
|
-
|
||||||
|
from time import asctime
|
||||||
|
-from typing import TYPE_CHECKING
|
||||||
|
+from typing import TYPE_CHECKING, Tuple
|
||||||
|
|
||||||
|
from sphinx.builders.text import TextBuilder
|
||||||
|
from sphinx.util import logging
|
||||||
|
-from sphinx.util.display import status_iterator
|
||||||
|
+try:
|
||||||
|
+ from sphinx.util.display import status_iterator
|
||||||
|
+except ModuleNotFoundError:
|
||||||
|
+ from sphinx.util import status_iterator
|
||||||
|
from sphinx.util.docutils import new_document
|
||||||
|
from sphinx.writers.text import TextTranslator
|
||||||
|
|
||||||
|
-if TYPE_CHECKING:
|
||||||
|
+try:
|
||||||
|
+ from typing import Sequence, Set
|
||||||
|
+except ModuleNotFoundError:
|
||||||
|
from collections.abc import Sequence, Set
|
||||||
|
|
||||||
|
- from sphinx.application import Sphinx
|
||||||
|
- from sphinx.util.typing import ExtensionMetadata
|
||||||
|
+from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@@ -162,7 +164,7 @@
|
||||||
|
self.outdir.joinpath("topics.py").write_text(topics, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
-def _display_labels(item: tuple[str, Sequence[tuple[str, str]]]) -> str:
|
||||||
|
+def _display_labels(item: Tuple[str, Sequence[Tuple[str, str]]]) -> str:
|
||||||
|
_docname, label_ids = item
|
||||||
|
labels = [name for name, _id in label_ids]
|
||||||
|
if len(labels) > 4:
|
||||||
|
@@ -170,7 +172,7 @@
|
||||||
|
return ", ".join(labels)
|
||||||
|
|
||||||
|
|
||||||
|
-def _repr(text: str, /) -> str:
|
||||||
|
+def _repr(text: str) -> str:
|
||||||
|
"""Return a triple-single-quoted representation of text."""
|
||||||
|
if "'''" not in text:
|
||||||
|
return f"r'''{text}'''"
|
||||||
|
@@ -178,7 +180,7 @@
|
||||||
|
return f"'''{text}'''"
|
||||||
|
|
||||||
|
|
||||||
|
-def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
|
+def setup(app: Sphinx) -> "ExtensionMetadata":
|
||||||
|
app.add_builder(PydocTopicsBuilder)
|
||||||
|
|
||||||
|
return {
|
||||||
32
fix-test-recursion-limit-15.6.patch
Normal file
32
fix-test-recursion-limit-15.6.patch
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_compile.py | 5 +++++
|
||||||
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_compile.py
|
||||||
|
+++ b/Lib/test/test_compile.py
|
||||||
|
@@ -21,6 +21,9 @@ from test.support import (script_helper,
|
||||||
|
from test.support.bytecode_helper import instructions_with_positions
|
||||||
|
from test.support.os_helper import FakePath
|
||||||
|
|
||||||
|
+IS_SLE_15_6 = os.environ.get("SLE_VERSION", "") == "0150700"
|
||||||
|
+IS_32bit = hasattr(os, "uname") and os.uname().machine in ["i386", "i486", "i586", "i686"]
|
||||||
|
+
|
||||||
|
class TestSpecifics(unittest.TestCase):
|
||||||
|
|
||||||
|
def compile_single(self, source):
|
||||||
|
@@ -117,6 +120,7 @@ class TestSpecifics(unittest.TestCase):
|
||||||
|
self.assertEqual(d['z'], 12)
|
||||||
|
|
||||||
|
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
|
||||||
|
+ @unittest.skipIf(IS_SLE_15_6 and IS_32bit, "fails on 15.6 i586")
|
||||||
|
def test_extended_arg(self):
|
||||||
|
repeat = int(get_c_recursion_limit() * 0.9)
|
||||||
|
longexpr = 'x = x or ' + '-x' * repeat
|
||||||
|
@@ -701,6 +705,7 @@ class TestSpecifics(unittest.TestCase):
|
||||||
|
|
||||||
|
@support.cpython_only
|
||||||
|
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
|
||||||
|
+ @unittest.skipIf(IS_SLE_15_6 and IS_32bit, "fails on 15.6 i586")
|
||||||
|
def test_compiler_recursion_limit(self):
|
||||||
|
# Expected limit is Py_C_RECURSION_LIMIT
|
||||||
|
limit = get_c_recursion_limit()
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
Misc/NEWS | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
--- a/Misc/NEWS
|
|
||||||
+++ b/Misc/NEWS
|
|
||||||
@@ -17608,7 +17608,7 @@ C API
|
|
||||||
- bpo-40939: Removed documentation for the removed ``PyParser_*`` C API.
|
|
||||||
|
|
||||||
- bpo-43795: The list in :ref:`limited-api-list` now shows the public name
|
|
||||||
- :c:struct:`PyFrameObject` rather than ``_frame``. The non-existing entry
|
|
||||||
+ :c:type:`PyFrameObject` rather than ``_frame``. The non-existing entry
|
|
||||||
``_node`` no longer appears in the list.
|
|
||||||
|
|
||||||
- bpo-44378: :c:func:`Py_IS_TYPE` no longer uses :c:func:`Py_TYPE` to avoid
|
|
||||||
95
gh126985-mv-pyvenv.cfg2getpath.patch
Normal file
95
gh126985-mv-pyvenv.cfg2getpath.patch
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
commit 2b0e2b2893a821ca36cd65a204bed932741ac189
|
||||||
|
Author: Filipe Laíns 🇵🇸 <lains@riseup.net>
|
||||||
|
Date: Tue Nov 26 13:46:33 2024 +0000
|
||||||
|
|
||||||
|
GH-126985: move pyvenv.cfg detection from site to getpath (#126987)
|
||||||
|
|
||||||
|
---
|
||||||
|
Lib/test/test_sysconfig.py | 67 ---------------------------------------------
|
||||||
|
1 file changed, 1 insertion(+), 66 deletions(-)
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Lib/test/test_sysconfig.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Lib/test/test_sysconfig.py 2025-11-04 17:41:28.521386489 +0100
|
||||||
|
+++ Python-3.13.9/Lib/test/test_sysconfig.py 2025-11-04 17:42:36.888243505 +0100
|
||||||
|
@@ -110,6 +110,7 @@
|
||||||
|
**venv_create_args,
|
||||||
|
)
|
||||||
|
|
||||||
|
+
|
||||||
|
def test_get_path_names(self):
|
||||||
|
self.assertEqual(get_path_names(), sysconfig._SCHEME_KEYS)
|
||||||
|
|
||||||
|
@@ -611,72 +612,6 @@
|
||||||
|
suffix = sysconfig.get_config_var('EXT_SUFFIX')
|
||||||
|
self.assertTrue(suffix.endswith('-darwin.so'), suffix)
|
||||||
|
|
||||||
|
- @requires_subprocess()
|
||||||
|
- def test_config_vars_depend_on_site_initialization(self):
|
||||||
|
- script = textwrap.dedent("""
|
||||||
|
- import sysconfig
|
||||||
|
-
|
||||||
|
- config_vars = sysconfig.get_config_vars()
|
||||||
|
-
|
||||||
|
- import json
|
||||||
|
- print(json.dumps(config_vars, indent=2))
|
||||||
|
- """)
|
||||||
|
-
|
||||||
|
- with self.venv() as venv:
|
||||||
|
- site_config_vars = json.loads(venv.run('-c', script).stdout)
|
||||||
|
- no_site_config_vars = json.loads(venv.run('-S', '-c', script).stdout)
|
||||||
|
-
|
||||||
|
- self.assertNotEqual(site_config_vars, no_site_config_vars)
|
||||||
|
- # With the site initialization, the virtual environment should be enabled.
|
||||||
|
- self.assertEqual(site_config_vars['base'], venv.prefix)
|
||||||
|
- self.assertEqual(site_config_vars['platbase'], venv.prefix)
|
||||||
|
- #self.assertEqual(site_config_vars['prefix'], venv.prefix) # # FIXME: prefix gets overwriten by _init_posix
|
||||||
|
- # Without the site initialization, the virtual environment should be disabled.
|
||||||
|
- self.assertEqual(no_site_config_vars['base'], site_config_vars['installed_base'])
|
||||||
|
- self.assertEqual(no_site_config_vars['platbase'], site_config_vars['installed_platbase'])
|
||||||
|
-
|
||||||
|
- @requires_subprocess()
|
||||||
|
- def test_config_vars_recalculation_after_site_initialization(self):
|
||||||
|
- script = textwrap.dedent("""
|
||||||
|
- import sysconfig
|
||||||
|
-
|
||||||
|
- before = sysconfig.get_config_vars()
|
||||||
|
-
|
||||||
|
- import site
|
||||||
|
- site.main()
|
||||||
|
-
|
||||||
|
- after = sysconfig.get_config_vars()
|
||||||
|
-
|
||||||
|
- import json
|
||||||
|
- print(json.dumps({'before': before, 'after': after}, indent=2))
|
||||||
|
- """)
|
||||||
|
-
|
||||||
|
- with self.venv() as venv:
|
||||||
|
- config_vars = json.loads(venv.run('-S', '-c', script).stdout)
|
||||||
|
-
|
||||||
|
- self.assertNotEqual(config_vars['before'], config_vars['after'])
|
||||||
|
- self.assertEqual(config_vars['after']['base'], venv.prefix)
|
||||||
|
- #self.assertEqual(config_vars['after']['prefix'], venv.prefix) # FIXME: prefix gets overwriten by _init_posix
|
||||||
|
- #self.assertEqual(config_vars['after']['exec_prefix'], venv.prefix) # FIXME: exec_prefix gets overwriten by _init_posix
|
||||||
|
-
|
||||||
|
- @requires_subprocess()
|
||||||
|
- def test_paths_depend_on_site_initialization(self):
|
||||||
|
- script = textwrap.dedent("""
|
||||||
|
- import sysconfig
|
||||||
|
-
|
||||||
|
- paths = sysconfig.get_paths()
|
||||||
|
-
|
||||||
|
- import json
|
||||||
|
- print(json.dumps(paths, indent=2))
|
||||||
|
- """)
|
||||||
|
-
|
||||||
|
- with self.venv() as venv:
|
||||||
|
- site_paths = json.loads(venv.run('-c', script).stdout)
|
||||||
|
- no_site_paths = json.loads(venv.run('-S', '-c', script).stdout)
|
||||||
|
-
|
||||||
|
- self.assertNotEqual(site_paths, no_site_paths)
|
||||||
|
-
|
||||||
|
-
|
||||||
|
class MakefileTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.platform.startswith('win'),
|
||||||
30
gh138131-exclude-pycache-from-digest.patch
Normal file
30
gh138131-exclude-pycache-from-digest.patch
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
From 4bb41b28d5bac09bccd636d8c5fefe1a462f63a7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alm <alon.menczer@gmail.com>
|
||||||
|
Date: Mon, 25 Aug 2025 08:56:38 +0300
|
||||||
|
Subject: [PATCH 1/4] Exclude .pyc files from the computed digest in the jit
|
||||||
|
stencils
|
||||||
|
|
||||||
|
---
|
||||||
|
Tools/jit/_targets.py | 3 +++
|
||||||
|
1 file changed, 3 insertions(+)
|
||||||
|
|
||||||
|
Index: Python-3.13.7/Tools/jit/_targets.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.7.orig/Tools/jit/_targets.py
|
||||||
|
+++ Python-3.13.7/Tools/jit/_targets.py
|
||||||
|
@@ -53,6 +53,9 @@ class _Target(typing.Generic[_S, _R]):
|
||||||
|
hasher.update(PYTHON_EXECUTOR_CASES_C_H.read_bytes())
|
||||||
|
hasher.update((out / "pyconfig.h").read_bytes())
|
||||||
|
for dirpath, _, filenames in sorted(os.walk(TOOLS_JIT)):
|
||||||
|
+ # Exclude cache files from digest computation to ensure reproducible builds.
|
||||||
|
+ if dirpath.endswith("__pycache__"):
|
||||||
|
+ continue
|
||||||
|
for filename in filenames:
|
||||||
|
hasher.update(pathlib.Path(dirpath, filename).read_bytes())
|
||||||
|
return hasher.hexdigest()
|
||||||
|
Index: Python-3.13.7/Misc/NEWS.d/next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ Python-3.13.7/Misc/NEWS.d/next/Build/2025-08-27-09-52-45.gh-issue-138061.fMVS9w.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Ensure reproducible builds by making JIT stencil header generation deterministic.
|
||||||
36
gh139257-Support-docutils-0.22.patch
Normal file
36
gh139257-Support-docutils-0.22.patch
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
From 19b61747df3d62c822285c488753d6fbdf91e3ac Mon Sep 17 00:00:00 2001
|
||||||
|
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
|
||||||
|
Date: Tue, 23 Sep 2025 10:20:16 +0200
|
||||||
|
Subject: [PATCH 1/2] gh-139257: Support docutils >= 0.22
|
||||||
|
|
||||||
|
---
|
||||||
|
Doc/tools/extensions/pyspecific.py | 12 +++++++++++-
|
||||||
|
1 file changed, 11 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
Index: Python-3.13.7/Doc/tools/extensions/pyspecific.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.7.orig/Doc/tools/extensions/pyspecific.py
|
||||||
|
+++ Python-3.13.7/Doc/tools/extensions/pyspecific.py
|
||||||
|
@@ -25,11 +25,21 @@ from sphinx.util.docutils import SphinxD
|
||||||
|
SOURCE_URI = 'https://github.com/python/cpython/tree/3.13/%s'
|
||||||
|
|
||||||
|
# monkey-patch reST parser to disable alphabetic and roman enumerated lists
|
||||||
|
+def _disable_alphabetic_and_roman(text):
|
||||||
|
+ try:
|
||||||
|
+ # docutils >= 0.22
|
||||||
|
+ from docutils.parsers.rst.states import InvalidRomanNumeralError
|
||||||
|
+ raise InvalidRomanNumeralError(text)
|
||||||
|
+ except ImportError:
|
||||||
|
+ # docutils < 0.22
|
||||||
|
+ return None
|
||||||
|
+
|
||||||
|
+
|
||||||
|
from docutils.parsers.rst.states import Body
|
||||||
|
Body.enum.converters['loweralpha'] = \
|
||||||
|
Body.enum.converters['upperalpha'] = \
|
||||||
|
Body.enum.converters['lowerroman'] = \
|
||||||
|
- Body.enum.converters['upperroman'] = lambda x: None
|
||||||
|
+ Body.enum.converters['upperroman'] = _disable_alphabetic_and_roman
|
||||||
|
|
||||||
|
|
||||||
|
class PyAwaitableMixin(object):
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<!-- Copyright 2017 Zbigniew Jędrzejewski-Szmek -->
|
<component type="desktop-application">
|
||||||
<application>
|
<id>org.python.IDLE3</id>
|
||||||
<id type="desktop">idle3.desktop</id>
|
<launchable type="desktop-id">idle3.desktop</launchable>
|
||||||
|
|
||||||
<name>IDLE3</name>
|
<name>IDLE3</name>
|
||||||
<metadata_licence>CC0</metadata_licence>
|
|
||||||
<project_license>Python-2.0</project_license>
|
|
||||||
<summary>Python 3 Integrated Development and Learning Environment</summary>
|
<summary>Python 3 Integrated Development and Learning Environment</summary>
|
||||||
|
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
IDLE is Python’s Integrated Development and Learning Environment.
|
IDLE is Python’s Integrated Development and Learning Environment.
|
||||||
The GUI is uniform between Windows, Unix, and Mac OS X.
|
The GUI is uniform between Windows, Unix, and macOS.
|
||||||
IDLE provides an easy way to start writing, running, and debugging
|
IDLE provides an easy way to start writing, running, and debugging
|
||||||
Python code.
|
Python code.
|
||||||
</p>
|
</p>
|
||||||
@@ -19,17 +19,33 @@
|
|||||||
It provides:
|
It provides:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,</li>
|
<li>a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,</li>
|
||||||
<li>a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,</li>
|
<li>a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,</li>
|
||||||
<li>search within any window, replace within editor windows, and search through multiple files (grep),</li>
|
<li>search within any window, replace within editor windows, and search through multiple files (grep),</li>
|
||||||
<li>a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.</li>
|
<li>a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
|
<developer id="org.python">
|
||||||
|
<name>Python Software Foundation</name>
|
||||||
|
</developer>
|
||||||
|
|
||||||
<url type="homepage">https://docs.python.org/3/library/idle.html</url>
|
<url type="homepage">https://docs.python.org/3/library/idle.html</url>
|
||||||
|
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</screenshot>
|
<screenshot type="default">
|
||||||
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</screenshot>
|
<image>https://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</image>
|
||||||
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</screenshot>
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<image>https://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</image>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<image>https://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</image>
|
||||||
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
|
<project_license>Python-2.0</project_license>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
<update_contact>zbyszek@in.waw.pl</update_contact>
|
<update_contact>zbyszek@in.waw.pl</update_contact>
|
||||||
</application>
|
</component>
|
||||||
|
|
||||||
|
|||||||
@@ -1,647 +0,0 @@
|
|||||||
only in patch2:
|
|
||||||
unchanged:
|
|
||||||
---
|
|
||||||
Doc/library/turtle.rst | 81 -------------------------------------------------
|
|
||||||
1 file changed, 81 deletions(-)
|
|
||||||
|
|
||||||
Index: Python-3.13.0a3/Doc/library/turtle.rst
|
|
||||||
===================================================================
|
|
||||||
--- Python-3.13.0a3.orig/Doc/library/turtle.rst
|
|
||||||
+++ Python-3.13.0a3/Doc/library/turtle.rst
|
|
||||||
@@ -449,7 +449,6 @@ Turtle motion
|
|
||||||
turtle is headed.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.position()
|
|
||||||
(0.00,0.00)
|
|
||||||
@@ -476,7 +475,6 @@ Turtle motion
|
|
||||||
>>> turtle.goto(0, 0)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.position()
|
|
||||||
(0.00,0.00)
|
|
||||||
@@ -495,13 +493,11 @@ Turtle motion
|
|
||||||
orientation depends on the turtle mode, see :func:`mode`.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.setheading(22)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.heading()
|
|
||||||
22.0
|
|
||||||
@@ -520,13 +516,11 @@ Turtle motion
|
|
||||||
orientation depends on the turtle mode, see :func:`mode`.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.setheading(22)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.heading()
|
|
||||||
22.0
|
|
||||||
@@ -549,13 +543,11 @@ Turtle motion
|
|
||||||
not change the turtle's orientation.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.goto(0, 0)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> tp = turtle.pos()
|
|
||||||
>>> tp
|
|
||||||
@@ -617,13 +609,11 @@ Turtle motion
|
|
||||||
unchanged.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.goto(0, 240)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.position()
|
|
||||||
(0.00,240.00)
|
|
||||||
@@ -639,13 +629,11 @@ Turtle motion
|
|
||||||
Set the turtle's second coordinate to *y*, leave first coordinate unchanged.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.goto(0, 40)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.position()
|
|
||||||
(0.00,40.00)
|
|
||||||
@@ -672,7 +660,6 @@ Turtle motion
|
|
||||||
=================== ====================
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.setheading(90)
|
|
||||||
>>> turtle.heading()
|
|
||||||
@@ -685,14 +672,12 @@ Turtle motion
|
|
||||||
its start-orientation (which depends on the mode, see :func:`mode`).
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.setheading(90)
|
|
||||||
>>> turtle.goto(0, -10)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.heading()
|
|
||||||
90.0
|
|
||||||
@@ -724,7 +709,6 @@ Turtle motion
|
|
||||||
calculated automatically. May be used to draw regular polygons.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.position()
|
|
||||||
@@ -753,7 +737,6 @@ Turtle motion
|
|
||||||
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.dot()
|
|
||||||
@@ -771,7 +754,6 @@ Turtle motion
|
|
||||||
it by calling ``clearstamp(stamp_id)``.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.color("blue")
|
|
||||||
>>> stamp_id = turtle.stamp()
|
|
||||||
@@ -786,7 +768,6 @@ Turtle motion
|
|
||||||
Delete stamp with given *stampid*.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.position()
|
|
||||||
(150.00,-0.00)
|
|
||||||
@@ -824,7 +805,6 @@ Turtle motion
|
|
||||||
undo actions is determined by the size of the undobuffer.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> for i in range(4):
|
|
||||||
... turtle.fd(50); turtle.lt(80)
|
|
||||||
@@ -857,7 +837,6 @@ Turtle motion
|
|
||||||
turtle turn instantly.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.speed()
|
|
||||||
3
|
|
||||||
@@ -878,7 +857,6 @@ Tell Turtle's state
|
|
||||||
Return the turtle's current location (x,y) (as a :class:`Vec2D` vector).
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.pos()
|
|
||||||
(440.00,-0.00)
|
|
||||||
@@ -894,7 +872,6 @@ Tell Turtle's state
|
|
||||||
orientation which depends on the mode - "standard"/"world" or "logo".
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.goto(10, 10)
|
|
||||||
>>> turtle.towards(0,0)
|
|
||||||
@@ -906,7 +883,6 @@ Tell Turtle's state
|
|
||||||
Return the turtle's x coordinate.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.left(50)
|
|
||||||
@@ -922,7 +898,6 @@ Tell Turtle's state
|
|
||||||
Return the turtle's y coordinate.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.left(60)
|
|
||||||
@@ -939,7 +914,6 @@ Tell Turtle's state
|
|
||||||
:func:`mode`).
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.left(67)
|
|
||||||
@@ -956,7 +930,6 @@ Tell Turtle's state
|
|
||||||
other turtle, in turtle step units.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.distance(30,40)
|
|
||||||
@@ -980,7 +953,6 @@ Settings for measurement
|
|
||||||
Default value is 360 degrees.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.left(90)
|
|
||||||
@@ -1003,7 +975,6 @@ Settings for measurement
|
|
||||||
``degrees(2*math.pi)``.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.left(90)
|
|
||||||
@@ -1014,7 +985,6 @@ Settings for measurement
|
|
||||||
1.5707963267948966
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.degrees(360)
|
|
||||||
@@ -1050,7 +1020,6 @@ Drawing state
|
|
||||||
thickness. If no argument is given, the current pensize is returned.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.pensize()
|
|
||||||
1
|
|
||||||
@@ -1082,7 +1051,6 @@ Drawing state
|
|
||||||
attributes in one statement.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:options: +NORMALIZE_WHITESPACE
|
|
||||||
|
|
||||||
>>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
|
|
||||||
@@ -1105,7 +1073,6 @@ Drawing state
|
|
||||||
Return ``True`` if pen is down, ``False`` if it's up.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.penup()
|
|
||||||
>>> turtle.isdown()
|
|
||||||
@@ -1146,7 +1113,6 @@ Color control
|
|
||||||
newly set pencolor.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> colormode()
|
|
||||||
1.0
|
|
||||||
@@ -1195,7 +1161,6 @@ Color control
|
|
||||||
with the newly set fillcolor.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.fillcolor("violet")
|
|
||||||
>>> turtle.fillcolor()
|
|
||||||
@@ -1234,7 +1199,6 @@ Color control
|
|
||||||
with the newly set colors.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.color("red", "green")
|
|
||||||
>>> turtle.color()
|
|
||||||
@@ -1251,7 +1215,6 @@ Filling
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
@@ -1261,7 +1224,6 @@ Filling
|
|
||||||
Return fillstate (``True`` if filling, ``False`` else).
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.begin_fill()
|
|
||||||
>>> if turtle.filling():
|
|
||||||
@@ -1286,7 +1248,6 @@ Filling
|
|
||||||
above may be either all yellow or have some white regions.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.color("black", "red")
|
|
||||||
>>> turtle.begin_fill()
|
|
||||||
@@ -1303,7 +1264,6 @@ More drawing control
|
|
||||||
variables to the default values.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.goto(0,-22)
|
|
||||||
>>> turtle.left(100)
|
|
||||||
@@ -1354,7 +1314,6 @@ Visibility
|
|
||||||
drawing observably.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.hideturtle()
|
|
||||||
|
|
||||||
@@ -1365,7 +1324,6 @@ Visibility
|
|
||||||
Make the turtle visible.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.showturtle()
|
|
||||||
|
|
||||||
@@ -1396,7 +1354,6 @@ Appearance
|
|
||||||
deal with shapes see Screen method :func:`register_shape`.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.shape()
|
|
||||||
'classic'
|
|
||||||
@@ -1422,7 +1379,6 @@ Appearance
|
|
||||||
``resizemode("user")`` is called by :func:`shapesize` when used with arguments.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.resizemode()
|
|
||||||
'noresize'
|
|
||||||
@@ -1446,7 +1402,6 @@ Appearance
|
|
||||||
of the shape's outline.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.shapesize()
|
|
||||||
(1.0, 1.0, 1)
|
|
||||||
@@ -1471,7 +1426,6 @@ Appearance
|
|
||||||
heading of the turtle are sheared.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.shape("circle")
|
|
||||||
>>> turtle.shapesize(5,2)
|
|
||||||
@@ -1488,7 +1442,6 @@ Appearance
|
|
||||||
change the turtle's heading (direction of movement).
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.reset()
|
|
||||||
>>> turtle.shape("circle")
|
|
||||||
@@ -1512,7 +1465,6 @@ Appearance
|
|
||||||
turtle (its direction of movement).
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.reset()
|
|
||||||
>>> turtle.shape("circle")
|
|
||||||
@@ -1541,7 +1493,6 @@ Appearance
|
|
||||||
given matrix.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle = Turtle()
|
|
||||||
>>> turtle.shape("square")
|
|
||||||
@@ -1557,7 +1508,6 @@ Appearance
|
|
||||||
can be used to define a new shape or components of a compound shape.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.shape("square")
|
|
||||||
>>> turtle.shapetransform(4, -1, 0, 2)
|
|
||||||
@@ -1582,7 +1532,6 @@ Using events
|
|
||||||
procedural way:
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> def turn(x, y):
|
|
||||||
... left(180)
|
|
||||||
@@ -1603,7 +1552,6 @@ Using events
|
|
||||||
``None``, existing bindings are removed.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> class MyTurtle(Turtle):
|
|
||||||
... def glow(self,x,y):
|
|
||||||
@@ -1631,7 +1579,6 @@ Using events
|
|
||||||
mouse-click event on that turtle.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.ondrag(turtle.goto)
|
|
||||||
|
|
||||||
@@ -1659,7 +1606,6 @@ Special Turtle methods
|
|
||||||
Return the last recorded polygon.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.home()
|
|
||||||
>>> turtle.begin_poly()
|
|
||||||
@@ -1679,7 +1625,6 @@ Special Turtle methods
|
|
||||||
turtle properties.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> mick = Turtle()
|
|
||||||
>>> joe = mick.clone()
|
|
||||||
@@ -1692,7 +1637,6 @@ Special Turtle methods
|
|
||||||
return the "anonymous turtle":
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> pet = getturtle()
|
|
||||||
>>> pet.fd(50)
|
|
||||||
@@ -1706,7 +1650,6 @@ Special Turtle methods
|
|
||||||
TurtleScreen methods can then be called for that object.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> ts = turtle.getscreen()
|
|
||||||
>>> ts
|
|
||||||
@@ -1724,7 +1667,6 @@ Special Turtle methods
|
|
||||||
``None``, the undobuffer is disabled.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> turtle.setundobuffer(42)
|
|
||||||
|
|
||||||
@@ -1734,7 +1676,6 @@ Special Turtle methods
|
|
||||||
Return number of entries in the undobuffer.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> while undobufferentries():
|
|
||||||
... undo()
|
|
||||||
@@ -1757,7 +1698,6 @@ below:
|
|
||||||
For example:
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> s = Shape("compound")
|
|
||||||
>>> poly1 = ((0,0),(10,-5),(0,10),(-10,-5))
|
|
||||||
@@ -1768,7 +1708,6 @@ below:
|
|
||||||
3. Now add the Shape to the Screen's shapelist and use it:
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> register_shape("myshape", s)
|
|
||||||
>>> shape("myshape")
|
|
||||||
@@ -1788,7 +1727,6 @@ Most of the examples in this section ref
|
|
||||||
``screen``.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> screen = Screen()
|
|
||||||
@@ -1805,7 +1743,6 @@ Window control
|
|
||||||
Set or return background color of the TurtleScreen.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.bgcolor("orange")
|
|
||||||
>>> screen.bgcolor()
|
|
||||||
@@ -1897,7 +1834,6 @@ Window control
|
|
||||||
distorted.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.reset()
|
|
||||||
>>> screen.setworldcoordinates(-50,-7.5,50,7.5)
|
|
||||||
@@ -1908,7 +1844,6 @@ Window control
|
|
||||||
... left(45); fd(2) # a regular octagon
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> screen.reset()
|
|
||||||
@@ -1930,7 +1865,6 @@ Animation control
|
|
||||||
Optional argument:
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.delay()
|
|
||||||
10
|
|
||||||
@@ -1952,7 +1886,6 @@ Animation control
|
|
||||||
:func:`delay`).
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.tracer(8, 25)
|
|
||||||
>>> dist = 2
|
|
||||||
@@ -1989,7 +1922,6 @@ Using screen events
|
|
||||||
must have the focus. (See method :func:`listen`.)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> def f():
|
|
||||||
... fd(50)
|
|
||||||
@@ -2010,7 +1942,6 @@ Using screen events
|
|
||||||
must have focus. (See method :func:`listen`.)
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> def f():
|
|
||||||
... fd(50)
|
|
||||||
@@ -2035,7 +1966,6 @@ Using screen events
|
|
||||||
named ``turtle``:
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.onclick(turtle.goto) # Subsequently clicking into the TurtleScreen will
|
|
||||||
>>> # make the turtle move to the clicked point.
|
|
||||||
@@ -2055,7 +1985,6 @@ Using screen events
|
|
||||||
Install a timer that calls *fun* after *t* milliseconds.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> running = True
|
|
||||||
>>> def f():
|
|
||||||
@@ -2137,7 +2066,6 @@ Settings and special methods
|
|
||||||
============ ========================= ===================
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> mode("logo") # resets turtle heading to north
|
|
||||||
>>> mode()
|
|
||||||
@@ -2152,7 +2080,6 @@ Settings and special methods
|
|
||||||
values of color triples have to be in the range 0..*cmode*.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.colormode(1)
|
|
||||||
>>> turtle.pencolor(240, 160, 80)
|
|
||||||
@@ -2173,7 +2100,6 @@ Settings and special methods
|
|
||||||
do with a Tkinter Canvas.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> cv = screen.getcanvas()
|
|
||||||
>>> cv
|
|
||||||
@@ -2185,7 +2111,6 @@ Settings and special methods
|
|
||||||
Return a list of names of all currently available turtle shapes.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.getshapes()
|
|
||||||
['arrow', 'blank', 'circle', ..., 'turtle']
|
|
||||||
@@ -2209,7 +2134,6 @@ Settings and special methods
|
|
||||||
coordinates: Install the corresponding polygon shape.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.register_shape("triangle", ((5,-3), (0,5), (-5,-3)))
|
|
||||||
|
|
||||||
@@ -2225,7 +2149,6 @@ Settings and special methods
|
|
||||||
Return the list of turtles on the screen.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> for turtle in screen.turtles():
|
|
||||||
... turtle.color("red")
|
|
||||||
@@ -2287,7 +2210,6 @@ Methods specific to Screen, not inherite
|
|
||||||
center window vertically
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.setup (width=200, height=200, startx=0, starty=0)
|
|
||||||
>>> # sets window to 200x200 pixels, in upper left of screen
|
|
||||||
@@ -2303,7 +2225,6 @@ Methods specific to Screen, not inherite
|
|
||||||
Set title of turtle window to *titlestring*.
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> screen.title("Welcome to the turtle zoo!")
|
|
||||||
|
|
||||||
@@ -2374,7 +2295,6 @@ Public classes
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
|
|
||||||
>>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
|
|
||||||
>>> s = Shape("compound")
|
|
||||||
@@ -2759,7 +2679,6 @@ Changes since Python 3.0
|
|
||||||
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
- :skipif: _tkinter is None
|
|
||||||
:hide:
|
|
||||||
|
|
||||||
>>> for turtle in turtles():
|
|
||||||
45
pass-test_write_read_limited_history.patch
Normal file
45
pass-test_write_read_limited_history.patch
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
Modules/readline.c | 23 +++++++++++++++++++++++
|
||||||
|
1 file changed, 23 insertions(+)
|
||||||
|
|
||||||
|
Index: Python-3.13.9/Modules/readline.c
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.13.9.orig/Modules/readline.c 2025-10-14 15:52:31.000000000 +0200
|
||||||
|
+++ Python-3.13.9/Modules/readline.c 2025-11-20 00:46:45.594286346 +0100
|
||||||
|
@@ -175,6 +175,8 @@
|
||||||
|
return PyUnicode_DecodeLocale(s, "surrogateescape");
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int _py_get_history_length(void);
|
||||||
|
+static void _py_free_history_entry(HIST_ENTRY *entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Explicitly disable bracketed paste in the interactive interpreter, even if it's
|
||||||
|
@@ -399,6 +401,27 @@
|
||||||
|
/*[clinic end generated code: output=e161a53e45987dc7 input=b8901bf16488b760]*/
|
||||||
|
{
|
||||||
|
_history_length = length;
|
||||||
|
+
|
||||||
|
+ if (length < 0) {
|
||||||
|
+ stifle_history(-1);
|
||||||
|
+ }
|
||||||
|
+ else {
|
||||||
|
+ int current_length = _py_get_history_length();
|
||||||
|
+ if (length < current_length) {
|
||||||
|
+#if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0500
|
||||||
|
+ HISTORY_STATE *state = history_get_history_state();
|
||||||
|
+ if (state) {
|
||||||
|
+ int i;
|
||||||
|
+ for (i = 0; i < current_length - length; i++) {
|
||||||
|
+ _py_free_history_entry(remove_history(0));
|
||||||
|
+ }
|
||||||
|
+ state->length = length;
|
||||||
|
+ free(state);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ }
|
||||||
|
+ stifle_history(length);
|
||||||
|
+ }
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
--- a/Makefile.pre.in
|
--- a/Makefile.pre.in
|
||||||
+++ b/Makefile.pre.in
|
+++ b/Makefile.pre.in
|
||||||
@@ -1664,11 +1664,18 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
|
@@ -1684,11 +1684,18 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
|
||||||
$(DTRACE_OBJS) \
|
$(DTRACE_OBJS) \
|
||||||
$(srcdir)/Modules/getbuildinfo.c
|
$(srcdir)/Modules/getbuildinfo.c
|
||||||
$(CC) -c $(PY_CORE_CFLAGS) \
|
$(CC) -c $(PY_CORE_CFLAGS) \
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
Index: Python-3.13.0a3/Lib/site.py
|
---
|
||||||
===================================================================
|
Lib/site.py | 2 +-
|
||||||
--- Python-3.13.0a3.orig/Lib/site.py
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
+++ Python-3.13.0a3/Lib/site.py
|
|
||||||
@@ -77,7 +77,7 @@ import io
|
--- a/Lib/site.py
|
||||||
import stat
|
+++ b/Lib/site.py
|
||||||
|
@@ -78,7 +78,7 @@ import stat
|
||||||
|
import errno
|
||||||
|
|
||||||
# Prefixes for site-packages; add additional prefixes like /usr/local here
|
# Prefixes for site-packages; add additional prefixes like /usr/local here
|
||||||
-PREFIXES = [sys.prefix, sys.exec_prefix]
|
-PREFIXES = [sys.prefix, sys.exec_prefix]
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
Lib/test/test_posix.py | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
Index: Python-3.13.0a3/Lib/test/test_posix.py
|
|
||||||
===================================================================
|
|
||||||
--- Python-3.13.0a3.orig/Lib/test/test_posix.py
|
|
||||||
+++ Python-3.13.0a3/Lib/test/test_posix.py
|
|
||||||
@@ -433,7 +433,7 @@ class PosixTester(unittest.TestCase):
|
|
||||||
def test_posix_fadvise(self):
|
|
||||||
fd = os.open(os_helper.TESTFN, os.O_RDONLY)
|
|
||||||
try:
|
|
||||||
- posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED)
|
|
||||||
+ posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_RANDOM)
|
|
||||||
finally:
|
|
||||||
os.close(fd)
|
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
addFilter("pem-certificate.*/usr/lib.*/python.*/test/*.pem")
|
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.*/tests/*.c")
|
||||||
|
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/*.c")
|
||||||
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/*.cpp")
|
addFilter("devel-file-in-non-devel-package.*/usr/lib.*/python.*/test/*.cpp")
|
||||||
|
|||||||
2646
python313.changes
2646
python313.changes
File diff suppressed because it is too large
Load Diff
281
python313.spec
281
python313.spec
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# spec file for package python313
|
# spec file for package python313
|
||||||
#
|
#
|
||||||
# Copyright (c) 2024 SUSE LLC
|
# Copyright (c) 2025 SUSE LLC and contributors
|
||||||
#
|
#
|
||||||
# All modifications and additions to the file contributed by third parties
|
# All modifications and additions to the file contributed by third parties
|
||||||
# remain the property of their copyright owners, unless otherwise agreed
|
# remain the property of their copyright owners, unless otherwise agreed
|
||||||
@@ -22,35 +22,83 @@
|
|||||||
%bcond_without doc
|
%bcond_without doc
|
||||||
%bcond_with base
|
%bcond_with base
|
||||||
%bcond_with general
|
%bcond_with general
|
||||||
|
%bcond_without GIL
|
||||||
%endif
|
%endif
|
||||||
%if "%{flavor}" == "base"
|
%if "%{flavor}" == "base"
|
||||||
%define psuffix -core
|
%define psuffix -core
|
||||||
%bcond_with doc
|
%bcond_with doc
|
||||||
%bcond_without base
|
%bcond_without base
|
||||||
%bcond_with general
|
%bcond_with general
|
||||||
|
%bcond_without GIL
|
||||||
%endif
|
%endif
|
||||||
%if "%{flavor}" == ""
|
%if "%{flavor}" == ""
|
||||||
%define psuffix %{nil}
|
%define psuffix %{nil}
|
||||||
%bcond_with doc
|
%bcond_with doc
|
||||||
%bcond_with base
|
%bcond_with base
|
||||||
%bcond_without general
|
%bcond_without general
|
||||||
|
%bcond_without GIL
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
# Currently supported architectures
|
%if "%{flavor}" == "nogil"
|
||||||
# https://peps.python.org/pep-0744/#support
|
%define psuffix %{nil}
|
||||||
%ifarch %{x86_64} aarch64
|
%bcond_with doc
|
||||||
%bcond_without experimental_jit
|
%bcond_with base
|
||||||
|
%bcond_without general
|
||||||
|
%bcond_with GIL
|
||||||
|
%endif
|
||||||
|
%if "%{flavor}" == "nogil-base"
|
||||||
|
%define psuffix -nogil-core
|
||||||
|
%bcond_with doc
|
||||||
|
%bcond_without base
|
||||||
|
%bcond_with general
|
||||||
|
%bcond_with GIL
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?do_profiling} && !0%{?want_reproducible_builds}
|
||||||
|
%bcond_without profileopt
|
||||||
%else
|
%else
|
||||||
%bcond_with experimental_jit
|
%bcond_with profileopt
|
||||||
|
%endif
|
||||||
|
|
||||||
|
# Only for Tumbleweed
|
||||||
|
# https://en.opensuse.org/openSUSE:Python:Externally_managed
|
||||||
|
%if 0%{?suse_version} > 1600
|
||||||
|
%bcond_without externally_managed
|
||||||
|
%else
|
||||||
|
%bcond_with externally_managed
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%define python_pkg_name python313
|
%define python_pkg_name python313
|
||||||
|
%if %{without GIL}
|
||||||
|
%define python_pkg_name python313-nogil
|
||||||
|
%define base_pkg_name python313
|
||||||
|
%endif
|
||||||
|
|
||||||
%if "%{python_pkg_name}" == "%{primary_python}"
|
%if "%{python_pkg_name}" == "%{primary_python}"
|
||||||
%define primary_interpreter 1
|
%define primary_interpreter 1
|
||||||
%else
|
%else
|
||||||
%define primary_interpreter 0
|
%define primary_interpreter 0
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
# No experimental_jit in SLES, there's no clang >=18
|
||||||
|
%if 0%{?suse_version} <= 1600
|
||||||
|
%bcond_with experimental_jit
|
||||||
|
%else
|
||||||
|
# Disable experimental_jit for primary python.
|
||||||
|
# llvm is not part of ring0 and experimental_jit requires clang >= 18
|
||||||
|
%if !%{primary_interpreter}
|
||||||
|
# Currently supported architectures
|
||||||
|
# https://peps.python.org/pep-0744/#support
|
||||||
|
%ifarch x86_64 %{x86_64} aarch64
|
||||||
|
%bcond_without experimental_jit
|
||||||
|
%else
|
||||||
|
%bcond_with experimental_jit
|
||||||
|
%endif
|
||||||
|
%else
|
||||||
|
%bcond_with experimental_jit
|
||||||
|
%endif
|
||||||
|
%endif
|
||||||
|
|
||||||
# %%if 0%%{?sle_version} && 0%%{?suse_version} < 1550
|
# %%if 0%%{?sle_version} && 0%%{?suse_version} < 1550
|
||||||
# Obsoleting previous "latest" Python versions
|
# Obsoleting previous "latest" Python versions
|
||||||
# Next versions will get more lines like for older versions
|
# Next versions will get more lines like for older versions
|
||||||
@@ -77,10 +125,14 @@
|
|||||||
# %%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.13.0
|
%define folderversion %{version}
|
||||||
%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}
|
||||||
|
%if %{without GIL}
|
||||||
|
%define abi_kind t
|
||||||
|
%define sitedir %{_libdir}/python%{python_version}%{abi_kind}
|
||||||
|
%endif
|
||||||
# python ABI version - used in some file names
|
# python ABI version - used in some file names
|
||||||
%define python_abi %{python_version}%{abi_kind}
|
%define python_abi %{python_version}%{abi_kind}
|
||||||
# soname ABI tag defined in PEP 3149
|
# soname ABI tag defined in PEP 3149
|
||||||
@@ -88,7 +140,11 @@
|
|||||||
# version part of "libpython" package
|
# version part of "libpython" package
|
||||||
%define so_major 1
|
%define so_major 1
|
||||||
%define so_minor 0
|
%define so_minor 0
|
||||||
|
%if "%{abi_kind}" == "t"
|
||||||
|
%define so_version %{python_version_soname}t%{so_major}_%{so_minor}
|
||||||
|
%else
|
||||||
%define so_version %{python_version_soname}%{abi_kind}-%{so_major}_%{so_minor}
|
%define so_version %{python_version_soname}%{abi_kind}-%{so_major}_%{so_minor}
|
||||||
|
%endif
|
||||||
# rpm and python have different ideas about what is an arch-dependent name, so:
|
# rpm and python have different ideas about what is an arch-dependent name, so:
|
||||||
%if "%{__isa_name}" == "ppc"
|
%if "%{__isa_name}" == "ppc"
|
||||||
%define archname %(echo %{_arch} | sed s/ppc/powerpc/)
|
%define archname %(echo %{_arch} | sed s/ppc/powerpc/)
|
||||||
@@ -110,17 +166,16 @@
|
|||||||
# pyexpat.cpython-35m-armv7-linux-gnueabihf
|
# pyexpat.cpython-35m-armv7-linux-gnueabihf
|
||||||
# _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
|
||||||
%bcond_without profileopt
|
|
||||||
Name: %{python_pkg_name}%{psuffix}
|
Name: %{python_pkg_name}%{psuffix}
|
||||||
Version: 3.13.0~b3
|
Version: 3.13.9
|
||||||
%define tarversion 3.13.0b3
|
%define tarversion %{version}
|
||||||
%define tarname Python-%{tarversion}
|
%define tarname Python-%{tarversion}
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: Python 3 Interpreter
|
Summary: Python 3 Interpreter
|
||||||
License: Python-2.0
|
License: Python-2.0
|
||||||
URL: https://www.python.org/
|
URL: https://www.python.org/
|
||||||
Source0: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz
|
Source0: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz
|
||||||
Source1: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.asc
|
Source1: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.sigstore
|
||||||
Source2: baselibs.conf
|
Source2: baselibs.conf
|
||||||
Source3: README.SUSE
|
Source3: README.SUSE
|
||||||
Source4: externally_managed.in
|
Source4: externally_managed.in
|
||||||
@@ -156,22 +211,39 @@ Patch02: F00251-change-user-install-location.patch
|
|||||||
Patch03: python-3.3.0b1-localpath.patch
|
Patch03: python-3.3.0b1-localpath.patch
|
||||||
# replace DATE, TIME and COMPILER by fixed definitions to aid reproducible builds
|
# replace DATE, TIME and COMPILER by fixed definitions to aid reproducible builds
|
||||||
Patch04: python-3.3.0b1-fix_date_time_compiler.patch
|
Patch04: python-3.3.0b1-fix_date_time_compiler.patch
|
||||||
# POSIX_FADV_WILLNEED throws EINVAL. Use a different constant in test
|
|
||||||
Patch05: python-3.3.0b1-test-posix_fadvise.patch
|
|
||||||
# Raise timeout value for test_subprocess
|
# Raise timeout value for test_subprocess
|
||||||
Patch06: subprocess-raise-timeout.patch
|
Patch06: subprocess-raise-timeout.patch
|
||||||
# PATCH-FEATURE-UPSTREAM bpo-31046_ensurepip_honours_prefix.patch bpo#31046 mcepl@suse.com
|
# PATCH-FEATURE-UPSTREAM bpo-31046_ensurepip_honours_prefix.patch bpo#31046 mcepl@suse.com
|
||||||
# ensurepip should honour the value of $(prefix)
|
# ensurepip should honour the value of $(prefix)
|
||||||
Patch07: bpo-31046_ensurepip_honours_prefix.patch
|
Patch07: bpo-31046_ensurepip_honours_prefix.patch
|
||||||
# PATCH-FIX-SLE no-skipif-doctests.patch jsc#SLE-13738 mcepl@suse.com
|
|
||||||
# SLE-15 version of Sphinx doesn't know about skipif directive in doctests.
|
|
||||||
Patch08: no-skipif-doctests.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-SLE fix_configure_rst.patch bpo#43774 mcepl@suse.com
|
# PATCH-FIX-OPENSUSE fix-test-recursion-limit-15.6.patch gh#python/cpython#115083
|
||||||
# remove duplicate link targets and make documentation with old Sphinx in SLE
|
# Skip some failing tests in test_compile for i586 arch in 15.6.
|
||||||
Patch10: fix_configure_rst.patch
|
Patch40: fix-test-recursion-limit-15.6.patch
|
||||||
|
# PATCH-FIX-SLE doc-py38-to-py36.patch mcepl@suse.com
|
||||||
|
# Make documentation extensions working with Python 3.6
|
||||||
|
Patch41: doc-py38-to-py36.patch
|
||||||
|
# PATCH-FIX-UPSTREAM gh126985-mv-pyvenv.cfg2getpath.patch mcepl@suse.com
|
||||||
|
# Remove tests failing in test_sysconfig
|
||||||
|
Patch42: gh126985-mv-pyvenv.cfg2getpath.patch
|
||||||
|
# PATCH-FIX-UPSTREAM bsc1243155-sphinx-non-determinism.patch bsc#1243155 mcepl@suse.com
|
||||||
|
# Doc: Generate ids for audit_events using docname
|
||||||
|
Patch43: bsc1243155-sphinx-non-determinism.patch
|
||||||
|
# PATCH-FIX-UPSTREAM gh138131-exclude-pycache-from-digest.patch bsc#1244680 daniel.garcia@suse.com
|
||||||
|
Patch44: gh138131-exclude-pycache-from-digest.patch
|
||||||
|
# PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch gh#python/cpython#139257 daniel.garcia@suse.com
|
||||||
|
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
|
||||||
|
# PATCH-FIX-UPSTREAM pass-test_write_read_limited_history.patch bsc#[0-9]+ mcepl@suse.com
|
||||||
|
# Fix readline history truncation when length is reduced
|
||||||
|
Patch48: pass-test_write_read_limited_history.patch
|
||||||
BuildRequires: autoconf-archive
|
BuildRequires: autoconf-archive
|
||||||
BuildRequires: automake
|
BuildRequires: automake
|
||||||
BuildRequires: fdupes
|
BuildRequires: fdupes
|
||||||
@@ -199,26 +271,39 @@ BuildRequires: pkgconfig(libtirpc)
|
|||||||
BuildRequires: mpdecimal-devel
|
BuildRequires: mpdecimal-devel
|
||||||
%endif
|
%endif
|
||||||
%if %{with doc}
|
%if %{with doc}
|
||||||
|
|
||||||
|
%if 0%{?sle_version} >= 150700 && !0%{?is_opensuse}
|
||||||
|
BuildRequires: python311-Sphinx
|
||||||
|
BuildRequires: python311-python-docs-theme
|
||||||
|
%else
|
||||||
BuildRequires: python3-Sphinx >= 4.0.0
|
BuildRequires: python3-Sphinx >= 4.0.0
|
||||||
%if 0%{?suse_version} >= 1500
|
%if 0%{?suse_version} >= 1500
|
||||||
BuildRequires: python3-python-docs-theme >= 2022.1
|
BuildRequires: python3-python-docs-theme >= 2022.1
|
||||||
%endif
|
%endif
|
||||||
|
%if 0%{?suse_version} < 1599
|
||||||
|
BuildRequires: python3-dataclasses
|
||||||
|
%endif
|
||||||
|
%endif
|
||||||
|
%endif
|
||||||
|
# end of {with doc}
|
||||||
|
|
||||||
|
%if %{with experimental_jit}
|
||||||
|
# needed for experimental_jit
|
||||||
|
BuildRequires: clang >= 18
|
||||||
|
BuildRequires: llvm >= 18
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
# needed for experimental_jit
|
%if %{without GIL}
|
||||||
BuildRequires: clang => 18
|
ExcludeArch: aarch64
|
||||||
BuildRequires: llvm => 18
|
%endif
|
||||||
|
|
||||||
%if %{with general}
|
%if %{with general}
|
||||||
# required for idle3 (.desktop and .appdata.xml files)
|
|
||||||
BuildRequires: appstream-glib
|
|
||||||
BuildRequires: gcc-c++
|
BuildRequires: gcc-c++
|
||||||
BuildRequires: gdbm-devel
|
BuildRequires: gdbm-devel
|
||||||
BuildRequires: gettext
|
BuildRequires: gettext
|
||||||
BuildRequires: readline-devel
|
BuildRequires: readline-devel
|
||||||
BuildRequires: sqlite-devel
|
BuildRequires: sqlite-devel
|
||||||
BuildRequires: timezone
|
BuildRequires: timezone
|
||||||
BuildRequires: update-desktop-files
|
|
||||||
BuildRequires: pkgconfig(ncurses)
|
BuildRequires: pkgconfig(ncurses)
|
||||||
BuildRequires: pkgconfig(tk)
|
BuildRequires: pkgconfig(tk)
|
||||||
BuildRequires: pkgconfig(x11)
|
BuildRequires: pkgconfig(x11)
|
||||||
@@ -250,6 +335,12 @@ Installing "python3" is sufficient for the vast majority of usecases.
|
|||||||
In addition, recommended packages provide UI toolkit support (python3-curses,
|
In addition, recommended packages provide UI toolkit support (python3-curses,
|
||||||
python3-tk), legacy UNIX database bindings (python3-dbm), and the IDLE
|
python3-tk), legacy UNIX database bindings (python3-dbm), and the IDLE
|
||||||
development environment (python3-idle).
|
development environment (python3-idle).
|
||||||
|
%if %{without GIL}
|
||||||
|
|
||||||
|
This package has been built with the Global Interpreter Lock removed.
|
||||||
|
This feature is still considered to be experimental. This package is
|
||||||
|
not ready to be used in production environments.
|
||||||
|
%endif
|
||||||
|
|
||||||
%package -n %{python_pkg_name}-tk
|
%package -n %{python_pkg_name}-tk
|
||||||
Summary: TkInter, a Python Tk Interface
|
Summary: TkInter, a Python Tk Interface
|
||||||
@@ -365,6 +456,12 @@ This package contains the interpreter core and most commonly used modules
|
|||||||
from the standard library. This is sufficient for many usecases, but it
|
from the standard library. This is sufficient for many usecases, but it
|
||||||
excludes components that depend on external libraries, most notably XML,
|
excludes components that depend on external libraries, most notably XML,
|
||||||
database and UI toolkits support.
|
database and UI toolkits support.
|
||||||
|
%if %{without GIL}
|
||||||
|
|
||||||
|
This package has been built with the Global Interpreter Lock removed.
|
||||||
|
This feature is still considered to be experimental. This package is
|
||||||
|
not ready to be used in production environments.
|
||||||
|
%endif
|
||||||
|
|
||||||
%package -n %{python_pkg_name}-tools
|
%package -n %{python_pkg_name}-tools
|
||||||
Summary: Python Utility and Demonstration Scripts
|
Summary: Python Utility and Demonstration Scripts
|
||||||
@@ -428,13 +525,7 @@ This package contains libpython3.2 shared library for embedding in
|
|||||||
other applications.
|
other applications.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%autosetup -p1 -N -n %{tarname}
|
%autosetup -p1 -n %{tarname}
|
||||||
%autopatch -p1 -M 07
|
|
||||||
|
|
||||||
%if 0%{?suse_version} <= 1500
|
|
||||||
%patch -P 08 -p1
|
|
||||||
%endif
|
|
||||||
%autopatch -p1 -m 09
|
|
||||||
|
|
||||||
# Fix devhelp doc build gh#python/cpython#120150
|
# Fix devhelp doc build gh#python/cpython#120150
|
||||||
echo "master_doc = 'contents'" >> Doc/conf.py
|
echo "master_doc = 'contents'" >> Doc/conf.py
|
||||||
@@ -442,6 +533,8 @@ echo "master_doc = 'contents'" >> Doc/conf.py
|
|||||||
# drop Autoconf version requirement
|
# drop Autoconf version requirement
|
||||||
sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac
|
sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac
|
||||||
|
|
||||||
|
sed -i "s/_LLVM_VERSION = .*/_LLVM_VERSION = $(realpath /usr/bin/clang | awk -F- '{print $2}')/g" ./Tools/jit/_llvm.py
|
||||||
|
|
||||||
%if %{primary_interpreter}
|
%if %{primary_interpreter}
|
||||||
# fix shebangs - convert /usr/local/bin/python and /usr/bin/env/python to /usr/bin/python3
|
# fix shebangs - convert /usr/local/bin/python and /usr/bin/env/python to /usr/bin/python3
|
||||||
for dir in Lib Tools; do
|
for dir in Lib Tools; do
|
||||||
@@ -462,7 +555,7 @@ sed -i -e '/Breakpoint 3 at ...pdb.py:97/s/97/96/' Lib/test/test_pdb.py
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
# Cannot remove it because of gh#python/cpython#92875
|
# Cannot remove it because of gh#python/cpython#92875
|
||||||
# rm -r Modules/expat
|
rm -r Modules/expat
|
||||||
|
|
||||||
# drop duplicate README from site-packages
|
# drop duplicate README from site-packages
|
||||||
rm Lib/site-packages/README.txt
|
rm Lib/site-packages/README.txt
|
||||||
@@ -470,17 +563,17 @@ rm Lib/site-packages/README.txt
|
|||||||
# Add vendored bluez-devel files
|
# Add vendored bluez-devel files
|
||||||
tar xvf %{SOURCE21}
|
tar xvf %{SOURCE21}
|
||||||
|
|
||||||
# Don't fail on warnings when building documentation
|
|
||||||
# sed -i -e '/^SPHINXERRORHANDLING/s/-W//' Doc/Makefile
|
|
||||||
|
|
||||||
%build
|
%build
|
||||||
|
export SUSE_VERSION="0%{?suse_version}"
|
||||||
|
export SLE_VERSION="0%{?sle_version}"
|
||||||
|
|
||||||
%if %{with doc}
|
%if %{with doc}
|
||||||
TODAY_DATE=`date -r %{SOURCE0} "+%%B %%d, %%Y"`
|
TODAY_DATE=`date -r %{SOURCE0} "+%%B %%d, %%Y"`
|
||||||
# TODO use not date of tarball but date of latest patch
|
# TODO use not date of tarball but date of latest patch
|
||||||
|
|
||||||
cd Doc
|
cd Doc
|
||||||
sed -i "s/^today = .*/today = '$TODAY_DATE'/" conf.py
|
sed -i "s/^today = .*/today = '$TODAY_DATE'/" conf.py
|
||||||
%make_build -j1 html
|
%make_build -j1 JOBS=1 html
|
||||||
|
|
||||||
# Build also devhelp files
|
# Build also devhelp files
|
||||||
sphinx-build -a -b devhelp . build/devhelp
|
sphinx-build -a -b devhelp . build/devhelp
|
||||||
@@ -520,6 +613,9 @@ export CFLAGS="%{optflags} -IVendor/"
|
|||||||
%endif
|
%endif
|
||||||
%if %{with experimental_jit}
|
%if %{with experimental_jit}
|
||||||
--enable-experimental-jit=yes-off \
|
--enable-experimental-jit=yes-off \
|
||||||
|
%endif
|
||||||
|
%if %{without GIL}
|
||||||
|
--disable-gil \
|
||||||
%endif
|
%endif
|
||||||
--enable-loadable-sqlite-extensions
|
--enable-loadable-sqlite-extensions
|
||||||
|
|
||||||
@@ -529,7 +625,6 @@ export CFLAGS="%{optflags} -IVendor/"
|
|||||||
# Objects/typeslots.inc \
|
# Objects/typeslots.inc \
|
||||||
# Python/opcode_targets.h \
|
# Python/opcode_targets.h \
|
||||||
# Include/opcode.h
|
# Include/opcode.h
|
||||||
%make_build
|
|
||||||
|
|
||||||
%if %{with general}
|
%if %{with general}
|
||||||
%make_build
|
%make_build
|
||||||
@@ -546,6 +641,8 @@ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH \
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%check
|
%check
|
||||||
|
export SUSE_VERSION="0%{?suse_version}"
|
||||||
|
export SLE_VERSION="0%{?sle_version}"
|
||||||
%if %{with general}
|
%if %{with general}
|
||||||
# exclude test_gdb -- it doesn't run in buildservice anyway, and fails on missing debuginfos
|
# exclude test_gdb -- it doesn't run in buildservice anyway, and fails on missing debuginfos
|
||||||
# when you install gdb into your test env
|
# when you install gdb into your test env
|
||||||
@@ -563,7 +660,14 @@ EXCLUDE="$EXCLUDE test_faulthandler"
|
|||||||
%endif
|
%endif
|
||||||
# some tests break in QEMU
|
# some tests break in QEMU
|
||||||
%if 0%{?qemu_user_space_build}
|
%if 0%{?qemu_user_space_build}
|
||||||
EXCLUDE="$EXCLUDE test_faulthandler test_multiprocessing_forkserver test_multiprocessing_spawn test_os test_posix test_signal test_socket test_subprocess"
|
# test_external_inspection: qemu does not support ptrace in test_self_trace
|
||||||
|
# test_faulthandler: test_register_chain is racy
|
||||||
|
# test_os: test_fork_warns_when_non_python_thread_exists fails
|
||||||
|
# test_posix: qemu does not support fexecve with O_CLOEXEC in test_fexecve
|
||||||
|
# test_signal: qemu crashes in test_stress_modifying_handlers
|
||||||
|
# test_socket: many CmsgTrunc tests fail
|
||||||
|
# test_subprocess: qemu does not support CLONE_VFORK
|
||||||
|
EXCLUDE="$EXCLUDE test_external_inspection test_faulthandler test_os test_posix test_signal test_socket test_subprocess"
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
# This test (part of test_uuid) requires real network interfaces
|
# This test (part of test_uuid) requires real network interfaces
|
||||||
@@ -571,12 +675,6 @@ EXCLUDE="$EXCLUDE test_faulthandler test_multiprocessing_forkserver test_multipr
|
|||||||
# done have any such interface breaking the uuid module.
|
# done have any such interface breaking the uuid module.
|
||||||
EXCLUDE="$EXCLUDE test_uuid"
|
EXCLUDE="$EXCLUDE test_uuid"
|
||||||
|
|
||||||
# bsc#1195140 and bpo#37169 - test_capi is failing on openSUSE, and not sure why
|
|
||||||
EXCLUDE="$EXCLUDE test_capi"
|
|
||||||
|
|
||||||
# Failing tests on python 3.13
|
|
||||||
EXCLUDE="$EXCLUDE test_regrtest test_sysconfig"
|
|
||||||
|
|
||||||
# Limit virtual memory to avoid spurious failures
|
# Limit virtual memory to avoid spurious failures
|
||||||
if test $(ulimit -v) = unlimited || test $(ulimit -v) -gt 10000000; then
|
if test $(ulimit -v) = unlimited || test $(ulimit -v) -gt 10000000; then
|
||||||
ulimit -v 11000000 || :
|
ulimit -v 11000000 || :
|
||||||
@@ -657,37 +755,39 @@ done
|
|||||||
|
|
||||||
# Idle is not packaged in base due to the appstream-glib dependency
|
# Idle is not packaged in base due to the appstream-glib dependency
|
||||||
# move idle config into /etc
|
# move idle config into /etc
|
||||||
install -d -m 755 %{buildroot}%{_sysconfdir}/idle%{python_version}
|
install -d -m 755 %{buildroot}%{_sysconfdir}/idle%{python_abi}
|
||||||
(
|
(
|
||||||
cd %{buildroot}/%{sitedir}/idlelib/
|
cd %{buildroot}/%{sitedir}/idlelib/
|
||||||
for file in *.def ; do
|
for file in *.def ; do
|
||||||
mv $file %{buildroot}%{_sysconfdir}/idle%{python_version}/
|
mv $file %{buildroot}%{_sysconfdir}/idle%{python_abi}/
|
||||||
ln -sf %{_sysconfdir}/idle%{python_version}/$file %{buildroot}/%{sitedir}/idlelib/
|
ln -sf %{_sysconfdir}/idle%{python_abi}/$file %{buildroot}/%{sitedir}/idlelib/
|
||||||
done
|
done
|
||||||
)
|
)
|
||||||
|
|
||||||
# keep just idle3.X
|
# keep just idle3.X
|
||||||
ls -l %{buildroot}%{_bindir}/
|
|
||||||
rm %{buildroot}%{_bindir}/idle3
|
rm %{buildroot}%{_bindir}/idle3
|
||||||
|
|
||||||
|
# mve idle binary to idle3.13t to avoid conflict
|
||||||
|
%if %{without GIL}
|
||||||
|
mv %{buildroot}%{_bindir}/idle%{python_version} %{buildroot}%{_bindir}/idle%{python_abi}
|
||||||
|
%endif
|
||||||
|
|
||||||
# install idle icons
|
# install idle icons
|
||||||
for size in 16 32 48 ; do
|
for size in 16 32 48 ; do
|
||||||
install -m 644 -D Lib/idlelib/Icons/idle_${size}.png \
|
install -m 644 -D Lib/idlelib/Icons/idle_${size}.png \
|
||||||
%{buildroot}%{_datadir}/icons/hicolor/${size}x${size}/apps/idle%{python_version}.png
|
%{buildroot}%{_datadir}/icons/hicolor/${size}x${size}/apps/idle%{python_abi}.png
|
||||||
done
|
done
|
||||||
|
|
||||||
# install idle desktop file
|
# install idle desktop file
|
||||||
cp %{SOURCE19} idle%{python_version}.desktop
|
cp %{SOURCE19} idle%{python_abi}.desktop
|
||||||
sed -i -e 's:idle3:idle%{python_version}:g' idle%{python_version}.desktop
|
sed -i -e 's:idle3:idle%{python_abi}:g' idle%{python_abi}.desktop
|
||||||
install -m 644 -D -t %{buildroot}%{_datadir}/applications idle%{python_version}.desktop
|
install -m 644 -D -t %{buildroot}%{_datadir}/applications idle%{python_abi}.desktop
|
||||||
%suse_update_desktop_file idle%{python_version}
|
|
||||||
|
|
||||||
cp %{SOURCE20} idle%{python_version}.appdata.xml
|
cp %{SOURCE20} idle%{python_abi}.appdata.xml
|
||||||
sed -i -e 's:idle3.desktop:idle%{python_version}.desktop:g' idle%{python_version}.appdata.xml
|
sed -i -e 's:idle3.desktop:idle%{python_abi}.desktop:g' idle%{python_abi}.appdata.xml
|
||||||
install -m 644 -D -t %{buildroot}%{_datadir}/metainfo idle%{python_version}.appdata.xml
|
install -m 644 -D -t %{buildroot}%{_datadir}/metainfo idle%{python_abi}.appdata.xml
|
||||||
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/idle%{python_version}.appdata.xml
|
|
||||||
|
|
||||||
%fdupes %{buildroot}/%{_libdir}/python%{python_version}
|
%fdupes %{buildroot}/%{_libdir}/python%{python_abi}
|
||||||
%endif
|
%endif
|
||||||
%if %{with base}
|
%if %{with base}
|
||||||
%make_install
|
%make_install
|
||||||
@@ -699,7 +799,7 @@ find %{buildroot} -name "*.a" -delete
|
|||||||
install -d -m 755 %{buildroot}%{sitedir}/site-packages
|
install -d -m 755 %{buildroot}%{sitedir}/site-packages
|
||||||
install -d -m 755 %{buildroot}%{sitedir}/site-packages/__pycache__
|
install -d -m 755 %{buildroot}%{sitedir}/site-packages/__pycache__
|
||||||
# and their 32bit counterparts explicitly
|
# and their 32bit counterparts explicitly
|
||||||
mkdir -p %{buildroot}%{_prefix}/lib/python%{python_version}/site-packages/__pycache__
|
mkdir -p %{buildroot}%{_prefix}/lib/python%{python_abi}/site-packages/__pycache__
|
||||||
|
|
||||||
# cleanup parts that don't belong
|
# cleanup parts that don't belong
|
||||||
for dir in curses dbm sqlite3 tkinter idlelib; do
|
for dir in curses dbm sqlite3 tkinter idlelib; do
|
||||||
@@ -721,13 +821,13 @@ rm %{buildroot}%{_libdir}/libpython3.so
|
|||||||
rm %{buildroot}%{_libdir}/pkgconfig/{python3,python3-embed}.pc
|
rm %{buildroot}%{_libdir}/pkgconfig/{python3,python3-embed}.pc
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if %{suse_version} > 1550
|
%if %{with externally_managed}
|
||||||
# PEP-0668 mark this as a distro maintained python
|
# PEP-0668 mark this as a distro maintained python
|
||||||
sed -e 's,__PYTHONPREFIX__,%{python_pkg_name},' -e 's,__PYTHON__,python%{python_version},' < %{SOURCE4} > %{buildroot}%{sitedir}/EXTERNALLY-MANAGED
|
sed -e 's,__PYTHONPREFIX__,%{python_pkg_name},' -e 's,__PYTHON__,python%{python_version},' < %{SOURCE4} > %{buildroot}%{sitedir}/EXTERNALLY-MANAGED
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
# link shared library instead of static library that tools expect
|
# link shared library instead of static library that tools expect
|
||||||
ln -s ../../libpython%{python_abi}.so %{buildroot}%{_libdir}/python%{python_version}/config-%{python_abi}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}/libpython%{python_abi}.so
|
ln -s ../../libpython%{python_abi}.so %{buildroot}%{_libdir}/python%{python_abi}/config-%{python_abi}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}/libpython%{python_abi}.so
|
||||||
|
|
||||||
# delete idle3, which has to many packaging dependencies for base
|
# delete idle3, which has to many packaging dependencies for base
|
||||||
rm %{buildroot}%{_bindir}/idle3*
|
rm %{buildroot}%{_bindir}/idle3*
|
||||||
@@ -757,6 +857,9 @@ install -m 755 -D Tools/gdb/libpython.py %{buildroot}%{_datadir}/gdb/auto-load/%
|
|||||||
# install devel files to /config
|
# install devel files to /config
|
||||||
#cp Makefile Makefile.pre.in Makefile.pre $RPM_BUILD_ROOT%{sitedir}/config-%{python_abi}/
|
#cp Makefile Makefile.pre.in Makefile.pre $RPM_BUILD_ROOT%{sitedir}/config-%{python_abi}/
|
||||||
|
|
||||||
|
# Remove -IVendor/ from python-config boo#1231795
|
||||||
|
sed -i 's/-IVendor\///' %{buildroot}%{_bindir}/python%{python_abi}-config
|
||||||
|
|
||||||
# RPM macros
|
# RPM macros
|
||||||
%if %{primary_interpreter}
|
%if %{primary_interpreter}
|
||||||
mkdir -p %{buildroot}%{_rpmconfigdir}/macros.d/
|
mkdir -p %{buildroot}%{_rpmconfigdir}/macros.d/
|
||||||
@@ -783,8 +886,24 @@ LD_LIBRARY_PATH=. ./python -O -c "from py_compile import compile; compile('$FAIL
|
|||||||
done < %{SOURCE9}
|
done < %{SOURCE9}
|
||||||
)
|
)
|
||||||
echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-import-failed-hooks.pth
|
echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-import-failed-hooks.pth
|
||||||
|
|
||||||
|
# not packaged without GIL
|
||||||
|
%if %{without GIL}
|
||||||
|
rm -rf %{buildroot}%{_libdir}/pkgconfig/python-%{python_version}.pc
|
||||||
|
rm -rf %{buildroot}%{_libdir}/pkgconfig/python-%{python_version}-embed.pc
|
||||||
|
rm %{buildroot}%{_bindir}/python%{python_version}
|
||||||
|
rm %{buildroot}%{_bindir}/pydoc%{python_version}
|
||||||
|
rm %{buildroot}%{_bindir}/python%{python_version}-config
|
||||||
|
rm %{buildroot}%{_mandir}/man1/python%{python_version}.1*
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%endif
|
||||||
|
|
||||||
|
# For the purposes of reproducibility, it is necessary to eliminate any *.pyc files inside documentation dirs
|
||||||
|
if [ -d %{buildroot}%{_defaultdocdir} ] ; then
|
||||||
|
find %{buildroot}%{_defaultdocdir} -type f -name \*.pyc -ls -exec rm -vf '{}' \;
|
||||||
|
fi
|
||||||
|
|
||||||
%if %{with general}
|
%if %{with general}
|
||||||
%files -n %{python_pkg_name}-tk
|
%files -n %{python_pkg_name}-tk
|
||||||
%{sitedir}/tkinter
|
%{sitedir}/tkinter
|
||||||
@@ -793,6 +912,7 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%files -n %{python_pkg_name}-curses
|
%files -n %{python_pkg_name}-curses
|
||||||
%{sitedir}/curses
|
%{sitedir}/curses
|
||||||
%{dynlib _curses}
|
%{dynlib _curses}
|
||||||
|
%{dynlib _curses_panel}
|
||||||
|
|
||||||
%files -n %{python_pkg_name}-dbm
|
%files -n %{python_pkg_name}-dbm
|
||||||
%{sitedir}/dbm
|
%{sitedir}/dbm
|
||||||
@@ -808,21 +928,22 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
|
|
||||||
%files -n %{python_pkg_name}-idle
|
%files -n %{python_pkg_name}-idle
|
||||||
%{sitedir}/idlelib
|
%{sitedir}/idlelib
|
||||||
%dir %{_sysconfdir}/idle%{python_version}
|
%dir %{_sysconfdir}/idle%{python_abi}
|
||||||
%config %{_sysconfdir}/idle%{python_version}/*
|
%config %{_sysconfdir}/idle%{python_abi}/*
|
||||||
%doc Lib/idlelib/README.txt
|
%doc Lib/idlelib/README.txt
|
||||||
%doc Lib/idlelib/TODO.txt
|
%doc Lib/idlelib/TODO.txt
|
||||||
%doc Lib/idlelib/extend.txt
|
%doc Lib/idlelib/extend.txt
|
||||||
%doc Lib/idlelib/ChangeLog
|
%doc Lib/idlelib/ChangeLog
|
||||||
%{_bindir}/idle%{python_version}
|
%{_bindir}/idle%{python_abi}
|
||||||
%{_datadir}/applications/idle%{python_version}.desktop
|
%{_datadir}/applications/idle%{python_abi}.desktop
|
||||||
%{_datadir}/metainfo/idle%{python_version}.appdata.xml
|
%{_datadir}/metainfo/idle%{python_abi}.appdata.xml
|
||||||
%{_datadir}/icons/hicolor/*/apps/idle%{python_version}.png
|
%{_datadir}/icons/hicolor/*/apps/idle%{python_abi}.png
|
||||||
%dir %{_datadir}/icons/hicolor
|
%dir %{_datadir}/icons/hicolor
|
||||||
%dir %{_datadir}/icons/hicolor/16x16
|
%dir %{_datadir}/icons/hicolor/16x16
|
||||||
%dir %{_datadir}/icons/hicolor/32x32
|
%dir %{_datadir}/icons/hicolor/32x32
|
||||||
%dir %{_datadir}/icons/hicolor/48x48
|
%dir %{_datadir}/icons/hicolor/48x48
|
||||||
%dir %{_datadir}/icons/hicolor/*/apps
|
%dir %{_datadir}/icons/hicolor/*/apps
|
||||||
|
|
||||||
# endif for if general
|
# endif for if general
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
@@ -897,8 +1018,12 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%if %{primary_interpreter}
|
%if %{primary_interpreter}
|
||||||
%{_mandir}/man1/python3.1%{?ext_man}
|
%{_mandir}/man1/python3.1%{?ext_man}
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with GIL}
|
||||||
%{_mandir}/man1/python%{python_version}.1%{?ext_man}
|
%{_mandir}/man1/python%{python_version}.1%{?ext_man}
|
||||||
%if %{suse_version} > 1550
|
%endif
|
||||||
|
|
||||||
|
%if %{with externally_managed}
|
||||||
# PEP-0668
|
# PEP-0668
|
||||||
%{sitedir}/EXTERNALLY-MANAGED
|
%{sitedir}/EXTERNALLY-MANAGED
|
||||||
%endif
|
%endif
|
||||||
@@ -908,8 +1033,10 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%if %{primary_interpreter}
|
%if %{primary_interpreter}
|
||||||
%{_rpmconfigdir}/macros.d/macros.python3
|
%{_rpmconfigdir}/macros.d/macros.python3
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
# binary parts
|
# binary parts
|
||||||
%dir %{sitedir}/lib-dynload
|
%dir %{sitedir}/lib-dynload
|
||||||
|
|
||||||
%{dynlib array}
|
%{dynlib array}
|
||||||
%{dynlib _asyncio}
|
%{dynlib _asyncio}
|
||||||
%{dynlib binascii}
|
%{dynlib binascii}
|
||||||
@@ -970,11 +1097,13 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%{dynlib _sha1}
|
%{dynlib _sha1}
|
||||||
%{dynlib _sha2}
|
%{dynlib _sha2}
|
||||||
%{dynlib _sha3}
|
%{dynlib _sha3}
|
||||||
# python parts
|
|
||||||
%dir %{_prefix}/lib/python%{python_version}
|
|
||||||
%dir %{_prefix}/lib/python%{python_version}/site-packages
|
|
||||||
%dir %{_prefix}/lib/python%{python_version}/site-packages/__pycache__
|
|
||||||
%dir %{sitedir}
|
%dir %{sitedir}
|
||||||
|
# python parts
|
||||||
|
%dir %{_prefix}/lib/python%{python_abi}
|
||||||
|
%dir %{_prefix}/lib/python%{python_abi}/site-packages
|
||||||
|
%dir %{_prefix}/lib/python%{python_abi}/site-packages/__pycache__
|
||||||
|
|
||||||
%dir %{sitedir}/site-packages
|
%dir %{sitedir}/site-packages
|
||||||
%dir %{sitedir}/site-packages/__pycache__
|
%dir %{sitedir}/site-packages/__pycache__
|
||||||
# %%exclude %%{sitedir}/*/test
|
# %%exclude %%{sitedir}/*/test
|
||||||
@@ -1018,9 +1147,11 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo
|
|||||||
%{_bindir}/pydoc3
|
%{_bindir}/pydoc3
|
||||||
%endif
|
%endif
|
||||||
# executables
|
# executables
|
||||||
|
%if %{with GIL}
|
||||||
%attr(755, root, root) %{_bindir}/pydoc%{python_version}
|
%attr(755, root, root) %{_bindir}/pydoc%{python_version}
|
||||||
# %%attr(755, root, root) %%{_bindir}/python%%{python_abi}
|
%endif
|
||||||
%attr(755, root, root) %{_bindir}/python%{python_version}
|
%attr(755, root, root) %{_bindir}/python%{python_abi}
|
||||||
|
|
||||||
# endif for if base
|
# endif for if base
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
---
|
---
|
||||||
Lib/test/test_subprocess.py | 3 ++-
|
Lib/test/test_subprocess.py | 6 +++++-
|
||||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
--- a/Lib/test/test_subprocess.py
|
--- a/Lib/test/test_subprocess.py
|
||||||
+++ b/Lib/test/test_subprocess.py
|
+++ b/Lib/test/test_subprocess.py
|
||||||
@@ -280,7 +280,8 @@ class ProcessTestCase(BaseTestCase):
|
@@ -292,7 +292,11 @@ class ProcessTestCase(BaseTestCase):
|
||||||
"time.sleep(3600)"],
|
output = subprocess.check_output(
|
||||||
# Some heavily loaded buildbots (sparc Debian 3.x) require
|
[sys.executable, "-c",
|
||||||
# this much time to start and print.
|
"import time; time.sleep(3600)"],
|
||||||
- timeout=3)
|
- timeout=0.1)
|
||||||
+ # OBS might require even more
|
+ # Some heavily loaded buildbots (sparc Debian 3.x) require
|
||||||
+ timeout=10)
|
+ # this much time to start and print.
|
||||||
self.fail("Expected TimeoutExpired.")
|
+ # timeout=3)
|
||||||
self.assertEqual(c.exception.output, b'BDFL')
|
+ # OBS might require even more
|
||||||
|
+ timeout=10)
|
||||||
|
|
||||||
|
def test_call_kwargs(self):
|
||||||
|
# call() function with keyword args
|
||||||
|
|||||||
Reference in New Issue
Block a user