Spec-Zone.ru › Python 3.13

Использование Python на iOS

Авторы:

Расселл Кит-Мэджи (2024-03)

Python на iOS отличается от Python на настольных платформах. На настольной платформе Python обычно устанавливается как системный ресурс, доступный любому пользователю компьютера. Затем пользователи взаимодействуют с Python, запуская исполняемый файл python и вводя команды в интерактивную оболочку или выполняя скрипт Python.

На iOS нет понятия установки в качестве системного ресурса. Единственная единица программного распространения — это «приложение». Также нет консоли, где можно запустить исполняемый файл python или взаимодействовать с интерактивной оболочкой 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 необходимо убедиться, что эти бинарные файлы-заглушки находятся на вашем пути.

7.2. Установка Python на iOS

7.2.1. Инструменты для разработки приложений для iOS

Для разработки приложений для iOS требуется использование инструментов Xcode от Apple. Сильно рекомендуется использовать последнюю стабильную версию 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 в проект Xcode для iOS:

  1. Соберите или получите дистрибутив Python. Смотрите инструкции в iOS/README.rst (в дистрибутиве исходного кода CPython) для получения подробной информации о сборке дистрибутива Python. Как минимум, вам потребуется сборка, поддерживающая arm64-apple-ios, плюс один из arm64-apple-ios-simulator или x86_64-apple-ios-simulator.
  2. Перетащите дистрибутив Python в свой проект iOS. В следующих инструкциях мы будем предполагать, что вы поместили дистрибутив в корень вашего проекта; однако, вы можете использовать любое другое местоположение, корректируя пути по мере необходимости.
  3. Перетащите файл iOS/Resources/dylib-Info-template.plist в свой проект и убедитесь, что он связан с целевым приложением.
  4. Добавьте код вашего приложения в виде папки в свой проект Xcode. В следующих инструкциях мы будем предполагать, что ваш пользовательский код находится в папке app в корне вашего проекта; вы можете использовать любое другое местоположение, скорректировав пути по мере необходимости. Убедитесь, что эта папка связана с целевым приложением.
  5. Выберите целевое приложение, выбрав корневой узел вашего проекта Xcode, а затем имя целевого приложения в появившемся боковом меню.
  6. В настройках «General» в разделе «Frameworks, Libraries and Embedded Content» добавьте Python.xcframework, выбрав «Embed & Sign».
  7. В вкладке «Build Settings» внесите следующие изменения:

    • Настройки сборки

      • User Script Sandboxing: Нет
      • Enable Testability: Да
    • Пути поиска

      • Framework Search Paths: $(PROJECT_DIR)
      • Header Search Paths: "$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"
    • Apple Clang - Предупреждения - Все языки

      • Quoted Include In Framework Header: Нет
  8. Добавьте этап сборки, копирующий стандартную библиотеку Python в ваше приложение. Во вкладке «Build Phases» добавьте новый этап «Run Script» перед этапом «Embed Frameworks», но после этапа «Copy Bundle Resources». Назовите этап «Install Target Specific Python Standard Library», отключите флажок «Based on dependency analysis» и задайте содержимое скрипта:

    set -e
    
    mkdir -p "$CODESIGNING_FOLDER_PATH/python/lib"
    if [ "$EFFECTIVE_PLATFORM_NAME" = "-iphonesimulator" ]; then
        echo "Installing Python modules for iOS Simulator"
        rsync -au --delete "$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/"
    else
        echo "Installing Python modules for iOS Device"
        rsync -au --delete "$PROJECT_DIR/Python.xcframework/ios-arm64/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/"
    fi
    

    Обратите внимание, что имя «фрагмента» симулятора в XCframework может отличаться в зависимости от архитектуры процессора, которую поддерживает ваш XCFramework.

  9. Добавьте второй этап сборки, обрабатывающий модули бинарных расширений в стандартной библиотеке в формате «Framework». Добавьте этап «Run Script» непосредственно после этапа, добавленного в шаге 8, с именем «Prepare Python Binary Modules». Он также должен иметь отключенный флажок «Based on dependency analysis» со следующим содержимым скрипта:

    set -e
    
    install_dylib () {
        INSTALL_BASE=$1
        FULL_EXT=$2
    
        # The name of the extension file
        EXT=$(basename "$FULL_EXT")
        # The location of the extension file, relative to the bundle
        RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/}
        # The path to the extension file, relative to the install base
        PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}
        # The full dotted name of the extension module, constructed from the file path.
        FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d "." -f 1 | tr "/" ".");
        # A bundle identifier; not actually used, but required by Xcode framework packaging
        FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr "_" "-")
        # The name of the framework folder.
        FRAMEWORK_FOLDER="Frameworks/$FULL_MODULE_NAME.framework"
    
        # If the framework folder doesn't exist, create it.
        if [ ! -d "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ]; then
            echo "Creating framework for $RELATIVE_EXT"
            mkdir -p "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER"
            cp "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist"
            plutil -replace CFBundleExecutable -string "$FULL_MODULE_NAME" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist"
            plutil -replace CFBundleIdentifier -string "$FRAMEWORK_BUNDLE_ID" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist"
        fi
    
        echo "Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME"
        mv "$FULL_EXT" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME"
        # Create a placeholder .fwork file where the .so was
        echo "$FRAMEWORK_FOLDER/$FULL_MODULE_NAME" > ${FULL_EXT%.so}.fwork
        # Create a back reference to the .so file location in the framework
        echo "${RELATIVE_EXT%.so}.fwork" > "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin"
     }
    
     PYTHON_VER=$(ls -1 "$CODESIGNING_FOLDER_PATH/python/lib")
     echo "Install Python $PYTHON_VER standard library extension modules..."
     find "$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload" -name "*.so" | while read FULL_EXT; do
        install_dylib python/lib/$PYTHON_VER/lib-dynload/ "$FULL_EXT"
     done
    
     # Clean up dylib template
     rm -f "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist"
    
     echo "Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)..."
     find "$CODESIGNING_FOLDER_PATH/Frameworks" -name "*.framework" -exec /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der "{}" \;
    
  10. Добавьте код Objective C для инициализации и использования интерпретатора Python в режиме встраивания. Убедитесь, что:
  • UTF-8 mode включен;
  • Buffered stdio выключен;
  • Writing bytecode выключен;
  • Signal handlers включены;
  • настройки интерпретатора нацелены на подпапку python пакета вашего приложения; и
  • Пути интерпретатора включают:

    • подпапку python/lib/python3.X пакета вашего приложения,
    • подпапку python/lib/python3.X/lib-dynload пакета вашего приложения, и
    • подпапку app пакета вашего приложения

Расположение пакета вашего приложения можно определить, используя [[NSBundle mainBundle] resourcePath].

Шаги 8, 9 и 10 этих инструкций предполагают, что у вас одна папка с чистым кодом приложения Python, названная app. Если у вас есть сторонние бинарные модули в приложении, потребуются дополнительные шаги:

  • Вы должны убедиться, что любые папки, содержащие сторонние бинарные файлы, либо связаны с целевым приложением, либо скопированы в рамках шага 8. Шаг 8 также должен удалить любые бинарные файлы, которые не подходят для конкретной платформы (то есть, удалить любые бинарные файлы устройства, если вы создаёте приложение для симулятора).
  • Любые папки, содержащие сторонние бинарные файлы, должны быть обработаны в формате фреймворка на этапе 9. Вызов install_dylib, обрабатывающий папку lib-dynload, может быть скопирован и адаптирован для этой цели.
  • Если вы используете отдельную папку для сторонних пакетов, убедитесь, что эта папка включена в настройки PYTHONPATH на шаге 10.

7.3. Соответствие требованиям App Store

Единственный способ распространения приложений на сторонние устройства iOS — это отправка приложения в App Store; приложения, представленные для распространения, должны пройти процесс проверки Apple. Этот процесс включает набор автоматических правил проверки, которые проверяют предоставленный пакет приложения на наличие проблемного кода.

Стандартная библиотека Python содержит некоторый код, известный нарушением этих автоматических правил. Хотя эти нарушения, похоже, являются ложными срабатываниями, правила проверки Apple не могут быть оспорены; поэтому необходимо изменить стандартную библиотеку Python, чтобы приложение прошло проверку App Store.

Дерево исходного кода Python содержит файл патча, который удалит весь код, известный как причина проблем с процессом проверки App Store. Этот патч применяется автоматически при построении приложения для iOS.

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/using/ios.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API