Skip to content
Closed
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
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Lib/test/data/*
/Makefile
/Makefile.pre
/iOSTestbed.*
/visionOSTestbed.*
iOS/Frameworks/
iOS/Resources/Info.plist
iOS/testbed/build
Expand All @@ -81,6 +82,20 @@ iOS/testbed/Python.xcframework/ios-*/Python.framework
iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace
iOS/testbed/iOSTestbed.xcodeproj/xcuserdata
iOS/testbed/iOSTestbed.xcodeproj/xcshareddata
MacCatalyst/Resources/Info.plist
visionOS/testbed/Python.xcframework/xros-*/bin
visionOS/testbed/Python.xcframework/xros-*/include
visionOS/testbed/Python.xcframework/xros-*/lib
visionOS/testbed/Python.xcframework/xros-*/Python.framework
visionOS/testbed/visionOSTestbed.xcodeproj/project.xcworkspace
visionOS/testbed/visionOSTestbed.xcodeproj/xcuserdata
visionOS/testbed/visionOSTestbed.xcodeproj/xcshareddata
tvOS/Frameworks
tvOS/Resources/Info.plist
watchOS/Frameworks
watchOS/Resources/Info.plist
visionOS/Frameworks
visionOS/Resources/Info.plist
Mac/Makefile
Mac/PythonLauncher/Info.plist
Mac/PythonLauncher/Makefile
Expand Down
3 changes: 2 additions & 1 deletion Lib/_ios_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
def get_platform_ios():
# Determine if this is a simulator using the multiarch value
is_simulator = sys.implementation._multiarch.endswith("simulator")
is_catalyst = sys.implementation._multiarch.endswith("macabi")

# We can't use ctypes; abort
if not objc:
Expand Down Expand Up @@ -68,4 +69,4 @@ def get_platform_ios():
release = objc.objc_msgSend(device_systemVersion, SEL_UTF8String).decode()
model = objc.objc_msgSend(device_model, SEL_UTF8String).decode()

return system, release, model, is_simulator
return system, release, model, is_simulator, is_catalyst
2 changes: 1 addition & 1 deletion Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None,
if name:
name = _os.fspath(name)

# If the filename that has been provided is an iOS/tvOS/watchOS
# If the filename that has been provided is an iOS/tvOS/watchOS/visionOS
# .fwork file, dereference the location to the true origin of the
# binary.
if name.endswith(".fwork"):
Expand Down
4 changes: 2 additions & 2 deletions Lib/ctypes/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def dllist():
if (name := _get_module_filename(h)) is not None]
return libraries

elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos"}:
elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos", "visionos"}:
from ctypes.macholib.dyld import dyld_find as _dyld_find
def find_library(name):
possible = ['lib%s.dylib' % name,
Expand Down Expand Up @@ -425,7 +425,7 @@ def find_library(name):
# https://man.openbsd.org/dl_iterate_phdr
# https://docs.oracle.com/cd/E88353_01/html/E37843/dl-iterate-phdr-3c.html
if (os.name == "posix" and
sys.platform not in {"darwin", "ios", "tvos", "watchos"}):
sys.platform not in {"darwin", "ios", "tvos", "watchos", "visionos"}):
import ctypes
if hasattr((_libc := ctypes.CDLL(None)), "dl_iterate_phdr"):

Expand Down
5 changes: 3 additions & 2 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', 'ios', 'tvos', 'watchos'
_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos', 'visionos'
_CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY
+ _CASE_INSENSITIVE_PLATFORMS_STR_KEY)

Expand Down Expand Up @@ -1535,7 +1535,8 @@ def _get_supported_file_loaders():
"""
extension_loaders = []
if hasattr(_imp, 'create_dynamic'):
if sys.platform in {"ios", "tvos", "watchos"}:
# AppleFrameworkLoader is unessaccary on Mac Catalyst because Mac Catalyst allows "raw" dylib files.
if sys.platform in {"ios", "tvos", "watchos", "visionos"} and not sys.implementation._multiarch.endswith("macabi"):
extension_loaders = [(AppleFrameworkLoader, [
suffix.replace(".so", ".fwork")
for suffix in _imp.extension_suffixes()
Expand Down
125 changes: 114 additions & 11 deletions Lib/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,11 +508,11 @@ def mac_ver(release='', versioninfo=('', '', ''), machine=''):
# A namedtuple for iOS version information.
IOSVersionInfo = collections.namedtuple(
"IOSVersionInfo",
["system", "release", "model", "is_simulator"]
["system", "release", "model", "is_simulator", "is_catalyst"]
)


def ios_ver(system="", release="", model="", is_simulator=False):
def ios_ver(system="", release="", model="", is_simulator=False, is_catalyst=False):
"""Get iOS version information, and return it as a namedtuple:
(system, release, model, is_simulator).

Expand All @@ -525,7 +525,82 @@ def ios_ver(system="", release="", model="", is_simulator=False):
if result is not None:
return IOSVersionInfo(*result)

return IOSVersionInfo(system, release, model, is_simulator)
return IOSVersionInfo(system, release, model, is_simulator, is_catalyst)


# A namedtuple for tvOS version information.
TVOSVersionInfo = collections.namedtuple(
"TVOSVersionInfo",
["system", "release", "model", "is_simulator"]
)


def tvos_ver(system="", release="", model="", is_simulator=False):
"""Get tvOS version information, and return it as a namedtuple:
(system, release, model, is_simulator).

If values can't be determined, they are set to values provided as
parameters.
"""
if sys.platform == "tvos":
# TODO: Can the iOS implementation be used here?
import _ios_support
result = _ios_support.get_platform_ios()
if result is not None:
result = result[:-1] # ignore the Catalyst flag
return TVOSVersionInfo(*result)

return TVOSVersionInfo(system, release, model, is_simulator)


# A namedtuple for watchOS version information.
WatchOSVersionInfo = collections.namedtuple(
"WatchOSVersionInfo",
["system", "release", "model", "is_simulator"]
)


def watchos_ver(system="", release="", model="", is_simulator=False):
"""Get watchOS version information, and return it as a namedtuple:
(system, release, model, is_simulator).

If values can't be determined, they are set to values provided as
parameters.
"""
if sys.platform == "watchos":
# TODO: Can the iOS implementation be used here?
import _ios_support
result = _ios_support.get_platform_ios()
if result is not None:
result = result[:-1] # ignore the Catalyst flag
return WatchOSVersionInfo(*result)

return WatchOSVersionInfo(system, release, model, is_simulator)


# A namedtuple for visionOS version information.
VisionOSVersionInfo = collections.namedtuple(
"VisionOSVersionInfo",
["system", "release", "model", "is_simulator"]
)


def visionos_ver(system="", release="", model="", is_simulator=False):
"""Get visionOS version information, and return it as a namedtuple:
(system, release, model, is_simulator).

If values can't be determined, they are set to values provided as
parameters.
"""
if sys.platform == "visionos":
# TODO: Can the iOS implementation be used here?
import _ios_support
result = _ios_support.get_platform_ios()
if result is not None:
result = result[:-1] # ignore the Catalyst flag
return VisionOSVersionInfo(*result)

return VisionOSVersionInfo(system, release, model, is_simulator)


def _java_getprop(name, default):
Expand Down Expand Up @@ -727,7 +802,7 @@ def _syscmd_file(target, default=''):
default in case the command should fail.

"""
if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}:
if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos', 'visionos'}:
# XXX Others too ?
return default

Expand Down Expand Up @@ -891,10 +966,26 @@ def get_OpenVMS():
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
return 'Alpha' if cpu_number >= 128 else 'VAX'

# On the iOS simulator, os.uname returns the architecture as uname.machine.
# On device it returns the model name for some reason; but there's only one
# CPU architecture for iOS devices, so we know the right answer.
# On the iOS/tvOS/watchOS/visionOS simulator and Mac Catalyst, os.uname returns
# the architecture as uname.machine. On device it returns the model name for
# some reason; but there's only one CPU architecture for devices, so we know the
# right answer.
def get_ios():
if sys.implementation._multiarch.endswith("simulator") or sys.implementation._multiarch.endswith("macabi"):
return os.uname().machine
return 'arm64'

def get_tvos():
if sys.implementation._multiarch.endswith("simulator"):
return os.uname().machine
return 'arm64'

def get_watchos():
if sys.implementation._multiarch.endswith("simulator"):
return os.uname().machine
return 'arm64_32'

def get_visionos():
if sys.implementation._multiarch.endswith("simulator"):
return os.uname().machine
return 'arm64'
Expand Down Expand Up @@ -1058,9 +1149,15 @@ def uname():
system = 'Android'
release = android_ver().release

# Normalize responses on iOS
# Normalize responses on Apple mobile platforms
if sys.platform == 'ios':
system, release, _, _ = ios_ver()
system, release, _, _, _ = ios_ver()
if sys.platform == 'tvos':
system, release, _, _ = tvos_ver()
if sys.platform == 'watchos':
system, release, _, _ = watchos_ver()
if sys.platform == 'visionos':
system, release, _, _ = visionos_ver()

vals = system, node, release, version, machine
# Replace 'unknown' values with the more portable ''
Expand Down Expand Up @@ -1347,9 +1444,15 @@ def platform(aliased=False, terse=False):
system, release, version = system_alias(system, release, version)

if system == 'Darwin':
# macOS and iOS both report as a "Darwin" kernel
# All Apple Platforms report as a "Darwin" kernel
if sys.platform == "ios":
system, release, _, _ = ios_ver()
system, release, _, _, _ = ios_ver()
elif sys.platform == "tvos":
system, release, _, _ = tvos_ver()
elif sys.platform == "watchos":
system, release, _, _ = watchos_ver()
elif sys.platform == "visionos":
system, release, _, _ = visionos_ver()
else:
macos_release = mac_ver()[0]
if macos_release:
Expand Down
4 changes: 2 additions & 2 deletions Lib/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ def _getuserbase():
if env_base:
return env_base

# Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
# Emscripten, iOS, tvOS, visionOS, VxWorks, WASI, and watchOS have no home directories
if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "visionos", "wasi", "watchos"}:
return None

def joinuser(*args):
Expand Down
5 changes: 4 additions & 1 deletion Lib/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@
_mswindows = True

# some platforms do not support subprocesses
_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"}
_can_fork_exec = (
sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"}
or sys.implementation._multiarch.endswith("macabi") # Mac Catalyst
)

if _mswindows:
import _winapi
Expand Down
17 changes: 16 additions & 1 deletion Lib/sysconfig/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
_ALWAYS_STR = {
'IPHONEOS_DEPLOYMENT_TARGET',
'MACOSX_DEPLOYMENT_TARGET',
'TVOS_DEPLOYMENT_TARGET',
'WATCHOS_DEPLOYMENT_TARGET',
'XROS_DEPLOYMENT_TARGET',
}

_INSTALL_SCHEMES = {
Expand Down Expand Up @@ -119,7 +122,7 @@ def _getuserbase():
# Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories.
# Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling.
system_name = os.environ.get('_PYTHON_HOST_PLATFORM', sys.platform).split('-')[0]
if system_name in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
if system_name in {"emscripten", "ios", "tvos", "visionos", "vxworks", "wasi", "watchos"}:
return None

def joinuser(*args):
Expand Down Expand Up @@ -730,6 +733,18 @@ def get_platform():
release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
osname = sys.platform
machine = sys.implementation._multiarch
elif sys.platform == "tvos":
release = get_config_vars().get("TVOS_DEPLOYMENT_TARGET", "9.0")
osname = sys.platform
machine = sys.implementation._multiarch
elif sys.platform == "watchos":
release = get_config_vars().get("WATCHOS_DEPLOYMENT_TARGET", "4.0")
osname = sys.platform
machine = sys.implementation._multiarch
elif sys.platform == "visionos":
release = get_config_vars().get("XROS_DEPLOYMENT_TARGET", "2.0")
osname = sys.platform
machine = sys.implementation._multiarch
else:
import _osx_support
osname, release, machine = _osx_support.get_platform_osx(
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -7159,9 +7159,9 @@ def test_datetime_from_timestamp(self):
self.assertEqual(dt_orig, dt_rt)

def test_type_check_in_subinterp(self):
# iOS requires the use of the custom framework loader,
# not the ExtensionFileLoader.
if sys.platform == "ios":
# Apple mobile platforms except Mac Catalyst require the use of the
# custom framework loader, not the ExtensionFileLoader.
if support.needs_apple_fworks:
extension_loader = "AppleFrameworkLoader"
else:
extension_loader = "ExtensionFileLoader"
Expand Down
13 changes: 11 additions & 2 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
# sys
"MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi",
"is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval",
"is_mac_catalyst", "needs_apple_fworks",
# os
"get_pagesize",
# network
Expand Down Expand Up @@ -558,7 +559,7 @@ def skip_android_selinux(name):
sys.platform == "android", f"Android blocks {name} with SELinux"
)

if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos"}:
if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos", "visionos"}:
unix_shell = '/system/bin/sh' if is_android else '/bin/sh'
else:
unix_shell = None
Expand All @@ -574,8 +575,10 @@ def skip_emscripten_stack_overflow():
def skip_wasi_stack_overflow():
return unittest.skipIf(is_wasi, "Exhausts stack on WASI")

is_apple_mobile = sys.platform in {"ios", "tvos", "watchos"}
is_apple_mobile = sys.platform in {"ios", "tvos", "watchos", "visionos"}
is_apple = is_apple_mobile or sys.platform == "darwin"
is_mac_catalyst = sys.implementation._multiarch.endswith("macabi")
needs_apple_fworks = is_apple_mobile and not is_mac_catalyst

has_fork_support = hasattr(os, "fork") and not (
# WASM and Apple mobile platforms do not support subprocesses.
Expand All @@ -586,6 +589,9 @@ def skip_wasi_stack_overflow():
# Although Android supports fork, it's unsafe to call it from Python because
# all Android apps are multi-threaded.
or is_android

# Mac Catalyst supports subprocesses.
and not is_mac_catalyst
)

def requires_fork():
Expand All @@ -601,6 +607,9 @@ def requires_fork():
# practice (see PEP 738). And most of the tests that use them are calling
# sys.executable, which won't work when Python is embedded in an Android app.
or is_android

# Mac Catalyst supports subprocesses.
and not is_mac_catalyst
)

def requires_subprocess():
Expand Down
Loading