Содержание страницы
7. Использование Python на iOS¶Using Python on iOS
- Авторы:
Russell Keith-Magee (2024-03)
Python на iOS отличается от Python на настольных платформах. На настольной платформе Python обычно устанавливается как системный ресурс, доступный любому пользователю этого компьютера. Пользователи взаимодействуют с Python, запуская python и вводя команды в интерактивном режиме или запуская сценарии Python.
В iOS нет понятия установки как системного ресурса. Единственная единица распространения программного обеспечения – это «приложение». Также здесь нет консоли, в которой можно запустить python или работать с REPL Python.
Поэтому единственный способ использовать Python на iOS – это встраивание: нужно
написать нативное приложение iOS и встроить в него интерпретатор Python
с помощью libPython, а затем вызывать код Python через API встраивания Python. Полный интерпретатор Python, стандартная библиотека и весь
ваш код Python упаковываются в отдельный пакет, который можно
распространять через App Store.
Если вы впервые пробуете написать приложение для iOS на Python, такие проекты, как BeeWare и Kivy, предоставят гораздо более простой пользовательский опыт. Они берут на себя все сложности запуска проекта под iOS, так что вам нужно работать только с кодом Python.
7.1. Python во время выполнения на iOS¶Python at runtime on iOS
7.1.1. Совместимость с версиями iOS¶iOS version compatibility
Минимальная поддерживаемая версия iOS задаётся на этапе компиляции с помощью
параметра --host для configure. По умолчанию при сборке для iOS
Python компилируется с минимальной поддерживаемой версией 13.0. Чтобы использовать
другую минимальную версию iOS, укажите номер версии в аргументе
--host – например,
--host=arm64-apple-ios15.4-simulator соберёт сборку для симулятора ARM64
с целевой версией 15.4.
7.1.2. Определение платформы¶Platform identification
При выполнении на iOS sys.platform возвращает ios. Это значение
возвращается на iPhone или iPad независимо от того, запущено ли приложение на
симуляторе или физическом устройстве.
Информацию о конкретной среде выполнения, включая версию iOS,
модель устройства и то, является ли оно симулятором, можно получить с помощью
platform.ios_ver(). platform.system() вернёт iOS или
iPadOS в зависимости от устройства.
os.uname() сообщает детали на уровне ядра; он вернёт имя
Darwin.
7.1.3. Доступность стандартной библиотеки¶Standard library availability
Стандартная библиотека Python имеет некоторые заметные пропуски и ограничения на iOS. Подробнее см. в руководстве по доступности API для iOS.
7.1.4. Двоичные модули расширения¶Binary extension modules
Одно из ключевых отличий iOS как платформы – распространение через App Store налагает жёсткие требования к упаковке приложения. Одно из них касается того, как распространяются двоичные модули расширения.
App Store требует, чтобы все двоичные модули в приложении iOS были
динамическими библиотеками, помещёнными во фреймворк с соответствующими метаданными и расположенными
в папке Frameworks упакованного приложения. Во фреймворке может быть только один
двоичный файл, и никаких исполняемых двоичных материалов за пределами
папки Frameworks быть не должно.
Это противоречит обычному подходу Python к распространению двоичных файлов,
который позволяет загружать двоичный модуль расширения из любого места на
sys.path. Чтобы соответствовать политикам App Store, проект iOS должен
постобработать все пакеты Python, преобразовав двоичные модули .so в
отдельные автономные фреймворки с соответствующими метаданными и подписью.
Подробнее о том, как выполнить эту постобработку, см. в руководстве по добавлению
Python в ваш проект.
Чтобы помочь Python находить двоичные файлы в новом расположении, исходный файл .so
на sys.path заменяется файлом .fwork. Этот файл – текстовый
файл, содержащий путь к фреймворку относительно пакета приложения. Чтобы
фреймворк мог разрешать обратно исходное расположение, он должен содержать
файл .origin с путём к
файлу .fwork относительно пакета приложения.
Например, рассмотрим случай импорта from foo.bar import _whiz,
где _whiz реализован двоичным модулем
sources/foo/bar/_whiz.abi3.so, а sources – это расположение,
зарегистрированное на sys.path, относительно пакета приложения. Этот модуль
должен распространяться как Frameworks/foo.bar._whiz.framework/foo.bar._whiz
(название фреймворка образуется из полного пути импорта модуля), с
файлом Info.plist в каталоге .framework, идентифицирующим двоичный файл как
фреймворк. Модуль foo.bar._whiz будет представлен в исходном
расположении маркерным файлом sources/foo/bar/_whiz.abi3.fwork, содержащим
путь Frameworks/foo.bar._whiz/foo.bar._whiz. Фреймворк также будет
содержать Frameworks/foo.bar._whiz.framework/foo.bar._whiz.origin с
путём к файлу .fwork.
При запуске на iOS интерпретатор Python устанавливает
AppleFrameworkLoader, который умеет читать и
импортировать файлы .fwork. После импорта атрибут __file__
двоичного модуля будет указывать на расположение файла .fwork. Однако
ModuleSpec для загруженного модуля будет сообщать
origin как расположение двоичного файла в папке фреймворка.
7.1.5. Компиляторные заглушки¶Compiler stub binaries
Xcode не предоставляет явные компиляторы для iOS; вместо этого он использует
скрипт xcrun, который разрешается в полный путь к компилятору (например,
xcrun --sdk iphoneos
clang для получения clang для устройства iPhone). Однако использование этого скрипта
создаёт две проблемы:
Вывод команды
xcrunсодержит пути, зависящие от конкретной машины, из-за чего модуль sysconfig невозможно использовать совместно между пользователями;Это приводит к определениям
CC/CPP/LD/AR, содержащим пробелы. Многие инструменты экосистемы C предполагают, что можно разделить командную строку по первому пробелу, чтобы получить путь к исполняемому файлу компилятора; при использованииxcrunэто не так.
Чтобы избежать этих проблем, Python предоставляет заглушки для этих инструментов. Эти заглушки представляют собой
обёртки в виде сценариев оболочки вокруг базовых инструментов xcrun,
распространяемые в папке bin вместе со скомпилированным фреймворком iOS. Эти сценарии
являются перемещаемыми и всегда разрешаются в соответствующие локальные системные пути.
Включение этих сценариев в папку bin, сопровождающую фреймворк, делает содержимое
модуля sysconfig полезным для конечных пользователей, собирающих
собственные модули. При компиляции сторонних модулей Python для iOS
убедитесь, что эти заглушки находятся в вашем PATH.
7.2. Установка Python на iOS¶Installing Python on iOS
7.2.1. Инструменты для сборки iOS-приложений¶Tools for building iOS apps
Сборка для iOS требует использования инструментов Apple Xcode. Настоятельно рекомендуется использовать самую последнюю стабильную версию Xcode. Для этого потребуется самая последняя (или предпоследняя) версия macOS, так как Apple не поддерживает Xcode для старых версий macOS. Для разработки под iOS недостаточно утилит Xcode Command Line Tools; необходима полная установка Xcode.
Если вы хотите запустить свой код на симуляторе iOS, вам также потребуется установить платформу симулятора iOS. При первом запуске Xcode вам будет предложено выбрать платформу симулятора iOS. Кроме того, платформу симулятора iOS можно добавить, выбрав её на вкладке Platforms панели настроек Xcode.
7.2.2. Добавление Python в проект iOS¶Adding Python to an iOS project
Python можно добавить в любой проект iOS, используя Swift или Objective C. В следующих примерах будет использоваться Objective C; если вы используете Swift, вам может пригодиться библиотека вроде PythonKit.
Чтобы добавить Python в проект iOS Xcode:
Соберите или получите Python
XCFramework. См. инструкции в Apple/iOS/README.md (в дистрибутиве исходных кодов CPython) для подробностей о том, как собрать PythonXCFramework. Как минимум потребуется сборка, поддерживающаяarm64-apple-ios, а также один изarm64-apple-ios-simulatorилиx86_64-apple-ios-simulator.Перетащите
XCframeworkв свой проект iOS. В следующих инструкциях мы будем считать, что вы поместилиXCframeworkв корень проекта; однако вы можете использовать любое другое расположение, скорректировав пути по необходимости.Добавьте код вашего приложения как папку в проект Xcode. В следующих инструкциях мы будем считать, что ваш пользовательский код находится в папке с именем
appв корне проекта; вы можете использовать любое другое расположение, скорректировав пути по необходимости. Убедитесь, что эта папка связана с вашей целью приложения.Выберите цель приложения, выбрав корневой узел вашего проекта Xcode, а затем имя цели на появившейся боковой панели.
В настройках “General”, в разделе “Frameworks, Libraries and Embedded Content”, добавьте
Python.xcframework, выбрав “Embed & Sign”.На вкладке “Build Settings” измените следующие параметры:
Параметры сборки
User Script Sandboxing: Нет
Enable Testability: Да
Пути поиска
Пути поиска фреймворков:
$(PROJECT_DIR)Пути поиска заголовков:
"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"
Apple Clang - Предупреждения - Все языки
Quoted Include In Framework Header: Нет
Добавьте шаг сборки, который обрабатывает стандартную библиотеку Python и ваши собственные бинарные зависимости Python. На вкладке “Build Phases” добавьте новый шаг сборки “Run Script” до шага “Embed Frameworks”, но после шага “Copy Bundle Resources”. Назовите шаг “Process Python libraries”, отключите флажок “Based on dependency analysis” и задайте содержимое скрипта:
set -e source $PROJECT_DIR/Python.xcframework/build/build_utils.sh install_python Python.xcframework app
Если вы разместили ваш XCframework не в корне проекта, измените путь в первом аргументе.
Добавьте код на Objective C для инициализации и использования интерпретатора Python во встроенном режиме. Следует убедиться, что:
Режим UTF-8 (
PyPreConfig.utf8_mode) включён;Буферизированный stdio (
PyConfig.buffered_stdio) отключён;Запись байт-кода (
PyConfig.write_bytecode) отключена;Обработчики сигналов (
PyConfig.install_signal_handlers) включены;Системное журналирование (
PyConfig.use_system_logger) включено (необязательно, но настоятельно рекомендуется; включено по умолчанию);PYTHONHOMEдля интерпретатора настроены указывать на подпапкуpythonпакета вашего приложения; иPYTHONPATHдля интерпретатора включает:подпапку
python/lib/python3.Xпакета вашего приложения,подпапка
python/lib/python3.X/lib-dynloadв составе пакета приложения иподпапка
appв составе пакета приложения
Расположение пакета вашего приложения можно определить с помощью
[[NSBundle mainBundle] resourcePath].
Шаги 7 и 8 данной инструкции предполагают, что у вас есть одна папка с чистым кодом приложения на Python, названная app. Если в вашем приложении есть сторонние бинарные модули, потребуются дополнительные действия:
Необходимо убедиться, что все папки, содержащие сторонние бинарные файлы, либо связаны с целевой платформой приложения, либо явно скопированы на шаге 7. На шаге 7 также следует удалить все бинарные файлы, не подходящие для платформы, под которую собирается конкретная сборка (например, удалить бинарные файлы для устройства, если собирается приложение для симулятора).
Если вы используете отдельную папку для сторонних пакетов, убедитесь, что эта папка добавлена в конец вызова
install_pythonна шаге 7, а также как часть конфигурацииPYTHONPATHна шаге 8.Если какая-либо из папок, содержащих сторонние пакеты, будет содержать файлы
.pth, следует добавить эту папку как каталог сайта (с помощьюsite.addsitedir()), а не добавлять напрямую кPYTHONPATHилиsys.path.
7.2.3. Тестирование пакета Python¶Testing a Python package
Дерево исходных кодов CPython содержит тестовый проект, который используется для запуска тестового набора CPython в симуляторе iOS. Этот тестовый проект также можно использовать для запуска тестов вашей библиотеки Python на iOS.
После сборки или получения XCFramework для iOS (см. Apple/iOS/README.md для подробностей), создайте клон тестового проекта Python для iOS. Если вы использовали скрипт сборки Apple для создания XCFramework, можно выполнить:
$ python cross-build/iOS/testbed clone --app <path/to/module1> --app <path/to/module2> app-testbed
Или, если вы получили свой собственный XCFramework, выполнив:
$ python Apple/testbed clone --platform iOS --framework <path/to/Python.xcframework> --app <path/to/module1> --app <path/to/module2> app-testbed
Все папки, указанные с помощью флага --app, будут скопированы в клонированный тестовый проект. Полученный тестовый проект будет создан в папке app-testbed. В этом примере module1 и module2 будут импортируемыми модулями во время выполнения. Если у вашего проекта есть дополнительные зависимости, их можно установить в папку app-testbed/Testbed/app_packages (используя pip
install --target app-testbed/Testbed/app_packages или аналогичный инструмент).
Затем можно использовать папку app-testbed для запуска тестового набора вашего приложения. Например, если module1.tests была точкой входа вашего тестового набора, можно выполнить:
$ python app-testbed run -- module1.tests
Это эквивалентно запуску python -m module1.tests в настольной сборке Python. Все аргументы после -- будут переданы тестовому проекту так, как если бы они были аргументами для python -m на настольной машине.
Тестовый проект также можно открыть в Xcode, выполнив:
$ open app-testbed/iOSTestbed.xcodeproj
Это позволит использовать полный набор инструментов Xcode для отладки.
Аргументы, используемые для запуска тестового набора, определяются в рамках тестового плана. Чтобы изменить тестовый план, выберите узел тестового плана в дереве проекта (это должен быть первый дочерний элемент корневого узла) и выберите вкладку «Configurations». Измените значение «Arguments Passed On Launch», чтобы изменить аргументы тестирования.
Тестовый план также отключает параллельное тестирование и предписывает использование файла Testbed.lldbinit для настройки отладчика. Конфигурация отладчика по умолчанию отключает автоматические точки останова на сигналы SIGINT, SIGUSR1, SIGUSR2 и SIGXFSZ.
7.3. Соответствие требованиям App Store¶App Store Compliance
Единственный механизм распространения приложений на сторонние устройства iOS – это отправка приложения в App Store; приложения, отправленные на распространение, должны пройти процесс проверки приложений Apple. Этот процесс включает набор автоматических правил проверки, которые анализируют отправленный пакет приложения на наличие проблемного кода. Необходимо предпринять некоторые шаги, чтобы ваше приложение могло пройти эти проверки.
7.3.1. Несовместимый код в стандартной библиотеке¶Incompatible code in the standard library
Стандартная библиотека Python содержит код, который, как известно, нарушает эти автоматические правила. Хотя эти нарушения кажутся ложными срабатываниями, правила проверки Apple не подлежат обжалованию; поэтому необходимо модифицировать стандартную библиотеку Python, чтобы приложение прошло проверку App Store.
Дерево исходных кодов Python содержит файл исправлений, который удалит весь код, вызывающий проблемы в процессе проверки App Store. Это исправление применяется автоматически при сборке для iOS.
7.3.2. Манифесты конфиденциальности¶Privacy manifests
В апреле 2025 года Apple ввела требование для определенных сторонних библиотек предоставлять манифест конфиденциальности. В результате, если у вас есть бинарный модуль, использующий одну из затронутых библиотек, вы должны предоставить файл .xcprivacy для этой библиотеки. OpenSSL – одна из библиотек, на которые распространяется это требование, но есть и другие.
Если вы создаете бинарный модуль с именем mymodule.so и используете скрипт сборки Xcode, описанный на шаге 7 выше, вы можете разместить файл mymodule.xcprivacy рядом с mymodule.so, и манифест конфиденциальности будет установлен в требуемое место при преобразовании бинарного модуля в framework.