forked from pool/python39
- Add gh139257-Support-docutils-0.22.patch to fix build with latest
docutils (>=0.22) gh#python/cpython#139257 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python39?expand=0&rev=243
This commit is contained in:
+4091
File diff suppressed because it is too large
Load Diff
+1057
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
<multibuild>
|
||||
<package>base</package>
|
||||
<package>doc</package>
|
||||
</multibuild>
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
.osc
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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'))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
@@ -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`.)
|
||||
@@ -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)
|
||||
@@ -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
Binary file not shown.
File diff suppressed because one or more lines are too long
+43
@@ -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.
|
||||
@@ -0,0 +1,3 @@
|
||||
python39-base
|
||||
python39
|
||||
libpython3_9-1_0
|
||||
BIN
Binary file not shown.
@@ -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`.
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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:
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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.""")
|
||||
@@ -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\
|
||||
}
|
||||
|
||||
|
||||
@@ -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():
|
||||
@@ -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
|
||||
|
||||
@@ -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)"' \
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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