شرح pyproject.toml: ملف واحد لإعدادات مشاريع بايثون

مَن فتح مشروع بايثون قديماً وجد على الأرجح ثلاثة ملفات متجاورة: setup.py للتغليف، وsetup.cfg للإعدادات، وملفات متفرقة لكل أداة فحص أو تنسيق. ثم جاء pyproject.toml فطوى هذه الفوضى كلها في ملف واحد قياسي تقرؤه pip وuv وPoetry على السواء، حتى صار وجوده أول ما تفترضه الأدوات في أي مشروع حديث. غير أنه لم يولد ترفاً تنظيمياً؛ فخلفه عيب تصميمي قديم في setup.py، ومنه تبدأ الحكاية.
إذا كنت تبني مشروعاً جديداً اليوم، فاجعل هذا الملف نقطة البداية. أما إن كان لديك مشروع قائم، فلا تنقل كل شيء دفعة واحدة؛ ابدأ ببيانات [project] وإعدادات الأدوات، ثم انتقل إلى إدارة الحزم والقفل عندما يكون الفريق مستعداً. ولرؤية الصورة الكاملة مع uv وRuff، راجع أدوات بايثون الحديثة: دليل uv وRuff وpyproject.toml.
لماذا ظهر pyproject.toml أصلاً؟
كان setup.py سكربت بايثون تنفيذياً، وهنا مربط الفرس: لا سبيل أمام الأداة لمعرفة اعتماديات بناء المشروع إلا بتشغيل السكربت، والسكربت نفسه قد لا يعمل من دون تلك الاعتماديات. خذ الحالة الكلاسيكية: حزمة علمية يبدأ ملف setup.py فيها بسطر import numpy لجلب مسارات ترويسات الترجمة. إذا ثبّتها مستخدم في بيئة خالية، انهار التثبيت قبل أن تتاح لـ pip فرصة معرفة أنه يحتاج numpy أصلاً. حلقة مفرغة. هذا الانغلاق الدائري هو ما دفع مجتمع بايثون نحو ملف إعدادات ثابت يُقرأ ولا يُنفَّذ، بصيغة TOML الواضحة.
ولم يأتِ الحل دفعة واحدة، بل عبر سلسلة معايير رسمية. استحدث PEP 518 (أُنشئ في 10 مايو 2016) الملف نفسه مع جدول [build-system] ومفتاح requires لإعلان اعتماديات البناء. وأضاف PEP 517 (أُنشئ في 30 سبتمبر 2015، أي قبله زمنياً) مفتاح build-backend وواجهة موحّدة تفتح الباب لواجهات بناء خلفية غير setuptools. بعدها عرّف PEP 621 (أُنشئ في 22 يونيو 2020) جدول [project] لبيانات المشروع الوصفية، حتى قُبل أخيراً PEP 735 في 10 أكتوبر 2024 معرّفاً جدول [dependency-groups] لاعتماديات التطوير التي لا تُنشر مع الحزمة، مثل مجموعتي test وdocs.
بنية الملف: ثلاثة جداول رئيسية
يقوم الملف على ثلاثة جداول. يتصدرها [build-system]، وهو جدول موصى به بشدة، يحدد نظام البناء: ماذا تحتاج الأداة لبناء حزمتك، وأي واجهة خلفية تتولى المهمة. يليه [project] بحقوله القياسية التي عرّفها PEP 621، ومنها name وversion وdescription وreadme وrequires-python وlicense وauthors وdependencies وoptional-dependencies وdynamic. وأخيراً [tool]، حيث تحجز كل أداة مساحتها الخاصة مثل [tool.ruff] أو [tool.uv].
وفي جدول [project] نقطتان تستحقان الانتباه: الحقل name إلزامي ولا يجوز جعله ديناميكياً، أما version فإلزامي أيضاً، لكنه كثيراً ما يُعلَن ضمن dynamic لتتولى الواجهة الخلفية حسابه من مصدر آخر.
مثال كامل جاهز للقراءة
هكذا يبدو ملف متكامل لأداة سطر أوامر صغيرة، باستخدام Hatchling واجهةً خلفية كما في مثال الدرس الرسمي للتغليف:
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"
[project]
name = "my-cli-tool"
version = "0.1.0"
description = "A small example CLI tool"
readme = "README.md"
requires-python = ">=3.10"
license = "MIT"
authors = [{ name = "Tqni Team", email = "[email protected]" }]
dependencies = [
"httpx>=0.27",
"rich>=13",
]
[project.optional-dependencies]
dev = ["pytest>=8", "ruff"]
[project.scripts]
my-cli-tool = "my_cli_tool.cli:main"
[tool.ruff]
line-length = 100
لاحظ أن الاعتماديات الاختيارية تُنشر مع الحزمة بوصفها إضافات، ويثبّتها المستخدم بأمر مثل pip install pkg[dev]. أما [project.scripts] فينشئ عند التثبيت أمراً تنفيذياً باسم الأداة.
هل يستبدل requirements.txt؟
هنا يخلط كثيرون. صحيح أن pyproject.toml يستبدل setup.py وsetup.cfg في البيانات الوصفية والإعداد، لكنه لا يمسّ المهمة الأصلية لملف requirements.txt: تثبيت بيئة كاملة بإصدارات مقفولة. الفرق جوهري. حقل dependencies «مجرّد» يعلن مدى إصدارات مرناً لحزمة قابلة لإعادة الاستخدام في بيئات مختلفة، بينما requirements.txt «ملموس» يصف بيئة بعينها لتكرارها بدقة. وتؤدي ملفات القفل مثل uv.lock الدور الملموس نفسه في الأدوات الحديثة.
كيف تتعامل الأدوات مع الملف؟
يدعم pip البناء عبر pyproject.toml منذ الإصدار 19.0 الصادر في 22 يناير 2019، والتثبيت القابل للتحرير عبره منذ الإصدار 21.3. وإذا وجد جدول [build-system] بلا مفتاح build-backend، سقط إلى setuptools.build_meta:__legacy__ افتراضاً توافقياً مع المشاريع القديمة.
setuptools بدوره يدعم جدول [project] منذ الإصدار 61.0.0، وصار setup.py معه اختيارياً تماماً. وتذهب uv أبعد من ذلك: تشترط وجود pyproject.toml لتحديد جذر المشروع، وينشئه أمر uv init تلقائياً، بل يعدّل uv add قائمة dependencies نيابة عنك، ولها جدول [tool.uv] لإعداداتها الخاصة. حتى Poetry، الذي طالما احتفظ باعتمادياته في [tool.poetry] بصيغة غير قياسية، انضم إلى المعيار في الإصدار 2.0.0 الصادر في 5 يناير 2025 بدعمه جدول [project] القياسي. أما Ruff للفحص والتنسيق فتُضبط بالكامل عبر [tool.ruff] و[tool.ruff.lint] و[tool.ruff.format].
النتيجة أن الملف صار نقطة التقاء بين الواجهات الأمامية للبناء مثل pip وuv، والواجهات الخلفية مثل Hatchling وsetuptools، وأدوات التطوير اليومية، وصولاً في النهاية إلى إنتاج التوزيعة المصدرية والعجلة.
أخطاء شائعة تجنّبها
- الظن أنه بديل عن requirements.txt: يستبدل setup.py وsetup.cfg فقط، وتحتفظ ملفات التثبيت الملموسة بدورها.
- نسيان build-backend في جدول [build-system]: لن يفشل البناء، لكن pip يسقط بصمت إلى السلوك التوافقي القديم.
- أخطاء صيغة TOML، وأشهرها كتابة dependencies كائناً بدل مصفوفة سلاسل، وهي زلة مألوفة لدى القادمين من صيغة Poetry القديمة.
- وضع إعدادات أداة داخل [project]: كل ما ليس حقلاً معيارياً مكانه [tool] حصراً.
- توقّع أن تعديل الملف يثبّت الحزم تلقائياً، والصواب أن تتبعه بتشغيل pip install -e . أو uv sync.
- الخلط بين optional-dependencies التي تُنشر مع الحزمة إضافاتٍ للمستخدمين، و[dependency-groups] التي تبقى للتطوير الداخلي ولا تُنشر.
متى تعتمد الملف إذاً؟ في أي مشروع جديد: فوراً، فهو ما تفترضه الأدوات الحديثة أصلاً. وفي المشاريع القائمة، انقل البيانات الوصفية إلى [project] وإعدادات الأدوات إلى [tool] على مراحل، وستنتهي إلى مشروع أسهل قراءةً وأقرب إلى معايير بايثون الحالية، بملف واحد بدل ثلاثة.
اقرأ أيضاً
- أداة uv لإدارة حزم بايثون: دليل عملي للانتقال من pip وvenv لمعرفة كيف تستخدم pyproject.toml في مشروع يومي فعلي.
- أدوات بايثون الحديثة: دليل uv وRuff وpyproject.toml كخريطة مختصرة للعدّة كاملة.