پایداری API زبان C

مگر آنکه خلاف آن مستند شده باشد، C API پایتون مشمول سیاست سازگاری رو به عقب، PEP 387، است. بیشتر تغییرات آن با کد منبع سازگار هستند (source-compatible) (معمولاً فقط با افزودن API‌های جدید). تغییر API‌های موجود یا حذف API تنها پس از یک دوره‌ی منسوخ‌سازی یا برای رفع مشکلات جدی انجام می‌شود.

رابط دودویی برنامه (ABI) سی‌پایتون در طول یک انتشار جزئی، به جلو و به عقب سازگار است (اگر این‌ها به یک شکل کامپایل شده باشند؛ به ملاحظات پلتفرم در ادامه مراجعه کنید). بنابراین، کدی که برای پایتون 3.10.0 کامپایل شده باشد، روی 3.10.8 کار خواهد کرد و برعکس، اما برای 3.9.x و 3.11.x باید جداگانه کامپایل شود.

دو سطح از C API با انتظارات پایداری متفاوت وجود دارد:

  • API ناپایدار، ممکن است در نسخه‌های جزئی بدون دوره‌ی منسوخ‌سازی تغییر کند. این API با پیشوند PyUnstable در نام‌ها مشخص می‌شود.

  • API محدود، در چندین نسخه‌ی فرعی سازگار است. هنگامی که Py_LIMITED_API تعریف شود، فقط این زیرمجموعه از Python.h در دسترس قرار می‌گیرد.

این موارد در ادامه با جزئیات بیشتر بررسی می‌شوند.

نام‌هایی که با خط زیر آغاز می‌شوند، مانند _Py_InternalState، API خصوصی هستند و می‌توانند حتی در نسخه‌های وصله (patch) بدون اطلاع قبلی تغییر کنند. اگر نیاز به استفاده از این API دارید، در نظر بگیرید که با توسعه‌دهندگان سی‌پایتون ارتباط برقرار کنید تا درباره‌ی افزودن API عمومی برای مورد استفاده‌ی خود گفت‌وگو کنید.

API ناپایدار C

هر API که با پیشوند PyUnstable نام‌گذاری شده باشد، جزئیات پیاده‌سازی سی‌پایتون را آشکار می‌کند و ممکن است در هر انتشار جزئی (برای مثال از 3.9 به 3.10) بدون هیچ هشدار منسوخ‌شدن تغییر کند. با این حال، در انتشار رفع اشکال (برای مثال از 3.10.0 به 3.10.1) تغییر نخواهد کرد.

این به‌طور کلی برای ابزارهای تخصصی و سطح پایین مانند اشکال‌زداها در نظر گرفته شده است.

انتظار می‌رود پروژه‌هایی که از این API استفاده می‌کنند، توسعه‌ی سی‌پایتون را دنبال کنند و برای سازگاری با تغییرات تلاش اضافی صرف کنند.

رابط دودویی پایدار برنامه (ABI)

برای سادگی، این سند درباره‌ی افزونه‌ها صحبت می‌کند، اما API محدود (Limited API) و ABI پایدار برای همه‌ی کاربردهای API به همان شکل کار می‌کنند – برای مثال، تعبیه کردن پایتون.

C API محدود

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

Py_LIMITED_API

این ماکرو را پیش از گنجاندن Python.h تعریف کنید تا فقط از API محدود استفاده کنید و نسخه‌ی API محدود را انتخاب کنید.

Py_LIMITED_API را برابر با مقدار PY_VERSION_HEX مربوط به کمترین نسخه‌ی پایتونی که ماژول توسعه‌ای شما از آن پشتیبانی می‌کند، تعریف کنید. ماژول توسعه‌ای از نظر ABI با تمام نسخه‌های پایتون 3 از نسخه‌ی مشخص‌شده به بعد سازگار خواهد بود و می‌تواند از API محدودی که تا آن نسخه معرفی شده است استفاده کند.

به‌جای استفاده‌ی مستقیم از ماکروی PY_VERSION_HEX، برای پایداری هنگام کامپایل با نسخه‌های آینده‌ی پایتون، یک حداقل نسخه‌ی فرعی (مثلاً 0x030A0000 برای پایتون 3.10) را به‌صورت هاردکد (hardcode) در کد بنویسید.

همچنین می‌توانید Py_LIMITED_API را با مقدار 3 تعریف کنید. این همانند 0x03020000 عمل می‌کند (پایتون 3.2، نسخه‌ای که API محدود را معرفی کرد).

رابط دودویی کاربردی پایدار

برای فعال‌سازی این، پایتون یک ABI پایدار فراهم می‌کند: مجموعه‌ای از نمادها که در تمام نسخه‌های Python 3.x از نظر ABI سازگار باقی خواهند ماند.

توجه

ABI پایدار از بروز مشکلات ABI جلوگیری می‌کند؛ مانند خطاهای پیونددهنده به دلیل نبود نمادها یا خرابی داده به دلیل تغییرات در چیدمان ساختارها یا امضاهای توابع. با این حال، تغییرات دیگر در پایتون می‌توانند رفتار ماژول‌های توسعه‌ای را تغییر دهند. برای جزئیات، سیاست سازگاری رو به عقب پایتون (PEP 387) را ببینید.

ABI پایدار شامل نمادهایی است که در API محدود افشا شده‌اند، اما نمادهای دیگری را نیز در بر می‌گیرد – برای مثال، توابعی که برای پشتیبانی از نسخه‌های قدیمی‌ترِ API محدود ضروری‌اند.

در ویندوز، ماژول‌های توسعه‌ای که از رابط دودویی پایدار استفاده می‌کنند، باید به‌جای کتابخانه‌ی خاص نسخه مانند python39.dll به python3.dll پیوند داده شوند.

در برخی پلتفرم‌ها، پایتون به دنبال پرونده‌های کتابخانه اشتراکی‌ای که با برچسب abi3 نام‌گذاری شده‌اند می‌گردد و آن‌ها را بارگذاری می‌کند (برای مثال، mymodule.abi3.so). پایتون بررسی نمی‌کند که آیا این‌گونه ماژول‌های توسعه‌ای با ABI پایدار مطابقت دارند یا خیر. کاربر (یا ابزارهای بسته‌بندی او) باید اطمینان حاصل کنند که مثلاً ماژول‌های توسعه‌ای ساخته‌شده با API محدودِ 3.10+ برای نسخه‌های پایین‌تر پایتون نصب نمی‌شوند.

تمام توابع موجود در ABI پایدار به‌صورت تابع در کتابخانه‌ی اشتراکی پایتون وجود دارند، نه صرفاً به‌صورت ماکرو. این امر امکان استفاده از آن‌ها را در زبان‌هایی که از پیش‌پردازنده‌ی C استفاده نمی‌کنند، فراهم می‌کند.

محدوده و کارایی API محدود

هدف API محدود (Limited API) این است که هر آنچه با C API کامل امکان‌پذیر است را ممکن سازد، اما احتمالاً با جریمه‌ای در کارایی (performance penalty).

برای مثال، در حالی که PyList_GetItem() دسترس‌پذیر است، گونه‌ی ماکروی «ناامن» آن PyList_GET_ITEM() دسترس‌پذیر نیست. این ماکرو می‌تواند سریع‌تر باشد، زیرا می‌تواند به جزئیات پیاده‌سازی وابسته به نسخه‌ی شیء فهرست تکیه کند.

بدون تعریف Py_LIMITED_API، برخی از توابع C API به‌صورت درون‌خطی تعریف می‌شوند یا با ماکروها جایگزین می‌گردند. تعریف Py_LIMITED_API این درون‌خطی‌سازی را غیرفعال می‌کند و همین امر باعث می‌شود با بهبود ساختارهای داده‌ی پایتون، پایداری حفظ شود، هرچند ممکن است کارایی را کاهش دهد.

با تعریف ن کردن Py_LIMITED_API، می‌توان یک افزونه‌ی API محدود را با ABI مخصوص نسخه کامپایل کرد. این کار می‌تواند کارایی را برای آن نسخه از پایتون بهبود بخشد، اما سازگاری را محدود خواهد کرد. در مقابل، کامپایل کردن با Py_LIMITED_API افزونه‌ای به دست می‌دهد که می‌توان آن را در جایی که افزونه‌ی مخصوص نسخه در دسترس نیست، توزیع کرد – برای مثال، برای نسخه‌های پیش‌انتشار یک نسخه‌ی آتی از پایتون.

هشدارهای API محدود

توجه داشته باشید که کامپایل با Py_LIMITED_API تضمین کاملی نیست که کد با API محدود یا ABI پایدار مطابقت داشته باشد. Py_LIMITED_API تنها تعاریف را پوشش می‌دهد، اما یک API همچنین شامل مسائل دیگری مانند معناشناسی مورد انتظار است.

یکی از مشکلاتی که Py_LIMITED_API در برابر آن محافظت نمی‌کند، فراخوانی تابعی با آرگومان‌هایی است که در نسخه‌ی پایین‌تر پایتون نامعتبر هستند. برای مثال، تابعی را در نظر بگیرید که شروع به پذیرش NULL برای یک آرگومان کرده است. در پایتون 3.9، NULL اکنون یک رفتار پیش‌فرض را انتخاب می‌کند، اما در پایتون 3.8، آرگومان مستقیماً استفاده خواهد شد که موجب ارجاع‌زدایی (dereference) NULL و فروپاشی می‌شود. استدلال مشابهی برای فیلدهای ساختارها نیز برقرار است.

مشکل دیگر این است که برخی از فیلدهای ساختار هنگامی که Py_LIMITED_API تعریف شده باشد، در حال حاضر پنهان نمی‌شوند، هرچند که بخشی از API محدود به شمار می‌روند.

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

همچنین توصیه می‌کنیم مستندات تمام API‌های استفاده‌شده را بررسی کنید تا مشخص شود که آیا به‌طور صریح بخشی از API محدود هستند یا خیر. حتی با تعریف Py_LIMITED_API، چند اعلان خصوصی به دلایل فنی (یا حتی ناخواسته، به‌عنوان باگ) در دسترس قرار می‌گیرند.

همچنین توجه داشته باشید که API محدود لزوماً پایدار نیست: کامپایل کردن با Py_LIMITED_API در پایتون 3.8 به این معناست که ماژول توسعه‌ای با پایتون 3.12 اجرا خواهد شد، اما لزوماً با پایتون 3.12 کامپایل نخواهد شد. به‌ویژه، ممکن است بخش‌هایی از API محدود منسوخ و حذف شوند، به شرط آنکه ABI پایدار پایدار باقی بماند.

ملاحظات پلتفرم

پایداری ABI نه‌تنها به پایتون، بلکه به کامپایلر استفاده‌شده، کتابخانه‌های سطح پایین‌تر و گزینه‌های کامپایلر نیز بستگی دارد. در چارچوب ABI پایدار، این جزئیات یک «پلتفرم» را تعریف می‌کنند. این جزئیات معمولاً به نوع سیستم‌عامل و معماری پردازنده بستگی دارند

مسئولیت هر توزیع‌کننده‌ی خاص پایتون این است که اطمینان حاصل کند تمام نسخه‌های پایتون روی یک پلتفرم خاص به گونه‌ای ساخته می‌شوند که ABI پایدار را نشکنند. این موضوع درباره‌ی نسخه‌های ویندوز و macOS از python.org و بسیاری از توزیع‌کنندگان شخص ثالث صادق است.

محتویات API محدود

در حال حاضر، API محدود شامل موارد زیر است: