summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/_ios_support.py71
-rwxr-xr-xLib/platform.py53
-rw-r--r--Lib/site.py4
-rw-r--r--Lib/sysconfig/__init__.py20
-rw-r--r--Lib/test/pythoninfo.py1
-rw-r--r--Lib/test/test_concurrent_futures/test_thread_pool.py1
-rw-r--r--Lib/test/test_gc.py1
-rw-r--r--Lib/test/test_platform.py69
-rw-r--r--Lib/test/test_sysconfig.py15
-rw-r--r--Lib/test/test_webbrowser.py83
-rwxr-xr-xLib/webbrowser.py67
11 files changed, 367 insertions, 18 deletions
diff --git a/Lib/_ios_support.py b/Lib/_ios_support.py
new file mode 100644
index 0000000..db3fe23
--- /dev/null
+++ b/Lib/_ios_support.py
@@ -0,0 +1,71 @@
+import sys
+try:
+ from ctypes import cdll, c_void_p, c_char_p, util
+except ImportError:
+ # ctypes is an optional module. If it's not present, we're limited in what
+ # we can tell about the system, but we don't want to prevent the module
+ # from working.
+ print("ctypes isn't available; iOS system calls will not be available")
+ objc = None
+else:
+ # ctypes is available. Load the ObjC library, and wrap the objc_getClass,
+ # sel_registerName methods
+ lib = util.find_library("objc")
+ if lib is None:
+ # Failed to load the objc library
+ raise RuntimeError("ObjC runtime library couldn't be loaded")
+
+ objc = cdll.LoadLibrary(lib)
+ objc.objc_getClass.restype = c_void_p
+ objc.objc_getClass.argtypes = [c_char_p]
+ objc.sel_registerName.restype = c_void_p
+ objc.sel_registerName.argtypes = [c_char_p]
+
+
+def get_platform_ios():
+ # Determine if this is a simulator using the multiarch value
+ is_simulator = sys.implementation._multiarch.endswith("simulator")
+
+ # We can't use ctypes; abort
+ if not objc:
+ return None
+
+ # Most of the methods return ObjC objects
+ objc.objc_msgSend.restype = c_void_p
+ # All the methods used have no arguments.
+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p]
+
+ # Equivalent of:
+ # device = [UIDevice currentDevice]
+ UIDevice = objc.objc_getClass(b"UIDevice")
+ SEL_currentDevice = objc.sel_registerName(b"currentDevice")
+ device = objc.objc_msgSend(UIDevice, SEL_currentDevice)
+
+ # Equivalent of:
+ # device_systemVersion = [device systemVersion]
+ SEL_systemVersion = objc.sel_registerName(b"systemVersion")
+ device_systemVersion = objc.objc_msgSend(device, SEL_systemVersion)
+
+ # Equivalent of:
+ # device_systemName = [device systemName]
+ SEL_systemName = objc.sel_registerName(b"systemName")
+ device_systemName = objc.objc_msgSend(device, SEL_systemName)
+
+ # Equivalent of:
+ # device_model = [device model]
+ SEL_model = objc.sel_registerName(b"model")
+ device_model = objc.objc_msgSend(device, SEL_model)
+
+ # UTF8String returns a const char*;
+ SEL_UTF8String = objc.sel_registerName(b"UTF8String")
+ objc.objc_msgSend.restype = c_char_p
+
+ # Equivalent of:
+ # system = [device_systemName UTF8String]
+ # release = [device_systemVersion UTF8String]
+ # model = [device_model UTF8String]
+ system = objc.objc_msgSend(device_systemName, SEL_UTF8String).decode()
+ release = objc.objc_msgSend(device_systemVersion, SEL_UTF8String).decode()
+ model = objc.objc_msgSend(device_model, SEL_UTF8String).decode()
+
+ return system, release, model, is_simulator
diff --git a/Lib/platform.py b/Lib/platform.py
index df1d987..dbcb636 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -496,6 +496,30 @@ def mac_ver(release='', versioninfo=('', '', ''), machine=''):
# If that also doesn't work return the default values
return release, versioninfo, machine
+
+# A namedtuple for iOS version information.
+IOSVersionInfo = collections.namedtuple(
+ "IOSVersionInfo",
+ ["system", "release", "model", "is_simulator"]
+)
+
+
+def ios_ver(system="", release="", model="", is_simulator=False):
+ """Get iOS 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 == "ios":
+ import _ios_support
+ result = _ios_support.get_platform_ios()
+ if result is not None:
+ return IOSVersionInfo(*result)
+
+ return IOSVersionInfo(system, release, model, is_simulator)
+
+
def _java_getprop(name, default):
"""This private helper is deprecated in 3.13 and will be removed in 3.15"""
from java.lang import System
@@ -654,7 +678,7 @@ def _platform(*args):
if cleaned == platform:
break
platform = cleaned
- while platform[-1] == '-':
+ while platform and platform[-1] == '-':
platform = platform[:-1]
return platform
@@ -695,7 +719,7 @@ def _syscmd_file(target, default=''):
default in case the command should fail.
"""
- if sys.platform in ('dos', 'win32', 'win16'):
+ if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}:
# XXX Others too ?
return default
@@ -859,6 +883,14 @@ class _Processor:
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.
+ def get_ios():
+ if sys.implementation._multiarch.endswith("simulator"):
+ return os.uname().machine
+ return 'arm64'
+
def from_subprocess():
"""
Fall back to `uname -p`
@@ -1018,6 +1050,10 @@ def uname():
system = 'Android'
release = android_ver().release
+ # Normalize responses on iOS
+ if sys.platform == 'ios':
+ system, release, _, _ = ios_ver()
+
vals = system, node, release, version, machine
# Replace 'unknown' values with the more portable ''
_uname_cache = uname_result(*map(_unknown_as_blank, vals))
@@ -1297,11 +1333,14 @@ def platform(aliased=False, terse=False):
system, release, version = system_alias(system, release, version)
if system == 'Darwin':
- # macOS (darwin kernel)
- macos_release = mac_ver()[0]
- if macos_release:
- system = 'macOS'
- release = macos_release
+ # macOS and iOS both report as a "Darwin" kernel
+ if sys.platform == "ios":
+ system, release, _, _ = ios_ver()
+ else:
+ macos_release = mac_ver()[0]
+ if macos_release:
+ system = 'macOS'
+ release = macos_release
if system == 'Windows':
# MS platforms
diff --git a/Lib/site.py b/Lib/site.py
index 2aee63e..162bbec 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -280,8 +280,8 @@ def _getuserbase():
if env_base:
return env_base
- # Emscripten, VxWorks, and WASI have no home directories
- if sys.platform in {"emscripten", "vxworks", "wasi"}:
+ # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
+ if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
return None
def joinuser(*args):
diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py
index 07ab27c..70bdecf 100644
--- a/Lib/sysconfig/__init__.py
+++ b/Lib/sysconfig/__init__.py
@@ -21,6 +21,7 @@ __all__ = [
# Keys for get_config_var() that are never converted to Python integers.
_ALWAYS_STR = {
+ 'IPHONEOS_DEPLOYMENT_TARGET',
'MACOSX_DEPLOYMENT_TARGET',
}
@@ -57,6 +58,7 @@ _INSTALL_SCHEMES = {
'scripts': '{base}/Scripts',
'data': '{base}',
},
+
# Downstream distributors can overwrite the default install scheme.
# This is done to support downstream modifications where distributors change
# the installation layout (eg. different site-packages directory).
@@ -114,8 +116,8 @@ def _getuserbase():
if env_base:
return env_base
- # Emscripten, VxWorks, and WASI have no home directories
- if sys.platform in {"emscripten", "vxworks", "wasi"}:
+ # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
+ if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
return None
def joinuser(*args):
@@ -290,6 +292,7 @@ def _get_preferred_schemes():
'home': 'posix_home',
'user': 'osx_framework_user',
}
+
return {
'prefix': 'posix_prefix',
'home': 'posix_home',
@@ -623,10 +626,15 @@ def get_platform():
if m:
release = m.group()
elif osname[:6] == "darwin":
- import _osx_support
- osname, release, machine = _osx_support.get_platform_osx(
- get_config_vars(),
- osname, release, machine)
+ if sys.platform == "ios":
+ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "12.0")
+ osname = sys.platform
+ machine = sys.implementation._multiarch
+ else:
+ import _osx_support
+ osname, release, machine = _osx_support.get_platform_osx(
+ get_config_vars(),
+ osname, release, machine)
return f"{osname}-{release}-{machine}"
diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
index 5612c55..c8bf16d 100644
--- a/Lib/test/pythoninfo.py
+++ b/Lib/test/pythoninfo.py
@@ -290,6 +290,7 @@ def collect_os(info_add):
"HOMEDRIVE",
"HOMEPATH",
"IDLESTARTUP",
+ "IPHONEOS_DEPLOYMENT_TARGET",
"LANG",
"LDFLAGS",
"LDSHARED",
diff --git a/Lib/test/test_concurrent_futures/test_thread_pool.py b/Lib/test/test_concurrent_futures/test_thread_pool.py
index 5926a63..16043fd 100644
--- a/Lib/test/test_concurrent_futures/test_thread_pool.py
+++ b/Lib/test/test_concurrent_futures/test_thread_pool.py
@@ -49,6 +49,7 @@ class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, BaseTestCase):
self.assertEqual(len(executor._threads), 1)
executor.shutdown(wait=True)
+ @support.requires_fork()
@unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork')
@support.requires_resource('cpu')
def test_hang_global_shutdown_lock(self):
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 3bf5c9e..fa8e50f 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -1223,6 +1223,7 @@ class GCCallbackTests(unittest.TestCase):
self.assertEqual(len(gc.garbage), 0)
+ @requires_subprocess()
@unittest.skipIf(BUILD_WITH_NDEBUG,
'built with -NDEBUG')
def test_refcount_errors(self):
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 57f27b2..40d5fb3 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -10,6 +10,14 @@ from unittest import mock
from test import support
from test.support import os_helper
+try:
+ # Some of the iOS tests need ctypes to operate.
+ # Confirm that the ctypes module is available
+ # is available.
+ import _ctypes
+except ImportError:
+ _ctypes = None
+
FEDORA_OS_RELEASE = """\
NAME=Fedora
VERSION="32 (Thirty Two)"
@@ -228,10 +236,21 @@ class PlatformTest(unittest.TestCase):
if sys.platform == "android":
self.assertEqual(res.system, "Android")
self.assertEqual(res.release, platform.android_ver().release)
+ elif sys.platform == "ios":
+ # Platform module needs ctypes for full operation. If ctypes
+ # isn't available, there's no ObjC module, and dummy values are
+ # returned.
+ if _ctypes:
+ self.assertIn(res.system, {"iOS", "iPadOS"})
+ self.assertEqual(res.release, platform.ios_ver().release)
+ else:
+ self.assertEqual(res.system, "")
+ self.assertEqual(res.release, "")
else:
self.assertEqual(res.system, uname.sysname)
self.assertEqual(res.release, uname.release)
+
@unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
def test_uname_win32_without_wmi(self):
def raises_oserror(*a):
@@ -422,6 +441,56 @@ class PlatformTest(unittest.TestCase):
# parent
support.wait_process(pid, exitcode=0)
+ def test_ios_ver(self):
+ result = platform.ios_ver()
+
+ # ios_ver is only fully available on iOS where ctypes is available.
+ if sys.platform == "ios" and _ctypes:
+ system, release, model, is_simulator = result
+ # Result is a namedtuple
+ self.assertEqual(result.system, system)
+ self.assertEqual(result.release, release)
+ self.assertEqual(result.model, model)
+ self.assertEqual(result.is_simulator, is_simulator)
+
+ # We can't assert specific values without reproducing the logic of
+ # ios_ver(), so we check that the values are broadly what we expect.
+
+ # System is either iOS or iPadOS, depending on the test device
+ self.assertIn(system, {"iOS", "iPadOS"})
+
+ # Release is a numeric version specifier with at least 2 parts
+ parts = release.split(".")
+ self.assertGreaterEqual(len(parts), 2)
+ self.assertTrue(all(part.isdigit() for part in parts))
+
+ # If this is a simulator, we get a high level device descriptor
+ # with no identifying model number. If this is a physical device,
+ # we get a model descriptor like "iPhone13,1"
+ if is_simulator:
+ self.assertIn(model, {"iPhone", "iPad"})
+ else:
+ self.assertTrue(
+ (model.startswith("iPhone") or model.startswith("iPad"))
+ and "," in model
+ )
+
+ self.assertEqual(type(is_simulator), bool)
+ else:
+ # On non-iOS platforms, calling ios_ver doesn't fail; you get
+ # default values
+ self.assertEqual(result.system, "")
+ self.assertEqual(result.release, "")
+ self.assertEqual(result.model, "")
+ self.assertFalse(result.is_simulator)
+
+ # Check the fallback values can be overridden by arguments
+ override = platform.ios_ver("Foo", "Bar", "Whiz", True)
+ self.assertEqual(override.system, "Foo")
+ self.assertEqual(override.release, "Bar")
+ self.assertEqual(override.model, "Whiz")
+ self.assertTrue(override.is_simulator)
+
@unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten")
def test_libc_ver(self):
# check that libc_ver(executable) doesn't raise an exception
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index c8315bb..61c6a5a 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -8,7 +8,11 @@ import shutil
from copy import copy
from test.support import (
- captured_stdout, PythonSymlink, requires_subprocess, is_wasi
+ captured_stdout,
+ is_apple_mobile,
+ is_wasi,
+ PythonSymlink,
+ requires_subprocess,
)
from test.support.import_helper import import_module
from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink,
@@ -346,6 +350,8 @@ class TestSysConfig(unittest.TestCase):
# XXX more platforms to tests here
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
+ @unittest.skipIf(is_apple_mobile,
+ f"{sys.platform} doesn't distribute header files in the runtime environment")
def test_get_config_h_filename(self):
config_h = sysconfig.get_config_h_filename()
self.assertTrue(os.path.isfile(config_h), config_h)
@@ -423,6 +429,9 @@ class TestSysConfig(unittest.TestCase):
self.assertTrue(library.startswith(f'python{major}{minor}'))
self.assertTrue(library.endswith('.dll'))
self.assertEqual(library, ldlibrary)
+ elif is_apple_mobile:
+ framework = sysconfig.get_config_var('PYTHONFRAMEWORK')
+ self.assertEqual(ldlibrary, f"{framework}.framework/{framework}")
else:
self.assertTrue(library.startswith(f'libpython{major}.{minor}'))
self.assertTrue(library.endswith('.a'))
@@ -476,6 +485,8 @@ class TestSysConfig(unittest.TestCase):
self.assertEqual(my_platform, test_platform)
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
+ @unittest.skipIf(is_apple_mobile,
+ f"{sys.platform} doesn't include config folder at runtime")
def test_srcdir(self):
# See Issues #15322, #15364.
srcdir = sysconfig.get_config_var('srcdir')
@@ -556,6 +567,8 @@ class MakefileTests(unittest.TestCase):
@unittest.skipIf(sys.platform.startswith('win'),
'Test is not Windows compatible')
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
+ @unittest.skipIf(is_apple_mobile,
+ f"{sys.platform} doesn't include config folder at runtime")
def test_get_makefile_filename(self):
makefile = sysconfig.get_makefile_filename()
self.assertTrue(os.path.isfile(makefile), makefile)
diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py
index 8c074cb..a1bccb5 100644
--- a/Lib/test/test_webbrowser.py
+++ b/Lib/test/test_webbrowser.py
@@ -5,11 +5,14 @@ import sys
import subprocess
from unittest import mock
from test import support
+from test.support import is_apple_mobile
from test.support import import_helper
from test.support import os_helper
+from test.support import requires_subprocess
+from test.support import threading_helper
-if not support.has_subprocess_support:
- raise unittest.SkipTest("test webserver requires subprocess")
+# The webbrowser module uses threading locks
+threading_helper.requires_working_threading(module=True)
URL = 'https://www.example.com'
CMD_NAME = 'test'
@@ -24,6 +27,7 @@ class PopenMock(mock.MagicMock):
return 0
+@requires_subprocess()
class CommandTestMixin:
def _test(self, meth, *, args=[URL], kw={}, options, arguments):
@@ -219,6 +223,73 @@ class ELinksCommandTest(CommandTestMixin, unittest.TestCase):
arguments=['openURL({},new-tab)'.format(URL)])
+@unittest.skipUnless(sys.platform == "ios", "Test only applicable to iOS")
+class IOSBrowserTest(unittest.TestCase):
+ def _obj_ref(self, *args):
+ # Construct a string representation of the arguments that can be used
+ # as a proxy for object instance references
+ return "|".join(str(a) for a in args)
+
+ @unittest.skipIf(getattr(webbrowser, "objc", None) is None,
+ "iOS Webbrowser tests require ctypes")
+ def setUp(self):
+ # Intercept the the objc library. Wrap the calls to get the
+ # references to classes and selectors to return strings, and
+ # wrap msgSend to return stringified object references
+ self.orig_objc = webbrowser.objc
+
+ webbrowser.objc = mock.Mock()
+ webbrowser.objc.objc_getClass = lambda cls: f"C#{cls.decode()}"
+ webbrowser.objc.sel_registerName = lambda sel: f"S#{sel.decode()}"
+ webbrowser.objc.objc_msgSend.side_effect = self._obj_ref
+
+ def tearDown(self):
+ webbrowser.objc = self.orig_objc
+
+ def _test(self, meth, **kwargs):
+ # The browser always gets focus, there's no concept of separate browser
+ # windows, and there's no API-level control over creating a new tab.
+ # Therefore, all calls to webbrowser are effectively the same.
+ getattr(webbrowser, meth)(URL, **kwargs)
+
+ # The ObjC String version of the URL is created with UTF-8 encoding
+ url_string_args = [
+ "C#NSString",
+ "S#stringWithCString:encoding:",
+ b'https://www.example.com',
+ 4,
+ ]
+ # The NSURL version of the URL is created from that string
+ url_obj_args = [
+ "C#NSURL",
+ "S#URLWithString:",
+ self._obj_ref(*url_string_args),
+ ]
+ # The openURL call is invoked on the shared application
+ shared_app_args = ["C#UIApplication", "S#sharedApplication"]
+
+ # Verify that the last call is the one that opens the URL.
+ webbrowser.objc.objc_msgSend.assert_called_with(
+ self._obj_ref(*shared_app_args),
+ "S#openURL:options:completionHandler:",
+ self._obj_ref(*url_obj_args),
+ None,
+ None
+ )
+
+ def test_open(self):
+ self._test('open')
+
+ def test_open_with_autoraise_false(self):
+ self._test('open', autoraise=False)
+
+ def test_open_new(self):
+ self._test('open_new')
+
+ def test_open_new_tab(self):
+ self._test('open_new_tab')
+
+
class BrowserRegistrationTest(unittest.TestCase):
def setUp(self):
@@ -314,6 +385,10 @@ class ImportTest(unittest.TestCase):
webbrowser.register(name, None, webbrowser.GenericBrowser(name))
webbrowser.get(sys.executable)
+ @unittest.skipIf(
+ is_apple_mobile,
+ "Apple mobile doesn't allow modifying browser with environment"
+ )
def test_environment(self):
webbrowser = import_helper.import_fresh_module('webbrowser')
try:
@@ -325,6 +400,10 @@ class ImportTest(unittest.TestCase):
webbrowser = import_helper.import_fresh_module('webbrowser')
webbrowser.get()
+ @unittest.skipIf(
+ is_apple_mobile,
+ "Apple mobile doesn't allow modifying browser with environment"
+ )
def test_environment_preferred(self):
webbrowser = import_helper.import_fresh_module('webbrowser')
try:
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
index 0424c53..7ef80a8 100755
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -478,6 +478,9 @@ def register_standard_browsers():
# OS X can use below Unix support (but we prefer using the OS X
# specific stuff)
+ if sys.platform == "ios":
+ register("iosbrowser", None, IOSBrowser(), preferred=True)
+
if sys.platform == "serenityos":
# SerenityOS webbrowser, simply called "Browser".
register("Browser", None, BackgroundBrowser("Browser"))
@@ -599,6 +602,70 @@ if sys.platform == 'darwin':
rc = osapipe.close()
return not rc
+#
+# Platform support for iOS
+#
+if sys.platform == "ios":
+ from _ios_support import objc
+ if objc:
+ # If objc exists, we know ctypes is also importable.
+ from ctypes import c_void_p, c_char_p, c_ulong
+
+ class IOSBrowser(BaseBrowser):
+ def open(self, url, new=0, autoraise=True):
+ sys.audit("webbrowser.open", url)
+ # If ctypes isn't available, we can't open a browser
+ if objc is None:
+ return False
+
+ # All the messages in this call return object references.
+ objc.objc_msgSend.restype = c_void_p
+
+ # This is the equivalent of:
+ # NSString url_string =
+ # [NSString stringWithCString:url.encode("utf-8")
+ # encoding:NSUTF8StringEncoding];
+ NSString = objc.objc_getClass(b"NSString")
+ constructor = objc.sel_registerName(b"stringWithCString:encoding:")
+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_char_p, c_ulong]
+ url_string = objc.objc_msgSend(
+ NSString,
+ constructor,
+ url.encode("utf-8"),
+ 4, # NSUTF8StringEncoding = 4
+ )
+
+ # Create an NSURL object representing the URL
+ # This is the equivalent of:
+ # NSURL *nsurl = [NSURL URLWithString:url];
+ NSURL = objc.objc_getClass(b"NSURL")
+ urlWithString_ = objc.sel_registerName(b"URLWithString:")
+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p]
+ ns_url = objc.objc_msgSend(NSURL, urlWithString_, url_string)
+
+ # Get the shared UIApplication instance
+ # This code is the equivalent of:
+ # UIApplication shared_app = [UIApplication sharedApplication]
+ UIApplication = objc.objc_getClass(b"UIApplication")
+ sharedApplication = objc.sel_registerName(b"sharedApplication")
+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p]
+ shared_app = objc.objc_msgSend(UIApplication, sharedApplication)
+
+ # Open the URL on the shared application
+ # This code is the equivalent of:
+ # [shared_app openURL:ns_url
+ # options:NIL
+ # completionHandler:NIL];
+ openURL_ = objc.sel_registerName(b"openURL:options:completionHandler:")
+ objc.objc_msgSend.argtypes = [
+ c_void_p, c_void_p, c_void_p, c_void_p, c_void_p
+ ]
+ # Method returns void
+ objc.objc_msgSend.restype = None
+ objc.objc_msgSend(shared_app, openURL_, ns_url, None, None)
+
+ return True
+
def main():
import getopt