forked from pool/python39
- Update to 3.9.24:
- Security
- gh-139700: Check consistency of the zip64 end of central
directory record. Support records with “zip64 extensible data”
if there are no bytes prepended to the ZIP file.
- gh-139400: xml.parsers.expat: Make sure that parent Expat
parsers are only garbage-collected once they are no longer
referenced by subparsers created by
ExternalEntityParserCreate(). Patch by Sebastian Pipping.
- gh-121227: Raise an SSL.SSLError if an empty protocols argument
is passed to ssl.SSLContext.set_npn_protocols() to fix
CVE-2024-5642.
- gh-135661: Fix parsing start and end tags in
html.parser.HTMLParser according to the HTML5 standard.
* Whitespaces no longer accepted between </ and the tag name.
E.g. </ script> does not end the script section.
* Vertical tabulation (\v) and non-ASCII whitespaces no longer
recognized as whitespaces. The only whitespaces are \t\n\r\f
and space.
* Null character (U+0000) no longer ends the tag name.
* Attributes and slashes after the tag name in end tags are now
ignored, instead of terminating after the first > in quoted
attribute value. E.g. </script/foo=">"/>.
* Multiple slashes and whitespaces between the last attribute
and closing > are now ignored in both start and end tags. E.g.
<a foo=bar/ //>.
* Multiple = between attribute name and value are no longer
collapsed. E.g. <a foo==bar> produces attribute “foo” with
value “=bar”.
- gh-135661: Fix CDATA section parsing in html.parser.HTMLParser
OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python39?expand=0&rev=245
This commit is contained in:
23
.gitattributes
vendored
Normal file
23
.gitattributes
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
## Default LFS
|
||||||
|
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bsp filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gem filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.obscpio filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.oxt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.rpm filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.txz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.whl filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zst filter=lfs diff=lfs merge=lfs -text
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.osc
|
||||||
58
98437-sphinx.locale._-as-gettext-in-pyspecific.patch
Normal file
58
98437-sphinx.locale._-as-gettext-in-pyspecific.patch
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
From 5775f51691d7d64fb676586e008b41261ce64ac2 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Matt.Wang" <mattwang44@gmail.com>
|
||||||
|
Date: Wed, 19 Oct 2022 14:49:08 +0800
|
||||||
|
Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for
|
||||||
|
backward-compatibility in pyspecific.py
|
||||||
|
|
||||||
|
[why] spinix 5.3 changed locale.translators from a defaultdict(gettext.NullTranslations) to a dict, which leads to failure of pyspecific.py. Use sphinx.locale._ as gettext to fix the issue.
|
||||||
|
---
|
||||||
|
Doc/tools/extensions/pyspecific.py | 8 ++++----
|
||||||
|
Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst | 1 +
|
||||||
|
2 files changed, 5 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
Index: Python-3.9.22/Doc/tools/extensions/pyspecific.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.9.22.orig/Doc/tools/extensions/pyspecific.py 2025-04-11 09:49:58.417019238 +0200
|
||||||
|
+++ Python-3.9.22/Doc/tools/extensions/pyspecific.py 2025-04-11 09:50:56.818993764 +0200
|
||||||
|
@@ -27,7 +27,7 @@
|
||||||
|
from sphinx.errors import NoUri
|
||||||
|
except ImportError:
|
||||||
|
from sphinx.environment import NoUri
|
||||||
|
-from sphinx.locale import translators
|
||||||
|
+from sphinx.locale import _ as sphinx_gettext
|
||||||
|
from sphinx.util import status_iterator, logging
|
||||||
|
from sphinx.util.nodes import split_explicit_title
|
||||||
|
from sphinx.writers.text import TextWriter, TextTranslator
|
||||||
|
@@ -111,7 +111,7 @@
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
pnode = nodes.compound(classes=['impl-detail'])
|
||||||
|
- label = translators['sphinx'].gettext(self.label_text)
|
||||||
|
+ label = sphinx_gettext(self.label_text)
|
||||||
|
content = self.content
|
||||||
|
add_text = nodes.strong(label, label)
|
||||||
|
if self.arguments:
|
||||||
|
@@ -180,7 +180,7 @@
|
||||||
|
else:
|
||||||
|
args = []
|
||||||
|
|
||||||
|
- label = translators['sphinx'].gettext(self._label[min(2, len(args))])
|
||||||
|
+ label = sphinx_gettext(self._label[min(2, len(args))])
|
||||||
|
text = label.format(name="``{}``".format(name),
|
||||||
|
args=", ".join("``{}``".format(a) for a in args if a))
|
||||||
|
|
||||||
|
@@ -380,7 +380,7 @@
|
||||||
|
else:
|
||||||
|
label = self._removed_label
|
||||||
|
|
||||||
|
- label = translators['sphinx'].gettext(label)
|
||||||
|
+ label = sphinx_gettext(label)
|
||||||
|
text = label.format(deprecated=version[0], removed=version[1])
|
||||||
|
if len(self.arguments) == 3:
|
||||||
|
inodes, messages = self.state.inline_text(self.arguments[2],
|
||||||
|
Index: Python-3.9.22/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ Python-3.9.22/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst 2025-04-11 09:50:08.952333342 +0200
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Use sphinx.locale._ as the gettext function in pyspecific.py.
|
||||||
79
99366-patch.dict-can-decorate-async.patch
Normal file
79
99366-patch.dict-can-decorate-async.patch
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
From c0dea0309b9a0a7cbc87727c9957f0a388fb9b0f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Nikita Sobolev <mail@sobolevn.me>
|
||||||
|
Date: Fri, 11 Nov 2022 11:04:30 +0300
|
||||||
|
Subject: [PATCH] gh-98086: Now ``patch.dict`` can decorate async functions
|
||||||
|
(GH-98095) (cherry picked from commit
|
||||||
|
67b4d2772c5124b908f8ed9b13166a79bbeb88d2)
|
||||||
|
|
||||||
|
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
|
||||||
|
---
|
||||||
|
Lib/unittest/mock.py | 18 ++++++++++
|
||||||
|
Lib/unittest/test/testmock/testasync.py | 17 +++++++++
|
||||||
|
Misc/NEWS.d/next/Library/2022-10-08-19-39-27.gh-issue-98086.y---WC.rst | 1
|
||||||
|
3 files changed, 36 insertions(+)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2022-10-08-19-39-27.gh-issue-98086.y---WC.rst
|
||||||
|
|
||||||
|
--- a/Lib/unittest/mock.py
|
||||||
|
+++ b/Lib/unittest/mock.py
|
||||||
|
@@ -1761,6 +1761,12 @@ class _patch_dict(object):
|
||||||
|
def __call__(self, f):
|
||||||
|
if isinstance(f, type):
|
||||||
|
return self.decorate_class(f)
|
||||||
|
+ if inspect.iscoroutinefunction(f):
|
||||||
|
+ return self.decorate_async_callable(f)
|
||||||
|
+ return self.decorate_callable(f)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ def decorate_callable(self, f):
|
||||||
|
@wraps(f)
|
||||||
|
def _inner(*args, **kw):
|
||||||
|
self._patch_dict()
|
||||||
|
@@ -1769,6 +1775,18 @@ class _patch_dict(object):
|
||||||
|
finally:
|
||||||
|
self._unpatch_dict()
|
||||||
|
|
||||||
|
+ return _inner
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ def decorate_async_callable(self, f):
|
||||||
|
+ @wraps(f)
|
||||||
|
+ async def _inner(*args, **kw):
|
||||||
|
+ self._patch_dict()
|
||||||
|
+ try:
|
||||||
|
+ return await f(*args, **kw)
|
||||||
|
+ finally:
|
||||||
|
+ self._unpatch_dict()
|
||||||
|
+
|
||||||
|
return _inner
|
||||||
|
|
||||||
|
|
||||||
|
--- a/Lib/unittest/test/testmock/testasync.py
|
||||||
|
+++ b/Lib/unittest/test/testmock/testasync.py
|
||||||
|
@@ -146,6 +146,23 @@ class AsyncPatchCMTest(unittest.TestCase
|
||||||
|
|
||||||
|
run(test_async())
|
||||||
|
|
||||||
|
+ def test_patch_dict_async_def(self):
|
||||||
|
+ foo = {'a': 'a'}
|
||||||
|
+ @patch.dict(foo, {'a': 'b'})
|
||||||
|
+ async def test_async():
|
||||||
|
+ self.assertEqual(foo['a'], 'b')
|
||||||
|
+
|
||||||
|
+ self.assertTrue(iscoroutinefunction(test_async))
|
||||||
|
+ run(test_async())
|
||||||
|
+
|
||||||
|
+ def test_patch_dict_async_def_context(self):
|
||||||
|
+ foo = {'a': 'a'}
|
||||||
|
+ async def test_async():
|
||||||
|
+ with patch.dict(foo, {'a': 'b'}):
|
||||||
|
+ self.assertEqual(foo['a'], 'b')
|
||||||
|
+
|
||||||
|
+ run(test_async())
|
||||||
|
+
|
||||||
|
|
||||||
|
class AsyncMockTest(unittest.TestCase):
|
||||||
|
def test_iscoroutinefunction_default(self):
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2022-10-08-19-39-27.gh-issue-98086.y---WC.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Make sure ``patch.dict()`` can be applied on async functions.
|
||||||
57
CVE-2023-52425-libexpat-2.6.0-backport.patch
Normal file
57
CVE-2023-52425-libexpat-2.6.0-backport.patch
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_pyexpat.py | 4 ++++
|
||||||
|
Lib/test/test_sax.py | 3 +++
|
||||||
|
Lib/test/test_xml_etree.py | 7 +++++++
|
||||||
|
3 files changed, 14 insertions(+)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_pyexpat.py
|
||||||
|
+++ b/Lib/test/test_pyexpat.py
|
||||||
|
@@ -766,6 +766,10 @@ class ReparseDeferralTest(unittest.TestC
|
||||||
|
self.assertEqual(started, ['doc'])
|
||||||
|
|
||||||
|
def test_reparse_deferral_disabled(self):
|
||||||
|
+ if expat.version_info < (2, 6, 0):
|
||||||
|
+ self.skipTest(f'Expat {expat.version_info} does not '
|
||||||
|
+ 'support reparse deferral')
|
||||||
|
+
|
||||||
|
started = []
|
||||||
|
|
||||||
|
def start_element(name, _):
|
||||||
|
--- a/Lib/test/test_sax.py
|
||||||
|
+++ b/Lib/test/test_sax.py
|
||||||
|
@@ -1236,6 +1236,9 @@ class ExpatReaderTest(XmlTestBase):
|
||||||
|
|
||||||
|
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
|
||||||
|
|
||||||
|
+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
|
||||||
|
+ f'Expat {pyexpat.version_info} does not '
|
||||||
|
+ 'support reparse deferral')
|
||||||
|
def test_flush_reparse_deferral_disabled(self):
|
||||||
|
result = BytesIO()
|
||||||
|
xmlgen = XMLGenerator(result)
|
||||||
|
--- a/Lib/test/test_xml_etree.py
|
||||||
|
+++ b/Lib/test/test_xml_etree.py
|
||||||
|
@@ -1416,9 +1416,13 @@ class XMLPullParserTest(unittest.TestCas
|
||||||
|
self.assert_event_tags(parser, [('end', 'root')])
|
||||||
|
self.assertIsNone(parser.close())
|
||||||
|
|
||||||
|
+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
|
||||||
|
+ f'Fail with patched version of Expat {pyexpat.version_info}')
|
||||||
|
def test_simple_xml_chunk_1(self):
|
||||||
|
self.test_simple_xml(chunk_size=1, flush=True)
|
||||||
|
|
||||||
|
+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
|
||||||
|
+ f'Fail with patched version of Expat {pyexpat.version_info}')
|
||||||
|
def test_simple_xml_chunk_5(self):
|
||||||
|
self.test_simple_xml(chunk_size=5, flush=True)
|
||||||
|
|
||||||
|
@@ -1643,6 +1647,9 @@ class XMLPullParserTest(unittest.TestCas
|
||||||
|
|
||||||
|
self.assert_event_tags(parser, [('end', 'doc')])
|
||||||
|
|
||||||
|
+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
|
||||||
|
+ f'Expat {pyexpat.version_info} does not '
|
||||||
|
+ 'support reparse deferral')
|
||||||
|
def test_flush_reparse_deferral_disabled(self):
|
||||||
|
parser = ET.XMLPullParser(events=('start', 'end'))
|
||||||
|
|
||||||
1891
CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch
Normal file
1891
CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch
Normal file
File diff suppressed because it is too large
Load Diff
238
CVE-2025-6069-quad-complex-HTMLParser.patch
Normal file
238
CVE-2025-6069-quad-complex-HTMLParser.patch
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
From 2a6869c71a3132eff9c7be96db9bdca48b3636aa Mon Sep 17 00:00:00 2001
|
||||||
|
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
Date: Fri, 13 Jun 2025 19:57:48 +0300
|
||||||
|
Subject: [PATCH] [3.9] gh-135462: Fix quadratic complexity in processing
|
||||||
|
special input in HTMLParser (GH-135464)
|
||||||
|
|
||||||
|
End-of-file errors are now handled according to the HTML5 specs --
|
||||||
|
comments and declarations are automatically closed, tags are ignored.
|
||||||
|
(cherry picked from commit 6eb6c5dbfb528bd07d77b60fd71fd05d81d45c41)
|
||||||
|
|
||||||
|
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
---
|
||||||
|
Lib/html/parser.py | 41 +++-
|
||||||
|
Lib/test/test_htmlparser.py | 95 ++++++++--
|
||||||
|
Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst | 4
|
||||||
|
3 files changed, 117 insertions(+), 23 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst
|
||||||
|
|
||||||
|
Index: Python-3.9.23/Lib/html/parser.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.9.23.orig/Lib/html/parser.py 2025-07-02 18:10:23.763249887 +0200
|
||||||
|
+++ Python-3.9.23/Lib/html/parser.py 2025-07-02 18:10:29.124564834 +0200
|
||||||
|
@@ -25,6 +25,7 @@
|
||||||
|
charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]')
|
||||||
|
|
||||||
|
starttagopen = re.compile('<[a-zA-Z]')
|
||||||
|
+endtagopen = re.compile('</[a-zA-Z]')
|
||||||
|
piclose = re.compile('>')
|
||||||
|
commentclose = re.compile(r'--\s*>')
|
||||||
|
# Note:
|
||||||
|
@@ -176,7 +177,7 @@
|
||||||
|
k = self.parse_pi(i)
|
||||||
|
elif startswith("<!", i):
|
||||||
|
k = self.parse_html_declaration(i)
|
||||||
|
- elif (i + 1) < n:
|
||||||
|
+ elif (i + 1) < n or end:
|
||||||
|
self.handle_data("<")
|
||||||
|
k = i + 1
|
||||||
|
else:
|
||||||
|
@@ -184,17 +185,35 @@
|
||||||
|
if k < 0:
|
||||||
|
if not end:
|
||||||
|
break
|
||||||
|
- k = rawdata.find('>', i + 1)
|
||||||
|
- if k < 0:
|
||||||
|
- k = rawdata.find('<', i + 1)
|
||||||
|
- if k < 0:
|
||||||
|
- k = i + 1
|
||||||
|
+ if starttagopen.match(rawdata, i): # < + letter
|
||||||
|
+ pass
|
||||||
|
+ elif startswith("</", i):
|
||||||
|
+ if i + 2 == n:
|
||||||
|
+ self.handle_data("</")
|
||||||
|
+ elif endtagopen.match(rawdata, i): # </ + letter
|
||||||
|
+ pass
|
||||||
|
+ else:
|
||||||
|
+ # bogus comment
|
||||||
|
+ self.handle_comment(rawdata[i+2:])
|
||||||
|
+ elif startswith("<!--", i):
|
||||||
|
+ j = n
|
||||||
|
+ for suffix in ("--!", "--", "-"):
|
||||||
|
+ if rawdata.endswith(suffix, i+4):
|
||||||
|
+ j -= len(suffix)
|
||||||
|
+ break
|
||||||
|
+ self.handle_comment(rawdata[i+4:j])
|
||||||
|
+ elif startswith("<![CDATA[", i):
|
||||||
|
+ self.unknown_decl(rawdata[i+3:])
|
||||||
|
+ elif rawdata[i:i+9].lower() == '<!doctype':
|
||||||
|
+ self.handle_decl(rawdata[i+2:])
|
||||||
|
+ elif startswith("<!", i):
|
||||||
|
+ # bogus comment
|
||||||
|
+ self.handle_comment(rawdata[i+2:])
|
||||||
|
+ elif startswith("<?", i):
|
||||||
|
+ self.handle_pi(rawdata[i+2:])
|
||||||
|
else:
|
||||||
|
- k += 1
|
||||||
|
- if self.convert_charrefs and not self.cdata_elem:
|
||||||
|
- self.handle_data(unescape(rawdata[i:k]))
|
||||||
|
- else:
|
||||||
|
- self.handle_data(rawdata[i:k])
|
||||||
|
+ raise AssertionError("we should not get here!")
|
||||||
|
+ k = n
|
||||||
|
i = self.updatepos(i, k)
|
||||||
|
elif startswith("&#", i):
|
||||||
|
match = charref.match(rawdata, i)
|
||||||
|
Index: Python-3.9.23/Lib/test/test_htmlparser.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.9.23.orig/Lib/test/test_htmlparser.py 2025-07-02 18:10:25.136241201 +0200
|
||||||
|
+++ Python-3.9.23/Lib/test/test_htmlparser.py 2025-07-02 18:10:29.124805368 +0200
|
||||||
|
@@ -4,6 +4,8 @@
|
||||||
|
import pprint
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
+from test import support
|
||||||
|
+
|
||||||
|
|
||||||
|
class EventCollector(html.parser.HTMLParser):
|
||||||
|
|
||||||
|
@@ -391,28 +393,34 @@
|
||||||
|
('data', '<'),
|
||||||
|
('starttag', 'bc<', [('a', None)]),
|
||||||
|
('endtag', 'html'),
|
||||||
|
- ('data', '\n<img src="URL>'),
|
||||||
|
- ('comment', '/img'),
|
||||||
|
- ('endtag', 'html<')])
|
||||||
|
+ ('data', '\n')])
|
||||||
|
|
||||||
|
def test_starttag_junk_chars(self):
|
||||||
|
+ self._run_check("<", [('data', '<')])
|
||||||
|
+ self._run_check("<>", [('data', '<>')])
|
||||||
|
+ self._run_check("< >", [('data', '< >')])
|
||||||
|
+ self._run_check("< ", [('data', '< ')])
|
||||||
|
self._run_check("</>", [])
|
||||||
|
+ self._run_check("<$>", [('data', '<$>')])
|
||||||
|
self._run_check("</$>", [('comment', '$')])
|
||||||
|
self._run_check("</", [('data', '</')])
|
||||||
|
- self._run_check("</a", [('data', '</a')])
|
||||||
|
+ self._run_check("</a", [])
|
||||||
|
+ self._run_check("</ a>", [('endtag', 'a')])
|
||||||
|
+ self._run_check("</ a", [('comment', ' a')])
|
||||||
|
self._run_check("<a<a>", [('starttag', 'a<a', [])])
|
||||||
|
self._run_check("</a<a>", [('endtag', 'a<a')])
|
||||||
|
- self._run_check("<!", [('data', '<!')])
|
||||||
|
- self._run_check("<a", [('data', '<a')])
|
||||||
|
- self._run_check("<a foo='bar'", [('data', "<a foo='bar'")])
|
||||||
|
- self._run_check("<a foo='bar", [('data', "<a foo='bar")])
|
||||||
|
- self._run_check("<a foo='>'", [('data', "<a foo='>'")])
|
||||||
|
- self._run_check("<a foo='>", [('data', "<a foo='>")])
|
||||||
|
+ self._run_check("<!", [('comment', '')])
|
||||||
|
+ self._run_check("<a", [])
|
||||||
|
+ self._run_check("<a foo='bar'", [])
|
||||||
|
+ self._run_check("<a foo='bar", [])
|
||||||
|
+ self._run_check("<a foo='>'", [])
|
||||||
|
+ self._run_check("<a foo='>", [])
|
||||||
|
self._run_check("<a$>", [('starttag', 'a$', [])])
|
||||||
|
self._run_check("<a$b>", [('starttag', 'a$b', [])])
|
||||||
|
self._run_check("<a$b/>", [('startendtag', 'a$b', [])])
|
||||||
|
self._run_check("<a$b >", [('starttag', 'a$b', [])])
|
||||||
|
self._run_check("<a$b />", [('startendtag', 'a$b', [])])
|
||||||
|
+ self._run_check("</a$b>", [('endtag', 'a$b')])
|
||||||
|
|
||||||
|
def test_slashes_in_starttag(self):
|
||||||
|
self._run_check('<a foo="var"/>', [('startendtag', 'a', [('foo', 'var')])])
|
||||||
|
@@ -537,13 +545,56 @@
|
||||||
|
for html, expected in data:
|
||||||
|
self._run_check(html, expected)
|
||||||
|
|
||||||
|
- def test_broken_comments(self):
|
||||||
|
- html = ('<! not really a comment >'
|
||||||
|
+ def test_eof_in_comments(self):
|
||||||
|
+ data = [
|
||||||
|
+ ('<!--', [('comment', '')]),
|
||||||
|
+ ('<!---', [('comment', '')]),
|
||||||
|
+ ('<!----', [('comment', '')]),
|
||||||
|
+ ('<!-----', [('comment', '-')]),
|
||||||
|
+ ('<!------', [('comment', '--')]),
|
||||||
|
+ ('<!----!', [('comment', '')]),
|
||||||
|
+ ('<!---!', [('comment', '-!')]),
|
||||||
|
+ ('<!---!>', [('comment', '-!>')]),
|
||||||
|
+ ('<!--foo', [('comment', 'foo')]),
|
||||||
|
+ ('<!--foo-', [('comment', 'foo')]),
|
||||||
|
+ ('<!--foo--', [('comment', 'foo')]),
|
||||||
|
+ ('<!--foo--!', [('comment', 'foo')]),
|
||||||
|
+ ('<!--<!--', [('comment', '<!')]),
|
||||||
|
+ ('<!--<!--!', [('comment', '<!')]),
|
||||||
|
+ ]
|
||||||
|
+ for html, expected in data:
|
||||||
|
+ self._run_check(html, expected)
|
||||||
|
+
|
||||||
|
+ def test_eof_in_declarations(self):
|
||||||
|
+ data = [
|
||||||
|
+ ('<!', [('comment', '')]),
|
||||||
|
+ ('<!-', [('comment', '-')]),
|
||||||
|
+ ('<![', [('comment', '[')]),
|
||||||
|
+ ('<![CDATA[', [('unknown decl', 'CDATA[')]),
|
||||||
|
+ ('<![CDATA[x', [('unknown decl', 'CDATA[x')]),
|
||||||
|
+ ('<![CDATA[x]', [('unknown decl', 'CDATA[x]')]),
|
||||||
|
+ ('<![CDATA[x]]', [('unknown decl', 'CDATA[x]]')]),
|
||||||
|
+ ('<!DOCTYPE', [('decl', 'DOCTYPE')]),
|
||||||
|
+ ('<!DOCTYPE ', [('decl', 'DOCTYPE ')]),
|
||||||
|
+ ('<!DOCTYPE html', [('decl', 'DOCTYPE html')]),
|
||||||
|
+ ('<!DOCTYPE html ', [('decl', 'DOCTYPE html ')]),
|
||||||
|
+ ('<!DOCTYPE html PUBLIC', [('decl', 'DOCTYPE html PUBLIC')]),
|
||||||
|
+ ('<!DOCTYPE html PUBLIC "foo', [('decl', 'DOCTYPE html PUBLIC "foo')]),
|
||||||
|
+ ('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "foo',
|
||||||
|
+ [('decl', 'DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "foo')]),
|
||||||
|
+ ]
|
||||||
|
+ for html, expected in data:
|
||||||
|
+ self._run_check(html, expected)
|
||||||
|
+
|
||||||
|
+ def test_bogus_comments(self):
|
||||||
|
+ html = ('<!ELEMENT br EMPTY>'
|
||||||
|
+ '<! not really a comment >'
|
||||||
|
'<! not a comment either -->'
|
||||||
|
'<! -- close enough -->'
|
||||||
|
'<!><!<-- this was an empty comment>'
|
||||||
|
'<!!! another bogus comment !!!>')
|
||||||
|
expected = [
|
||||||
|
+ ('comment', 'ELEMENT br EMPTY'),
|
||||||
|
('comment', ' not really a comment '),
|
||||||
|
('comment', ' not a comment either --'),
|
||||||
|
('comment', ' -- close enough --'),
|
||||||
|
@@ -598,6 +649,26 @@
|
||||||
|
('endtag', 'a'), ('data', ' bar & baz')]
|
||||||
|
)
|
||||||
|
|
||||||
|
+ @support.requires_resource('cpu')
|
||||||
|
+ def test_eof_no_quadratic_complexity(self):
|
||||||
|
+ # Each of these examples used to take about an hour.
|
||||||
|
+ # Now they take a fraction of a second.
|
||||||
|
+ def check(source):
|
||||||
|
+ parser = html.parser.HTMLParser()
|
||||||
|
+ parser.feed(source)
|
||||||
|
+ parser.close()
|
||||||
|
+ n = 120_000
|
||||||
|
+ check("<a " * n)
|
||||||
|
+ check("<a a=" * n)
|
||||||
|
+ check("</a " * 14 * n)
|
||||||
|
+ check("</a a=" * 11 * n)
|
||||||
|
+ check("<!--" * 4 * n)
|
||||||
|
+ check("<!" * 60 * n)
|
||||||
|
+ check("<?" * 19 * n)
|
||||||
|
+ check("</$" * 15 * n)
|
||||||
|
+ check("<![CDATA[" * 9 * n)
|
||||||
|
+ check("<!doctype" * 35 * n)
|
||||||
|
+
|
||||||
|
|
||||||
|
class AttributesTestCase(TestCaseBase):
|
||||||
|
|
||||||
|
Index: Python-3.9.23/Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ Python-3.9.23/Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst 2025-07-02 18:10:29.125044785 +0200
|
||||||
|
@@ -0,0 +1,4 @@
|
||||||
|
+Fix quadratic complexity in processing specially crafted input in
|
||||||
|
+:class:`html.parser.HTMLParser`. End-of-file errors are now handled according
|
||||||
|
+to the HTML5 specs -- comments and declarations are automatically closed,
|
||||||
|
+tags are ignored.
|
||||||
212
CVE-2025-8194-tarfile-no-neg-offsets.patch
Normal file
212
CVE-2025-8194-tarfile-no-neg-offsets.patch
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
From ea07f6b7faccc71c3ce4edeb88a8322f3453d0ff Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexander Urieles <aeurielesn@users.noreply.github.com>
|
||||||
|
Date: Mon, 28 Jul 2025 17:37:26 +0200
|
||||||
|
Subject: [PATCH] [3.9] gh-130577: tarfile now validates archives to ensure
|
||||||
|
member offsets are non-negative (GH-137027) (cherry picked from commit
|
||||||
|
7040aa54f14676938970e10c5f74ea93cd56aa38)
|
||||||
|
|
||||||
|
Co-authored-by: Alexander Urieles <aeurielesn@users.noreply.github.com>
|
||||||
|
Co-authored-by: Gregory P. Smith <greg@krypto.org>
|
||||||
|
---
|
||||||
|
Lib/tarfile.py | 3
|
||||||
|
Lib/test/test_tarfile.py | 156 ++++++++++
|
||||||
|
Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3
|
||||||
|
3 files changed, 162 insertions(+)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
|
||||||
|
|
||||||
|
Index: Python-3.9.23/Lib/tarfile.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.9.23.orig/Lib/tarfile.py 2025-08-02 17:56:38.706191816 +0200
|
||||||
|
+++ Python-3.9.23/Lib/tarfile.py 2025-08-02 17:56:43.118456301 +0200
|
||||||
|
@@ -1601,6 +1601,9 @@
|
||||||
|
"""Round up a byte count by BLOCKSIZE and return it,
|
||||||
|
e.g. _block(834) => 1024.
|
||||||
|
"""
|
||||||
|
+ # Only non-negative offsets are allowed
|
||||||
|
+ if count < 0:
|
||||||
|
+ raise InvalidHeaderError("invalid offset")
|
||||||
|
blocks, remainder = divmod(count, BLOCKSIZE)
|
||||||
|
if remainder:
|
||||||
|
blocks += 1
|
||||||
|
Index: Python-3.9.23/Lib/test/test_tarfile.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.9.23.orig/Lib/test/test_tarfile.py 2025-08-02 17:56:40.012303862 +0200
|
||||||
|
+++ Python-3.9.23/Lib/test/test_tarfile.py 2025-08-02 17:56:56.856747140 +0200
|
||||||
|
@@ -48,6 +48,7 @@
|
||||||
|
xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
|
||||||
|
tmpname = os.path.join(TEMPDIR, "tmp.tar")
|
||||||
|
dotlessname = os.path.join(TEMPDIR, "testtar")
|
||||||
|
+SPACE = b" "
|
||||||
|
|
||||||
|
sha256_regtype = (
|
||||||
|
"e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
|
||||||
|
@@ -4234,6 +4235,161 @@
|
||||||
|
self.expect_exception(TypeError) # errorlevel is not int
|
||||||
|
|
||||||
|
|
||||||
|
+class OffsetValidationTests(unittest.TestCase):
|
||||||
|
+ tarname = tmpname
|
||||||
|
+ invalid_posix_header = (
|
||||||
|
+ # name: 100 bytes
|
||||||
|
+ tarfile.NUL * tarfile.LENGTH_NAME
|
||||||
|
+ # mode, space, null terminator: 8 bytes
|
||||||
|
+ + b"000755" + SPACE + tarfile.NUL
|
||||||
|
+ # uid, space, null terminator: 8 bytes
|
||||||
|
+ + b"000001" + SPACE + tarfile.NUL
|
||||||
|
+ # gid, space, null terminator: 8 bytes
|
||||||
|
+ + b"000001" + SPACE + tarfile.NUL
|
||||||
|
+ # size, space: 12 bytes
|
||||||
|
+ + b"\xff" * 11 + SPACE
|
||||||
|
+ # mtime, space: 12 bytes
|
||||||
|
+ + tarfile.NUL * 11 + SPACE
|
||||||
|
+ # chksum: 8 bytes
|
||||||
|
+ + b"0011407" + tarfile.NUL
|
||||||
|
+ # type: 1 byte
|
||||||
|
+ + tarfile.REGTYPE
|
||||||
|
+ # linkname: 100 bytes
|
||||||
|
+ + tarfile.NUL * tarfile.LENGTH_LINK
|
||||||
|
+ # magic: 6 bytes, version: 2 bytes
|
||||||
|
+ + tarfile.POSIX_MAGIC
|
||||||
|
+ # uname: 32 bytes
|
||||||
|
+ + tarfile.NUL * 32
|
||||||
|
+ # gname: 32 bytes
|
||||||
|
+ + tarfile.NUL * 32
|
||||||
|
+ # devmajor, space, null terminator: 8 bytes
|
||||||
|
+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
|
||||||
|
+ # devminor, space, null terminator: 8 bytes
|
||||||
|
+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
|
||||||
|
+ # prefix: 155 bytes
|
||||||
|
+ + tarfile.NUL * tarfile.LENGTH_PREFIX
|
||||||
|
+ # padding: 12 bytes
|
||||||
|
+ + tarfile.NUL * 12
|
||||||
|
+ )
|
||||||
|
+ invalid_gnu_header = (
|
||||||
|
+ # name: 100 bytes
|
||||||
|
+ tarfile.NUL * tarfile.LENGTH_NAME
|
||||||
|
+ # mode, null terminator: 8 bytes
|
||||||
|
+ + b"0000755" + tarfile.NUL
|
||||||
|
+ # uid, null terminator: 8 bytes
|
||||||
|
+ + b"0000001" + tarfile.NUL
|
||||||
|
+ # gid, space, null terminator: 8 bytes
|
||||||
|
+ + b"0000001" + tarfile.NUL
|
||||||
|
+ # size, space: 12 bytes
|
||||||
|
+ + b"\xff" * 11 + SPACE
|
||||||
|
+ # mtime, space: 12 bytes
|
||||||
|
+ + tarfile.NUL * 11 + SPACE
|
||||||
|
+ # chksum: 8 bytes
|
||||||
|
+ + b"0011327" + tarfile.NUL
|
||||||
|
+ # type: 1 byte
|
||||||
|
+ + tarfile.REGTYPE
|
||||||
|
+ # linkname: 100 bytes
|
||||||
|
+ + tarfile.NUL * tarfile.LENGTH_LINK
|
||||||
|
+ # magic: 8 bytes
|
||||||
|
+ + tarfile.GNU_MAGIC
|
||||||
|
+ # uname: 32 bytes
|
||||||
|
+ + tarfile.NUL * 32
|
||||||
|
+ # gname: 32 bytes
|
||||||
|
+ + tarfile.NUL * 32
|
||||||
|
+ # devmajor, null terminator: 8 bytes
|
||||||
|
+ + tarfile.NUL * 8
|
||||||
|
+ # devminor, null terminator: 8 bytes
|
||||||
|
+ + tarfile.NUL * 8
|
||||||
|
+ # padding: 167 bytes
|
||||||
|
+ + tarfile.NUL * 167
|
||||||
|
+ )
|
||||||
|
+ invalid_v7_header = (
|
||||||
|
+ # name: 100 bytes
|
||||||
|
+ tarfile.NUL * tarfile.LENGTH_NAME
|
||||||
|
+ # mode, space, null terminator: 8 bytes
|
||||||
|
+ + b"000755" + SPACE + tarfile.NUL
|
||||||
|
+ # uid, space, null terminator: 8 bytes
|
||||||
|
+ + b"000001" + SPACE + tarfile.NUL
|
||||||
|
+ # gid, space, null terminator: 8 bytes
|
||||||
|
+ + b"000001" + SPACE + tarfile.NUL
|
||||||
|
+ # size, space: 12 bytes
|
||||||
|
+ + b"\xff" * 11 + SPACE
|
||||||
|
+ # mtime, space: 12 bytes
|
||||||
|
+ + tarfile.NUL * 11 + SPACE
|
||||||
|
+ # chksum: 8 bytes
|
||||||
|
+ + b"0010070" + tarfile.NUL
|
||||||
|
+ # type: 1 byte
|
||||||
|
+ + tarfile.REGTYPE
|
||||||
|
+ # linkname: 100 bytes
|
||||||
|
+ + tarfile.NUL * tarfile.LENGTH_LINK
|
||||||
|
+ # padding: 255 bytes
|
||||||
|
+ + tarfile.NUL * 255
|
||||||
|
+ )
|
||||||
|
+ valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT)
|
||||||
|
+ data_block = b"\xff" * tarfile.BLOCKSIZE
|
||||||
|
+
|
||||||
|
+ def _write_buffer(self, buffer):
|
||||||
|
+ with open(self.tarname, "wb") as f:
|
||||||
|
+ f.write(buffer)
|
||||||
|
+
|
||||||
|
+ def _get_members(self, ignore_zeros=None):
|
||||||
|
+ with open(self.tarname, "rb") as f:
|
||||||
|
+ with tarfile.open(
|
||||||
|
+ mode="r", fileobj=f, ignore_zeros=ignore_zeros
|
||||||
|
+ ) as tar:
|
||||||
|
+ return tar.getmembers()
|
||||||
|
+
|
||||||
|
+ def _assert_raises_read_error_exception(self):
|
||||||
|
+ with self.assertRaisesRegex(
|
||||||
|
+ tarfile.ReadError, "file could not be opened successfully"
|
||||||
|
+ ):
|
||||||
|
+ self._get_members()
|
||||||
|
+
|
||||||
|
+ def test_invalid_offset_header_validations(self):
|
||||||
|
+ for tar_format, invalid_header in (
|
||||||
|
+ ("posix", self.invalid_posix_header),
|
||||||
|
+ ("gnu", self.invalid_gnu_header),
|
||||||
|
+ ("v7", self.invalid_v7_header),
|
||||||
|
+ ):
|
||||||
|
+ with self.subTest(format=tar_format):
|
||||||
|
+ self._write_buffer(invalid_header)
|
||||||
|
+ self._assert_raises_read_error_exception()
|
||||||
|
+
|
||||||
|
+ def test_early_stop_at_invalid_offset_header(self):
|
||||||
|
+ buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header
|
||||||
|
+ self._write_buffer(buffer)
|
||||||
|
+ members = self._get_members()
|
||||||
|
+ self.assertEqual(len(members), 1)
|
||||||
|
+ self.assertEqual(members[0].name, "filename")
|
||||||
|
+ self.assertEqual(members[0].offset, 0)
|
||||||
|
+
|
||||||
|
+ def test_ignore_invalid_archive(self):
|
||||||
|
+ # 3 invalid headers with their respective data
|
||||||
|
+ buffer = (self.invalid_gnu_header + self.data_block) * 3
|
||||||
|
+ self._write_buffer(buffer)
|
||||||
|
+ members = self._get_members(ignore_zeros=True)
|
||||||
|
+ self.assertEqual(len(members), 0)
|
||||||
|
+
|
||||||
|
+ def test_ignore_invalid_offset_headers(self):
|
||||||
|
+ for first_block, second_block, expected_offset in (
|
||||||
|
+ (
|
||||||
|
+ (self.valid_gnu_header),
|
||||||
|
+ (self.invalid_gnu_header + self.data_block),
|
||||||
|
+ 0,
|
||||||
|
+ ),
|
||||||
|
+ (
|
||||||
|
+ (self.invalid_gnu_header + self.data_block),
|
||||||
|
+ (self.valid_gnu_header),
|
||||||
|
+ 1024,
|
||||||
|
+ ),
|
||||||
|
+ ):
|
||||||
|
+ self._write_buffer(first_block + second_block)
|
||||||
|
+ members = self._get_members(ignore_zeros=True)
|
||||||
|
+ self.assertEqual(len(members), 1)
|
||||||
|
+ self.assertEqual(members[0].name, "filename")
|
||||||
|
+ self.assertEqual(members[0].offset, expected_offset)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def setUpModule():
|
||||||
|
support.unlink(TEMPDIR)
|
||||||
|
os.makedirs(TEMPDIR)
|
||||||
|
Index: Python-3.9.23/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||||
|
+++ Python-3.9.23/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst 2025-08-02 17:56:43.119864870 +0200
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+:mod:`tarfile` now validates archives to ensure member offsets are
|
||||||
|
+non-negative. (Contributed by Alexander Enrique Urieles Nieto in
|
||||||
|
+:gh:`130577`.)
|
||||||
57
F00251-change-user-install-location.patch
Normal file
57
F00251-change-user-install-location.patch
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
From 910f38d9768d39d4d31426743ae4081ed1ab66b6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Michal Cyprian <m.cyprian@gmail.com>
|
||||||
|
Date: Mon, 26 Jun 2017 16:32:56 +0200
|
||||||
|
Subject: [PATCH] 00251: Change user install location
|
||||||
|
|
||||||
|
Set values of prefix and exec_prefix in distutils install command
|
||||||
|
to /usr/local if executable is /usr/bin/python* and RPM build
|
||||||
|
is not detected to make pip and distutils install into separate location.
|
||||||
|
|
||||||
|
Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
---
|
||||||
|
Lib/distutils/command/install.py | 15 +++++++++++++--
|
||||||
|
Lib/site.py | 9 ++++++++-
|
||||||
|
2 files changed, 21 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
--- a/Lib/distutils/command/install.py
|
||||||
|
+++ b/Lib/distutils/command/install.py
|
||||||
|
@@ -419,8 +419,19 @@ class install(Command):
|
||||||
|
raise DistutilsOptionError(
|
||||||
|
"must not supply exec-prefix without prefix")
|
||||||
|
|
||||||
|
- self.prefix = os.path.normpath(sys.prefix)
|
||||||
|
- self.exec_prefix = os.path.normpath(sys.exec_prefix)
|
||||||
|
+ # self.prefix is set to sys.prefix + /local/
|
||||||
|
+ # if neither RPM build nor virtual environment is
|
||||||
|
+ # detected to make pip and distutils install packages
|
||||||
|
+ # into the separate location.
|
||||||
|
+ if (not (hasattr(sys, 'real_prefix') or
|
||||||
|
+ sys.prefix != sys.base_prefix) and
|
||||||
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
|
+ addition = "/local"
|
||||||
|
+ else:
|
||||||
|
+ addition = ""
|
||||||
|
+
|
||||||
|
+ self.prefix = os.path.normpath(sys.prefix) + addition
|
||||||
|
+ self.exec_prefix = os.path.normpath(sys.exec_prefix) + addition
|
||||||
|
|
||||||
|
else:
|
||||||
|
if self.exec_prefix is None:
|
||||||
|
--- a/Lib/site.py
|
||||||
|
+++ b/Lib/site.py
|
||||||
|
@@ -362,7 +362,14 @@ def getsitepackages(prefixes=None):
|
||||||
|
return sitepackages
|
||||||
|
|
||||||
|
def addsitepackages(known_paths, prefixes=None):
|
||||||
|
- """Add site-packages to sys.path"""
|
||||||
|
+ """Add site-packages to sys.path
|
||||||
|
+
|
||||||
|
+ '/usr/local' is included in PREFIXES if RPM build is not detected
|
||||||
|
+ to make packages installed into this location visible.
|
||||||
|
+
|
||||||
|
+ """
|
||||||
|
+ if ENABLE_USER_SITE and 'RPM_BUILD_ROOT' not in os.environ:
|
||||||
|
+ PREFIXES.insert(0, "/usr/local")
|
||||||
|
for sitedir in getsitepackages(prefixes):
|
||||||
|
if os.path.isdir(sitedir):
|
||||||
|
addsitedir(sitedir, known_paths)
|
||||||
26
PACKAGING-NOTES
Normal file
26
PACKAGING-NOTES
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Notes for packagers of Python3
|
||||||
|
==============================
|
||||||
|
|
||||||
|
0. Faster build turnaround
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
By default, python builds with profile-guided optimization. This needs
|
||||||
|
an additional run of the test suite and it is generally slow.
|
||||||
|
PGO build takes around 50 minutes.
|
||||||
|
|
||||||
|
For development, use "--without profileopt" option to disable PGO. This
|
||||||
|
shortens the build time to ~5 minutes including test suite.
|
||||||
|
|
||||||
|
1. import_failed.map
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
This is a mechanism installed as part of python3-base, that places shim modules
|
||||||
|
on python's path (through a generated zzzz-import-failed-hooks.pth file, so that
|
||||||
|
it is imported as much at the end as makes sense; and an _import_failed subdir
|
||||||
|
of /usr/lib/pythonX.Y). Then when the user tries to import a module that is part
|
||||||
|
of a subpackage, the ImportError will contain a helpful message telling them
|
||||||
|
which missing subpackage to install.
|
||||||
|
|
||||||
|
This can sometimes cause problems on non-standard configurations, if the pth
|
||||||
|
gets included too early (for instance if you are using a script to include all
|
||||||
|
pths by hand in some strange order). Just something to look out for.
|
||||||
BIN
Python-3.9.23.tar.xz
(Stored with Git LFS)
Normal file
BIN
Python-3.9.23.tar.xz
(Stored with Git LFS)
Normal file
Binary file not shown.
1
Python-3.9.23.tar.xz.sigstore
Normal file
1
Python-3.9.23.tar.xz.sigstore
Normal file
File diff suppressed because one or more lines are too long
BIN
Python-3.9.24.tar.xz
(Stored with Git LFS)
Normal file
BIN
Python-3.9.24.tar.xz
(Stored with Git LFS)
Normal file
Binary file not shown.
1
Python-3.9.24.tar.xz.sigstore
Normal file
1
Python-3.9.24.tar.xz.sigstore
Normal file
File diff suppressed because one or more lines are too long
43
README.SUSE
Normal file
43
README.SUSE
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
Python 3 in SUSE
|
||||||
|
==============
|
||||||
|
|
||||||
|
* Subpackages *
|
||||||
|
|
||||||
|
Python 3 is split into several subpackages, based on external dependencies.
|
||||||
|
The main package 'python3' has soft dependencies on all subpackages needed to
|
||||||
|
assemble the standard library; however, these might not all be installed by default.
|
||||||
|
|
||||||
|
If you attempt to import a module that is currently not installed, an ImportError is thrown,
|
||||||
|
with instructions to install the missing subpackage. Installing the subpackage might result
|
||||||
|
in installing libraries that the subpackage requires to function.
|
||||||
|
|
||||||
|
|
||||||
|
* ensurepip *
|
||||||
|
|
||||||
|
The 'ensurepip' module from Python 3 standard library (PEP 453) is supposed to deploy
|
||||||
|
a bundled copy of the pip installer. This makes no sense in a managed distribution like SUSE.
|
||||||
|
Instead, you need to install package 'python3-pip'. Usually this will be installed automatically
|
||||||
|
with 'python3'.
|
||||||
|
|
||||||
|
Using 'ensurepip' when pip is not installed will result in an ImportError with instructions
|
||||||
|
to install 'python3-pip'.
|
||||||
|
|
||||||
|
|
||||||
|
* Documentation *
|
||||||
|
|
||||||
|
You can find documentation in seprarate packages: python3-doc and
|
||||||
|
python3-doc-pdf. These contan following documents:
|
||||||
|
|
||||||
|
Tutorial, What's New in Python, Global Module Index, Library Reference,
|
||||||
|
Macintosh Module Reference, Installing Python Modules, Distributing Python
|
||||||
|
Modules, Language Reference, Extending and Embedding, Python/C API,
|
||||||
|
Documenting Python
|
||||||
|
|
||||||
|
The python3-doc package constains many text files from source tarball.
|
||||||
|
|
||||||
|
|
||||||
|
* Interactive mode *
|
||||||
|
|
||||||
|
Interactive mode is by default enhanced with of history and command completion.
|
||||||
|
If you don't like these features, you can unset the PYTHONSTARTUP variable
|
||||||
|
in your .profile or disable it system wide in /etc/profile.d/python.sh.
|
||||||
4
_multibuild
Normal file
4
_multibuild
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<multibuild>
|
||||||
|
<package>base</package>
|
||||||
|
<package>doc</package>
|
||||||
|
</multibuild>
|
||||||
3
baselibs.conf
Normal file
3
baselibs.conf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
python39-base
|
||||||
|
python39
|
||||||
|
libpython3_9-1_0
|
||||||
3
bluez-devel-vendor.tar.xz
Normal file
3
bluez-devel-vendor.tar.xz
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d030d6ff641577625745b435f4a45e9025e11143e60d0bba7dddf53e8bf71941
|
||||||
|
size 24976
|
||||||
163
bpo-31046_ensurepip_honours_prefix.patch
Normal file
163
bpo-31046_ensurepip_honours_prefix.patch
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
From 5754521af1d51aa8e445cba07a093bbc0c88596d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Zackery Spytz <zspytz@gmail.com>
|
||||||
|
Date: Mon, 16 Dec 2019 18:24:08 -0700
|
||||||
|
Subject: [PATCH] bpo-31046: ensurepip does not honour the value of $(prefix)
|
||||||
|
|
||||||
|
Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
||||||
|
---
|
||||||
|
Doc/library/ensurepip.rst | 9 +++--
|
||||||
|
Lib/ensurepip/__init__.py | 18 +++++++---
|
||||||
|
Lib/test/test_ensurepip.py | 11 ++++++
|
||||||
|
Makefile.pre.in | 4 +-
|
||||||
|
Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst | 1
|
||||||
|
5 files changed, 34 insertions(+), 9 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst
|
||||||
|
|
||||||
|
--- a/Doc/library/ensurepip.rst
|
||||||
|
+++ b/Doc/library/ensurepip.rst
|
||||||
|
@@ -56,8 +56,9 @@ is at least as recent as the one bundled
|
||||||
|
By default, ``pip`` is installed into the current virtual environment
|
||||||
|
(if one is active) or into the system site packages (if there is no
|
||||||
|
active virtual environment). The installation location can be controlled
|
||||||
|
-through two additional command line options:
|
||||||
|
+through some additional command line options:
|
||||||
|
|
||||||
|
+* ``--prefix <dir>``: Installs ``pip`` using the given directory prefix.
|
||||||
|
* ``--root <dir>``: Installs ``pip`` relative to the given root directory
|
||||||
|
rather than the root of the currently active virtual environment (if any)
|
||||||
|
or the default root for the current Python installation.
|
||||||
|
@@ -89,7 +90,7 @@ Module API
|
||||||
|
Returns a string specifying the bundled version of pip that will be
|
||||||
|
installed when bootstrapping an environment.
|
||||||
|
|
||||||
|
-.. function:: bootstrap(root=None, upgrade=False, user=False, \
|
||||||
|
+.. function:: bootstrap(root=None, prefix=None, upgrade=False, user=False, \
|
||||||
|
altinstall=False, default_pip=False, \
|
||||||
|
verbosity=0)
|
||||||
|
|
||||||
|
@@ -99,6 +100,8 @@ Module API
|
||||||
|
If *root* is ``None``, then installation uses the default install location
|
||||||
|
for the current environment.
|
||||||
|
|
||||||
|
+ *prefix* specifies the directory prefix to use when installing.
|
||||||
|
+
|
||||||
|
*upgrade* indicates whether or not to upgrade an existing installation
|
||||||
|
of an earlier version of ``pip`` to the bundled version.
|
||||||
|
|
||||||
|
@@ -119,6 +122,8 @@ Module API
|
||||||
|
*verbosity* controls the level of output to :data:`sys.stdout` from the
|
||||||
|
bootstrapping operation.
|
||||||
|
|
||||||
|
+ .. versionchanged:: 3.9 the *prefix* parameter was added.
|
||||||
|
+
|
||||||
|
.. audit-event:: ensurepip.bootstrap root ensurepip.bootstrap
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
--- a/Lib/ensurepip/__init__.py
|
||||||
|
+++ b/Lib/ensurepip/__init__.py
|
||||||
|
@@ -57,27 +57,27 @@ def _disable_pip_configuration_settings(
|
||||||
|
os.environ['PIP_CONFIG_FILE'] = os.devnull
|
||||||
|
|
||||||
|
|
||||||
|
-def bootstrap(*, root=None, upgrade=False, user=False,
|
||||||
|
+def bootstrap(*, root=None, prefix=None, upgrade=False, user=False,
|
||||||
|
altinstall=False, default_pip=False,
|
||||||
|
verbosity=0):
|
||||||
|
"""
|
||||||
|
Bootstrap pip into the current Python installation (or the given root
|
||||||
|
- directory).
|
||||||
|
+ and directory prefix).
|
||||||
|
|
||||||
|
Note that calling this function will alter both sys.path and os.environ.
|
||||||
|
"""
|
||||||
|
# Discard the return value
|
||||||
|
- _bootstrap(root=root, upgrade=upgrade, user=user,
|
||||||
|
+ _bootstrap(root=root, prefix=prefix, upgrade=upgrade, user=user,
|
||||||
|
altinstall=altinstall, default_pip=default_pip,
|
||||||
|
verbosity=verbosity)
|
||||||
|
|
||||||
|
|
||||||
|
-def _bootstrap(*, root=None, upgrade=False, user=False,
|
||||||
|
+def _bootstrap(*, root=None, prefix=None, upgrade=False, user=False,
|
||||||
|
altinstall=False, default_pip=False,
|
||||||
|
verbosity=0):
|
||||||
|
"""
|
||||||
|
Bootstrap pip into the current Python installation (or the given root
|
||||||
|
- directory). Returns pip command status code.
|
||||||
|
+ and directory prefix). Returns pip command status code.
|
||||||
|
|
||||||
|
Note that calling this function will alter both sys.path and os.environ.
|
||||||
|
"""
|
||||||
|
@@ -120,6 +120,8 @@ def _bootstrap(*, root=None, upgrade=Fal
|
||||||
|
args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir]
|
||||||
|
if root:
|
||||||
|
args += ["--root", root]
|
||||||
|
+ if prefix:
|
||||||
|
+ args += ["--prefix", prefix]
|
||||||
|
if upgrade:
|
||||||
|
args += ["--upgrade"]
|
||||||
|
if user:
|
||||||
|
@@ -192,6 +194,11 @@ def _main(argv=None):
|
||||||
|
help="Install everything relative to this alternate root directory.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
+ "--prefix",
|
||||||
|
+ default=None,
|
||||||
|
+ help="Install everything using this prefix.",
|
||||||
|
+ )
|
||||||
|
+ parser.add_argument(
|
||||||
|
"--altinstall",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
@@ -210,6 +217,7 @@ def _main(argv=None):
|
||||||
|
|
||||||
|
return _bootstrap(
|
||||||
|
root=args.root,
|
||||||
|
+ prefix=args.prefix,
|
||||||
|
upgrade=args.upgrade,
|
||||||
|
user=args.user,
|
||||||
|
verbosity=args.verbosity,
|
||||||
|
--- a/Lib/test/test_ensurepip.py
|
||||||
|
+++ b/Lib/test/test_ensurepip.py
|
||||||
|
@@ -61,6 +61,17 @@ class TestBootstrap(EnsurepipMixin, unit
|
||||||
|
unittest.mock.ANY,
|
||||||
|
)
|
||||||
|
|
||||||
|
+ def test_bootstrapping_with_prefix(self):
|
||||||
|
+ ensurepip.bootstrap(prefix="/foo/bar/")
|
||||||
|
+ self.run_pip.assert_called_once_with(
|
||||||
|
+ [
|
||||||
|
+ "install", "--no-cache-dir", "--no-index", "--find-links",
|
||||||
|
+ unittest.mock.ANY, "--prefix", "/foo/bar/",
|
||||||
|
+ "setuptools", "pip",
|
||||||
|
+ ],
|
||||||
|
+ unittest.mock.ANY,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
def test_bootstrapping_with_user(self):
|
||||||
|
ensurepip.bootstrap(user=True)
|
||||||
|
|
||||||
|
--- a/Makefile.pre.in
|
||||||
|
+++ b/Makefile.pre.in
|
||||||
|
@@ -1263,7 +1263,7 @@ install: @FRAMEWORKINSTALLFIRST@ commoni
|
||||||
|
install|*) ensurepip="" ;; \
|
||||||
|
esac; \
|
||||||
|
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
||||||
|
- $$ensurepip --root=$(DESTDIR)/ ; \
|
||||||
|
+ $$ensurepip --root=$(DESTDIR)/ --prefix=$(prefix) ; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
altinstall: commoninstall
|
||||||
|
@@ -1273,7 +1273,7 @@ altinstall: commoninstall
|
||||||
|
install|*) ensurepip="--altinstall" ;; \
|
||||||
|
esac; \
|
||||||
|
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
||||||
|
- $$ensurepip --root=$(DESTDIR)/ ; \
|
||||||
|
+ $$ensurepip --root=$(DESTDIR)/ --prefix=$(prefix) ; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+A directory prefix can now be specified when using :mod:`ensurepip`.
|
||||||
102
bpo-37596-make-set-marshalling.patch
Normal file
102
bpo-37596-make-set-marshalling.patch
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
From 33d95c6facdfda3c8c0feffa7a99184e4abc2f63 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Brandt Bucher <brandt@python.org>
|
||||||
|
Date: Wed, 25 Aug 2021 04:14:34 -0700
|
||||||
|
Subject: [PATCH] bpo-37596: Make `set` and `frozenset` marshalling
|
||||||
|
deterministic (GH-27926)
|
||||||
|
|
||||||
|
---
|
||||||
|
Lib/test/test_marshal.py | 26 ++++++++
|
||||||
|
Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst | 2
|
||||||
|
Python/marshal.c | 32 ++++++++++
|
||||||
|
3 files changed, 60 insertions(+)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst
|
||||||
|
|
||||||
|
--- a/Lib/test/test_marshal.py
|
||||||
|
+++ b/Lib/test/test_marshal.py
|
||||||
|
@@ -1,4 +1,5 @@
|
||||||
|
from test import support
|
||||||
|
+from test.support.script_helper import assert_python_ok
|
||||||
|
import array
|
||||||
|
import io
|
||||||
|
import marshal
|
||||||
|
@@ -324,6 +325,31 @@ class BugsTestCase(unittest.TestCase):
|
||||||
|
for i in range(len(data)):
|
||||||
|
self.assertRaises(EOFError, marshal.loads, data[0: i])
|
||||||
|
|
||||||
|
+ def test_deterministic_sets(self):
|
||||||
|
+ # bpo-37596: To support reproducible builds, sets and frozensets need to
|
||||||
|
+ # have their elements serialized in a consistent order (even when they
|
||||||
|
+ # have been scrambled by hash randomization):
|
||||||
|
+ for kind in ("set", "frozenset"):
|
||||||
|
+ for elements in (
|
||||||
|
+ "float('nan'), b'a', b'b', b'c', 'x', 'y', 'z'",
|
||||||
|
+ # Also test for bad interactions with backreferencing:
|
||||||
|
+ "('string', 1), ('string', 2), ('string', 3)",
|
||||||
|
+ ):
|
||||||
|
+ s = f"{kind}([{elements}])"
|
||||||
|
+ with self.subTest(s):
|
||||||
|
+ # First, make sure that our test case still has different
|
||||||
|
+ # orders under hash seeds 0 and 1. If this check fails, we
|
||||||
|
+ # need to update this test with different elements:
|
||||||
|
+ args = ["-c", f"print({s})"]
|
||||||
|
+ _, repr_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0")
|
||||||
|
+ _, repr_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1")
|
||||||
|
+ self.assertNotEqual(repr_0, repr_1)
|
||||||
|
+ # Then, perform the actual test:
|
||||||
|
+ args = ["-c", f"import marshal; print(marshal.dumps({s}))"]
|
||||||
|
+ _, dump_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0")
|
||||||
|
+ _, dump_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1")
|
||||||
|
+ self.assertEqual(dump_0, dump_1)
|
||||||
|
+
|
||||||
|
LARGE_SIZE = 2**31
|
||||||
|
pointer_size = 8 if sys.maxsize > 0xFFFFFFFF else 4
|
||||||
|
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+Ensure that :class:`set` and :class:`frozenset` objects are always
|
||||||
|
+:mod:`marshalled <marshal>` reproducibly.
|
||||||
|
--- a/Python/marshal.c
|
||||||
|
+++ b/Python/marshal.c
|
||||||
|
@@ -502,9 +502,41 @@ w_complex_object(PyObject *v, char flag,
|
||||||
|
W_TYPE(TYPE_SET, p);
|
||||||
|
n = PySet_GET_SIZE(v);
|
||||||
|
W_SIZE(n, p);
|
||||||
|
+ // bpo-37596: To support reproducible builds, sets and frozensets need
|
||||||
|
+ // to have their elements serialized in a consistent order (even when
|
||||||
|
+ // they have been scrambled by hash randomization). To ensure this, we
|
||||||
|
+ // use an order equivalent to sorted(v, key=marshal.dumps):
|
||||||
|
+ PyObject *pairs = PyList_New(0);
|
||||||
|
+ if (pairs == NULL) {
|
||||||
|
+ p->error = WFERR_NOMEMORY;
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
while (_PySet_NextEntry(v, &pos, &value, &hash)) {
|
||||||
|
+ PyObject *dump = PyMarshal_WriteObjectToString(value, p->version);
|
||||||
|
+ if (dump == NULL) {
|
||||||
|
+ p->error = WFERR_UNMARSHALLABLE;
|
||||||
|
+ goto anyset_done;
|
||||||
|
+ }
|
||||||
|
+ PyObject *pair = PyTuple_Pack(2, dump, value);
|
||||||
|
+ Py_DECREF(dump);
|
||||||
|
+ if (pair == NULL || PyList_Append(pairs, pair)) {
|
||||||
|
+ p->error = WFERR_NOMEMORY;
|
||||||
|
+ Py_XDECREF(pair);
|
||||||
|
+ goto anyset_done;
|
||||||
|
+ }
|
||||||
|
+ Py_DECREF(pair);
|
||||||
|
+ }
|
||||||
|
+ if (PyList_Sort(pairs)) {
|
||||||
|
+ p->error = WFERR_NOMEMORY;
|
||||||
|
+ goto anyset_done;
|
||||||
|
+ }
|
||||||
|
+ for (Py_ssize_t i = 0; i < n; i++) {
|
||||||
|
+ PyObject *pair = PyList_GET_ITEM(pairs, i);
|
||||||
|
+ value = PyTuple_GET_ITEM(pair, 1);
|
||||||
|
w_object(value, p);
|
||||||
|
}
|
||||||
|
+ anyset_done:
|
||||||
|
+ Py_DECREF(pairs);
|
||||||
|
}
|
||||||
|
else if (PyCode_Check(v)) {
|
||||||
|
PyCodeObject *co = (PyCodeObject *)v;
|
||||||
37
bso1227999-reproducible-builds.patch
Normal file
37
bso1227999-reproducible-builds.patch
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
From ac2b8869724d7a57d9b5efbdce2f20423214e8bb Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Bernhard M. Wiedemann" <bwiedemann@suse.de>
|
||||||
|
Date: Tue, 16 Jul 2024 21:39:33 +0200
|
||||||
|
Subject: [PATCH] Allow to override build date with SOURCE_DATE_EPOCH
|
||||||
|
|
||||||
|
to make builds reproducible.
|
||||||
|
See https://reproducible-builds.org/ for why this is good
|
||||||
|
and https://reproducible-builds.org/specs/source-date-epoch/
|
||||||
|
for the definition of this variable.
|
||||||
|
---
|
||||||
|
Doc/conf.py | 3 ++-
|
||||||
|
Doc/library/functions.rst | 2 +-
|
||||||
|
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/conf.py
|
||||||
|
+++ b/Doc/conf.py
|
||||||
|
@@ -80,7 +80,8 @@ html_short_title = '%s Documentation' %
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
-html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
+html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
|
||||||
|
+html_last_updated_fmt = time.strftime('%b %d, %Y (%H:%M UTC)', time.gmtime(html_time))
|
||||||
|
|
||||||
|
# Path to find HTML templates.
|
||||||
|
templates_path = ['tools/templates']
|
||||||
|
--- a/Doc/library/functions.rst
|
||||||
|
+++ b/Doc/library/functions.rst
|
||||||
|
@@ -1254,7 +1254,7 @@ are always available. They are listed h
|
||||||
|
(where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`,
|
||||||
|
and :mod:`shutil`.
|
||||||
|
|
||||||
|
- .. audit-event:: open file,mode,flags open
|
||||||
|
+ .. audit-event:: open path,mode,flags open
|
||||||
|
|
||||||
|
The ``mode`` and ``flags`` arguments may have been modified or inferred from
|
||||||
|
the original call.
|
||||||
36
decimal.patch
Normal file
36
decimal.patch
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
Modules/_decimal/_decimal.c | 4 ++--
|
||||||
|
setup.py | 2 +-
|
||||||
|
2 files changed, 3 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
--- a/Modules/_decimal/_decimal.c
|
||||||
|
+++ b/Modules/_decimal/_decimal.c
|
||||||
|
@@ -3279,7 +3279,7 @@ dec_format(PyObject *dec, PyObject *args
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size_t n = strlen(spec.dot);
|
||||||
|
- if (n > 1 || (n == 1 && !isascii((uchar)spec.dot[0]))) {
|
||||||
|
+ if (n > 1 || (n == 1 && !isascii((unsigned char)spec.dot[0]))) {
|
||||||
|
/* fix locale dependent non-ascii characters */
|
||||||
|
dot = dotsep_as_utf8(spec.dot);
|
||||||
|
if (dot == NULL) {
|
||||||
|
@@ -3288,7 +3288,7 @@ dec_format(PyObject *dec, PyObject *args
|
||||||
|
spec.dot = PyBytes_AS_STRING(dot);
|
||||||
|
}
|
||||||
|
n = strlen(spec.sep);
|
||||||
|
- if (n > 1 || (n == 1 && !isascii((uchar)spec.sep[0]))) {
|
||||||
|
+ if (n > 1 || (n == 1 && !isascii((unsigned char)spec.sep[0]))) {
|
||||||
|
/* fix locale dependent non-ascii characters */
|
||||||
|
sep = dotsep_as_utf8(spec.sep);
|
||||||
|
if (sep == NULL) {
|
||||||
|
--- a/setup.py
|
||||||
|
+++ b/setup.py
|
||||||
|
@@ -2205,7 +2205,7 @@ class PyBuildExt(build_ext):
|
||||||
|
undef_macros = []
|
||||||
|
if '--with-system-libmpdec' in sysconfig.get_config_var("CONFIG_ARGS"):
|
||||||
|
include_dirs = []
|
||||||
|
- libraries = [':libmpdec.so.2']
|
||||||
|
+ libraries = ['mpdec']
|
||||||
|
sources = ['_decimal/_decimal.c']
|
||||||
|
depends = ['_decimal/docstrings.h']
|
||||||
|
else:
|
||||||
11
distutils-reproducible-compile.patch
Normal file
11
distutils-reproducible-compile.patch
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
--- a/Lib/distutils/util.py
|
||||||
|
+++ b/Lib/distutils/util.py
|
||||||
|
@@ -433,7 +433,7 @@ byte_compile(files, optimize=%r, force=%
|
||||||
|
else:
|
||||||
|
from py_compile import compile
|
||||||
|
|
||||||
|
- for file in py_files:
|
||||||
|
+ for file in sorted(py_files):
|
||||||
|
if file[-3:] != ".py":
|
||||||
|
# This lets us be lazy and not filter filenames in
|
||||||
|
# the "install_lib" command.
|
||||||
90
downport-Sphinx-features.patch
Normal file
90
downport-Sphinx-features.patch
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
Doc/library/tarfile.rst | 11 -----------
|
||||||
|
1 file changed, 11 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/library/tarfile.rst
|
||||||
|
+++ b/Doc/library/tarfile.rst
|
||||||
|
@@ -504,7 +504,6 @@ be finalized; only the internally used f
|
||||||
|
Return an :class:`io.BufferedReader` object.
|
||||||
|
|
||||||
|
.. attribute:: TarFile.errorlevel
|
||||||
|
- :type: int
|
||||||
|
|
||||||
|
If *errorlevel* is ``0``, errors are ignored when using :meth:`TarFile.extract`
|
||||||
|
and :meth:`TarFile.extractall`.
|
||||||
|
@@ -683,19 +682,16 @@ A ``TarInfo`` object has the following p
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.name
|
||||||
|
- :type: str
|
||||||
|
|
||||||
|
Name of the archive member.
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.size
|
||||||
|
- :type: int
|
||||||
|
|
||||||
|
Size in bytes.
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.mtime
|
||||||
|
- :type: int | float
|
||||||
|
|
||||||
|
Time of last modification in seconds since the :ref:`epoch <epoch>`,
|
||||||
|
as in :attr:`os.stat_result.st_mtime`.
|
||||||
|
@@ -707,7 +703,6 @@ A ``TarInfo`` object has the following p
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.mode
|
||||||
|
- :type: int
|
||||||
|
|
||||||
|
Permission bits, as for :func:`os.chmod`.
|
||||||
|
|
||||||
|
@@ -727,7 +722,6 @@ A ``TarInfo`` object has the following p
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.linkname
|
||||||
|
- :type: str
|
||||||
|
|
||||||
|
Name of the target file name, which is only present in :class:`TarInfo` objects
|
||||||
|
of type :const:`LNKTYPE` and :const:`SYMTYPE`.
|
||||||
|
@@ -739,7 +733,6 @@ A ``TarInfo`` object has the following p
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.uid
|
||||||
|
- :type: int
|
||||||
|
|
||||||
|
User ID of the user who originally stored this member.
|
||||||
|
|
||||||
|
@@ -750,7 +743,6 @@ A ``TarInfo`` object has the following p
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.gid
|
||||||
|
- :type: int
|
||||||
|
|
||||||
|
Group ID of the user who originally stored this member.
|
||||||
|
|
||||||
|
@@ -761,7 +753,6 @@ A ``TarInfo`` object has the following p
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.uname
|
||||||
|
- :type: str
|
||||||
|
|
||||||
|
User name.
|
||||||
|
|
||||||
|
@@ -772,7 +763,6 @@ A ``TarInfo`` object has the following p
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.gname
|
||||||
|
- :type: str
|
||||||
|
|
||||||
|
Group name.
|
||||||
|
|
||||||
|
@@ -783,7 +773,6 @@ A ``TarInfo`` object has the following p
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.pax_headers
|
||||||
|
- :type: dict
|
||||||
|
|
||||||
|
A dictionary containing key-value pairs of an associated pax extended header.
|
||||||
|
|
||||||
28
gh-78214-marshal_stabilize_FLAG_REF.patch
Normal file
28
gh-78214-marshal_stabilize_FLAG_REF.patch
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
From 6c8ea7c1dacd42f3ba00440231ec0e6b1a38300d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Inada Naoki <songofacandy@gmail.com>
|
||||||
|
Date: Sat, 14 Jul 2018 00:46:11 +0900
|
||||||
|
Subject: [PATCH] Use FLAG_REF always for interned strings
|
||||||
|
|
||||||
|
---
|
||||||
|
Python/marshal.c | 9 +++++++--
|
||||||
|
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
--- a/Python/marshal.c
|
||||||
|
+++ b/Python/marshal.c
|
||||||
|
@@ -298,9 +298,14 @@ w_ref(PyObject *v, char *flag, WFILE *p)
|
||||||
|
if (p->version < 3 || p->hashtable == NULL)
|
||||||
|
return 0; /* not writing object references */
|
||||||
|
|
||||||
|
- /* if it has only one reference, it definitely isn't shared */
|
||||||
|
- if (Py_REFCNT(v) == 1)
|
||||||
|
+ /* If it has only one reference, it definitely isn't shared.
|
||||||
|
+ * But we use TYPE_REF always for interned string, to PYC file stable
|
||||||
|
+ * as possible.
|
||||||
|
+ */
|
||||||
|
+ if (Py_REFCNT(v) == 1 &&
|
||||||
|
+ !(PyUnicode_CheckExact(v) && PyUnicode_CHECK_INTERNED(v))) {
|
||||||
|
return 0;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
entry = _Py_hashtable_get_entry(p->hashtable, v);
|
||||||
|
if (entry != NULL) {
|
||||||
35
gh120226-fix-sendfile-test-kernel-610.patch
Normal file
35
gh120226-fix-sendfile-test-kernel-610.patch
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
From 1b3f6523a5c83323cdc44031b33a1c062e5dc698 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Xi Ruoyao <xry111@xry111.site>
|
||||||
|
Date: Fri, 7 Jun 2024 23:51:32 +0800
|
||||||
|
Subject: [PATCH] gh-120226: Fix
|
||||||
|
test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10
|
||||||
|
(GH-120227)
|
||||||
|
|
||||||
|
The worst case is that the kernel buffers 17 pages with a page size of 64k.
|
||||||
|
(cherry picked from commit a7584245661102a5768c643fbd7db8395fd3c90e)
|
||||||
|
|
||||||
|
Co-authored-by: Xi Ruoyao <xry111@xry111.site>
|
||||||
|
---
|
||||||
|
Lib/test/test_asyncio/test_sendfile.py | 11 ++++-------
|
||||||
|
1 file changed, 4 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_asyncio/test_sendfile.py
|
||||||
|
+++ b/Lib/test/test_asyncio/test_sendfile.py
|
||||||
|
@@ -87,13 +87,10 @@ class MyProto(asyncio.Protocol):
|
||||||
|
|
||||||
|
class SendfileBase:
|
||||||
|
|
||||||
|
- # 256 KiB plus small unaligned to buffer chunk
|
||||||
|
- # Newer versions of Windows seems to have increased its internal
|
||||||
|
- # buffer and tries to send as much of the data as it can as it
|
||||||
|
- # has some form of buffering for this which is less than 256KiB
|
||||||
|
- # on newer server versions and Windows 11.
|
||||||
|
- # So DATA should be larger than 256 KiB to make this test reliable.
|
||||||
|
- DATA = b"x" * (1024 * 256 + 1)
|
||||||
|
+ # Linux >= 6.10 seems buffering up to 17 pages of data.
|
||||||
|
+ # So DATA should be large enough to make this test reliable even with a
|
||||||
|
+ # 64 KiB page configuration.
|
||||||
|
+ DATA = b"x" * (1024 * 17 * 64 + 1)
|
||||||
|
# Reduce socket buffer size to test on relative small data sets.
|
||||||
|
BUF_SIZE = 4 * 1024 # 4 KiB
|
||||||
|
|
||||||
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.9.23/Doc/tools/extensions/pyspecific.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.9.23.orig/Doc/tools/extensions/pyspecific.py 2025-09-30 18:26:26.712969300 +0200
|
||||||
|
+++ Python-3.9.23/Doc/tools/extensions/pyspecific.py 2025-09-30 18:26:26.763263890 +0200
|
||||||
|
@@ -55,11 +55,21 @@
|
||||||
|
SOURCE_URI = 'https://github.com/python/cpython/tree/3.9/%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
|
||||||
|
|
||||||
|
|
||||||
|
# Support for marking up and linking to bugs.python.org issues
|
||||||
35
idle3.appdata.xml
Normal file
35
idle3.appdata.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!-- Copyright 2017 Zbigniew Jędrzejewski-Szmek -->
|
||||||
|
<application>
|
||||||
|
<id type="desktop">idle3.desktop</id>
|
||||||
|
<name>IDLE3</name>
|
||||||
|
<metadata_licence>CC0</metadata_licence>
|
||||||
|
<project_license>Python-2.0</project_license>
|
||||||
|
<summary>Python 3 Integrated Development and Learning Environment</summary>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
IDLE is Python’s Integrated Development and Learning Environment.
|
||||||
|
The GUI is uniform between Windows, Unix, and Mac OS X.
|
||||||
|
IDLE provides an easy way to start writing, running, and debugging
|
||||||
|
Python code.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
IDLE is written in pure Python, and uses the tkinter GUI toolkit.
|
||||||
|
It provides:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<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>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>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
<url type="homepage">https://docs.python.org/3/library/idle.html</url>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</screenshot>
|
||||||
|
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</screenshot>
|
||||||
|
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<update_contact>zbyszek@in.waw.pl</update_contact>
|
||||||
|
</application>
|
||||||
12
idle3.desktop
Normal file
12
idle3.desktop
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=IDLE 3
|
||||||
|
GenericName=Python 3 IDE
|
||||||
|
Comment=Python 3 Integrated Development and Learning Environment
|
||||||
|
Exec=idle3 %F
|
||||||
|
TryExec=idle3
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Icon=idle3
|
||||||
|
Categories=Development;IDE;
|
||||||
|
MimeType=text/x-python;
|
||||||
7
import_failed.map
Normal file
7
import_failed.map
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
python39-curses: curses _curses _curses_panel
|
||||||
|
python39-dbm: dbm _dbm _gdbm
|
||||||
|
python39-idle: idlelib
|
||||||
|
python39-testsuite: test _ctypes_test _testbuffer _testcapi _testinternalcapi _testimportmultiple _testmultiphase xxlimited
|
||||||
|
python39-tk: tkinter _tkinter
|
||||||
|
python39-tools: turtledemo
|
||||||
|
python39: sqlite3 readline _sqlite3 nis
|
||||||
23
import_failed.py
Normal file
23
import_failed.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import sys, os
|
||||||
|
from sysconfig import get_path
|
||||||
|
|
||||||
|
failed_map_path = os.path.join(get_path('stdlib'), '_import_failed', 'import_failed.map')
|
||||||
|
|
||||||
|
if __spec__:
|
||||||
|
failed_name = __spec__.name
|
||||||
|
else:
|
||||||
|
failed_name = __name__
|
||||||
|
|
||||||
|
with open(failed_map_path) as fd:
|
||||||
|
for line in fd:
|
||||||
|
package = line.split(':')[0]
|
||||||
|
imports = line.split(':')[1]
|
||||||
|
if failed_name in imports:
|
||||||
|
raise ImportError(f"""Module '{failed_name}' is not installed.
|
||||||
|
Use:
|
||||||
|
sudo zypper install {package}
|
||||||
|
to install it.""")
|
||||||
|
|
||||||
|
raise ImportError(f"""Module '{failed_name}' is not installed.
|
||||||
|
It is supposed to be part of python3 distribution, but missing from failed import map.
|
||||||
|
Please file a bug on the SUSE Bugzilla.""")
|
||||||
28
macros.python3
Normal file
28
macros.python3
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
%have_python3 1
|
||||||
|
|
||||||
|
# commented out legacy macro definitions
|
||||||
|
#py3_prefix /usr
|
||||||
|
#py3_incdir /usr/include/python3.5m
|
||||||
|
#py3_ver 3.5
|
||||||
|
|
||||||
|
# these should now be provided by macros.python_all
|
||||||
|
#python3_sitearch /usr/lib64/python3.5/site-packages
|
||||||
|
#python3_sitelib /usr/lib/python3.5/site-packages
|
||||||
|
#python3_version 3.5
|
||||||
|
|
||||||
|
# hard to say if anyone ever used these?
|
||||||
|
#py3_soflags cpython-35m-x86_64-linux-gnu
|
||||||
|
#py3_abiflags m
|
||||||
|
%cpython3_soabi %(python3 -c "import sysconfig; print(sysconfig.get_config_var('SOABI'))")
|
||||||
|
%py3_soflags %cpython3_soabi
|
||||||
|
|
||||||
|
# compilation macros that might be in use somewhere
|
||||||
|
%py3_compile(O) \
|
||||||
|
find %1 -name '*.pyc' -exec rm -f {} ";"\
|
||||||
|
python3 -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1\
|
||||||
|
%{-O:\
|
||||||
|
find %1 -name '*.pyo' -exec rm -f {} ";"\
|
||||||
|
python3 -O -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1\
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
665
no-skipif-doctests.patch
Normal file
665
no-skipif-doctests.patch
Normal file
@@ -0,0 +1,665 @@
|
|||||||
|
only in patch2:
|
||||||
|
unchanged:
|
||||||
|
---
|
||||||
|
Doc/conf.py | 2 -
|
||||||
|
Doc/library/turtle.rst | 82 -------------------------------------------------
|
||||||
|
2 files changed, 1 insertion(+), 83 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/conf.py
|
||||||
|
+++ b/Doc/conf.py
|
||||||
|
@@ -46,7 +46,7 @@ today_fmt = '%B %d, %Y'
|
||||||
|
highlight_language = 'python3'
|
||||||
|
|
||||||
|
# Minimum version of sphinx required
|
||||||
|
-needs_sphinx = '1.8'
|
||||||
|
+needs_sphinx = '1.7.6'
|
||||||
|
|
||||||
|
# Ignore any .rst files in the venv/ directory.
|
||||||
|
exclude_patterns = ['venv/*', 'README.rst']
|
||||||
|
--- a/Doc/library/turtle.rst
|
||||||
|
+++ b/Doc/library/turtle.rst
|
||||||
|
@@ -250,7 +250,6 @@ Turtle motion
|
||||||
|
turtle is headed.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(0.00,0.00)
|
||||||
|
@@ -277,7 +276,6 @@ Turtle motion
|
||||||
|
>>> turtle.goto(0, 0)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(0.00,0.00)
|
||||||
|
@@ -296,13 +294,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
|
||||||
|
@@ -321,13 +317,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
|
||||||
|
@@ -350,13 +344,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
|
||||||
|
@@ -380,13 +372,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)
|
||||||
|
@@ -402,13 +392,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)
|
||||||
|
@@ -435,7 +423,6 @@ Turtle motion
|
||||||
|
=================== ====================
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.setheading(90)
|
||||||
|
>>> turtle.heading()
|
||||||
|
@@ -448,14 +435,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
|
||||||
|
@@ -487,7 +472,6 @@ Turtle motion
|
||||||
|
calculated automatically. May be used to draw regular polygons.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.position()
|
||||||
|
@@ -516,7 +500,6 @@ Turtle motion
|
||||||
|
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.dot()
|
||||||
|
@@ -534,7 +517,6 @@ Turtle motion
|
||||||
|
it by calling ``clearstamp(stamp_id)``.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.color("blue")
|
||||||
|
>>> turtle.stamp()
|
||||||
|
@@ -550,7 +532,6 @@ Turtle motion
|
||||||
|
Delete stamp with given *stampid*.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(150.00,-0.00)
|
||||||
|
@@ -595,7 +576,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)
|
||||||
|
@@ -628,7 +608,6 @@ Turtle motion
|
||||||
|
turtle turn instantly.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.speed()
|
||||||
|
3
|
||||||
|
@@ -649,7 +628,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)
|
||||||
|
@@ -665,7 +643,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)
|
||||||
|
@@ -677,7 +654,6 @@ Tell Turtle's state
|
||||||
|
Return the turtle's x coordinate.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(50)
|
||||||
|
@@ -693,7 +669,6 @@ Tell Turtle's state
|
||||||
|
Return the turtle's y coordinate.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(60)
|
||||||
|
@@ -710,7 +685,6 @@ Tell Turtle's state
|
||||||
|
:func:`mode`).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(67)
|
||||||
|
@@ -727,7 +701,6 @@ Tell Turtle's state
|
||||||
|
other turtle, in turtle step units.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.distance(30,40)
|
||||||
|
@@ -751,7 +724,6 @@ Settings for measurement
|
||||||
|
Default value is 360 degrees.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(90)
|
||||||
|
@@ -774,7 +746,6 @@ Settings for measurement
|
||||||
|
``degrees(2*math.pi)``.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(90)
|
||||||
|
@@ -785,7 +756,6 @@ Settings for measurement
|
||||||
|
1.5707963267948966
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.degrees(360)
|
||||||
|
@@ -821,7 +791,6 @@ Drawing state
|
||||||
|
thickness. If no argument is given, the current pensize is returned.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.pensize()
|
||||||
|
1
|
||||||
|
@@ -853,7 +822,6 @@ Drawing state
|
||||||
|
attributes in one statement.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:options: +NORMALIZE_WHITESPACE
|
||||||
|
|
||||||
|
>>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
|
||||||
|
@@ -876,7 +844,6 @@ Drawing state
|
||||||
|
Return ``True`` if pen is down, ``False`` if it's up.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.penup()
|
||||||
|
>>> turtle.isdown()
|
||||||
|
@@ -917,7 +884,6 @@ Color control
|
||||||
|
newly set pencolor.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> colormode()
|
||||||
|
1.0
|
||||||
|
@@ -966,7 +932,6 @@ Color control
|
||||||
|
with the newly set fillcolor.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.fillcolor("violet")
|
||||||
|
>>> turtle.fillcolor()
|
||||||
|
@@ -1005,7 +970,6 @@ Color control
|
||||||
|
with the newly set colors.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.color("red", "green")
|
||||||
|
>>> turtle.color()
|
||||||
|
@@ -1022,7 +986,6 @@ Filling
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
@@ -1032,7 +995,6 @@ Filling
|
||||||
|
Return fillstate (``True`` if filling, ``False`` else).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.begin_fill()
|
||||||
|
>>> if turtle.filling():
|
||||||
|
@@ -1057,7 +1019,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()
|
||||||
|
@@ -1074,7 +1035,6 @@ More drawing control
|
||||||
|
variables to the default values.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.goto(0,-22)
|
||||||
|
>>> turtle.left(100)
|
||||||
|
@@ -1125,7 +1085,6 @@ Visibility
|
||||||
|
drawing observably.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.hideturtle()
|
||||||
|
|
||||||
|
@@ -1136,7 +1095,6 @@ Visibility
|
||||||
|
Make the turtle visible.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.showturtle()
|
||||||
|
|
||||||
|
@@ -1167,7 +1125,6 @@ Appearance
|
||||||
|
deal with shapes see Screen method :func:`register_shape`.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.shape()
|
||||||
|
'classic'
|
||||||
|
@@ -1193,7 +1150,6 @@ Appearance
|
||||||
|
``resizemode("user")`` is called by :func:`shapesize` when used with arguments.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.resizemode()
|
||||||
|
'noresize'
|
||||||
|
@@ -1217,7 +1173,6 @@ Appearance
|
||||||
|
of the shapes's outline.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.shapesize()
|
||||||
|
(1.0, 1.0, 1)
|
||||||
|
@@ -1242,7 +1197,6 @@ Appearance
|
||||||
|
heading of the turtle are sheared.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
>>> turtle.shapesize(5,2)
|
||||||
|
@@ -1259,7 +1213,6 @@ Appearance
|
||||||
|
change the turtle's heading (direction of movement).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.reset()
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
@@ -1279,7 +1232,6 @@ Appearance
|
||||||
|
(direction of movement).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.reset()
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
@@ -1305,7 +1257,6 @@ Appearance
|
||||||
|
turtle (its direction of movement).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.reset()
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
@@ -1334,7 +1285,6 @@ Appearance
|
||||||
|
given matrix.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle = Turtle()
|
||||||
|
>>> turtle.shape("square")
|
||||||
|
@@ -1350,7 +1300,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)
|
||||||
|
@@ -1375,7 +1324,6 @@ Using events
|
||||||
|
procedural way:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> def turn(x, y):
|
||||||
|
... left(180)
|
||||||
|
@@ -1396,7 +1344,6 @@ Using events
|
||||||
|
``None``, existing bindings are removed.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> class MyTurtle(Turtle):
|
||||||
|
... def glow(self,x,y):
|
||||||
|
@@ -1424,7 +1371,6 @@ Using events
|
||||||
|
mouse-click event on that turtle.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.ondrag(turtle.goto)
|
||||||
|
|
||||||
|
@@ -1452,7 +1398,6 @@ Special Turtle methods
|
||||||
|
Return the last recorded polygon.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.begin_poly()
|
||||||
|
@@ -1472,7 +1417,6 @@ Special Turtle methods
|
||||||
|
turtle properties.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> mick = Turtle()
|
||||||
|
>>> joe = mick.clone()
|
||||||
|
@@ -1485,7 +1429,6 @@ Special Turtle methods
|
||||||
|
return the "anonymous turtle":
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> pet = getturtle()
|
||||||
|
>>> pet.fd(50)
|
||||||
|
@@ -1499,7 +1442,6 @@ Special Turtle methods
|
||||||
|
TurtleScreen methods can then be called for that object.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> ts = turtle.getscreen()
|
||||||
|
>>> ts
|
||||||
|
@@ -1517,7 +1459,6 @@ Special Turtle methods
|
||||||
|
``None``, the undobuffer is disabled.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.setundobuffer(42)
|
||||||
|
|
||||||
|
@@ -1527,7 +1468,6 @@ Special Turtle methods
|
||||||
|
Return number of entries in the undobuffer.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> while undobufferentries():
|
||||||
|
... undo()
|
||||||
|
@@ -1550,7 +1490,6 @@ below:
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> s = Shape("compound")
|
||||||
|
>>> poly1 = ((0,0),(10,-5),(0,10),(-10,-5))
|
||||||
|
@@ -1561,7 +1500,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")
|
||||||
|
@@ -1581,7 +1519,6 @@ Most of the examples in this section ref
|
||||||
|
``screen``.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> screen = Screen()
|
||||||
|
@@ -1598,7 +1535,6 @@ Window control
|
||||||
|
Set or return background color of the TurtleScreen.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.bgcolor("orange")
|
||||||
|
>>> screen.bgcolor()
|
||||||
|
@@ -1690,7 +1626,6 @@ Window control
|
||||||
|
distorted.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.reset()
|
||||||
|
>>> screen.setworldcoordinates(-50,-7.5,50,7.5)
|
||||||
|
@@ -1701,7 +1636,6 @@ Window control
|
||||||
|
... left(45); fd(2) # a regular octagon
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> screen.reset()
|
||||||
|
@@ -1723,7 +1657,6 @@ Animation control
|
||||||
|
Optional argument:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.delay()
|
||||||
|
10
|
||||||
|
@@ -1745,7 +1678,6 @@ Animation control
|
||||||
|
:func:`delay`).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.tracer(8, 25)
|
||||||
|
>>> dist = 2
|
||||||
|
@@ -1782,7 +1714,6 @@ Using screen events
|
||||||
|
must have the focus. (See method :func:`listen`.)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> def f():
|
||||||
|
... fd(50)
|
||||||
|
@@ -1803,7 +1734,6 @@ Using screen events
|
||||||
|
must have focus. (See method :func:`listen`.)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> def f():
|
||||||
|
... fd(50)
|
||||||
|
@@ -1828,7 +1758,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.
|
||||||
|
@@ -1848,7 +1777,6 @@ Using screen events
|
||||||
|
Install a timer that calls *fun* after *t* milliseconds.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> running = True
|
||||||
|
>>> def f():
|
||||||
|
@@ -1930,7 +1858,6 @@ Settings and special methods
|
||||||
|
============ ========================= ===================
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> mode("logo") # resets turtle heading to north
|
||||||
|
>>> mode()
|
||||||
|
@@ -1945,7 +1872,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)
|
||||||
|
@@ -1966,7 +1892,6 @@ Settings and special methods
|
||||||
|
do with a Tkinter Canvas.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> cv = screen.getcanvas()
|
||||||
|
>>> cv
|
||||||
|
@@ -1978,7 +1903,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']
|
||||||
|
@@ -2002,7 +1926,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)))
|
||||||
|
|
||||||
|
@@ -2018,7 +1941,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")
|
||||||
|
@@ -2080,7 +2002,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
|
||||||
|
@@ -2096,7 +2017,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!")
|
||||||
|
|
||||||
|
@@ -2167,7 +2087,6 @@ Public classes
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
|
||||||
|
>>> s = Shape("compound")
|
||||||
|
@@ -2514,7 +2433,6 @@ Changes since Python 3.0
|
||||||
|
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> for turtle in turtles():
|
||||||
78
pre_checkin.sh
Normal file
78
pre_checkin.sh
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export LC_ALL=C
|
||||||
|
|
||||||
|
master=python*.spec
|
||||||
|
|
||||||
|
# create import_failed.map from package definitions
|
||||||
|
pkgname=$(grep python_pkg_name $master |grep define |awk -F' ' '{print $3}')
|
||||||
|
MAPFILE=import_failed.map
|
||||||
|
function new_map_line () {
|
||||||
|
package=$1
|
||||||
|
package=$(echo $1 |sed -e "s:%{python_pkg_name}:$pkgname:")
|
||||||
|
modules=$2
|
||||||
|
if [ -z "$package" -o -z "$modules" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [[ "$package" =~ "-base" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "$package:$modules" >> $MAPFILE.tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
for spec in *.spec; do
|
||||||
|
basename=${spec%.spec}
|
||||||
|
package=
|
||||||
|
modules=
|
||||||
|
while read line; do
|
||||||
|
case $line in
|
||||||
|
"%files -n "*)
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
package=${line#"%files -n "}
|
||||||
|
modules=
|
||||||
|
;;
|
||||||
|
"%files "*)
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
package=$basename-${line#"%files "}
|
||||||
|
modules=
|
||||||
|
;;
|
||||||
|
"%files")
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
package=$basename
|
||||||
|
modules=
|
||||||
|
;;
|
||||||
|
"%{sitedir}/config-"*)
|
||||||
|
# ignore
|
||||||
|
;;
|
||||||
|
"%{sitedir}/"*)
|
||||||
|
word=${line#"%{sitedir}/"}
|
||||||
|
if ! echo $word | grep -q /; then
|
||||||
|
modules="$modules $word"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"%{dynlib "*"}")
|
||||||
|
word=${line#"%{dynlib "}
|
||||||
|
word=${word%"}"}
|
||||||
|
modules="$modules $word"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < $spec
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
done
|
||||||
|
|
||||||
|
cat $MAPFILE.tmp |sort -u > $MAPFILE
|
||||||
|
rm $MAPFILE.tmp
|
||||||
|
|
||||||
|
# run test inclusion check
|
||||||
|
tar xJf Python-*.xz
|
||||||
|
python3 skipped_tests.py
|
||||||
|
|
||||||
|
# generate baselibs.conf
|
||||||
|
VERSION=$(grep ^Version $master|awk -F':' '{print $2}' |sed -e 's/ //g')
|
||||||
|
python_version=${VERSION:0:3} # 3.3
|
||||||
|
python_version_abitag=${python_version//./} # 33
|
||||||
|
python_version_soname=${python_version//./_} # 3_3
|
||||||
|
echo "$pkgname-base" > baselibs.conf
|
||||||
|
echo "$pkgname" >> baselibs.conf
|
||||||
|
echo "libpython$python_version_soname-1_0" >> baselibs.conf
|
||||||
|
|
||||||
25
python-3.3.0b1-fix_date_time_compiler.patch
Normal file
25
python-3.3.0b1-fix_date_time_compiler.patch
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
Makefile.pre.in | 7 +++++++
|
||||||
|
1 file changed, 7 insertions(+)
|
||||||
|
|
||||||
|
--- a/Makefile.pre.in
|
||||||
|
+++ b/Makefile.pre.in
|
||||||
|
@@ -781,11 +781,18 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
|
||||||
|
$(DTRACE_OBJS) \
|
||||||
|
$(srcdir)/Modules/getbuildinfo.c
|
||||||
|
$(CC) -c $(PY_CORE_CFLAGS) \
|
||||||
|
+ -DDATE="\"`date -u -r Makefile.pre.in +"%b %d %Y"`\"" \
|
||||||
|
+ -DTIME="\"`date -u -r Makefile.pre.in +"%T"`\"" \
|
||||||
|
-DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \
|
||||||
|
-DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \
|
||||||
|
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \
|
||||||
|
-o $@ $(srcdir)/Modules/getbuildinfo.c
|
||||||
|
|
||||||
|
+Python/getcompiler.o: $(srcdir)/Python/getcompiler.c Makefile
|
||||||
|
+ $(CC) -c $(PY_CORE_CFLAGS) \
|
||||||
|
+ -DCOMPILER='"[GCC]"' \
|
||||||
|
+ -o $@ $(srcdir)/Python/getcompiler.c
|
||||||
|
+
|
||||||
|
Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile
|
||||||
|
$(CC) -c $(PY_CORE_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \
|
||||||
|
-DPREFIX='"$(prefix)"' \
|
||||||
15
python-3.3.0b1-localpath.patch
Normal file
15
python-3.3.0b1-localpath.patch
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
Lib/site.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/Lib/site.py
|
||||||
|
+++ b/Lib/site.py
|
||||||
|
@@ -77,7 +77,7 @@ import io
|
||||||
|
import stat
|
||||||
|
|
||||||
|
# Prefixes for site-packages; add additional prefixes like /usr/local here
|
||||||
|
-PREFIXES = [sys.prefix, sys.exec_prefix]
|
||||||
|
+PREFIXES = [sys.prefix, sys.exec_prefix, '/usr/local']
|
||||||
|
# Enable per user site-packages directory
|
||||||
|
# set it to False to disable the feature or True to force the feature
|
||||||
|
ENABLE_USER_SITE = None
|
||||||
1008
python.keyring
Normal file
1008
python.keyring
Normal file
File diff suppressed because it is too large
Load Diff
53
python3-imp-returntype.patch
Normal file
53
python3-imp-returntype.patch
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
From 7bd6f0e5500f778e940374237b94651f60ae1990 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Fri, 6 Jul 2018 21:00:45 -0700
|
||||||
|
Subject: [PATCH] closes bpo-34056: Always return bytes from
|
||||||
|
_HackedGetData.get_data(). (GH-8130)
|
||||||
|
|
||||||
|
* Always return bytes from _HackedGetData.get_data().
|
||||||
|
|
||||||
|
Ensure the imp.load_source shim always returns bytes by reopening the file in
|
||||||
|
binary mode if needed. Hash-based pycs have to receive the source code in bytes.
|
||||||
|
|
||||||
|
It's tempting to change imp.get_suffixes() to always return 'rb' as a mode, but
|
||||||
|
that breaks some stdlib tests and likely 3rdparty code, too.
|
||||||
|
(cherry picked from commit b0274f2cddd36b49fe5080efbe160277ef546471)
|
||||||
|
|
||||||
|
Co-authored-by: Benjamin Peterson <benjamin@python.org>
|
||||||
|
---
|
||||||
|
Lib/imp.py | 13 ++++++-------
|
||||||
|
Lib/test/test_imp.py | 15 +++++++++++++++
|
||||||
|
.../2018-07-05-22-45-46.bpo-34056.86isrU.rst | 3 +++
|
||||||
|
3 files changed, 24 insertions(+), 7 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2018-07-05-22-45-46.bpo-34056.86isrU.rst
|
||||||
|
|
||||||
|
--- a/Lib/test/test_imp.py
|
||||||
|
+++ b/Lib/test/test_imp.py
|
||||||
|
@@ -376,6 +376,20 @@ class ImportTests(unittest.TestCase):
|
||||||
|
mod = imp.load_module('mymod', file, path, description)
|
||||||
|
self.assertEqual(mod.x, 42)
|
||||||
|
|
||||||
|
+ def test_find_and_load_checked_pyc(self):
|
||||||
|
+ # issue 34056
|
||||||
|
+ with support.temp_cwd():
|
||||||
|
+ with open('mymod.py', 'wb') as fp:
|
||||||
|
+ fp.write(b'x = 42\n')
|
||||||
|
+ py_compile.compile(
|
||||||
|
+ 'mymod.py',
|
||||||
|
+ doraise=True,
|
||||||
|
+ invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
|
||||||
|
+ )
|
||||||
|
+ file, path, description = imp.find_module('mymod', path=['.'])
|
||||||
|
+ mod = imp.load_module('mymod', file, path, description)
|
||||||
|
+ self.assertEqual(mod.x, 42)
|
||||||
|
+
|
||||||
|
|
||||||
|
class ReloadTests(unittest.TestCase):
|
||||||
|
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2018-07-05-22-45-46.bpo-34056.86isrU.rst
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+Ensure the loader shim created by ``imp.load_module`` always returns bytes
|
||||||
|
+from its ``get_data()`` function. This fixes using ``imp.load_module`` with
|
||||||
|
+:pep:`552` hash-based pycs.
|
||||||
4155
python39.changes
Normal file
4155
python39.changes
Normal file
File diff suppressed because it is too large
Load Diff
1049
python39.spec
Normal file
1049
python39.spec
Normal file
File diff suppressed because it is too large
Load Diff
14
skip-test_pyobject_freed_is_freed.patch
Normal file
14
skip-test_pyobject_freed_is_freed.patch
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_capi.py | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_capi.py
|
||||||
|
+++ b/Lib/test/test_capi.py
|
||||||
|
@@ -794,6 +794,7 @@ class PyMemDebugTests(unittest.TestCase)
|
||||||
|
def test_pyobject_forbidden_bytes_is_freed(self):
|
||||||
|
self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed')
|
||||||
|
|
||||||
|
+ @unittest.skip('Failing on Leap 15.*')
|
||||||
|
def test_pyobject_freed_is_freed(self):
|
||||||
|
self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
|
||||||
|
|
||||||
69
skipped_tests.py
Normal file
69
skipped_tests.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
"""
|
||||||
|
Simple regexp-based skipped test checker.
|
||||||
|
It lists tests that are mentioned (presumably for exclusion)
|
||||||
|
in BASE, and in MAIN (presumably for inclusion)
|
||||||
|
and reports discrepancies.
|
||||||
|
|
||||||
|
This will have a number of
|
||||||
|
"""
|
||||||
|
|
||||||
|
MAIN = "python39.spec"
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
from os.path import basename
|
||||||
|
|
||||||
|
alltests = set()
|
||||||
|
qemu_exclusions = set()
|
||||||
|
|
||||||
|
for item in glob.glob("Python-*/Lib/test/test_*"):
|
||||||
|
testname = basename(item)
|
||||||
|
if testname.endswith(".py"):
|
||||||
|
testname = testname[:-3]
|
||||||
|
alltests.add(testname)
|
||||||
|
|
||||||
|
testre = re.compile(r'[\s"](test_\w+)\b')
|
||||||
|
|
||||||
|
def find_tests_in_spec(specname):
|
||||||
|
global qemu_exclusions
|
||||||
|
|
||||||
|
found_tests = set()
|
||||||
|
with open(specname) as spec:
|
||||||
|
in_qemu = False
|
||||||
|
for line in spec:
|
||||||
|
line = line.strip()
|
||||||
|
if "#" in line:
|
||||||
|
line = line[:line.index("#")]
|
||||||
|
tests = set(testre.findall(line))
|
||||||
|
found_tests |= tests
|
||||||
|
if line == "%if 0%{?qemu_user_space_build} > 0":
|
||||||
|
in_qemu = True
|
||||||
|
if in_qemu:
|
||||||
|
if line == "%endif":
|
||||||
|
in_qemu = False
|
||||||
|
qemu_exclusions |= tests
|
||||||
|
return found_tests
|
||||||
|
|
||||||
|
excluded = find_tests_in_spec(MAIN)
|
||||||
|
|
||||||
|
#print("--- excluded tests:", " ".join(sorted(excluded)))
|
||||||
|
#print("--- included tests:", " ".join(sorted(included)))
|
||||||
|
|
||||||
|
mentioned = excluded
|
||||||
|
nonexistent = mentioned - alltests
|
||||||
|
missing = excluded - qemu_exclusions
|
||||||
|
|
||||||
|
print("--- the following tests are excluded for QEMU and not tested in python")
|
||||||
|
print("--- (that probably means we don't need to worry about them)")
|
||||||
|
for test in sorted(qemu_exclusions - excluded):
|
||||||
|
print(test)
|
||||||
|
|
||||||
|
print("--- the following tests might be excluded in python:")
|
||||||
|
for test in sorted(missing):
|
||||||
|
print(test)
|
||||||
|
|
||||||
|
if nonexistent:
|
||||||
|
print("--- the following tests don't exist:")
|
||||||
|
for test in sorted(nonexistent):
|
||||||
|
print(test)
|
||||||
30
sphinx-802.patch
Normal file
30
sphinx-802.patch
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
Doc/tools/extensions/pyspecific.py | 10 ++++++++--
|
||||||
|
1 file changed, 8 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/tools/extensions/pyspecific.py
|
||||||
|
+++ b/Doc/tools/extensions/pyspecific.py
|
||||||
|
@@ -28,7 +28,13 @@ try:
|
||||||
|
except ImportError:
|
||||||
|
from sphinx.environment import NoUri
|
||||||
|
from sphinx.locale import _ as sphinx_gettext
|
||||||
|
-from sphinx.util import status_iterator, logging
|
||||||
|
+try:
|
||||||
|
+ from sphinx.util.display import status_iterator
|
||||||
|
+except ImportError:
|
||||||
|
+ # This method was moved into sphinx.util.display in Sphinx 6.1.0. Before
|
||||||
|
+ # that it resided in sphinx.util.
|
||||||
|
+ from sphinx.util import status_iterator
|
||||||
|
+from sphinx.util import logging
|
||||||
|
from sphinx.util.nodes import split_explicit_title
|
||||||
|
from sphinx.writers.text import TextWriter, TextTranslator
|
||||||
|
from sphinx.writers.latex import LaTeXTranslator
|
||||||
|
@@ -338,7 +344,7 @@ class PyAbstractMethod(PyMethod):
|
||||||
|
def expand_version_arg(argument, release):
|
||||||
|
"""Expand "next" to the current version"""
|
||||||
|
if argument == 'next':
|
||||||
|
- return translators['sphinx'].gettext('{} (unreleased)').format(release)
|
||||||
|
+ return sphinx_gettext('{} (unreleased)').format(release)
|
||||||
|
return argument
|
||||||
|
|
||||||
|
|
||||||
22
sphinx-update-removed-function.patch
Normal file
22
sphinx-update-removed-function.patch
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
Doc/tools/extensions/pyspecific.py | 7 ++++++-
|
||||||
|
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
Index: Python-3.9.22/Doc/tools/extensions/pyspecific.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.9.22.orig/Doc/tools/extensions/pyspecific.py 2025-04-08 17:21:55.000000000 +0200
|
||||||
|
+++ Python-3.9.22/Doc/tools/extensions/pyspecific.py 2025-04-11 09:49:58.417019238 +0200
|
||||||
|
@@ -407,7 +407,12 @@
|
||||||
|
translatable=False)
|
||||||
|
node.append(para)
|
||||||
|
env = self.state.document.settings.env
|
||||||
|
- env.get_domain('changeset').note_changeset(node)
|
||||||
|
+ # deprecated pre-Sphinx-2 method
|
||||||
|
+ if hasattr(env, 'note_versionchange'):
|
||||||
|
+ env.note_versionchange('deprecated', version[0], node, self.lineno)
|
||||||
|
+ # new method
|
||||||
|
+ else:
|
||||||
|
+ env.get_domain('changeset').note_changeset(node)
|
||||||
|
return [node] + messages
|
||||||
|
|
||||||
|
|
||||||
12
subprocess-raise-timeout.patch
Normal file
12
subprocess-raise-timeout.patch
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
--- a/Lib/test/test_subprocess.py
|
||||||
|
+++ b/Lib/test/test_subprocess.py
|
||||||
|
@@ -253,7 +253,8 @@ class ProcessTestCase(BaseTestCase):
|
||||||
|
"time.sleep(3600)"],
|
||||||
|
# Some heavily loaded buildbots (sparc Debian 3.x) require
|
||||||
|
# this much time to start and print.
|
||||||
|
- timeout=3)
|
||||||
|
+ # OBS might require even more
|
||||||
|
+ timeout=10)
|
||||||
|
self.fail("Expected TimeoutExpired.")
|
||||||
|
self.assertEqual(c.exception.output, b'BDFL')
|
||||||
|
|
||||||
75
support-expat-CVE-2022-25236-patched.patch
Normal file
75
support-expat-CVE-2022-25236-patched.patch
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
From 7da97f61816f3cadaa6788804b22a2434b40e8c5 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Mon, 21 Feb 2022 08:16:09 -0800
|
||||||
|
Subject: [PATCH] bpo-46811: Make test suite support Expat >=2.4.5 (GH-31453)
|
||||||
|
(GH-31472)
|
||||||
|
|
||||||
|
Curly brackets were never allowed in namespace URIs
|
||||||
|
according to RFC 3986, and so-called namespace-validating
|
||||||
|
XML parsers have the right to reject them a invalid URIs.
|
||||||
|
|
||||||
|
libexpat >=2.4.5 has become strcter in that regard due to
|
||||||
|
related security issues; with ET.XML instantiating a
|
||||||
|
namespace-aware parser under the hood, this test has no
|
||||||
|
future in CPython.
|
||||||
|
|
||||||
|
References:
|
||||||
|
- https://datatracker.ietf.org/doc/html/rfc3968
|
||||||
|
- https://www.w3.org/TR/xml-names/
|
||||||
|
|
||||||
|
Also, test_minidom.py: Support Expat >=2.4.5
|
||||||
|
(cherry picked from commit 2cae93832f46b245847bdc252456ddf7742ef45e)
|
||||||
|
|
||||||
|
Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
|
||||||
|
---
|
||||||
|
Lib/test/test_minidom.py | 23 +++++++++--------------
|
||||||
|
1 file changed, 9 insertions(+), 14 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst
|
||||||
|
|
||||||
|
--- a/Lib/test/test_minidom.py
|
||||||
|
+++ b/Lib/test/test_minidom.py
|
||||||
|
@@ -6,7 +6,6 @@ import io
|
||||||
|
from test import support
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
-import pyexpat
|
||||||
|
import xml.dom.minidom
|
||||||
|
|
||||||
|
from xml.dom.minidom import parse, Node, Document, parseString
|
||||||
|
@@ -1149,13 +1148,11 @@ class MinidomTest(unittest.TestCase):
|
||||||
|
|
||||||
|
# Verify that character decoding errors raise exceptions instead
|
||||||
|
# of crashing
|
||||||
|
- if pyexpat.version_info >= (2, 4, 5):
|
||||||
|
- self.assertRaises(ExpatError, parseString,
|
||||||
|
- b'<fran\xe7ais></fran\xe7ais>')
|
||||||
|
- self.assertRaises(ExpatError, parseString,
|
||||||
|
- b'<franais>Comment \xe7a va ? Tr\xe8s bien ?</franais>')
|
||||||
|
- else:
|
||||||
|
- self.assertRaises(UnicodeDecodeError, parseString,
|
||||||
|
+ # It doesn’t make any sense to insist on the exact text of the
|
||||||
|
+ # error message, or even the exact Exception … it is enough that
|
||||||
|
+ # the error has been discovered.
|
||||||
|
+ with self.assertRaises((UnicodeDecodeError, ExpatError)):
|
||||||
|
+ parseString(
|
||||||
|
b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
|
||||||
|
|
||||||
|
doc.unlink()
|
||||||
|
@@ -1617,12 +1614,10 @@ class MinidomTest(unittest.TestCase):
|
||||||
|
self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
|
||||||
|
|
||||||
|
def testExceptionOnSpacesInXMLNSValue(self):
|
||||||
|
- if pyexpat.version_info >= (2, 4, 5):
|
||||||
|
- context = self.assertRaisesRegex(ExpatError, 'syntax error')
|
||||||
|
- else:
|
||||||
|
- context = self.assertRaisesRegex(ValueError, 'Unsupported syntax')
|
||||||
|
-
|
||||||
|
- with context:
|
||||||
|
+ # It doesn’t make any sense to insist on the exact text of the
|
||||||
|
+ # error message, or even the exact Exception … it is enough that
|
||||||
|
+ # the error has been discovered.
|
||||||
|
+ with self.assertRaises((ExpatError, ValueError)):
|
||||||
|
parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')
|
||||||
|
|
||||||
|
def testDocRemoveChild(self):
|
||||||
Reference in New Issue
Block a user