مكتبتي الجديدة للتعامل مع ملفات الإعدادات في بايثون
بواسطة سليمان القسيمي
بتاريخ 12 / 02 / 2025
الأعزاء المبرمجون أينما تصلكم هذه الحروف, سلام الله عليكم جميعًا.
يثير المبرمجون على بعضهم البعض في كثير من الأحيان شيء من الأساطير التي يحدث أن تصادف كثيرًا واقعًا ملموسًا يعيشه ذلك المبرمج. إحدى تلك الأساطير تزعم أن أفضل ما ينتجه المبرمجون هو ذلك العمل الذي يأتي على سبيل العبث والرغبة في التجارب, وهو ما أتيت به اليوم بمشيئة الله تعالى 😊.
في الأيام الماضية؛ كنت أعمل على تطوير برنامج لجهة ما, واحتجت إلى إنشاء صفحة للإعدادات لذلك البرنامج.
ولا أخفيكم, فأنا أستثقل كثيرًا استخدام مكتبة configparser المدمجة مع بايثون كوني أرى أنها تعمل على تعقيد بعض الجوانب التي كان من المفترض أن تكون أبسط مما هي عليه.
وكون أنه كان صاحب البرنامج لديه متسع من الوقت للانتظار؛ قررت أن أصنع مكتبة خاصة للتعامل مع ملف الإعدادات, متجاهلًا كل تلك المكتبات الموجودة من الأساس, وارتأيت أن أشارككم إياها لعلها تفيد أحدكم.
قبل أن نتحدث عن أي شيء يخص المكتبة, علينا أولًا تثبيتها من خلال pip بكتابة الآتي في موجه الأوامر:
pip install oop-config
لقد أسميت المكتبة oop config كون أنها تعتمد على نمط البرمجة الكائنية في التعامل مع ملف الإعدادات, وذلك باستخدام ملفات json في هيكلة جميع الخيارات والأقسام الفرعية.
تدعم المكتبة استخدام نمط القسم الواحد في تضمين جميع الإعدادات, وكذلك نمط الأقسام المتعددة بل وامتفرعة أيضًا
وأقصد بالأقسام المتفرعة أن يكون للقسم الواحد أقسامه الفرعية كلٌ له إعداداته الخاصة
إن المكتبة تقوم بتنفيذ هذه الجوانب كلها بأقصى مراتب التبسيط, كما سترون في الأسطر القادمة:
في بداية الأمر, ستحتاج إلى استدعاء الكلاس Config من المكتبة هكذا
from oop_config import Config
هذا الكلاس يقبل منك معاملين اثنين, الأول هو مسار ملف .json الذي سيتم قراءته أو إنشاؤه إن لم يكن موجودًا
أما المعامل الثاني, فهو عبارة عن معامل اختياري يمكن من خلاله تمرير قاموس بايثون يحتوي على أهم الخيارات الافتراضية لملف الإعدادات.
هذا القاموس يمكن أن يحتوي على قيم مفردة, كالأرقام والنصوص والقيم المنطقية والقوائم
أو أن يحتوي على قيم من نوع قواميس, وفي هذه الحالة يتم تحويل القواميس الضمنية إلى أقسام فرعية
انظر معي المثال:
defaults = {
"debug": True,
"general": {
"language": {
"display": "arabic",
"keyboard": "auto"}}
}
config = Config("settings.json", defaults) # إنشاء ملف الإعدادات في المجلد الحالي وتعيين الخيارات الافتراضية
الآن, ستقوم المكتبة بقراءة قاموس الإعدادات, وتحويل مفاتيح القاموس إلى خصائص للكائن config بحيث يمكن قراءتها وتغييرها بشكل سريع عن طريق كتابة اسم الكائن متبوعًا بنقطة ثم اسم المفتاح هكذا
config.debug = False
واعلم عزيزي القارئ أن بمجرد تعيينك لقيمة الخاصية فإنه سيتم حفظ القيمة الجديدة تلقائيًا في ملف الإعدادات دون الحاجة إلى استدعاء أية دالة للحفظ أو كتابة المحتويات
بالنسبة للقواميس الضمنية, كما في المثال, فهذه القواميس سيتم تحويلها أيضًا إلى خصائص للكائن config, لكنها ستكون عبارة عن أقسام محتوية على إعداداتها الخاصة
كما أنه يمكن للقسم الفرعي أن يضم أقسامًا فرعية إذا كان هو الآخر يحتوي على قواميس
في مثالنا السابق, نجد أن لدينا قسمًا رئيسيًا باسم general, وهذا القسم يحتوي على قسم فرعي باسم language, وهذا القسم لديه إعداداته الخاصة
وفي هذه الحالة يمكن الوصول لإعدادات القسم بكل سهولة هكذا
print(config.general.language.display)
config.general.language.display = "english"
print(config.general.language.display)
انتبه إلى أنه بإمكان الأقسام أن تحوي خليطًأ بين الأقسام الفرعية والقيم المفردة
بمعنى, أنه في القسم general يمكنني إضافة قيمة فردية يتم الوصول إليها بصفة مباشرة
وهذا يقودنا إلى طريقة إضافة العناصر إن لم تكن موجودة
سأقوم الآن بإضافة عنصر باسم "path" إلى القسم general مستخدمًا الوظيفة add_setting على القسم نفسه
config.general.add_setting("path", "default")
ماذا لو أردت إضافة الإعداد مباشرة إلى الكائن config دون تضمينه في أحد الأقسام الفرعية
config.add_setting("path", "default")
print(config.path)
ستُطبع الكلمة default
بنفس الطريقة؛ يمكن إضافة الأقسام الفرعية إما مباشرة إلى الكائن config أو إلى أحد أقسامه الفرعية أو ما دونها من الأقسام الضمنية
وهنا سنستخدم الوظيفة add_section, وهذه الوظيفة تستقبل معامل إجباري واحد وهو اسم القسم, ومعامل آخر اختياري عبارة عن قاموس يحوي الإعدادات الافتراضية لذلك القسم
دعونا نضيف قسم آخر إلى الكائن config مباشرة باسم updates ثم نضيف بشكل يدوي إعدادًأ باسم auto_check ونعين قيمته الافتراضية إلى True
config.add_section("updates")
config.updates.add_setting("auto_check", True)
ماذا لو أردت تمرير الخيارات الافتراضية للقسم الجديد بصفة مباشرة؟
config.add_section("updates", {"auto_check": True})
دعونا نطبع قيمة auto_check ونجرب تغييرها؟
print(config.updates.auto_check)
config.updates.auto_check = False
print(config.updates.auto_check)
يمكنني إضافة القسم updates كقسم فرعي للقسم general هكذا:
config.general.add_section("updates", {"auto_check": True})
print(config.general.updates.auto_check)
هذا كل شيء تقريبًا
قبل أن أختم, لعلي أطرح بعض الملحوظات البسيطة:
1. في المشروع الذي ستستخدم فيه هذه المكتبة, أفضل أن تقوم بإنشاء كائن واحد فقط من الكلاس Config وتقوم باستدعاء هذا الكائن في جميع ملفاتك الأخرى التي تحتاج فيها إلى التعامل مع الإعدادات سواء بتغييرها أو جلبها.
وبطبيعة الحال لن يحدث شيء في حال أنشأت كائنات متعددة؛ إلا أنه لا أرى من الداعي القيام بذلك.
2. هذه أول نسخة من المكتبة, وهذا يعني احتمالية وجود أخطاء, لذلك أسعد بتلقي أية ملاحظات إن وجدت.
شكرًا لكم جميعًا.