نخستین ماژول توسعه‌ی C API شما

این خودآموز شما را با ساخت یک ماژول توسعه‌ی ساده‌ی پایتون که به زبان C یا C++ نوشته شده است، همراهی می‌کند.

ما مستقیماً از Python C API سطح پایین استفاده خواهیم کرد. برای راه‌های ساده‌تر جهت ساخت ماژول‌های توسعه، به ابزارهای شخص ثالث توصیه‌شده مراجعه کنید.

این خودآموز دانش پایه‌ای درباره‌ی پایتون را فرض می‌گیرد: شما باید پیش از شروع نوشتن توابع در زبان C، قادر به تعریف تابع در کد پایتون باشید. برای آشنایی با خودِ پایتون به راهنمای پایتون مراجعه کنید.

این خودآموز برای هر کسی که بتواند یک کتابخانه‌ی پایه‌ای C را بنویسد، قابل درک خواهد بود. با اینکه به مفاهیمی اشاره خواهیم کرد که انتظار نمی‌رود یک تازه‌کار C بداند، مانند توابع static یا اعلان‌های پیوند (linkage)، درک آن‌ها برای موفقیت ضروری نیست.

ما بر این تمرکز خواهیم کرد که «حسی» از چگونگی C API پایتون به شما بدهیم. این خودآموز مفاهیم مهمی مانند مدیریت خطا و شمارش ارجاع را به شما نخواهد آموخت که در فصول بعدی پوشش داده شده‌اند.

ما فرض می‌کنیم که شما از یک سیستم مشابه یونیکس (شامل macOS و لینوکس) یا ویندوز استفاده می‌کنید. در سایر سیستم‌ها، ممکن است نیاز باشد برخی جزئیات -- به عنوان مثال، نام یک دستور سیستمی -- را تنظیم کنید.

شما باید یک کامپایلر مناسب C و سرآیندهای توسعه‌ی پایتون را نصب کرده باشید. در لینوکس، سرآیندها معمولاً در بسته‌ای مانند python3-dev یا python3-devel قرار دارند.

شما باید توانایی نصب بسته‌های پایتون را داشته باشید. این خودآموز از pip (pip install) استفاده می‌کند، اما می‌توانید هر ابزاری را که توانایی ساخت و نصب پروژه‌های مبتنی بر pyproject.toml را دارد جایگزین کنید، مانند uv (uv pip install). ترجیحاً، یک محیط مجازی را فعال کنید.

نکته

این خودآموز از APIهایی استفاده می‌کند که در سی‌پایتون ۳.۱۵ اضافه شده‌اند. برای ساخت بسته‌ای که با نسخه‌های قبلی سی‌پایتون سازگار باشد، لطفاً نسخه‌ی قبلی این مستندات را دنبال کنید.

این خودآموز از نحو C اضافه شده در C11 و C++20 استفاده می‌کند. اگر افزونه‌ی شما نیاز دارد با استانداردهای قدیمی‌تر سازگار باشد، لطفاً خودآموزهای موجود در مستندات پایتون ۳.۱۴ یا پایین‌تر را دنبال کنید.

آنچه انجام خواهیم داد

بیایید یک ماژول توسعه‌ی به نام spam [1] بسازیم که شامل یک رابط پایتون به تابع کتابخانه‌ی استاندارد C یعنی system() خواهد بود. این تابع در stdlib.h تعریف شده است. این تابع یک رشته‌ی C را به عنوان آرگومان می‌گیرد، آرگومان را به عنوان یک دستور سیستمی اجرا می‌کند و یک مقدار نتیجه را به صورت یک عدد صحیح برمی‌گرداند. یک صفحه‌ی راهنما (man page) برای system() ممکن است آن را به این شکل خلاصه کند:

#include <stdlib.h>
int system(const char *command);

توجه داشته باشید که مانند بسیاری از توابع در کتابخانه‌ی استاندارد C، این تابع از پیش در پایتون در معرض دید قرار گرفته است. در محیط تولید، به جای ماژولی که در اینجا خواهید نوشت، از os.system() یا subprocess.run() استفاده کنید.

ما می‌خواهیم این تابع به صورت زیر از پایتون قابل فراخوانی باشد:

>>> import spam
>>> status = spam.system("whoami")
User Name
>>> status
0

نکته

دستور سیستمی whoami نام کاربری شما را چاپ می‌کند. این دستور در خودآموزهایی مانند این مفید است زیرا نام یکسانی در هر دو سیستم یونیکس و ویندوز دارد.

شروع با سرآیندها

با ساخت یک پوشه برای این خودآموز و رفتن به درون آن در خط فرمان شروع کنید. سپس، یک پرونده با نام spammodule.c در پوشه‌ی خود ایجاد کنید. [2]

در این پرونده، دو سرآیند را اضافه می‌کنیم: Python.h برای فراخوانی تمام تعاریف مربوط به API زبان C در پایتون، و stdlib.h برای تابع system(). [3]

سطرهای زیر را به spammodule.c اضافه کنید:

#include <Python.h>
#include <stdlib.h>     // for system()

مطمئن شوید که stdlib.h و هر سرآیند استاندارد دیگری را بعد از Python.h قرار دهید. در برخی سیستم‌ها، پایتون ممکن است تعاریف پیش‌پردازنده‌ای را تعیین کند که روی سرآیندهای استاندارد تأثیر می‌گذارند.

اجرای ابزار ساخت

با قرارگیری این سرآیندها، افزونه‌ی شما کار خاصی انجام نخواهد داد. با این حال، زمان مناسبی است که آن را کامپایل کرده و تلاش کنید ایمپورتش کنید. این کار تضمین می‌کند که ابزار ساخت شما به‌درستی کار می‌کند، به‌طوری که بتوانید در حین دنبال کردن ادامه‌ی متن، تغییرات افزایشی ایجاد کرده و آن‌ها را آزمون کنید.

سی‌پایتون به خودی خود ابزاری برای ساخت ماژول‌های توسعه‌ای ندارد؛ توصیه می‌شود برای این کار از یک پروژه‌ی شخص ثالث استفاده کنید. در این آموزش، از meson-python استفاده خواهیم کرد. (اگر می‌خواهید از ابزار دیگری استفاده کنید، به پیوست: سایر ابزارهای ساخت مراجعه کنید.)

ابزار meson-python نیازمند تعریف یک «پروژه» با استفاده از دو پرونده‌ی اضافی است.

ابتدا پرونده‌ی pyproject.toml را با این محتوا اضافه کنید:

[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']

[project]
# Placeholder project information
# (change this before distributing the module)
name = 'sampleproject'
version = '0'

سپس، پرونده‌ی meson.build را با محتوای زیر ایجاد کنید:

project('sampleproject', 'c')

py = import('python').find_installation(pure: false)

py.extension_module(
   'spam',          # name of the importable Python module
   'spammodule.c',  # the C source file
   install: true,
)

نکته

برای کسب اطلاعات بیشتر درباره‌ی پیکربندی، به مستندات meson-python مراجعه کنید.

اکنون، پروژه‌ی موجود در پوشه‌ی جاری (.) را از طریق pip ساخت و نصب کنید:

python -m pip -v install .

گزینه‌ی -v (--verbose) باعث می‌شود pip خروجی کامپایلر را نمایش دهد، که اغلب در طول توسعه مفید است.

نکته‌ی کاربردی

اگر pip را نصب ندارید، دستور python -m ensurepip را اجرا کنید، ترجیحاً در یک محیط مجازی (یا اگر ابزار دیگری را ترجیح می‌دهید که بتواند پروژه‌های مبتنی بر pyproject.toml را ساخت و نصب کند، از آن استفاده کنید.)

توجه داشته باشید که هر بار افزونه‌ی خود را تغییر می‌دهید، باید این دستور را دوباره اجرا کنید. برخلاف پایتون، زبان C دارای یک مرحله‌ی صریح کامپایل است.

هنگامی که افزونه‌ی شما کامپایل‌‌شده و نصب شد، پایتون را اجرا کرده و تلاش کنید آن را ایمپورت کنید. این کار باید با خطای زیر مواجه شود:

>>> import spam
Traceback (most recent call last):
   ...
ImportError: dynamic module does not define module export function (PyModExport_spam or PyInit_spam)

قلاب اکسپورت ماژول

استثنایی که هنگام تلاش برای ایمپورت کردن ماژول دریافت کردید به شما گفت که پایتون به دنبال یک «تابع اکسپورت ماژول» می‌گردد که با نام قلاب اکسپورت ماژول نیز شناخته می‌شود. بیایید یکی را تعریف کنیم.

ابتدا، یک پیش‌نمونه در زیر سطرهای #include اضافه کنید:


PyMODEXPORT_FUNC PyModExport_spam(void);

نکته‌ی کاربردی

پیش‌نمونه به طور دقیق ضروری نیست، اما برخی از کامپایلرهای مدرن بدون آن هشدارهایی صادر می‌کنند. اضافه کردن پیش‌نمونه عموماً بهتر از غیرفعال کردن هشدار است.

کلان‌دستور PyMODEXPORT_FUNC نوع بازگشت تابع را اعلام می‌کند و هرگونه اعلام پیوند ویژه‌ای را که برای قابل دیدن و قابل استفاده کردن تابع هنگام بارگذاری آن توسط سی‌پایتون لازم است، اضافه می‌کند.

پس از پیش‌نمونه، خود تابع را اضافه کنید. فعلاً کاری کنید که NULL را برگرداند:

PyMODEXPORT_FUNC
PyModExport_spam(void)
{
   return NULL;
}

ماژول را دوباره کامپایل و بارگذاری کنید. این بار باید خطای متفاوتی دریافت کنید.

>>> import spam
Traceback (most recent call last):
   ...
SystemError: module export hook for module 'spam' failed without setting an exception

صرفاً برگرداندن NULL رفتار درستی برای یک قلاب اکسپورت نیست و سی‌پایتون از آن شکایت می‌کند. این خوب است -- به این معنی است که سی‌پایتون تابع را پیدا کرده است! حالا بیایید کاری کنیم که کار مفیدی انجام دهد.

جدول جایگاه

به‌جای NULL، قلاب اکسپورت باید اطلاعات لازم برای ایجاد یک ماژول را برگرداند. بیایید با اصول اولیه شروع کنیم: نام و رشته مستند.

اطلاعات باید در آرایه‌ای از ورودی‌های PySlot تعریف شوند که در واقع جفت‌های کلید-مقدار هستند. این آرایه را درست قبل از قلاب اکسپورت خود تعریف کنید:

PyABIInfo_VAR(abi_info);

static PySlot spam_slots[] = {
   PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
   PySlot_STATIC_DATA(Py_mod_name, "spam"),
   PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
   PySlot_END
};

کلان‌دستور PySlot_STATIC_DATA زمانی استفاده می‌شود که مقدار جایگاه (اینجا: &abi_info، "spam" و رشته مستند) یک اشاره‌گر به داده‌ی ثابت و تخصیص‌یافته‌ی ایستا باشد.

کلان‌دستور PyABIInfo_VAR(abi_info); و جایگاه Py_mod_abi مقداری کد پیش‌ساخته هستند که کمک می‌کنند از کرش کردن مفسر توسط افزونه‌های کامپایل‌شده برای نسخه‌ی متفاوتی از پایتون جلوگیری شود.

برای هر دو Py_mod_name و Py_mod_doc، مقادیر رشته‌های C هستند -- یعنی آرایه‌های بایتی پایان‌یافته با NUL و کدگذاری‌شده با UTF-8.

ورودی نشانگر PySlot_END را در انتها توجه کنید. این نشان‌دهنده‌ی پایان آرایه است. اگر آن را فراموش کنید، رفتار نامشخصی را فعال خواهید کرد.

این آرایه به صورت static تعریف می‌شود -- یعنی خارج از این پرونده‌ی .c قابل مشاهده نیست. این یک روند رایج خواهد بود. سی‌پایتون فقط باید به قلاب اکسپورت دسترسی داشته باشد؛ تمام متغیرهای سراسری و تمام عملکردهای دیگر به‌طور کلی باید static باشند تا با سایر افزونه‌ها تداخل پیدا نکنند.

این آرایه را به‌جای NULL از قلاب (hook) اکسپورت خود برگردانید:

PyMODEXPORT_FUNC
PyModExport_spam(void)
{
   return spam_slots;
}

اکنون، برنامه را دوباره کامپایل کرده و امتحان کنید:

>>> import spam
>>> print(spam)
<module 'spam' from '/home/encukou/dev/cpython/spam.so'>

شما یک ماژول توسعه‌ای دارید! برای دیدن رشته مستند (docstring)، عبارت help(spam) را امتحان کنید.

گام بعدی افزودن یک تابع خواهد بود.

عرضه کردن یک تابع

برای عرضه‌ی مستقیم تابع C با نام system() به پایتون، باید لایه‌ای از کدهای واسط بنویسیم تا آرگومان‌ها را از اشیاء پایتون به مقادیر C و مقدار برگشتی C را دوباره به پایتون تبدیل کنند.

یکی از ساده‌ترین راه‌ها برای نوشتن کدهای واسط، تابعی با نماد "METH_O" است که دو شیء پایتون را دریافت کرده و یکی را برمی‌گرداند. تمام اشیاء پایتون -- صرف‌نظر از نوع پایتون -- در زبان C به شکل اشاره‌گرهایی به ساختار PyObject نمایش داده می‌شوند.

چنین تابعی را بالای آرایه جایگاه‌ها (slots) اضافه کنید:

static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   Py_RETURN_NONE;
}

فعلاً از آرگومان‌ها صرف‌نظر می‌کنیم و از کلان‌دستور Py_RETURN_NONE بهره می‌بریم؛ این کلان‌دستور به یک دستور return گسترش می‌یابد که به شکل درستی یک شیء None پایتون را برمی‌گرداند.

افزونه‌ی خود را دوباره کامپایل کنید تا مطمئن شوید خطای نحوی ندارید. ما هنوز spam_system را به ماژول اضافه نکرده‌ایم، بنابراین ممکن است هشداری مبنی بر استفاده‌نشدن از spam_system دریافت کنید.

تعاریف متدها

برای عرضه‌ی تابع C به پایتون، باید چندین قطعه اطلاعات را در ساختاری به نام PyMethodDef [4] فراهم کنید:

  • ml_name: نام تابع پایتون؛

  • ml_doc: یک رشته مستند (docstring)؛

  • ml_meth: تابع C که باید فراخوانی شود؛ و

  • ml_flags: مجموعه‌ای از پرچم‌ها (flags) که جزئیاتی مانند نحوه‌ی ارسال آرگومان‌های پایتون به تابع C را توصیف می‌کنند. ما در اینجا از METH_O استفاده می‌کنیم -- یعنی پرچمی که با امضای تابع spam_system ما مطابقت دارد.

از آنجا که ماژول‌ها معمولاً چندین تابع ایجاد می‌کنند، این تعاریف باید در یک آرایه جمع‌آوری شوند و در انتها یک نشانگر (sentinel) صفر پرشده قرار گیرد. این آرایه را درست در پایین تابع spam_system اضافه کنید:


static PyMethodDef spam_methods[] = {
    {
        .ml_name="system",
        .ml_meth=spam_system,
        .ml_flags=METH_O,
        .ml_doc="Execute a shell command.",
    },
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

همانند جایگاه‌های ماژول، یک نشانگر صفرگذاری‌شده پایان آرایه را مشخص می‌کند.

در ادامه، متد را به ماژول اضافه می‌کنیم. یک جایگاه Py_mod_methods به آرایه‌ی PyMethodDef خود اضافه کنید:


PyABIInfo_VAR(abi_info);

static PySlot spam_slots[] = {
    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
    PySlot_STATIC_DATA(Py_mod_name, "spam"),
    PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
    PySlot_STATIC_DATA(Py_mod_methods, spam_methods),
    PySlot_END
};

افزونه‌ی خود را مجدداً کامپایل و آن را آزمون کنید. حتماً مفسر پایتون را راه‌اندازی مجدد کنید تا import spam نسخه‌ی جدید ماژول را بارگذاری کند.

اکنون باید بتوانید تابع را فراخوانی کنید:

>>> import spam
>>> print(spam.system)
<built-in function system>
>>> print(spam.system('whoami'))
None

توجه داشته باشید که spam.system ما هنوز دستور whoami را اجرا نمی‌کند؛ بلکه فقط مقدار None را برمی‌گرداند.

بررسی کنید که تابع، همان‌طور که با پرچم METH_O مشخص شده است، دقیقاً یک آرگومان بپذیرد:

>>> print(spam.system('too', 'many', 'arguments'))
Traceback (most recent call last):
   ...
TypeError: spam.system() takes exactly one argument (3 given)

برگرداندن یک عدد صحیح

اکنون، بیایید نگاهی به مقدار بازگشتی بیندازیم. به‌جای None، می‌خواهیم spam.system یک عدد -- یعنی یک شیء int پایتون -- برگرداند. در نهایت این مقدار، کد خروج یک دستور پوسته خواهد بود، اما بیایید با یک مقدار ثابت، مثلاً 3، شروع کنیم.

رابط برنامه‌نویسی کاربردی C پایتون تابعی برای ایجاد یک شیء int پایتون از یک مقدار int زبان C فراهم می‌کند: PyLong_FromLong(). [5]

برای فراخوانی آن، Py_RETURN_NONE را با ۳ سطر زیر جایگزین کنید:

static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   int status = 3;
   PyObject *result = PyLong_FromLong(status);
   return result;
}

مجدداً کامپایل کنید، مفسر پایتون را راه‌اندازی مجدد کنید و بررسی کنید که تابع اکنون عدد ۳ را برمی‌گرداند:

>>> import spam
>>> spam.system('whoami')
3

پذیرش یک رشته

در نهایت، بیایید آرگومان تابع را مدیریت کنیم.

تابع C ما، یعنی spam_system()، دو آرگومان می‌پذیرد. اولین آرگومان، یعنی PyObject *self، روی شیء ماژول spam تنظیم خواهد شد. این مورد در سناریوی ما کاربردی ندارد، بنابراین آن را نادیده می‌گیریم.

مورد دیگر، یعنی PyObject *arg، به شیءای که کاربر از پایتون ارسال کرده است تنظیم خواهد شد. انتظار داریم که این شیء یک رشته‌ی پایتون باشد. برای استفاده از اطلاعات موجود در آن، باید آن را به یک مقدار C -- در اینجا، یک رشته‌ی C (const char *) -- تبدیل کنیم.

در اینجا ناسازگاری کوچکی در نوع داده وجود دارد: اشیاء str در پایتون متن یونیکد را ذخیره می‌کنند، اما رشته‌های C آرایه‌هایی از بایت‌ها هستند. بنابراین، باید داده‌ها را کدگذاری (encode) کنیم و برای این کار از کدگذاری UTF-8 استفاده خواهیم کرد. (ممکن است UTF-8 همیشه برای دستورات سیستم درست نباشد، اما همان چیزی است که str.encode() به‌طور پیش‌فرض استفاده می‌کند و C API پشتیبانی ویژه‌ای از آن دارد.)

تابع برای کدگذاری یک رشته‌ی پایتون در یک بافر UTF-8 PyUnicode_AsUTF8AndSize() [6] نام دارد. آن را به این صورت فراخوانی کنید:

static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg, NULL);
   int status = 3;
   PyObject *result = PyLong_FromLong(status);
   return result;
}

اگر PyUnicode_AsUTF8AndSize() موفقیت‌آمیز باشد، command به رشته‌ی C حاصل -- یک آرایه‌ی بایت ختم‌شده با صفر [7] -- اشاره خواهد کرد. این بافر توسط شیء arg مدیریت می‌شود، به این معنی که نیازی به آزادسازی آن نداریم، اما باید چند قانون را رعایت کنیم:

  • ما باید فقط از بافر درون تابع spam_system استفاده کنیم. پس از اینکه spam_system برگردد، ممکن است arg و بافری که مدیریت می‌کند زباله‌روبی (garbage-collected) شوند.

  • ما نباید آن را تغییر دهیم. به همین دلیل است که از const استفاده می‌کنیم.

اگر PyUnicode_AsUTF8AndSize() موفقیت‌آمیز نباشد، یک اشاره‌گر NULL برمی‌گرداند. هنگام فراخوانی هر C API پایتون، همیشه باید چنین مواردی از خطا را مدیریت کنیم. روش انجام این کار به‌طور کلی به فصول بعدی این مستندات موکول می‌شود. فعلاً مطمئن باشید که ما در حال حاضر خطاهای PyLong_FromLong() را به درستی مدیریت می‌کنیم.

برای فراخوانی PyUnicode_AsUTF8AndSize()، روش صحیح مدیریت خطاها برگرداندن NULL از spam_system است. یک بلوک if برای این کار اضافه کنید:

static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg);
   if (command == NULL) {
      return NULL;
   }
   int status = 3;
   PyObject *result = PyLong_FromLong(status);
   return result;
}

برای آزمون اینکه مدیریت خطا کار می‌کند، دوباره کامپایل کنید، پایتون را مجدداً راه‌اندازی کنید تا import spam نسخه‌ی جدید ماژول شما را بارگیری کند، و سعی کنید یک مقدار غیررشته‌ای به تابع خود ارسال کنید:

>>> import spam
>>> spam.system(3)
Traceback (most recent call last):
   ...
TypeError: bad argument type for built-in operation

اکنون، تنها چیزی که باقی مانده است فراخوانی تابع کتابخانه‌ی C system() با بافر char * و استفاده از نتیجه‌ی آن به جای 3 است:

static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg);
   if (command == NULL) {
      return NULL;
   }
   int status = system(command);
   PyObject *result = PyLong_FromLong(status);
   return result;
}

ماژول خود را کامپایل کنید، پایتون را مجدداً راه‌اندازی کنید و آزمون کنید. این بار، باید نام کاربری خود را -- که خروجی دستور سیستمی whoami است -- ببینید:

>>> import spam
>>> result = spam.system('whoami')
User Name
>>> result
0

همچنین می‌توانید با دستورات دیگر، مانند ls، dir، یا دستوری که وجود ندارد، آزمون کنید:

>>> import spam
>>> result = spam.system('nonexistent-command')
sh: line 1: nonexistent-command: command not found
>>> result
32512

نتیجه

تبریک می‌گوییم! شما یک ماژول توسعه‌ای کامل با C API پایتون نوشته‌اید و این آموزش را به پایان رسانده‌اید!

برای راحتی شما، کل پرونده‌ی سورس در ادامه آورده شده است:

/// Includes

#include <Python.h>
#include <stdlib.h>     // for system()

/// Implementation of spam.system

static PyObject *
spam_system(PyObject *self, PyObject *arg)
{
   const char *command = PyUnicode_AsUTF8AndSize(arg, NULL);
   if (command == NULL) {
      return NULL;
   }
   int status = system(command);
   PyObject *result = PyLong_FromLong(status);
   return result;
}

/// Module method table

static PyMethodDef spam_methods[] = {
    {
        .ml_name="system",
        .ml_meth=spam_system,
        .ml_flags=METH_O,
        .ml_doc="Execute a shell command.",
    },
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

/// Module slot table

PyABIInfo_VAR(abi_info);

static PySlot spam_slots[] = {
    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
    PySlot_STATIC_DATA(Py_mod_name, "spam"),
    PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
    PySlot_STATIC_DATA(Py_mod_methods, spam_methods),
    PySlot_END
};

/// Export hook prototype

PyMODEXPORT_FUNC PyModExport_spam(void);

/// Module export hook

PyMODEXPORT_FUNC
PyModExport_spam(void)
{
   return spam_slots;
}

پیوست: سایر ابزارهای ساخت

شما باید بتوانید این خودآموز را — به جز خود بخش Running your build tool — با ابزار ساختی به غیر از meson-python دنبال کنید.

«راهنمای کاربر بسته‌بندی پایتون» دارای یک فهرست از ابزارهای توصیه‌شده است؛ حتماً یکی را برای زبان C انتخاب کنید.

راه‌حل موقت برای تابع PyInit گمشده

اگر خروجی ابزار ساخت شما از گم شدن PyInit_spam گله می‌کند، فعلاً تابع زیر را به ماژول خود اضافه کنید:

// A workaround
void *PyInit_spam(void) { return NULL; }

این یک لایه (shim) برای تابع مقداردهی اولیه‌ی سبک قدیم است که در ماژول‌های توسعه‌ای برای سی‌پایتون نسخه‌ی ۳.۱۴ و پایین‌تر الزامی بود. سی‌پایتون فعلی به آن نیازی ندارد، اما برخی از ابزارهای ساخت ممکن است همچنان فرض کنند که تمام ماژول‌های توسعه‌ای باید آن را تعریف کنند.

اگر از این راه‌حل موقت استفاده کنید، به جای ImportError: dynamic module does not define module export function، استثنای SystemError: initialization of spam failed without raising an exception را دریافت خواهید کرد.

کامپایل مستقیم

استفاده از یک ابزار ساخت شخص ثالث بسیار توصیه می‌شود، زیرا به جزئیات مختلف پلتفرم و نصب پایتون شما، نام‌گذاری اکستنشن حاصل و بعداً توزیع کار شما رسیدگی می‌کند.

اگر در حال ساخت یک اکستنشن برای یک سیستم خاص هستید، یا فقط برای خودتان، ممکن است بخواهید مستقیماً کامپایلر خود را اجرا کنید. نحوه‌ی انجام این کار سیستم‌محور است؛ برای مشکلاتی که باید خودتان حل کنید آماده باشید.

لینوکس

روی لینوکس، بسته‌ی توسعه‌ی پایتون ممکن است شامل دستوری به نام python3-config باشد که پرچم‌های لازم کامپایلر را چاپ می‌کند. اگر از آن استفاده می‌کنید، بررسی کنید که با مفسر سی‌پایتونی که برای بارگذاری ماژول استفاده خواهید کرد مطابقت داشته باشد. سپس با دستور زیر شروع کنید:

gcc --shared $(python3-config --cflags --ldflags) spammodule.c -o spam.so

این کار باید پرونده‌ای به نام spam.so تولید کند که لازم است آن را در پوشه‌ای روی sys.path قرار دهید.

پاورقی‌ها