commit 379872e3783bf06239d7b888e11b7686b665fcfd01c81e4fdd47eb1b0ffeb580 Author: Matej Cepl Date: Thu Oct 16 16:28:18 2025 +0000 - Update to 3.9.24: - Security - gh-139700: Check consistency of the zip64 end of central directory record. Support records with “zip64 extensible data” if there are no bytes prepended to the ZIP file. - gh-139400: xml.parsers.expat: Make sure that parent Expat parsers are only garbage-collected once they are no longer referenced by subparsers created by ExternalEntityParserCreate(). Patch by Sebastian Pipping. - gh-121227: Raise an SSL.SSLError if an empty protocols argument is passed to ssl.SSLContext.set_npn_protocols() to fix CVE-2024-5642. - gh-135661: Fix parsing start and end tags in html.parser.HTMLParser according to the HTML5 standard. * Whitespaces no longer accepted between does not end the script section. * Vertical tabulation (\v) and non-ASCII whitespaces no longer recognized as whitespaces. The only whitespaces are \t\n\r\f and space. * Null character (U+0000) no longer ends the tag name. * Attributes and slashes after the tag name in end tags are now ignored, instead of terminating after the first > in quoted attribute value. E.g. . * Multiple slashes and whitespaces between the last attribute and closing > are now ignored in both start and end tags. E.g. . * Multiple = between attribute name and value are no longer collapsed. E.g. produces attribute “foo” with value “=bar”. - gh-135661: Fix CDATA section parsing in html.parser.HTMLParser OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python39?expand=0&rev=245 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/98437-sphinx.locale._-as-gettext-in-pyspecific.patch b/98437-sphinx.locale._-as-gettext-in-pyspecific.patch new file mode 100644 index 0000000..fb68fc9 --- /dev/null +++ b/98437-sphinx.locale._-as-gettext-in-pyspecific.patch @@ -0,0 +1,58 @@ +From 5775f51691d7d64fb676586e008b41261ce64ac2 Mon Sep 17 00:00:00 2001 +From: "Matt.Wang" +Date: Wed, 19 Oct 2022 14:49:08 +0800 +Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for + backward-compatibility in pyspecific.py + +[why] spinix 5.3 changed locale.translators from a defaultdict(gettext.NullTranslations) to a dict, which leads to failure of pyspecific.py. Use sphinx.locale._ as gettext to fix the issue. +--- + Doc/tools/extensions/pyspecific.py | 8 ++++---- + Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst | 1 + + 2 files changed, 5 insertions(+), 4 deletions(-) + +Index: Python-3.9.22/Doc/tools/extensions/pyspecific.py +=================================================================== +--- Python-3.9.22.orig/Doc/tools/extensions/pyspecific.py 2025-04-11 09:49:58.417019238 +0200 ++++ Python-3.9.22/Doc/tools/extensions/pyspecific.py 2025-04-11 09:50:56.818993764 +0200 +@@ -27,7 +27,7 @@ + from sphinx.errors import NoUri + except ImportError: + from sphinx.environment import NoUri +-from sphinx.locale import translators ++from sphinx.locale import _ as sphinx_gettext + from sphinx.util import status_iterator, logging + from sphinx.util.nodes import split_explicit_title + from sphinx.writers.text import TextWriter, TextTranslator +@@ -111,7 +111,7 @@ + + def run(self): + pnode = nodes.compound(classes=['impl-detail']) +- label = translators['sphinx'].gettext(self.label_text) ++ label = sphinx_gettext(self.label_text) + content = self.content + add_text = nodes.strong(label, label) + if self.arguments: +@@ -180,7 +180,7 @@ + else: + args = [] + +- label = translators['sphinx'].gettext(self._label[min(2, len(args))]) ++ label = sphinx_gettext(self._label[min(2, len(args))]) + text = label.format(name="``{}``".format(name), + args=", ".join("``{}``".format(a) for a in args if a)) + +@@ -380,7 +380,7 @@ + else: + label = self._removed_label + +- label = translators['sphinx'].gettext(label) ++ label = sphinx_gettext(label) + text = label.format(deprecated=version[0], removed=version[1]) + if len(self.arguments) == 3: + inodes, messages = self.state.inline_text(self.arguments[2], +Index: Python-3.9.22/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ Python-3.9.22/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst 2025-04-11 09:50:08.952333342 +0200 +@@ -0,0 +1 @@ ++Use sphinx.locale._ as the gettext function in pyspecific.py. diff --git a/99366-patch.dict-can-decorate-async.patch b/99366-patch.dict-can-decorate-async.patch new file mode 100644 index 0000000..b1e5c02 --- /dev/null +++ b/99366-patch.dict-can-decorate-async.patch @@ -0,0 +1,79 @@ +From c0dea0309b9a0a7cbc87727c9957f0a388fb9b0f Mon Sep 17 00:00:00 2001 +From: Nikita Sobolev +Date: Fri, 11 Nov 2022 11:04:30 +0300 +Subject: [PATCH] gh-98086: Now ``patch.dict`` can decorate async functions + (GH-98095) (cherry picked from commit + 67b4d2772c5124b908f8ed9b13166a79bbeb88d2) + +Co-authored-by: Nikita Sobolev +--- + Lib/unittest/mock.py | 18 ++++++++++ + Lib/unittest/test/testmock/testasync.py | 17 +++++++++ + Misc/NEWS.d/next/Library/2022-10-08-19-39-27.gh-issue-98086.y---WC.rst | 1 + 3 files changed, 36 insertions(+) + create mode 100644 Misc/NEWS.d/next/Library/2022-10-08-19-39-27.gh-issue-98086.y---WC.rst + +--- a/Lib/unittest/mock.py ++++ b/Lib/unittest/mock.py +@@ -1761,6 +1761,12 @@ class _patch_dict(object): + def __call__(self, f): + if isinstance(f, type): + return self.decorate_class(f) ++ if inspect.iscoroutinefunction(f): ++ return self.decorate_async_callable(f) ++ return self.decorate_callable(f) ++ ++ ++ def decorate_callable(self, f): + @wraps(f) + def _inner(*args, **kw): + self._patch_dict() +@@ -1769,6 +1775,18 @@ class _patch_dict(object): + finally: + self._unpatch_dict() + ++ return _inner ++ ++ ++ def decorate_async_callable(self, f): ++ @wraps(f) ++ async def _inner(*args, **kw): ++ self._patch_dict() ++ try: ++ return await f(*args, **kw) ++ finally: ++ self._unpatch_dict() ++ + return _inner + + +--- a/Lib/unittest/test/testmock/testasync.py ++++ b/Lib/unittest/test/testmock/testasync.py +@@ -146,6 +146,23 @@ class AsyncPatchCMTest(unittest.TestCase + + run(test_async()) + ++ def test_patch_dict_async_def(self): ++ foo = {'a': 'a'} ++ @patch.dict(foo, {'a': 'b'}) ++ async def test_async(): ++ self.assertEqual(foo['a'], 'b') ++ ++ self.assertTrue(iscoroutinefunction(test_async)) ++ run(test_async()) ++ ++ def test_patch_dict_async_def_context(self): ++ foo = {'a': 'a'} ++ async def test_async(): ++ with patch.dict(foo, {'a': 'b'}): ++ self.assertEqual(foo['a'], 'b') ++ ++ run(test_async()) ++ + + class AsyncMockTest(unittest.TestCase): + def test_iscoroutinefunction_default(self): +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2022-10-08-19-39-27.gh-issue-98086.y---WC.rst +@@ -0,0 +1 @@ ++Make sure ``patch.dict()`` can be applied on async functions. diff --git a/CVE-2023-52425-libexpat-2.6.0-backport.patch b/CVE-2023-52425-libexpat-2.6.0-backport.patch new file mode 100644 index 0000000..299dc3e --- /dev/null +++ b/CVE-2023-52425-libexpat-2.6.0-backport.patch @@ -0,0 +1,57 @@ +--- + Lib/test/test_pyexpat.py | 4 ++++ + Lib/test/test_sax.py | 3 +++ + Lib/test/test_xml_etree.py | 7 +++++++ + 3 files changed, 14 insertions(+) + +--- a/Lib/test/test_pyexpat.py ++++ b/Lib/test/test_pyexpat.py +@@ -766,6 +766,10 @@ class ReparseDeferralTest(unittest.TestC + self.assertEqual(started, ['doc']) + + def test_reparse_deferral_disabled(self): ++ if expat.version_info < (2, 6, 0): ++ self.skipTest(f'Expat {expat.version_info} does not ' ++ 'support reparse deferral') ++ + started = [] + + def start_element(name, _): +--- a/Lib/test/test_sax.py ++++ b/Lib/test/test_sax.py +@@ -1236,6 +1236,9 @@ class ExpatReaderTest(XmlTestBase): + + self.assertEqual(result.getvalue(), start + b"") + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Expat {pyexpat.version_info} does not ' ++ 'support reparse deferral') + def test_flush_reparse_deferral_disabled(self): + result = BytesIO() + xmlgen = XMLGenerator(result) +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -1416,9 +1416,13 @@ class XMLPullParserTest(unittest.TestCas + self.assert_event_tags(parser, [('end', 'root')]) + self.assertIsNone(parser.close()) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Fail with patched version of Expat {pyexpat.version_info}') + def test_simple_xml_chunk_1(self): + self.test_simple_xml(chunk_size=1, flush=True) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Fail with patched version of Expat {pyexpat.version_info}') + def test_simple_xml_chunk_5(self): + self.test_simple_xml(chunk_size=5, flush=True) + +@@ -1643,6 +1647,9 @@ class XMLPullParserTest(unittest.TestCas + + self.assert_event_tags(parser, [('end', 'doc')]) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Expat {pyexpat.version_info} does not ' ++ 'support reparse deferral') + def test_flush_reparse_deferral_disabled(self): + parser = ET.XMLPullParser(events=('start', 'end')) + diff --git a/CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch b/CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch new file mode 100644 index 0000000..d58c211 --- /dev/null +++ b/CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch @@ -0,0 +1,1891 @@ +From 3ddf7fa83b19463e710b75ae6e8a28831e575f3d Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Wed, 28 Oct 2020 09:26:39 +0100 +Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer + +- Remove HAVE_X509_VERIFY_PARAM_SET1_HOST check +- Update hashopenssl to require OpenSSL 1.1.1 +- multissltests only OpenSSL > 1.1.0 +- ALPN is always supported +- SNI is always supported +- Remove deprecated NPN code. Python wrappers are no-op. +- ECDH is always supported +- Remove OPENSSL_VERSION_1_1 macro +- Remove locking callbacks +- Drop PY_OPENSSL_1_1_API macro +- Drop HAVE_SSL_CTX_CLEAR_OPTIONS macro +- SSL_CTRL_GET_MAX_PROTO_VERSION is always defined now +- security level is always available now +- get_num_tickets is available with TLS 1.3 +- X509_V_ERR MISMATCH is always available now +- Always set SSL_MODE_RELEASE_BUFFERS +- X509_V_FLAG_TRUSTED_FIRST is always available +- get_ciphers is always supported +- SSL_CTX_set_keylog_callback is always available +- Update Modules/Setup with static link example +- Mention PEP in whatsnew +- Drop 1.0.2 and 1.1.0 from GHA tests +--- + Doc/using/unix.rst | 1 + Lib/ssl.py | 10 + Lib/test/test_ssl.py | 119 -- + Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst | 1 + Modules/Setup | 22 + Modules/_hashopenssl.c | 108 -- + Modules/_ssl.c | 518 ---------- + Modules/_ssl/debughelpers.c | 4 + Modules/clinic/_hashopenssl.c.h | 11 + Modules/clinic/_ssl.c.h | 85 - + Tools/ssl/multissltests.py | 4 + configure | 9 + configure.ac | 36 + pyconfig.h.in | 3 + setup.py | 19 + 15 files changed, 77 insertions(+), 873 deletions(-) + create mode 100644 Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst + +--- a/Doc/using/unix.rst ++++ b/Doc/using/unix.rst +@@ -113,6 +113,7 @@ For example, on most Linux systems, the + | | embedding the interpreter. | + +-----------------------------------------------+------------------------------------------+ + ++.. _unix_custom_openssl: + + Miscellaneous + ============= +--- a/Lib/ssl.py ++++ b/Lib/ssl.py +@@ -912,15 +912,12 @@ class SSLObject: + """Return the currently selected NPN protocol as a string, or ``None`` + if a next protocol was not negotiated or if NPN is not supported by one + of the peers.""" +- if _ssl.HAS_NPN: +- return self._sslobj.selected_npn_protocol() + + def selected_alpn_protocol(self): + """Return the currently selected ALPN protocol as a string, or ``None`` + if a next protocol was not negotiated or if ALPN is not supported by one + of the peers.""" +- if _ssl.HAS_ALPN: +- return self._sslobj.selected_alpn_protocol() ++ return self._sslobj.selected_alpn_protocol() + + def cipher(self): + """Return the currently selected cipher as a 3-tuple ``(name, +@@ -1162,10 +1159,7 @@ class SSLSocket(socket): + @_sslcopydoc + def selected_npn_protocol(self): + self._checkClosed() +- if self._sslobj is None or not _ssl.HAS_NPN: +- return None +- else: +- return self._sslobj.selected_npn_protocol() ++ return None + + @_sslcopydoc + def selected_alpn_protocol(self): +--- a/Lib/test/test_ssl.py ++++ b/Lib/test/test_ssl.py +@@ -39,7 +39,6 @@ Py_DEBUG_WIN32 = Py_DEBUG and sys.platfo + PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) + HOST = socket_helper.HOST + IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL') +-IS_OPENSSL_1_1_0 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0) + IS_OPENSSL_1_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1) + IS_OPENSSL_3_0_0 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (3, 0, 0) + PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS') +@@ -269,18 +268,6 @@ def handle_error(prefix): + if support.verbose: + sys.stdout.write(prefix + exc_format) + +-def can_clear_options(): +- # 0.9.8m or higher +- return ssl._OPENSSL_API_VERSION >= (0, 9, 8, 13, 15) +- +-def no_sslv2_implies_sslv3_hello(): +- # 0.9.7h or higher +- return ssl.OPENSSL_VERSION_INFO >= (0, 9, 7, 8, 15) +- +-def have_verify_flags(): +- # 0.9.8 or higher +- return ssl.OPENSSL_VERSION_INFO >= (0, 9, 8, 0, 15) +- + def _have_secp_curves(): + if not ssl.HAS_ECDH: + return False +@@ -371,17 +358,15 @@ class BasicSocketTests(unittest.TestCase + ssl.OP_SINGLE_DH_USE + if ssl.HAS_ECDH: + ssl.OP_SINGLE_ECDH_USE +- if ssl.OPENSSL_VERSION_INFO >= (1, 0): +- ssl.OP_NO_COMPRESSION ++ ssl.OP_NO_COMPRESSION + self.assertIn(ssl.HAS_SNI, {True, False}) + self.assertIn(ssl.HAS_ECDH, {True, False}) + ssl.OP_NO_SSLv2 + ssl.OP_NO_SSLv3 + ssl.OP_NO_TLSv1 + ssl.OP_NO_TLSv1_3 +- if ssl.OPENSSL_VERSION_INFO >= (1, 0, 1): +- ssl.OP_NO_TLSv1_1 +- ssl.OP_NO_TLSv1_2 ++ ssl.OP_NO_TLSv1_1 ++ ssl.OP_NO_TLSv1_2 + self.assertEqual(ssl.PROTOCOL_TLS, ssl.PROTOCOL_SSLv23) + + def test_private_init(self): +@@ -1169,7 +1154,6 @@ class ContextTests(unittest.TestCase): + self.assertNotIn("RC4", name) + self.assertNotIn("3DES", name) + +- @unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old') + def test_get_ciphers(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.set_ciphers('AESGCM') +@@ -1201,15 +1185,11 @@ class ContextTests(unittest.TestCase): + self.assertEqual(default, ctx.options) + ctx.options |= ssl.OP_NO_TLSv1 + self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) +- if can_clear_options(): +- ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) +- self.assertEqual(default, ctx.options) +- ctx.options = 0 +- # Ubuntu has OP_NO_SSLv3 forced on by default +- self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) +- else: +- with self.assertRaises(ValueError): +- ctx.options = 0 ++ ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) ++ self.assertEqual(default, ctx.options) ++ ctx.options = 0 ++ # Ubuntu has OP_NO_SSLv3 forced on by default ++ self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) + + def test_verify_mode_protocol(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS) +@@ -1328,8 +1308,6 @@ class ContextTests(unittest.TestCase): + with self.assertRaises(ValueError): + ctx.maximum_version = ssl.TLSVersion.TLSv1 + +- @unittest.skipUnless(have_verify_flags(), +- "verify_flags need OpenSSL > 0.9.8") + def test_verify_flags(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + # default value +@@ -1807,7 +1785,6 @@ class ContextTests(unittest.TestCase): + obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO()) + self.assertIsInstance(obj, MySSLObject) + +- @unittest.skipUnless(IS_OPENSSL_1_1_1, "Test requires OpenSSL 1.1.1") + def test_num_tickest(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + self.assertEqual(ctx.num_tickets, 2) +@@ -2972,8 +2949,6 @@ class ThreadedTests(unittest.TestCase): + after = ssl.cert_time_to_seconds(cert['notAfter']) + self.assertLess(before, after) + +- @unittest.skipUnless(have_verify_flags(), +- "verify_flags need OpenSSL > 0.9.8") + def test_crl_check(self): + if support.verbose: + sys.stdout.write("\n") +@@ -3877,12 +3852,7 @@ class ThreadedTests(unittest.TestCase): + self.assertIs(s.version(), None) + self.assertIs(s._sslobj, None) + s.connect((HOST, server.port)) +- if IS_OPENSSL_1_1_1 and has_tls_version('TLSv1_3'): +- self.assertEqual(s.version(), 'TLSv1.3') +- elif ssl.OPENSSL_VERSION_INFO >= (1, 0, 2): +- self.assertEqual(s.version(), 'TLSv1.2') +- else: # 0.9.8 to 1.0.1 +- self.assertIn(s.version(), ('TLSv1', 'TLSv1.2')) ++ self.assertEqual(s.version(), 'TLSv1.3') + self.assertIs(s._sslobj, None) + self.assertIs(s.version(), None) + +@@ -3984,8 +3954,6 @@ class ThreadedTests(unittest.TestCase): + # explicitly using the 'ECCdraft' cipher alias. Otherwise, + # our default cipher list should prefer ECDH-based ciphers + # automatically. +- if ssl.OPENSSL_VERSION_INFO < (1, 0, 0): +- context.set_ciphers("ECCdraft:ECDH") + with ThreadedEchoServer(context=context) as server: + with context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) +@@ -4117,15 +4085,11 @@ class ThreadedTests(unittest.TestCase): + server_context.set_ciphers("ECDHE:!eNULL:!aNULL") + server_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 + try: +- stats = server_params_test(client_context, server_context, +- chatty=True, connectionchatty=True, +- sni_name=hostname) ++ server_params_test(client_context, server_context, ++ chatty=True, connectionchatty=True, ++ sni_name=hostname) + except ssl.SSLError: +- pass +- else: +- # OpenSSL 1.0.2 does not fail although it should. +- if IS_OPENSSL_1_1_0: +- self.fail("mismatch curve did not fail") ++ self.fail("mismatch curve did not fail") + + def test_selected_alpn_protocol(self): + # selected_alpn_protocol() is None unless ALPN is used. +@@ -4135,7 +4099,6 @@ class ThreadedTests(unittest.TestCase): + sni_name=hostname) + self.assertIs(stats['client_alpn_protocol'], None) + +- @unittest.skipUnless(ssl.HAS_ALPN, "ALPN support required") + def test_selected_alpn_protocol_if_server_uses_alpn(self): + # selected_alpn_protocol() is None unless ALPN is used by the client. + client_context, server_context, hostname = testing_context() +@@ -4145,7 +4108,6 @@ class ThreadedTests(unittest.TestCase): + sni_name=hostname) + self.assertIs(stats['client_alpn_protocol'], None) + +- @unittest.skipUnless(ssl.HAS_ALPN, "ALPN support needed for this test") + def test_alpn_protocols(self): + server_protocols = ['foo', 'bar', 'milkshake'] + protocol_tests = [ +@@ -4168,22 +4130,17 @@ class ThreadedTests(unittest.TestCase): + except ssl.SSLError as e: + stats = e + +- if (expected is None and IS_OPENSSL_1_1_0 +- and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)): +- # OpenSSL 1.1.0 to 1.1.0e raises handshake error +- self.assertIsInstance(stats, ssl.SSLError) +- else: +- msg = "failed trying %s (s) and %s (c).\n" \ +- "was expecting %s, but got %%s from the %%s" \ +- % (str(server_protocols), str(client_protocols), +- str(expected)) +- client_result = stats['client_alpn_protocol'] +- self.assertEqual(client_result, expected, +- msg % (client_result, "client")) +- server_result = stats['server_alpn_protocols'][-1] \ +- if len(stats['server_alpn_protocols']) else 'nothing' +- self.assertEqual(server_result, expected, +- msg % (server_result, "server")) ++ msg = "failed trying %s (s) and %s (c).\n" \ ++ "was expecting %s, but got %%s from the %%s" \ ++ % (str(server_protocols), str(client_protocols), ++ str(expected)) ++ client_result = stats['client_alpn_protocol'] ++ self.assertEqual(client_result, expected, ++ msg % (client_result, "client")) ++ server_result = stats['server_alpn_protocols'][-1] \ ++ if len(stats['server_alpn_protocols']) else 'nothing' ++ self.assertEqual(server_result, expected, ++ msg % (server_result, "server")) + + def test_selected_npn_protocol(self): + # selected_npn_protocol() is None unless NPN is used +@@ -4193,31 +4150,8 @@ class ThreadedTests(unittest.TestCase): + sni_name=hostname) + self.assertIs(stats['client_npn_protocol'], None) + +- @unittest.skipUnless(ssl.HAS_NPN, "NPN support needed for this test") + def test_npn_protocols(self): +- server_protocols = ['http/1.1', 'spdy/2'] +- protocol_tests = [ +- (['http/1.1', 'spdy/2'], 'http/1.1'), +- (['spdy/2', 'http/1.1'], 'http/1.1'), +- (['spdy/2', 'test'], 'spdy/2'), +- (['abc', 'def'], 'abc') +- ] +- for client_protocols, expected in protocol_tests: +- client_context, server_context, hostname = testing_context() +- server_context.set_npn_protocols(server_protocols) +- client_context.set_npn_protocols(client_protocols) +- stats = server_params_test(client_context, server_context, +- chatty=True, connectionchatty=True, +- sni_name=hostname) +- msg = "failed trying %s (s) and %s (c).\n" \ +- "was expecting %s, but got %%s from the %%s" \ +- % (str(server_protocols), str(client_protocols), +- str(expected)) +- client_result = stats['client_npn_protocol'] +- self.assertEqual(client_result, expected, msg % (client_result, "client")) +- server_result = stats['server_npn_protocols'][-1] \ +- if len(stats['server_npn_protocols']) else 'nothing' +- self.assertEqual(server_result, expected, msg % (server_result, "server")) ++ assert not ssl.HAS_NPN + + def test_empty_npn_protocols(self): + """npn_protocols cannot be empty, see CVE-2024-5642 & gh-121227""" +@@ -4393,8 +4327,7 @@ class ThreadedTests(unittest.TestCase): + self.assertGreater(session.time, 0) + self.assertGreater(session.timeout, 0) + self.assertTrue(session.has_ticket) +- if ssl.OPENSSL_VERSION_INFO > (1, 0, 1): +- self.assertGreater(session.ticket_lifetime_hint, 0) ++ self.assertGreater(session.ticket_lifetime_hint, 0) + self.assertFalse(stats['session_reused']) + sess_stat = server_context.session_stats() + self.assertEqual(sess_stat['accept'], 1) +--- /dev/null ++++ b/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst +@@ -0,0 +1 @@ ++Implement :pep:`644`. Python now requires OpenSSL 1.1.1 or newer. +--- a/Modules/Setup ++++ b/Modules/Setup +@@ -210,11 +210,23 @@ _symtable symtablemodule.c + #_socket socketmodule.c + + # Socket module helper for SSL support; you must comment out the other +-# socket line above, and possibly edit the SSL variable: +-#SSL=/usr/local/ssl +-#_ssl _ssl.c \ +-# -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ +-# -L$(SSL)/lib -lssl -lcrypto ++# socket line above, and edit the OPENSSL variable: ++# OPENSSL=/path/to/openssl/directory ++# _ssl _ssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -lssl -lcrypto ++#_hashlib _hashopenssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -lcrypto ++ ++# To statically link OpenSSL: ++# _ssl _ssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -l:libssl.a -Wl,--exclude-libs,libssl.a \ ++# -l:libcrypto.a -Wl,--exclude-libs,libcrypto.a ++#_hashlib _hashopenssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -l:libcrypto.a -Wl,--exclude-libs,libcrypto.a + + # The crypt module is now disabled by default because it breaks builds + # on many systems (where -lcrypt is needed), e.g. Linux (I believe). +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -43,51 +43,12 @@ + # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" + #endif + +-#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +-/* OpenSSL < 1.1.0 */ +-#define EVP_MD_CTX_new EVP_MD_CTX_create +-#define EVP_MD_CTX_free EVP_MD_CTX_destroy +- +-HMAC_CTX * +-HMAC_CTX_new(void) +-{ +- HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX)); +- if (ctx != NULL) { +- memset(ctx, 0, sizeof(HMAC_CTX)); +- HMAC_CTX_init(ctx); +- } +- return ctx; +-} +- +-void +-HMAC_CTX_free(HMAC_CTX *ctx) +-{ +- if (ctx != NULL) { +- HMAC_CTX_cleanup(ctx); +- OPENSSL_free(ctx); +- } +-} +- +-const EVP_MD * +-HMAC_CTX_get_md(const HMAC_CTX *ctx) +-{ +- return ctx->md; +-} +-#endif +- + #define MUNCH_SIZE INT_MAX + +-#ifdef NID_sha3_224 ++#define PY_OPENSSL_HAS_SCRYPT 1 + #define PY_OPENSSL_HAS_SHA3 1 +-#endif +- +-#if defined(EVP_MD_FLAG_XOF) && defined(NID_shake128) + #define PY_OPENSSL_HAS_SHAKE 1 +-#endif +- +-#if defined(NID_blake2b512) && !defined(OPENSSL_NO_BLAKE2) + #define PY_OPENSSL_HAS_BLAKE2 1 +-#endif + + #if OPENSSL_VERSION_NUMBER >= 0x30000000L + #define PY_EVP_MD EVP_MD +@@ -1311,8 +1272,7 @@ pbkdf2_hmac_impl(PyObject *module, const + return key_obj; + } + +-#if OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_SCRYPT) && !defined(LIBRESSL_VERSION_NUMBER) +-#define PY_SCRYPT 1 ++#ifdef PY_OPENSSL_HAS_SCRYPT + + /* XXX: Parameters salt, n, r and p should be required keyword-only parameters. + They are optional in the Argument Clinic declaration only due to a +@@ -1433,7 +1393,7 @@ _hashlib_scrypt_impl(PyObject *module, P + } + return key_obj; + } +-#endif ++#endif /* PY_OPENSSL_HAS_SCRYPT */ + + /* Fast HMAC for hmac.digest() + */ +@@ -1920,12 +1880,6 @@ hashlib_md_meth_names(PyObject *module) + return 0; + } + +-/* LibreSSL doesn't support FIPS: +- https://marc.info/?l=openbsd-misc&m=139819485423701&w=2 +- +- Ted Unangst wrote: "I figured I should mention our current libressl policy +- wrt FIPS mode. It's gone and it's not coming back." */ +-#ifndef LIBRESSL_VERSION_NUMBER + /*[clinic input] + _hashlib.get_fips_mode -> int + +@@ -1963,7 +1917,6 @@ _hashlib_get_fips_mode_impl(PyObject *mo + return result; + #endif + } +-#endif // !LIBRESSL_VERSION_NUMBER + + + static int +@@ -2144,17 +2097,6 @@ hashlib_free(void *m) + + /* Py_mod_exec functions */ + static int +-hashlib_openssl_legacy_init(PyObject *module) +-{ +-#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +- /* Load all digest algorithms and initialize cpuid */ +- OPENSSL_add_all_algorithms_noconf(); +- ERR_load_crypto_strings(); +-#endif +- return 0; +-} +- +-static int + hashlib_init_hashtable(PyObject *module) + { + _hashlibstate *state = get_hashlib_state(module); +@@ -2227,10 +2169,7 @@ hashlib_init_hmactype(PyObject *module) + return 0; + } + +-#if 0 + static PyModuleDef_Slot hashlib_slots[] = { +- /* OpenSSL 1.0.2 and LibreSSL */ +- {Py_mod_exec, hashlib_openssl_legacy_init}, + {Py_mod_exec, hashlib_init_hashtable}, + {Py_mod_exec, hashlib_init_evptype}, + {Py_mod_exec, hashlib_init_evpxoftype}, +@@ -2238,7 +2177,6 @@ static PyModuleDef_Slot hashlib_slots[] + {Py_mod_exec, hashlib_md_meth_names}, + {0, NULL} + }; +-#endif + + static struct PyModuleDef _hashlibmodule = { + PyModuleDef_HEAD_INIT, +@@ -2246,7 +2184,7 @@ static struct PyModuleDef _hashlibmodule + .m_doc = "OpenSSL interface for hashlib module", + .m_size = sizeof(_hashlibstate), + .m_methods = EVP_functions, +- .m_slots = NULL, ++ .m_slots = hashlib_slots, + .m_traverse = hashlib_traverse, + .m_clear = hashlib_clear, + .m_free = hashlib_free +@@ -2255,41 +2193,5 @@ static struct PyModuleDef _hashlibmodule + PyMODINIT_FUNC + PyInit__hashlib(void) + { +- PyObject *m = PyState_FindModule(&_hashlibmodule); +- if (m != NULL) { +- Py_INCREF(m); +- return m; +- } +- +- m = PyModule_Create(&_hashlibmodule); +- if (m == NULL) { +- return NULL; +- } +- +- if (hashlib_openssl_legacy_init(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_hashtable(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_evptype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_evpxoftype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_hmactype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_md_meth_names(m) == -1) { +- Py_DECREF(m); +- return NULL; +- } +- +- return m; ++ return PyModuleDef_Init(&_hashlibmodule); + } +--- a/Modules/_ssl.c ++++ b/Modules/_ssl.c +@@ -29,9 +29,9 @@ + #define _PySSL_FIX_ERRNO + + #define PySSL_BEGIN_ALLOW_THREADS_S(save) \ +- do { if (_ssl_locks_count>0) { (save) = PyEval_SaveThread(); } } while (0) ++ do { (save) = PyEval_SaveThread(); } while(0) + #define PySSL_END_ALLOW_THREADS_S(save) \ +- do { if (_ssl_locks_count>0) { PyEval_RestoreThread(save); } _PySSL_FIX_ERRNO; } while (0) ++ do { PyEval_RestoreThread(save); _PySSL_FIX_ERRNO; } while(0) + #define PySSL_BEGIN_ALLOW_THREADS { \ + PyThreadState *_save = NULL; \ + PySSL_BEGIN_ALLOW_THREADS_S(_save); +@@ -62,16 +62,6 @@ static PySocketModule_APIObject PySocket + #include "openssl/bio.h" + #include "openssl/dh.h" + +-#ifndef HAVE_X509_VERIFY_PARAM_SET1_HOST +-# ifdef LIBRESSL_VERSION_NUMBER +-# error "LibreSSL is missing X509_VERIFY_PARAM_set1_host(), see https://github.com/libressl-portable/portable/issues/381" +-# elif OPENSSL_VERSION_NUMBER > 0x1000200fL +-# define HAVE_X509_VERIFY_PARAM_SET1_HOST +-# else +-# error "libssl is too old and does not support X509_VERIFY_PARAM_set1_host()" +-# endif +-#endif +- + #ifndef OPENSSL_THREADS + # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" + #endif +@@ -142,15 +132,7 @@ static void _PySSLFixErrno(void) { + #include "_ssl_data.h" + #endif + +-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) +-# define OPENSSL_VERSION_1_1 1 +-# define PY_OPENSSL_1_1_API 1 +-#endif +- +-/* OpenSSL API 1.1.0+ does not include version methods. Define the methods +- * unless OpenSSL is compiled without the methods. It's the easiest way to +- * make 1.0.2, 1.1.0, 1.1.1, and 3.0.0 happy without deprecation warnings. +- */ ++/* OpenSSL API 1.1.0+ does not include version methods */ + #ifndef OPENSSL_NO_TLS1_METHOD + extern const SSL_METHOD *TLSv1_method(void); + #endif +@@ -161,59 +143,10 @@ extern const SSL_METHOD *TLSv1_1_method( + extern const SSL_METHOD *TLSv1_2_method(void); + #endif + +-/* LibreSSL 2.7.0 provides necessary OpenSSL 1.1.0 APIs */ +-#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070000fL +-# define PY_OPENSSL_1_1_API 1 +-#endif +- +-#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER) +-# define OPENSSL_VERSION_3_3 1 +-#endif +- +-/* SNI support (client- and server-side) appeared in OpenSSL 1.0.0 and 0.9.8f +- * This includes the SSL_set_SSL_CTX() function. +- */ +-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME +-# define HAVE_SNI 1 +-#else +-# define HAVE_SNI 0 +-#endif +- +-#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation +-# define HAVE_ALPN 1 +-#else +-# define HAVE_ALPN 0 +-#endif +- +-/* We cannot rely on OPENSSL_NO_NEXTPROTONEG because LibreSSL 2.6.1 dropped +- * NPN support but did not set OPENSSL_NO_NEXTPROTONEG for compatibility +- * reasons. The check for TLSEXT_TYPE_next_proto_neg works with +- * OpenSSL 1.0.1+ and LibreSSL. +- * OpenSSL 1.1.1-pre1 dropped NPN but still has TLSEXT_TYPE_next_proto_neg. +- */ +-#ifdef OPENSSL_NO_NEXTPROTONEG +-# define HAVE_NPN 0 +-#elif (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) +-# define HAVE_NPN 0 +-#elif defined(TLSEXT_TYPE_next_proto_neg) +-# define HAVE_NPN 1 +-#else +-# define HAVE_NPN 0 +-#endif +- +-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) +-#define HAVE_OPENSSL_KEYLOG 1 +-#endif +- + #ifndef INVALID_SOCKET /* MS defines this */ + #define INVALID_SOCKET (-1) + #endif + +-/* OpenSSL 1.0.2 and LibreSSL needs extra code for locking */ +-#ifndef OPENSSL_VERSION_1_1 +-#define HAVE_OPENSSL_CRYPTO_LOCK +-#endif +- + /* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */ + #ifdef OPENSSL_VERSION_1_1 + #define HAVE_OPENSSL_X509_STORE_LOCK +@@ -224,80 +157,8 @@ extern const SSL_METHOD *TLSv1_2_method( + #define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1 + #endif + +-#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2) ++/* OpenSSL 1.1 does not have SSL 2.0 */ + #define OPENSSL_NO_SSL2 +-#endif +- +-#ifndef PY_OPENSSL_1_1_API +-/* OpenSSL 1.1 API shims for OpenSSL < 1.1.0 and LibreSSL < 2.7.0 */ +- +-#define TLS_method SSLv23_method +-#define TLS_client_method SSLv23_client_method +-#define TLS_server_method SSLv23_server_method +-#define ASN1_STRING_get0_data ASN1_STRING_data +-#define X509_get0_notBefore X509_get_notBefore +-#define X509_get0_notAfter X509_get_notAfter +-#define OpenSSL_version_num SSLeay +-#define OpenSSL_version SSLeay_version +-#define OPENSSL_VERSION SSLEAY_VERSION +- +-static int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne) +-{ +- return ne->set; +-} +- +-#ifndef OPENSSL_NO_COMP +-/* LCOV_EXCL_START */ +-static int COMP_get_type(const COMP_METHOD *meth) +-{ +- return meth->type; +-} +-/* LCOV_EXCL_STOP */ +-#endif +- +-static pem_password_cb *SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx) +-{ +- return ctx->default_passwd_callback; +-} +- +-static void *SSL_CTX_get_default_passwd_cb_userdata(SSL_CTX *ctx) +-{ +- return ctx->default_passwd_callback_userdata; +-} +- +-static int X509_OBJECT_get_type(X509_OBJECT *x) +-{ +- return x->type; +-} +- +-static X509 *X509_OBJECT_get0_X509(X509_OBJECT *x) +-{ +- return x->data.x509; +-} +- +-static int BIO_up_ref(BIO *b) +-{ +- CRYPTO_add(&b->references, 1, CRYPTO_LOCK_BIO); +- return 1; +-} +- +-static STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *store) { +- return store->objs; +-} +- +-static int +-SSL_SESSION_has_ticket(const SSL_SESSION *s) +-{ +- return (s->tlsext_ticklen > 0) ? 1 : 0; +-} +- +-static unsigned long +-SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s) +-{ +- return s->tlsext_tick_lifetime_hint; +-} +- +-#endif /* OpenSSL < 1.1.0 or LibreSSL < 2.7.0 */ + + /* Default cipher suites */ + #ifndef PY_SSL_DEFAULT_CIPHERS +@@ -409,24 +270,10 @@ enum py_proto_version { + #endif + }; + +- +-/* serves as a flag to see whether we've initialized the SSL thread support. */ +-/* 0 means no, greater than 0 means yes */ +- +-static unsigned int _ssl_locks_count = 0; +- + /* SSL socket object */ + + #define X509_NAME_MAXLEN 256 + +-/* SSL_CTX_clear_options() and SSL_clear_options() were first added in +- * OpenSSL 0.9.8m but do not appear in some 0.9.9-dev versions such the +- * 0.9.9 from "May 2008" that NetBSD 5.0 uses. */ +-#if OPENSSL_VERSION_NUMBER >= 0x009080dfL && OPENSSL_VERSION_NUMBER != 0x00909000L +-# define HAVE_SSL_CTX_CLEAR_OPTIONS +-#else +-# undef HAVE_SSL_CTX_CLEAR_OPTIONS +-#endif + + /* In case of 'tls-unique' it will be 12 bytes for TLS, 36 bytes for + * older SSL, but let's be safe */ +@@ -436,17 +283,9 @@ static unsigned int _ssl_locks_count = 0 + typedef struct { + PyObject_HEAD + SSL_CTX *ctx; +-#if HAVE_NPN +- unsigned char *npn_protocols; +- int npn_protocols_len; +-#endif +-#if HAVE_ALPN + unsigned char *alpn_protocols; + unsigned int alpn_protocols_len; +-#endif +-#ifndef OPENSSL_NO_TLSEXT + PyObject *set_sni_cb; +-#endif + int check_hostname; + /* OpenSSL has no API to get hostflags from X509_VERIFY_PARAM* struct. + * We have to maintain our own copy. OpenSSL's hostflags default to 0. +@@ -457,10 +296,8 @@ typedef struct { + int post_handshake_auth; + #endif + PyObject *msg_cb; +-#ifdef HAVE_OPENSSL_KEYLOG + PyObject *keylog_filename; + BIO *keylog_bio; +-#endif + } PySSLContext; + + typedef struct { +@@ -667,23 +504,18 @@ fill_and_set_sslerror(PySSLSocket *sslso + } + + switch (verify_code) { +-#ifdef X509_V_ERR_HOSTNAME_MISMATCH +- /* OpenSSL >= 1.0.2, LibreSSL >= 2.5.3 */ + case X509_V_ERR_HOSTNAME_MISMATCH: + verify_obj = PyUnicode_FromFormat( + "Hostname mismatch, certificate is not valid for '%S'.", + sslsock->server_hostname + ); + break; +-#endif +-#ifdef X509_V_ERR_IP_ADDRESS_MISMATCH + case X509_V_ERR_IP_ADDRESS_MISMATCH: + verify_obj = PyUnicode_FromFormat( + "IP address mismatch, certificate is not valid for '%S'.", + sslsock->server_hostname + ); + break; +-#endif + default: + verify_str = X509_verify_cert_error_string(verify_code); + if (verify_str != NULL) { +@@ -2014,7 +1846,6 @@ cipher_to_tuple(const SSL_CIPHER *cipher + return NULL; + } + +-#if OPENSSL_VERSION_NUMBER >= 0x10002000UL + static PyObject * + cipher_to_dict(const SSL_CIPHER *cipher) + { +@@ -2023,10 +1854,8 @@ cipher_to_dict(const SSL_CIPHER *cipher) + unsigned long cipher_id; + int alg_bits, strength_bits, len; + char buf[512] = {0}; +-#if OPENSSL_VERSION_1_1 + int aead, nid; + const char *skcipher = NULL, *digest = NULL, *kx = NULL, *auth = NULL; +-#endif + + /* can be NULL */ + cipher_name = SSL_CIPHER_get_name(cipher); +@@ -2039,7 +1868,6 @@ cipher_to_dict(const SSL_CIPHER *cipher) + buf[len-1] = '\0'; + strength_bits = SSL_CIPHER_get_bits(cipher, &alg_bits); + +-#if OPENSSL_VERSION_1_1 + aead = SSL_CIPHER_is_aead(cipher); + nid = SSL_CIPHER_get_cipher_nid(cipher); + skcipher = nid != NID_undef ? OBJ_nid2ln(nid) : NULL; +@@ -2049,13 +1877,10 @@ cipher_to_dict(const SSL_CIPHER *cipher) + kx = nid != NID_undef ? OBJ_nid2ln(nid) : NULL; + nid = SSL_CIPHER_get_auth_nid(cipher); + auth = nid != NID_undef ? OBJ_nid2ln(nid) : NULL; +-#endif + + return Py_BuildValue( + "{sksssssssisi" +-#if OPENSSL_VERSION_1_1 + "sOssssssss" +-#endif + "}", + "id", cipher_id, + "name", cipher_name, +@@ -2063,16 +1888,13 @@ cipher_to_dict(const SSL_CIPHER *cipher) + "description", buf, + "strength_bits", strength_bits, + "alg_bits", alg_bits +-#if OPENSSL_VERSION_1_1 + ,"aead", aead ? Py_True : Py_False, + "symmetric", skcipher, + "digest", digest, + "kea", kx, + "auth", auth +-#endif + ); + } +-#endif + + /*[clinic input] + _ssl._SSLSocket.shared_ciphers +@@ -2143,28 +1965,6 @@ _ssl__SSLSocket_version_impl(PySSLSocket + return PyUnicode_FromString(version); + } + +-#if HAVE_NPN +-/*[clinic input] +-_ssl._SSLSocket.selected_npn_protocol +-[clinic start generated code]*/ +- +-static PyObject * +-_ssl__SSLSocket_selected_npn_protocol_impl(PySSLSocket *self) +-/*[clinic end generated code: output=b91d494cd207ecf6 input=c28fde139204b826]*/ +-{ +- const unsigned char *out; +- unsigned int outlen; +- +- SSL_get0_next_proto_negotiated(self->ssl, +- &out, &outlen); +- +- if (out == NULL) +- Py_RETURN_NONE; +- return PyUnicode_FromStringAndSize((char *)out, outlen); +-} +-#endif +- +-#if HAVE_ALPN + /*[clinic input] + _ssl._SSLSocket.selected_alpn_protocol + [clinic start generated code]*/ +@@ -2182,7 +1982,6 @@ _ssl__SSLSocket_selected_alpn_protocol_i + Py_RETURN_NONE; + return PyUnicode_FromStringAndSize((char *)out, outlen); + } +-#endif + + /*[clinic input] + _ssl._SSLSocket.compression +@@ -2219,11 +2018,6 @@ static int PySSL_set_context(PySSLSocket + void *closure) { + + if (PyObject_TypeCheck(value, &PySSLContext_Type)) { +-#if !HAVE_SNI +- PyErr_SetString(PyExc_NotImplementedError, "setting a socket's " +- "context is not supported by your OpenSSL library"); +- return -1; +-#else + Py_INCREF(value); + Py_SETREF(self->ctx, (PySSLContext *)value); + SSL_set_SSL_CTX(self->ssl, self->ctx->ctx); +@@ -2232,7 +2026,6 @@ static int PySSL_set_context(PySSLSocket + self->ssl, + self->ctx->msg_cb ? _PySSL_msg_callback : NULL + ); +-#endif + } else { + PyErr_SetString(PyExc_TypeError, "The value must be a SSLContext"); + return -1; +@@ -2857,8 +2650,6 @@ _ssl__SSLSocket_verify_client_post_hands + #endif + } + +-#ifdef OPENSSL_VERSION_1_1 +- + static SSL_SESSION* + _ssl_session_dup(SSL_SESSION *session) { + SSL_SESSION *newsession = NULL; +@@ -2899,7 +2690,6 @@ _ssl_session_dup(SSL_SESSION *session) { + } + return NULL; + } +-#endif + + static PyObject * + PySSL_get_session(PySSLSocket *self, void *closure) { +@@ -2908,7 +2698,6 @@ PySSL_get_session(PySSLSocket *self, voi + PySSLSession *pysess; + SSL_SESSION *session; + +-#ifdef OPENSSL_VERSION_1_1 + /* duplicate session as workaround for session bug in OpenSSL 1.1.0, + * https://github.com/openssl/openssl/issues/1550 */ + session = SSL_get0_session(self->ssl); /* borrowed reference */ +@@ -2918,12 +2707,10 @@ PySSL_get_session(PySSLSocket *self, voi + if ((session = _ssl_session_dup(session)) == NULL) { + return NULL; + } +-#else + session = SSL_get1_session(self->ssl); + if (session == NULL) { + Py_RETURN_NONE; + } +-#endif + pysess = PyObject_GC_New(PySSLSession, &PySSLSession_Type); + if (pysess == NULL) { + SSL_SESSION_free(session); +@@ -2942,9 +2729,7 @@ static int PySSL_set_session(PySSLSocket + void *closure) + { + PySSLSession *pysess; +-#ifdef OPENSSL_VERSION_1_1 + SSL_SESSION *session; +-#endif + int result; + + if (!PySSLSession_Check(value)) { +@@ -2968,7 +2753,6 @@ static int PySSL_set_session(PySSLSocket + "Cannot set session after handshake."); + return -1; + } +-#ifdef OPENSSL_VERSION_1_1 + /* duplicate session */ + if ((session = _ssl_session_dup(pysess->session)) == NULL) { + return -1; +@@ -2976,9 +2760,6 @@ static int PySSL_set_session(PySSLSocket + result = SSL_set_session(self->ssl, session); + /* free duplicate, SSL_set_session() bumps ref count */ + SSL_SESSION_free(session); +-#else +- result = SSL_set_session(self->ssl, pysess->session); +-#endif + if (result == 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return -1; +@@ -3029,7 +2810,6 @@ static PyMethodDef PySSLMethods[] = { + _SSL__SSLSOCKET_CIPHER_METHODDEF + _SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF + _SSL__SSLSOCKET_VERSION_METHODDEF +- _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF + _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF + _SSL__SSLSOCKET_COMPRESSION_METHODDEF + _SSL__SSLSOCKET_SHUTDOWN_METHODDEF +@@ -3123,9 +2903,6 @@ _ssl__SSLContext_impl(PyTypeObject *type + SSL_CTX *ctx = NULL; + X509_VERIFY_PARAM *params; + int result; +-#if defined(SSL_MODE_RELEASE_BUFFERS) +- unsigned long libver; +-#endif + + PySSL_BEGIN_ALLOW_THREADS + switch(proto_version) { +@@ -3190,19 +2967,10 @@ _ssl__SSLContext_impl(PyTypeObject *type + self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; + self->protocol = proto_version; + self->msg_cb = NULL; +-#ifdef HAVE_OPENSSL_KEYLOG + self->keylog_filename = NULL; + self->keylog_bio = NULL; +-#endif +-#if HAVE_NPN +- self->npn_protocols = NULL; +-#endif +-#if HAVE_ALPN + self->alpn_protocols = NULL; +-#endif +-#ifndef OPENSSL_NO_TLSEXT + self->set_sni_cb = NULL; +-#endif + /* Don't check host name by default */ + if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { + self->check_hostname = 1; +@@ -3264,37 +3032,9 @@ _ssl__SSLContext_impl(PyTypeObject *type + return NULL; + } + +-#if defined(SSL_MODE_RELEASE_BUFFERS) + /* Set SSL_MODE_RELEASE_BUFFERS. This potentially greatly reduces memory +- usage for no cost at all. However, don't do this for OpenSSL versions +- between 1.0.1 and 1.0.1h or 1.0.0 and 1.0.0m, which are affected by CVE +- 2014-0198. I can't find exactly which beta fixed this CVE, so be +- conservative and assume it wasn't fixed until release. We do this check +- at runtime to avoid problems from the dynamic linker. +- See #25672 for more on this. */ +- libver = OpenSSL_version_num(); +- if (!(libver >= 0x10001000UL && libver < 0x1000108fUL) && +- !(libver >= 0x10000000UL && libver < 0x100000dfUL)) { +- SSL_CTX_set_mode(self->ctx, SSL_MODE_RELEASE_BUFFERS); +- } +-#endif +- +- +-#if !defined(OPENSSL_NO_ECDH) && !defined(OPENSSL_VERSION_1_1) +- /* Allow automatic ECDH curve selection (on OpenSSL 1.0.2+), or use +- prime256v1 by default. This is Apache mod_ssl's initialization +- policy, so we should be safe. OpenSSL 1.1 has it enabled by default. +- */ +-#if defined(SSL_CTX_set_ecdh_auto) +- SSL_CTX_set_ecdh_auto(self->ctx, 1); +-#else +- { +- EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); +- SSL_CTX_set_tmp_ecdh(self->ctx, key); +- EC_KEY_free(key); +- } +-#endif +-#endif ++ usage for no cost at all. */ ++ SSL_CTX_set_mode(self->ctx, SSL_MODE_RELEASE_BUFFERS); + + #define SID_CTX "Python" + SSL_CTX_set_session_id_context(self->ctx, (const unsigned char *) SID_CTX, +@@ -3302,11 +3042,9 @@ _ssl__SSLContext_impl(PyTypeObject *type + #undef SID_CTX + + params = SSL_CTX_get0_param(self->ctx); +-#ifdef X509_V_FLAG_TRUSTED_FIRST + /* Improve trust chain building when cross-signed intermediate + certificates are present. See https://bugs.python.org/issue23476. */ + X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_TRUSTED_FIRST); +-#endif + X509_VERIFY_PARAM_set_hostflags(params, self->hostflags); + + #ifdef TLS1_3_VERSION +@@ -3320,9 +3058,7 @@ _ssl__SSLContext_impl(PyTypeObject *type + static int + context_traverse(PySSLContext *self, visitproc visit, void *arg) + { +-#ifndef OPENSSL_NO_TLSEXT + Py_VISIT(self->set_sni_cb); +-#endif + Py_VISIT(self->msg_cb); + return 0; + } +@@ -3330,11 +3066,8 @@ context_traverse(PySSLContext *self, vis + static int + context_clear(PySSLContext *self) + { +-#ifndef OPENSSL_NO_TLSEXT + Py_CLEAR(self->set_sni_cb); +-#endif + Py_CLEAR(self->msg_cb); +-#ifdef HAVE_OPENSSL_KEYLOG + Py_CLEAR(self->keylog_filename); + if (self->keylog_bio != NULL) { + PySSL_BEGIN_ALLOW_THREADS +@@ -3342,7 +3075,6 @@ context_clear(PySSLContext *self) + PySSL_END_ALLOW_THREADS + self->keylog_bio = NULL; + } +-#endif + return 0; + } + +@@ -3353,12 +3085,7 @@ context_dealloc(PySSLContext *self) + PyObject_GC_UnTrack(self); + context_clear(self); + SSL_CTX_free(self->ctx); +-#if HAVE_NPN +- PyMem_FREE(self->npn_protocols); +-#endif +-#if HAVE_ALPN + PyMem_FREE(self->alpn_protocols); +-#endif + Py_TYPE(self)->tp_free(self); + } + +@@ -3385,7 +3112,6 @@ _ssl__SSLContext_set_ciphers_impl(PySSLC + Py_RETURN_NONE; + } + +-#if OPENSSL_VERSION_NUMBER >= 0x10002000UL + /*[clinic input] + _ssl._SSLContext.get_ciphers + [clinic start generated code]*/ +@@ -3428,10 +3154,8 @@ _ssl__SSLContext_get_ciphers_impl(PySSLC + return result; + + } +-#endif + + +-#if HAVE_NPN || HAVE_ALPN + static int + do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen, + const unsigned char *server_protocols, unsigned int server_protocols_len, +@@ -3455,77 +3179,7 @@ do_protocol_selection(int alpn, unsigned + + return SSL_TLSEXT_ERR_OK; + } +-#endif + +-#if HAVE_NPN +-/* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */ +-static int +-_advertiseNPN_cb(SSL *s, +- const unsigned char **data, unsigned int *len, +- void *args) +-{ +- PySSLContext *ssl_ctx = (PySSLContext *) args; +- +- if (ssl_ctx->npn_protocols == NULL) { +- *data = (unsigned char *)""; +- *len = 0; +- } else { +- *data = ssl_ctx->npn_protocols; +- *len = ssl_ctx->npn_protocols_len; +- } +- +- return SSL_TLSEXT_ERR_OK; +-} +-/* this callback gets passed to SSL_CTX_set_next_proto_select_cb */ +-static int +-_selectNPN_cb(SSL *s, +- unsigned char **out, unsigned char *outlen, +- const unsigned char *server, unsigned int server_len, +- void *args) +-{ +- PySSLContext *ctx = (PySSLContext *)args; +- return do_protocol_selection(0, out, outlen, server, server_len, +- ctx->npn_protocols, ctx->npn_protocols_len); +-} +-#endif +- +-/*[clinic input] +-_ssl._SSLContext._set_npn_protocols +- protos: Py_buffer +- / +-[clinic start generated code]*/ +- +-static PyObject * +-_ssl__SSLContext__set_npn_protocols_impl(PySSLContext *self, +- Py_buffer *protos) +-/*[clinic end generated code: output=72b002c3324390c6 input=319fcb66abf95bd7]*/ +-{ +-#if HAVE_NPN +- PyMem_Free(self->npn_protocols); +- self->npn_protocols = PyMem_Malloc(protos->len); +- if (self->npn_protocols == NULL) +- return PyErr_NoMemory(); +- memcpy(self->npn_protocols, protos->buf, protos->len); +- self->npn_protocols_len = (int) protos->len; +- +- /* set both server and client callbacks, because the context can +- * be used to create both types of sockets */ +- SSL_CTX_set_next_protos_advertised_cb(self->ctx, +- _advertiseNPN_cb, +- self); +- SSL_CTX_set_next_proto_select_cb(self->ctx, +- _selectNPN_cb, +- self); +- +- Py_RETURN_NONE; +-#else +- PyErr_SetString(PyExc_NotImplementedError, +- "The NPN extension requires OpenSSL 1.0.1 or later."); +- return NULL; +-#endif +-} +- +-#if HAVE_ALPN + static int + _selectALPN_cb(SSL *s, + const unsigned char **out, unsigned char *outlen, +@@ -3537,7 +3191,6 @@ _selectALPN_cb(SSL *s, + ctx->alpn_protocols, ctx->alpn_protocols_len, + client_protocols, client_protocols_len); + } +-#endif + + /*[clinic input] + _ssl._SSLContext._set_alpn_protocols +@@ -3550,7 +3203,6 @@ _ssl__SSLContext__set_alpn_protocols_imp + Py_buffer *protos) + /*[clinic end generated code: output=87599a7f76651a9b input=9bba964595d519be]*/ + { +-#if HAVE_ALPN + if ((size_t)protos->len > UINT_MAX) { + PyErr_Format(PyExc_OverflowError, + "protocols longer than %u bytes", UINT_MAX); +@@ -3569,11 +3221,6 @@ _ssl__SSLContext__set_alpn_protocols_imp + SSL_CTX_set_alpn_select_cb(self->ctx, _selectALPN_cb, self); + + Py_RETURN_NONE; +-#else +- PyErr_SetString(PyExc_NotImplementedError, +- "The ALPN extension requires OpenSSL 1.0.2 or later."); +- return NULL; +-#endif + } + + static PyObject * +@@ -3649,9 +3296,6 @@ set_verify_flags(PySSLContext *self, PyO + } + + /* Getter and setter for protocol version */ +-#if defined(SSL_CTRL_GET_MAX_PROTO_VERSION) +- +- + static int + set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) + { +@@ -3746,9 +3390,8 @@ set_maximum_version(PySSLContext *self, + { + return set_min_max_proto_version(self, arg, 1); + } +-#endif /* SSL_CTRL_GET_MAX_PROTO_VERSION */ + +-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) ++#ifdef TLS1_3_VERSION + static PyObject * + get_num_tickets(PySSLContext *self, void *c) + { +@@ -3779,7 +3422,7 @@ set_num_tickets(PySSLContext *self, PyOb + + PyDoc_STRVAR(PySSLContext_num_tickets_doc, + "Control the number of TLSv1.3 session tickets"); +-#endif /* OpenSSL 1.1.1 */ ++#endif /* TLS1_3_VERSION */ + + static PyObject * + get_options(PySSLContext *self, void *c) +@@ -3797,13 +3440,7 @@ set_options(PySSLContext *self, PyObject + clear = opts & ~new_opts; + set = ~opts & new_opts; + if (clear) { +-#ifdef HAVE_SSL_CTX_CLEAR_OPTIONS + SSL_CTX_clear_options(self->ctx, clear); +-#else +- PyErr_SetString(PyExc_ValueError, +- "can't clear options before OpenSSL 0.9.8m"); +- return -1; +-#endif + } + if (set) + SSL_CTX_set_options(self->ctx, set); +@@ -4500,7 +4137,6 @@ _ssl__SSLContext_set_default_verify_path + Py_RETURN_NONE; + } + +-#ifndef OPENSSL_NO_ECDH + /*[clinic input] + _ssl._SSLContext.set_ecdh_curve + name: object +@@ -4535,9 +4171,7 @@ _ssl__SSLContext_set_ecdh_curve(PySSLCon + EC_KEY_free(key); + Py_RETURN_NONE; + } +-#endif + +-#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) + static int + _servername_callback(SSL *s, int *al, void *args) + { +@@ -4641,7 +4275,6 @@ error: + PyGILState_Release(gstate); + return ret; + } +-#endif + + static PyObject * + get_sni_callback(PySSLContext *self, void *c) +@@ -4662,7 +4295,6 @@ set_sni_callback(PySSLContext *self, PyO + "sni_callback cannot be set on TLS_CLIENT context"); + return -1; + } +-#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) + Py_CLEAR(self->set_sni_cb); + if (arg == Py_None) { + SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); +@@ -4680,13 +4312,6 @@ set_sni_callback(PySSLContext *self, PyO + SSL_CTX_set_tlsext_servername_arg(self->ctx, self); + } + return 0; +-#else +- PyErr_SetString(PyExc_NotImplementedError, +- "The TLS extension servername callback, " +- "SSL_CTX_set_tlsext_servername_callback, " +- "is not in the current OpenSSL library."); +- return -1; +-#endif + } + + /* Shim of X509_STORE_get1_objects API from OpenSSL 3.3 +@@ -4882,21 +4507,17 @@ static PyGetSetDef context_getsetlist[] + (setter) set_check_hostname, NULL}, + {"_host_flags", (getter) get_host_flags, + (setter) set_host_flags, NULL}, +-#if SSL_CTRL_GET_MAX_PROTO_VERSION + {"minimum_version", (getter) get_minimum_version, + (setter) set_minimum_version, NULL}, + {"maximum_version", (getter) get_maximum_version, + (setter) set_maximum_version, NULL}, +-#endif +-#ifdef HAVE_OPENSSL_KEYLOG + {"keylog_filename", (getter) _PySSLContext_get_keylog_filename, + (setter) _PySSLContext_set_keylog_filename, NULL}, +-#endif + {"_msg_callback", (getter) _PySSLContext_get_msg_callback, + (setter) _PySSLContext_set_msg_callback, NULL}, + {"sni_callback", (getter) get_sni_callback, + (setter) set_sni_callback, PySSLContext_sni_callback_doc}, +-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) ++#ifdef TLS1_3_VERSION + {"num_tickets", (getter) get_num_tickets, + (setter) set_num_tickets, PySSLContext_num_tickets_doc}, + #endif +@@ -4923,7 +4544,6 @@ static struct PyMethodDef context_method + _SSL__SSLCONTEXT__WRAP_BIO_METHODDEF + _SSL__SSLCONTEXT_SET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT__SET_ALPN_PROTOCOLS_METHODDEF +- _SSL__SSLCONTEXT__SET_NPN_PROTOCOLS_METHODDEF + _SSL__SSLCONTEXT_LOAD_CERT_CHAIN_METHODDEF + _SSL__SSLCONTEXT_LOAD_DH_PARAMS_METHODDEF + _SSL__SSLCONTEXT_LOAD_VERIFY_LOCATIONS_METHODDEF +@@ -5441,11 +5061,7 @@ PySSL_RAND(int len, int pseudo) + if (bytes == NULL) + return NULL; + if (pseudo) { +-#ifdef PY_OPENSSL_1_1_API + ok = RAND_bytes((unsigned char*)PyBytes_AS_STRING(bytes), len); +-#else +- ok = RAND_pseudo_bytes((unsigned char*)PyBytes_AS_STRING(bytes), len); +-#endif + if (ok == 0 || ok == 1) + return Py_BuildValue("NO", bytes, ok == 1 ? Py_True : Py_False); + } +@@ -6000,92 +5616,6 @@ static PyMethodDef PySSL_methods[] = { + }; + + +-#ifdef HAVE_OPENSSL_CRYPTO_LOCK +- +-/* an implementation of OpenSSL threading operations in terms +- * of the Python C thread library +- * Only used up to 1.0.2. OpenSSL 1.1.0+ has its own locking code. +- */ +- +-static PyThread_type_lock *_ssl_locks = NULL; +- +-#if OPENSSL_VERSION_NUMBER >= 0x10000000 +-/* use new CRYPTO_THREADID API. */ +-static void +-_ssl_threadid_callback(CRYPTO_THREADID *id) +-{ +- CRYPTO_THREADID_set_numeric(id, PyThread_get_thread_ident()); +-} +-#else +-/* deprecated CRYPTO_set_id_callback() API. */ +-static unsigned long +-_ssl_thread_id_function (void) { +- return PyThread_get_thread_ident(); +-} +-#endif +- +-static void _ssl_thread_locking_function +- (int mode, int n, const char *file, int line) { +- /* this function is needed to perform locking on shared data +- structures. (Note that OpenSSL uses a number of global data +- structures that will be implicitly shared whenever multiple +- threads use OpenSSL.) Multi-threaded applications will +- crash at random if it is not set. +- +- locking_function() must be able to handle up to +- CRYPTO_num_locks() different mutex locks. It sets the n-th +- lock if mode & CRYPTO_LOCK, and releases it otherwise. +- +- file and line are the file number of the function setting the +- lock. They can be useful for debugging. +- */ +- +- if ((_ssl_locks == NULL) || +- (n < 0) || ((unsigned)n >= _ssl_locks_count)) +- return; +- +- if (mode & CRYPTO_LOCK) { +- PyThread_acquire_lock(_ssl_locks[n], 1); +- } else { +- PyThread_release_lock(_ssl_locks[n]); +- } +-} +- +-static int _setup_ssl_threads(void) { +- +- unsigned int i; +- +- if (_ssl_locks == NULL) { +- _ssl_locks_count = CRYPTO_num_locks(); +- _ssl_locks = PyMem_Calloc(_ssl_locks_count, +- sizeof(PyThread_type_lock)); +- if (_ssl_locks == NULL) { +- PyErr_NoMemory(); +- return 0; +- } +- for (i = 0; i < _ssl_locks_count; i++) { +- _ssl_locks[i] = PyThread_allocate_lock(); +- if (_ssl_locks[i] == NULL) { +- unsigned int j; +- for (j = 0; j < i; j++) { +- PyThread_free_lock(_ssl_locks[j]); +- } +- PyMem_Free(_ssl_locks); +- return 0; +- } +- } +- CRYPTO_set_locking_callback(_ssl_thread_locking_function); +-#if OPENSSL_VERSION_NUMBER >= 0x10000000 +- CRYPTO_THREADID_set_callback(_ssl_threadid_callback); +-#else +- CRYPTO_set_id_callback(_ssl_thread_id_function); +-#endif +- } +- return 1; +-} +- +-#endif /* HAVE_OPENSSL_CRYPTO_LOCK for OpenSSL < 1.1.0 */ +- + PyDoc_STRVAR(module_doc, + "Implementation module for SSL socket operations. See the socket module\n\ + for documentation."); +@@ -6152,14 +5682,6 @@ PyInit__ssl(void) + return NULL; + PySocketModule = *socket_api; + +-#ifndef OPENSSL_VERSION_1_1 +- /* Load all algorithms and initialize cpuid */ +- OPENSSL_add_all_algorithms_noconf(); +- /* Init OpenSSL */ +- SSL_load_error_strings(); +- SSL_library_init(); +-#endif +- + #ifdef HAVE_OPENSSL_CRYPTO_LOCK + /* note that this will start threading if not already started */ + if (!_setup_ssl_threads()) { +@@ -6266,10 +5788,8 @@ PyInit__ssl(void) + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + PyModule_AddIntConstant(m, "VERIFY_X509_STRICT", + X509_V_FLAG_X509_STRICT); +-#ifdef X509_V_FLAG_TRUSTED_FIRST + PyModule_AddIntConstant(m, "VERIFY_X509_TRUSTED_FIRST", + X509_V_FLAG_TRUSTED_FIRST); +-#endif + + /* Alert Descriptions from ssl.h */ + /* note RESERVED constants no longer intended for use have been removed */ +@@ -6426,31 +5946,11 @@ PyInit__ssl(void) + PyModule_AddObject((m), (key), bool_obj); \ + } while (0) + +-#if HAVE_SNI + addbool(m, "HAS_SNI", 1); +-#else +- addbool(m, "HAS_SNI", 0); +-#endif +- + addbool(m, "HAS_TLS_UNIQUE", 1); +- +-#ifndef OPENSSL_NO_ECDH + addbool(m, "HAS_ECDH", 1); +-#else +- addbool(m, "HAS_ECDH", 0); +-#endif +- +-#if HAVE_NPN +- addbool(m, "HAS_NPN", 1); +-#else + addbool(m, "HAS_NPN", 0); +-#endif +- +-#if HAVE_ALPN + addbool(m, "HAS_ALPN", 1); +-#else +- addbool(m, "HAS_ALPN", 0); +-#endif + + #if defined(SSL2_VERSION) && !defined(OPENSSL_NO_SSL2) + addbool(m, "HAS_SSLv2", 1); +--- a/Modules/_ssl/debughelpers.c ++++ b/Modules/_ssl/debughelpers.c +@@ -114,8 +114,6 @@ _PySSLContext_set_msg_callback(PySSLCont + return 0; + } + +-#ifdef HAVE_OPENSSL_KEYLOG +- + static void + _PySSL_keylog_callback(const SSL *ssl, const char *line) + { +@@ -219,5 +217,3 @@ _PySSLContext_set_keylog_filename(PySSLC + SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback); + return 0; + } +- +-#endif +--- a/Modules/clinic/_hashopenssl.c.h ++++ b/Modules/clinic/_hashopenssl.c.h +@@ -965,7 +965,7 @@ exit: + return return_value; + } + +-#if (OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_SCRYPT) && !defined(LIBRESSL_VERSION_NUMBER)) ++#if defined(PY_OPENSSL_HAS_SCRYPT) + + PyDoc_STRVAR(_hashlib_scrypt__doc__, + "scrypt($module, /, password, *, salt=None, n=None, r=None, p=None,\n" +@@ -1093,7 +1093,7 @@ exit: + return return_value; + } + +-#endif /* (OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_SCRYPT) && !defined(LIBRESSL_VERSION_NUMBER)) */ ++#endif /* defined(PY_OPENSSL_HAS_SCRYPT) */ + + PyDoc_STRVAR(_hashlib_hmac_singleshot__doc__, + "hmac_digest($module, /, key, msg, digest)\n" +@@ -1324,8 +1324,6 @@ _hashlib_HMAC_hexdigest(HMACobject *self + return _hashlib_HMAC_hexdigest_impl(self); + } + +-#if !defined(LIBRESSL_VERSION_NUMBER) +- + PyDoc_STRVAR(_hashlib_get_fips_mode__doc__, + "get_fips_mode($module, /)\n" + "--\n" +@@ -1361,8 +1359,6 @@ exit: + return return_value; + } + +-#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ +- + PyDoc_STRVAR(_hashlib_compare_digest__doc__, + "compare_digest($module, a, b, /)\n" + "--\n" +@@ -1439,7 +1435,4 @@ exit: + #define _HASHLIB_SCRYPT_METHODDEF + #endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */ + +-#ifndef _HASHLIB_GET_FIPS_MODE_METHODDEF +- #define _HASHLIB_GET_FIPS_MODE_METHODDEF +-#endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */ + /*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/ +--- a/Modules/clinic/_ssl.c.h ++++ b/Modules/clinic/_ssl.c.h +@@ -139,29 +139,6 @@ _ssl__SSLSocket_version(PySSLSocket *sel + return _ssl__SSLSocket_version_impl(self); + } + +-#if (HAVE_NPN) +- +-PyDoc_STRVAR(_ssl__SSLSocket_selected_npn_protocol__doc__, +-"selected_npn_protocol($self, /)\n" +-"--\n" +-"\n"); +- +-#define _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF \ +- {"selected_npn_protocol", (PyCFunction)_ssl__SSLSocket_selected_npn_protocol, METH_NOARGS, _ssl__SSLSocket_selected_npn_protocol__doc__}, +- +-static PyObject * +-_ssl__SSLSocket_selected_npn_protocol_impl(PySSLSocket *self); +- +-static PyObject * +-_ssl__SSLSocket_selected_npn_protocol(PySSLSocket *self, PyObject *Py_UNUSED(ignored)) +-{ +- return _ssl__SSLSocket_selected_npn_protocol_impl(self); +-} +- +-#endif /* (HAVE_NPN) */ +- +-#if (HAVE_ALPN) +- + PyDoc_STRVAR(_ssl__SSLSocket_selected_alpn_protocol__doc__, + "selected_alpn_protocol($self, /)\n" + "--\n" +@@ -179,8 +156,6 @@ _ssl__SSLSocket_selected_alpn_protocol(P + return _ssl__SSLSocket_selected_alpn_protocol_impl(self); + } + +-#endif /* (HAVE_ALPN) */ +- + PyDoc_STRVAR(_ssl__SSLSocket_compression__doc__, + "compression($self, /)\n" + "--\n" +@@ -457,8 +432,6 @@ exit: + return return_value; + } + +-#if (OPENSSL_VERSION_NUMBER >= 0x10002000UL) +- + PyDoc_STRVAR(_ssl__SSLContext_get_ciphers__doc__, + "get_ciphers($self, /)\n" + "--\n" +@@ -476,44 +449,6 @@ _ssl__SSLContext_get_ciphers(PySSLContex + return _ssl__SSLContext_get_ciphers_impl(self); + } + +-#endif /* (OPENSSL_VERSION_NUMBER >= 0x10002000UL) */ +- +-PyDoc_STRVAR(_ssl__SSLContext__set_npn_protocols__doc__, +-"_set_npn_protocols($self, protos, /)\n" +-"--\n" +-"\n"); +- +-#define _SSL__SSLCONTEXT__SET_NPN_PROTOCOLS_METHODDEF \ +- {"_set_npn_protocols", (PyCFunction)_ssl__SSLContext__set_npn_protocols, METH_O, _ssl__SSLContext__set_npn_protocols__doc__}, +- +-static PyObject * +-_ssl__SSLContext__set_npn_protocols_impl(PySSLContext *self, +- Py_buffer *protos); +- +-static PyObject * +-_ssl__SSLContext__set_npn_protocols(PySSLContext *self, PyObject *arg) +-{ +- PyObject *return_value = NULL; +- Py_buffer protos = {NULL, NULL}; +- +- if (PyObject_GetBuffer(arg, &protos, PyBUF_SIMPLE) != 0) { +- goto exit; +- } +- if (!PyBuffer_IsContiguous(&protos, 'C')) { +- _PyArg_BadArgument("_set_npn_protocols", "argument", "contiguous buffer", arg); +- goto exit; +- } +- return_value = _ssl__SSLContext__set_npn_protocols_impl(self, &protos); +- +-exit: +- /* Cleanup for protos */ +- if (protos.obj) { +- PyBuffer_Release(&protos); +- } +- +- return return_value; +-} +- + PyDoc_STRVAR(_ssl__SSLContext__set_alpn_protocols__doc__, + "_set_alpn_protocols($self, protos, /)\n" + "--\n" +@@ -844,8 +779,6 @@ _ssl__SSLContext_set_default_verify_path + return _ssl__SSLContext_set_default_verify_paths_impl(self); + } + +-#if !defined(OPENSSL_NO_ECDH) +- + PyDoc_STRVAR(_ssl__SSLContext_set_ecdh_curve__doc__, + "set_ecdh_curve($self, name, /)\n" + "--\n" +@@ -854,8 +787,6 @@ PyDoc_STRVAR(_ssl__SSLContext_set_ecdh_c + #define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF \ + {"set_ecdh_curve", (PyCFunction)_ssl__SSLContext_set_ecdh_curve, METH_O, _ssl__SSLContext_set_ecdh_curve__doc__}, + +-#endif /* !defined(OPENSSL_NO_ECDH) */ +- + PyDoc_STRVAR(_ssl__SSLContext_cert_store_stats__doc__, + "cert_store_stats($self, /)\n" + "--\n" +@@ -1455,22 +1386,6 @@ exit: + + #endif /* defined(_MSC_VER) */ + +-#ifndef _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF +- #define _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF +-#endif /* !defined(_SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF) */ +- +-#ifndef _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF +- #define _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF +-#endif /* !defined(_SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF) */ +- +-#ifndef _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF +- #define _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF +-#endif /* !defined(_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF) */ +- +-#ifndef _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF +- #define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF +-#endif /* !defined(_SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF) */ +- + #ifndef _SSL_RAND_EGD_METHODDEF + #define _SSL_RAND_EGD_METHODDEF + #endif /* !defined(_SSL_RAND_EGD_METHODDEF) */ +--- a/Tools/ssl/multissltests.py ++++ b/Tools/ssl/multissltests.py +@@ -44,8 +44,6 @@ import tarfile + log = logging.getLogger("multissl") + + OPENSSL_OLD_VERSIONS = [ +- "1.0.2u", +- "1.1.0l", + ] + + OPENSSL_RECENT_VERSIONS = [ +@@ -54,11 +52,9 @@ OPENSSL_RECENT_VERSIONS = [ + ] + + LIBRESSL_OLD_VERSIONS = [ +- "2.9.2", + ] + + LIBRESSL_RECENT_VERSIONS = [ +- "3.1.0", + ] + + # store files in ../multissl +--- a/configure ++++ b/configure +@@ -88,6 +88,13 @@ fi + # splitting by setting IFS to empty value.) + IFS=" "" $as_nl" + ++# IFS ++# We need space, tab and new line, in precisely that order. Quoting is ++# there to prevent editors from complaining about space-tab. ++# (If _AS_PATH_WALK were called with IFS unset, it would disable word ++# splitting by setting IFS to empty value.) ++IFS=" "" $as_nl" ++ + # Find who we are. Look in the path if we contain no directory separator. + as_myself= + case $0 in #(( +@@ -17997,7 +18004,6 @@ as_fn_error () + as_fn_exit $as_status + } # as_fn_error + +- + # as_fn_set_status STATUS + # ----------------------- + # Set $? to STATUS, without forking. +@@ -19043,4 +19049,3 @@ if test "$Py_OPT" = 'false' -a "$Py_DEBU + echo "" >&6 + echo "" >&6 + fi +- +--- a/configure.ac ++++ b/configure.ac +@@ -5756,42 +5756,6 @@ ac_includes_default="$save_includes_defa + # Check for usable OpenSSL + AX_CHECK_OPENSSL([have_openssl=yes],[have_openssl=no]) + +-if test "$have_openssl" = yes; then +- AC_MSG_CHECKING([for X509_VERIFY_PARAM_set1_host in libssl]) +- +- save_LIBS="$LIBS" +- save_LDFLAGS="$LDFLAGS" +- save_CPPFLAGS="$CPPFLAGS" +- LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" +- LIBS="$OPENSSL_LIBS $LIBS" +- CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" +- +- AC_LINK_IFELSE([AC_LANG_PROGRAM([ +- [#include ] +- ], [ +- [X509_VERIFY_PARAM *p = X509_VERIFY_PARAM_new();] +- [X509_VERIFY_PARAM_set1_host(p, "localhost", 0);] +- [X509_VERIFY_PARAM_set1_ip_asc(p, "127.0.0.1");] +- [X509_VERIFY_PARAM_set_hostflags(p, 0);] +- ]) +- ], +- [ +- ac_cv_has_x509_verify_param_set1_host=yes +- ], +- [ +- ac_cv_has_x509_verify_param_set1_host=no +- ]) +- AC_MSG_RESULT($ac_cv_has_x509_verify_param_set1_host) +- if test "$ac_cv_has_x509_verify_param_set1_host" = "yes"; then +- AC_DEFINE(HAVE_X509_VERIFY_PARAM_SET1_HOST, 1, +- [Define if libssl has X509_VERIFY_PARAM_set1_host and related function]) +- fi +- +- CPPFLAGS="$save_CPPFLAGS" +- LDFLAGS="$save_LDFLAGS" +- LIBS="$save_LIBS" +-fi +- + # ssl module default cipher suite string + AH_TEMPLATE(PY_SSL_DEFAULT_CIPHERS, + [Default cipher suites list for ssl module. +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -1351,9 +1351,6 @@ + /* Define to 1 if you have the `writev' function. */ + #undef HAVE_WRITEV + +-/* Define if libssl has X509_VERIFY_PARAM_set1_host and related function */ +-#undef HAVE_X509_VERIFY_PARAM_SET1_HOST +- + /* Define if the zlib library has inflateCopy */ + #undef HAVE_ZLIB_COPY + +--- a/setup.py ++++ b/setup.py +@@ -539,10 +539,7 @@ class PyBuildExt(build_ext): + for l in (self.missing, self.failed, self.failed_on_import)): + print() + print("Could not build the ssl module!") +- print("Python requires an OpenSSL 1.0.2 or 1.1 compatible " +- "libssl with X509_VERIFY_PARAM_set1_host().") +- print("LibreSSL 2.6.4 and earlier do not provide the necessary " +- "APIs, https://github.com/libressl-portable/portable/issues/381") ++ print("Python requires a OpenSSL 1.1.1 or newer") + print() + + if os.environ.get("PYTHONSTRICTEXTENSIONBUILD") and (self.failed or self.failed_on_import): +@@ -2346,13 +2343,13 @@ class PyBuildExt(build_ext): + self.missing.extend(['_ssl', '_hashlib']) + return None, None + +- # OpenSSL 1.0.2 uses Kerberos for KRB5 ciphers +- krb5_h = find_file( +- 'krb5.h', self.inc_dirs, +- ['/usr/kerberos/include'] ++ self.add(Extension( ++ '_ssl', ['_ssl.c'], ++ include_dirs=openssl_includes, ++ library_dirs=openssl_libdirs, ++ libraries=openssl_libs, ++ depends=['socketmodule.h', '_ssl/debughelpers.c']) + ) +- if krb5_h: +- ssl_incs.extend(krb5_h) + + if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"): + self.add(Extension( +@@ -2367,8 +2364,6 @@ class PyBuildExt(build_ext): + '_ssl_data_111.h', + '_ssl_data_300.h', + ])) +- else: +- self.missing.append('_ssl') + + self.add(Extension('_hashlib', ['_hashopenssl.c'], + depends=['hashlib.h'], diff --git a/CVE-2025-6069-quad-complex-HTMLParser.patch b/CVE-2025-6069-quad-complex-HTMLParser.patch new file mode 100644 index 0000000..e75d9bf --- /dev/null +++ b/CVE-2025-6069-quad-complex-HTMLParser.patch @@ -0,0 +1,238 @@ +From 2a6869c71a3132eff9c7be96db9bdca48b3636aa Mon Sep 17 00:00:00 2001 +From: Serhiy Storchaka +Date: Fri, 13 Jun 2025 19:57:48 +0300 +Subject: [PATCH] [3.9] gh-135462: Fix quadratic complexity in processing + special input in HTMLParser (GH-135464) + +End-of-file errors are now handled according to the HTML5 specs -- +comments and declarations are automatically closed, tags are ignored. +(cherry picked from commit 6eb6c5dbfb528bd07d77b60fd71fd05d81d45c41) + +Co-authored-by: Serhiy Storchaka +--- + Lib/html/parser.py | 41 +++- + Lib/test/test_htmlparser.py | 95 ++++++++-- + Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst | 4 + 3 files changed, 117 insertions(+), 23 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst + +Index: Python-3.9.23/Lib/html/parser.py +=================================================================== +--- Python-3.9.23.orig/Lib/html/parser.py 2025-07-02 18:10:23.763249887 +0200 ++++ Python-3.9.23/Lib/html/parser.py 2025-07-02 18:10:29.124564834 +0200 +@@ -25,6 +25,7 @@ + charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]') + + starttagopen = re.compile('<[a-zA-Z]') ++endtagopen = re.compile('') + commentclose = re.compile(r'--\s*>') + # Note: +@@ -176,7 +177,7 @@ + k = self.parse_pi(i) + elif startswith("', i + 1) +- if k < 0: +- k = rawdata.find('<', i + 1) +- if k < 0: +- k = i + 1 ++ if starttagopen.match(rawdata, i): # < + letter ++ pass ++ elif startswith("'), +- ('comment', '/img'), +- ('endtag', 'html<')]) ++ ('data', '\n')]) + + def test_starttag_junk_chars(self): ++ self._run_check("<", [('data', '<')]) ++ self._run_check("<>", [('data', '<>')]) ++ self._run_check("< >", [('data', '< >')]) ++ self._run_check("< ", [('data', '< ')]) + self._run_check("", []) ++ self._run_check("<$>", [('data', '<$>')]) + self._run_check("", [('comment', '$')]) + self._run_check("", [('endtag', 'a')]) ++ self._run_check("", [('starttag', 'a", [('endtag', 'a'", [('data', "'", []) ++ self._run_check("", [('starttag', 'a$b', [])]) + self._run_check("", [('startendtag', 'a$b', [])]) + self._run_check("", [('starttag', 'a$b', [])]) + self._run_check("", [('startendtag', 'a$b', [])]) ++ self._run_check("", [('endtag', 'a$b')]) + + def test_slashes_in_starttag(self): + self._run_check('', [('startendtag', 'a', [('foo', 'var')])]) +@@ -537,13 +545,56 @@ + for html, expected in data: + self._run_check(html, expected) + +- def test_broken_comments(self): +- html = ('' ++ def test_eof_in_comments(self): ++ data = [ ++ ('', [('comment', '-!>')]), ++ ('' + '' + '' + '') + expected = [ ++ ('comment', 'ELEMENT br EMPTY'), + ('comment', ' not really a comment '), + ('comment', ' not a comment either --'), + ('comment', ' -- close enough --'), +@@ -598,6 +649,26 @@ + ('endtag', 'a'), ('data', ' bar & baz')] + ) + ++ @support.requires_resource('cpu') ++ def test_eof_no_quadratic_complexity(self): ++ # Each of these examples used to take about an hour. ++ # Now they take a fraction of a second. ++ def check(source): ++ parser = html.parser.HTMLParser() ++ parser.feed(source) ++ parser.close() ++ n = 120_000 ++ check(" +Date: Mon, 28 Jul 2025 17:37:26 +0200 +Subject: [PATCH] [3.9] gh-130577: tarfile now validates archives to ensure + member offsets are non-negative (GH-137027) (cherry picked from commit + 7040aa54f14676938970e10c5f74ea93cd56aa38) + +Co-authored-by: Alexander Urieles +Co-authored-by: Gregory P. Smith +--- + Lib/tarfile.py | 3 + Lib/test/test_tarfile.py | 156 ++++++++++ + Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3 + 3 files changed, 162 insertions(+) + create mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst + +Index: Python-3.9.23/Lib/tarfile.py +=================================================================== +--- Python-3.9.23.orig/Lib/tarfile.py 2025-08-02 17:56:38.706191816 +0200 ++++ Python-3.9.23/Lib/tarfile.py 2025-08-02 17:56:43.118456301 +0200 +@@ -1601,6 +1601,9 @@ + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ ++ # Only non-negative offsets are allowed ++ if count < 0: ++ raise InvalidHeaderError("invalid offset") + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 +Index: Python-3.9.23/Lib/test/test_tarfile.py +=================================================================== +--- Python-3.9.23.orig/Lib/test/test_tarfile.py 2025-08-02 17:56:40.012303862 +0200 ++++ Python-3.9.23/Lib/test/test_tarfile.py 2025-08-02 17:56:56.856747140 +0200 +@@ -48,6 +48,7 @@ + xzname = os.path.join(TEMPDIR, "testtar.tar.xz") + tmpname = os.path.join(TEMPDIR, "tmp.tar") + dotlessname = os.path.join(TEMPDIR, "testtar") ++SPACE = b" " + + sha256_regtype = ( + "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce" +@@ -4234,6 +4235,161 @@ + self.expect_exception(TypeError) # errorlevel is not int + + ++class OffsetValidationTests(unittest.TestCase): ++ tarname = tmpname ++ invalid_posix_header = ( ++ # name: 100 bytes ++ tarfile.NUL * tarfile.LENGTH_NAME ++ # mode, space, null terminator: 8 bytes ++ + b"000755" + SPACE + tarfile.NUL ++ # uid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # gid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # size, space: 12 bytes ++ + b"\xff" * 11 + SPACE ++ # mtime, space: 12 bytes ++ + tarfile.NUL * 11 + SPACE ++ # chksum: 8 bytes ++ + b"0011407" + tarfile.NUL ++ # type: 1 byte ++ + tarfile.REGTYPE ++ # linkname: 100 bytes ++ + tarfile.NUL * tarfile.LENGTH_LINK ++ # magic: 6 bytes, version: 2 bytes ++ + tarfile.POSIX_MAGIC ++ # uname: 32 bytes ++ + tarfile.NUL * 32 ++ # gname: 32 bytes ++ + tarfile.NUL * 32 ++ # devmajor, space, null terminator: 8 bytes ++ + tarfile.NUL * 6 + SPACE + tarfile.NUL ++ # devminor, space, null terminator: 8 bytes ++ + tarfile.NUL * 6 + SPACE + tarfile.NUL ++ # prefix: 155 bytes ++ + tarfile.NUL * tarfile.LENGTH_PREFIX ++ # padding: 12 bytes ++ + tarfile.NUL * 12 ++ ) ++ invalid_gnu_header = ( ++ # name: 100 bytes ++ tarfile.NUL * tarfile.LENGTH_NAME ++ # mode, null terminator: 8 bytes ++ + b"0000755" + tarfile.NUL ++ # uid, null terminator: 8 bytes ++ + b"0000001" + tarfile.NUL ++ # gid, space, null terminator: 8 bytes ++ + b"0000001" + tarfile.NUL ++ # size, space: 12 bytes ++ + b"\xff" * 11 + SPACE ++ # mtime, space: 12 bytes ++ + tarfile.NUL * 11 + SPACE ++ # chksum: 8 bytes ++ + b"0011327" + tarfile.NUL ++ # type: 1 byte ++ + tarfile.REGTYPE ++ # linkname: 100 bytes ++ + tarfile.NUL * tarfile.LENGTH_LINK ++ # magic: 8 bytes ++ + tarfile.GNU_MAGIC ++ # uname: 32 bytes ++ + tarfile.NUL * 32 ++ # gname: 32 bytes ++ + tarfile.NUL * 32 ++ # devmajor, null terminator: 8 bytes ++ + tarfile.NUL * 8 ++ # devminor, null terminator: 8 bytes ++ + tarfile.NUL * 8 ++ # padding: 167 bytes ++ + tarfile.NUL * 167 ++ ) ++ invalid_v7_header = ( ++ # name: 100 bytes ++ tarfile.NUL * tarfile.LENGTH_NAME ++ # mode, space, null terminator: 8 bytes ++ + b"000755" + SPACE + tarfile.NUL ++ # uid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # gid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # size, space: 12 bytes ++ + b"\xff" * 11 + SPACE ++ # mtime, space: 12 bytes ++ + tarfile.NUL * 11 + SPACE ++ # chksum: 8 bytes ++ + b"0010070" + tarfile.NUL ++ # type: 1 byte ++ + tarfile.REGTYPE ++ # linkname: 100 bytes ++ + tarfile.NUL * tarfile.LENGTH_LINK ++ # padding: 255 bytes ++ + tarfile.NUL * 255 ++ ) ++ valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT) ++ data_block = b"\xff" * tarfile.BLOCKSIZE ++ ++ def _write_buffer(self, buffer): ++ with open(self.tarname, "wb") as f: ++ f.write(buffer) ++ ++ def _get_members(self, ignore_zeros=None): ++ with open(self.tarname, "rb") as f: ++ with tarfile.open( ++ mode="r", fileobj=f, ignore_zeros=ignore_zeros ++ ) as tar: ++ return tar.getmembers() ++ ++ def _assert_raises_read_error_exception(self): ++ with self.assertRaisesRegex( ++ tarfile.ReadError, "file could not be opened successfully" ++ ): ++ self._get_members() ++ ++ def test_invalid_offset_header_validations(self): ++ for tar_format, invalid_header in ( ++ ("posix", self.invalid_posix_header), ++ ("gnu", self.invalid_gnu_header), ++ ("v7", self.invalid_v7_header), ++ ): ++ with self.subTest(format=tar_format): ++ self._write_buffer(invalid_header) ++ self._assert_raises_read_error_exception() ++ ++ def test_early_stop_at_invalid_offset_header(self): ++ buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header ++ self._write_buffer(buffer) ++ members = self._get_members() ++ self.assertEqual(len(members), 1) ++ self.assertEqual(members[0].name, "filename") ++ self.assertEqual(members[0].offset, 0) ++ ++ def test_ignore_invalid_archive(self): ++ # 3 invalid headers with their respective data ++ buffer = (self.invalid_gnu_header + self.data_block) * 3 ++ self._write_buffer(buffer) ++ members = self._get_members(ignore_zeros=True) ++ self.assertEqual(len(members), 0) ++ ++ def test_ignore_invalid_offset_headers(self): ++ for first_block, second_block, expected_offset in ( ++ ( ++ (self.valid_gnu_header), ++ (self.invalid_gnu_header + self.data_block), ++ 0, ++ ), ++ ( ++ (self.invalid_gnu_header + self.data_block), ++ (self.valid_gnu_header), ++ 1024, ++ ), ++ ): ++ self._write_buffer(first_block + second_block) ++ members = self._get_members(ignore_zeros=True) ++ self.assertEqual(len(members), 1) ++ self.assertEqual(members[0].name, "filename") ++ self.assertEqual(members[0].offset, expected_offset) ++ ++ + def setUpModule(): + support.unlink(TEMPDIR) + os.makedirs(TEMPDIR) +Index: Python-3.9.23/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ Python-3.9.23/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst 2025-08-02 17:56:43.119864870 +0200 +@@ -0,0 +1,3 @@ ++:mod:`tarfile` now validates archives to ensure member offsets are ++non-negative. (Contributed by Alexander Enrique Urieles Nieto in ++:gh:`130577`.) diff --git a/F00251-change-user-install-location.patch b/F00251-change-user-install-location.patch new file mode 100644 index 0000000..356c890 --- /dev/null +++ b/F00251-change-user-install-location.patch @@ -0,0 +1,57 @@ +From 910f38d9768d39d4d31426743ae4081ed1ab66b6 Mon Sep 17 00:00:00 2001 +From: Michal Cyprian +Date: Mon, 26 Jun 2017 16:32:56 +0200 +Subject: [PATCH] 00251: Change user install location + +Set values of prefix and exec_prefix in distutils install command +to /usr/local if executable is /usr/bin/python* and RPM build +is not detected to make pip and distutils install into separate location. + +Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe +--- + Lib/distutils/command/install.py | 15 +++++++++++++-- + Lib/site.py | 9 ++++++++- + 2 files changed, 21 insertions(+), 3 deletions(-) + +--- a/Lib/distutils/command/install.py ++++ b/Lib/distutils/command/install.py +@@ -419,8 +419,19 @@ class install(Command): + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") + +- self.prefix = os.path.normpath(sys.prefix) +- self.exec_prefix = os.path.normpath(sys.exec_prefix) ++ # self.prefix is set to sys.prefix + /local/ ++ # if neither RPM build nor virtual environment is ++ # detected to make pip and distutils install packages ++ # into the separate location. ++ if (not (hasattr(sys, 'real_prefix') or ++ sys.prefix != sys.base_prefix) and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ addition = "/local" ++ else: ++ addition = "" ++ ++ self.prefix = os.path.normpath(sys.prefix) + addition ++ self.exec_prefix = os.path.normpath(sys.exec_prefix) + addition + + else: + if self.exec_prefix is None: +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -362,7 +362,14 @@ def getsitepackages(prefixes=None): + return sitepackages + + def addsitepackages(known_paths, prefixes=None): +- """Add site-packages to sys.path""" ++ """Add site-packages to sys.path ++ ++ '/usr/local' is included in PREFIXES if RPM build is not detected ++ to make packages installed into this location visible. ++ ++ """ ++ if ENABLE_USER_SITE and 'RPM_BUILD_ROOT' not in os.environ: ++ PREFIXES.insert(0, "/usr/local") + for sitedir in getsitepackages(prefixes): + if os.path.isdir(sitedir): + addsitedir(sitedir, known_paths) diff --git a/PACKAGING-NOTES b/PACKAGING-NOTES new file mode 100644 index 0000000..e28c88c --- /dev/null +++ b/PACKAGING-NOTES @@ -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. diff --git a/Python-3.9.23.tar.xz b/Python-3.9.23.tar.xz new file mode 100644 index 0000000..1edf267 --- /dev/null +++ b/Python-3.9.23.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61a42919e13d539f7673cf11d1c404380e28e540510860b9d242196e165709c9 +size 19659284 diff --git a/Python-3.9.23.tar.xz.sigstore b/Python-3.9.23.tar.xz.sigstore new file mode 100644 index 0000000..f3e8c70 --- /dev/null +++ b/Python-3.9.23.tar.xz.sigstore @@ -0,0 +1 @@ +{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIIC0DCCAlWgAwIBAgIUG229RKhIVHHNhr+bZV55P8F/JiYwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUwNjAzMTkyMDQ1WhcNMjUwNjAzMTkzMDQ1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHAdkk3GmQSTLHaA3qgs8Mto97OSVbVhaNbzSF2eatgDIKJUkUF9BFqx+RKVYbHU5FQJHdwA5T67OfhaDYHiyaqOCAXQwggFwMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUaB0HmIo69ITnU91ryuyAKS4hsbgwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHQYDVR0RAQH/BBMwEYEPbHVrYXN6QGxhbmdhLnBsMCwGCisGAQQBg78wAQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDAuBgorBgEEAYO/MAEIBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCBiwYKKwYBBAHWeQIEAgR9BHsAeQB3AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABlzc9LZwAAAQDAEgwRgIhAKVxDJnomsWJaXHsKKu19qVvfaPnwK5YQXGoI9SKn+NJAiEA+CLl5/VDMS+UMV8Nh+89J2zcpcypEhw6C4tW1BCCuDAwCgYIKoZIzj0EAwMDaQAwZgIxAICtnwUolcM1gk7JBamvFMSw9K1YXhTaErgTVTlJK+pVy7GnuG9sFdBAo1cu2l8KpwIxAPa0s2b6co6pQfHxXbTADBsp4WT6YmiW7A+92JlwY87vD+7dP1rKzW3NWRZOE5BnqQ=="}, "tlogEntries": [{"logIndex": "228949549", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1748978445", "inclusionPromise": {"signedEntryTimestamp": "MEUCIQCNuRResQCE4b2mA5/+gTwctji/1qL67SbeaciSlgj6NQIgE7vY0Fdoem2aODoKaHoieIRo89rcg7mX2mZL3B/XYh8="}, "inclusionProof": {"logIndex": "107045287", "rootHash": "hgR6aB6kU+T1uo4iUx75tlIhKkYbUyPsHac2yhFgZpE=", "treeSize": "107045297", "hashes": ["31ltlTE5JIioyJ8rdH3OjwO9d3Us17sfd29GleLF5J0=", "AkqFsr2XZZPs2xu+kw64e2CheTwS6pgmH1DXBgpJfAo=", "LJsE/B9ZOF5PmmdxEQjiLmyHEEzfGmsX9HiXp3ZTiTU=", "VEnlDOQVZn3+NExF7G53geFQZYNf6U5DexkG4vgRlLQ=", "auvWhUrmnBq8g0KEcbAMvjyfrOYhAmiC5+yXjoBsGiw=", "8Cr3zC0dQe124OAQufmKfTZ8lnAYWQuw6AnXuy6DDMQ=", "tbcHjIX6G446NLcoiLw+hjALDmPwWWErWEOvrndCH7Q=", "Bj4reJ88xQpUq0P43RDNLi1sLcLaEeH443F87S4CHoc=", "mAX/zvx1jR0ujLtDApsQpHyxmoDGidClHMOn0BX1aQA=", "u5LKLBPTYgXZg0fBi6/8LuEeNy3EBAxJF0AkkB4Co6E=", "SPUVncwJRVX/n/RICCYqLpAzraqx7S0eMdXRr1RLRgg=", "uEJFtwcGQJMd9kjQhkXb7gl2WD3WMElCc15uDFvFGxs=", "VdOKzpQhJlpXgijzXANf/hNlje1G/N1kUuVnKNskkso=", "mta5fH/gFwxJ/0fT8yGpn3sFCY0G1RY555Iflm0LInM=", "7v8qPHNDLerpduaMx06eb/MwgoQwczTn/cYGKX/9wZ4="], "checkpoint": {"envelope": "rekor.sigstore.dev - 1193050959916656506\n107045297\nhgR6aB6kU+T1uo4iUx75tlIhKkYbUyPsHac2yhFgZpE=\n\n\u2014 rekor.sigstore.dev wNI9ajBFAiEAqS6BBP1515FNbJUk/993J1ftsTnoVvZ3qFtXGB5WT6cCIGjBPbInr7P6zATImuD6RaTQfWxcPpmcquKCZbAnIseq\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MWE0MjkxOWUxM2Q1MzlmNzY3M2NmMTFkMWM0MDQzODBlMjhlNTQwNTEwODYwYjlkMjQyMTk2ZTE2NTcwOWM5In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJRVJHRUpUU0RXOVhjR0lDbFZKVXdXOFcyeTlHdVRIQnNJUXozTE9pVkU1R0FpQWFINnVTdVBSNmYvbURkeXN0Q1JLZWVreWdrVmZvQ3gyekJGWGVNRUNxNEE9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTXdSRU5EUVd4WFowRjNTVUpCWjBsVlJ6SXlPVkpMYUVsV1NFaE9hSElyWWxwV05UVlFPRVl2U21sWmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFZkMDVxUVhwTlZHdDVUVVJSTVZkb1kwNU5hbFYzVG1wQmVrMVVhM3BOUkZFeFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZJUVdScmF6TkhiVkZUVkV4SVlVRXpjV2R6T0UxMGJ6azNUMU5XWWxab1lVNWllbE1LUmpKbFlYUm5SRWxMU2xWclZVWTVRa1p4ZUN0U1MxWlpZa2hWTlVaUlNraGtkMEUxVkRZM1QyWm9ZVVJaU0dsNVlYRlBRMEZZVVhkblowWjNUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZoUWpCSUNtMUpielk1U1ZSdVZUa3hjbmwxZVVGTFV6Um9jMkpuZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoUldVUldVakJTUVZGSUwwSkNUWGRGV1VWUVlraFdjbGxZVGpaUlIzaG9ZbTFrYUV4dVFuTk5RM2RIUTJselIwRlJVVUpuTnpoM1FWRkZSUXBJYldnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemx6WWpKa2NHSnBPWFpaV0ZZd1lVUkJkVUpuYjNKQ1owVkZRVmxQTDAxQlJVbENRMEZOQ2todGFEQmtTRUo2VDJrNGRsb3liREJoU0ZacFRHMU9kbUpUT1hOaU1tUndZbWs1ZGxsWVZqQmhSRU5DYVhkWlMwdDNXVUpDUVVoWFpWRkpSVUZuVWprS1FraHpRV1ZSUWpOQlRqQTVUVWR5UjNoNFJYbFplR3RsU0Vwc2JrNTNTMmxUYkRZME0ycDVkQzgwWlV0amIwRjJTMlUyVDBGQlFVSnNlbU01VEZwM1FRcEJRVkZFUVVWbmQxSm5TV2hCUzFaNFJFcHViMjF6VjBwaFdFaHpTMHQxTVRseFZuWm1ZVkJ1ZDBzMVdWRllSMjlKT1ZOTGJpdE9Ta0ZwUlVFclEweHNDalV2VmtSTlV5dFZUVlk0VG1nck9EbEtNbnBqY0dONWNFVm9kelpETkhSWE1VSkRRM1ZFUVhkRFoxbEpTMjlhU1hwcU1FVkJkMDFFWVZGQmQxcG5TWGdLUVVsRGRHNTNWVzlzWTAweFoyczNTa0poYlhaR1RWTjNPVXN4V1Zob1ZHRkZjbWRVVmxSc1Nrc3JjRlo1TjBkdWRVYzVjMFprUWtGdk1XTjFNbXc0U3dwd2QwbDRRVkJoTUhNeVlqWmpielp3VVdaSWVGaGlWRUZFUW5Od05GZFVObGx0YVZjM1FTczVNa3BzZDFrNE4zWkVLemRrVURGeVMzcFhNMDVYVWxwUENrVTFRbTV4VVQwOUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19"}], "timestampVerificationData": {}}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "YaQpGeE9U592c88R0cQEOA4o5UBRCGC50kIZbhZXCck="}, "signature": "MEQCIERGEJTSDW9XcGIClVJUwW8W2y9GuTHBsIQz3LOiVE5GAiAaH6uSuPR6f/mDdystCRKeekygkVfoCx2zBFXeMECq4A=="}} diff --git a/Python-3.9.24.tar.xz b/Python-3.9.24.tar.xz new file mode 100644 index 0000000..962bcf2 --- /dev/null +++ b/Python-3.9.24.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:668391afabd5083faafa4543753d190f82f33ce6ba22d6e9ac728b43644b278a +size 20176216 diff --git a/Python-3.9.24.tar.xz.sigstore b/Python-3.9.24.tar.xz.sigstore new file mode 100644 index 0000000..46b8ad5 --- /dev/null +++ b/Python-3.9.24.tar.xz.sigstore @@ -0,0 +1 @@ +{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIICzjCCAlSgAwIBAgIUWUQZdrlPpYck5mBa5p/erkhWfVEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUxMDA5MTI0MDE5WhcNMjUxMDA5MTI1MDE5WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwTfhkUPkWQYRXgJJLSSh9G4rT7+j6+rS6dHBDvvKixE46/x1FMOSgLKo6A8iHnpQ/kPm8uFvYnWxujVq4S0PhqOCAXMwggFvMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU3yQFVdjJqb+Kw/3MQTpHDpBXbS0wHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHQYDVR0RAQH/BBMwEYEPbHVrYXN6QGxhbmdhLnBsMCwGCisGAQQBg78wAQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDAuBgorBgEEAYO/MAEIBCAMHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABmcj8kSMAAAQDAEcwRQIhALKTMOxAFvHDvYKLeaw3HoTfbHMK727w8GN5s4XBsvAdAiAmbuJY8VgFLdgV/Vp8ZKkHIndltHPmf9cm9g1TwWN1tTAKBggqhkjOPQQDAwNoADBlAjEApwnH/TqWUsWTIfgmobddn3e5dLnj3wR4CvhCP57zl/1ezrRJdr5zpVjyUIRqT09eAjAyTWTpTbQRzC/0mQYxIB9O1dfr0oNhFXwXWvoS9LAoKo4t7LJtnoBUaU9NL85U6vE="}, "tlogEntries": [{"logIndex": "597490354", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1760013619", "inclusionPromise": {"signedEntryTimestamp": "MEUCIAb40wGh1ztseI9BBY86I0ah8P4983EWJ5QtqsyD7tWWAiEAmCPsL0vrCvrm/zajIUlVzZ/NWW0kEjHFJk5XR+bUPIY="}, "inclusionProof": {"logIndex": "475586092", "rootHash": "gKgTH5eS9k2SUCoQcopHE+4F+U0tBp+n5nTE5kJatgQ=", "treeSize": "475586094", "hashes": ["JnuG6tFwRjh31ssfrULXX0EM6EgDKb/ESxDp2nObzAs=", "R3G5ETpA1x2NN+EpiCYQmQE49ioWrhA+bqub6yxk8nw=", "nhxC9B2j4yZZSHbBXHq0z+eLVPemMtv0Q94L6Dwwx8U=", "dmrDoQ/a4v0UsKdQnhhW0SW9EJO7NYXjihdJogGzgJs=", "Mx5p6Pjxdx4jeyljoyPJqio5/HuoDXt3VsGBKxo4h/U=", "ozwjpRC/chHKIVP1B+5ZxF6lhlftOEOfaUXH5TIP/Gg=", "xS6Mho1XDz30bzMrHeVbL1WVtsUsW8/91aa15y2oCa8=", "PlwzzkmGBQJUxByS0DKMURlgLQdfW+2lfQCGAIjlgkk=", "GDhIZR0BNnPq5tBSGD0X/jc3ecOF6kzwjg54z61Q7bg=", "aivWZ0d9X4kfaLqxl2h+DSSFA4OYi0wHqV94C0yFguM=", "qXhJobQjWl6SO/pue3trUW2uL4jXx24Ip7lpd4hc5bU=", "56ObhlROm9L8Q4JyN+mxEQ5pZD5QdobB1xZFIeL0lVg=", "EGaD/cNavzxGYLx1Gl0uNNWBZvyXlSHSdlIeH7m+63A=", "2Wv4GiithwNukRKV06clevnQQYCzXmSS/+/OJtXgsXQ=", "1mfy94KpcItqshH9+gwqV6jccupcaMpVsF28New8zDY=", "vS7O4ozHIQZJWBiov+mkpI27GE8zAmVCEkRcP3NDyNE="], "checkpoint": {"envelope": "rekor.sigstore.dev - 1193050959916656506\n475586094\ngKgTH5eS9k2SUCoQcopHE+4F+U0tBp+n5nTE5kJatgQ=\n\n\u2014 rekor.sigstore.dev wNI9ajBEAiACw0JCrZ/MG3tqbpRlB2A+S8Uu1S6an7Cd9yTjW1DzwwIgGv4xTwJ3cqoQVnLO8WNO8fYL/sxLlogjMUj/iJsvJBU=\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2NjgzOTFhZmFiZDUwODNmYWFmYTQ1NDM3NTNkMTkwZjgyZjMzY2U2YmEyMmQ2ZTlhYzcyOGI0MzY0NGIyNzhhIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJRmxCalRaMEtaT0JzY2RNL1lBWDdGS1ZKZ0k4alJGeXR3ZTFVQ0tyWFZqTkFpQXZsTmJ4WXI0MjJCSzlySHRTbGVWR0JSNGJBbk9ETVN3eHNFdGxHVkxtd0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjZha05EUVd4VFowRjNTVUpCWjBsVlYxVlJXbVJ5YkZCd1dXTnJOVzFDWVRWd0wyVnlhMmhYWmxaRmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFZlRTFFUVRWTlZFa3dUVVJGTlZkb1kwNU5hbFY0VFVSQk5VMVVTVEZOUkVVMVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVYzVkdab2ExVlFhMWRSV1ZKWVowcEtURk5UYURsSE5ISlVOeXRxTml0eVV6WmtTRUlLUkhaMlMybDRSVFEyTDNneFJrMVBVMmRNUzI4MlFUaHBTRzV3VVM5clVHMDRkVVoyV1c1WGVIVnFWbkUwVXpCUWFIRlBRMEZZVFhkblowWjJUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlV6ZVZGR0NsWmtha3B4WWl0TGR5OHpUVkZVY0VoRWNFSllZbE13ZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBoUldVUldVakJTUVZGSUwwSkNUWGRGV1VWUVlraFdjbGxZVGpaUlIzaG9ZbTFrYUV4dVFuTk5RM2RIUTJselIwRlJVVUpuTnpoM1FWRkZSUXBJYldnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemx6WWpKa2NHSnBPWFpaV0ZZd1lVUkJkVUpuYjNKQ1owVkZRVmxQTDAxQlJVbENRMEZOQ2todGFEQmtTRUo2VDJrNGRsb3liREJoU0ZacFRHMU9kbUpUT1hOaU1tUndZbWs1ZGxsWVZqQmhSRU5DYVdkWlMwdDNXVUpDUVVoWFpWRkpSVUZuVWpnS1FraHZRV1ZCUWpKQlRqQTVUVWR5UjNoNFJYbFplR3RsU0Vwc2JrNTNTMmxUYkRZME0ycDVkQzgwWlV0amIwRjJTMlUyVDBGQlFVSnRZMm80YTFOTlFRcEJRVkZFUVVWamQxSlJTV2hCVEV0VVRVOTRRVVoyU0VSMldVdE1aV0YzTTBodlZHWmlTRTFMTnpJM2R6aEhUalZ6TkZoQ2MzWkJaRUZwUVcxaWRVcFpDamhXWjBaTVpHZFdMMVp3T0ZwTGEwaEpibVJzZEVoUWJXWTVZMjA1WnpGVWQxZE9NWFJVUVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV2UVVSQ2JFRnFSVUVLY0hkdVNDOVVjVmRWYzFkVVNXWm5iVzlpWkdSdU0yVTFaRXh1YWpOM1VqUkRkbWhEVURVM2Vtd3ZNV1Y2Y2xKS1pISTFlbkJXYW5sVlNWSnhWREE1WlFwQmFrRjVWRmRVY0ZSaVVWSjZReTh3YlZGWmVFbENPVTh4WkdaeU1HOU9hRVpZZDFoWGRtOVRPVXhCYjB0dk5IUTNURXAwYm05Q1ZXRlZPVTVNT0RWVkNqWjJSVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0="}], "timestampVerificationData": {}}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "ZoORr6vVCD+q+kVDdT0ZD4LzPOa6ItbprHKLQ2RLJ4o="}, "signature": "MEQCIFlBjTZ0KZOBscdM/YAX7FKVJgI8jRFytwe1UCKrXVjNAiAvlNbxYr422BK9rHtSleVGBR4bAnODMSwxsEtlGVLmwA=="}} diff --git a/README.SUSE b/README.SUSE new file mode 100644 index 0000000..0053bcf --- /dev/null +++ b/README.SUSE @@ -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. diff --git a/_multibuild b/_multibuild new file mode 100644 index 0000000..1d50bc4 --- /dev/null +++ b/_multibuild @@ -0,0 +1,4 @@ + + base + doc + diff --git a/baselibs.conf b/baselibs.conf new file mode 100644 index 0000000..8522338 --- /dev/null +++ b/baselibs.conf @@ -0,0 +1,3 @@ +python39-base +python39 +libpython3_9-1_0 diff --git a/bluez-devel-vendor.tar.xz b/bluez-devel-vendor.tar.xz new file mode 100644 index 0000000..8c61d7a --- /dev/null +++ b/bluez-devel-vendor.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d030d6ff641577625745b435f4a45e9025e11143e60d0bba7dddf53e8bf71941 +size 24976 diff --git a/bpo-31046_ensurepip_honours_prefix.patch b/bpo-31046_ensurepip_honours_prefix.patch new file mode 100644 index 0000000..73effd3 --- /dev/null +++ b/bpo-31046_ensurepip_honours_prefix.patch @@ -0,0 +1,163 @@ +From 5754521af1d51aa8e445cba07a093bbc0c88596d Mon Sep 17 00:00:00 2001 +From: Zackery Spytz +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 +--- + Doc/library/ensurepip.rst | 9 +++-- + Lib/ensurepip/__init__.py | 18 +++++++--- + Lib/test/test_ensurepip.py | 11 ++++++ + Makefile.pre.in | 4 +- + Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst | 1 + 5 files changed, 34 insertions(+), 9 deletions(-) + create mode 100644 Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst + +--- a/Doc/library/ensurepip.rst ++++ b/Doc/library/ensurepip.rst +@@ -56,8 +56,9 @@ is at least as recent as the one bundled + By default, ``pip`` is installed into the current virtual environment + (if one is active) or into the system site packages (if there is no + active virtual environment). The installation location can be controlled +-through two additional command line options: ++through some additional command line options: + ++* ``--prefix ``: Installs ``pip`` using the given directory prefix. + * ``--root ``: Installs ``pip`` relative to the given root directory + rather than the root of the currently active virtual environment (if any) + or the default root for the current Python installation. +@@ -89,7 +90,7 @@ Module API + Returns a string specifying the bundled version of pip that will be + installed when bootstrapping an environment. + +-.. function:: bootstrap(root=None, upgrade=False, user=False, \ ++.. function:: bootstrap(root=None, prefix=None, upgrade=False, user=False, \ + altinstall=False, default_pip=False, \ + verbosity=0) + +@@ -99,6 +100,8 @@ Module API + If *root* is ``None``, then installation uses the default install location + for the current environment. + ++ *prefix* specifies the directory prefix to use when installing. ++ + *upgrade* indicates whether or not to upgrade an existing installation + of an earlier version of ``pip`` to the bundled version. + +@@ -119,6 +122,8 @@ Module API + *verbosity* controls the level of output to :data:`sys.stdout` from the + bootstrapping operation. + ++ .. versionchanged:: 3.9 the *prefix* parameter was added. ++ + .. audit-event:: ensurepip.bootstrap root ensurepip.bootstrap + + .. note:: +--- a/Lib/ensurepip/__init__.py ++++ b/Lib/ensurepip/__init__.py +@@ -57,27 +57,27 @@ def _disable_pip_configuration_settings( + os.environ['PIP_CONFIG_FILE'] = os.devnull + + +-def bootstrap(*, root=None, upgrade=False, user=False, ++def bootstrap(*, root=None, prefix=None, upgrade=False, user=False, + altinstall=False, default_pip=False, + verbosity=0): + """ + Bootstrap pip into the current Python installation (or the given root +- directory). ++ and directory prefix). + + Note that calling this function will alter both sys.path and os.environ. + """ + # Discard the return value +- _bootstrap(root=root, upgrade=upgrade, user=user, ++ _bootstrap(root=root, prefix=prefix, upgrade=upgrade, user=user, + altinstall=altinstall, default_pip=default_pip, + verbosity=verbosity) + + +-def _bootstrap(*, root=None, upgrade=False, user=False, ++def _bootstrap(*, root=None, prefix=None, upgrade=False, user=False, + altinstall=False, default_pip=False, + verbosity=0): + """ + Bootstrap pip into the current Python installation (or the given root +- directory). Returns pip command status code. ++ and directory prefix). Returns pip command status code. + + Note that calling this function will alter both sys.path and os.environ. + """ +@@ -120,6 +120,8 @@ def _bootstrap(*, root=None, upgrade=Fal + args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir] + if root: + args += ["--root", root] ++ if prefix: ++ args += ["--prefix", prefix] + if upgrade: + args += ["--upgrade"] + if user: +@@ -192,6 +194,11 @@ def _main(argv=None): + help="Install everything relative to this alternate root directory.", + ) + parser.add_argument( ++ "--prefix", ++ default=None, ++ help="Install everything using this prefix.", ++ ) ++ parser.add_argument( + "--altinstall", + action="store_true", + default=False, +@@ -210,6 +217,7 @@ def _main(argv=None): + + return _bootstrap( + root=args.root, ++ prefix=args.prefix, + upgrade=args.upgrade, + user=args.user, + verbosity=args.verbosity, +--- a/Lib/test/test_ensurepip.py ++++ b/Lib/test/test_ensurepip.py +@@ -61,6 +61,17 @@ class TestBootstrap(EnsurepipMixin, unit + unittest.mock.ANY, + ) + ++ def test_bootstrapping_with_prefix(self): ++ ensurepip.bootstrap(prefix="/foo/bar/") ++ self.run_pip.assert_called_once_with( ++ [ ++ "install", "--no-cache-dir", "--no-index", "--find-links", ++ unittest.mock.ANY, "--prefix", "/foo/bar/", ++ "setuptools", "pip", ++ ], ++ unittest.mock.ANY, ++ ) ++ + def test_bootstrapping_with_user(self): + ensurepip.bootstrap(user=True) + +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -1263,7 +1263,7 @@ install: @FRAMEWORKINSTALLFIRST@ commoni + install|*) ensurepip="" ;; \ + esac; \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \ +- $$ensurepip --root=$(DESTDIR)/ ; \ ++ $$ensurepip --root=$(DESTDIR)/ --prefix=$(prefix) ; \ + fi + + altinstall: commoninstall +@@ -1273,7 +1273,7 @@ altinstall: commoninstall + install|*) ensurepip="--altinstall" ;; \ + esac; \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \ +- $$ensurepip --root=$(DESTDIR)/ ; \ ++ $$ensurepip --root=$(DESTDIR)/ --prefix=$(prefix) ; \ + fi + + commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \ +--- /dev/null ++++ b/Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst +@@ -0,0 +1 @@ ++A directory prefix can now be specified when using :mod:`ensurepip`. diff --git a/bpo-37596-make-set-marshalling.patch b/bpo-37596-make-set-marshalling.patch new file mode 100644 index 0000000..f484ed3 --- /dev/null +++ b/bpo-37596-make-set-marshalling.patch @@ -0,0 +1,102 @@ +From 33d95c6facdfda3c8c0feffa7a99184e4abc2f63 Mon Sep 17 00:00:00 2001 +From: Brandt Bucher +Date: Wed, 25 Aug 2021 04:14:34 -0700 +Subject: [PATCH] bpo-37596: Make `set` and `frozenset` marshalling + deterministic (GH-27926) + +--- + Lib/test/test_marshal.py | 26 ++++++++ + Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst | 2 + Python/marshal.c | 32 ++++++++++ + 3 files changed, 60 insertions(+) + create mode 100644 Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst + +--- a/Lib/test/test_marshal.py ++++ b/Lib/test/test_marshal.py +@@ -1,4 +1,5 @@ + from test import support ++from test.support.script_helper import assert_python_ok + import array + import io + import marshal +@@ -324,6 +325,31 @@ class BugsTestCase(unittest.TestCase): + for i in range(len(data)): + self.assertRaises(EOFError, marshal.loads, data[0: i]) + ++ def test_deterministic_sets(self): ++ # bpo-37596: To support reproducible builds, sets and frozensets need to ++ # have their elements serialized in a consistent order (even when they ++ # have been scrambled by hash randomization): ++ for kind in ("set", "frozenset"): ++ for elements in ( ++ "float('nan'), b'a', b'b', b'c', 'x', 'y', 'z'", ++ # Also test for bad interactions with backreferencing: ++ "('string', 1), ('string', 2), ('string', 3)", ++ ): ++ s = f"{kind}([{elements}])" ++ with self.subTest(s): ++ # First, make sure that our test case still has different ++ # orders under hash seeds 0 and 1. If this check fails, we ++ # need to update this test with different elements: ++ args = ["-c", f"print({s})"] ++ _, repr_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0") ++ _, repr_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1") ++ self.assertNotEqual(repr_0, repr_1) ++ # Then, perform the actual test: ++ args = ["-c", f"import marshal; print(marshal.dumps({s}))"] ++ _, dump_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0") ++ _, dump_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1") ++ self.assertEqual(dump_0, dump_1) ++ + LARGE_SIZE = 2**31 + pointer_size = 8 if sys.maxsize > 0xFFFFFFFF else 4 + +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst +@@ -0,0 +1,2 @@ ++Ensure that :class:`set` and :class:`frozenset` objects are always ++:mod:`marshalled ` 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; diff --git a/bso1227999-reproducible-builds.patch b/bso1227999-reproducible-builds.patch new file mode 100644 index 0000000..6f1930b --- /dev/null +++ b/bso1227999-reproducible-builds.patch @@ -0,0 +1,37 @@ +From ac2b8869724d7a57d9b5efbdce2f20423214e8bb Mon Sep 17 00:00:00 2001 +From: "Bernhard M. Wiedemann" +Date: Tue, 16 Jul 2024 21:39:33 +0200 +Subject: [PATCH] Allow to override build date with SOURCE_DATE_EPOCH + +to make builds reproducible. +See https://reproducible-builds.org/ for why this is good +and https://reproducible-builds.org/specs/source-date-epoch/ +for the definition of this variable. +--- + Doc/conf.py | 3 ++- + Doc/library/functions.rst | 2 +- + 2 files changed, 3 insertions(+), 2 deletions(-) + +--- a/Doc/conf.py ++++ b/Doc/conf.py +@@ -80,7 +80,8 @@ html_short_title = '%s Documentation' % + + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, + # using the given strftime format. +-html_last_updated_fmt = '%b %d, %Y' ++html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) ++html_last_updated_fmt = time.strftime('%b %d, %Y (%H:%M UTC)', time.gmtime(html_time)) + + # Path to find HTML templates. + templates_path = ['tools/templates'] +--- a/Doc/library/functions.rst ++++ b/Doc/library/functions.rst +@@ -1254,7 +1254,7 @@ are always available. They are listed h + (where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`, + and :mod:`shutil`. + +- .. audit-event:: open file,mode,flags open ++ .. audit-event:: open path,mode,flags open + + The ``mode`` and ``flags`` arguments may have been modified or inferred from + the original call. diff --git a/decimal.patch b/decimal.patch new file mode 100644 index 0000000..0f1599c --- /dev/null +++ b/decimal.patch @@ -0,0 +1,36 @@ +--- + Modules/_decimal/_decimal.c | 4 ++-- + setup.py | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +--- a/Modules/_decimal/_decimal.c ++++ b/Modules/_decimal/_decimal.c +@@ -3279,7 +3279,7 @@ dec_format(PyObject *dec, PyObject *args + } + else { + size_t n = strlen(spec.dot); +- if (n > 1 || (n == 1 && !isascii((uchar)spec.dot[0]))) { ++ if (n > 1 || (n == 1 && !isascii((unsigned char)spec.dot[0]))) { + /* fix locale dependent non-ascii characters */ + dot = dotsep_as_utf8(spec.dot); + if (dot == NULL) { +@@ -3288,7 +3288,7 @@ dec_format(PyObject *dec, PyObject *args + spec.dot = PyBytes_AS_STRING(dot); + } + n = strlen(spec.sep); +- if (n > 1 || (n == 1 && !isascii((uchar)spec.sep[0]))) { ++ if (n > 1 || (n == 1 && !isascii((unsigned char)spec.sep[0]))) { + /* fix locale dependent non-ascii characters */ + sep = dotsep_as_utf8(spec.sep); + if (sep == NULL) { +--- a/setup.py ++++ b/setup.py +@@ -2205,7 +2205,7 @@ class PyBuildExt(build_ext): + undef_macros = [] + if '--with-system-libmpdec' in sysconfig.get_config_var("CONFIG_ARGS"): + include_dirs = [] +- libraries = [':libmpdec.so.2'] ++ libraries = ['mpdec'] + sources = ['_decimal/_decimal.c'] + depends = ['_decimal/docstrings.h'] + else: diff --git a/distutils-reproducible-compile.patch b/distutils-reproducible-compile.patch new file mode 100644 index 0000000..4ec5773 --- /dev/null +++ b/distutils-reproducible-compile.patch @@ -0,0 +1,11 @@ +--- a/Lib/distutils/util.py ++++ b/Lib/distutils/util.py +@@ -433,7 +433,7 @@ byte_compile(files, optimize=%r, force=% + else: + from py_compile import compile + +- for file in py_files: ++ for file in sorted(py_files): + if file[-3:] != ".py": + # This lets us be lazy and not filter filenames in + # the "install_lib" command. diff --git a/downport-Sphinx-features.patch b/downport-Sphinx-features.patch new file mode 100644 index 0000000..cedb1d9 --- /dev/null +++ b/downport-Sphinx-features.patch @@ -0,0 +1,90 @@ +--- + Doc/library/tarfile.rst | 11 ----------- + 1 file changed, 11 deletions(-) + +--- a/Doc/library/tarfile.rst ++++ b/Doc/library/tarfile.rst +@@ -504,7 +504,6 @@ be finalized; only the internally used f + Return an :class:`io.BufferedReader` object. + + .. attribute:: TarFile.errorlevel +- :type: int + + If *errorlevel* is ``0``, errors are ignored when using :meth:`TarFile.extract` + and :meth:`TarFile.extractall`. +@@ -683,19 +682,16 @@ A ``TarInfo`` object has the following p + + + .. attribute:: TarInfo.name +- :type: str + + Name of the archive member. + + + .. attribute:: TarInfo.size +- :type: int + + Size in bytes. + + + .. attribute:: TarInfo.mtime +- :type: int | float + + Time of last modification in seconds since the :ref:`epoch `, + as in :attr:`os.stat_result.st_mtime`. +@@ -707,7 +703,6 @@ A ``TarInfo`` object has the following p + attribute. + + .. attribute:: TarInfo.mode +- :type: int + + Permission bits, as for :func:`os.chmod`. + +@@ -727,7 +722,6 @@ A ``TarInfo`` object has the following p + + + .. attribute:: TarInfo.linkname +- :type: str + + Name of the target file name, which is only present in :class:`TarInfo` objects + of type :const:`LNKTYPE` and :const:`SYMTYPE`. +@@ -739,7 +733,6 @@ A ``TarInfo`` object has the following p + + + .. attribute:: TarInfo.uid +- :type: int + + User ID of the user who originally stored this member. + +@@ -750,7 +743,6 @@ A ``TarInfo`` object has the following p + attribute. + + .. attribute:: TarInfo.gid +- :type: int + + Group ID of the user who originally stored this member. + +@@ -761,7 +753,6 @@ A ``TarInfo`` object has the following p + attribute. + + .. attribute:: TarInfo.uname +- :type: str + + User name. + +@@ -772,7 +763,6 @@ A ``TarInfo`` object has the following p + attribute. + + .. attribute:: TarInfo.gname +- :type: str + + Group name. + +@@ -783,7 +773,6 @@ A ``TarInfo`` object has the following p + attribute. + + .. attribute:: TarInfo.pax_headers +- :type: dict + + A dictionary containing key-value pairs of an associated pax extended header. + diff --git a/gh-78214-marshal_stabilize_FLAG_REF.patch b/gh-78214-marshal_stabilize_FLAG_REF.patch new file mode 100644 index 0000000..d255f3c --- /dev/null +++ b/gh-78214-marshal_stabilize_FLAG_REF.patch @@ -0,0 +1,28 @@ +From 6c8ea7c1dacd42f3ba00440231ec0e6b1a38300d Mon Sep 17 00:00:00 2001 +From: Inada Naoki +Date: Sat, 14 Jul 2018 00:46:11 +0900 +Subject: [PATCH] Use FLAG_REF always for interned strings + +--- + Python/marshal.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/Python/marshal.c ++++ b/Python/marshal.c +@@ -298,9 +298,14 @@ w_ref(PyObject *v, char *flag, WFILE *p) + if (p->version < 3 || p->hashtable == NULL) + return 0; /* not writing object references */ + +- /* if it has only one reference, it definitely isn't shared */ +- if (Py_REFCNT(v) == 1) ++ /* If it has only one reference, it definitely isn't shared. ++ * But we use TYPE_REF always for interned string, to PYC file stable ++ * as possible. ++ */ ++ if (Py_REFCNT(v) == 1 && ++ !(PyUnicode_CheckExact(v) && PyUnicode_CHECK_INTERNED(v))) { + return 0; ++ } + + entry = _Py_hashtable_get_entry(p->hashtable, v); + if (entry != NULL) { diff --git a/gh120226-fix-sendfile-test-kernel-610.patch b/gh120226-fix-sendfile-test-kernel-610.patch new file mode 100644 index 0000000..79a50dc --- /dev/null +++ b/gh120226-fix-sendfile-test-kernel-610.patch @@ -0,0 +1,35 @@ +From 1b3f6523a5c83323cdc44031b33a1c062e5dc698 Mon Sep 17 00:00:00 2001 +From: Xi Ruoyao +Date: Fri, 7 Jun 2024 23:51:32 +0800 +Subject: [PATCH] gh-120226: Fix + test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10 + (GH-120227) + +The worst case is that the kernel buffers 17 pages with a page size of 64k. +(cherry picked from commit a7584245661102a5768c643fbd7db8395fd3c90e) + +Co-authored-by: Xi Ruoyao +--- + Lib/test/test_asyncio/test_sendfile.py | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +--- a/Lib/test/test_asyncio/test_sendfile.py ++++ b/Lib/test/test_asyncio/test_sendfile.py +@@ -87,13 +87,10 @@ class MyProto(asyncio.Protocol): + + class SendfileBase: + +- # 256 KiB plus small unaligned to buffer chunk +- # Newer versions of Windows seems to have increased its internal +- # buffer and tries to send as much of the data as it can as it +- # has some form of buffering for this which is less than 256KiB +- # on newer server versions and Windows 11. +- # So DATA should be larger than 256 KiB to make this test reliable. +- DATA = b"x" * (1024 * 256 + 1) ++ # Linux >= 6.10 seems buffering up to 17 pages of data. ++ # So DATA should be large enough to make this test reliable even with a ++ # 64 KiB page configuration. ++ DATA = b"x" * (1024 * 17 * 64 + 1) + # Reduce socket buffer size to test on relative small data sets. + BUF_SIZE = 4 * 1024 # 4 KiB + diff --git a/gh139257-Support-docutils-0.22.patch b/gh139257-Support-docutils-0.22.patch new file mode 100644 index 0000000..cd9fc15 --- /dev/null +++ b/gh139257-Support-docutils-0.22.patch @@ -0,0 +1,36 @@ +From 19b61747df3d62c822285c488753d6fbdf91e3ac Mon Sep 17 00:00:00 2001 +From: Daniel Garcia Moreno +Date: Tue, 23 Sep 2025 10:20:16 +0200 +Subject: [PATCH 1/2] gh-139257: Support docutils >= 0.22 + +--- + Doc/tools/extensions/pyspecific.py | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +Index: Python-3.9.23/Doc/tools/extensions/pyspecific.py +=================================================================== +--- Python-3.9.23.orig/Doc/tools/extensions/pyspecific.py 2025-09-30 18:26:26.712969300 +0200 ++++ Python-3.9.23/Doc/tools/extensions/pyspecific.py 2025-09-30 18:26:26.763263890 +0200 +@@ -55,11 +55,21 @@ + SOURCE_URI = 'https://github.com/python/cpython/tree/3.9/%s' + + # monkey-patch reST parser to disable alphabetic and roman enumerated lists ++def _disable_alphabetic_and_roman(text): ++ try: ++ # docutils >= 0.22 ++ from docutils.parsers.rst.states import InvalidRomanNumeralError ++ raise InvalidRomanNumeralError(text) ++ except ImportError: ++ # docutils < 0.22 ++ return None ++ ++ + from docutils.parsers.rst.states import Body + Body.enum.converters['loweralpha'] = \ + Body.enum.converters['upperalpha'] = \ + Body.enum.converters['lowerroman'] = \ +- Body.enum.converters['upperroman'] = lambda x: None ++ Body.enum.converters['upperroman'] = _disable_alphabetic_and_roman + + + # Support for marking up and linking to bugs.python.org issues diff --git a/idle3.appdata.xml b/idle3.appdata.xml new file mode 100644 index 0000000..554b7c4 --- /dev/null +++ b/idle3.appdata.xml @@ -0,0 +1,35 @@ + + + + + idle3.desktop + IDLE3 + CC0 + Python-2.0 + Python 3 Integrated Development and Learning Environment + +

+ IDLE is Python’s Integrated Development and Learning Environment. + The GUI is uniform between Windows, Unix, and Mac OS X. + IDLE provides an easy way to start writing, running, and debugging + Python code. +

+

+ IDLE is written in pure Python, and uses the tkinter GUI toolkit. + It provides: +

+
    +
  • a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,
  • +
  • a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,
  • +
  • search within any window, replace within editor windows, and search through multiple files (grep),
  • +
  • a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.
  • +
+
+ https://docs.python.org/3/library/idle.html + + http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png + http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png + http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png + + zbyszek@in.waw.pl +
diff --git a/idle3.desktop b/idle3.desktop new file mode 100644 index 0000000..43f5a4c --- /dev/null +++ b/idle3.desktop @@ -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; diff --git a/import_failed.map b/import_failed.map new file mode 100644 index 0000000..9f01f41 --- /dev/null +++ b/import_failed.map @@ -0,0 +1,7 @@ +python39-curses: curses _curses _curses_panel +python39-dbm: dbm _dbm _gdbm +python39-idle: idlelib +python39-testsuite: test _ctypes_test _testbuffer _testcapi _testinternalcapi _testimportmultiple _testmultiphase xxlimited +python39-tk: tkinter _tkinter +python39-tools: turtledemo +python39: sqlite3 readline _sqlite3 nis diff --git a/import_failed.py b/import_failed.py new file mode 100644 index 0000000..258b5a5 --- /dev/null +++ b/import_failed.py @@ -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.""") diff --git a/macros.python3 b/macros.python3 new file mode 100644 index 0000000..2bd193b --- /dev/null +++ b/macros.python3 @@ -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\ +} + + diff --git a/no-skipif-doctests.patch b/no-skipif-doctests.patch new file mode 100644 index 0000000..2394bf5 --- /dev/null +++ b/no-skipif-doctests.patch @@ -0,0 +1,665 @@ +only in patch2: +unchanged: +--- + Doc/conf.py | 2 - + Doc/library/turtle.rst | 82 ------------------------------------------------- + 2 files changed, 1 insertion(+), 83 deletions(-) + +--- a/Doc/conf.py ++++ b/Doc/conf.py +@@ -46,7 +46,7 @@ today_fmt = '%B %d, %Y' + highlight_language = 'python3' + + # Minimum version of sphinx required +-needs_sphinx = '1.8' ++needs_sphinx = '1.7.6' + + # Ignore any .rst files in the venv/ directory. + exclude_patterns = ['venv/*', 'README.rst'] +--- a/Doc/library/turtle.rst ++++ b/Doc/library/turtle.rst +@@ -250,7 +250,6 @@ Turtle motion + turtle is headed. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,0.00) +@@ -277,7 +276,6 @@ Turtle motion + >>> turtle.goto(0, 0) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,0.00) +@@ -296,13 +294,11 @@ Turtle motion + orientation depends on the turtle mode, see :func:`mode`. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.setheading(22) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.heading() + 22.0 +@@ -321,13 +317,11 @@ Turtle motion + orientation depends on the turtle mode, see :func:`mode`. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.setheading(22) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.heading() + 22.0 +@@ -350,13 +344,11 @@ Turtle motion + not change the turtle's orientation. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.goto(0, 0) + + .. doctest:: +- :skipif: _tkinter is None + + >>> tp = turtle.pos() + >>> tp +@@ -380,13 +372,11 @@ Turtle motion + unchanged. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.goto(0, 240) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,240.00) +@@ -402,13 +392,11 @@ Turtle motion + Set the turtle's second coordinate to *y*, leave first coordinate unchanged. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.goto(0, 40) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,40.00) +@@ -435,7 +423,6 @@ Turtle motion + =================== ==================== + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.setheading(90) + >>> turtle.heading() +@@ -448,14 +435,12 @@ Turtle motion + its start-orientation (which depends on the mode, see :func:`mode`). + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.setheading(90) + >>> turtle.goto(0, -10) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.heading() + 90.0 +@@ -487,7 +472,6 @@ Turtle motion + calculated automatically. May be used to draw regular polygons. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.position() +@@ -516,7 +500,6 @@ Turtle motion + + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.dot() +@@ -534,7 +517,6 @@ Turtle motion + it by calling ``clearstamp(stamp_id)``. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.color("blue") + >>> turtle.stamp() +@@ -550,7 +532,6 @@ Turtle motion + Delete stamp with given *stampid*. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (150.00,-0.00) +@@ -595,7 +576,6 @@ Turtle motion + undo actions is determined by the size of the undobuffer. + + .. doctest:: +- :skipif: _tkinter is None + + >>> for i in range(4): + ... turtle.fd(50); turtle.lt(80) +@@ -628,7 +608,6 @@ Turtle motion + turtle turn instantly. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.speed() + 3 +@@ -649,7 +628,6 @@ Tell Turtle's state + Return the turtle's current location (x,y) (as a :class:`Vec2D` vector). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.pos() + (440.00,-0.00) +@@ -665,7 +643,6 @@ Tell Turtle's state + orientation which depends on the mode - "standard"/"world" or "logo". + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.goto(10, 10) + >>> turtle.towards(0,0) +@@ -677,7 +654,6 @@ Tell Turtle's state + Return the turtle's x coordinate. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(50) +@@ -693,7 +669,6 @@ Tell Turtle's state + Return the turtle's y coordinate. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(60) +@@ -710,7 +685,6 @@ Tell Turtle's state + :func:`mode`). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(67) +@@ -727,7 +701,6 @@ Tell Turtle's state + other turtle, in turtle step units. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.distance(30,40) +@@ -751,7 +724,6 @@ Settings for measurement + Default value is 360 degrees. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(90) +@@ -774,7 +746,6 @@ Settings for measurement + ``degrees(2*math.pi)``. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(90) +@@ -785,7 +756,6 @@ Settings for measurement + 1.5707963267948966 + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.degrees(360) +@@ -821,7 +791,6 @@ Drawing state + thickness. If no argument is given, the current pensize is returned. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.pensize() + 1 +@@ -853,7 +822,6 @@ Drawing state + attributes in one statement. + + .. doctest:: +- :skipif: _tkinter is None + :options: +NORMALIZE_WHITESPACE + + >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10) +@@ -876,7 +844,6 @@ Drawing state + Return ``True`` if pen is down, ``False`` if it's up. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.penup() + >>> turtle.isdown() +@@ -917,7 +884,6 @@ Color control + newly set pencolor. + + .. doctest:: +- :skipif: _tkinter is None + + >>> colormode() + 1.0 +@@ -966,7 +932,6 @@ Color control + with the newly set fillcolor. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.fillcolor("violet") + >>> turtle.fillcolor() +@@ -1005,7 +970,6 @@ Color control + with the newly set colors. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.color("red", "green") + >>> turtle.color() +@@ -1022,7 +986,6 @@ Filling + ~~~~~~~ + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.home() +@@ -1032,7 +995,6 @@ Filling + Return fillstate (``True`` if filling, ``False`` else). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.begin_fill() + >>> if turtle.filling(): +@@ -1057,7 +1019,6 @@ Filling + above may be either all yellow or have some white regions. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.color("black", "red") + >>> turtle.begin_fill() +@@ -1074,7 +1035,6 @@ More drawing control + variables to the default values. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.goto(0,-22) + >>> turtle.left(100) +@@ -1125,7 +1085,6 @@ Visibility + drawing observably. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.hideturtle() + +@@ -1136,7 +1095,6 @@ Visibility + Make the turtle visible. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.showturtle() + +@@ -1167,7 +1125,6 @@ Appearance + deal with shapes see Screen method :func:`register_shape`. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shape() + 'classic' +@@ -1193,7 +1150,6 @@ Appearance + ``resizemode("user")`` is called by :func:`shapesize` when used with arguments. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.resizemode() + 'noresize' +@@ -1217,7 +1173,6 @@ Appearance + of the shapes's outline. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shapesize() + (1.0, 1.0, 1) +@@ -1242,7 +1197,6 @@ Appearance + heading of the turtle are sheared. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) +@@ -1259,7 +1213,6 @@ Appearance + change the turtle's heading (direction of movement). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.reset() + >>> turtle.shape("circle") +@@ -1279,7 +1232,6 @@ Appearance + (direction of movement). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.reset() + >>> turtle.shape("circle") +@@ -1305,7 +1257,6 @@ Appearance + turtle (its direction of movement). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.reset() + >>> turtle.shape("circle") +@@ -1334,7 +1285,6 @@ Appearance + given matrix. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle = Turtle() + >>> turtle.shape("square") +@@ -1350,7 +1300,6 @@ Appearance + can be used to define a new shape or components of a compound shape. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shape("square") + >>> turtle.shapetransform(4, -1, 0, 2) +@@ -1375,7 +1324,6 @@ Using events + procedural way: + + .. doctest:: +- :skipif: _tkinter is None + + >>> def turn(x, y): + ... left(180) +@@ -1396,7 +1344,6 @@ Using events + ``None``, existing bindings are removed. + + .. doctest:: +- :skipif: _tkinter is None + + >>> class MyTurtle(Turtle): + ... def glow(self,x,y): +@@ -1424,7 +1371,6 @@ Using events + mouse-click event on that turtle. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.ondrag(turtle.goto) + +@@ -1452,7 +1398,6 @@ Special Turtle methods + Return the last recorded polygon. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.begin_poly() +@@ -1472,7 +1417,6 @@ Special Turtle methods + turtle properties. + + .. doctest:: +- :skipif: _tkinter is None + + >>> mick = Turtle() + >>> joe = mick.clone() +@@ -1485,7 +1429,6 @@ Special Turtle methods + return the "anonymous turtle": + + .. doctest:: +- :skipif: _tkinter is None + + >>> pet = getturtle() + >>> pet.fd(50) +@@ -1499,7 +1442,6 @@ Special Turtle methods + TurtleScreen methods can then be called for that object. + + .. doctest:: +- :skipif: _tkinter is None + + >>> ts = turtle.getscreen() + >>> ts +@@ -1517,7 +1459,6 @@ Special Turtle methods + ``None``, the undobuffer is disabled. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.setundobuffer(42) + +@@ -1527,7 +1468,6 @@ Special Turtle methods + Return number of entries in the undobuffer. + + .. doctest:: +- :skipif: _tkinter is None + + >>> while undobufferentries(): + ... undo() +@@ -1550,7 +1490,6 @@ below: + For example: + + .. doctest:: +- :skipif: _tkinter is None + + >>> s = Shape("compound") + >>> poly1 = ((0,0),(10,-5),(0,10),(-10,-5)) +@@ -1561,7 +1500,6 @@ below: + 3. Now add the Shape to the Screen's shapelist and use it: + + .. doctest:: +- :skipif: _tkinter is None + + >>> register_shape("myshape", s) + >>> shape("myshape") +@@ -1581,7 +1519,6 @@ Most of the examples in this section ref + ``screen``. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> screen = Screen() +@@ -1598,7 +1535,6 @@ Window control + Set or return background color of the TurtleScreen. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.bgcolor("orange") + >>> screen.bgcolor() +@@ -1690,7 +1626,6 @@ Window control + distorted. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.reset() + >>> screen.setworldcoordinates(-50,-7.5,50,7.5) +@@ -1701,7 +1636,6 @@ Window control + ... left(45); fd(2) # a regular octagon + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> screen.reset() +@@ -1723,7 +1657,6 @@ Animation control + Optional argument: + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.delay() + 10 +@@ -1745,7 +1678,6 @@ Animation control + :func:`delay`). + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.tracer(8, 25) + >>> dist = 2 +@@ -1782,7 +1714,6 @@ Using screen events + must have the focus. (See method :func:`listen`.) + + .. doctest:: +- :skipif: _tkinter is None + + >>> def f(): + ... fd(50) +@@ -1803,7 +1734,6 @@ Using screen events + must have focus. (See method :func:`listen`.) + + .. doctest:: +- :skipif: _tkinter is None + + >>> def f(): + ... fd(50) +@@ -1828,7 +1758,6 @@ Using screen events + named ``turtle``: + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.onclick(turtle.goto) # Subsequently clicking into the TurtleScreen will + >>> # make the turtle move to the clicked point. +@@ -1848,7 +1777,6 @@ Using screen events + Install a timer that calls *fun* after *t* milliseconds. + + .. doctest:: +- :skipif: _tkinter is None + + >>> running = True + >>> def f(): +@@ -1930,7 +1858,6 @@ Settings and special methods + ============ ========================= =================== + + .. doctest:: +- :skipif: _tkinter is None + + >>> mode("logo") # resets turtle heading to north + >>> mode() +@@ -1945,7 +1872,6 @@ Settings and special methods + values of color triples have to be in the range 0..\ *cmode*. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.colormode(1) + >>> turtle.pencolor(240, 160, 80) +@@ -1966,7 +1892,6 @@ Settings and special methods + do with a Tkinter Canvas. + + .. doctest:: +- :skipif: _tkinter is None + + >>> cv = screen.getcanvas() + >>> cv +@@ -1978,7 +1903,6 @@ Settings and special methods + Return a list of names of all currently available turtle shapes. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.getshapes() + ['arrow', 'blank', 'circle', ..., 'turtle'] +@@ -2002,7 +1926,6 @@ Settings and special methods + coordinates: Install the corresponding polygon shape. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.register_shape("triangle", ((5,-3), (0,5), (-5,-3))) + +@@ -2018,7 +1941,6 @@ Settings and special methods + Return the list of turtles on the screen. + + .. doctest:: +- :skipif: _tkinter is None + + >>> for turtle in screen.turtles(): + ... turtle.color("red") +@@ -2080,7 +2002,6 @@ Methods specific to Screen, not inherite + center window vertically + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.setup (width=200, height=200, startx=0, starty=0) + >>> # sets window to 200x200 pixels, in upper left of screen +@@ -2096,7 +2017,6 @@ Methods specific to Screen, not inherite + Set title of turtle window to *titlestring*. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.title("Welcome to the turtle zoo!") + +@@ -2167,7 +2087,6 @@ Public classes + Example: + + .. doctest:: +- :skipif: _tkinter is None + + >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) + >>> s = Shape("compound") +@@ -2514,7 +2433,6 @@ Changes since Python 3.0 + + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> for turtle in turtles(): diff --git a/pre_checkin.sh b/pre_checkin.sh new file mode 100644 index 0000000..a2cf992 --- /dev/null +++ b/pre_checkin.sh @@ -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 + diff --git a/python-3.3.0b1-fix_date_time_compiler.patch b/python-3.3.0b1-fix_date_time_compiler.patch new file mode 100644 index 0000000..29586aa --- /dev/null +++ b/python-3.3.0b1-fix_date_time_compiler.patch @@ -0,0 +1,25 @@ +--- + Makefile.pre.in | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -781,11 +781,18 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \ + $(DTRACE_OBJS) \ + $(srcdir)/Modules/getbuildinfo.c + $(CC) -c $(PY_CORE_CFLAGS) \ ++ -DDATE="\"`date -u -r Makefile.pre.in +"%b %d %Y"`\"" \ ++ -DTIME="\"`date -u -r Makefile.pre.in +"%T"`\"" \ + -DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \ + -DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \ + -DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \ + -o $@ $(srcdir)/Modules/getbuildinfo.c + ++Python/getcompiler.o: $(srcdir)/Python/getcompiler.c Makefile ++ $(CC) -c $(PY_CORE_CFLAGS) \ ++ -DCOMPILER='"[GCC]"' \ ++ -o $@ $(srcdir)/Python/getcompiler.c ++ + Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile + $(CC) -c $(PY_CORE_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \ + -DPREFIX='"$(prefix)"' \ diff --git a/python-3.3.0b1-localpath.patch b/python-3.3.0b1-localpath.patch new file mode 100644 index 0000000..f527f1f --- /dev/null +++ b/python-3.3.0b1-localpath.patch @@ -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 diff --git a/python.keyring b/python.keyring new file mode 100644 index 0000000..5326686 --- /dev/null +++ b/python.keyring @@ -0,0 +1,1008 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +Xw8b +=4+Ub +-----END PGP PUBLIC KEY BLOCK----- diff --git a/python3-imp-returntype.patch b/python3-imp-returntype.patch new file mode 100644 index 0000000..78f4d53 --- /dev/null +++ b/python3-imp-returntype.patch @@ -0,0 +1,53 @@ +From 7bd6f0e5500f778e940374237b94651f60ae1990 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Fri, 6 Jul 2018 21:00:45 -0700 +Subject: [PATCH] closes bpo-34056: Always return bytes from + _HackedGetData.get_data(). (GH-8130) + +* Always return bytes from _HackedGetData.get_data(). + +Ensure the imp.load_source shim always returns bytes by reopening the file in +binary mode if needed. Hash-based pycs have to receive the source code in bytes. + +It's tempting to change imp.get_suffixes() to always return 'rb' as a mode, but +that breaks some stdlib tests and likely 3rdparty code, too. +(cherry picked from commit b0274f2cddd36b49fe5080efbe160277ef546471) + +Co-authored-by: Benjamin Peterson +--- + Lib/imp.py | 13 ++++++------- + Lib/test/test_imp.py | 15 +++++++++++++++ + .../2018-07-05-22-45-46.bpo-34056.86isrU.rst | 3 +++ + 3 files changed, 24 insertions(+), 7 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2018-07-05-22-45-46.bpo-34056.86isrU.rst + +--- a/Lib/test/test_imp.py ++++ b/Lib/test/test_imp.py +@@ -376,6 +376,20 @@ class ImportTests(unittest.TestCase): + mod = imp.load_module('mymod', file, path, description) + self.assertEqual(mod.x, 42) + ++ def test_find_and_load_checked_pyc(self): ++ # issue 34056 ++ with support.temp_cwd(): ++ with open('mymod.py', 'wb') as fp: ++ fp.write(b'x = 42\n') ++ py_compile.compile( ++ 'mymod.py', ++ doraise=True, ++ invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, ++ ) ++ file, path, description = imp.find_module('mymod', path=['.']) ++ mod = imp.load_module('mymod', file, path, description) ++ self.assertEqual(mod.x, 42) ++ + + class ReloadTests(unittest.TestCase): + +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2018-07-05-22-45-46.bpo-34056.86isrU.rst +@@ -0,0 +1,3 @@ ++Ensure the loader shim created by ``imp.load_module`` always returns bytes ++from its ``get_data()`` function. This fixes using ``imp.load_module`` with ++:pep:`552` hash-based pycs. diff --git a/python39.changes b/python39.changes new file mode 100644 index 0000000..58ca0d3 --- /dev/null +++ b/python39.changes @@ -0,0 +1,4155 @@ +------------------------------------------------------------------- +Wed Oct 15 06:28:09 UTC 2025 - Daniel Garcia + +- Update to 3.9.24: + - Security + - gh-139700: Check consistency of the zip64 end of central + directory record. Support records with “zip64 extensible data” + if there are no bytes prepended to the ZIP file. + - gh-139400: xml.parsers.expat: Make sure that parent Expat + parsers are only garbage-collected once they are no longer + referenced by subparsers created by + ExternalEntityParserCreate(). Patch by Sebastian Pipping. + - gh-121227: Raise an SSL.SSLError if an empty protocols argument + is passed to ssl.SSLContext.set_npn_protocols() to fix + CVE-2024-5642. + - gh-135661: Fix parsing start and end tags in + html.parser.HTMLParser according to the HTML5 standard. + * Whitespaces no longer accepted between does not end the script section. + * Vertical tabulation (\v) and non-ASCII whitespaces no longer + recognized as whitespaces. The only whitespaces are \t\n\r\f + and space. + * Null character (U+0000) no longer ends the tag name. + * Attributes and slashes after the tag name in end tags are now + ignored, instead of terminating after the first > in quoted + attribute value. E.g. . + * Multiple slashes and whitespaces between the last attribute + and closing > are now ignored in both start and end tags. E.g. +
. + * Multiple = between attribute name and value are no longer + collapsed. E.g. produces attribute “foo” with + value “=bar”. + - gh-135661: Fix CDATA section parsing in html.parser.HTMLParser + according to the HTML5 standard: ] ]> and ]] > no longer end the + CDATA section. Add private method _set_support_cdata() which can + be used to specify how to parse <[CDATA[ — as a CDATA section in + foreign content (SVG or MathML) or as a bogus comment in the + HTML namespace. + - gh-102555: Fix comment parsing in html.parser.HTMLParser + according to the HTML5 standard. --!> now ends the comment. -- > + no longer ends the comment. Support abnormally ended empty + comments <--> and <--->. + - gh-135462: Fix quadratic complexity in processing specially + crafted input in html.parser.HTMLParser. End-of-file errors are + now handled according to the HTML5 specs – comments and + declarations are automatically closed, tags are ignored. + - gh-118350: Fix support of escapable raw text mode (elements + “textarea” and “title”) in html.parser.HTMLParser. + - gh-86155: html.parser.HTMLParser.close() no longer loses data + when the