Skip to content

Test infrastructure upgrade to 3.13.2 #5517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Lib/importlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@
# Fully bootstrapped at this point, import whatever you like, circular
# dependencies and startup overhead minimisation permitting :)

import warnings


# Public API #########################################################

Expand Down Expand Up @@ -105,7 +103,7 @@ def reload(module):
try:
name = module.__name__
except AttributeError:
raise TypeError("reload() argument must be a module")
raise TypeError("reload() argument must be a module") from None

if sys.modules.get(name) is not module:
raise ImportError(f"module {name} not in sys.modules", name=name)
Expand Down
17 changes: 11 additions & 6 deletions Lib/importlib/_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _new_module(name):

# For a list that can have a weakref to it.
class _List(list):
pass
__slots__ = ("__weakref__",)


# Copied from weakref.py with some simplifications and modifications unique to
Expand Down Expand Up @@ -490,8 +490,7 @@ def _call_with_frames_removed(f, *args, **kwds):

def _verbose_message(message, *args, verbosity=1):
"""Print the message to stderr if -v/PYTHONVERBOSE is turned on."""
# XXX RUSTPYTHON: hasattr check because we might be bootstrapping and we wouldn't have stderr yet
if sys.flags.verbose >= verbosity and hasattr(sys, "stderr"):
if sys.flags.verbose >= verbosity:
if not message.startswith(('#', 'import ')):
message = '# ' + message
print(message.format(*args), file=sys.stderr)
Expand Down Expand Up @@ -825,10 +824,16 @@ def _module_repr_from_spec(spec):
"""Return the repr to use for the module."""
name = '?' if spec.name is None else spec.name
if spec.origin is None:
if spec.loader is None:
loader = spec.loader
if loader is None:
return f'<module {name!r}>'
elif (
_bootstrap_external is not None
and isinstance(loader, _bootstrap_external.NamespaceLoader)
):
return f'<module {name!r} (namespace) from {list(loader._path)}>'
else:
return f'<module {name!r} (namespace) from {list(spec.loader._path)}>'
return f'<module {name!r} ({loader!r})>'
else:
if spec.has_location:
return f'<module {name!r} from {spec.origin!r}>'
Expand Down Expand Up @@ -1129,7 +1134,7 @@ def find_spec(cls, fullname, path=None, target=None):
# part of the importer), instead of here (the finder part).
# The loader is the usual place to get the data that will
# be loaded into the module. (For example, see _LoaderBasics
# in _bootstra_external.py.) Most importantly, this importer
# in _bootstrap_external.py.) Most importantly, this importer
# is simpler if we wait to get the data.
# However, getting as much data in the finder as possible
# to later load the module is okay, and sometimes important.
Expand Down
98 changes: 90 additions & 8 deletions Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

# Bootstrap-related code ######################################################
_CASE_INSENSITIVE_PLATFORMS_STR_KEY = 'win',
_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin'
_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos'
_CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY
+ _CASE_INSENSITIVE_PLATFORMS_STR_KEY)

Expand Down Expand Up @@ -81,6 +81,11 @@ def _pack_uint32(x):
return (int(x) & 0xFFFFFFFF).to_bytes(4, 'little')


def _unpack_uint64(data):
"""Convert 8 bytes in little-endian to an integer."""
assert len(data) == 8
return int.from_bytes(data, 'little')

def _unpack_uint32(data):
"""Convert 4 bytes in little-endian to an integer."""
assert len(data) == 4
Expand Down Expand Up @@ -204,7 +209,11 @@ def _write_atomic(path, data, mode=0o666):
# We first write data to a temporary file, and then use os.replace() to
# perform an atomic rename.
with _io.FileIO(fd, 'wb') as file:
file.write(data)
bytes_written = file.write(data)
if bytes_written != len(data):
# Raise an OSError so the 'except' below cleans up the partially
# written file.
raise OSError("os.write() didn't write the full pyc file")
_os.replace(path_tmp, path)
except OSError:
try:
Expand Down Expand Up @@ -413,6 +422,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.11a7 3492 (make POP_JUMP_IF_NONE/NOT_NONE/TRUE/FALSE relative)
# Python 3.11a7 3493 (Make JUMP_IF_TRUE_OR_POP/JUMP_IF_FALSE_OR_POP relative)
# Python 3.11a7 3494 (New location info table)
# Python 3.11b4 3495 (Set line number of module's RESUME instr to 0 per PEP 626)
# Python 3.12a1 3500 (Remove PRECALL opcode)
# Python 3.12a1 3501 (YIELD_VALUE oparg == stack_depth)
# Python 3.12a1 3502 (LOAD_FAST_CHECK, no NULL-check in LOAD_FAST)
Expand Down Expand Up @@ -445,8 +455,30 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12b1 3529 (Inline list/dict/set comprehensions)
# Python 3.12b1 3530 (Shrink the LOAD_SUPER_ATTR caches)
# Python 3.12b1 3531 (Add PEP 695 changes)

# Python 3.13 will start with 3550
# Python 3.13a1 3550 (Plugin optimizer support)
# Python 3.13a1 3551 (Compact superinstructions)
# Python 3.13a1 3552 (Remove LOAD_FAST__LOAD_CONST and LOAD_CONST__LOAD_FAST)
# Python 3.13a1 3553 (Add SET_FUNCTION_ATTRIBUTE)
# Python 3.13a1 3554 (more efficient bytecodes for f-strings)
# Python 3.13a1 3555 (generate specialized opcodes metadata from bytecodes.c)
# Python 3.13a1 3556 (Convert LOAD_CLOSURE to a pseudo-op)
# Python 3.13a1 3557 (Make the conversion to boolean in jumps explicit)
# Python 3.13a1 3558 (Reorder the stack items for CALL)
# Python 3.13a1 3559 (Generate opcode IDs from bytecodes.c)
# Python 3.13a1 3560 (Add RESUME_CHECK instruction)
# Python 3.13a1 3561 (Add cache entry to branch instructions)
# Python 3.13a1 3562 (Assign opcode IDs for internal ops in separate range)
# Python 3.13a1 3563 (Add CALL_KW and remove KW_NAMES)
# Python 3.13a1 3564 (Removed oparg from YIELD_VALUE, changed oparg values of RESUME)
# Python 3.13a1 3565 (Oparg of YIELD_VALUE indicates whether it is in a yield-from)
# Python 3.13a1 3566 (Emit JUMP_NO_INTERRUPT instead of JUMP for non-loop no-lineno cases)
# Python 3.13a1 3567 (Reimplement line number propagation by the compiler)
# Python 3.13a1 3568 (Change semantics of END_FOR)
# Python 3.13a5 3569 (Specialize CONTAINS_OP)
# Python 3.13a6 3570 (Add __firstlineno__ class attribute)
# Python 3.13b1 3571 (Fix miscompilation of private names in generic classes)

# Python 3.14 will start with 3600

# Please don't copy-paste the same pre-release tag for new entries above!!!
# You should always use the *upcoming* tag. For example, if 3.12a6 came out
Expand All @@ -461,7 +493,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3531).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3571).to_bytes(2, 'little') + b'\r\n'

_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

Expand Down Expand Up @@ -1437,7 +1469,7 @@ class PathFinder:
@staticmethod
def invalidate_caches():
"""Call the invalidate_caches() method on all path entry finders
stored in sys.path_importer_caches (where implemented)."""
stored in sys.path_importer_cache (where implemented)."""
for name, finder in list(sys.path_importer_cache.items()):
# Drop entry if finder name is a relative path. The current
# working directory may have changed.
Expand All @@ -1449,6 +1481,9 @@ def invalidate_caches():
# https://bugs.python.org/issue45703
_NamespacePath._epoch += 1

from importlib.metadata import MetadataPathFinder
MetadataPathFinder.invalidate_caches()

@staticmethod
def _path_hooks(path):
"""Search sys.path_hooks for a finder for 'path'."""
Expand Down Expand Up @@ -1690,6 +1725,46 @@ def __repr__(self):
return f'FileFinder({self.path!r})'


class AppleFrameworkLoader(ExtensionFileLoader):
"""A loader for modules that have been packaged as frameworks for
compatibility with Apple's iOS App Store policies.
"""
def create_module(self, spec):
# If the ModuleSpec has been created by the FileFinder, it will have
# been created with an origin pointing to the .fwork file. We need to
# redirect this to the location in the Frameworks folder, using the
# content of the .fwork file.
if spec.origin.endswith(".fwork"):
with _io.FileIO(spec.origin, 'r') as file:
framework_binary = file.read().decode().strip()
bundle_path = _path_split(sys.executable)[0]
spec.origin = _path_join(bundle_path, framework_binary)

# If the loader is created based on the spec for a loaded module, the
# path will be pointing at the Framework location. If this occurs,
# get the original .fwork location to use as the module's __file__.
if self.path.endswith(".fwork"):
path = self.path
else:
with _io.FileIO(self.path + ".origin", 'r') as file:
origin = file.read().decode().strip()
bundle_path = _path_split(sys.executable)[0]
path = _path_join(bundle_path, origin)

module = _bootstrap._call_with_frames_removed(_imp.create_dynamic, spec)

_bootstrap._verbose_message(
"Apple framework extension module {!r} loaded from {!r} (path {!r})",
spec.name,
spec.origin,
path,
)

# Ensure that the __file__ points at the .fwork location
module.__file__ = path

return module

# Import setup ###############################################################

def _fix_up_module(ns, name, pathname, cpathname=None):
Expand Down Expand Up @@ -1722,10 +1797,17 @@ def _get_supported_file_loaders():

Each item is a tuple (loader, suffixes).
"""
extensions = ExtensionFileLoader, _imp.extension_suffixes()
extension_loaders = []
if hasattr(_imp, 'create_dynamic'):
if sys.platform in {"ios", "tvos", "watchos"}:
extension_loaders = [(AppleFrameworkLoader, [
suffix.replace(".so", ".fwork")
for suffix in _imp.extension_suffixes()
])]
extension_loaders.append((ExtensionFileLoader, _imp.extension_suffixes()))
source = SourceFileLoader, SOURCE_SUFFIXES
bytecode = SourcelessFileLoader, BYTECODE_SUFFIXES
return [extensions, source, bytecode]
return extension_loaders + [source, bytecode]


def _set_bootstrap_module(_bootstrap_module):
Expand Down
6 changes: 5 additions & 1 deletion Lib/importlib/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,11 @@ def get_code(self, fullname):
else:
return self.source_to_code(source, path)

_register(ExecutionLoader, machinery.ExtensionFileLoader)
_register(
ExecutionLoader,
machinery.ExtensionFileLoader,
machinery.AppleFrameworkLoader,
)


class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader):
Expand Down
1 change: 1 addition & 0 deletions Lib/importlib/machinery.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from ._bootstrap_external import SourceFileLoader
from ._bootstrap_external import SourcelessFileLoader
from ._bootstrap_external import ExtensionFileLoader
from ._bootstrap_external import AppleFrameworkLoader
from ._bootstrap_external import NamespaceLoader


Expand Down
Loading