Python Programming

فراتر از PyPI: تسلط بر C-Extensions و Binary Wheels با Scikit-Build برای علم داده

پایتون به زبان مشترک علم داده تبدیل شده است، اما تکیه اکوسیستم آن بر پایتون خالص، اغلب یک گلوگاه حیاتی را پنهان می‌کند: عملکرد. برای محاسبات عددی سنگین، دانشمندان داده اغلب به کتابخانه‌هایی روی می‌آورند که بر پایه C، C++ یا Fortran ساخته شده‌اند، مانند NumPy، SciPy و Pandas. با این حال، توزیع این کتابخانه‌ها چالشی منحصر‌به‌فرد برای نگهدارندگان ایجاد می‌کند. برخلاف بسته‌های استاندارد پایتون، کتابخانه‌هایی که دارای افزونه‌های بومی هستند، نمی‌توانند صرفاً از سورس کد روی هر ماشین کاربر نصب شوند، مگر اینکه یک محیط ساخت پیچیده، ابزارهای کامپایلر و وابستگی‌های سازگار در دسترس باشد. نتیجه اغلب توزیع‌هایی «فقط سورس» است که نصب آن‌ها کند است و در برابر شکست‌های ساخت در سیستم‌عامل‌های مختلف آسیب‌پذیرند.

اینجاست که ابزارهای مدرن بسته‌بندی پایتون وارد عمل می‌شوند. پیکربندی سنتی setuptools با اسکریپت‌های setup.py به‌طور فزاینده‌ای توسط بک‌اند‌های ساختی که از PEP 517 و PEP 621 پیروی می‌کنند، جایگزین می‌شود. در میان این ابزارها، scikit-build-core به عنوان استاندارد طلایی برای پروژه‌هایی که به افزونه‌های C/C++ نیاز دارند، ظهور کرده است. در این راهنما، بررسی می‌کنیم که چگونه می‌توان از scikit-build-core برای ایجاد Binary Wheels قوی و خاص معماری استفاده کرد تا اطمینان حاصل شود که کتابخانه علم داده شما، بدون توجه به محیط کاربر، بالاترین عملکرد را ارائه می‌دهد.

تکامل بسته‌بندی پایتون

برای درک اینکه چرا scikit-build راه‌حل است، باید ابتدا نقاط درد گذشته را بپذیریم. در گذشته، توسعه‌دهندگان به فایل‌های setup.py تکیه می‌کردند که منطق ساخت را با منطق نصب ترکیب می‌کردند. این رویکرد تشریحی اغلب منجر به شکست‌های «یک‌بار ساخت، همه‌جا استقرار» می‌شد زمانی که محیط ساخت با محیط هدف مطابقت نداشت. علاوه بر این، ایجاد Wheels (توزیع‌های باینری) برای پلتفرم‌های چندگانه (لینوکس، مک‌او‌اس، ویندوز) و معماری‌های مختلف (x86_64، ARM64) نیازمند پیکربندی‌های پیچیده و دستی بود.

بسته‌بندی پایتون به سمت پیکربندی‌های اعلانی (Declarative) حرکت کرده است. PEP 621 اجازه می‌دهد تا متادیتای پروژه در pyproject.toml تعریف شود و منطق ساخت را از پیکربندی پروژه جدا می‌کند. Scikit-build-core یک بک‌اند ساخت مدرن مبتنی بر CMake است که به‌طور خاص برای این جریان کاری اعلانی طراحی شده است. این ابزار پیچیدگی‌های فراخوانی CMake، مدیریت کامپایلرها و بسته‌بندی باینری‌ها در Wheels را به‌طور خودکار مدیریت می‌کند.

ساختار پروژه و پیکربندی

راه‌اندازی یک پروژه با scikit-build-core با یک چیدمان دایرکتوری به‌خوبی ساختاریافته آغاز می‌شود. شما به یک پوشه سورس برای کد پایتون خود، یک دایرکتوری src برای کد C/C++ خود و یک فایل pyproject.toml نیاز دارید که به سیستم ساخت بگوید چه کاری انجام دهد.

project-root/
├── src/
│   ├── my_package/
│   │   ├── __init__.py
│   │   └── _core.c
├── pyproject.toml
└── build_requirements.txt

قلب پیکربندی در pyproject.toml قرار دارد. ما بک‌اند ساخت را به عنوان scikit_build_core تعریف می‌کنیم و الزامات CMake را مشخص می‌کنیم. این کار به‌طور کامل setup.py قدیمی را جایگزین می‌کند.

[build-system]
requires = ["scikit-build-core", "cmake>=3.15", "setuptools"]
build-backend = "scikit_build_core.build"

[project]
name = "fast-data-lib"
version = "0.1.0"
description = "پردازش داده با عملکرد بالا با استفاده از افزونه‌های C"
requires-python = ">=3.9"
dependencies = [
    "numpy>=1.24.0",
]

[tool.scikit-build]
cmake.version = "3.20.0"
build.type = "Release"
wheel.install-dir = "fast_data_lib"
# این به scikit-build می‌گوید که برای تمام پلتفرم‌های سازگار بسازد
manylinux.manylinux_x86_64 = true

نوشتن C-Extension با CMake

پس از تنظیم پیکربندی پایتون، تنظیمات CMake بر عهده می‌گیرد. ما به یک فایل CMakeLists.txt در ریشه پروژه خود نیاز داریم که توصیف کند چگونه کد منبع C کامپایل و به عنوان ماژول پایتون لینک شود. Scikit-build-core به‌طور خودکار متغیرهایی مانند PYTHON_INCLUDE_DIRS و PYTHON_LIBRARIES را به CMake می‌فرستد و فرآیند لینک شدن را ساده می‌کند.

cmake_minimum_required(VERSION 3.20)
project(fast_data_lib C)

# استفاده از پیکربندی پایتون scikit-build-core
include_directories(${PYTHON_INCLUDE_DIRS})

# تعریف ماژول افزونه
python_add_library(_core MODULE src/my_package/_core.c)

# لینک با کتابخانه‌های استاندارد پایتون
target_link_libraries(_core ${PYTHON_LIBRARIES})

# اطمینان از نصب کتابخانه در مکان صحیح
set_target_properties(_core PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/src/my_package"
)

در اینجا، دستور python_add_library یک ماکروی سطح بالا است که توسط CMake ارائه شده و ایجاد یک کتابخانه اشتراکی (`.so` در لینوکس، `.dll` در ویندوز، `.dylib` در مک‌او‌اس) را مدیریت می‌کند که با مفسر پایتون سازگار است. نام افزونه (_core) باید با نام ایمپورت در کد پایتون شما مطابقت داشته باشد.

ساخت و توزیع Binary Wheels

قدرت scikit-build-core در توانایی آن برای ساخت Wheels برای پلتفرم‌های مختلف نهفته است. شما می‌توانید یک Wheel محلی با استفاده از دستور زیر بسازید:

pip wheel . --no-build-isolation

با این حال، برای توزیع واقعی چندپلتفرمی، استفاده از cibuildwheel را توصیه می‌کنیم؛ ابزاری که درون یک پایپ‌لاین CI/CD (مانند GitHub Actions) اجرا می‌شود. این ابزار به‌طور خودکار Wheels را برای لینوکس، مک‌او‌اس و ویندوز در تمام نسخه‌های پشتیبانی‌شده پایتون می‌سازد. وقتی این Wheels را به PyPI آپلود می‌کنید، pip به‌طور هوشمندانه Wheel باینری صحیح را برای ماشین کاربر انتخاب می‌کند و نیازی به کامپایل محلی وجود نخواهد داشت.

# نمونه قطعه .github/workflows/build.yml
jobs:
  build_wheels:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - name: Build wheels
        uses: pypa/cibuildwheel@v2.19
      - uses: pypa/gh-action-pypi-publish@release/v1

نتیجه‌گیری

تحویل کتابخانه‌های علم داده با عملکرد بالا نیازمند بیش از الگوریتم‌های بهینه‌شده است؛ بلکه نیازمند یک استراتژی بسته‌بندی قوی است. با مهاجرت از اسکریپت‌های قدیمی setup.py به رویکرد مدرن و اعلانی scikit-build-core، توسعه‌دهندگان می‌توانند توزیع C-Extensions و Binary Wheels را ساده‌سازی کنند. این اطمینان را ایجاد می‌کند که کاربران شما از مزایای سرعت کد بومی بدون اصطکاک محیط‌های کامپایل محلی پیچیده بهره‌مند شوند. همان‌طور که اکوسیستم پایتون به تکامل خود ادامه می‌دهد، اتخاذ این ابزارها نه تنها یک بهترین شیوه، بلکه ضروری برای حفظ نرم‌افزار علمی با کیفیت بالا و در دسترس است.

Share: