Другое

Полное руководство по расширениям Python C в wheel для macOS

Полное руководство по исправлению отсутствующих функций Python C при упаковке в wheel для macOS. Узнайте решения для тегирования wheel, целей развертывания и конфигурации scikit-build-core.

Почему функции Python исчезают при сборке C-библиотеки Python как wheel-файла на macOS?

Я пытаюсь собрать Python-библиотеку из C-кода на macOS. Когда я собираю только .so файл и подключаю его напрямую в Python, все функции работают как задумано:

python
import sys
sys.path.append("python/easyPyEngine")
import easyPyEngine

Однако, когда я собираю его с помощью scikit-build-core для создания .whl файла и устанавливаю его с помощью pip install <file>.whl, я все еще могу импортировать модуль, но функции отсутствуют.

Когда я запускаю print(dir(easyPyEngine)), я получаю разные результаты:

Рабочая версия:

['Engine', 'Sprite', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

Сломанная версия:

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']

Типы ‘Engine’ и ‘Sprite’ должны появляться в рабочей версии.

Моя структура проекта:

easyPyEngine/
├── CMakeLists.txt
├── pyproject.toml
├── build/
├── dist/
|     └── easyPyEngine.whl
├── python/
│   └── easyPyEngine/
│   |   ├── __init__.py
|   |   └── easyPyEngine.so
│   └── easyPyEngine.c
├── src/
│   └── *.c
├── include/
│   └--- *.h

Я включил конфигурации CMakeLists.txt и pyproject.toml, и wheel-файл содержит ожидаемые модули, включая .so файл. Что может вызывать исчезновение функций при упаковке в wheel, и как это можно исправить?

Проблемы с исчезновением функций при сборке Python C расширений как wheel файлов на macOS

При сборке Python C расширений как wheel файлов на macOS функции могут исчезать из-за нескольких проблем упаковки и конфигурации. Наиболее распространенные причины включают неправильное тегирование wheel, отсутствующие данные пакета, некорректную конфигурацию scikit-build-core и настройки целевой версии развертывания macOS.


Содержание


Общие причины исчезновения функций

Исчезновение ваших функций Engine и Sprite при упаковке в wheel обычно связано с одной из этих основных причин:

  1. Неправильное тегирование wheel - wheel создается как чисто Python wheel (py3-none-any.whl) вместо платформо-специфичного wheel
  2. Отсутствующие данные пакета - скомпилированный файл .so не включается в wheel должным образом
  3. Неправильная инициализация модуля расширения - инициализация модуля расширения C не работает корректно в контексте wheel
  4. Проблемы с целевой версией развертывания macOS - отсутствующие или некорректные настройки MACOSX_DEPLOYMENT_TARGET

Проблемы с тегированием wheel

Когда setuptools обнаруживает, что ваш пакет содержит расширения, он должен автоматически создавать платформо-специфичный wheel с правильными тегами, такими как cp39-cp39-macosx_10_13_x86_64.whl. Однако на macOS это обнаружение может не сработать.

Согласно Руководству пользователя Python Packaging, “Платформо-специфичные wheel - это wheel, специфичные для определенной платформы, такой как Linux, macOS или Windows, обычно из-за наличия скомпилированных расширений. Пакет wheel обнаружит, что код не является чистым Python, и создаст wheel с именем, которое можно использовать только на этой платформе…”

Если ваш wheel создается как py3-none-any.whl, это указывает на то, что setuptools не распознает ваш пакет как имеющий платформо-специфичные зависимости. Это распространенная проблема на macOS, где механизм обнаружения может не сработать.


Проблемы конфигурации scikit-build-core

Поскольку вы используете scikit-build-core, конфигурация в вашем pyproject.toml и CMakeLists.txt имеет решающее значение. Распространенные проблемы включают:

Отсутствующее определение C расширения

Ваше C расширение может быть неправильно определено в конфигурации scikit-build-core. Расширение должно быть явно объявлено, чтобы setuptools распознал его.

Некорректная структура модуля

Способ, которым ваш Python код импортирует C расширение, может отличаться при прямом загрузке и установке через wheel. При прямой загрузке через sys.path.append() Python находит файл .so в ожидаемом месте. Однако при установке через pip разрешение модуля работает иначе.


Проблемы с целевой версией развертывания macOS

macOS имеет специфические требования для C расширений, которые могут вызывать проблемы упаковки:

Как указано в документации cibuildwheel, “Кроме того, вы можете установить MACOSX_DEPLOYMENT_TARGET в переменную окружения, чтобы правильно пометить wheel как несовместимый с более старыми версиями macOS. Эта ошибка может возникать при установке библиотеки с помощью менеджера пакетов, такого как Homebrew, который компилирует библиотеку для версии macOS сборочной машины.”

Отсутствующие или некорректные настройки целевой версии развертывания могут привести к:

  • Неправильному именованию wheel
  • Проблемам совместимости
  • Проблемам видимости функций

Данные пакета и включение файлов

Файл .so должен быть правильно включен в ваш пакет wheel:

Согласно pythontutorials.net, “Отсутствующие package_data: если ваши платформо-специфичные файлы не включены в wheel, setuptools все равно может пометить его как чистый Python. Используйте package_data в setup.cfg, чтобы включить их.”

Ваш файл __init__.py должен правильно импортировать и экспортировать функции C расширения. При установке wheel путь импорта изменяется, и если ваш __init__.py не правильно ссылается на скомпилированное расширение, функции не будут доступны.


Решения и исправления

1. Принудительное создание платформо-специфичного wheel

Переопределите метод is_pure(), чтобы обеспечить создание платформо-специфичного wheel:

python
# В вашем setup.py или pyproject.toml
from setuptools import setup

setup(
    # ... другая конфигурация ...
    zip_safe=False,  # Важно для C расширений
)

2. Настройка MACOSX_DEPLOYMENT_TARGET

Установите целевую версию развертывания перед сборкой:

bash
export MACOSX_DEPLOYMENT_TARGET=10.9
pip wheel .

Как упоминалось на Stack Overflow, “Чтобы исправить проблему, мне также пришлось установить export MACOSX_DEPLOYMENT_TARGET=10.9 перед установкой python с помощью pyenv. Теперь pip wheel создает мой wheel с тегом macosx_10_9_x86_64.”

3. Правильная конфигурация данных пакета

Убедитесь, что ваш pyproject.toml включает файлы .so:

toml
[tool.setuptools.package-data]
"*" = ["*.so"]

4. Проверка инициализации C расширения

Проверьте ваш файл __init__.py, чтобы убедиться, что он правильно импортирует C расширение:

python
# python/easyPyEngine/__init__.py
from . import easyPyEngine  # Это должно импортировать .so файл
from .easyPyEngine import Engine, Sprite  # Явно экспортируем классы

Рекомендуемая конфигурация

pyproject.toml

toml
[build-system]
requires = ["scikit-build-core", "cmake", "ninja"]
build-backend = "scikit_build_core.build"

[project]
name = "easyPyEngine"
version = "0.1.0"
description = "Python привязки для easyPyEngine C библиотеки"

[tool.scikit-build]
# Убедимся, что мы строим платформо-специфичный wheel
wheel.packages = ["python/easyPyEngine"]
cmake.source-dir = "src"
cmake.build-type = "Release"

[tool.setuptools]
zip-safe = false

[tool.setuptools.package-data]
"*" = ["*.so"]

CMakeLists.txt

cmake
cmake_minimum_required(VERSION 3.15)
project(easyPyEngine VERSION 0.1.0)

# Найти Python
find_package(Python REQUIRED COMPONENTS Interpreter Development)

# Установить целевую версию macOS
if(APPLE)
    set(CMAKE_MACOSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Целевая версия macOS")
endif()

# Добавить Python расширение
add_library(easyPyEngine MODULE python/easyPyEngine.c)
target_include_directories(easyPyEngine PRIVATE include)
target_link_libraries(easyPyEngine PRIVATE your_c_library)

# Установить расширение
install(TARGETS easyPyEngine
    LIBRARY DESTINATION python/easyPyEngine
)

Шаги проверки

  1. Проверьте теги wheel: После сборки убедитесь, что ваш wheel имеет платформо-специфичные теги:

    easyPyEngine-0.1.0-cp39-cp39-macosx_10_13_x86_64.whl
    
  2. Протестируйте установку: Установите wheel в чистой среде и протестируйте:

    bash
    pip install easyPyEngine-0.1.0-cp39-cp39-macosx_10_13_x86_64.whl
    python -c "import easyPyEngine; print(dir(easyPyEngine))"
    
  3. Проверьте содержимое файлов: Убедитесь, что файл .so включен:

    bash
    wheel unpack easyPyEngine-0.1.0-cp39-cp39-macosx_10_13_x86_64.whl
    ls -la easyPyEngine-0.1.0.dist-info/easyPyEngine/
    
  4. Отладьте импорты: Добавьте отладочные выводы в ваш __init__.py для отслеживания процесса импорта.


Источники

  1. Советы и хитрости - cibuildwheel
  2. Python: создать wheel из существующего c расширения - Stack Overflow
  3. Упаковка и распространение проектов — Руководство пользователя Python Packaging
  4. Как заставить Python Wheel быть платформо-специфичным при сборке - pythontutorials.net
  5. Создать Python C расширение для MacOS 10.15 (Catalina) с обратной совместимостью - Stack Overflow
  6. Python C расширение пакет PyPI - Earthly Blog

Заключение

Исчезновение функций при упаковке Python C расширений как wheel на macOS обычно вызвано проблемами тегирования wheel, отсутствующими данными пакета или некорректной конфигурацией. Ключевые решения:

  1. Принудительное создание платформо-специфичного wheel путем установки zip-safe = false и правильной настройки данных пакета
  2. Установка MACOSX_DEPLOYMENT_TARGET для обеспечения правильной совместимости с macOS
  3. Проверка структуры пакета и правильного включения файла .so в wheel
  4. Тестирование в чистой среде для исключения проблем с путем импорта
  5. Проверка конфигурации scikit-build-core для правильной сборки и установки C расширений

Реализовав эти исправления и следуя рекомендованной конфигурации, ваши классы Engine и Sprite должны быть правильно доступны при установке wheel через pip.

Авторы
Проверено модерацией
Модерация