Использование Python в 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.
Если вы впервые хотите попробовать написать приложение iOS на Python, такие проекты, как BeeWare и Kivy, обеспечат гораздо более простой пользовательский опыт. Эти проекты берут на себя сложности, связанные с запуском проекта iOS, поэтому вам нужно будет заниматься только самим кодом Python.
7.1. Python во время выполнения в iOS
7.1.1. Совместимость с версиями iOS
Минимальная поддерживаемая версия iOS задаётся во время компиляции с помощью параметра --host для configure. По умолчанию при компиляции для iOS Python собирается с минимальной поддерживаемой версией iOS 13.0. Чтобы использовать другую минимальную версию iOS, укажите номер версии в аргументе --host — например, --host=arm64-apple-ios15.4-simulator скомпилирует сборку симулятора ARM64 с целевой версией развёртывания 15.4.
7.1.2. Определение платформы
При выполнении в iOS sys.platform возвращает значение ios. Это значение возвращается на iPhone или iPad независимо от того, работает ли приложение в симуляторе или на физическом устройстве.
Информацию о конкретной среде выполнения, включая версию iOS, модель устройства и то, является ли устройство симулятором, можно получить с помощью platform.ios_ver(). platform.system() возвращает iOS или iPadOS в зависимости от устройства.
os.uname() сообщает сведения на уровне ядра; возвращаемое имя — Darwin.
7.1.3. Доступность стандартной библиотеки
Стандартная библиотека Python в iOS имеет ряд существенных ограничений и пропусков. Подробности см. в руководстве по доступности API для iOS.
7.1.4. Бинарные модули расширения
Одно из существенных отличий iOS как платформы состоит в том, что распространение через App Store предъявляет строгие требования к упаковке приложения. Одно из этих требований определяет способ распространения бинарных модулей расширения.
App Store для iOS требует, чтобы все бинарные модули в приложении 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. Заглушки бинарных файлов компилятора
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
7.2.1. Инструменты для сборки приложений iOS
Для сборки приложений iOS требуются инструменты Apple Xcode. Настоятельно рекомендуется использовать последнюю стабильную версию Xcode. Для этого потребуется последняя (или предпоследняя) версия macOS, поскольку Apple не поддерживает Xcode для более старых версий macOS. Инструментов командной строки Xcode недостаточно для разработки под iOS; необходима полная установка Xcode.
Если вы хотите запускать код в симуляторе iOS, также потребуется установить платформу симулятора iOS. При первом запуске Xcode вам будет предложено выбрать платформу симулятора iOS. Кроме того, платформу симулятора iOS можно добавить на вкладке Platforms панели настроек Xcode.
7.2.2. Добавление Python в проект iOS
Python можно добавить в любой проект iOS, используя Swift или Objective-C. В следующих примерах используется Objective-C; если вы используете Swift, вам может пригодиться библиотека вроде PythonKit.
Чтобы добавить Python в проект iOS в Xcode:
- Соберите или получите Python
XCFramework. Подробные инструкции по сборке PythonXCFrameworkсм. в Apple/iOS/README.md (в дистрибутиве исходного кода CPython). Как минимум, вам потребуется сборка с поддержкой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» измените следующие параметры:
-
Параметры сборки
- Изоляция пользовательских скриптов: Нет
- Включить тестируемость: Да
-
Пути поиска
- Пути поиска фреймворков:
$(PROJECT_DIR) - Пути поиска заголовочных файлов:
"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"
- Пути поиска фреймворков:
-
Apple Clang — предупреждения — все языки
- Подключение в кавычках в заголовочном файле фреймворка: Нет
-
-
Добавьте этап сборки, который обрабатывает стандартную библиотеку 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) включён; - буферизация стандартных потоков ввода-вывода (
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]. - режим UTF-8 (
Шаги 7 и 8 этих инструкций предполагают, что у вас есть одна папка с кодом приложения на чистом Python под названием app. Если в приложении есть сторонние бинарные модули, потребуются дополнительные действия:
- Необходимо убедиться, что все папки со сторонними бинарными файлами либо связаны с целью приложения, либо явно копируются на шаге 7. На шаге 7 также следует удалять все бинарные файлы, неподходящие для платформы, на которую нацелена конкретная сборка (например, удалять бинарные файлы для устройства, если вы собираете приложение для симулятора).
- Если для сторонних пакетов используется отдельная папка, убедитесь, что эта папка добавлена в конец вызова
install_pythonна шаге 7, а также в конфигурацию переменнойPYTHONPATHна шаге 8. - Если в каких-либо папках со сторонними пакетами будут находиться файлы
.pth, следует добавить эту папку как каталог site (с помощьюsite.addsitedir()), а не добавлять её непосредственно вPYTHONPATHилиsys.path.
7.2.3. Тестирование пакета Python
Дерево исходного кода CPython содержит тестовый проект, используемый для запуска набора тестов CPython в симуляторе iOS. Этот тестовый проект можно также использовать для запуска набора тестов вашей библиотеки Python в iOS.
После сборки или получения XCFramework для iOS (подробности см. в Apple/iOS/README.md) создайте копию тестового проекта Python для iOS. Если для сборки XCFramework вы использовали скрипт сборки Apple, выполните команду:
$ 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
Единственный способ распространять приложения на сторонние устройства iOS — отправить приложение в App Store для iOS; отправленные для распространения приложения должны пройти проверку Apple. Этот процесс включает набор автоматизированных правил проверки, которые анализируют отправленный пакет приложения на наличие проблемного кода. Чтобы приложение прошло эту проверку, необходимо выполнить несколько действий.
7.3.1. Несовместимый код в стандартной библиотеке
В стандартной библиотеке Python есть код, который, как известно, нарушает эти автоматизированные правила. Хотя нарушения, по-видимому, являются ложными срабатываниями, оспорить правила проверки Apple нельзя; поэтому для прохождения проверки App Store необходимо изменить стандартную библиотеку Python.
В дереве исходного кода Python есть файл исправления, удаляющий весь код, который, как известно, вызывает проблемы при проверке App Store. Это исправление применяется автоматически при сборке для iOS.
7.3.2. Манифесты конфиденциальности
В апреле 2025 года Apple ввела требование, согласно которому некоторые сторонние библиотеки должны предоставлять манифест конфиденциальности. В результате, если у вас есть бинарный модуль, использующий одну из затронутых библиотек, необходимо предоставить файл .xcprivacy для этой библиотеки. Это требование распространяется, в частности, на OpenSSL, а также на другие библиотеки.
Если вы создаёте бинарный модуль с именем mymodule.so и используете скрипт сборки Xcode, описанный выше в шаге 7, можно поместить файл mymodule.xcprivacy рядом с mymodule.so, и при преобразовании бинарного модуля во фреймворк манифест конфиденциальности будет установлен в требуемое расположение.
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/using/ios.html