Полное руководство по расширениям Python C в wheel для macOS
Полное руководство по исправлению отсутствующих функций Python C при упаковке в wheel для macOS. Узнайте решения для тегирования wheel, целей развертывания и конфигурации scikit-build-core.
Почему функции Python исчезают при сборке C-библиотеки Python как wheel-файла на macOS?
Я пытаюсь собрать Python-библиотеку из C-кода на macOS. Когда я собираю только .so файл и подключаю его напрямую в 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.
Содержание
- Общие причины исчезновения функций
- Проблемы с тегированием wheel
- Проблемы конфигурации scikit-build-core
- Проблемы с целевой версией развертывания macOS
- Данные пакета и включение файлов
- Решения и исправления
- Рекомендуемая конфигурация
- Шаги проверки
Общие причины исчезновения функций
Исчезновение ваших функций Engine и Sprite при упаковке в wheel обычно связано с одной из этих основных причин:
- Неправильное тегирование wheel - wheel создается как чисто Python wheel (
py3-none-any.whl) вместо платформо-специфичного wheel - Отсутствующие данные пакета - скомпилированный файл
.soне включается в wheel должным образом - Неправильная инициализация модуля расширения - инициализация модуля расширения C не работает корректно в контексте wheel
- Проблемы с целевой версией развертывания 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:
# В вашем setup.py или pyproject.toml
from setuptools import setup
setup(
# ... другая конфигурация ...
zip_safe=False, # Важно для C расширений
)
2. Настройка MACOSX_DEPLOYMENT_TARGET
Установите целевую версию развертывания перед сборкой:
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:
[tool.setuptools.package-data]
"*" = ["*.so"]
4. Проверка инициализации C расширения
Проверьте ваш файл __init__.py, чтобы убедиться, что он правильно импортирует C расширение:
# python/easyPyEngine/__init__.py
from . import easyPyEngine # Это должно импортировать .so файл
from .easyPyEngine import Engine, Sprite # Явно экспортируем классы
Рекомендуемая конфигурация
pyproject.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_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
)
Шаги проверки
-
Проверьте теги wheel: После сборки убедитесь, что ваш wheel имеет платформо-специфичные теги:
easyPyEngine-0.1.0-cp39-cp39-macosx_10_13_x86_64.whl -
Протестируйте установку: Установите wheel в чистой среде и протестируйте:
bashpip install easyPyEngine-0.1.0-cp39-cp39-macosx_10_13_x86_64.whl python -c "import easyPyEngine; print(dir(easyPyEngine))" -
Проверьте содержимое файлов: Убедитесь, что файл
.soвключен:bashwheel unpack easyPyEngine-0.1.0-cp39-cp39-macosx_10_13_x86_64.whl ls -la easyPyEngine-0.1.0.dist-info/easyPyEngine/ -
Отладьте импорты: Добавьте отладочные выводы в ваш
__init__.pyдля отслеживания процесса импорта.
Источники
- Советы и хитрости - cibuildwheel
- Python: создать wheel из существующего c расширения - Stack Overflow
- Упаковка и распространение проектов — Руководство пользователя Python Packaging
- Как заставить Python Wheel быть платформо-специфичным при сборке - pythontutorials.net
- Создать Python C расширение для MacOS 10.15 (Catalina) с обратной совместимостью - Stack Overflow
- Python C расширение пакет PyPI - Earthly Blog
Заключение
Исчезновение функций при упаковке Python C расширений как wheel на macOS обычно вызвано проблемами тегирования wheel, отсутствующими данными пакета или некорректной конфигурацией. Ключевые решения:
- Принудительное создание платформо-специфичного wheel путем установки
zip-safe = falseи правильной настройки данных пакета - Установка MACOSX_DEPLOYMENT_TARGET для обеспечения правильной совместимости с macOS
- Проверка структуры пакета и правильного включения файла
.soв wheel - Тестирование в чистой среде для исключения проблем с путем импорта
- Проверка конфигурации scikit-build-core для правильной сборки и установки C расширений
Реализовав эти исправления и следуя рекомендованной конфигурации, ваши классы Engine и Sprite должны быть правильно доступны при установке wheel через pip.