الانتقال إلى Ruff: فحص وتنسيق بايثون بأداة واحدة

بقلم فريق تقني ·· أدوات المطورين
الانتقال إلى Ruff: فحص وتنسيق بايثون بأداة واحدة

إذا كان مشروع بايثون لديك يشغّل flake8 للفحص وblack للتنسيق وisort لترتيب الاستيراد، فأنت تدير ثلاث أدوات ومعها إعدادات متناثرة في أكثر من ملف. Ruff يجمع هذه الأدوار في أداة واحدة أسرع بمراحل، يقرأ إعداده من pyproject.toml، ويصلح جزءاً كبيراً من الملاحظات آلياً. العقبة ليست في الاقتناع به؛ العقبة يوم الانتقال نفسه: كيف تنقل مشروعاً قائماً دون أن تنهال عليك آلاف التحذيرات دفعة واحدة. أما تعريف الأداة وتثبيتها فتجده في أدوات بايثون الحديثة: دليل uv وRuff وpyproject.toml.

ماذا يستبدل بالضبط

Ruff يحلّ محل flake8 وإضافاته، وisort، وblack كمنسّق متوافق، ومعظم pyupgrade وpydocstyle، ويحذف تعليقات noqa غير المستخدمة. كل عائلة قواعد لها بادئة تبحث عنها بسهولة: Pyflakes هي F، وpycodestyle هي E/W، وisort هي I، وflake8-bugbear هي B، وflake8-comprehensions هي C4، وflake8-simplify هي SIM، وpyupgrade هي UP، وpep8-naming هي N، وpylint هي PL، وقواعد Ruff الأصلية هي RUF.

يبقى حدّ واحد لا يتجاوزه: Ruff لا يفحص الأنواع إطلاقاً. أبقِ mypy أو Pyright بجانبه (وأداة ty من Astral ما زالت في مرحلة تجريبية منذ 2025-12-16).

الفحص والتنسيق أمران منفصلان

هنا تحديداً يقع أغلب المنتقلين في الخطأ. ruff check يدقّق ويبلّغ عن المخالفات، وruff check --fix يصلحها، بينما ruff format هو ما يتولّى التنسيق بديلاً عن black. أمر الفحص لا ينسّق شيئاً. شغّل الاثنين معاً دائماً، محلياً وفي CI.

الإصلاح الآلي: آمن وغير آمن

الإصلاحات الآمنة تُطبَّق افتراضياً وتحافظ على سلوك الكود. غير الآمنة تحتاج --unsafe-fixes لأنها قد تغيّر السلوك أو تحذف تعليقات. خذ RUF015 مثالاً: يستبدل list(...)[0] بـ next(iter(...))، فيتغيّر نوع الاستثناء عند مجموعة فارغة. لا تشغّل الإصلاحات غير الآمنة على دفعة كبيرة ثم تدفعها؛ راجعها ملفاً ملفاً.

إعداد pyproject.toml

مكانه الطبيعي هو pyproject.toml (راجع شرح pyproject.toml). الافتراضي المفعّل محافظ: E4, E7, E9 مع F. ابدأ من مجموعة صغيرة موثوقة ثم وسّع:

[tool.ruff]
line-length = 88

[tool.ruff.lint]
select = ["E", "F", "I"]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
"tests/*.py" = ["D"]

select يحدّد المجموعة الفعّالة، وextend-select يضيف إليها، وignore يستثني قاعدة بعينها. القيم تقبل الكود الكامل مثل F401 أو البادئة F. تجنّب ALL في مشروع قائم؛ إنه يفعّل كل شيء دفعة واحدة.

الهجرة التدريجية دون إغراق

لا تفعّل عشرات المجموعات على كود قديم في يوم واحد. قِس الحجم أولاً بـ ruff check --statistics لترى عدد كل مخالفة، ثم عالج على مراحل. ابدأ بـ E, F, I، أصلح ونسّق:

ruff check --fix . && ruff format .

بعد أن يستقر المشروع، أضِف B ثم UP ثم SIM وC4 مجموعةً مجموعة، كل واحدة في التزام منفصل. حين تنتهي، احذف الإعدادات القديمة: ملف .flake8، وقسمي [tool.black] و[tool.isort]. تركها يربك المساهمين ويخلق تعارضاً صامتاً.

الدمج مع pre-commit وCI والمحرر

في pre-commit أضِف خطافين: ruff-check مع args: [--fix] ثم ruff-format، بهذا الترتيب (الفحص قبل التنسيق)، وثبّت rev على إصدار محدد. في CI استخدم astral-sh/ruff-action@v3. في المحرر ثبّت إضافة Ruff الرسمية لـ VS Code، أو شغّل خادم اللغة ruff server عبر LSP في أي محرر آخر، لترى الملاحظات والتنسيق فور الكتابة.

قرار عملي

أحدث إصدار مستقر هو Ruff 0.15.20 (2026-06-25)، وهو ما زال في سلسلة 0.x، فقد يتغيّر السلوك الافتراضي بين الإصدارات الثانوية؛ لذا ثبّت الإصدار في CI وpre-commit. تعتمده مشاريع كبرى مثل FastAPI وpandas وpytest، فمسألة نضجه في الإنتاج محسومة عملياً. انقل مشروعك اليوم، لكن على مراحل: مجموعة قواعد صغيرة، إصلاحات آمنة أولاً، وحذف الأدوات القديمة بعد أن يخضرّ الفحص. ويبقى مدقّق الأنواع مسؤوليةً منفصلة لا يرفعها Ruff عن كاهلك.