الوحدة configparser‎‎ في بايثون

من موسوعة حسوب

تقدّم هذه الوحدة الصنف ConfigParser الذي يستخدم لغة إعدادات بسيطة تقدّم بنية مشابهة للبنية التي تقدّمها ملفات INI في نظام Microsoft Windows. ويمكن استخدام هذا الصنف لكتابة برامج وتطبيقات يمكن للمستخدمين تخصيصها بكل سهولة.

ملاحظة: لا تفسّر هذه المكتبة ولا تكتب لواحق قيمة-نوع التي يستخدمها إصدار سجلّ نظام ويندوز Windows Registry الموسّع بصيغة INI.

بداية سريعة

لنبدأ بملف الإعدادات البسيط التالي:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no

يتكوّن الملف بصورة أساسية من أقسام sections ويتضمّن كل قسمٍ مفاتيح وقيم. يمكن للأصناف التي تقدّمها وحدة configparser أن تقرأ وتكتب مثل هذه الملفات. يمكن إنشاء محتويات ملف الإعدادات السابق برمجيًا باستخدام الشيفرة التالية:

>>> import configparser
>>> config = configparser.ConfigParser()
>>> config['DEFAULT'] = {'ServerAliveInterval': '45',
...                      'Compression': 'yes',
...                      'CompressionLevel': '9'}
>>> config['bitbucket.org'] = {}
>>> config['bitbucket.org']['User'] = 'hg'
>>> config['topsecret.server.com'] = {}
>>> topsecret = config['topsecret.server.com']
>>> topsecret['Port'] = '50022'     # تعديل المفسر
>>> topsecret['ForwardX11'] = 'no'  # تعديل المفسر
>>> config['DEFAULT']['ForwardX11'] = 'yes'
>>> with open('example.ini', 'w') as configfile:
...   config.write(configfile)
...

يبين المثال السابق أنّ التعامل مع مفسّر الإعدادات يشبه إلى حدٍّ كبير التعامل مع القواميس ولكن مع بعض الفروقات التي سيرد ذكرها لاحقًا. يمكن كذلك قراءة ملفات الإعدادات والاستفادة من محتوياتها كما يبيّن المثال التالي:

>>> config = configparser.ConfigParser()
>>> config.sections()
[]
>>> config.read('example.ini')
['example.ini']
>>> config.sections()
['bitbucket.org', 'topsecret.server.com']
>>> 'bitbucket.org' in config
True
>>> 'bytebong.com' in config
False
>>> config['bitbucket.org']['User']
'hg'
>>> config['DEFAULT']['Compression']
'yes'
>>> topsecret = config['topsecret.server.com']
>>> topsecret['ForwardX11']
'no'
>>> topsecret['Port']
'50022'
>>> for key in config['bitbucket.org']:  
...     print(key)
user
compressionlevel
serveraliveinterval
compression
forwardx11
>>> config['bitbucket.org']['ForwardX11']
'yes'

يقدّم القسم DEFAULT القيم الافتراضية لجميع الأقسام، ويجب التنبيه هنا إلى أنّ أسماء المفاتيح في الأقسام غير حساسة لحالة الأحرف وتخزّن بالأحرف الصغيرة.

أنواع البيانات المدعومة

لا يستطيع مفسّر الإعدادات تخمين نوع البيانات في القيم المضمّنة في ملفات الإعدادات، ويخزّنها داخليًا كسلاسل نصيّة على الدوام، وهذا يعني وجوب تحويل البيانات إلى الأنواع المطلوبة يدويًا:

>>> int(topsecret['Port'])
50022
>>> float(topsecret['CompressionLevel'])
9.0

ولمّا كانت هذه العملية شائعة جدًّا، فإنّ مفسّر الإعدادات يقدّم مجموعة من توابع getter للتعامل مع الأعداد الصحيحة، والأعداد ذات الفواصل العشرية، والقيم البوليانية والتي تمثّل المشكلة الأكبر ذلك لأنّه لا فائدة من استخدام الدالة bool()‎ لأنّ التعبير bool('False')‎ يعطي النتيجة True دائمًا؛ ولهذا السبب يقدّم مفسّر الإعدادات التابع getboolean()‎. هذا التابع غير حساس لحالة الأحرف ويمكنه التعرّف على القيم البوليانية من الكلمات ‎'yes'/'no'‎، ‎'true'/'false'‎، ‎'on'/'off'‎ و ‎'1'/'0'‎. فعلى سبيل المثال:

>>> topsecret.getboolean('ForwardX11')
False
>>> config['bitbucket.org'].getboolean('ForwardX11')
True
>>> config.getboolean('bitbucket.org', 'Compression')
True

يقدّم مفسّر الإعدادات كذلك التابعين getint()‎ و getfloat()‎، ويمكن إلى جانب ما سبق تسجيل محوّلات خاصة بالمستخدم وتخصيص المحوّلات الموجودة أيضًا.

القيم التراجعية Fallback Values

كما هو الحال مع القواميس يمكن استخدام التابع get()‎ الخاص بقسم معين لتقديم قيم تراجعية:

>>> topsecret.get('Port')
'50022'
>>> topsecret.get('CompressionLevel')
'9'
>>> topsecret.get('Cipher')
>>> topsecret.get('Cipher', '3des-cbc')
'3des-cbc'

ولكن يجب الانتباه إلى أنّ القيم الافتراضية تمتلك الأسبقية على القيم التراجعية، ففي المثال السابق جرى تعيين المفتاح 'CompressionLevel' في القسم 'DEFAULT' فقط، وإن حاولنا الحصول على قيمته من القسم 'topsecret.server.com' فسنحصل دائمًا على القيم الافتراضية، حتى لو جرى تحديد قيمته التراجعية:

>>> topsecret.get('CompressionLevel', '3')
'9'

كذلك يجب التنبيه إلى أنّ التابع get()‎ الذي يعمل على مستوى المفسّر يقدّم واجهة مخصّصة وأكثر تعقيدًا، وهي متوافقة مع الإصدارات السابقة. عند استخدام هذا التابع يمكن تقديم قيمة تراجعية باستخدام المعامل المفتاحي fallback:

>>> config.get('bitbucket.org', 'monster',
...            fallback='No such things as monsters')
'No such things as monsters'

يمكن استخدام المعامل fallback مع التوابع getint()‎‎ و getfloat()‎ و getboolean()‎‎، فعلى سبيل المثال:

>>> 'BatchMode' in topsecret
False
>>> topsecret.getboolean('BatchMode', fallback=True)
True
>>> config['DEFAULT']['BatchMode'] = 'no'
>>> topsecret.getboolean('BatchMode', fallback=True)
False

بنية ملفات INI المدعومة

يتكوّن ملف الإعدادات من أقسام، ويبدأ كل قسم منها بترويسة تحمل اسم القسم [section] متبوعةً بعدد من المدخلات على هيئة مفتاح/قيمة، وتفصل سلسلة نصية معينة بين المفتاح والقيمة (= أو : افتراضيًا). تكون أسماء الأقسام غير حساسة لحالة الأحرف افتراضيًا، أمّا المفاتيح فلا، وتُحذف جميع المسافات البيضاء من بداية ونهاية المفاتيح والقيم. يمكن الاستغناء عن القيم، وفي هذه الحالة يمكن الاستغناء أيضًا عن الفاصل بين المفتاح والقيمة. ويمكن للقيم أن تمتدّ على أسطر متعددة، ما دامت مزاحة عن السطر الأول من القيمة. يمكن أن يتعامل مفسّر الإعدادات مع الأسطر الفارغة كجزء من قيم متعددة الأسطر أو يمكنه أن يتجاهلها، وكلّ ذلك بالاعتماد على وضع المفسّر المستخدم.

يمكن أن تتضمّن ملفات الإعدادات التعليقات، وتكون مسبوقة بمحارف محددة (# و ; افتراضيًا)، وتظهر في سطر منفرد وتكون قابلة للإزاحة.

فعلى سبيل المثال:

[Simple Values]
key=value
spaces in keys=allowed
spaces in values=allowed as well
spaces around the delimiter = obviously
you can also use : to delimit keys from values

[All Values Are Strings]
values like this: 1000000
or this: 3.14159265359
are they treated as numbers? : no
integers, floats and booleans are held as: strings
can use the API to get converted values directly: true

[Multiline Values]
chorus: I'm a lumberjack, and I'm okay
    I sleep all night and I work all day

[No Values]
key_without_value
empty string value here =

[You can use comments]
# like this
; or this

# By default only in an empty line.
# Inline comments can be harmful because they prevent users
# from using the delimiting characters as parts of values.
# That being said, this can be customized.

    [Sections Can Be Indented]
        can_values_be_as_well = True
        does_that_mean_anything_special = False
        purpose = formatting for readability
        multiline_values = are
            handled just fine as
            long as they are indented
            deeper than the first line
            of a value
        # Did I mention we can indent comments, too?

استيفاء القيم Interpolation of values

يدعم الصنف ConfigParser استيفاء القيم، ما يعني إمكانية معالجة القيم قبل إعادتها من استدعاءات التابع get()‎.

الصنف configparser.BasicInterpolation

هذا الصنف هو الطريقة الافتراضية التي يستخدمها الصنف ConfigParser لاستيفاء القيم. يتيح هذا الصنف استخدام سلاسل تنسيق نصية في قيم ملف الإعدادات وتشير سلاسل التنسيق هذه إلى قيم اخرى في القسم نفسه، أو إلى قيم في قسم DEFAULT الخاص، ويمكن تقديم قيم افتراضية إضافية عند تهيئة الملف.

فعلى سبيل المثال:

[Paths]
home_dir: /Users
my_dir: %(home_dir)s/lumberjack
my_pictures: %(my_dir)s/Pictures

إن أخذ المعامل interpolation في الدالة البانية للصنف ConfigParser القيمة BasicInterpolation()‎ فإنّ الصنف ConfigParser سيحلّل ‎%(home_dir)s‎ إلى القيمة home_dir (وهو المجلد ‎/Users‎ في هذه الحالة)، وسيحلّل ‎%(my_dir)s‎ إلى ‎/Users/lumberjack.

من الجدير بالذكر أنّ عمليات الاستيفاء كلها تجري حسب الطلب؛ لهذا لا حاجة إلى استخدام ترتيب معيّن للمفاتيح في ملف الإعدادات عند استخدامها في إشارات مترابطة.

في حال تعيين القيمة None للمعامل interpolation في الدالة البانية للصنف ConfigParser، فإنّ الصنف سيعيد العبارة ‎%(home_dir)s/Pictures كقيمة للمفتاح my_pictures والعبارة ‎%(home_dir)‎‎s/lumberjack كقيمة للمفتاح my_dir.

الصنف configparser.ExtendedInterpolation

طريقة أخرى لاستيفاء القيم وتستخدم صيغة أكثر تقدّمًا، وتستخدم في حزمة zc.buildout على سبيل المثال. يُقصد بالاستيفاء الموسّع Extended interpolation استخدام الصيغة ‎${section:option}‎ للإشارة إلى قيمة في قسم آخر. يمكن للاستيفاء أن يمتد لمستويات متعددة، وإن حذف الجزء section:‎ من الصيغة فإنّ عملية الاستيفاء تكون ضمن القسم الحالي افتراضيًا (ومن المحتمل أن تُستوفى القيم الافتراضية من القسم الخاص).

فعلى سبيل المثال، ستظهر الإعدادات التي مرّ ذكرها في قسم الاستيفاء العادي بالمظهر التالي في الاستيفاء الموسّع:

[Paths]
home_dir: /Users
my_dir: ${home_dir}/lumberjack
my_pictures: ${my_dir}/Pictures

يمكن جلب قيم من أقسام أخرى أيضًا:

[Common]
home_dir: /Users
library_dir: /Library
system_dir: /System
macports_dir: /opt/local

[Frameworks]
Python: 3.2
path: ${Common:system_dir}/Library/Frameworks/

[Arthur]
nickname: Two Sheds
last_name: Jackson
my_dir: ${Common:home_dir}/twosheds
my_pictures: ${my_dir}/Pictures
python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}

الوصول إلى القيم باستخدام بروتوكول الربط Mapping Protocol

ملاحظة: هذا البروتوكول جديد في الإصدار 3.2 من بايثون.

يُطلق هذا الاسم على الوظيفة التي تتيح استخدام كائنات خاصة كما لو أنّها كانت قواميس. وتُستخدم الصيغة ‎parser['section']['option']‎ كواجهة للربط في وحدة configparser.

يعيد الجزء ‎parser['section']‎ وسيطًا proxy لبيانات القسم في المحلل، وهذا يعني أنّ القيم لا تنسخ ولكنّها تُؤخذ من المفسّر الأصلي عند الحاجة. ويجب التنبيه إلى أنّه عند تغيير القيم في وسيط القسم فإنّ ما يجري في الواقع هو تعديل القيم في المفسّر الأصلي.

تسلك كائنات الوحدة configparser سلوكًا مقاربًا للقواميس إلى حدٍّ كبير، وواجهة الربط كاملة وتلتزم بما يفرضه الصنف MutableMapping ABC، ولكن هناك بعض الاختلافات التي يجب أخذها بالحسبان:

  • جميع المفاتيح في الأقسام غير حساسة لحالة الأحرف، فعلى سبيل المثال يعيد التعبير:
for option in parser["section"]

يعيد أسماء المفاتيح التي المحوّلة بواسطة التابع optionxform()‎، أي أنّ المفاتيح تكون بالحرف الصغير افتراضيًا، وفي نفس الوقت، يعطي التعبيران التاليان النتيجة True لقسم يتضمّن المفتاحين:

"a" in parser["section"]
"A" in parser["section"]
  • تتضمّن جميع الأقسام كذلك قيم DEFAULTSECT، وهذا يعني أنّ القسم لن يبدو فارغًا إن استدعي التابع clear()‎ على ذلك القسم؛ وذلك لعدم إمكانية حذف القيم الافتراضية من القسم (لأنّها ليست موجودة فيه أصلًا)، وإن كان القسم يعيد تعريف القيم الافتراضية، فإنّ حذف القيم الجديدة في القسم سيتسبب في إعادة إظهار القيم الافتراضية. يؤدي حذف أيّ قيمة افتراضية إلى إطلاق الخطأ KeyError.
  • لا يمكن حذف DEFAULTSECT من المفسّر:
    • يؤدي حذفه إلى إطلاق الخطأ ValueError
    • لا يؤثر استدعاء التابع parser.clear()‎ عليه إطلاقًا
    • لن يعاد نتيجة لاستدعاء التابع parser.popitem()
  • المعامل الثاني في التابع parser.get(section, option, **kwargs)‎ لا يمثّل قيمة تراجعية fallback value. ولكن يجب الانتباه إلى أنّ توابع get()‎ التي تعمل على مستوى القسم متوافقة مع بروتوكول الربط والواجهة البرمجية القديمة للوحدة configparser.
  • التابع ()parser.items متوافق مع بروتوكول الربط (يعيد قائمة تحتوى على أزواج تتألف من (اسم القسم، وسيط القسم) section_name, section_proxy إضافة إلى DEFAULTSECT). ولكن يمكن تنفيذ هذا التابع باستخدام التوابع التالية: parser.items(section, raw, vars)‎، وفي هذه الحالة سيعيد التابع قائمة تتضمّن أزواجًا تتألّف من (الخيار، القيمة) option, value للقسم المحدّد، مع توسيع جميع عمليات الاستيفاء (إلا إذا كان المعامل raw يحمل القيمة True).

يأخذ بروتوكول الربط الأولوية على الواجهة البرمجية القديمة؛ ولهذا فإنّ الأصناف الفرعية التي تعيد تعريف الواجهة الأصلية ستمتلك روابط mappings تعمل كما هو متوقع منها.

تخصيص عمل المفسّر

تتعدّد صيغ ملفات INI بتعدد التطبيقات التي تستخدم هذه الملفات، وتحاول وحدة configparser تقديم دعم جيّد لأكبر مجموعة من أنماط INI المتوفّرة حاليًّا. صمّمت طريقة عمل الوحدة بالاستناد إلى أنماط قديمة، ومن المرجّح أنّك سترغب في تخصيص بعض الميزات فيها.

يمكن استخدام الخيارات المتاحة في التابع ‎__init__()‎ (وهي الطريقة الأكثر شيوعًا) لتغيير طريقة عمل مفسّر الإعدادات:

  • defaults, القيمة الافتراضية: None

يستقبل هذا الخيار قاموسًا مكوّنًا من أزواج مفتاح-قيمة والتي ستُضاف في البداية إلى القسم DEFAULT. وتمثّل هذه الطريقة وسيلة جيّدة لدعم ملفات الإعدادات المختصرة والتي لا تحدّد قيمًا معيّنة، والتي تككون شبيهة بالإعدادات الافتراضية الموثّقة.

ملاحظة: إن كنت ترغب في تحديد قيم افتراضية لقسم معيّن، استخدم التابع read_dict()‎ قبل قراءة الملف الأصلي.

يؤثّر هذا الخيار على طريقة عمل بروتوكول الربط وعلى طريقة كتابة ملف الإعدادات تأثيرًا كبيرًا؛ فعند استخدام القاموس المرتّب الافتراضي، يُخزّن كل قسم في ملف الإعدادات بنفس ترتيب إضافته إلى المفسّر، وينطبق الأمر نفسه على الخيارات التي تنطوي عليها الأقسام.

ويمكن استخدام نوع آخر من أنواع القواميس لترتيب الأقسام والخيارات -على سبيل المثال- عند إعادة كتابة ملف الإعدادات. كذلك يمكن استخدام قاموس عاديّ إن كان الأداء الجيّد أمرًا مطلوبًا.

ملاحظة: هناك عدد من الطرق لإضافة مجموعة من أزواج مفتاح-قيمة بخطوة واحدة، وإن كنت تستخدم قاموسًا عاديًا في مثل هذه العمليات، فإنّ ترتيب المفاتيح سيكون عشوائيًا، إليك المثال التالي:

>>> parser = configparser.ConfigParser()
>>> parser.read_dict({'section1': {'key1': 'value1',
...                                'key2': 'value2',
...                                'key3': 'value3'},
...                   'section2': {'keyA': 'valueA',
...                                'keyB': 'valueB',
...                                'keyC': 'valueC'},
...                   'section3': {'foo': 'x',
...                                'bar': 'y',
...                                'baz': 'z'}
... })
>>> parser.sections()  
['section3', 'section2', 'section1']
>>> [option for option in parser['section3']] 
['baz', 'foo', 'bar']

ستحتاج إلى استخدام قاموس مرتّب في هذه العمليات أيضًا:

>>> from collections import OrderedDict
>>> parser = configparser.ConfigParser()
>>> parser.read_dict(
...   OrderedDict((
...     ('s1',
...      OrderedDict((
...        ('1', '2'),
...        ('3', '4'),
...        ('5', '6'),
...      ))
...     ),
...     ('s2',
...      OrderedDict((
...        ('a', 'b'),
...        ('c', 'd'),
...        ('e', 'f'),
...      ))
...     ),
...   ))
... )
>>> parser.sections()  
['s1', 's2']
>>> [option for option in parser['s1']]  
['1', '3', '5']
>>> [option for option in parser['s2'].values()]  
['b', 'd', 'f']
  • allow_no_value, القيمة الافتراضية: False

من المعروف أنّ بعض ملفات الإعدادات تتضمّن إعدادات خالية من القيم، ومع ذلك فإنّها متوافقة مع الصيغة التي تدعمها وحدة configparser.

يمكن استخدام المعامل allow_no_value في الدالة البانية للسماح باستخدام مثل هذه القيم:

>>> import configparser

>>> sample_config = """
... [mysqld]
...   user = mysql
...   pid-file = /var/run/mysqld/mysqld.pid
...   skip-external-locking
...   old_passwords = 1
...   skip-bdb
...   # we don't need ACID today
...   skip-innodb
... """
>>> config = configparser.ConfigParser(allow_no_value=True)
>>> config.read_string(sample_config)

>>> # يجري التعامل مع الإعدادات ذات القيم بالطريقة المعتادة:
>>> config["mysqld"]["user"]
'mysql'

>>> # الإعدادات الخالية من القيم تقدّم القيمة None:
>>> config["mysqld"]["skip-bdb"]

>>> # ما زالت الإعدادات غير الموجودة تطلق أخطاءً:
>>> config["mysqld"]["does-not-exist"]
Traceback (most recent call last):
  ...
KeyError: 'does-not-exist'
  • delimiters, القيمة الافتراضية: ('=', ':')

الفواصل عبارة عن سلاسل نصية فرعية تفصل بين المفاتيح والقيم في القسم الواحد، ويعدّ أول ظهور للسلسلة النصية الفرعية الفاصلة في السطر على أنّه فاصل، وهذا يعني أنّ من الممكن أن تكون الفواصل موجودة في القيم ولكن ليس في المفاتيح. راجع المعامل space_around_delimiters في التابع ConfigParser.write()‎.

  • comment_prefixes, القيمة الافتراضية: ‎('#' ، ';')
  • inline_comment_prefixes, القيمة الافتراضية: None

سوابق التعليقات Comment prefixes هي سلاسل نصية تشير إلى بداية التعليق في ملف الإعدادات. تستخدم سوابق التعليقات في الأسطر الفارغة (والتي يمكن أن تكون مزاحة) أما سوابق التعليقات السطرية فيمكن استخدامها بعد أي قيمة صالحة (مثل: أسماء الأقسام، الخيارات والأسطر الفارغة أيضًا). تكون التعليقات السطرية معطّلة افتراضيًا، وتستخدم السابقتان '#' و ';' مع التعليقات.

ملاحظة: في الإصدارات السابقة من الوحدة configparser كانت القيمة الافتراضية لسوابق التعليقات comment_prefixes=('#', ';')‎ ولسوابق التعليقات السطرية inline_comment_prefixes=(';',)‎. هذا السلوك تغيّر في الإصدار 3.2 من بايثون.

يجب الانتباه إلى أنّه ليس بمقدور مفسّرات الإعدادات أن تهرّب المحارف المستخدمة كسوابق للتعليقات؛ ولهذا يمكن أن يمنع استخدام سوابق التعليقات السطرية المستخدمين من تحديد قيم للخيارات تتضمّن المحارف المستخدمة كسوابق للتعليقات؛ لذا ينصح بالامتناع عن تحديد أيّ قيمة لسوابق التعليقات السطرية.

في كثير من الأحيان تكون الطريقة الوحيدة لتخزين المحارف المستخدمة كسوابق للتعليقات في بداية السطر ضمن قيم متعددة الأسطر هي في استيفاء interpolate السابقة، فعلى سبيل المثال:

>>> from configparser import ConfigParser, ExtendedInterpolation
>>> parser = ConfigParser(interpolation=ExtendedInterpolation())
>>> # the default BasicInterpolation could be used as well
>>> parser.read_string("""
... [DEFAULT]
... hash = #
...
... [hashes]
... shebang =
...   ${hash}!/usr/bin/env python
...   ${hash} -*- coding: utf-8 -*-
...
... extensions =
...   enabled_extension
...   another_extension
...   #disabled_by_comment
...   yet_another_extension
...
... interpolation not necessary = if # is not at line start
... even in multiline values = line #1
...   line #2
...   line #3
... """)
>>> print(parser['hashes']['shebang'])

#!/usr/bin/env python
# -*- coding: utf-8 -*-
>>> print(parser['hashes']['extensions'])

enabled_extension
another_extension
yet_another_extension
>>> print(parser['hashes']['interpolation not necessary'])
if # is not at line start
>>> print(parser['hashes']['even in multiline values'])
line #1
line #2
line #3
  • strict, القيمة الافتراضية: True

إن أخذ هذا الخيار القيمة True، لن يسمح المفسّر بتكرار أيّ قسم أو خيار عند قراءة الإعدادات من مصدر واحد (باستخدام التوابع read_file() أو read_string()‎ أو read_dict()). يُنصح باستخدام المفسّر في وضع التدقيق strict في التطبيقات الجديدة.

ملاحظة: كانت القيمة الافتراضية لهذا الخيار هي False في الإصدارات التي تسبق الإصدار 3.2 من بايثون.

  • empty_lines_in_values, القيمة الافتراضية: True

يمكن للقيم في مفسّرات الإعدادات أن تمتدّ على أسطر متعددة ما دامات مزاحة عن المفتاح الذي يحمل تلك القيمة، وتسمح المفسّرات افتراضيًا بأن تكون الأسطر الفارغة جزءًا من القيم، كذلك يمكن إزاحة المفاتيح حسب الرغبة وذلك لزيادة مقروئية الملف، ولكن ارتفاع حجم الملف ودرجة تعقيد بنيته قد يؤديان إلى التسبب في ارتباك المستخدم عند التعامل مع الملف. إليك المثال التالي:

[Section]
key = multiline
  value with a gotcha

 this = is still a part of the multiline value of 'key'

قد تتسبب بنية هذا الملف في حدوث مشاكل إن كان المستخدم يستعمل خطًا نسبيًا لتحرير الملف، وهذا هو السبب الذي يدعو إلى تعطيل الأسطر الفارغة إن كان التطبيق في غنى عنها؛ وبذلك تفصل الأسطر الفارغة بين المفاتيح دائمًا، وهكذا ينتج عن الملف في المثال السابق مفتاحان هما key و this.

  • default_section, القيمة الافتراضية: configparser.DEFAULTSECT (أي: "DEFAULT")

إن من أقوى الميّزات التي تقدّمها هذه المكتبة هي إمكانية استخدام قسم خاص بالقيم الافتراضية الخاصة بالأقسام الأخرى أو لأغراض الاستيفاء، وتسمح هذه الميزة للمستخدمين بإنشاء ملفات إعدادات معقّدة وواضحة في نفس الوقت. يدعى هذا القسم عادة بالاسم "DEFAULT" ولكن يمكن تخصيص ذلك للإشارة إلى اسم أي قسم آخر صالح للاستخدام، وعادة ما تستعمل الأسماء "general" أو "common". يستخدم الاسم المقدّم لتمييز الأقسام الافتراضية عند قراءة الإعدادات من أي مصدر ويستخدم عند كتابة الإعدادات في ملف الإعدادات. يمكن الحصول على القيمة الحالية لهذا القسم عن طريق الخاصية parser_instance.default_section ويمكن تعديلها في وقت التشغيل (أي تحويل الملفات من صيغة إلى أخرى).

  • interpolation, القيمة الافتراضية: configparser.BasicInterpolation

يمكن تخصيص طريقة استيفاء القيم عن طريق تقديم وسيلة خاصة في المعامل interpolation. يمكن استخدام القيمة None لإيقاف عملية الاستيفاء تمامًا، ويمكن استخدام القيمة ExtendedInterpolation()‎ والتي تقدّم طريقة متقدّمة في استيفاء القيم مستوحاة من الحزمة zc.buildout. راجع قسم استيفاء القيم للاطلاع على المزيد من المعلومات. تمتلك كائنات RawConfigParser القيمة الافتراضية None.

  • converters, القيمة الافتراضية: غير محددة

تقدّم مفسّرات الإعدادات توابع مهمّتها جلب قيمة الخيار في ملف الإعدادات مع التحويل إلى الأنواع المناسبة، وتستخدم المفسّرات التوابع getint()‎ و getfloat()‎ و getboolean()‎ افتراضيًا. ويمكن للمستخدم إنشاء توابع خاصة حسب حاجته وذلك بتعريفها في صنف فرعي أو تمرير قاموس يكون كل مفتاح فيه هو اسم المحول وكلّ قيمة فيه هي كائنًا قابلًا للاستدعاء callable يطبّق عملية التحويل المطلوبة. فعلى سبيل المثال يؤدي تمرير القاموس {'decimal':decimal.Decimal} إلى إضافة التابع getdecimal()‎ في كائن المفسّر وفي وسائط الأقسام section proxies كلّها. وبعبارة أخرى، سيكون بالإمكان استخدام التعبيرين ‎parser_instance.getdecimal('section','key', fallback=0)‎ و ‎parser_instance['section'].getdecimal('key', 0)‎.

إن كان المحوّل بحاجة غلى الوصول إلى حالة المفسّر، فيمكن استخدامه حينئذ كتابع في صنف فرعي لمفسّر الإعدادات. إن كان اسم هذا التابع يبدأ بالكلمة get، فإنّه سيكون متوفّرًا في جميع وسائط الأقسام وبصيغة متوافقة مع القواميس (راجع المثال عن التابع getdecimal()‎ في أعلاه).

يمكن زيادة التخصيص عن طريق إعادة تعريف القيم الافتراضية لخصائص المفسّر، وتعرّف القيم الافتراضية في الأصناف؛ ليكون بالإمكان إعادة تعريفها بواسطة الأصناف المتفرّعة عنها أو عن طريق إسناد القيم إلى خصائص الأصناف.

ConfigParser.BOOLEAN_STATES

ينظر مفسّر الإعدادات -افتراضيًا- عند استخدامه للتابعgetboolean()‎ إلى القيم: ‎'1'، 'yes'، 'true'، 'on'‎ على أنّها True، والقيم ‎'0'، 'no'، 'false'، 'off'‎ على أنّها False. يمكن إعادة تعريف ما سبق باستخدام قاموس مخصّص يتضمّن السلاسل النصية والقيم المنطقية الناتجة عنها، فعلى سبيل المثال:

>>> custom = configparser.ConfigParser()
>>> custom['section1'] = {'funky': 'nope'}
>>> custom['section1'].getboolean('funky')
Traceback (most recent call last):
...
ValueError: Not a boolean: nope
>>> custom.BOOLEAN_STATES = {'sure': True, 'nope': False}
>>> custom['section1'].getboolean('funky')
False

كذلك يمكن استخدام عبارات مثل accept/reject أو enabled/disabled.

التابع ConfigParser.optionxform(option)‎

يحوّل التابع أسماء الخيارات في كل عمليّة قراءة أو جلب أو تعيين، ويحوّل الحروف في الأسماء -افتراضيًا- إلى حروف صغيرة؛ وهذا يعني كذلك أنّه عند كتابة ملفات الإعدادات فإنّ جميع المفاتيح ستكتب بحروف صغيرة. يمكن إعادة تعريف هذا التابع إن كانت طريقة عمله غير ملائمة.

مثال:

>>> config = """
... [Section1]
... Key = Value
...
... [Section2]
... AnotherKey = Value
... """
>>> typical = configparser.ConfigParser()
>>> typical.read_string(config)
>>> list(typical['Section1'].keys())
['key']
>>> list(typical['Section2'].keys())
['anotherkey']
>>> custom = configparser.RawConfigParser()
>>> custom.optionxform = lambda option: option
>>> custom.read_string(config)
>>> list(custom['Section1'].keys())
['Key']
>>> list(custom['Section2'].keys())
['AnotherKey']

ConfigParser.SECTCRE

تعبير نمطيّ مصرّف يستخدم لتفسير عناوين الأقسام. يطابق التعبير النمطي الافتراضي العبارة [section] مع الاسم "section". تعدّ المسافات البيضاء جزءًا من اسم القسم، ولهذا فإنّ التعبير [ larch ] سيُقرأ هكذا " larch ".

يمكن إعادة تعريف هذه الخاصية إن كانت طريقة عملها غير ملائمة.

مثال:

>>> import re
>>> config = """
... [Section 1]
... option = value
...
... [  Section 2  ]
... another = val
... """
>>> typical = configparser.ConfigParser()
>>> typical.read_string(config)
>>> typical.sections()
['Section 1', '  Section 2  ']
>>> custom = configparser.ConfigParser()
>>> custom.SECTCRE = re.compile(r"\[ *(?P<header>[^]]+?) *\]")
>>> custom.read_string(config)
>>> custom.sections()
['Section 1', 'Section 2']

ملاحظة:

تستخدم كائنات ConfigParser الخاصية OPTCRE والتي تتعرّف على أسطر الخيارات، ولكن لا ينصح بإعادة تعريفها لأنّ ذلك قد يؤدي إلى حدوث تضارب مع خيار الدالة البانية allow_no_value ومع الفواصل delimiters.

أمثلة على الواجهة البرمجية القديمة

تقدّم وحدة configparser واجهة برمجية قديمة تتضمّن توابع get و set وذلك بهدف التوافق مع الإصدارات السابقة من اللغة. وعلى الرغم من وجود بعض الحالات التي يمكن استخدام التوابع المبيّنة أدناه فيها؛ يُنصح بالوصول إلى البيانات باستخدام بروتوكول الربط في المشاريع الجديدة. ويجدر التنبيه إلى أنّ الواجهة البرمجية القديمة قد تكون في بعض الأحيان أكثر تقدّمًا وذات مستوى أدنى وقد تكون في بعض الأحيان معاكسة للتوقعات.

يبين المثال التالي طريقة كتابة ملف إعدادات:

import configparser

config = configparser.RawConfigParser()

# يمكن استخدام دوال التعيين في كائنات RawConfigParser لتعيين قيم غير نصية
# في المفاتيح داخليًا، ولكن تطلق اللغة خطأ عند محاولة الكتابة إلى ملفّ إعدادات
# عند الوصول إليه في الوضع non-raw. لا تسمح عملية تعيين القيم باستخدام بروتوكول الربط أو التابع set()‎ في كائنات ConfigParser بحدوث عمليات إسناد مماثلة.

config.add_section('Section1')
config.set('Section1', 'an_int', '15')
config.set('Section1', 'a_bool', 'true')
config.set('Section1', 'a_float', '3.1415')
config.set('Section1', 'baz', 'fun')
config.set('Section1', 'bar', 'Python')
config.set('Section1', 'foo', '%(bar)s is %(baz)s!')

# كتابة ملف الإعدادات إلى الملف 'example.cfg'

with open('example.cfg', 'w') as configfile:
    config.write(configfile)

يبين المثال التالي طريقة قراءة ملفّ الإعدادات مرة أخرى:

import configparser

config = configparser.RawConfigParser()
config.read('example.cfg')

# يطلق التابع getfloat()‎ استثناءً إن لم تكن القيمة عددًا ذا فاصلة عائمة
# وكذلك الأمر بالنسبة إلى التابعين getint()‎ وgetboolean()‎ مع الأنواع المقابلة لكل تابع.

a_float = config.getfloat('Section1', 'a_float')
an_int = config.getint('Section1', 'an_int')
print(a_float + an_int)

# لاحظ أنّ المخرجات التالية لا تستوفي ‎'%(bar)s'‎ أو ‎'%(baz)s'‎.
# وذلك لأنّنا نستخدم RawConfigParser()‎.

if config.getboolean('Section1', 'a_bool'):
    print(config.get('Section1', 'foo'))

يجب استخدام الكائن ConfigParser لإجراء عملية الاستيفاء:

import configparser

cfg = configparser.ConfigParser()
cfg.read('example.cfg')

# أسند القيمة True إلى المعامل *raw* في التابع get()‎ إن كنت ترغب
# في تعطيل عملية الاستيفاء في إحدى عمليات get.
print(cfg.get('Section1', 'foo', raw=False))  # -> "Python is fun!"
print(cfg.get('Section1', 'foo', raw=True))   # -> "%(bar)s is %(baz)s!"

# المعامل الاختياري *vars* قاموس يتضمّن عناصر تأخذ
# الأسبقية على عملية الاستيفاء.
print(cfg.get('Section1', 'foo', vars={'bar': 'Documentation',
                                       'baz': 'evil'}))

# يمكن استخدام المعامل الاختياري *fallback* لتوفير قيمة تراجعية
print(cfg.get('Section1', 'foo'))
      # -> "Python is fun!"

print(cfg.get('Section1', 'foo', fallback='Monty is not.'))
      # -> "Python is fun!"

print(cfg.get('Section1', 'monster', fallback='No such things as monsters.'))
      # -> "No such things as monsters."

# لا يمكن استخدام التعبير 
# print(cfg.get('Section1', 'monster'))
# وذلك لأنّه سيتسبب في إطلاق الاستثناء NoOptionError، ولكن يمكن استخدام:

print(cfg.get('Section1', 'monster', fallback=None))
      # -> None

القيم الافتراضية متوفّرة في كلا نوعي الكائن ConfigParser، وتستخدم هذه القيم في عملية الاستيفاء إن كان الخيار المستخدم غير معرّف في مكان آخر.

import configparser

# نسخة جديدة من الصنف مع تمرير القيم الافتراضية لكل خيار
config = configparser.ConfigParser({'bar': 'Life', 'baz': 'hard'})
config.read('example.cfg')

print(config.get('Section1', 'foo'))     # -> "Python is fun!"
config.remove_option('Section1', 'bar')
config.remove_option('Section1', 'baz')
print(config.get('Section1', 'foo'))     # -> "Life is hard!"

كائنات ConfigParser

تعدّ هذه الكائنات المفسّر الرئيسي لملفات الإعدادات.

كائنات RawConfigParser

هذه الكائنات هي النسخة القديمة من كائنات ConfigParser.

استثناءات الوحدة configparser

الاستثناء configparser.Error

الصنف الأساسي الذي تتفرّع منه بقية الاستثناءات في وحدة configparser.

الاستثناء configparser.NoSectionError

يُطلق هذا الاستثناء عند عدم عثور المحلّل على قسم معيّن في ملف الإعدادات.

الاستثناء configparser.DuplicateSectionError

يُطلق هذا الاستثناء إن استدعي التابع add_section()‎ مع تمرير اسم لقسم موجودًا أصلًا في ملف الإعدادات، أو عند استخدام المحلّل في وضع التدقيق حين يعثر على قسمين يحملان الاسم ذاته في ملف أو سلسلة نصية أو قاموس واحد.

ملاحظة: أضيفت الخصائص والمعاملات الاختيارية source و lineno إلى التابع ‎__init__()‎ في الإصدار 3.2 من بايثون.

الاستثناء configparser.DuplicateOptionError

يطلق هذا الاستثناء بواسطة المحللّات التي تعمل في وضع التدقيق إن تكرر ظهور أحد الخيارات أثناء قراءة البيانات من ملف أو سلسلة نصية أو قاموس واحد. ويمكن لهذا الاستثناء أن يلتقط الأخطاء الإملائية في الأسماء إضافة إلى الأخطاء المتعلقة بحالة الأحرف، فعلى سبيل المثال يمكن لقاموس أن يمتلك مفتاحين يمثّلان نفس مفتاح الإعدادات الذي لا يكون حساسًا لحالة الأحرف.

الاستثناء configparser.NoOptionError

يُطلق هذا الاستثناء عند عدم عثور المحلل على خيار معيّن في القسم المحدد.

الاستثناء configparser.InterpolationError

الصنف الأساسي الذي تتفرّع منه الاستثناءات التي تُطلق عند حدوث مشاكل أثناء إجراء عملية استيفاء السلاسل النصية.

الاستثناء configparser.InterpolationDepthError

يُطلق هذا الاستثناء عند عدم القدرة على إنهاء عملية استيفاء السلاسل النصية بسبب تجاوز عدد الدورات الحد الأقصى المعيّن في MAX_INTERPOLATION_DEPTH. يتفرّع هذا الصنف من الصنف InterpolationError.

الاستثناء configparser.InterpolationMissingOptionError

يُطلق هذا الاستثناء عند غياب الخيار الذي تشير إليه قيمة معيّنة. يتفرّع هذا الصنف من الصنف InterpolationError.

الاستثناء configparser.InterpolationSyntaxError

يُطلق هذا الاستثناء عندما يكون النص المصدري الذي تجري فيه عملية الاستبدال غير متوافق مع الصيغة المطلوبة. يتفرّع هذا الصنف من الصنف InterpolationError.

الاستثناء configparser.MissingSectionHeaderError

يُطلق هذا الاستثناء عند محاولة المحلل تحليل ملف لا يمتلك ترويسات العناوين.

الاستثناء configparser.ParsingError

يُطلق هذا الاستثناء عند حدوث أخطاء أثناء محاولة تحليل ملفّ معين.

ملاحظة: تغير اسم الخاصية ومعامل التابع ‎__init__()‎ من filname إلى tosource في الإصدار 3.2 من بايثون.

مصادر