Шрифт:
poll imap.ccil.org with proto IMAP and options no dns aka snark.thyrsus.com locke.ccil.org ccil.org user esr there is esr here
options fetchall dropstatus warnings 3600
poll imap.netaxs.com with proto IMAP
user "esr" there is esr here options dropstatus warnings 3600
Анализатор для синтаксиса конфигурационного файла fetchmail является довольно сложным. Фактически он написан с использованием программ уасс и lex, двух классических инструментов Unix для создания кода синтаксического анализатора на языке С. Вначале разработчикам показалось, что для того чтобы с помощью fetchmailconf можно было редактировать существующие конфигурационные файлы, понадобится воспроизвести тот же сложный синтаксический анализатор в языке реализации fetchmailconf — Python.
Такая тактика выглядела бесперспективной. Даже если не принимать во внимание объем необходимой дублирующей работы, очень трудно быть уверенным в том, что два синтаксических анализатора, написанных на двух различных языках, допускают использование одной и той же грамматики. Обеспечить в дальнейшем их синхронизацию по мере развития конфигурационного языка будет чрезвычайно сложно. Данный подход нарушал бы правило SPOT, описанное в главе 4.
На какое-то время автор был поставлен в тупик данной проблемой. Разрешило ее интуитивное понимание того, что программа fetchmailconf может использовать собственный синтаксический анализатор fetchmail в качестве фильтра. В результате в fetchmail был добавлен параметр --conf igdurap, который позволял бы анализировать файл . fetchmailrc и отправлять результаты на стандартный вывод в формате РуЛоп-инициализатора. Для приведенного выше файла результаты выглядели бы приблизительно так, как показано в примере 9.2 (для экономии места некоторые данные, не связанные с примером, опущены). ^
Основное препятствие было преодолено. Интерпретатор Python мог затем оценить вывод fetchmail– - conf igdump и прочесть доступную для fetchmailconf конфигурацию как значение переменой "fetchmail".
Однако описанное препятствие было не последним. Было действительно необходимо не только предоставить fetchmail существующую конфигурацию, но превратить ее в связанное дерево действующих объектов. В данном дереве было бы 3 вида объектов: Configuration (объект верхнего уровня, представляющий всю конфигурацию), Site (представляющий один из серверов для опроса) и User (представляющий пользовательские данные, связанные с узлом). Файл в примере описывает 3 объекта Site, каждый из которых связан с одним пользовательским объектом.
Данные 3 класса объектов уже существовали в fetchmailconf. Каждый из них имел метод, который заставлял его выводить на экран GUI-панель редактирования для модификации своего экземпляра данных. Последняя проблема сводилась к некоторому преобразованию статических данных в Python-инициализаторе в действующие объекты.
'port':0, 'timeout':300, 'dns 69 :TRUE, "aka":None,
'users': [ {
"remote":"esr", "password":"masked_two", 1 localnames':["esr"], 'fetchall':FALSE, 'keep':FALSE, 'flush':FALSE, "mda":None, 'limit':0, 1 warnings 1 :3600,
}
1
} /
]
}
Пример 9.3. Код метакласса copy_instance def copy_instance(toclass, fromdict):
# Make a class object of given type from a conformant dictionary.
class_sig = toclass._diet_.keysO; class_sig. sort
dict_keys = fromdict.keys; dict_keys.sort common = set_intersection(class_sig, dict_keys) if 'typemap' in class_sig:
class_sig.remove( 1 typemap') if tuple(class_sig) != tuple(dict_keys): print "Conformability error"
# print "Class signature: " + "class_sig"
# print "Dictionary keys: " + ~dict_keys" print "Not matched in class signature: "+ \
"set_diff(class_sig, common)" print "Not matched in dictionary keys: "+ \
"set_diff(dict_keys, common)~
sys.exit(1) else:
for x in dict_keys:
setattr(toclass, x, fromdict[x])
Большую часть в примере представляет код контроля ошибок, учитывая возможность того, что члены класса и генерация отчета - -conf igdump выпали из синхронизации. Такая проверка гарантирует, что в случае возникновения сбой в коде будет обнаружен на ранней стадии, т.е. реализуется правило исправности. Главной частью кода являются две последние строки, которые устанавливают атрибуты в классе из соответствующих членов в словаре. Данные строки эквивалентны следующим строкам.
def copy_instance(toclass, fromdict): for x in fromdict.keys:
setattr(toclass, x, fromdict[x])
Если разрабатываемый код настолько же прост, то весьма вероятно, что он верен. В примере 9.4 приведен код, вызывающий данный метакласс.
Ключевым моментом в данном коде является то, что он проходит 3 уровня инициализатора (конфигурация/сервер/пользователь), устанавливая корректные объекты каждого уровня в списки, содержащиеся в следующем объекте более высокого уровня. Поскольку метакласс copy_instance управляется данными и является полностью общим, его можно использовать во всех 3 уровнях для 3 различных типов объектов.