forked from pool/python310
- Stop using %%defattr, it seems to be breaking proper executable
attributes on /usr/bin/ scripts (bsc#1227378). OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python310?expand=0&rev=136
This commit is contained in:
commit
c2077eafb7
23
.gitattributes
vendored
Normal file
23
.gitattributes
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
## Default LFS
|
||||||
|
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bsp filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gem filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.obscpio filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.oxt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.rpm filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.txz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.whl filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zst filter=lfs diff=lfs merge=lfs -text
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.osc
|
461
CVE-2023-27043-email-parsing-errors.patch
Normal file
461
CVE-2023-27043-email-parsing-errors.patch
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
---
|
||||||
|
Doc/library/email.utils.rst | 19 -
|
||||||
|
Lib/email/utils.py | 151 +++++++-
|
||||||
|
Lib/test/test_email/test_email.py | 187 +++++++++-
|
||||||
|
Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8
|
||||||
|
4 files changed, 344 insertions(+), 21 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/library/email.utils.rst
|
||||||
|
+++ b/Doc/library/email.utils.rst
|
||||||
|
@@ -60,13 +60,18 @@ of the new API.
|
||||||
|
begins with angle brackets, they are stripped off.
|
||||||
|
|
||||||
|
|
||||||
|
-.. function:: parseaddr(address)
|
||||||
|
+.. function:: parseaddr(address, *, strict=True)
|
||||||
|
|
||||||
|
Parse address -- which should be the value of some address-containing field such
|
||||||
|
as :mailheader:`To` or :mailheader:`Cc` -- into its constituent *realname* and
|
||||||
|
*email address* parts. Returns a tuple of that information, unless the parse
|
||||||
|
fails, in which case a 2-tuple of ``('', '')`` is returned.
|
||||||
|
|
||||||
|
+ If *strict* is true, use a strict parser which rejects malformed inputs.
|
||||||
|
+
|
||||||
|
+ .. versionchanged:: 3.13
|
||||||
|
+ Add *strict* optional parameter and reject malformed inputs by default.
|
||||||
|
+
|
||||||
|
|
||||||
|
.. function:: formataddr(pair, charset='utf-8')
|
||||||
|
|
||||||
|
@@ -84,12 +89,15 @@ of the new API.
|
||||||
|
Added the *charset* option.
|
||||||
|
|
||||||
|
|
||||||
|
-.. function:: getaddresses(fieldvalues)
|
||||||
|
+.. function:: getaddresses(fieldvalues, *, strict=True)
|
||||||
|
|
||||||
|
This method returns a list of 2-tuples of the form returned by ``parseaddr()``.
|
||||||
|
*fieldvalues* is a sequence of header field values as might be returned by
|
||||||
|
- :meth:`Message.get_all <email.message.Message.get_all>`. Here's a simple
|
||||||
|
- example that gets all the recipients of a message::
|
||||||
|
+ :meth:`Message.get_all <email.message.Message.get_all>`.
|
||||||
|
+
|
||||||
|
+ If *strict* is true, use a strict parser which rejects malformed inputs.
|
||||||
|
+
|
||||||
|
+ Here's a simple example that gets all the recipients of a message::
|
||||||
|
|
||||||
|
from email.utils import getaddresses
|
||||||
|
|
||||||
|
@@ -99,6 +107,9 @@ of the new API.
|
||||||
|
resent_ccs = msg.get_all('resent-cc', [])
|
||||||
|
all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
|
||||||
|
|
||||||
|
+ .. versionchanged:: 3.13
|
||||||
|
+ Add *strict* optional parameter and reject malformed inputs by default.
|
||||||
|
+
|
||||||
|
|
||||||
|
.. function:: parsedate(date)
|
||||||
|
|
||||||
|
--- a/Lib/email/utils.py
|
||||||
|
+++ b/Lib/email/utils.py
|
||||||
|
@@ -48,6 +48,7 @@ TICK = "'"
|
||||||
|
specialsre = re.compile(r'[][\\()<>@,:;".]')
|
||||||
|
escapesre = re.compile(r'[\\"]')
|
||||||
|
|
||||||
|
+
|
||||||
|
def _has_surrogates(s):
|
||||||
|
"""Return True if s contains surrogate-escaped binary data."""
|
||||||
|
# This check is based on the fact that unless there are surrogates, utf8
|
||||||
|
@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'):
|
||||||
|
return address
|
||||||
|
|
||||||
|
|
||||||
|
+def _iter_escaped_chars(addr):
|
||||||
|
+ pos = 0
|
||||||
|
+ escape = False
|
||||||
|
+ for pos, ch in enumerate(addr):
|
||||||
|
+ if escape:
|
||||||
|
+ yield (pos, '\\' + ch)
|
||||||
|
+ escape = False
|
||||||
|
+ elif ch == '\\':
|
||||||
|
+ escape = True
|
||||||
|
+ else:
|
||||||
|
+ yield (pos, ch)
|
||||||
|
+ if escape:
|
||||||
|
+ yield (pos, '\\')
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _strip_quoted_realnames(addr):
|
||||||
|
+ """Strip real names between quotes."""
|
||||||
|
+ if '"' not in addr:
|
||||||
|
+ # Fast path
|
||||||
|
+ return addr
|
||||||
|
+
|
||||||
|
+ start = 0
|
||||||
|
+ open_pos = None
|
||||||
|
+ result = []
|
||||||
|
+ for pos, ch in _iter_escaped_chars(addr):
|
||||||
|
+ if ch == '"':
|
||||||
|
+ if open_pos is None:
|
||||||
|
+ open_pos = pos
|
||||||
|
+ else:
|
||||||
|
+ if start != open_pos:
|
||||||
|
+ result.append(addr[start:open_pos])
|
||||||
|
+ start = pos + 1
|
||||||
|
+ open_pos = None
|
||||||
|
|
||||||
|
-def getaddresses(fieldvalues):
|
||||||
|
- """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
|
||||||
|
- all = COMMASPACE.join(str(v) for v in fieldvalues)
|
||||||
|
- a = _AddressList(all)
|
||||||
|
- return a.addresslist
|
||||||
|
+ if start < len(addr):
|
||||||
|
+ result.append(addr[start:])
|
||||||
|
+
|
||||||
|
+ return ''.join(result)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+supports_strict_parsing = True
|
||||||
|
+
|
||||||
|
+def getaddresses(fieldvalues, *, strict=True):
|
||||||
|
+ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue.
|
||||||
|
+
|
||||||
|
+ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in
|
||||||
|
+ its place.
|
||||||
|
+
|
||||||
|
+ If strict is true, use a strict parser which rejects malformed inputs.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ # If strict is true, if the resulting list of parsed addresses is greater
|
||||||
|
+ # than the number of fieldvalues in the input list, a parsing error has
|
||||||
|
+ # occurred and consequently a list containing a single empty 2-tuple [('',
|
||||||
|
+ # '')] is returned in its place. This is done to avoid invalid output.
|
||||||
|
+ #
|
||||||
|
+ # Malformed input: getaddresses(['alice@example.com <bob@example.com>'])
|
||||||
|
+ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')]
|
||||||
|
+ # Safe output: [('', '')]
|
||||||
|
+
|
||||||
|
+ if not strict:
|
||||||
|
+ all = COMMASPACE.join(str(v) for v in fieldvalues)
|
||||||
|
+ a = _AddressList(all)
|
||||||
|
+ return a.addresslist
|
||||||
|
+
|
||||||
|
+ fieldvalues = [str(v) for v in fieldvalues]
|
||||||
|
+ fieldvalues = _pre_parse_validation(fieldvalues)
|
||||||
|
+ addr = COMMASPACE.join(fieldvalues)
|
||||||
|
+ a = _AddressList(addr)
|
||||||
|
+ result = _post_parse_validation(a.addresslist)
|
||||||
|
+
|
||||||
|
+ # Treat output as invalid if the number of addresses is not equal to the
|
||||||
|
+ # expected number of addresses.
|
||||||
|
+ n = 0
|
||||||
|
+ for v in fieldvalues:
|
||||||
|
+ # When a comma is used in the Real Name part it is not a deliminator.
|
||||||
|
+ # So strip those out before counting the commas.
|
||||||
|
+ v = _strip_quoted_realnames(v)
|
||||||
|
+ # Expected number of addresses: 1 + number of commas
|
||||||
|
+ n += 1 + v.count(',')
|
||||||
|
+ if len(result) != n:
|
||||||
|
+ return [('', '')]
|
||||||
|
+
|
||||||
|
+ return result
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _check_parenthesis(addr):
|
||||||
|
+ # Ignore parenthesis in quoted real names.
|
||||||
|
+ addr = _strip_quoted_realnames(addr)
|
||||||
|
+
|
||||||
|
+ opens = 0
|
||||||
|
+ for pos, ch in _iter_escaped_chars(addr):
|
||||||
|
+ if ch == '(':
|
||||||
|
+ opens += 1
|
||||||
|
+ elif ch == ')':
|
||||||
|
+ opens -= 1
|
||||||
|
+ if opens < 0:
|
||||||
|
+ return False
|
||||||
|
+ return (opens == 0)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _pre_parse_validation(email_header_fields):
|
||||||
|
+ accepted_values = []
|
||||||
|
+ for v in email_header_fields:
|
||||||
|
+ if not _check_parenthesis(v):
|
||||||
|
+ v = "('', '')"
|
||||||
|
+ accepted_values.append(v)
|
||||||
|
+
|
||||||
|
+ return accepted_values
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _post_parse_validation(parsed_email_header_tuples):
|
||||||
|
+ accepted_values = []
|
||||||
|
+ # The parser would have parsed a correctly formatted domain-literal
|
||||||
|
+ # The existence of an [ after parsing indicates a parsing failure
|
||||||
|
+ for v in parsed_email_header_tuples:
|
||||||
|
+ if '[' in v[1]:
|
||||||
|
+ v = ('', '')
|
||||||
|
+ accepted_values.append(v)
|
||||||
|
+
|
||||||
|
+ return accepted_values
|
||||||
|
|
||||||
|
|
||||||
|
def _format_timetuple_and_zone(timetuple, zone):
|
||||||
|
@@ -205,16 +321,33 @@ def parsedate_to_datetime(data):
|
||||||
|
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
|
||||||
|
|
||||||
|
|
||||||
|
-def parseaddr(addr):
|
||||||
|
+def parseaddr(addr, *, strict=True):
|
||||||
|
"""
|
||||||
|
Parse addr into its constituent realname and email address parts.
|
||||||
|
|
||||||
|
Return a tuple of realname and email address, unless the parse fails, in
|
||||||
|
which case return a 2-tuple of ('', '').
|
||||||
|
+
|
||||||
|
+ If strict is True, use a strict parser which rejects malformed inputs.
|
||||||
|
"""
|
||||||
|
- addrs = _AddressList(addr).addresslist
|
||||||
|
- if not addrs:
|
||||||
|
- return '', ''
|
||||||
|
+ if not strict:
|
||||||
|
+ addrs = _AddressList(addr).addresslist
|
||||||
|
+ if not addrs:
|
||||||
|
+ return ('', '')
|
||||||
|
+ return addrs[0]
|
||||||
|
+
|
||||||
|
+ if isinstance(addr, list):
|
||||||
|
+ addr = addr[0]
|
||||||
|
+
|
||||||
|
+ if not isinstance(addr, str):
|
||||||
|
+ return ('', '')
|
||||||
|
+
|
||||||
|
+ addr = _pre_parse_validation([addr])[0]
|
||||||
|
+ addrs = _post_parse_validation(_AddressList(addr).addresslist)
|
||||||
|
+
|
||||||
|
+ if not addrs or len(addrs) > 1:
|
||||||
|
+ return ('', '')
|
||||||
|
+
|
||||||
|
return addrs[0]
|
||||||
|
|
||||||
|
|
||||||
|
--- a/Lib/test/test_email/test_email.py
|
||||||
|
+++ b/Lib/test/test_email/test_email.py
|
||||||
|
@@ -16,6 +16,7 @@ from unittest.mock import patch
|
||||||
|
|
||||||
|
import email
|
||||||
|
import email.policy
|
||||||
|
+import email.utils
|
||||||
|
|
||||||
|
from email.charset import Charset
|
||||||
|
from email.generator import Generator, DecodedGenerator, BytesGenerator
|
||||||
|
@@ -3288,15 +3289,137 @@ Foo
|
||||||
|
[('Al Person', 'aperson@dom.ain'),
|
||||||
|
('Bud Person', 'bperson@dom.ain')])
|
||||||
|
|
||||||
|
+ def test_parsing_errors(self):
|
||||||
|
+ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056"""
|
||||||
|
+ alice = 'alice@example.org'
|
||||||
|
+ bob = 'bob@example.com'
|
||||||
|
+ empty = ('', '')
|
||||||
|
+
|
||||||
|
+ # Test utils.getaddresses() and utils.parseaddr() on malformed email
|
||||||
|
+ # addresses: default behavior (strict=True) rejects malformed address,
|
||||||
|
+ # and strict=False which tolerates malformed address.
|
||||||
|
+ for invalid_separator, expected_non_strict in (
|
||||||
|
+ ('(', [(f'<{bob}>', alice)]),
|
||||||
|
+ (')', [('', alice), empty, ('', bob)]),
|
||||||
|
+ ('<', [('', alice), empty, ('', bob), empty]),
|
||||||
|
+ ('>', [('', alice), empty, ('', bob)]),
|
||||||
|
+ ('[', [('', f'{alice}[<{bob}>]')]),
|
||||||
|
+ (']', [('', alice), empty, ('', bob)]),
|
||||||
|
+ ('@', [empty, empty, ('', bob)]),
|
||||||
|
+ (';', [('', alice), empty, ('', bob)]),
|
||||||
|
+ (':', [('', alice), ('', bob)]),
|
||||||
|
+ ('.', [('', alice + '.'), ('', bob)]),
|
||||||
|
+ ('"', [('', alice), ('', f'<{bob}>')]),
|
||||||
|
+ ):
|
||||||
|
+ address = f'{alice}{invalid_separator}<{bob}>'
|
||||||
|
+ with self.subTest(address=address):
|
||||||
|
+ self.assertEqual(utils.getaddresses([address]),
|
||||||
|
+ [empty])
|
||||||
|
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||||
|
+ expected_non_strict)
|
||||||
|
+
|
||||||
|
+ self.assertEqual(utils.parseaddr([address]),
|
||||||
|
+ empty)
|
||||||
|
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||||
|
+ ('', address))
|
||||||
|
+
|
||||||
|
+ # Comma (',') is treated differently depending on strict parameter.
|
||||||
|
+ # Comma without quotes.
|
||||||
|
+ address = f'{alice},<{bob}>'
|
||||||
|
+ self.assertEqual(utils.getaddresses([address]),
|
||||||
|
+ [('', alice), ('', bob)])
|
||||||
|
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||||
|
+ [('', alice), ('', bob)])
|
||||||
|
+ self.assertEqual(utils.parseaddr([address]),
|
||||||
|
+ empty)
|
||||||
|
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||||
|
+ ('', address))
|
||||||
|
+
|
||||||
|
+ # Real name between quotes containing comma.
|
||||||
|
+ address = '"Alice, alice@example.org" <bob@example.com>'
|
||||||
|
+ expected_strict = ('Alice, alice@example.org', 'bob@example.com')
|
||||||
|
+ self.assertEqual(utils.getaddresses([address]), [expected_strict])
|
||||||
|
+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict])
|
||||||
|
+ self.assertEqual(utils.parseaddr([address]), expected_strict)
|
||||||
|
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||||
|
+ ('', address))
|
||||||
|
+
|
||||||
|
+ # Valid parenthesis in comments.
|
||||||
|
+ address = 'alice@example.org (Alice)'
|
||||||
|
+ expected_strict = ('Alice', 'alice@example.org')
|
||||||
|
+ self.assertEqual(utils.getaddresses([address]), [expected_strict])
|
||||||
|
+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict])
|
||||||
|
+ self.assertEqual(utils.parseaddr([address]), expected_strict)
|
||||||
|
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||||
|
+ ('', address))
|
||||||
|
+
|
||||||
|
+ # Invalid parenthesis in comments.
|
||||||
|
+ address = 'alice@example.org )Alice('
|
||||||
|
+ self.assertEqual(utils.getaddresses([address]), [empty])
|
||||||
|
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||||
|
+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')])
|
||||||
|
+ self.assertEqual(utils.parseaddr([address]), empty)
|
||||||
|
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||||
|
+ ('', address))
|
||||||
|
+
|
||||||
|
+ # Two addresses with quotes separated by comma.
|
||||||
|
+ address = '"Jane Doe" <jane@example.net>, "John Doe" <john@example.net>'
|
||||||
|
+ self.assertEqual(utils.getaddresses([address]),
|
||||||
|
+ [('Jane Doe', 'jane@example.net'),
|
||||||
|
+ ('John Doe', 'john@example.net')])
|
||||||
|
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||||
|
+ [('Jane Doe', 'jane@example.net'),
|
||||||
|
+ ('John Doe', 'john@example.net')])
|
||||||
|
+ self.assertEqual(utils.parseaddr([address]), empty)
|
||||||
|
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||||
|
+ ('', address))
|
||||||
|
+
|
||||||
|
+ # Test email.utils.supports_strict_parsing attribute
|
||||||
|
+ self.assertEqual(email.utils.supports_strict_parsing, True)
|
||||||
|
+
|
||||||
|
def test_getaddresses_nasty(self):
|
||||||
|
- eq = self.assertEqual
|
||||||
|
- eq(utils.getaddresses(['foo: ;']), [('', '')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['[]*-- =~$']),
|
||||||
|
- [('', ''), ('', ''), ('', '*--')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
|
||||||
|
- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
|
||||||
|
+ for addresses, expected in (
|
||||||
|
+ (['"Sürname, Firstname" <to@example.com>'],
|
||||||
|
+ [('Sürname, Firstname', 'to@example.com')]),
|
||||||
|
+
|
||||||
|
+ (['foo: ;'],
|
||||||
|
+ [('', '')]),
|
||||||
|
+
|
||||||
|
+ (['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>'],
|
||||||
|
+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]),
|
||||||
|
+
|
||||||
|
+ ([r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>'],
|
||||||
|
+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]),
|
||||||
|
+
|
||||||
|
+ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'],
|
||||||
|
+ [('', '')]),
|
||||||
|
+
|
||||||
|
+ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'],
|
||||||
|
+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]),
|
||||||
|
+
|
||||||
|
+ (['John Doe <jdoe@machine(comment). example>'],
|
||||||
|
+ [('John Doe (comment)', 'jdoe@machine.example')]),
|
||||||
|
+
|
||||||
|
+ (['"Mary Smith: Personal Account" <smith@home.example>'],
|
||||||
|
+ [('Mary Smith: Personal Account', 'smith@home.example')]),
|
||||||
|
+
|
||||||
|
+ (['Undisclosed recipients:;'],
|
||||||
|
+ [('', '')]),
|
||||||
|
+
|
||||||
|
+ ([r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>'],
|
||||||
|
+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]),
|
||||||
|
+ ):
|
||||||
|
+ with self.subTest(addresses=addresses):
|
||||||
|
+ self.assertEqual(utils.getaddresses(addresses),
|
||||||
|
+ expected)
|
||||||
|
+ self.assertEqual(utils.getaddresses(addresses, strict=False),
|
||||||
|
+ expected)
|
||||||
|
+
|
||||||
|
+ addresses = ['[]*-- =~$']
|
||||||
|
+ self.assertEqual(utils.getaddresses(addresses),
|
||||||
|
+ [('', '')])
|
||||||
|
+ self.assertEqual(utils.getaddresses(addresses, strict=False),
|
||||||
|
+ [('', ''), ('', ''), ('', '*--')])
|
||||||
|
|
||||||
|
def test_getaddresses_embedded_comment(self):
|
||||||
|
"""Test proper handling of a nested comment"""
|
||||||
|
@@ -3485,6 +3608,54 @@ multipart/report
|
||||||
|
m = cls(*constructor, policy=email.policy.default)
|
||||||
|
self.assertIs(m.policy, email.policy.default)
|
||||||
|
|
||||||
|
+ def test_iter_escaped_chars(self):
|
||||||
|
+ self.assertEqual(list(utils._iter_escaped_chars(r'a\\b\"c\\"d')),
|
||||||
|
+ [(0, 'a'),
|
||||||
|
+ (2, '\\\\'),
|
||||||
|
+ (3, 'b'),
|
||||||
|
+ (5, '\\"'),
|
||||||
|
+ (6, 'c'),
|
||||||
|
+ (8, '\\\\'),
|
||||||
|
+ (9, '"'),
|
||||||
|
+ (10, 'd')])
|
||||||
|
+ self.assertEqual(list(utils._iter_escaped_chars('a\\')),
|
||||||
|
+ [(0, 'a'), (1, '\\')])
|
||||||
|
+
|
||||||
|
+ def test_strip_quoted_realnames(self):
|
||||||
|
+ def check(addr, expected):
|
||||||
|
+ self.assertEqual(utils._strip_quoted_realnames(addr), expected)
|
||||||
|
+
|
||||||
|
+ check('"Jane Doe" <jane@example.net>, "John Doe" <john@example.net>',
|
||||||
|
+ ' <jane@example.net>, <john@example.net>')
|
||||||
|
+ check(r'"Jane \"Doe\"." <jane@example.net>',
|
||||||
|
+ ' <jane@example.net>')
|
||||||
|
+
|
||||||
|
+ # special cases
|
||||||
|
+ check(r'before"name"after', 'beforeafter')
|
||||||
|
+ check(r'before"name"', 'before')
|
||||||
|
+ check(r'b"name"', 'b') # single char
|
||||||
|
+ check(r'"name"after', 'after')
|
||||||
|
+ check(r'"name"a', 'a') # single char
|
||||||
|
+ check(r'"name"', '')
|
||||||
|
+
|
||||||
|
+ # no change
|
||||||
|
+ for addr in (
|
||||||
|
+ 'Jane Doe <jane@example.net>, John Doe <john@example.net>',
|
||||||
|
+ 'lone " quote',
|
||||||
|
+ ):
|
||||||
|
+ self.assertEqual(utils._strip_quoted_realnames(addr), addr)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ def test_check_parenthesis(self):
|
||||||
|
+ addr = 'alice@example.net'
|
||||||
|
+ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)'))
|
||||||
|
+ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice('))
|
||||||
|
+ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))'))
|
||||||
|
+ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)'))
|
||||||
|
+
|
||||||
|
+ # Ignore real name between quotes
|
||||||
|
+ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}'))
|
||||||
|
+
|
||||||
|
|
||||||
|
# Test the iterator/generators
|
||||||
|
class TestIterators(TestEmailBase):
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst
|
||||||
|
@@ -0,0 +1,8 @@
|
||||||
|
+:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now
|
||||||
|
+return ``('', '')`` 2-tuples in more situations where invalid email
|
||||||
|
+addresses are encountered instead of potentially inaccurate values. Add
|
||||||
|
+optional *strict* parameter to these two functions: use ``strict=False`` to
|
||||||
|
+get the old behavior, accept malformed inputs.
|
||||||
|
+``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check
|
||||||
|
+if the *strict* paramater is available. Patch by Thomas Dwyer and Victor
|
||||||
|
+Stinner to improve the CVE-2023-27043 fix.
|
57
CVE-2023-52425-libexpat-2.6.0-backport.patch
Normal file
57
CVE-2023-52425-libexpat-2.6.0-backport.patch
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_pyexpat.py | 4 ++++
|
||||||
|
Lib/test/test_sax.py | 3 +++
|
||||||
|
Lib/test/test_xml_etree.py | 7 +++++++
|
||||||
|
3 files changed, 14 insertions(+)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_pyexpat.py
|
||||||
|
+++ b/Lib/test/test_pyexpat.py
|
||||||
|
@@ -766,6 +766,10 @@ class ReparseDeferralTest(unittest.TestC
|
||||||
|
self.assertEqual(started, ['doc'])
|
||||||
|
|
||||||
|
def test_reparse_deferral_disabled(self):
|
||||||
|
+ if expat.version_info < (2, 6, 0):
|
||||||
|
+ self.skipTest(f'Expat {expat.version_info} does not '
|
||||||
|
+ 'support reparse deferral')
|
||||||
|
+
|
||||||
|
started = []
|
||||||
|
|
||||||
|
def start_element(name, _):
|
||||||
|
--- a/Lib/test/test_sax.py
|
||||||
|
+++ b/Lib/test/test_sax.py
|
||||||
|
@@ -1240,6 +1240,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
|
||||||
|
@@ -1420,9 +1420,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)
|
||||||
|
|
||||||
|
@@ -1648,6 +1652,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'))
|
||||||
|
|
376
CVE-2024-4032-private-IP-addrs.patch
Normal file
376
CVE-2024-4032-private-IP-addrs.patch
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
From 0740166e60b8cdae9448220beb28721f0126ee03 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Petr Viktorin <encukou@gmail.com>
|
||||||
|
Date: Wed, 24 Apr 2024 14:29:30 +0200
|
||||||
|
Subject: [PATCH 1/2] gh-113171: gh-65056: Fix "private" (non-global) IP
|
||||||
|
address ranges (GH-113179) (GH-113186) (GH-118177)
|
||||||
|
|
||||||
|
* GH-113171: Fix "private" (non-global) IP address ranges (GH-113179)
|
||||||
|
|
||||||
|
The _private_networks variables, used by various is_private
|
||||||
|
implementations, were missing some ranges and at the same time had
|
||||||
|
overly strict ranges (where there are more specific ranges considered
|
||||||
|
globally reachable by the IANA registries).
|
||||||
|
|
||||||
|
This patch updates the ranges with what was missing or otherwise
|
||||||
|
incorrect.
|
||||||
|
|
||||||
|
100.64.0.0/10 is left alone, for now, as it's been made special in [1].
|
||||||
|
|
||||||
|
The _address_exclude_many() call returns 8 networks for IPv4, 121
|
||||||
|
networks for IPv6.
|
||||||
|
|
||||||
|
[1] https://github.com/python/cpython/issues/61602
|
||||||
|
|
||||||
|
* GH-65056: Improve the IP address' is_global/is_private documentation (GH-113186)
|
||||||
|
|
||||||
|
It wasn't clear what the semantics of is_global/is_private are and, when
|
||||||
|
one gets to the bottom of it, it's not quite so simple (hence the
|
||||||
|
exceptions listed).
|
||||||
|
|
||||||
|
(cherry picked from commit 2a4cbf17af19a01d942f9579342f77c39fbd23c4)
|
||||||
|
(cherry picked from commit 40d75c2b7f5c67e254d0a025e0f2e2c7ada7f69f)
|
||||||
|
|
||||||
|
---------
|
||||||
|
|
||||||
|
(cherry picked from commit f86b17ac511e68192ba71f27e752321a3252cee3)
|
||||||
|
|
||||||
|
Co-authored-by: Jakub Stasiak <jakub@stasiak.at>
|
||||||
|
---
|
||||||
|
Doc/library/ipaddress.rst | 43 +++-
|
||||||
|
Doc/whatsnew/3.10.rst | 9
|
||||||
|
Lib/ipaddress.py | 99 +++++++---
|
||||||
|
Lib/test/test_ipaddress.py | 52 +++++
|
||||||
|
Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst | 9
|
||||||
|
5 files changed, 187 insertions(+), 25 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst
|
||||||
|
|
||||||
|
--- a/Doc/library/ipaddress.rst
|
||||||
|
+++ b/Doc/library/ipaddress.rst
|
||||||
|
@@ -188,18 +188,53 @@ write code that handles both IP versions
|
||||||
|
|
||||||
|
.. attribute:: is_private
|
||||||
|
|
||||||
|
- ``True`` if the address is allocated for private networks. See
|
||||||
|
+ ``True`` if the address is defined as not globally reachable by
|
||||||
|
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||||
|
- (for IPv6).
|
||||||
|
+ (for IPv6) with the following exceptions:
|
||||||
|
+
|
||||||
|
+ * ``is_private`` is ``False`` for the shared address space (``100.64.0.0/10``)
|
||||||
|
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||||
|
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||||
|
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||||
|
+
|
||||||
|
+ address.is_private == address.ipv4_mapped.is_private
|
||||||
|
+
|
||||||
|
+ ``is_private`` has value opposite to :attr:`is_global`, except for the shared address space
|
||||||
|
+ (``100.64.0.0/10`` range) where they are both ``False``.
|
||||||
|
+
|
||||||
|
+ .. versionchanged:: 3.10.15
|
||||||
|
+
|
||||||
|
+ Fixed some false positives and false negatives.
|
||||||
|
+
|
||||||
|
+ * ``192.0.0.0/24`` is considered private with the exception of ``192.0.0.9/32`` and
|
||||||
|
+ ``192.0.0.10/32`` (previously: only the ``192.0.0.0/29`` sub-range was considered private).
|
||||||
|
+ * ``64:ff9b:1::/48`` is considered private.
|
||||||
|
+ * ``2002::/16`` is considered private.
|
||||||
|
+ * There are exceptions within ``2001::/23`` (otherwise considered private): ``2001:1::1/128``,
|
||||||
|
+ ``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``.
|
||||||
|
+ The exceptions are not considered private.
|
||||||
|
|
||||||
|
.. attribute:: is_global
|
||||||
|
|
||||||
|
- ``True`` if the address is allocated for public networks. See
|
||||||
|
+ ``True`` if the address is defined as globally reachable by
|
||||||
|
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||||
|
- (for IPv6).
|
||||||
|
+ (for IPv6) with the following exception:
|
||||||
|
+
|
||||||
|
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||||
|
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||||
|
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||||
|
+
|
||||||
|
+ address.is_global == address.ipv4_mapped.is_global
|
||||||
|
+
|
||||||
|
+ ``is_global`` has value opposite to :attr:`is_private`, except for the shared address space
|
||||||
|
+ (``100.64.0.0/10`` range) where they are both ``False``.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
+ .. versionchanged:: 3.10.15
|
||||||
|
+
|
||||||
|
+ Fixed some false positives and false negatives, see :attr:`is_private` for details.
|
||||||
|
+
|
||||||
|
.. attribute:: is_unspecified
|
||||||
|
|
||||||
|
``True`` if the address is unspecified. See :RFC:`5735` (for IPv4)
|
||||||
|
--- a/Doc/whatsnew/3.10.rst
|
||||||
|
+++ b/Doc/whatsnew/3.10.rst
|
||||||
|
@@ -2348,3 +2348,12 @@ tarfile
|
||||||
|
:exc:`DeprecationWarning`.
|
||||||
|
In Python 3.14, the default will switch to ``'data'``.
|
||||||
|
(Contributed by Petr Viktorin in :pep:`706`.)
|
||||||
|
+
|
||||||
|
+Notable changes in 3.10.15
|
||||||
|
+==========================
|
||||||
|
+
|
||||||
|
+ipaddress
|
||||||
|
+---------
|
||||||
|
+
|
||||||
|
+* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
|
||||||
|
+ ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.
|
||||||
|
--- a/Lib/ipaddress.py
|
||||||
|
+++ b/Lib/ipaddress.py
|
||||||
|
@@ -1323,18 +1323,41 @@ class IPv4Address(_BaseV4, _BaseAddress)
|
||||||
|
@property
|
||||||
|
@functools.lru_cache()
|
||||||
|
def is_private(self):
|
||||||
|
- """Test if this address is allocated for private networks.
|
||||||
|
-
|
||||||
|
- Returns:
|
||||||
|
- A boolean, True if the address is reserved per
|
||||||
|
- iana-ipv4-special-registry.
|
||||||
|
-
|
||||||
|
- """
|
||||||
|
- return any(self in net for net in self._constants._private_networks)
|
||||||
|
+ """``True`` if the address is defined as not globally reachable by
|
||||||
|
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||||
|
+ (for IPv6) with the following exceptions:
|
||||||
|
+
|
||||||
|
+ * ``is_private`` is ``False`` for ``100.64.0.0/10``
|
||||||
|
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||||
|
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||||
|
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||||
|
+
|
||||||
|
+ address.is_private == address.ipv4_mapped.is_private
|
||||||
|
+
|
||||||
|
+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
|
||||||
|
+ IPv4 range where they are both ``False``.
|
||||||
|
+ """
|
||||||
|
+ return (
|
||||||
|
+ any(self in net for net in self._constants._private_networks)
|
||||||
|
+ and all(self not in net for net in self._constants._private_networks_exceptions)
|
||||||
|
+ )
|
||||||
|
|
||||||
|
@property
|
||||||
|
@functools.lru_cache()
|
||||||
|
def is_global(self):
|
||||||
|
+ """``True`` if the address is defined as globally reachable by
|
||||||
|
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||||
|
+ (for IPv6) with the following exception:
|
||||||
|
+
|
||||||
|
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||||
|
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||||
|
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||||
|
+
|
||||||
|
+ address.is_global == address.ipv4_mapped.is_global
|
||||||
|
+
|
||||||
|
+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
|
||||||
|
+ IPv4 range where they are both ``False``.
|
||||||
|
+ """
|
||||||
|
return self not in self._constants._public_network and not self.is_private
|
||||||
|
|
||||||
|
@property
|
||||||
|
@@ -1538,13 +1561,15 @@ class _IPv4Constants:
|
||||||
|
|
||||||
|
_public_network = IPv4Network('100.64.0.0/10')
|
||||||
|
|
||||||
|
+ # Not globally reachable address blocks listed on
|
||||||
|
+ # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
||||||
|
_private_networks = [
|
||||||
|
IPv4Network('0.0.0.0/8'),
|
||||||
|
IPv4Network('10.0.0.0/8'),
|
||||||
|
IPv4Network('127.0.0.0/8'),
|
||||||
|
IPv4Network('169.254.0.0/16'),
|
||||||
|
IPv4Network('172.16.0.0/12'),
|
||||||
|
- IPv4Network('192.0.0.0/29'),
|
||||||
|
+ IPv4Network('192.0.0.0/24'),
|
||||||
|
IPv4Network('192.0.0.170/31'),
|
||||||
|
IPv4Network('192.0.2.0/24'),
|
||||||
|
IPv4Network('192.168.0.0/16'),
|
||||||
|
@@ -1555,6 +1580,11 @@ class _IPv4Constants:
|
||||||
|
IPv4Network('255.255.255.255/32'),
|
||||||
|
]
|
||||||
|
|
||||||
|
+ _private_networks_exceptions = [
|
||||||
|
+ IPv4Network('192.0.0.9/32'),
|
||||||
|
+ IPv4Network('192.0.0.10/32'),
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
_reserved_network = IPv4Network('240.0.0.0/4')
|
||||||
|
|
||||||
|
_unspecified_address = IPv4Address('0.0.0.0')
|
||||||
|
@@ -1996,27 +2026,42 @@ class IPv6Address(_BaseV6, _BaseAddress)
|
||||||
|
@property
|
||||||
|
@functools.lru_cache()
|
||||||
|
def is_private(self):
|
||||||
|
- """Test if this address is allocated for private networks.
|
||||||
|
+ """``True`` if the address is defined as not globally reachable by
|
||||||
|
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||||
|
+ (for IPv6) with the following exceptions:
|
||||||
|
+
|
||||||
|
+ * ``is_private`` is ``False`` for ``100.64.0.0/10``
|
||||||
|
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||||
|
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||||
|
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||||
|
|
||||||
|
- Returns:
|
||||||
|
- A boolean, True if the address is reserved per
|
||||||
|
- iana-ipv6-special-registry, or is ipv4_mapped and is
|
||||||
|
- reserved in the iana-ipv4-special-registry.
|
||||||
|
+ address.is_private == address.ipv4_mapped.is_private
|
||||||
|
|
||||||
|
+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
|
||||||
|
+ IPv4 range where they are both ``False``.
|
||||||
|
"""
|
||||||
|
ipv4_mapped = self.ipv4_mapped
|
||||||
|
if ipv4_mapped is not None:
|
||||||
|
return ipv4_mapped.is_private
|
||||||
|
- return any(self in net for net in self._constants._private_networks)
|
||||||
|
+ return (
|
||||||
|
+ any(self in net for net in self._constants._private_networks)
|
||||||
|
+ and all(self not in net for net in self._constants._private_networks_exceptions)
|
||||||
|
+ )
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_global(self):
|
||||||
|
- """Test if this address is allocated for public networks.
|
||||||
|
+ """``True`` if the address is defined as globally reachable by
|
||||||
|
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||||
|
+ (for IPv6) with the following exception:
|
||||||
|
+
|
||||||
|
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||||
|
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||||
|
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||||
|
|
||||||
|
- Returns:
|
||||||
|
- A boolean, true if the address is not reserved per
|
||||||
|
- iana-ipv6-special-registry.
|
||||||
|
+ address.is_global == address.ipv4_mapped.is_global
|
||||||
|
|
||||||
|
+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
|
||||||
|
+ IPv4 range where they are both ``False``.
|
||||||
|
"""
|
||||||
|
return not self.is_private
|
||||||
|
|
||||||
|
@@ -2257,19 +2302,31 @@ class _IPv6Constants:
|
||||||
|
|
||||||
|
_multicast_network = IPv6Network('ff00::/8')
|
||||||
|
|
||||||
|
+ # Not globally reachable address blocks listed on
|
||||||
|
+ # https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
|
||||||
|
_private_networks = [
|
||||||
|
IPv6Network('::1/128'),
|
||||||
|
IPv6Network('::/128'),
|
||||||
|
IPv6Network('::ffff:0:0/96'),
|
||||||
|
+ IPv6Network('64:ff9b:1::/48'),
|
||||||
|
IPv6Network('100::/64'),
|
||||||
|
IPv6Network('2001::/23'),
|
||||||
|
- IPv6Network('2001:2::/48'),
|
||||||
|
IPv6Network('2001:db8::/32'),
|
||||||
|
- IPv6Network('2001:10::/28'),
|
||||||
|
+ # IANA says N/A, let's consider it not globally reachable to be safe
|
||||||
|
+ IPv6Network('2002::/16'),
|
||||||
|
IPv6Network('fc00::/7'),
|
||||||
|
IPv6Network('fe80::/10'),
|
||||||
|
]
|
||||||
|
|
||||||
|
+ _private_networks_exceptions = [
|
||||||
|
+ IPv6Network('2001:1::1/128'),
|
||||||
|
+ IPv6Network('2001:1::2/128'),
|
||||||
|
+ IPv6Network('2001:3::/32'),
|
||||||
|
+ IPv6Network('2001:4:112::/48'),
|
||||||
|
+ IPv6Network('2001:20::/28'),
|
||||||
|
+ IPv6Network('2001:30::/28'),
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
_reserved_networks = [
|
||||||
|
IPv6Network('::/8'), IPv6Network('100::/8'),
|
||||||
|
IPv6Network('200::/7'), IPv6Network('400::/6'),
|
||||||
|
--- a/Lib/test/test_ipaddress.py
|
||||||
|
+++ b/Lib/test/test_ipaddress.py
|
||||||
|
@@ -2263,6 +2263,10 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
|
self.assertEqual(True, ipaddress.ip_address(
|
||||||
|
'172.31.255.255').is_private)
|
||||||
|
self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global)
|
||||||
|
|
||||||
|
self.assertEqual(True,
|
||||||
|
ipaddress.ip_address('169.254.100.200').is_link_local)
|
||||||
|
@@ -2278,6 +2282,40 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
|
self.assertEqual(False, ipaddress.ip_address('128.0.0.0').is_loopback)
|
||||||
|
self.assertEqual(True, ipaddress.ip_network('0.0.0.0').is_unspecified)
|
||||||
|
|
||||||
|
+ def testPrivateNetworks(self):
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/0").is_private)
|
||||||
|
+ self.assertEqual(False, ipaddress.ip_network("1.0.0.0/8").is_private)
|
||||||
|
+
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/8").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("10.0.0.0/8").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("127.0.0.0/8").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("169.254.0.0/16").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("172.16.0.0/12").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("192.0.0.0/29").is_private)
|
||||||
|
+ self.assertEqual(False, ipaddress.ip_network("192.0.0.9/32").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("192.0.0.170/31").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("192.0.2.0/24").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("192.168.0.0/16").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("198.18.0.0/15").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("198.51.100.0/24").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("203.0.113.0/24").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("240.0.0.0/4").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("255.255.255.255/32").is_private)
|
||||||
|
+
|
||||||
|
+ self.assertEqual(False, ipaddress.ip_network("::/0").is_private)
|
||||||
|
+ self.assertEqual(False, ipaddress.ip_network("::ff/128").is_private)
|
||||||
|
+
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("::1/128").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("::/128").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("::ffff:0:0/96").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("100::/64").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("2001:2::/48").is_private)
|
||||||
|
+ self.assertEqual(False, ipaddress.ip_network("2001:3::/48").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("2001:db8::/32").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("2001:10::/28").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("fc00::/7").is_private)
|
||||||
|
+ self.assertEqual(True, ipaddress.ip_network("fe80::/10").is_private)
|
||||||
|
+
|
||||||
|
def testReservedIpv6(self):
|
||||||
|
|
||||||
|
self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast)
|
||||||
|
@@ -2351,6 +2389,20 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||||
|
self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified)
|
||||||
|
self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified)
|
||||||
|
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('2001::').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('2001:1::1').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('2001:1::2').is_global)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('2001:2::').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('2001:3::').is_global)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('2001:4::').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('2001:10::').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('2001:20::').is_global)
|
||||||
|
+ self.assertTrue(ipaddress.ip_address('2001:30::').is_global)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('2001:40::').is_global)
|
||||||
|
+ self.assertFalse(ipaddress.ip_address('2002::').is_global)
|
||||||
|
+
|
||||||
|
# some generic IETF reserved addresses
|
||||||
|
self.assertEqual(True, ipaddress.ip_address('100::').is_reserved)
|
||||||
|
self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved)
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst
|
||||||
|
@@ -0,0 +1,9 @@
|
||||||
|
+Fixed various false positives and false negatives in
|
||||||
|
+
|
||||||
|
+* :attr:`ipaddress.IPv4Address.is_private` (see these docs for details)
|
||||||
|
+* :attr:`ipaddress.IPv4Address.is_global`
|
||||||
|
+* :attr:`ipaddress.IPv6Address.is_private`
|
||||||
|
+* :attr:`ipaddress.IPv6Address.is_global`
|
||||||
|
+
|
||||||
|
+Also in the corresponding :class:`ipaddress.IPv4Network` and :class:`ipaddress.IPv6Network`
|
||||||
|
+attributes.
|
187
F00251-change-user-install-location.patch
Normal file
187
F00251-change-user-install-location.patch
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
Index: Python-3.10.14/Lib/distutils/command/install.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/distutils/command/install.py
|
||||||
|
+++ Python-3.10.14/Lib/distutils/command/install.py
|
||||||
|
@@ -441,8 +441,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:
|
||||||
|
Index: Python-3.10.14/Lib/site.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/site.py
|
||||||
|
+++ Python-3.10.14/Lib/site.py
|
||||||
|
@@ -390,8 +390,15 @@ 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.
|
||||||
|
+
|
||||||
|
+ """
|
||||||
|
_trace("Processing global site-packages")
|
||||||
|
+ 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)
|
||||||
|
Index: Python-3.10.14/Lib/sysconfig.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/sysconfig.py
|
||||||
|
+++ Python-3.10.14/Lib/sysconfig.py
|
||||||
|
@@ -117,6 +117,19 @@ if _HAS_USER_BASE:
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
+# This is used by distutils.command.install in the stdlib
|
||||||
|
+# as well as pypa/distutils (e.g. bundled in setuptools).
|
||||||
|
+# The self.prefix value is set to sys.prefix + /local/
|
||||||
|
+# if neither RPM build nor virtual environment is
|
||||||
|
+# detected to make distutils install packages
|
||||||
|
+# into the separate location.
|
||||||
|
+# https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+if (not (hasattr(sys, 'real_prefix') or
|
||||||
|
+ sys.prefix != sys.base_prefix) and
|
||||||
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
|
+ _prefix_addition = '/local'
|
||||||
|
+
|
||||||
|
+
|
||||||
|
_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
|
||||||
|
'scripts', 'data')
|
||||||
|
|
||||||
|
@@ -136,6 +149,16 @@ _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)
|
||||||
|
_findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
|
||||||
|
_findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"
|
||||||
|
|
||||||
|
+# For a brief period of time in the Fedora 36 life cycle,
|
||||||
|
+# this installation scheme existed and was documented in the release notes.
|
||||||
|
+# For backwards compatibility, we keep it here (at least on 3.10 and 3.11).
|
||||||
|
+_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix']
|
||||||
|
+
|
||||||
|
+# For a brief period of time in the Fedora 36 life cycle,
|
||||||
|
+# this installation scheme existed and was documented in the release notes.
|
||||||
|
+# For backwards compatibility, we keep it here (at least on 3.10 and 3.11).
|
||||||
|
+_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix']
|
||||||
|
+
|
||||||
|
|
||||||
|
def _safe_realpath(path):
|
||||||
|
try:
|
||||||
|
@@ -211,11 +234,39 @@ def _extend_dict(target_dict, other_dict
|
||||||
|
target_dict[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
+_CONFIG_VARS_LOCAL = None
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _config_vars_local():
|
||||||
|
+ # This function returns the config vars with prefixes amended to /usr/local
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ global _CONFIG_VARS_LOCAL
|
||||||
|
+ if _CONFIG_VARS_LOCAL is None:
|
||||||
|
+ _CONFIG_VARS_LOCAL = dict(get_config_vars())
|
||||||
|
+ _CONFIG_VARS_LOCAL['base'] = '/usr/local'
|
||||||
|
+ _CONFIG_VARS_LOCAL['platbase'] = '/usr/local'
|
||||||
|
+ return _CONFIG_VARS_LOCAL
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def _expand_vars(scheme, vars):
|
||||||
|
res = {}
|
||||||
|
if vars is None:
|
||||||
|
vars = {}
|
||||||
|
- _extend_dict(vars, get_config_vars())
|
||||||
|
+
|
||||||
|
+ # when we are not in a virtual environment or an RPM build
|
||||||
|
+ # we change '/usr' to '/usr/local'
|
||||||
|
+ # to avoid surprises, we explicitly check for the /usr/ prefix
|
||||||
|
+ # Python virtual environments have different prefixes
|
||||||
|
+ # we only do this for posix_prefix, not to mangle the venv scheme
|
||||||
|
+ # posix_prefix is used by sudo pip install
|
||||||
|
+ # we only change the defaults here, so explicit --prefix will take precedence
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ if (scheme == 'posix_prefix' and
|
||||||
|
+ _PREFIX == '/usr' and
|
||||||
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
|
+ _extend_dict(vars, _config_vars_local())
|
||||||
|
+ else:
|
||||||
|
+ _extend_dict(vars, get_config_vars())
|
||||||
|
|
||||||
|
for key, value in _INSTALL_SCHEMES[scheme].items():
|
||||||
|
if os.name in ('posix', 'nt'):
|
||||||
|
Index: Python-3.10.14/Lib/test/test_sysconfig.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.14.orig/Lib/test/test_sysconfig.py
|
||||||
|
+++ Python-3.10.14/Lib/test/test_sysconfig.py
|
||||||
|
@@ -105,8 +105,19 @@ class TestSysConfig(unittest.TestCase):
|
||||||
|
for scheme in _INSTALL_SCHEMES:
|
||||||
|
for name in _INSTALL_SCHEMES[scheme]:
|
||||||
|
expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
|
||||||
|
+ tested = get_path(name, scheme)
|
||||||
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
|
+ if tested.startswith('/usr/local'):
|
||||||
|
+ # /usr/local should only be used in posix_prefix
|
||||||
|
+ self.assertEqual(scheme, 'posix_prefix')
|
||||||
|
+ # Fedora CI runs tests for venv and virtualenv that check for other prefixes
|
||||||
|
+ self.assertEqual(sys.prefix, '/usr')
|
||||||
|
+ # When building the RPM of Python, %check runs this with RPM_BUILD_ROOT set
|
||||||
|
+ # Fedora CI runs this with RPM_BUILD_ROOT unset
|
||||||
|
+ self.assertNotIn('RPM_BUILD_ROOT', os.environ)
|
||||||
|
+ tested = tested.replace('/usr/local', '/usr')
|
||||||
|
self.assertEqual(
|
||||||
|
- os.path.normpath(get_path(name, scheme)),
|
||||||
|
+ os.path.normpath(tested),
|
||||||
|
os.path.normpath(expected),
|
||||||
|
)
|
||||||
|
|
||||||
|
@@ -263,7 +274,7 @@ class TestSysConfig(unittest.TestCase):
|
||||||
|
self.assertTrue(os.path.isfile(config_h), config_h)
|
||||||
|
|
||||||
|
def test_get_scheme_names(self):
|
||||||
|
- wanted = ['nt', 'posix_home', 'posix_prefix']
|
||||||
|
+ wanted = ['nt', 'posix_home', 'posix_prefix', 'rpm_prefix']
|
||||||
|
if HAS_USER_BASE:
|
||||||
|
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
|
||||||
|
self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
|
||||||
|
@@ -274,6 +285,8 @@ class TestSysConfig(unittest.TestCase):
|
||||||
|
cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
|
||||||
|
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
||||||
|
|
||||||
|
+ @unittest.skipIf('RPM_BUILD_ROOT' not in os.environ,
|
||||||
|
+ "Test doesn't expect Fedora's paths")
|
||||||
|
def test_user_similar(self):
|
||||||
|
# Issue #8759: make sure the posix scheme for the users
|
||||||
|
# is similar to the global posix_prefix one
|
26
PACKAGING-NOTES
Normal file
26
PACKAGING-NOTES
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Notes for packagers of Python3
|
||||||
|
==============================
|
||||||
|
|
||||||
|
0. Faster build turnaround
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
By default, python builds with profile-guided optimization. This needs
|
||||||
|
an additional run of the test suite and it is generally slow.
|
||||||
|
PGO build takes around 50 minutes.
|
||||||
|
|
||||||
|
For development, use "--without profileopt" option to disable PGO. This
|
||||||
|
shortens the build time to ~5 minutes including test suite.
|
||||||
|
|
||||||
|
1. import_failed.map
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
This is a mechanism installed as part of python3-base, that places shim modules
|
||||||
|
on python's path (through a generated zzzz-import-failed-hooks.pth file, so that
|
||||||
|
it is imported as much at the end as makes sense; and an _import_failed subdir
|
||||||
|
of /usr/lib/pythonX.Y). Then when the user tries to import a module that is part
|
||||||
|
of a subpackage, the ImportError will contain a helpful message telling them
|
||||||
|
which missing subpackage to install.
|
||||||
|
|
||||||
|
This can sometimes cause problems on non-standard configurations, if the pth
|
||||||
|
gets included too early (for instance if you are using a script to include all
|
||||||
|
pths by hand in some strange order). Just something to look out for.
|
3
Python-3.10.14.tar.xz
Normal file
3
Python-3.10.14.tar.xz
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9c50481faa8c2832329ba0fc8868d0a606a680fc4f60ec48d26ce8e076751fda
|
||||||
|
size 19600188
|
16
Python-3.10.14.tar.xz.asc
Normal file
16
Python-3.10.14.tar.xz.asc
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmX76AkACgkQ/+h0BBaL
|
||||||
|
2EeKvhAAuN+7X3iFv8tYwUTbKJT9x9fLsADI5wOn5j8xuDiXQCOMzsqqyB1RSdEd
|
||||||
|
tbCQXg9XJj1bVHc4DY337vUix9jvFcTqbQqlzUm/peX4buY2bKkZu2quti1iWSJf
|
||||||
|
IN26jDYO2TobPvGdvNiH2Hceqe1dc5tU7iYEfaLR5ImgO4aGgK6x4DiLdmFqo2bk
|
||||||
|
ZZWZLkXbwenrSdLVmUZLP5Gg2dsfMkbfFpydau9Zk3RVl6mVYATwzJaY9K5otC0K
|
||||||
|
7kc+nKPwkTxKAjndbznjsVrWK0Xcr4hrlMHs4Re2Nrdqa2mVd1jAAFO5xETJJtd7
|
||||||
|
YqL6mQuJ9wQfEEq2QWz1hEi67l8g8VeEgzYQOjZ6pTxwYYt0YDfKBjRtRCWuJ11c
|
||||||
|
w6Q+pniGcgIHAMkQGjZds88CwAdIiyG7IAIT2ovW+xVxH/JqLPHeRsHMKYx4DPqL
|
||||||
|
2y23Tchw+gBUvmbwCdObXWL1eq5R3Xz3ikkdX/I6zknmEvgPTi5N59C1IQqh0W/6
|
||||||
|
8uMrHOdELz9I5Fd+zGTJ8iyh/wrecMiIx+HOsBTYv/FYbMVnQUshUBOiD70geUb5
|
||||||
|
uSeHyxl/P7VK/0phbxOznU4oDot2fHPmZRK3q+K67J9L16q7pEou1AJAw8E7ed5C
|
||||||
|
Ywf+y2tdxsuqChQK/OA6uuqW6rXjZPuCoG5Bn6YIEuU769LsHcY=
|
||||||
|
=1PBR
|
||||||
|
-----END PGP SIGNATURE-----
|
43
README.SUSE
Normal file
43
README.SUSE
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
Python 3 in SUSE
|
||||||
|
==============
|
||||||
|
|
||||||
|
* Subpackages *
|
||||||
|
|
||||||
|
Python 3 is split into several subpackages, based on external dependencies.
|
||||||
|
The main package 'python3' has soft dependencies on all subpackages needed to
|
||||||
|
assemble the standard library; however, these might not all be installed by default.
|
||||||
|
|
||||||
|
If you attempt to import a module that is currently not installed, an ImportError is thrown,
|
||||||
|
with instructions to install the missing subpackage. Installing the subpackage might result
|
||||||
|
in installing libraries that the subpackage requires to function.
|
||||||
|
|
||||||
|
|
||||||
|
* ensurepip *
|
||||||
|
|
||||||
|
The 'ensurepip' module from Python 3 standard library (PEP 453) is supposed to deploy
|
||||||
|
a bundled copy of the pip installer. This makes no sense in a managed distribution like SUSE.
|
||||||
|
Instead, you need to install package 'python3-pip'. Usually this will be installed automatically
|
||||||
|
with 'python3'.
|
||||||
|
|
||||||
|
Using 'ensurepip' when pip is not installed will result in an ImportError with instructions
|
||||||
|
to install 'python3-pip'.
|
||||||
|
|
||||||
|
|
||||||
|
* Documentation *
|
||||||
|
|
||||||
|
You can find documentation in seprarate packages: python3-doc and
|
||||||
|
python3-doc-pdf. These contan following documents:
|
||||||
|
|
||||||
|
Tutorial, What's New in Python, Global Module Index, Library Reference,
|
||||||
|
Macintosh Module Reference, Installing Python Modules, Distributing Python
|
||||||
|
Modules, Language Reference, Extending and Embedding, Python/C API,
|
||||||
|
Documenting Python
|
||||||
|
|
||||||
|
The python3-doc package constains many text files from source tarball.
|
||||||
|
|
||||||
|
|
||||||
|
* Interactive mode *
|
||||||
|
|
||||||
|
Interactive mode is by default enhanced with of history and command completion.
|
||||||
|
If you don't like these features, you can unset the PYTHONSTARTUP variable
|
||||||
|
in your .profile or disable it system wide in /etc/profile.d/python.sh.
|
4
_multibuild
Normal file
4
_multibuild
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<multibuild>
|
||||||
|
<package>base</package>
|
||||||
|
<package>doc</package>
|
||||||
|
</multibuild>
|
3
baselibs.conf
Normal file
3
baselibs.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
python310-base
|
||||||
|
python310
|
||||||
|
libpython3_10-1_0
|
BIN
bluez-devel-vendor.tar.xz
(Stored with Git LFS)
Normal file
BIN
bluez-devel-vendor.tar.xz
(Stored with Git LFS)
Normal file
Binary file not shown.
163
bpo-31046_ensurepip_honours_prefix.patch
Normal file
163
bpo-31046_ensurepip_honours_prefix.patch
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
From 5754521af1d51aa8e445cba07a093bbc0c88596d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Zackery Spytz <zspytz@gmail.com>
|
||||||
|
Date: Mon, 16 Dec 2019 18:24:08 -0700
|
||||||
|
Subject: [PATCH] bpo-31046: ensurepip does not honour the value of $(prefix)
|
||||||
|
|
||||||
|
Co-Authored-By: Xavier de Gaye <xdegaye@gmail.com>
|
||||||
|
---
|
||||||
|
Doc/library/ensurepip.rst | 9 +++--
|
||||||
|
Lib/ensurepip/__init__.py | 18 +++++++---
|
||||||
|
Lib/test/test_ensurepip.py | 11 ++++++
|
||||||
|
Makefile.pre.in | 4 +-
|
||||||
|
Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst | 1
|
||||||
|
5 files changed, 34 insertions(+), 9 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst
|
||||||
|
|
||||||
|
--- a/Doc/library/ensurepip.rst
|
||||||
|
+++ b/Doc/library/ensurepip.rst
|
||||||
|
@@ -58,8 +58,9 @@ is at least as recent as the one availab
|
||||||
|
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.
|
||||||
|
@@ -91,7 +92,7 @@ Module API
|
||||||
|
Returns a string specifying the available 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)
|
||||||
|
|
||||||
|
@@ -101,6 +102,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 available version.
|
||||||
|
|
||||||
|
@@ -121,6 +124,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
|
||||||
|
@@ -123,27 +123,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.
|
||||||
|
"""
|
||||||
|
@@ -193,6 +193,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:
|
||||||
|
@@ -268,6 +270,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,
|
||||||
|
@@ -286,6 +293,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
|
||||||
|
@@ -112,6 +112,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
|
||||||
|
@@ -1278,7 +1278,7 @@ install: @FRAMEWORKINSTALLFIRST@ commoni
|
||||||
|
install|*) ensurepip="" ;; \
|
||||||
|
esac; \
|
||||||
|
$(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \
|
||||||
|
- $$ensurepip --root=$(DESTDIR)/ ; \
|
||||||
|
+ $$ensurepip --root=$(DESTDIR)/ --prefix=$(prefix) ; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
altinstall: commoninstall
|
||||||
|
@@ -1288,7 +1288,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`.
|
103
bpo-37596-make-set-marshalling.patch
Normal file
103
bpo-37596-make-set-marshalling.patch
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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,5 +1,6 @@
|
||||||
|
from test import support
|
||||||
|
from test.support import os_helper
|
||||||
|
+from test.support.script_helper import assert_python_ok
|
||||||
|
import array
|
||||||
|
import io
|
||||||
|
import marshal
|
||||||
|
@@ -318,6 +319,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;
|
30
distutils-reproducible-compile.patch
Normal file
30
distutils-reproducible-compile.patch
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
Index: Python-3.10.5/Lib/compileall.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.5.orig/Lib/compileall.py
|
||||||
|
+++ Python-3.10.5/Lib/compileall.py
|
||||||
|
@@ -96,6 +96,12 @@ def compile_dir(dir, maxlevels=None, ddi
|
||||||
|
maxlevels = sys.getrecursionlimit()
|
||||||
|
files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels)
|
||||||
|
success = True
|
||||||
|
+ # work around https://github.com/python/cpython/issues/93317
|
||||||
|
+ sys.intern('{')
|
||||||
|
+ sys.intern('{{')
|
||||||
|
+ sys.intern('}')
|
||||||
|
+ sys.intern('}}')
|
||||||
|
+
|
||||||
|
if workers != 1 and ProcessPoolExecutor is not None:
|
||||||
|
# If workers == 0, let ProcessPoolExecutor choose
|
||||||
|
workers = workers or None
|
||||||
|
Index: Python-3.10.5/Lib/distutils/util.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.5.orig/Lib/distutils/util.py
|
||||||
|
+++ Python-3.10.5/Lib/distutils/util.py
|
||||||
|
@@ -436,7 +436,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.
|
3049
fix-sphinx-72.patch
Normal file
3049
fix-sphinx-72.patch
Normal file
File diff suppressed because it is too large
Load Diff
40
fix_configure_rst.patch
Normal file
40
fix_configure_rst.patch
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
Doc/using/configure.rst | 3 ---
|
||||||
|
Misc/NEWS | 2 +-
|
||||||
|
2 files changed, 1 insertion(+), 4 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/using/configure.rst
|
||||||
|
+++ b/Doc/using/configure.rst
|
||||||
|
@@ -42,7 +42,6 @@ General Options
|
||||||
|
|
||||||
|
See :data:`sys.int_info.bits_per_digit <sys.int_info>`.
|
||||||
|
|
||||||
|
-.. cmdoption:: --with-cxx-main
|
||||||
|
.. cmdoption:: --with-cxx-main=COMPILER
|
||||||
|
|
||||||
|
Compile the Python ``main()`` function and link Python executable with C++
|
||||||
|
@@ -473,13 +472,11 @@ macOS Options
|
||||||
|
|
||||||
|
See ``Mac/README.rst``.
|
||||||
|
|
||||||
|
-.. cmdoption:: --enable-universalsdk
|
||||||
|
.. cmdoption:: --enable-universalsdk=SDKDIR
|
||||||
|
|
||||||
|
Create a universal binary build. *SDKDIR* specifies which macOS SDK should
|
||||||
|
be used to perform the build (default is no).
|
||||||
|
|
||||||
|
-.. cmdoption:: --enable-framework
|
||||||
|
.. cmdoption:: --enable-framework=INSTALLDIR
|
||||||
|
|
||||||
|
Create a Python.framework rather than a traditional Unix install. Optional
|
||||||
|
--- a/Misc/NEWS
|
||||||
|
+++ b/Misc/NEWS
|
||||||
|
@@ -3731,7 +3731,7 @@ C API
|
||||||
|
-----
|
||||||
|
|
||||||
|
- bpo-43795: The list in :ref:`stable-abi-list` now shows the public name
|
||||||
|
- :c:struct:`PyFrameObject` rather than ``_frame``. The non-existing entry
|
||||||
|
+ :c:type:`PyFrameObject` rather than ``_frame``. The non-existing entry
|
||||||
|
``_node`` no longer appears in the list.
|
||||||
|
|
||||||
|
- bpo-44378: :c:func:`Py_IS_TYPE` no longer uses :c:func:`Py_TYPE` to avoid
|
30
gh-78214-marshal_stabilize_FLAG_REF.patch
Normal file
30
gh-78214-marshal_stabilize_FLAG_REF.patch
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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(-)
|
||||||
|
|
||||||
|
Index: Python-3.10.12/Python/marshal.c
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.12.orig/Python/marshal.c
|
||||||
|
+++ Python-3.10.12/Python/marshal.c
|
||||||
|
@@ -298,9 +298,14 @@ w_ref(PyObject *v, char *flag, WFILE *p)
|
||||||
|
if (p->version < 3 || p->hashtable == NULL)
|
||||||
|
return 0; /* not writing object references */
|
||||||
|
|
||||||
|
- /* if it has only one reference, it definitely isn't shared */
|
||||||
|
- if (Py_REFCNT(v) == 1)
|
||||||
|
+ /* If it has only one reference, it definitely isn't shared.
|
||||||
|
+ * But we use TYPE_REF always for interned string, to PYC file stable
|
||||||
|
+ * as possible.
|
||||||
|
+ */
|
||||||
|
+ if (Py_REFCNT(v) == 1 &&
|
||||||
|
+ !(PyUnicode_CheckExact(v) && PyUnicode_CHECK_INTERNED(v))) {
|
||||||
|
return 0;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
entry = _Py_hashtable_get_entry(p->hashtable, v);
|
||||||
|
if (entry != NULL) {
|
35
idle3.appdata.xml
Normal file
35
idle3.appdata.xml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!-- Copyright 2017 Zbigniew Jędrzejewski-Szmek -->
|
||||||
|
<application>
|
||||||
|
<id type="desktop">idle3.desktop</id>
|
||||||
|
<name>IDLE3</name>
|
||||||
|
<metadata_licence>CC0</metadata_licence>
|
||||||
|
<project_license>Python-2.0</project_license>
|
||||||
|
<summary>Python 3 Integrated Development and Learning Environment</summary>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
IDLE is Python’s Integrated Development and Learning Environment.
|
||||||
|
The GUI is uniform between Windows, Unix, and Mac OS X.
|
||||||
|
IDLE provides an easy way to start writing, running, and debugging
|
||||||
|
Python code.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
IDLE is written in pure Python, and uses the tkinter GUI toolkit.
|
||||||
|
It provides:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,</li>
|
||||||
|
<li>a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,</li>
|
||||||
|
<li>search within any window, replace within editor windows, and search through multiple files (grep),</li>
|
||||||
|
<li>a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
<url type="homepage">https://docs.python.org/3/library/idle.html</url>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</screenshot>
|
||||||
|
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</screenshot>
|
||||||
|
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<update_contact>zbyszek@in.waw.pl</update_contact>
|
||||||
|
</application>
|
12
idle3.desktop
Normal file
12
idle3.desktop
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=IDLE 3
|
||||||
|
GenericName=Python 3 IDE
|
||||||
|
Comment=Python 3 Integrated Development and Learning Environment
|
||||||
|
Exec=idle3 %F
|
||||||
|
TryExec=idle3
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Icon=idle3
|
||||||
|
Categories=Development;IDE;
|
||||||
|
MimeType=text/x-python;
|
7
import_failed.map
Normal file
7
import_failed.map
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
python310-curses: curses _curses _curses_panel
|
||||||
|
python310-dbm: dbm _dbm _gdbm
|
||||||
|
python310-idle: idlelib
|
||||||
|
python310-testsuite: test _ctypes_test _testbuffer _testcapi _testinternalcapi _testimportmultiple _testmultiphase xxlimited
|
||||||
|
python310-tk: tkinter _tkinter
|
||||||
|
python310-tools: turtledemo
|
||||||
|
python310: sqlite3 readline _sqlite3 nis
|
23
import_failed.py
Normal file
23
import_failed.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import sys, os
|
||||||
|
from sysconfig import get_path
|
||||||
|
|
||||||
|
failed_map_path = os.path.join(get_path('stdlib'), '_import_failed', 'import_failed.map')
|
||||||
|
|
||||||
|
if __spec__:
|
||||||
|
failed_name = __spec__.name
|
||||||
|
else:
|
||||||
|
failed_name = __name__
|
||||||
|
|
||||||
|
with open(failed_map_path) as fd:
|
||||||
|
for line in fd:
|
||||||
|
package = line.split(':')[0]
|
||||||
|
imports = line.split(':')[1]
|
||||||
|
if failed_name in imports:
|
||||||
|
raise ImportError(f"""Module '{failed_name}' is not installed.
|
||||||
|
Use:
|
||||||
|
sudo zypper install {package}
|
||||||
|
to install it.""")
|
||||||
|
|
||||||
|
raise ImportError(f"""Module '{failed_name}' is not installed.
|
||||||
|
It is supposed to be part of python3 distribution, but missing from failed import map.
|
||||||
|
Please file a bug on the SUSE Bugzilla.""")
|
28
macros.python3
Normal file
28
macros.python3
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
%have_python3 1
|
||||||
|
|
||||||
|
# commented out legacy macro definitions
|
||||||
|
#py3_prefix /usr
|
||||||
|
#py3_incdir /usr/include/python3.5m
|
||||||
|
#py3_ver 3.5
|
||||||
|
|
||||||
|
# these should now be provided by macros.python_all
|
||||||
|
#python3_sitearch /usr/lib64/python3.5/site-packages
|
||||||
|
#python3_sitelib /usr/lib/python3.5/site-packages
|
||||||
|
#python3_version 3.5
|
||||||
|
|
||||||
|
# hard to say if anyone ever used these?
|
||||||
|
#py3_soflags cpython-35m-x86_64-linux-gnu
|
||||||
|
#py3_abiflags m
|
||||||
|
%cpython3_soabi %(python3 -c "import sysconfig; print(sysconfig.get_config_var('SOABI'))")
|
||||||
|
%py3_soflags %cpython3_soabi
|
||||||
|
|
||||||
|
# compilation macros that might be in use somewhere
|
||||||
|
%py3_compile(O) \
|
||||||
|
find %1 -name '*.pyc' -exec rm -f {} ";"\
|
||||||
|
python3 -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1\
|
||||||
|
%{-O:\
|
||||||
|
find %1 -name '*.pyo' -exec rm -f {} ";"\
|
||||||
|
python3 -O -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1\
|
||||||
|
}
|
||||||
|
|
||||||
|
|
653
no-skipif-doctests.patch
Normal file
653
no-skipif-doctests.patch
Normal file
@ -0,0 +1,653 @@
|
|||||||
|
only in patch2:
|
||||||
|
unchanged:
|
||||||
|
---
|
||||||
|
Doc/library/turtle.rst | 82 -------------------------------------------------
|
||||||
|
1 file changed, 82 deletions(-)
|
||||||
|
|
||||||
|
--- a/Doc/library/turtle.rst
|
||||||
|
+++ b/Doc/library/turtle.rst
|
||||||
|
@@ -250,7 +250,6 @@ Turtle motion
|
||||||
|
turtle is headed.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(0.00,0.00)
|
||||||
|
@@ -277,7 +276,6 @@ Turtle motion
|
||||||
|
>>> turtle.goto(0, 0)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(0.00,0.00)
|
||||||
|
@@ -296,13 +294,11 @@ Turtle motion
|
||||||
|
orientation depends on the turtle mode, see :func:`mode`.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.setheading(22)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.heading()
|
||||||
|
22.0
|
||||||
|
@@ -321,13 +317,11 @@ Turtle motion
|
||||||
|
orientation depends on the turtle mode, see :func:`mode`.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.setheading(22)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.heading()
|
||||||
|
22.0
|
||||||
|
@@ -350,13 +344,11 @@ Turtle motion
|
||||||
|
not change the turtle's orientation.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.goto(0, 0)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> tp = turtle.pos()
|
||||||
|
>>> tp
|
||||||
|
@@ -380,13 +372,11 @@ Turtle motion
|
||||||
|
unchanged.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.goto(0, 240)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(0.00,240.00)
|
||||||
|
@@ -402,13 +392,11 @@ Turtle motion
|
||||||
|
Set the turtle's second coordinate to *y*, leave first coordinate unchanged.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.goto(0, 40)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(0.00,40.00)
|
||||||
|
@@ -435,7 +423,6 @@ Turtle motion
|
||||||
|
=================== ====================
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.setheading(90)
|
||||||
|
>>> turtle.heading()
|
||||||
|
@@ -448,14 +435,12 @@ Turtle motion
|
||||||
|
its start-orientation (which depends on the mode, see :func:`mode`).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.setheading(90)
|
||||||
|
>>> turtle.goto(0, -10)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.heading()
|
||||||
|
90.0
|
||||||
|
@@ -487,7 +472,6 @@ Turtle motion
|
||||||
|
calculated automatically. May be used to draw regular polygons.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.position()
|
||||||
|
@@ -516,7 +500,6 @@ Turtle motion
|
||||||
|
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.dot()
|
||||||
|
@@ -534,7 +517,6 @@ Turtle motion
|
||||||
|
it by calling ``clearstamp(stamp_id)``.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.color("blue")
|
||||||
|
>>> turtle.stamp()
|
||||||
|
@@ -550,7 +532,6 @@ Turtle motion
|
||||||
|
Delete stamp with given *stampid*.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.position()
|
||||||
|
(150.00,-0.00)
|
||||||
|
@@ -595,7 +576,6 @@ Turtle motion
|
||||||
|
undo actions is determined by the size of the undobuffer.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> for i in range(4):
|
||||||
|
... turtle.fd(50); turtle.lt(80)
|
||||||
|
@@ -628,7 +608,6 @@ Turtle motion
|
||||||
|
turtle turn instantly.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.speed()
|
||||||
|
3
|
||||||
|
@@ -649,7 +628,6 @@ Tell Turtle's state
|
||||||
|
Return the turtle's current location (x,y) (as a :class:`Vec2D` vector).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.pos()
|
||||||
|
(440.00,-0.00)
|
||||||
|
@@ -665,7 +643,6 @@ Tell Turtle's state
|
||||||
|
orientation which depends on the mode - "standard"/"world" or "logo".
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.goto(10, 10)
|
||||||
|
>>> turtle.towards(0,0)
|
||||||
|
@@ -677,7 +654,6 @@ Tell Turtle's state
|
||||||
|
Return the turtle's x coordinate.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(50)
|
||||||
|
@@ -693,7 +669,6 @@ Tell Turtle's state
|
||||||
|
Return the turtle's y coordinate.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(60)
|
||||||
|
@@ -710,7 +685,6 @@ Tell Turtle's state
|
||||||
|
:func:`mode`).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(67)
|
||||||
|
@@ -727,7 +701,6 @@ Tell Turtle's state
|
||||||
|
other turtle, in turtle step units.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.distance(30,40)
|
||||||
|
@@ -751,7 +724,6 @@ Settings for measurement
|
||||||
|
Default value is 360 degrees.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(90)
|
||||||
|
@@ -774,7 +746,6 @@ Settings for measurement
|
||||||
|
``degrees(2*math.pi)``.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.left(90)
|
||||||
|
@@ -785,7 +756,6 @@ Settings for measurement
|
||||||
|
1.5707963267948966
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.degrees(360)
|
||||||
|
@@ -821,7 +791,6 @@ Drawing state
|
||||||
|
thickness. If no argument is given, the current pensize is returned.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.pensize()
|
||||||
|
1
|
||||||
|
@@ -853,7 +822,6 @@ Drawing state
|
||||||
|
attributes in one statement.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:options: +NORMALIZE_WHITESPACE
|
||||||
|
|
||||||
|
>>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
|
||||||
|
@@ -876,7 +844,6 @@ Drawing state
|
||||||
|
Return ``True`` if pen is down, ``False`` if it's up.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.penup()
|
||||||
|
>>> turtle.isdown()
|
||||||
|
@@ -917,7 +884,6 @@ Color control
|
||||||
|
newly set pencolor.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> colormode()
|
||||||
|
1.0
|
||||||
|
@@ -966,7 +932,6 @@ Color control
|
||||||
|
with the newly set fillcolor.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.fillcolor("violet")
|
||||||
|
>>> turtle.fillcolor()
|
||||||
|
@@ -1005,7 +970,6 @@ Color control
|
||||||
|
with the newly set colors.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.color("red", "green")
|
||||||
|
>>> turtle.color()
|
||||||
|
@@ -1022,7 +986,6 @@ Filling
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
@@ -1032,7 +995,6 @@ Filling
|
||||||
|
Return fillstate (``True`` if filling, ``False`` else).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.begin_fill()
|
||||||
|
>>> if turtle.filling():
|
||||||
|
@@ -1057,7 +1019,6 @@ Filling
|
||||||
|
above may be either all yellow or have some white regions.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.color("black", "red")
|
||||||
|
>>> turtle.begin_fill()
|
||||||
|
@@ -1074,7 +1035,6 @@ More drawing control
|
||||||
|
variables to the default values.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.goto(0,-22)
|
||||||
|
>>> turtle.left(100)
|
||||||
|
@@ -1125,7 +1085,6 @@ Visibility
|
||||||
|
drawing observably.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.hideturtle()
|
||||||
|
|
||||||
|
@@ -1136,7 +1095,6 @@ Visibility
|
||||||
|
Make the turtle visible.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.showturtle()
|
||||||
|
|
||||||
|
@@ -1167,7 +1125,6 @@ Appearance
|
||||||
|
deal with shapes see Screen method :func:`register_shape`.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.shape()
|
||||||
|
'classic'
|
||||||
|
@@ -1193,7 +1150,6 @@ Appearance
|
||||||
|
``resizemode("user")`` is called by :func:`shapesize` when used with arguments.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.resizemode()
|
||||||
|
'noresize'
|
||||||
|
@@ -1217,7 +1173,6 @@ Appearance
|
||||||
|
of the shapes's outline.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.shapesize()
|
||||||
|
(1.0, 1.0, 1)
|
||||||
|
@@ -1242,7 +1197,6 @@ Appearance
|
||||||
|
heading of the turtle are sheared.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
>>> turtle.shapesize(5,2)
|
||||||
|
@@ -1259,7 +1213,6 @@ Appearance
|
||||||
|
change the turtle's heading (direction of movement).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.reset()
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
@@ -1279,7 +1232,6 @@ Appearance
|
||||||
|
(direction of movement).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.reset()
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
@@ -1305,7 +1257,6 @@ Appearance
|
||||||
|
turtle (its direction of movement).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.reset()
|
||||||
|
>>> turtle.shape("circle")
|
||||||
|
@@ -1334,7 +1285,6 @@ Appearance
|
||||||
|
given matrix.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle = Turtle()
|
||||||
|
>>> turtle.shape("square")
|
||||||
|
@@ -1350,7 +1300,6 @@ Appearance
|
||||||
|
can be used to define a new shape or components of a compound shape.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.shape("square")
|
||||||
|
>>> turtle.shapetransform(4, -1, 0, 2)
|
||||||
|
@@ -1375,7 +1324,6 @@ Using events
|
||||||
|
procedural way:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> def turn(x, y):
|
||||||
|
... left(180)
|
||||||
|
@@ -1396,7 +1344,6 @@ Using events
|
||||||
|
``None``, existing bindings are removed.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> class MyTurtle(Turtle):
|
||||||
|
... def glow(self,x,y):
|
||||||
|
@@ -1424,7 +1371,6 @@ Using events
|
||||||
|
mouse-click event on that turtle.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.ondrag(turtle.goto)
|
||||||
|
|
||||||
|
@@ -1452,7 +1398,6 @@ Special Turtle methods
|
||||||
|
Return the last recorded polygon.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.home()
|
||||||
|
>>> turtle.begin_poly()
|
||||||
|
@@ -1472,7 +1417,6 @@ Special Turtle methods
|
||||||
|
turtle properties.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> mick = Turtle()
|
||||||
|
>>> joe = mick.clone()
|
||||||
|
@@ -1485,7 +1429,6 @@ Special Turtle methods
|
||||||
|
return the "anonymous turtle":
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> pet = getturtle()
|
||||||
|
>>> pet.fd(50)
|
||||||
|
@@ -1499,7 +1442,6 @@ Special Turtle methods
|
||||||
|
TurtleScreen methods can then be called for that object.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> ts = turtle.getscreen()
|
||||||
|
>>> ts
|
||||||
|
@@ -1517,7 +1459,6 @@ Special Turtle methods
|
||||||
|
``None``, the undobuffer is disabled.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> turtle.setundobuffer(42)
|
||||||
|
|
||||||
|
@@ -1527,7 +1468,6 @@ Special Turtle methods
|
||||||
|
Return number of entries in the undobuffer.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> while undobufferentries():
|
||||||
|
... undo()
|
||||||
|
@@ -1550,7 +1490,6 @@ below:
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> s = Shape("compound")
|
||||||
|
>>> poly1 = ((0,0),(10,-5),(0,10),(-10,-5))
|
||||||
|
@@ -1561,7 +1500,6 @@ below:
|
||||||
|
3. Now add the Shape to the Screen's shapelist and use it:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> register_shape("myshape", s)
|
||||||
|
>>> shape("myshape")
|
||||||
|
@@ -1581,7 +1519,6 @@ Most of the examples in this section ref
|
||||||
|
``screen``.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> screen = Screen()
|
||||||
|
@@ -1598,7 +1535,6 @@ Window control
|
||||||
|
Set or return background color of the TurtleScreen.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.bgcolor("orange")
|
||||||
|
>>> screen.bgcolor()
|
||||||
|
@@ -1690,7 +1626,6 @@ Window control
|
||||||
|
distorted.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.reset()
|
||||||
|
>>> screen.setworldcoordinates(-50,-7.5,50,7.5)
|
||||||
|
@@ -1701,7 +1636,6 @@ Window control
|
||||||
|
... left(45); fd(2) # a regular octagon
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> screen.reset()
|
||||||
|
@@ -1723,7 +1657,6 @@ Animation control
|
||||||
|
Optional argument:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.delay()
|
||||||
|
10
|
||||||
|
@@ -1745,7 +1678,6 @@ Animation control
|
||||||
|
:func:`delay`).
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.tracer(8, 25)
|
||||||
|
>>> dist = 2
|
||||||
|
@@ -1782,7 +1714,6 @@ Using screen events
|
||||||
|
must have the focus. (See method :func:`listen`.)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> def f():
|
||||||
|
... fd(50)
|
||||||
|
@@ -1803,7 +1734,6 @@ Using screen events
|
||||||
|
must have focus. (See method :func:`listen`.)
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> def f():
|
||||||
|
... fd(50)
|
||||||
|
@@ -1828,7 +1758,6 @@ Using screen events
|
||||||
|
named ``turtle``:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.onclick(turtle.goto) # Subsequently clicking into the TurtleScreen will
|
||||||
|
>>> # make the turtle move to the clicked point.
|
||||||
|
@@ -1848,7 +1777,6 @@ Using screen events
|
||||||
|
Install a timer that calls *fun* after *t* milliseconds.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> running = True
|
||||||
|
>>> def f():
|
||||||
|
@@ -1930,7 +1858,6 @@ Settings and special methods
|
||||||
|
============ ========================= ===================
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> mode("logo") # resets turtle heading to north
|
||||||
|
>>> mode()
|
||||||
|
@@ -1945,7 +1872,6 @@ Settings and special methods
|
||||||
|
values of color triples have to be in the range 0..\ *cmode*.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.colormode(1)
|
||||||
|
>>> turtle.pencolor(240, 160, 80)
|
||||||
|
@@ -1966,7 +1892,6 @@ Settings and special methods
|
||||||
|
do with a Tkinter Canvas.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> cv = screen.getcanvas()
|
||||||
|
>>> cv
|
||||||
|
@@ -1978,7 +1903,6 @@ Settings and special methods
|
||||||
|
Return a list of names of all currently available turtle shapes.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.getshapes()
|
||||||
|
['arrow', 'blank', 'circle', ..., 'turtle']
|
||||||
|
@@ -2002,7 +1926,6 @@ Settings and special methods
|
||||||
|
coordinates: Install the corresponding polygon shape.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.register_shape("triangle", ((5,-3), (0,5), (-5,-3)))
|
||||||
|
|
||||||
|
@@ -2018,7 +1941,6 @@ Settings and special methods
|
||||||
|
Return the list of turtles on the screen.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> for turtle in screen.turtles():
|
||||||
|
... turtle.color("red")
|
||||||
|
@@ -2080,7 +2002,6 @@ Methods specific to Screen, not inherite
|
||||||
|
center window vertically
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.setup (width=200, height=200, startx=0, starty=0)
|
||||||
|
>>> # sets window to 200x200 pixels, in upper left of screen
|
||||||
|
@@ -2096,7 +2017,6 @@ Methods specific to Screen, not inherite
|
||||||
|
Set title of turtle window to *titlestring*.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> screen.title("Welcome to the turtle zoo!")
|
||||||
|
|
||||||
|
@@ -2167,7 +2087,6 @@ Public classes
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
|
||||||
|
>>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
|
||||||
|
>>> s = Shape("compound")
|
||||||
|
@@ -2514,7 +2433,6 @@ Changes since Python 3.0
|
||||||
|
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
- :skipif: _tkinter is None
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
>>> for turtle in turtles():
|
78
pre_checkin.sh
Normal file
78
pre_checkin.sh
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export LC_ALL=C
|
||||||
|
|
||||||
|
master=python*.spec
|
||||||
|
|
||||||
|
# create import_failed.map from package definitions
|
||||||
|
pkgname=$(grep python_pkg_name $master |grep define |awk -F' ' '{print $3}')
|
||||||
|
MAPFILE=import_failed.map
|
||||||
|
function new_map_line () {
|
||||||
|
package=$1
|
||||||
|
package=$(echo $1 |sed -e "s:%{python_pkg_name}:$pkgname:")
|
||||||
|
modules=$2
|
||||||
|
if [ -z "$package" -o -z "$modules" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [[ "$package" =~ "-base" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "$package:$modules" >> $MAPFILE.tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
for spec in *.spec; do
|
||||||
|
basename=${spec%.spec}
|
||||||
|
package=
|
||||||
|
modules=
|
||||||
|
while read line; do
|
||||||
|
case $line in
|
||||||
|
"%files -n "*)
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
package=${line#"%files -n "}
|
||||||
|
modules=
|
||||||
|
;;
|
||||||
|
"%files "*)
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
package=$basename-${line#"%files "}
|
||||||
|
modules=
|
||||||
|
;;
|
||||||
|
"%files")
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
package=$basename
|
||||||
|
modules=
|
||||||
|
;;
|
||||||
|
"%{sitedir}/config-"*)
|
||||||
|
# ignore
|
||||||
|
;;
|
||||||
|
"%{sitedir}/"*)
|
||||||
|
word=${line#"%{sitedir}/"}
|
||||||
|
if ! echo $word | grep -q /; then
|
||||||
|
modules="$modules $word"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"%{dynlib "*"}")
|
||||||
|
word=${line#"%{dynlib "}
|
||||||
|
word=${word%"}"}
|
||||||
|
modules="$modules $word"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < $spec
|
||||||
|
new_map_line $package "$modules"
|
||||||
|
done
|
||||||
|
|
||||||
|
cat $MAPFILE.tmp |sort -u > $MAPFILE
|
||||||
|
rm $MAPFILE.tmp
|
||||||
|
|
||||||
|
# run test inclusion check
|
||||||
|
tar xJf Python-*.xz
|
||||||
|
python3 skipped_tests.py
|
||||||
|
|
||||||
|
# generate baselibs.conf
|
||||||
|
VERSION=$(grep ^Version $master|awk -F':' '{print $2}' |sed -e 's/ //g')
|
||||||
|
python_version=${VERSION:0:3} # 3.3
|
||||||
|
python_version_abitag=${python_version//./} # 33
|
||||||
|
python_version_soname=${python_version//./_} # 3_3
|
||||||
|
echo "$pkgname-base" > baselibs.conf
|
||||||
|
echo "$pkgname" >> baselibs.conf
|
||||||
|
echo "libpython$python_version_soname-1_0" >> baselibs.conf
|
||||||
|
|
25
python-3.3.0b1-fix_date_time_compiler.patch
Normal file
25
python-3.3.0b1-fix_date_time_compiler.patch
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
Makefile.pre.in | 7 +++++++
|
||||||
|
1 file changed, 7 insertions(+)
|
||||||
|
|
||||||
|
--- a/Makefile.pre.in
|
||||||
|
+++ b/Makefile.pre.in
|
||||||
|
@@ -784,11 +784,18 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
|
||||||
|
$(DTRACE_OBJS) \
|
||||||
|
$(srcdir)/Modules/getbuildinfo.c
|
||||||
|
$(CC) -c $(PY_CORE_CFLAGS) \
|
||||||
|
+ -DDATE="\"`date -u -r Makefile.pre.in +"%b %d %Y"`\"" \
|
||||||
|
+ -DTIME="\"`date -u -r Makefile.pre.in +"%T"`\"" \
|
||||||
|
-DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \
|
||||||
|
-DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \
|
||||||
|
-DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \
|
||||||
|
-o $@ $(srcdir)/Modules/getbuildinfo.c
|
||||||
|
|
||||||
|
+Python/getcompiler.o: $(srcdir)/Python/getcompiler.c Makefile
|
||||||
|
+ $(CC) -c $(PY_CORE_CFLAGS) \
|
||||||
|
+ -DCOMPILER='"[GCC]"' \
|
||||||
|
+ -o $@ $(srcdir)/Python/getcompiler.c
|
||||||
|
+
|
||||||
|
Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile
|
||||||
|
$(CC) -c $(PY_CORE_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \
|
||||||
|
-DPREFIX='"$(prefix)"' \
|
15
python-3.3.0b1-localpath.patch
Normal file
15
python-3.3.0b1-localpath.patch
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
Lib/site.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/Lib/site.py
|
||||||
|
+++ b/Lib/site.py
|
||||||
|
@@ -77,7 +77,7 @@ import io
|
||||||
|
import stat
|
||||||
|
|
||||||
|
# Prefixes for site-packages; add additional prefixes like /usr/local here
|
||||||
|
-PREFIXES = [sys.prefix, sys.exec_prefix]
|
||||||
|
+PREFIXES = [sys.prefix, sys.exec_prefix, '/usr/local']
|
||||||
|
# Enable per user site-packages directory
|
||||||
|
# set it to False to disable the feature or True to force the feature
|
||||||
|
ENABLE_USER_SITE = None
|
15
python-3.3.0b1-test-posix_fadvise.patch
Normal file
15
python-3.3.0b1-test-posix_fadvise.patch
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_posix.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_posix.py
|
||||||
|
+++ b/Lib/test/test_posix.py
|
||||||
|
@@ -425,7 +425,7 @@ class PosixTester(unittest.TestCase):
|
||||||
|
def test_posix_fadvise(self):
|
||||||
|
fd = os.open(os_helper.TESTFN, os.O_RDONLY)
|
||||||
|
try:
|
||||||
|
- posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED)
|
||||||
|
+ posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_RANDOM)
|
||||||
|
finally:
|
||||||
|
os.close(fd)
|
||||||
|
|
109
python.keyring
Normal file
109
python.keyring
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQINBFq+ToQBEADRYvIVtbK6owynD3j3nxwpW2KEk/p+aDvtXmc2SR2dBcZ8sFW2
|
||||||
|
R5vEsG8d3/D3wgv5pcL3KfNNXQYUnXVbobrFUUWQYc79qIsE3MgiPf5NVOtwKPUR
|
||||||
|
i5g9YJgKvpBxkQfqp3LYGm9ZBtwo3DVLA3yn7KsazCmAgTNFJYw7ku1XxgmIzY6K
|
||||||
|
5J30DfbJiqDqj4f9GslCCCCH3qiPnuLG/HUyVLHMpbWlaiy9NI0GcaLxjJewHj9w
|
||||||
|
W2D2lydkxe5JGo7egUkV3ILcuLVSVKA35SKY27dYqfuyqp9tAzaRbjDYjsYdHA6G
|
||||||
|
BqrNrKBn/GwlFDPrVdcvN3ZSY2wMLTxWE3Axc/FweuHxFnou/80FwX7F3JD+oEQ6
|
||||||
|
rofmcxOBCC7J98I7HZAhP9jBn88XIS2hztbLq8d6rZJZRtcz0k61VR0ddO+TrFmf
|
||||||
|
9rMYCPgCckRtVxeFIVIabrN1IzKynLFeo040h8hSGswd6YKDOVwjJY6Oa6EmVefZ
|
||||||
|
a8QSt4+M65RSzH6SEPY008F3nJUAK6MEkzTak+tFltZNrVWu8p2xd1j9nmxAwEhZ
|
||||||
|
/lgbxLqzYgaUWmfyHeZ8yVA0MhHzdiAL8nVUEdG3KecIq0RWCJLGLWWIjd6KAJl1
|
||||||
|
yAmhRYKK/sjPDsL3elHsFACfZbyx3o5GGQNlas1FYoPLWbaNGaJtgFTF2QARAQAB
|
||||||
|
tCtQYWJsbyBHYWxpbmRvIFNhbGdhZG8gPHBhYmxvZ3NhbEBnbWFpbC5jb20+iQJO
|
||||||
|
BBMBCgA4FiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+ToQCGwMFCwkIBwMFFQoJ
|
||||||
|
CAsFFgIDAQACHgECF4AACgkQZOYo+NaEaW2bmA/+PXIap2udLoUVOHxnsIBdqYwp
|
||||||
|
sv1Aj5lfIJmNhmxPbHShwp1Jg+w4urxe+2Dj5ofKVlIo1i83bQkvnKJMDXDVuc/K
|
||||||
|
P6zqhBJ3rT4Q3qx2mzX8bIfQoJ2JHuH4lkP+I7doDcHHRyeNASyk72VdQmU4twNw
|
||||||
|
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
|
||||||
|
=6Zb4
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
1
python310-rpmlintrc
Normal file
1
python310-rpmlintrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
addFilter("pem-certificate.*/usr/lib.*/python.*/test/*.pem")
|
4140
python310.changes
Normal file
4140
python310.changes
Normal file
File diff suppressed because it is too large
Load Diff
1056
python310.spec
Normal file
1056
python310.spec
Normal file
File diff suppressed because it is too large
Load Diff
23
skip-test_pyobject_freed_is_freed.patch
Normal file
23
skip-test_pyobject_freed_is_freed.patch
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_capi/test_misc.py | 3 +++
|
||||||
|
1 file changed, 3 insertions(+)
|
||||||
|
|
||||||
|
--- a/Lib/test/test_capi/test_misc.py
|
||||||
|
+++ b/Lib/test/test_capi/test_misc.py
|
||||||
|
@@ -38,6 +38,8 @@ Py_DEBUG = hasattr(sys, 'gettotalrefcoun
|
||||||
|
def decode_stderr(err):
|
||||||
|
return err.decode('utf-8', 'replace').replace('\r', '')
|
||||||
|
|
||||||
|
+# Which version of the SLE distro we build on?
|
||||||
|
+SLE_VERSION = int(os.environ.get('SLE_VERSION', '0'), 10)
|
||||||
|
|
||||||
|
def testfunction(self):
|
||||||
|
"""some doc"""
|
||||||
|
@@ -967,6 +969,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.skipIf(0 < SLE_VERSION < 150300, 'Failing on Leap 15.*')
|
||||||
|
def test_pyobject_freed_is_freed(self):
|
||||||
|
self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
|
||||||
|
|
69
skipped_tests.py
Normal file
69
skipped_tests.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
"""
|
||||||
|
Simple regexp-based skipped test checker.
|
||||||
|
It lists tests that are mentioned (presumably for exclusion)
|
||||||
|
in BASE, and in MAIN (presumably for inclusion)
|
||||||
|
and reports discrepancies.
|
||||||
|
|
||||||
|
This will have a number of
|
||||||
|
"""
|
||||||
|
|
||||||
|
MAIN = "python39.spec"
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
from os.path import basename
|
||||||
|
|
||||||
|
alltests = set()
|
||||||
|
qemu_exclusions = set()
|
||||||
|
|
||||||
|
for item in glob.glob("Python-*/Lib/test/test_*"):
|
||||||
|
testname = basename(item)
|
||||||
|
if testname.endswith(".py"):
|
||||||
|
testname = testname[:-3]
|
||||||
|
alltests.add(testname)
|
||||||
|
|
||||||
|
testre = re.compile(r'[\s"](test_\w+)\b')
|
||||||
|
|
||||||
|
def find_tests_in_spec(specname):
|
||||||
|
global qemu_exclusions
|
||||||
|
|
||||||
|
found_tests = set()
|
||||||
|
with open(specname) as spec:
|
||||||
|
in_qemu = False
|
||||||
|
for line in spec:
|
||||||
|
line = line.strip()
|
||||||
|
if "#" in line:
|
||||||
|
line = line[:line.index("#")]
|
||||||
|
tests = set(testre.findall(line))
|
||||||
|
found_tests |= tests
|
||||||
|
if line == "%if 0%{?qemu_user_space_build} > 0":
|
||||||
|
in_qemu = True
|
||||||
|
if in_qemu:
|
||||||
|
if line == "%endif":
|
||||||
|
in_qemu = False
|
||||||
|
qemu_exclusions |= tests
|
||||||
|
return found_tests
|
||||||
|
|
||||||
|
excluded = find_tests_in_spec(MAIN)
|
||||||
|
|
||||||
|
#print("--- excluded tests:", " ".join(sorted(excluded)))
|
||||||
|
#print("--- included tests:", " ".join(sorted(included)))
|
||||||
|
|
||||||
|
mentioned = excluded
|
||||||
|
nonexistent = mentioned - alltests
|
||||||
|
missing = excluded - qemu_exclusions
|
||||||
|
|
||||||
|
print("--- the following tests are excluded for QEMU and not tested in python")
|
||||||
|
print("--- (that probably means we don't need to worry about them)")
|
||||||
|
for test in sorted(qemu_exclusions - excluded):
|
||||||
|
print(test)
|
||||||
|
|
||||||
|
print("--- the following tests might be excluded in python:")
|
||||||
|
for test in sorted(missing):
|
||||||
|
print(test)
|
||||||
|
|
||||||
|
if nonexistent:
|
||||||
|
print("--- the following tests don't exist:")
|
||||||
|
for test in sorted(nonexistent):
|
||||||
|
print(test)
|
18
subprocess-raise-timeout.patch
Normal file
18
subprocess-raise-timeout.patch
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
Lib/test/test_subprocess.py | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
Index: Python-3.10.12/Lib/test/test_subprocess.py
|
||||||
|
===================================================================
|
||||||
|
--- Python-3.10.12.orig/Lib/test/test_subprocess.py
|
||||||
|
+++ Python-3.10.12/Lib/test/test_subprocess.py
|
||||||
|
@@ -268,7 +268,8 @@ class ProcessTestCase(BaseTestCase):
|
||||||
|
"time.sleep(3600)"],
|
||||||
|
# Some heavily loaded buildbots (sparc Debian 3.x) require
|
||||||
|
# this much time to start and print.
|
||||||
|
- timeout=3)
|
||||||
|
+ # OBS might require even more
|
||||||
|
+ timeout=10)
|
||||||
|
self.fail("Expected TimeoutExpired.")
|
||||||
|
self.assertEqual(c.exception.output, b'BDFL')
|
||||||
|
|
75
support-expat-CVE-2022-25236-patched.patch
Normal file
75
support-expat-CVE-2022-25236-patched.patch
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
From 7da97f61816f3cadaa6788804b22a2434b40e8c5 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Mon, 21 Feb 2022 08:16:09 -0800
|
||||||
|
Subject: [PATCH] bpo-46811: Make test suite support Expat >=2.4.5 (GH-31453)
|
||||||
|
(GH-31472)
|
||||||
|
|
||||||
|
Curly brackets were never allowed in namespace URIs
|
||||||
|
according to RFC 3986, and so-called namespace-validating
|
||||||
|
XML parsers have the right to reject them a invalid URIs.
|
||||||
|
|
||||||
|
libexpat >=2.4.5 has become strcter in that regard due to
|
||||||
|
related security issues; with ET.XML instantiating a
|
||||||
|
namespace-aware parser under the hood, this test has no
|
||||||
|
future in CPython.
|
||||||
|
|
||||||
|
References:
|
||||||
|
- https://datatracker.ietf.org/doc/html/rfc3968
|
||||||
|
- https://www.w3.org/TR/xml-names/
|
||||||
|
|
||||||
|
Also, test_minidom.py: Support Expat >=2.4.5
|
||||||
|
(cherry picked from commit 2cae93832f46b245847bdc252456ddf7742ef45e)
|
||||||
|
|
||||||
|
Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
|
||||||
|
---
|
||||||
|
Lib/test/test_minidom.py | 23 +++++++++--------------
|
||||||
|
1 file changed, 9 insertions(+), 14 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst
|
||||||
|
|
||||||
|
--- a/Lib/test/test_minidom.py
|
||||||
|
+++ b/Lib/test/test_minidom.py
|
||||||
|
@@ -6,7 +6,6 @@ import io
|
||||||
|
from test import support
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
-import pyexpat
|
||||||
|
import xml.dom.minidom
|
||||||
|
|
||||||
|
from xml.dom.minidom import parse, Attr, Node, Document, parseString
|
||||||
|
@@ -1163,13 +1162,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()
|
||||||
|
@@ -1631,12 +1628,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):
|
Loading…
Reference in New Issue
Block a user