diff --git a/00251-change-user-install-location.patch b/00251-change-user-install-location.patch new file mode 100644 index 0000000000000000000000000000000000000000..dc29bca68b6e25f023248808f5616d6e43a45633 --- /dev/null +++ b/00251-change-user-install-location.patch @@ -0,0 +1,173 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= +Date: Mon, 15 Feb 2021 12:19:27 +0100 +Subject: [PATCH] 00251: Change user install location +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Set values of base and platbase in sysconfig from /usr +to /usr/local when RPM build is not detected +to make pip and similar tools install into separate location. + +Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe +Downstream only. + +We've tried to rework in Fedora 36/Python 3.10 to follow https://bugs.python.org/issue43976 +but we have identified serious problems with that approach, +see https://bugzilla.redhat.com/2026979 or https://bugzilla.redhat.com/2097183 + +pypa/distutils integration: https://github.com/pypa/distutils/pull/70 + +Co-authored-by: Petr Viktorin +Co-authored-by: Miro Hrončok +Co-authored-by: Michal Cyprian +Co-authored-by: Lumír Balhar +--- + Lib/site.py | 9 ++++++- + Lib/sysconfig.py | 49 +++++++++++++++++++++++++++++++++++++- + Lib/test/test_sysconfig.py | 17 +++++++++++-- + 3 files changed, 71 insertions(+), 4 deletions(-) + +diff --git a/Lib/site.py b/Lib/site.py +index 924b2460d9..51b5baca93 100644 +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -387,8 +387,15 @@ def getsitepackages(prefixes=None): + return sitepackages + + def addsitepackages(known_paths, prefixes=None): +- """Add site-packages to sys.path""" ++ """Add site-packages to sys.path ++ ++ '/usr/local' is included in PREFIXES if RPM build is not detected ++ to make packages installed into this location visible. ++ ++ """ + _trace("Processing global site-packages") ++ if ENABLE_USER_SITE and 'RPM_BUILD_ROOT' not in os.environ: ++ PREFIXES.insert(0, "/usr/local") + for sitedir in getsitepackages(prefixes): + if os.path.isdir(sitedir): + addsitedir(sitedir, known_paths) +diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py +index 122d441bd1..2d354a11da 100644 +--- a/Lib/sysconfig.py ++++ b/Lib/sysconfig.py +@@ -104,6 +104,11 @@ + else: + _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv'] + ++# For a brief period of time in the Fedora 36 life cycle, ++# this installation scheme existed and was documented in the release notes. ++# For backwards compatibility, we keep it here (at least on 3.10 and 3.11). ++_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix'] ++ + + # NOTE: site.py has copy of this function. + # Sync it when modify this function. +@@ -163,6 +168,19 @@ def joinuser(*args): + }, + } + ++# This is used by distutils.command.install in the stdlib ++# as well as pypa/distutils (e.g. bundled in setuptools). ++# The self.prefix value is set to sys.prefix + /local/ ++# if neither RPM build nor virtual environment is ++# detected to make distutils install packages ++# into the separate location. ++# https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++if (not (hasattr(sys, 'real_prefix') or ++ sys.prefix != sys.base_prefix) and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ _prefix_addition = '/local' ++ ++ + _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', + 'scripts', 'data') + +@@ -263,11 +281,40 @@ def _extend_dict(target_dict, other_dict): + target_dict[key] = value + + ++_CONFIG_VARS_LOCAL = None ++ ++ ++def _config_vars_local(): ++ # This function returns the config vars with prefixes amended to /usr/local ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ global _CONFIG_VARS_LOCAL ++ if _CONFIG_VARS_LOCAL is None: ++ _CONFIG_VARS_LOCAL = dict(get_config_vars()) ++ _CONFIG_VARS_LOCAL['base'] = '/usr/local' ++ _CONFIG_VARS_LOCAL['platbase'] = '/usr/local' ++ return _CONFIG_VARS_LOCAL ++ ++ + def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} +- _extend_dict(vars, get_config_vars()) ++ ++ # when we are not in a virtual environment or an RPM build ++ # we change '/usr' to '/usr/local' ++ # to avoid surprises, we explicitly check for the /usr/ prefix ++ # Python virtual environments have different prefixes ++ # we only do this for posix_prefix, not to mangle the venv scheme ++ # posix_prefix is used by sudo pip install ++ # we only change the defaults here, so explicit --prefix will take precedence ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ if (scheme == 'posix_prefix' and ++ _PREFIX == '/usr' and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ _extend_dict(vars, _config_vars_local()) ++ else: ++ _extend_dict(vars, get_config_vars()) ++ + if os.name == 'nt': + # On Windows we want to substitute 'lib' for schemes rather + # than the native value (without modifying vars, in case it +diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py +index b6dbf3d52c..4f06a7673c 100644 +--- a/Lib/test/test_sysconfig.py ++++ b/Lib/test/test_sysconfig.py +@@ -110,8 +110,19 @@ def test_get_path(self): + for scheme in _INSTALL_SCHEMES: + for name in _INSTALL_SCHEMES[scheme]: + expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars) ++ tested = get_path(name, scheme) ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ if tested.startswith('/usr/local'): ++ # /usr/local should only be used in posix_prefix ++ self.assertEqual(scheme, 'posix_prefix') ++ # Fedora CI runs tests for venv and virtualenv that check for other prefixes ++ self.assertEqual(sys.prefix, '/usr') ++ # When building the RPM of Python, %check runs this with RPM_BUILD_ROOT set ++ # Fedora CI runs this with RPM_BUILD_ROOT unset ++ self.assertNotIn('RPM_BUILD_ROOT', os.environ) ++ tested = tested.replace('/usr/local', '/usr') + self.assertEqual( +- os.path.normpath(get_path(name, scheme)), ++ os.path.normpath(tested), + os.path.normpath(expected), + ) + +@@ -335,7 +346,7 @@ def test_get_config_h_filename(self): + self.assertTrue(os.path.isfile(config_h), config_h) + + def test_get_scheme_names(self): +- wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv'] ++ wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv', 'rpm_prefix'] + if HAS_USER_BASE: + wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) + self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) +@@ -347,6 +358,8 @@ def test_symlink(self): # Issue 7880 + cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" + self.assertEqual(py.call_real(*cmd), py.call_link(*cmd)) + ++ @unittest.skipIf('RPM_BUILD_ROOT' not in os.environ, ++ "Test doesn't expect Fedora's paths") + def test_user_similar(self): + # Issue #8759: make sure the posix scheme for the users + # is similar to the global posix_prefix one diff --git a/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch b/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch new file mode 100644 index 0000000000000000000000000000000000000000..7146a82ec0bbcc51eb0268e02e67d221f7fde9f1 --- /dev/null +++ b/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch @@ -0,0 +1,103 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= +Date: Tue, 25 Oct 2022 12:02:33 +0200 +Subject: [PATCH] 00371: Revert "bpo-1596321: Fix threading._shutdown() for the + main thread (GH-28549) (GH-28589)" + +This reverts commit 38c67738c64304928c68d5c2bd78bbb01d979b94. It +introduced regression causing FreeIPA's tests to fail. + +For more info see: +https://bodhi.fedoraproject.org/updates/FEDORA-2021-e152ce5f31 +https://github.com/GrahamDumpleton/mod_wsgi/issues/730 +--- + Lib/test/test_threading.py | 33 --------------------------------- + Lib/threading.py | 25 ++++++++----------------- + 2 files changed, 8 insertions(+), 50 deletions(-) + +diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py +index 00d9e591c7..3314319fec 100644 +--- a/Lib/test/test_threading.py ++++ b/Lib/test/test_threading.py +@@ -1089,39 +1089,6 @@ def noop(): pass + threading.Thread(target=noop).start() + # Thread.join() is not called + +- def test_import_from_another_thread(self): +- # bpo-1596321: If the threading module is first import from a thread +- # different than the main thread, threading._shutdown() must handle +- # this case without logging an error at Python exit. +- code = textwrap.dedent(''' +- import _thread +- import sys +- +- event = _thread.allocate_lock() +- event.acquire() +- +- def import_threading(): +- import threading +- event.release() +- +- if 'threading' in sys.modules: +- raise Exception('threading is already imported') +- +- _thread.start_new_thread(import_threading, ()) +- +- # wait until the threading module is imported +- event.acquire() +- event.release() +- +- if 'threading' not in sys.modules: +- raise Exception('threading is not imported') +- +- # don't wait until the thread completes +- ''') +- rc, out, err = assert_python_ok("-c", code) +- self.assertEqual(out, b'') +- self.assertEqual(err, b'') +- + def test_start_new_thread_at_exit(self): + code = """if 1: + import atexit +diff --git a/Lib/threading.py b/Lib/threading.py +index 98cb43c697..ee647f8549 100644 +--- a/Lib/threading.py ++++ b/Lib/threading.py +@@ -1585,29 +1585,20 @@ def _shutdown(): + + global _SHUTTING_DOWN + _SHUTTING_DOWN = True ++ # Main thread ++ tlock = _main_thread._tstate_lock ++ # The main thread isn't finished yet, so its thread state lock can't have ++ # been released. ++ assert tlock is not None ++ assert tlock.locked() ++ tlock.release() ++ _main_thread._stop() + + # Call registered threading atexit functions before threads are joined. + # Order is reversed, similar to atexit. + for atexit_call in reversed(_threading_atexits): + atexit_call() + +- # Main thread +- if _main_thread.ident == get_ident(): +- tlock = _main_thread._tstate_lock +- # The main thread isn't finished yet, so its thread state lock can't +- # have been released. +- assert tlock is not None +- assert tlock.locked() +- tlock.release() +- _main_thread._stop() +- else: +- # bpo-1596321: _shutdown() must be called in the main thread. +- # If the threading module was not imported by the main thread, +- # _main_thread is the thread which imported the threading module. +- # In this case, ignore _main_thread, similar behavior than for threads +- # spawned by C libraries or using _thread.start_new_thread(). +- pass +- + # Join all non-deamon threads + while True: + with _shutdown_locks_lock: diff --git a/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch b/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch new file mode 100644 index 0000000000000000000000000000000000000000..352fef8eb849b11ab2253d8a6819a2f27f37567e --- /dev/null +++ b/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch @@ -0,0 +1,483 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Fri, 15 Dec 2023 16:10:40 +0100 +Subject: [PATCH] 00415: [CVE-2023-27043] gh-102988: Reject malformed addresses + in email.parseaddr() (#111116) + +Detect email address parsing errors and return empty tuple to +indicate the parsing error (old API). Add an optional 'strict' +parameter to getaddresses() and parseaddr() functions. Patch by +Thomas Dwyer. + +Co-Authored-By: Thomas Dwyer +--- + Doc/library/email.utils.rst | 19 +- + Lib/email/utils.py | 151 +++++++++++++- + Lib/test/test_email/test_email.py | 187 +++++++++++++++++- + ...-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 + + 4 files changed, 344 insertions(+), 21 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst + +diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst +index 345b64001c..d693a9bc39 100644 +--- a/Doc/library/email.utils.rst ++++ b/Doc/library/email.utils.rst +@@ -58,13 +58,18 @@ of the new API. + begins with angle brackets, they are stripped off. + + +-.. function:: parseaddr(address) ++.. function:: parseaddr(address, *, strict=True) + + Parse address -- which should be the value of some address-containing field such + as :mailheader:`To` or :mailheader:`Cc` -- into its constituent *realname* and + *email address* parts. Returns a tuple of that information, unless the parse + fails, in which case a 2-tuple of ``('', '')`` is returned. + ++ If *strict* is true, use a strict parser which rejects malformed inputs. ++ ++ .. versionchanged:: 3.13 ++ Add *strict* optional parameter and reject malformed inputs by default. ++ + + .. function:: formataddr(pair, charset='utf-8') + +@@ -82,12 +87,15 @@ of the new API. + Added the *charset* option. + + +-.. function:: getaddresses(fieldvalues) ++.. function:: getaddresses(fieldvalues, *, strict=True) + + This method returns a list of 2-tuples of the form returned by ``parseaddr()``. + *fieldvalues* is a sequence of header field values as might be returned by +- :meth:`Message.get_all `. Here's a simple +- example that gets all the recipients of a message:: ++ :meth:`Message.get_all `. ++ ++ If *strict* is true, use a strict parser which rejects malformed inputs. ++ ++ Here's a simple example that gets all the recipients of a message:: + + from email.utils import getaddresses + +@@ -97,6 +105,9 @@ of the new API. + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + ++ .. versionchanged:: 3.13 ++ Add *strict* optional parameter and reject malformed inputs by default. ++ + + .. function:: parsedate(date) + +diff --git a/Lib/email/utils.py b/Lib/email/utils.py +index aa949aa933..af2fb14754 100644 +--- a/Lib/email/utils.py ++++ b/Lib/email/utils.py +@@ -48,6 +48,7 @@ + specialsre = re.compile(r'[][\\()<>@,:;".]') + escapesre = re.compile(r'[\\"]') + ++ + def _has_surrogates(s): + """Return True if s may contain surrogate-escaped binary data.""" + # This check is based on the fact that unless there are surrogates, utf8 +@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'): + return address + + ++def _iter_escaped_chars(addr): ++ pos = 0 ++ escape = False ++ for pos, ch in enumerate(addr): ++ if escape: ++ yield (pos, '\\' + ch) ++ escape = False ++ elif ch == '\\': ++ escape = True ++ else: ++ yield (pos, ch) ++ if escape: ++ yield (pos, '\\') + +-def getaddresses(fieldvalues): +- """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" +- all = COMMASPACE.join(str(v) for v in fieldvalues) +- a = _AddressList(all) +- return a.addresslist ++ ++def _strip_quoted_realnames(addr): ++ """Strip real names between quotes.""" ++ if '"' not in addr: ++ # Fast path ++ return addr ++ ++ start = 0 ++ open_pos = None ++ result = [] ++ for pos, ch in _iter_escaped_chars(addr): ++ if ch == '"': ++ if open_pos is None: ++ open_pos = pos ++ else: ++ if start != open_pos: ++ result.append(addr[start:open_pos]) ++ start = pos + 1 ++ open_pos = None ++ ++ if start < len(addr): ++ result.append(addr[start:]) ++ ++ return ''.join(result) ++ ++ ++supports_strict_parsing = True ++ ++def getaddresses(fieldvalues, *, strict=True): ++ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. ++ ++ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in ++ its place. ++ ++ If strict is true, use a strict parser which rejects malformed inputs. ++ """ ++ ++ # If strict is true, if the resulting list of parsed addresses is greater ++ # than the number of fieldvalues in the input list, a parsing error has ++ # occurred and consequently a list containing a single empty 2-tuple [('', ++ # '')] is returned in its place. This is done to avoid invalid output. ++ # ++ # Malformed input: getaddresses(['alice@example.com ']) ++ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')] ++ # Safe output: [('', '')] ++ ++ if not strict: ++ all = COMMASPACE.join(str(v) for v in fieldvalues) ++ a = _AddressList(all) ++ return a.addresslist ++ ++ fieldvalues = [str(v) for v in fieldvalues] ++ fieldvalues = _pre_parse_validation(fieldvalues) ++ addr = COMMASPACE.join(fieldvalues) ++ a = _AddressList(addr) ++ result = _post_parse_validation(a.addresslist) ++ ++ # Treat output as invalid if the number of addresses is not equal to the ++ # expected number of addresses. ++ n = 0 ++ for v in fieldvalues: ++ # When a comma is used in the Real Name part it is not a deliminator. ++ # So strip those out before counting the commas. ++ v = _strip_quoted_realnames(v) ++ # Expected number of addresses: 1 + number of commas ++ n += 1 + v.count(',') ++ if len(result) != n: ++ return [('', '')] ++ ++ return result ++ ++ ++def _check_parenthesis(addr): ++ # Ignore parenthesis in quoted real names. ++ addr = _strip_quoted_realnames(addr) ++ ++ opens = 0 ++ for pos, ch in _iter_escaped_chars(addr): ++ if ch == '(': ++ opens += 1 ++ elif ch == ')': ++ opens -= 1 ++ if opens < 0: ++ return False ++ return (opens == 0) ++ ++ ++def _pre_parse_validation(email_header_fields): ++ accepted_values = [] ++ for v in email_header_fields: ++ if not _check_parenthesis(v): ++ v = "('', '')" ++ accepted_values.append(v) ++ ++ return accepted_values ++ ++ ++def _post_parse_validation(parsed_email_header_tuples): ++ accepted_values = [] ++ # The parser would have parsed a correctly formatted domain-literal ++ # The existence of an [ after parsing indicates a parsing failure ++ for v in parsed_email_header_tuples: ++ if '[' in v[1]: ++ v = ('', '') ++ accepted_values.append(v) ++ ++ return accepted_values + + + def _format_timetuple_and_zone(timetuple, zone): +@@ -205,16 +321,33 @@ def parsedate_to_datetime(data): + tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) + + +-def parseaddr(addr): ++def parseaddr(addr, *, strict=True): + """ + Parse addr into its constituent realname and email address parts. + + Return a tuple of realname and email address, unless the parse fails, in + which case return a 2-tuple of ('', ''). ++ ++ If strict is True, use a strict parser which rejects malformed inputs. + """ +- addrs = _AddressList(addr).addresslist +- if not addrs: +- return '', '' ++ if not strict: ++ addrs = _AddressList(addr).addresslist ++ if not addrs: ++ return ('', '') ++ return addrs[0] ++ ++ if isinstance(addr, list): ++ addr = addr[0] ++ ++ if not isinstance(addr, str): ++ return ('', '') ++ ++ addr = _pre_parse_validation([addr])[0] ++ addrs = _post_parse_validation(_AddressList(addr).addresslist) ++ ++ if not addrs or len(addrs) > 1: ++ return ('', '') ++ + return addrs[0] + + +diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py +index 2a237095b9..4672b790d8 100644 +--- a/Lib/test/test_email/test_email.py ++++ b/Lib/test/test_email/test_email.py +@@ -16,6 +16,7 @@ + + import email + import email.policy ++import email.utils + + from email.charset import Charset + from email.generator import Generator, DecodedGenerator, BytesGenerator +@@ -3337,15 +3338,137 @@ def test_getaddresses_comma_in_name(self): + ], + ) + ++ def test_parsing_errors(self): ++ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056""" ++ alice = 'alice@example.org' ++ bob = 'bob@example.com' ++ empty = ('', '') ++ ++ # Test utils.getaddresses() and utils.parseaddr() on malformed email ++ # addresses: default behavior (strict=True) rejects malformed address, ++ # and strict=False which tolerates malformed address. ++ for invalid_separator, expected_non_strict in ( ++ ('(', [(f'<{bob}>', alice)]), ++ (')', [('', alice), empty, ('', bob)]), ++ ('<', [('', alice), empty, ('', bob), empty]), ++ ('>', [('', alice), empty, ('', bob)]), ++ ('[', [('', f'{alice}[<{bob}>]')]), ++ (']', [('', alice), empty, ('', bob)]), ++ ('@', [empty, empty, ('', bob)]), ++ (';', [('', alice), empty, ('', bob)]), ++ (':', [('', alice), ('', bob)]), ++ ('.', [('', alice + '.'), ('', bob)]), ++ ('"', [('', alice), ('', f'<{bob}>')]), ++ ): ++ address = f'{alice}{invalid_separator}<{bob}>' ++ with self.subTest(address=address): ++ self.assertEqual(utils.getaddresses([address]), ++ [empty]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ expected_non_strict) ++ ++ self.assertEqual(utils.parseaddr([address]), ++ empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Comma (',') is treated differently depending on strict parameter. ++ # Comma without quotes. ++ address = f'{alice},<{bob}>' ++ self.assertEqual(utils.getaddresses([address]), ++ [('', alice), ('', bob)]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('', alice), ('', bob)]) ++ self.assertEqual(utils.parseaddr([address]), ++ empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Real name between quotes containing comma. ++ address = '"Alice, alice@example.org" ' ++ expected_strict = ('Alice, alice@example.org', 'bob@example.com') ++ self.assertEqual(utils.getaddresses([address]), [expected_strict]) ++ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) ++ self.assertEqual(utils.parseaddr([address]), expected_strict) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Valid parenthesis in comments. ++ address = 'alice@example.org (Alice)' ++ expected_strict = ('Alice', 'alice@example.org') ++ self.assertEqual(utils.getaddresses([address]), [expected_strict]) ++ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) ++ self.assertEqual(utils.parseaddr([address]), expected_strict) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Invalid parenthesis in comments. ++ address = 'alice@example.org )Alice(' ++ self.assertEqual(utils.getaddresses([address]), [empty]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('', 'alice@example.org'), ('', ''), ('', 'Alice')]) ++ self.assertEqual(utils.parseaddr([address]), empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Two addresses with quotes separated by comma. ++ address = '"Jane Doe" , "John Doe" ' ++ self.assertEqual(utils.getaddresses([address]), ++ [('Jane Doe', 'jane@example.net'), ++ ('John Doe', 'john@example.net')]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('Jane Doe', 'jane@example.net'), ++ ('John Doe', 'john@example.net')]) ++ self.assertEqual(utils.parseaddr([address]), empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Test email.utils.supports_strict_parsing attribute ++ self.assertEqual(email.utils.supports_strict_parsing, True) ++ + def test_getaddresses_nasty(self): +- eq = self.assertEqual +- eq(utils.getaddresses(['foo: ;']), [('', '')]) +- eq(utils.getaddresses( +- ['[]*-- =~$']), +- [('', ''), ('', ''), ('', '*--')]) +- eq(utils.getaddresses( +- ['foo: ;', '"Jason R. Mastaler" ']), +- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) ++ for addresses, expected in ( ++ (['"Sürname, Firstname" '], ++ [('Sürname, Firstname', 'to@example.com')]), ++ ++ (['foo: ;'], ++ [('', '')]), ++ ++ (['foo: ;', '"Jason R. Mastaler" '], ++ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]), ++ ++ ([r'Pete(A nice \) chap) '], ++ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]), ++ ++ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'], ++ [('', '')]), ++ ++ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'], ++ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]), ++ ++ (['John Doe '], ++ [('John Doe (comment)', 'jdoe@machine.example')]), ++ ++ (['"Mary Smith: Personal Account" '], ++ [('Mary Smith: Personal Account', 'smith@home.example')]), ++ ++ (['Undisclosed recipients:;'], ++ [('', '')]), ++ ++ ([r', "Giant; \"Big\" Box" '], ++ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]), ++ ): ++ with self.subTest(addresses=addresses): ++ self.assertEqual(utils.getaddresses(addresses), ++ expected) ++ self.assertEqual(utils.getaddresses(addresses, strict=False), ++ expected) ++ ++ addresses = ['[]*-- =~$'] ++ self.assertEqual(utils.getaddresses(addresses), ++ [('', '')]) ++ self.assertEqual(utils.getaddresses(addresses, strict=False), ++ [('', ''), ('', ''), ('', '*--')]) + + def test_getaddresses_embedded_comment(self): + """Test proper handling of a nested comment""" +@@ -3536,6 +3659,54 @@ def test_mime_classes_policy_argument(self): + m = cls(*constructor, policy=email.policy.default) + self.assertIs(m.policy, email.policy.default) + ++ def test_iter_escaped_chars(self): ++ self.assertEqual(list(utils._iter_escaped_chars(r'a\\b\"c\\"d')), ++ [(0, 'a'), ++ (2, '\\\\'), ++ (3, 'b'), ++ (5, '\\"'), ++ (6, 'c'), ++ (8, '\\\\'), ++ (9, '"'), ++ (10, 'd')]) ++ self.assertEqual(list(utils._iter_escaped_chars('a\\')), ++ [(0, 'a'), (1, '\\')]) ++ ++ def test_strip_quoted_realnames(self): ++ def check(addr, expected): ++ self.assertEqual(utils._strip_quoted_realnames(addr), expected) ++ ++ check('"Jane Doe" , "John Doe" ', ++ ' , ') ++ check(r'"Jane \"Doe\"." ', ++ ' ') ++ ++ # special cases ++ check(r'before"name"after', 'beforeafter') ++ check(r'before"name"', 'before') ++ check(r'b"name"', 'b') # single char ++ check(r'"name"after', 'after') ++ check(r'"name"a', 'a') # single char ++ check(r'"name"', '') ++ ++ # no change ++ for addr in ( ++ 'Jane Doe , John Doe ', ++ 'lone " quote', ++ ): ++ self.assertEqual(utils._strip_quoted_realnames(addr), addr) ++ ++ ++ def test_check_parenthesis(self): ++ addr = 'alice@example.net' ++ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice(')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)')) ++ ++ # Ignore real name between quotes ++ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}')) ++ + + # Test the iterator/generators + class TestIterators(TestEmailBase): +diff --git a/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst +new file mode 100644 +index 0000000000..3d0e9e4078 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst +@@ -0,0 +1,8 @@ ++:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now ++return ``('', '')`` 2-tuples in more situations where invalid email ++addresses are encountered instead of potentially inaccurate values. Add ++optional *strict* parameter to these two functions: use ``strict=False`` to ++get the old behavior, accept malformed inputs. ++``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check ++if the *strict* paramater is available. Patch by Thomas Dwyer and Victor ++Stinner to improve the CVE-2023-27043 fix. diff --git a/00418-don-t-generate-sbom-in-make-regen-all.patch b/00418-don-t-generate-sbom-in-make-regen-all.patch new file mode 100644 index 0000000000000000000000000000000000000000..d41da28bef61cd6411215e7246e0ba0f3d0b1da5 --- /dev/null +++ b/00418-don-t-generate-sbom-in-make-regen-all.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Karolina Surma +Date: Thu, 8 Feb 2024 15:53:26 +0100 +Subject: [PATCH] 00418: Don't generate sbom in make regen-all +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The script and make target, added in Python 3.12.2, assumes a fixed +location of pip wheel and other bundled libraries, resulting in an +error and failed build when not found. +Reported upstream: https://github.com/python/cpython/issues/114240 +and https://github.com/python/cpython/issues/114244 + +Co-Authored-By: Tomáš Hrnčiar +--- + Makefile.pre.in | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index dd5e69f7ab..40097647b5 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -1320,9 +1320,10 @@ regen-limited-abi: all + regen-all: regen-cases regen-opcode regen-opcode-targets regen-typeslots \ + regen-token regen-ast regen-keyword regen-sre regen-frozen \ + regen-pegen-metaparser regen-pegen regen-test-frozenmain \ +- regen-test-levenshtein regen-global-objects regen-sbom ++ regen-test-levenshtein regen-global-objects + @echo +- @echo "Note: make regen-stdlib-module-names and make regen-configure should be run manually" ++ @echo "Note: make regen-stdlib-module-names, make regen-configure and make regen-sbom " ++ @echo "should be run manually" + + ############################################################################ + # Special rules for object files diff --git a/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch b/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch new file mode 100644 index 0000000000000000000000000000000000000000..1c1e560bf8a47660d35fee4a55bac8259dcbbc56 --- /dev/null +++ b/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Serhiy Storchaka +Date: Sun, 11 Feb 2024 12:08:39 +0200 +Subject: [PATCH] 00422: gh-115133: Fix tests for XMLPullParser with Expat + 2.6.0 + +Feeding the parser by too small chunks defers parsing to prevent +CVE-2023-52425. Future versions of Expat may be more reactive. + +(cherry picked from commit 4a08e7b3431cd32a0daf22a33421cd3035343dc4) +--- + Lib/test/test_xml_etree.py | 58 ++++++++++++------- + ...-02-08-14-21-28.gh-issue-115133.ycl4ko.rst | 2 + + 2 files changed, 38 insertions(+), 22 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst + +diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py +index b50898f1d1..6fb888cb21 100644 +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -13,6 +13,7 @@ + import operator + import os + import pickle ++import pyexpat + import sys + import textwrap + import types +@@ -120,6 +121,10 @@ + + """ + ++fails_with_expat_2_6_0 = (unittest.expectedFailure ++ if pyexpat.version_info >= (2, 6, 0) else ++ lambda test: test) ++ + def checkwarnings(*filters, quiet=False): + def decorator(test): + def newtest(*args, **kwargs): +@@ -1400,28 +1405,37 @@ def assert_event_tags(self, parser, expected, max_events=None): + self.assertEqual([(action, elem.tag) for action, elem in events], + expected) + +- def test_simple_xml(self): +- for chunk_size in (None, 1, 5): +- with self.subTest(chunk_size=chunk_size): +- parser = ET.XMLPullParser() +- self.assert_event_tags(parser, []) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, []) +- self._feed(parser, +- "\n text\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'element')]) +- self._feed(parser, "texttail\n", chunk_size) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [ +- ('end', 'element'), +- ('end', 'empty-element'), +- ]) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'root')]) +- self.assertIsNone(parser.close()) ++ def test_simple_xml(self, chunk_size=None): ++ parser = ET.XMLPullParser() ++ self.assert_event_tags(parser, []) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, []) ++ self._feed(parser, ++ "\n text\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'element')]) ++ self._feed(parser, "texttail\n", chunk_size) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [ ++ ('end', 'element'), ++ ('end', 'empty-element'), ++ ]) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'root')]) ++ self.assertIsNone(parser.close()) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_1(self): ++ self.test_simple_xml(chunk_size=1) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_5(self): ++ self.test_simple_xml(chunk_size=5) ++ ++ def test_simple_xml_chunk_22(self): ++ self.test_simple_xml(chunk_size=22) + + def test_feed_while_iterating(self): + parser = ET.XMLPullParser() +diff --git a/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +new file mode 100644 +index 0000000000..6f1015235c +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +@@ -0,0 +1,2 @@ ++Fix tests for :class:`~xml.etree.ElementTree.XMLPullParser` with Expat ++2.6.0. diff --git a/README.en.md b/README.en.md deleted file mode 100644 index f5db7be159f7a0ae972ecb946da5c46fa49030f3..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# python3.12 - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md deleted file mode 100644 index 9e77af534d9bfb5d4dcdbfce22b331a43efc855f..0000000000000000000000000000000000000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# python3.12 - -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/idle3.desktop b/idle3.desktop new file mode 100644 index 0000000000000000000000000000000000000000..dc1d3c3426e877b45bf507d03c9cc193fb76a525 --- /dev/null +++ b/idle3.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +Name=IDLE 3 +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; \ No newline at end of file diff --git a/pathfix.py b/pathfix.py new file mode 100755 index 0000000000000000000000000000000000000000..cf38c5d3c9c2e580d2ac8f31ee246345bb0c2582 --- /dev/null +++ b/pathfix.py @@ -0,0 +1,226 @@ +#! /usr/bin/python3.11 + +# Change the #! line (shebang) occurring in Python scripts. The new interpreter +# pathname must be given with a -i option. +# +# Command line arguments are files or directories to be processed. +# Directories are searched recursively for files whose name looks +# like a python module. +# Symbolic links are always ignored (except as explicit directory +# arguments). +# The original file is kept as a back-up (with a "~" attached to its name), +# -n flag can be used to disable this. + +# Sometimes you may find shebangs with flags such as `#! /usr/bin/env python -si`. +# Normally, pathfix overwrites the entire line, including the flags. +# To change interpreter and keep flags from the original shebang line, use -k. +# If you want to keep flags and add to them one single literal flag, use option -a. + + +# Undoubtedly you can do this using find and sed or perl, but this is +# a nice example of Python code that recurses down a directory tree +# and uses regular expressions. Also note several subtleties like +# preserving the file's mode and avoiding to even write a temp file +# when no changes are needed for a file. +# +# NB: by changing only the function fixfile() you can turn this +# into a program for a different change to Python programs... + +import sys +import re +import os +from stat import * +import getopt + +err = sys.stderr.write +dbg = err +rep = sys.stdout.write + +new_interpreter = None +preserve_timestamps = False +create_backup = True +keep_flags = False +add_flags = b'' + + +def main(): + global new_interpreter + global preserve_timestamps + global create_backup + global keep_flags + global add_flags + + usage = ('usage: %s -i /interpreter -p -n -k -a file-or-directory ...\n' % + sys.argv[0]) + try: + opts, args = getopt.getopt(sys.argv[1:], 'i:a:kpn') + except getopt.error as msg: + err(str(msg) + '\n') + err(usage) + sys.exit(2) + for o, a in opts: + if o == '-i': + new_interpreter = a.encode() + if o == '-p': + preserve_timestamps = True + if o == '-n': + create_backup = False + if o == '-k': + keep_flags = True + if o == '-a': + add_flags = a.encode() + if b' ' in add_flags: + err("-a option doesn't support whitespaces") + sys.exit(2) + if not new_interpreter or not new_interpreter.startswith(b'/') or \ + not args: + err('-i option or file-or-directory missing\n') + err(usage) + sys.exit(2) + bad = 0 + for arg in args: + if os.path.isdir(arg): + if recursedown(arg): bad = 1 + elif os.path.islink(arg): + err(arg + ': will not process symbolic links\n') + bad = 1 + else: + if fix(arg): bad = 1 + sys.exit(bad) + + +def ispython(name): + return name.endswith('.py') + + +def recursedown(dirname): + dbg('recursedown(%r)\n' % (dirname,)) + bad = 0 + try: + names = os.listdir(dirname) + except OSError as msg: + err('%s: cannot list directory: %r\n' % (dirname, msg)) + return 1 + names.sort() + subdirs = [] + for name in names: + if name in (os.curdir, os.pardir): continue + fullname = os.path.join(dirname, name) + if os.path.islink(fullname): pass + elif os.path.isdir(fullname): + subdirs.append(fullname) + elif ispython(name): + if fix(fullname): bad = 1 + for fullname in subdirs: + if recursedown(fullname): bad = 1 + return bad + + +def fix(filename): +## dbg('fix(%r)\n' % (filename,)) + try: + f = open(filename, 'rb') + except IOError as msg: + err('%s: cannot open: %r\n' % (filename, msg)) + return 1 + with f: + line = f.readline() + fixed = fixline(line) + if line == fixed: + rep(filename+': no change\n') + return + head, tail = os.path.split(filename) + tempname = os.path.join(head, '@' + tail) + try: + g = open(tempname, 'wb') + except IOError as msg: + err('%s: cannot create: %r\n' % (tempname, msg)) + return 1 + with g: + rep(filename + ': updating\n') + g.write(fixed) + BUFSIZE = 8*1024 + while 1: + buf = f.read(BUFSIZE) + if not buf: break + g.write(buf) + + # Finishing touch -- move files + + mtime = None + atime = None + # First copy the file's mode to the temp file + try: + statbuf = os.stat(filename) + mtime = statbuf.st_mtime + atime = statbuf.st_atime + os.chmod(tempname, statbuf[ST_MODE] & 0o7777) + except OSError as msg: + err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) + # Then make a backup of the original file as filename~ + if create_backup: + try: + os.rename(filename, filename + '~') + except OSError as msg: + err('%s: warning: backup failed (%r)\n' % (filename, msg)) + else: + try: + os.remove(filename) + except OSError as msg: + err('%s: warning: removing failed (%r)\n' % (filename, msg)) + # Now move the temp file to the original file + try: + os.rename(tempname, filename) + except OSError as msg: + err('%s: rename failed (%r)\n' % (filename, msg)) + return 1 + if preserve_timestamps: + if atime and mtime: + try: + os.utime(filename, (atime, mtime)) + except OSError as msg: + err('%s: reset of timestamp failed (%r)\n' % (filename, msg)) + return 1 + # Return success + return 0 + + +def parse_shebang(shebangline): + shebangline = shebangline.rstrip(b'\n') + start = shebangline.find(b' -') + if start == -1: + return b'' + return shebangline[start:] + + +def populate_flags(shebangline): + old_flags = b'' + if keep_flags: + old_flags = parse_shebang(shebangline) + if old_flags: + old_flags = old_flags[2:] + if not (old_flags or add_flags): + return b'' + # On Linux, the entire string following the interpreter name + # is passed as a single argument to the interpreter. + # e.g. "#! /usr/bin/python3 -W Error -s" runs "/usr/bin/python3 "-W Error -s" + # so shebang should have single '-' where flags are given and + # flag might need argument for that reasons adding new flags is + # between '-' and original flags + # e.g. #! /usr/bin/python3 -sW Error + return b' -' + add_flags + old_flags + + +def fixline(line): + if not line.startswith(b'#!'): + return line + + if b"python" not in line: + return line + + flags = populate_flags(line) + return b'#! ' + new_interpreter + flags + b'\n' + + +if __name__ == '__main__': + main() diff --git a/python3.12.spec b/python3.12.spec new file mode 100644 index 0000000000000000000000000000000000000000..ac98970b6ee8e48ebe4d9a7fc039c117f9f28366 --- /dev/null +++ b/python3.12.spec @@ -0,0 +1,1071 @@ +%global src_version 3.12.2 +%global pybasever %%(echo %{src_version} | cut -d. -f1-2) +%global pyshortver %%(echo %{pybasever} | tr -d '.') +%global pip_version 24.0 +%global setuptools_version 67.6.1 +%global wheel_version 0.40.0 + +%if "%{?__default_python3_pkgversion}" == "%{pybasever}" +%bcond_without main_python +%else +%bcond_with main_python +%endif + +%bcond_without bootstrap +%if %{with bootstrap} +%bcond_with rpmwheels +%else +%bcond_without rpmwheels +%endif + +%bcond_without optimizations +%bcond_without tests +%bcond_without gdb_hooks +%bcond_without gdbm +%bcond_without computed_gotos +%bcond_without debug_build + +%ifarch %{valgrind_arches} +%bcond_without valgrind +%else +%bcond_with valgrind +%endif + +%if %{with main_python} +%global pkgname python3 +%else +%global pkgname python%{pybasever} +%endif + +%global pylibdir %{_libdir}/python%{pybasever} +%global dynload_dir %{pylibdir}/lib-dynload +%global ABIFLAGS_debug d +%global LDVERSION_optimized %{pybasever} +%global LDVERSION_debug %{pybasever}%{ABIFLAGS_debug} + +%global platform_triplet %{expand:%(echo %{_arch}-linux%{_gnu} | sed -E \\ + -e 's/^arm(eb)?-linux-gnueabi$/arm\\1-linux-gnueabihf/' \\ + -e 's/^mips64(el)?-linux-gnu$/mips64\\1-linux-gnuabi64/' \\ + -e 's/^ppc(64)?(le)?-linux-gnu$/powerpc\\1\\2-linux-gnu/')} + +%global SOABI_optimized cpython-%{pyshortver}-%{platform_triplet} +%global SOABI_debug cpython-%{pyshortver}%{ABIFLAGS_debug}-%{platform_triplet} +%global bytecode_suffixes .cpython-%{pyshortver}*.pyc +%global py_SOVERSION 1.0 +%global py_INSTSONAME_optimized libpython%{LDVERSION_optimized}.so.%{py_SOVERSION} +%global py_INSTSONAME_debug libpython%{LDVERSION_debug}.so.%{py_SOVERSION} + +%undefine py_auto_byte_compile +%undefine __pythonname_provides +%undefine _debuginfo_subpackages +%undefine _include_frame_pointers + + + +Summary: Version %{pybasever} of the Python interpreter +Name: python%{pybasever} +Version: %{src_version} +Release: 1%{?dist} +License: Python-2.0.1 +URL: https://www.python.org/ + +Source0: %{url}ftp/python/%{version}/Python-%{version}.tar.xz +# Idle is the official IDE of Python +Source1: idle3.desktop + +# pathfix.py has been remove by official Python +# It is very useful, so we keep it as source +Source2: pathfix.py + +Patch3000: 00251-change-user-install-location.patch +Patch3001: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch +Patch3002: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch +Patch3003: 00418-don-t-generate-sbom-in-make-regen-all.patch +Patch3004: 00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch + +BuildRequires: autoconf, make, pkgconfig, gdb, gcc-c++, findutils, glibc-all-langpacks, glibc-devel +BuildRequires: bzip2, bzip2-devel, tar, zlib-devel, expat-devel, xz-devel +BuildRequires: desktop-file-utils, libGL-devel, libX11-devel, tcl-devel, tix-devel, tk-devel +BuildRequires: git-core, gmp-devel, libappstream-glib, libb2-devel, libffi-devel, libnsl2-devel, libtirpc-devel +BuildRequires: libuuid-devel, mpdecimal-devel, ncurses-devel, openssl-devel, readline-devel, sqlite-devel, system-rpm-config +BuildRequires: tzdata, systemtap-sdt-devel, net-tools, bluez-libs-devel +%if %{with gdbm} +BuildRequires: gdbm-devel +%endif +%if %{with valgrind} +BuildRequires: valgrind-devel +%endif + +%if %{with rpmwheels} +BuildRequires: %{python_wheel_pkg_prefix}-setuptools-wheel +BuildRequires: %{python_wheel_pkg_prefix}-pip-wheel +%endif + +%if %{without bootstrap} +BuildRequires: python%{pybasever} +BuildRequires: python3-rpm-generators +%endif + + + +%if %{with main_python} +%description +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +%package -n %{pkgname} +Summary: Python %{pybasever} interpreter +Provides: python%{pybasever} = %{version}-%{release} +Provides: python%{pyshortver} = %{version}-%{release} +Provides: python(abi) = %{pybasever} +Provides: python-is-python3 = %{version}-%{release} +Provides: python = %{version}-%{release} + +# default install in user environment and not install in koji +Recommends: %{_bindir}/python +%else +%global __requires_exclude ^python\\(abi\\) = 3\\..+ +%global __provides_exclude ^python\\(abi\\) = 3\\..+ +%endif + +Requires: %{pkgname}-libs = %{version}-%{release} +%global __requires_exclude ^(/usr/bin/python3|python\\(abi\\)) + + +%description -n %{pkgname} +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +This package contains the Python3 interpreter. + + +%package -n %{pkgname}-libs +Summary: Python runtime libraries +%if %{with rpmwheels} +Requires: %{python_wheel_pkg_prefix}-setuptools-wheel +Requires: %{python_wheel_pkg_prefix}-pip-wheel +%else +Requires: tzdata +Provides: bundled(python3dist(pip)) = %{pip_version} +Provides: bundled(python3dist(setuptools)) = %{setuptools_version} +%endif + +Recommends: %{pkgname} = %{version}-%{release} +Recommends: (%{pkgname}-tkinter = %{version}-%{release} if tk) + +%description -n %{pkgname}-libs +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +This package contains Python C/C++ libraries by apps and Python standard +library. + + +%package -n %{pkgname}-devel +Summary: Libraries and header files needed for Python development +Requires: %{pkgname} = %{version}-%{release} +Requires: %{pkgname}-libs = %{version}-%{release} +Requires: (python-rpm-macros if rpm-build) +Requires: (python3-rpm-macros if rpm-build) +Requires: (pyproject-rpm-macros if rpm-build) +Requires: (python3-rpm-generators if rpm-build) + +%if %{with main_python} +Recommends: python3-pip +Provides: 2to3 = %{version}-%{release} +%endif + +Supplements: tox +Provides: %{pkgname}-2to3 = %{version}-%{release} + +%description -n %{pkgname}-devel +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +This package contains the development files. + + +%package -n %{pkgname}-idle +Summary: A basic graphical development environment for Python +Requires: %{pkgname} = %{version}-%{release} +Requires: %{pkgname}-tkinter = %{version}-%{release} +Provides: %{pkgname}-tools = %{version}-%{release} + +%if %{with main_python} +Provides: idle3 = %{version}-%{release} +Provides: idle = %{version}-%{release} +%endif + +%description -n %{pkgname}-idle +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +IDLE is the Python IDE built with the Tkinter GUI toolkit. + + +%package -n %{pkgname}-tkinter +Summary: A GUI toolkit for Python +Requires: %{pkgname} = %{version}-%{release} +Provides: %{pkgname}-turtle = %{version}-%{release} + +%description -n %{pkgname}-tkinter +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +Tkinter is the standard Python interface to the Tcl/Tk GUI toolkit. + + +%package -n %{pkgname}-test +Summary: The self-test suite for the main python3 package +Requires: %{pkgname} = %{version}-%{release} +Requires: %{pkgname}-libs = %{version}-%{release} + +%description -n %{pkgname}-test +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +This package contains self-test suite for the Python interpreter. + + +%if %{with debug_build} +%package -n %{pkgname}-debug +Summary: Debug version of the Python runtime +Requires: %{pkgname} = %{version}-%{release} +Requires: %{pkgname}-libs = %{version}-%{release} +Requires: %{pkgname}-devel = %{version}-%{release} +Requires: %{pkgname}-test = %{version}-%{release} +Requires: %{pkgname}-tkinter = %{version}-%{release} +Requires: %{pkgname}-idle = %{version}-%{release} + +%description -n %{pkgname}-debug +Python is a great object-oriented, interpreted, and interactive programming +language. It is often compared (favorably of course :-) ) to Lisp, Tcl, +Perl, Ruby, C#, Visual Basic, Visual Fox Pro, Scheme or Java... and it's +much more fun. + +Python combines remarkable power with very clear syntax. It has modules, +classes, exceptions, very high level dynamic data types, and dynamic typing. +There are interfaces to many system calls and libraries, as well as to various +windowing systems. New built-in modules are easily written in C or C++ (or +other languages, depending on the chosen implementation). Python is also +usable as an extension language for applications written in other languages +that need easy-to-use scripting or automation interfaces. + +This package contains a Python runtime with numerous debugging features +enabled, aimed at advanced Python users such as developers of Python +extension modules. +%endif + +%if %{with main_python} +%package -n python-unversioned-command +Summary: The "python" command that runs Python 3 +BuildArch: noarch + +Requires: python3 == %{version}-%{release} +Provides: python = %{version}-%{release} +Provides: python-is-python3 = %{version}-%{release} + +%description -n python-unversioned-command +This package contains /usr/bin/python - the "python" command that runs Python 3. +%endif + +%prep +%autosetup -p1 -n Python-%{version} + +cp %{SOURCE2} Tools/scripts + +%if %{with rpmwheels} +rm Lib/ensurepip/_bundled/pip-%{pip_version}-py3-none-any.whl +rm Lib/test/wheeldata/setuptools-%{setuptools_version}-py3-none-any.whl +rm Lib/test/wheeldata/wheel-%{wheel_version}-py3-none-any.whl +%endif + +find -name '*.exe' -print -delete +rm -r Modules/expat +rm -r Modules/_decimal/libmpdec +rm configure pyconfig.h.in + + + +%build +export HAS_GIT=not-found +autoconf +autoheader + +%if %{with computed_gotos} +%global computed_gotos_flag yes +%else +%global computed_gotos_flag no +%endif + +%if %{with optimizations} +%global optimizations_flag "--enable-optimizations" +%else +%global optimizations_flag "--disable-optimizations" +%endif + +export CFLAGS="%{extension_cflags}" +export CFLAGS_NODIST="%{build_cflags} -D_GNU_SOURCE -fPIC -fwrapv" +export CXXFLAGS="%{extension_cxxflags}" +export CPPFLAGS="$(pkg-config --cflags-only-I libffi)" +export OPT="%{extension_cflags}" +export LINKCC="gcc" +export CFLAGS="$CFLAGS $(pkg-config --cflags openssl)" +export LDFLAGS="%{extension_ldflags} $(pkg-config --libs-only-L openssl)" +export LDFLAGS_NODIST="%{build_ldflags} -g $(pkg-config --libs-only-L openssl)" +export DFLAGS=" " + +topdir=$(pwd) +mkdir -p normal +pushd normal +%global _configure ${topdir}/configure + +%configure \ + --with-platlibdir=%{_lib} \ + --enable-ipv6 \ + --enable-shared \ + --with-computed-gotos=%{computed_gotos_flag} \ + --with-dbmliborder=gdbm:ndbm:bdb \ + --with-system-expat \ + --with-system-ffi \ + --with-system-libmpdec \ + --enable-loadable-sqlite-extensions \ + --with-dtrace \ + --with-lto \ + --with-ssl-default-suites=openssl \ + --without-static-libpython \ +%if %{with rpmwheels} + --with-wheel-pkg-dir=%{python_wheel_dir} \ +%endif +%if %{with valgrind} + --with-valgrind \ +%endif + --without-ensurepip %{optimizations_flag} + +%if %{without bootstrap} + %make_build CFLAGS_NODIST="$CFLAGS_NODIST" regen-all PYTHON_FOR_REGEN="python%{pybasever}" +%endif + +%make_build CFLAGS_NODIST="$CFLAGS_NODIST" +popd + +# debug build +%if %{with debug_build} +topdir=$(pwd) + +mkdir -p debug +pushd debug +%global _configure ${topdir}/configure + +%configure \ + --with-platlibdir=%{_lib} \ + --enable-ipv6 \ + --enable-shared \ + --with-computed-gotos=%{computed_gotos_flag} \ + --with-dbmliborder=gdbm:ndbm:bdb \ + --with-system-expat \ + --with-system-ffi \ + --with-system-libmpdec \ + --enable-loadable-sqlite-extensions \ + --with-dtrace \ + --with-lto \ + --with-ssl-default-suites=openssl \ + --without-static-libpython \ +%if %{with rpmwheels} + --with-wheel-pkg-dir=%{python_wheel_dir} \ +%endif +%if %{with valgrind} + --with-valgrind \ +%endif + --without-ensurepip \ + --with-pydebug + +%if %{without bootstrap} + %make_build EXTRA_CFLAGS="-O0 -Wno-cpp" CFLAGS_NODIST="$CFLAGS_NODIST -O0 -Wno-cpp" regen-all PYTHON_FOR_REGEN="python%{pybasever}" +%endif + +%make_build EXTRA_CFLAGS="-O0 -Wno-cpp" CFLAGS_NODIST="$CFLAGS_NODIST -O0 -Wno-cpp" +popd +%endif + + + +%install +%if %{with gdb_hooks} +DirHoldingGdbPy=%{_usr}/lib/debug/%{_libdir} +mkdir -p %{buildroot}$DirHoldingGdbPy +%endif + +%global _pyconfig32_h pyconfig-32.h +%global _pyconfig64_h pyconfig-64.h +%global _pyconfig_h pyconfig-%{__isa_bits}.h + +# +# debug install +# + +%if %{with debug_build} +pushd debug +%make_install EXTRA_CFLAGS="-O0" +popd + +LDVersion=%{LDVERSION_debug} + +%if %{with gdb_hooks} + PathOfGdbPy=$DirHoldingGdbPy/%{py_INSTSONAME_debug}-%{version}-%{release}.%{_arch}.debug-gdb.py + cp Tools/gdb/libpython.py %{buildroot}$PathOfGdbPy +%endif + + mv %{buildroot}%{_bindir}/python${LDVersion}-{,`uname -m`-}config + echo -e '#!/bin/sh\nexec %{_bindir}/python'${LDVersion}'-`uname -m`-config "$@"' > \ + %{buildroot}%{_bindir}/python${LDVersion}-config + chmod +x %{buildroot}%{_bindir}/python${LDVersion}-config + + mv %{buildroot}%{_includedir}/python${LDVersion}/pyconfig.h \ + %{buildroot}%{_includedir}/python${LDVersion}/%{_pyconfig_h} + cat > %{buildroot}%{_includedir}/python${LDVersion}/pyconfig.h << EOF +#include + +#if __WORDSIZE == 32 +#include "%{_pyconfig32_h}" +#elif __WORDSIZE == 64 +#include "%{_pyconfig64_h}" +#else +#error "Unknown word size" +#endif +EOF +%endif + +# +# normal install +# +pushd normal +%make_install +popd + +%if %{with gdb_hooks} + PathOfGdbPy=$DirHoldingGdbPy/%{py_INSTSONAME_optimized}-%{version}-%{release}.%{_arch}.debug-gdb.py + cp Tools/gdb/libpython.py %{buildroot}$PathOfGdbPy +%endif + +mv %{buildroot}%{_bindir}/python%{LDVERSION_optimized}-{,`uname -m`-}config +echo -e '#!/bin/sh\nexec %{_bindir}/python'%{pybasever}'-`uname -m`-config "$@"' > \ + %{buildroot}%{_bindir}/python%{LDVERSION_optimized}-config + chmod +x %{buildroot}%{_bindir}/python%{LDVERSION_optimized}-config + +mv %{buildroot}%{_includedir}/python%{LDVERSION_optimized}/pyconfig.h \ + %{buildroot}%{_includedir}/python%{LDVERSION_optimized}/%{_pyconfig_h} +cat > %{buildroot}%{_includedir}/python%{LDVERSION_optimized}/pyconfig.h << EOF +#include + +#if __WORDSIZE == 32 +#include "%{_pyconfig32_h}" +#elif __WORDSIZE == 64 +#include "%{_pyconfig64_h}" +#else +#error "Unknown word size" +#endif +EOF + +install -d -m 0755 %{buildroot}%{pylibdir}/site-packages/__pycache__ +%if "%{_lib}" == "lib64" +install -d -m 0755 %{buildroot}%{_prefix}/lib/python%{pybasever}/site-packages/__pycache__ +%endif + +%if %{with main_python} +install -D -m 0644 Lib/idlelib/Icons/idle_16.png %{buildroot}%{_datadir}/icons/hicolor/16x16/apps/idle3.png +install -D -m 0644 Lib/idlelib/Icons/idle_32.png %{buildroot}%{_datadir}/icons/hicolor/32x32/apps/idle3.png +install -D -m 0644 Lib/idlelib/Icons/idle_48.png %{buildroot}%{_datadir}/icons/hicolor/48x48/apps/idle3.png +install -D -m 0644 Lib/idlelib/Icons/idle_256.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/idle3.png +desktop-file-install --dir=%{buildroot}%{_datadir}/applications %{SOURCE1} +%endif + +sed -i -e "s/'pyconfig.h'/'%{_pyconfig_h}'/" \ + %{buildroot}%{pylibdir}/sysconfig.py + +for tool in pygettext msgfmt; do + cp -p Tools/i18n/${tool}.py %{buildroot}%{_bindir}/${tool}%{pybasever}.py + ln -s ${tool}%{pybasever}.py %{buildroot}%{_bindir}/${tool}3.py +done + +LD_LIBRARY_PATH=./normal ./normal/python \ + Tools/scripts/pathfix.py \ + -i "%{_bindir}/python%{pybasever}" -pn \ + %{buildroot} \ + %{buildroot}%{_bindir}/*%{pybasever}.py \ + %{?with_gdb_hooks:%{buildroot}$DirHoldingGdbPy/*.py} + +find %{buildroot} -name \*.py \ + \( \( \! -perm /u+x,g+x,o+x -exec sed -e '/^#!/Q 0' -e 'Q 1' {} \; \ + -print -exec sed -i '1d' {} \; \) -o \( \ + -perm /u+x,g+x,o+x ! -exec grep -m 1 -q '^#!' {} \; \ + -exec chmod a-x {} \; \) \) + +find %{buildroot} -name \*.bat -exec rm {} \; +find %{buildroot}/ -name "*~" -exec rm -f {} \; +find . -name "*~" -exec rm -f {} \; + +%if 0%{?clamp_mtime_to_source_date_epoch} +LD_LIBRARY_PATH="%{buildroot}%{dynload_dir}/:%{buildroot}%{_libdir}" \ +PYTHONPATH="%{_rpmconfigdir}/%{_vendor}" \ +%{buildroot}%{_bindir}/python%{pybasever} -s -B -m clamp_source_mtime %{buildroot}%{pylibdir} +%endif + +LD_LIBRARY_PATH="%{buildroot}%{dynload_dir}/:%{buildroot}%{_libdir}" \ +%{buildroot}%{_bindir}/python%{pybasever} -s -B -m compileall \ +-f %{_smp_mflags} -o 0 -o 1 -o 2 -s %{buildroot} -p / %{buildroot} --hardlink-dupes --invalidation-mode=timestamp \ +-x 'bad_coding|badsyntax|site-packages|test/test_lib2to3/data' + +%global __brp_python_hardlink %{nil} + +rm -rf %{buildroot}%{_bindir}/__pycache__ + +find %{buildroot} -perm 555 -exec chmod 755 {} \; + +%if %{with debug_build} && %{with main_python} +ln -s %{_bindir}/python%{LDVERSION_debug} %{buildroot}%{_bindir}/python3-debug +ln -s ./python3-debug %{buildroot}%{_bindir}/python-debug +%endif + +%if %{without main_python} +rm %{buildroot}%{_bindir}/python3 +rm %{buildroot}%{_bindir}/pydoc3 +rm %{buildroot}%{_bindir}/pygettext3.py +rm %{buildroot}%{_bindir}/msgfmt3.py +rm %{buildroot}%{_bindir}/idle3 +rm %{buildroot}%{_bindir}/python3-* +rm %{buildroot}%{_bindir}/2to3 +rm %{buildroot}%{_libdir}/libpython3.so +rm %{buildroot}%{_mandir}/man1/python3.1 +rm %{buildroot}%{_libdir}/pkgconfig/python3.pc +rm %{buildroot}%{_libdir}/pkgconfig/python3-embed.pc +%else +ln -s ./python3 %{buildroot}%{_bindir}/python +ln -s ./pydoc3 %{buildroot}%{_bindir}/pydoc +ln -s ./pygettext3.py %{buildroot}%{_bindir}/pygettext.py +ln -s ./msgfmt3.py %{buildroot}%{_bindir}/msgfmt.py +ln -s ./idle3 %{buildroot}%{_bindir}/idle +ln -s ./python3-config %{buildroot}%{_bindir}/python-config +ln -s ./python3.1 %{buildroot}%{_mandir}/man1/python.1 +ln -s ./python3.pc %{buildroot}%{_libdir}/pkgconfig/python.pc +%endif + +for file in %{buildroot}%{pylibdir}/pydoc_data/topics.py $(grep --include='*.py' -lr %{buildroot}%{pylibdir}/encodings -e 'Python Character Mapping Codec .* from .* with gencodec.py'); do + directory=$(dirname ${file}) + module=$(basename ${file%%.py}) + mv ${directory}/{__pycache__/${module}.cpython-%{pyshortver}.pyc,${module}.pyc} + rm ${directory}/{__pycache__/${module}.cpython-%{pyshortver}.opt-?.pyc,${module}.py} +done + + + +%check +# if zope related python packages were installed in system, test will be failed on test_embed case. +LD_LIBRARY_PATH=$(pwd)/normal $(pwd)/normal/python -m test.pythoninfo + +LD_LIBRARY_PATH=$(pwd)/normal $(pwd)/normal/python -m test.regrtest \ + -wW --slowest -j0 --timeout=1800 \ + -i test_freeze_simple_script \ + -i test_check_probes \ + %if %{with bootstrap} + -x test_distutils \ + %endif + + + +%files -n %{pkgname} +%doc README.rst +%if %{with main_python} +%{_bindir}/pydoc* +%{_bindir}/python3 +%else +%{_bindir}/pydoc%{pybasever} +%endif + +%{_bindir}/python%{pybasever} +%{_bindir}/python%{LDVERSION_optimized} +%{_mandir}/*/*3* + +%files -n %{pkgname}-libs +%license %{pylibdir}/LICENSE.txt +%doc README.rst +%dir %{pylibdir} +%dir %{dynload_dir} +%{pylibdir}/lib2to3/ + +%dir %{pylibdir}/unittest/ +%dir %{pylibdir}/unittest/__pycache__/ +%{pylibdir}/unittest/*.py +%{pylibdir}/unittest/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/asyncio/ +%dir %{pylibdir}/asyncio/__pycache__/ +%{pylibdir}/asyncio/*.py +%{pylibdir}/asyncio/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/venv/ +%dir %{pylibdir}/venv/__pycache__/ +%{pylibdir}/venv/*.py +%{pylibdir}/venv/__pycache__/*%{bytecode_suffixes} +%{pylibdir}/venv/scripts +%{pylibdir}/wsgiref +%{pylibdir}/xmlrpc + +%dir %{pylibdir}/ensurepip/ +%dir %{pylibdir}/ensurepip/__pycache__/ +%{pylibdir}/ensurepip/*.py +%{pylibdir}/ensurepip/__pycache__/*%{bytecode_suffixes} + +%if %{with rpmwheels} +%exclude %{pylibdir}/ensurepip/_bundled +%else +%dir %{pylibdir}/ensurepip/_bundled +%{pylibdir}/ensurepip/_bundled/pip-%{pip_version}-py3-none-any.whl +%endif + +%dir %{pylibdir}/concurrent/ +%dir %{pylibdir}/concurrent/__pycache__/ +%{pylibdir}/concurrent/*.py +%{pylibdir}/concurrent/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/concurrent/futures/ +%dir %{pylibdir}/concurrent/futures/__pycache__/ +%{pylibdir}/concurrent/futures/*.py +%{pylibdir}/concurrent/futures/__pycache__/*%{bytecode_suffixes} + +%{pylibdir}/pydoc_data + +%{dynload_dir}/_blake2.%{SOABI_optimized}.so +%{dynload_dir}/_md5.%{SOABI_optimized}.so +%{dynload_dir}/_sha1.%{SOABI_optimized}.so +%{dynload_dir}/_sha2.%{SOABI_optimized}.so +%{dynload_dir}/_sha3.%{SOABI_optimized}.so +%{dynload_dir}/_asyncio.%{SOABI_optimized}.so +%{dynload_dir}/_bisect.%{SOABI_optimized}.so +%{dynload_dir}/_bz2.%{SOABI_optimized}.so +%{dynload_dir}/_codecs_cn.%{SOABI_optimized}.so +%{dynload_dir}/_codecs_hk.%{SOABI_optimized}.so +%{dynload_dir}/_codecs_iso2022.%{SOABI_optimized}.so +%{dynload_dir}/_codecs_jp.%{SOABI_optimized}.so +%{dynload_dir}/_codecs_kr.%{SOABI_optimized}.so +%{dynload_dir}/_codecs_tw.%{SOABI_optimized}.so +%{dynload_dir}/_contextvars.%{SOABI_optimized}.so +%{dynload_dir}/_crypt.%{SOABI_optimized}.so +%{dynload_dir}/_csv.%{SOABI_optimized}.so +%{dynload_dir}/_ctypes.%{SOABI_optimized}.so +%{dynload_dir}/_curses.%{SOABI_optimized}.so +%{dynload_dir}/_curses_panel.%{SOABI_optimized}.so +%{dynload_dir}/_dbm.%{SOABI_optimized}.so +%{dynload_dir}/_decimal.%{SOABI_optimized}.so +%{dynload_dir}/_elementtree.%{SOABI_optimized}.so +%if %{with gdbm} +%{dynload_dir}/_gdbm.%{SOABI_optimized}.so +%endif +%{dynload_dir}/_hashlib.%{SOABI_optimized}.so +%{dynload_dir}/_heapq.%{SOABI_optimized}.so +%{dynload_dir}/_json.%{SOABI_optimized}.so +%{dynload_dir}/_lsprof.%{SOABI_optimized}.so +%{dynload_dir}/_lzma.%{SOABI_optimized}.so +%{dynload_dir}/_multibytecodec.%{SOABI_optimized}.so +%{dynload_dir}/_multiprocessing.%{SOABI_optimized}.so +%{dynload_dir}/_opcode.%{SOABI_optimized}.so +%{dynload_dir}/_pickle.%{SOABI_optimized}.so +%{dynload_dir}/_posixsubprocess.%{SOABI_optimized}.so +%{dynload_dir}/_queue.%{SOABI_optimized}.so +%{dynload_dir}/_random.%{SOABI_optimized}.so +%{dynload_dir}/_socket.%{SOABI_optimized}.so +%{dynload_dir}/_sqlite3.%{SOABI_optimized}.so +%{dynload_dir}/_ssl.%{SOABI_optimized}.so +%{dynload_dir}/_statistics.%{SOABI_optimized}.so +%{dynload_dir}/_struct.%{SOABI_optimized}.so +%{dynload_dir}/array.%{SOABI_optimized}.so +%{dynload_dir}/audioop.%{SOABI_optimized}.so +%{dynload_dir}/binascii.%{SOABI_optimized}.so +%{dynload_dir}/cmath.%{SOABI_optimized}.so +%{dynload_dir}/_datetime.%{SOABI_optimized}.so +%{dynload_dir}/fcntl.%{SOABI_optimized}.so +%{dynload_dir}/grp.%{SOABI_optimized}.so +%{dynload_dir}/math.%{SOABI_optimized}.so +%{dynload_dir}/mmap.%{SOABI_optimized}.so +%{dynload_dir}/nis.%{SOABI_optimized}.so +%{dynload_dir}/ossaudiodev.%{SOABI_optimized}.so +%{dynload_dir}/_posixshmem.%{SOABI_optimized}.so +%{dynload_dir}/pyexpat.%{SOABI_optimized}.so +%{dynload_dir}/readline.%{SOABI_optimized}.so +%{dynload_dir}/resource.%{SOABI_optimized}.so +%{dynload_dir}/select.%{SOABI_optimized}.so +%{dynload_dir}/spwd.%{SOABI_optimized}.so +%{dynload_dir}/syslog.%{SOABI_optimized}.so +%{dynload_dir}/termios.%{SOABI_optimized}.so +%{dynload_dir}/unicodedata.%{SOABI_optimized}.so +%{dynload_dir}/_uuid.%{SOABI_optimized}.so +%{dynload_dir}/zlib.%{SOABI_optimized}.so +%{dynload_dir}/_zoneinfo.%{SOABI_optimized}.so + +%dir %{pylibdir}/site-packages/ +%dir %{pylibdir}/site-packages/__pycache__/ +%{pylibdir}/site-packages/README.txt + +%{pylibdir}/*.py +%dir %{pylibdir}/__pycache__/ +%{pylibdir}/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/collections/ +%dir %{pylibdir}/collections/__pycache__/ +%{pylibdir}/collections/*.py +%{pylibdir}/collections/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/ctypes/ +%dir %{pylibdir}/ctypes/__pycache__/ +%{pylibdir}/ctypes/*.py +%{pylibdir}/ctypes/__pycache__/*%{bytecode_suffixes} +%{pylibdir}/ctypes/macholib + +%{pylibdir}/curses + +%dir %{pylibdir}/dbm/ +%dir %{pylibdir}/dbm/__pycache__/ +%{pylibdir}/dbm/*.py +%{pylibdir}/dbm/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/email/ +%dir %{pylibdir}/email/__pycache__/ +%{pylibdir}/email/*.py +%{pylibdir}/email/__pycache__/*%{bytecode_suffixes} +%{pylibdir}/email/mime +%doc %{pylibdir}/email/architecture.rst + +%{pylibdir}/encodings + +%{pylibdir}/html +%{pylibdir}/http + +%dir %{pylibdir}/importlib/ +%dir %{pylibdir}/importlib/__pycache__/ +%{pylibdir}/importlib/*.py +%{pylibdir}/importlib/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/importlib/metadata/ +%dir %{pylibdir}/importlib/metadata/__pycache__/ +%{pylibdir}/importlib/metadata/*.py +%{pylibdir}/importlib/metadata/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/importlib/resources/ +%dir %{pylibdir}/importlib/resources/__pycache__/ +%{pylibdir}/importlib/resources/*.py +%{pylibdir}/importlib/resources/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/json/ +%dir %{pylibdir}/json/__pycache__/ +%{pylibdir}/json/*.py +%{pylibdir}/json/__pycache__/*%{bytecode_suffixes} + +%{pylibdir}/logging +%{pylibdir}/multiprocessing + +%dir %{pylibdir}/re/ +%{pylibdir}/re/*.py +%{pylibdir}/re/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/sqlite3/ +%dir %{pylibdir}/sqlite3/__pycache__/ +%{pylibdir}/sqlite3/*.py +%{pylibdir}/sqlite3/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/tomllib/ +%{pylibdir}/tomllib/*.py +%{pylibdir}/tomllib/__pycache__/*%{bytecode_suffixes} +%exclude %{pylibdir}/turtle.py +%exclude %{pylibdir}/__pycache__/turtle*%{bytecode_suffixes} + +%{pylibdir}/urllib +%{pylibdir}/xml + +%dir %{pylibdir}/zipfile/ +%dir %{pylibdir}/zipfile/__pycache__/ +%{pylibdir}/zipfile/*.py +%{pylibdir}/zipfile/__pycache__/*%{bytecode_suffixes} +%dir %{pylibdir}/zipfile/_path/ +%dir %{pylibdir}/zipfile/_path/__pycache__/ +%{pylibdir}/zipfile/_path/*.py +%{pylibdir}/zipfile/_path/__pycache__/*%{bytecode_suffixes} + +%{pylibdir}/zoneinfo + +%if "%{_lib}" == "lib64" +%attr(0755,root,root) %dir %{_prefix}/lib/python%{pybasever} +%attr(0755,root,root) %dir %{_prefix}/lib/python%{pybasever}/site-packages +%attr(0755,root,root) %dir %{_prefix}/lib/python%{pybasever}/site-packages/__pycache__/ +%endif + +%dir %{pylibdir}/config-%{LDVERSION_optimized}-%{platform_triplet}/ +%{pylibdir}/config-%{LDVERSION_optimized}-%{platform_triplet}/Makefile +%dir %{_includedir}/python%{LDVERSION_optimized}/ +%{_includedir}/python%{LDVERSION_optimized}/%{_pyconfig_h} + +%{_libdir}/%{py_INSTSONAME_optimized} +%if %{with main_python} +%{_libdir}/libpython3.so +%endif + + +%files -n %{pkgname}-devel +%doc Misc/README.valgrind Misc/valgrind-python.supp +%if %{with main_python} +%{_bindir}/2to3 +%{_bindir}/python3-config +%{_bindir}/python-config +%{_libdir}/pkgconfig/python3.pc +%{_libdir}/pkgconfig/python.pc +%{_libdir}/pkgconfig/python3-embed.pc +%{_bindir}/pygettext3.py +%{_bindir}/pygettext.py +%{_bindir}/msgfmt3.py +%{_bindir}/msgfmt.py +%endif +%{_bindir}/2to3-%{pybasever} +%{_bindir}/pygettext%{pybasever}.py +%{_bindir}/msgfmt%{pybasever}.py +%{_bindir}/python%{pybasever}-config +%{_bindir}/python%{LDVERSION_optimized}-config +%{_bindir}/python%{LDVERSION_optimized}-*-config +%{_libdir}/libpython%{LDVERSION_optimized}.so +%{_libdir}/pkgconfig/python-%{LDVERSION_optimized}.pc +%{_libdir}/pkgconfig/python-%{LDVERSION_optimized}-embed.pc +%{_libdir}/pkgconfig/python-%{pybasever}.pc +%{_libdir}/pkgconfig/python-%{pybasever}-embed.pc +%{pylibdir}/config-%{LDVERSION_optimized}-%{platform_triplet}/* +%exclude %{pylibdir}/config-%{LDVERSION_optimized}-%{platform_triplet}/Makefile +%exclude %{_includedir}/python%{LDVERSION_optimized}/%{_pyconfig_h} +%{_includedir}/python%{LDVERSION_optimized}/*.h +%{_includedir}/python%{LDVERSION_optimized}/internal/ +%{_includedir}/python%{LDVERSION_optimized}/cpython/ + +%files -n %{pkgname}-idle +%if %{with main_python} +%{_bindir}/idle* +%{_datadir}/applications/idle3.desktop +%{_datadir}/icons/hicolor/*/apps/idle3.* +%else +%{_bindir}/idle%{pybasever} +%endif +%{pylibdir}/idlelib + +%files -n %{pkgname}-tkinter +%{pylibdir}/tkinter +%{dynload_dir}/_tkinter.%{SOABI_optimized}.so +%{pylibdir}/turtle.py +%{pylibdir}/__pycache__/turtle*%{bytecode_suffixes} +%dir %{pylibdir}/turtledemo +%{pylibdir}/turtledemo/*.py +%{pylibdir}/turtledemo/*.cfg +%dir %{pylibdir}/turtledemo/__pycache__/ +%{pylibdir}/turtledemo/__pycache__/*%{bytecode_suffixes} + + +%files -n %{pkgname}-test +%{pylibdir}/test +%{dynload_dir}/xxlimited.%{SOABI_optimized}.so +%{dynload_dir}/xxlimited_35.%{SOABI_optimized}.so +%{dynload_dir}/xxsubtype.%{SOABI_optimized}.so +%{dynload_dir}/_ctypes_test.%{SOABI_optimized}.so +%{dynload_dir}/_testbuffer.%{SOABI_optimized}.so +%{dynload_dir}/_testcapi.%{SOABI_optimized}.so +%{dynload_dir}/_testclinic.%{SOABI_optimized}.so +%{dynload_dir}/_testimportmultiple.%{SOABI_optimized}.so +%{dynload_dir}/_testinternalcapi.%{SOABI_optimized}.so +%{dynload_dir}/_testmultiphase.%{SOABI_optimized}.so +%{dynload_dir}/_testsinglephase.%{SOABI_optimized}.so +%{dynload_dir}/_xxinterpchannels.%{SOABI_optimized}.so +%{dynload_dir}/_xxsubinterpreters.%{SOABI_optimized}.so +%{dynload_dir}/_xxtestfuzz.%{SOABI_optimized}.so + +%dir %{pylibdir}/__phello__/ +%dir %{pylibdir}/__phello__/__pycache__/ +%{pylibdir}/__phello__/__init__.py +%{pylibdir}/__phello__/spam.py +%{pylibdir}/__phello__/__pycache__/*%{bytecode_suffixes} + +%if %{with debug_build} +%files -n %{pkgname}-debug +%if %{with main_python} +%{_bindir}/python3-debug +%{_bindir}/python-debug +%endif + +%{_bindir}/python%{LDVERSION_debug} +%{dynload_dir}/_blake2.%{SOABI_debug}.so +%{dynload_dir}/_md5.%{SOABI_debug}.so +%{dynload_dir}/_sha1.%{SOABI_debug}.so +%{dynload_dir}/_sha2.%{SOABI_debug}.so +%{dynload_dir}/_sha3.%{SOABI_debug}.so +%{dynload_dir}/_asyncio.%{SOABI_debug}.so +%{dynload_dir}/_bisect.%{SOABI_debug}.so +%{dynload_dir}/_bz2.%{SOABI_debug}.so +%{dynload_dir}/_codecs_cn.%{SOABI_debug}.so +%{dynload_dir}/_codecs_hk.%{SOABI_debug}.so +%{dynload_dir}/_codecs_iso2022.%{SOABI_debug}.so +%{dynload_dir}/_codecs_jp.%{SOABI_debug}.so +%{dynload_dir}/_codecs_kr.%{SOABI_debug}.so +%{dynload_dir}/_codecs_tw.%{SOABI_debug}.so +%{dynload_dir}/_contextvars.%{SOABI_debug}.so +%{dynload_dir}/_crypt.%{SOABI_debug}.so +%{dynload_dir}/_csv.%{SOABI_debug}.so +%{dynload_dir}/_ctypes.%{SOABI_debug}.so +%{dynload_dir}/_curses.%{SOABI_debug}.so +%{dynload_dir}/_curses_panel.%{SOABI_debug}.so +%{dynload_dir}/_dbm.%{SOABI_debug}.so +%{dynload_dir}/_decimal.%{SOABI_debug}.so +%{dynload_dir}/_elementtree.%{SOABI_debug}.so +%if %{with gdbm} +%{dynload_dir}/_gdbm.%{SOABI_debug}.so +%endif +%{dynload_dir}/_hashlib.%{SOABI_debug}.so +%{dynload_dir}/_heapq.%{SOABI_debug}.so +%{dynload_dir}/_json.%{SOABI_debug}.so +%{dynload_dir}/_lsprof.%{SOABI_debug}.so +%{dynload_dir}/_lzma.%{SOABI_debug}.so +%{dynload_dir}/_multibytecodec.%{SOABI_debug}.so +%{dynload_dir}/_multiprocessing.%{SOABI_debug}.so +%{dynload_dir}/_opcode.%{SOABI_debug}.so +%{dynload_dir}/_pickle.%{SOABI_debug}.so +%{dynload_dir}/_posixsubprocess.%{SOABI_debug}.so +%{dynload_dir}/_queue.%{SOABI_debug}.so +%{dynload_dir}/_random.%{SOABI_debug}.so +%{dynload_dir}/_socket.%{SOABI_debug}.so +%{dynload_dir}/_sqlite3.%{SOABI_debug}.so +%{dynload_dir}/_ssl.%{SOABI_debug}.so +%{dynload_dir}/_statistics.%{SOABI_debug}.so +%{dynload_dir}/_struct.%{SOABI_debug}.so +%{dynload_dir}/array.%{SOABI_debug}.so +%{dynload_dir}/audioop.%{SOABI_debug}.so +%{dynload_dir}/binascii.%{SOABI_debug}.so +%{dynload_dir}/cmath.%{SOABI_debug}.so +%{dynload_dir}/_datetime.%{SOABI_debug}.so +%{dynload_dir}/fcntl.%{SOABI_debug}.so +%{dynload_dir}/grp.%{SOABI_debug}.so +%{dynload_dir}/math.%{SOABI_debug}.so +%{dynload_dir}/mmap.%{SOABI_debug}.so +%{dynload_dir}/nis.%{SOABI_debug}.so +%{dynload_dir}/ossaudiodev.%{SOABI_debug}.so +%{dynload_dir}/_posixshmem.%{SOABI_debug}.so +%{dynload_dir}/pyexpat.%{SOABI_debug}.so +%{dynload_dir}/readline.%{SOABI_debug}.so +%{dynload_dir}/resource.%{SOABI_debug}.so +%{dynload_dir}/select.%{SOABI_debug}.so +%{dynload_dir}/spwd.%{SOABI_debug}.so +%{dynload_dir}/syslog.%{SOABI_debug}.so +%{dynload_dir}/termios.%{SOABI_debug}.so +%{dynload_dir}/unicodedata.%{SOABI_debug}.so +%{dynload_dir}/_uuid.%{SOABI_debug}.so +%{dynload_dir}/zlib.%{SOABI_debug}.so +%{dynload_dir}/_zoneinfo.%{SOABI_debug}.so +%{_libdir}/%{py_INSTSONAME_debug} +%{pylibdir}/config-%{LDVERSION_debug}-%{platform_triplet} +%{_includedir}/python%{LDVERSION_debug} +%{_bindir}/python%{LDVERSION_debug}-config +%{_bindir}/python%{LDVERSION_debug}-*-config +%{_libdir}/libpython%{LDVERSION_debug}.so +%{_libdir}/libpython%{LDVERSION_debug}.so.%{py_SOVERSION} +%{_libdir}/pkgconfig/python-%{LDVERSION_debug}.pc +%{_libdir}/pkgconfig/python-%{LDVERSION_debug}-embed.pc +%{dynload_dir}/_tkinter.%{SOABI_debug}.so +%{dynload_dir}/_ctypes_test.%{SOABI_debug}.so +%{dynload_dir}/_testbuffer.%{SOABI_debug}.so +%{dynload_dir}/_testcapi.%{SOABI_debug}.so +%{dynload_dir}/_testclinic.%{SOABI_debug}.so +%{dynload_dir}/_testimportmultiple.%{SOABI_debug}.so +%{dynload_dir}/_testinternalcapi.%{SOABI_debug}.so +%{dynload_dir}/_testmultiphase.%{SOABI_debug}.so +%{dynload_dir}/_testsinglephase.%{SOABI_debug}.so +%{dynload_dir}/_xxinterpchannels.%{SOABI_debug}.so +%{dynload_dir}/_xxsubinterpreters.%{SOABI_debug}.so +%{dynload_dir}/_xxtestfuzz.%{SOABI_debug}.so +%{dynload_dir}/xxlimited.%{SOABI_debug}.so +%{dynload_dir}/xxlimited_35.%{SOABI_debug}.so +%{dynload_dir}/xxsubtype.%{SOABI_debug}.so + +%{pylibdir}/_sysconfigdata_%{ABIFLAGS_debug}_linux_%{platform_triplet}.py +%{pylibdir}/__pycache__/_sysconfigdata_%{ABIFLAGS_debug}_linux_%{platform_triplet}%{bytecode_suffixes} +%endif + +%if %{with main_python} +%files -n python-unversioned-command +%{_bindir}/python +%{_mandir}/*/python.1* +%endif + +%changelog +* Wed Apr 10 2024 Shuo Wang - 3.12.2-1 +- init build python3.12 + diff --git a/sources b/sources new file mode 100644 index 0000000000000000000000000000000000000000..c9cf414edfd716ac3309aa806871539c0013c0b7 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (Python-3.12.2.tar.xz) = 2ccfae7b9f95d8e15ea85d3f66eea5f6a8fdcaffc0b405095fecb33efc0df50b831c1215542910ced948b54e6de1f7242b0b8b9afc5f89079451c552430d7d9f