На какое-то время автор был поставлен в тупик данной проблемой. Разрешило ее интуитивное понимание того, что программа fetchmailconf может использовать собственный синтаксический анализатор fetchmail в качестве фильтра. В результате в fetchmail был добавлен параметр --configdump
, который позволял бы анализировать файл .fetchmailrc
и отправлять результаты на стандартный вывод в формате Python-инициализатора. Для приведенного выше файла результаты выглядели бы приблизительно так, как показано в примере 9.2 (для экономии места некоторые данные, не связанные с примером, опущены).
Основное препятствие было преодолено. Интерпретатор Python мог затем оценить вывод fetchmail --configdump
и прочесть доступную для fetchmailconf конфигурацию как значение переменой "fetchmail".
Однако описанное препятствие было не последним. Было действительно необходимо не только предоставить fetchmail существующую конфигурацию, но превратить ее в связанное дерево действующих объектов. В данном дереве было бы 3 вида объектов: Configuration (объект верхнего уровня, представляющий всю конфигурацию), Site (представляющий один из серверов для опроса) и User (представляющий пользовательские данные, связанные с узлом). Файл в примере описывает 3 объекта Site, каждый из которых связан с одним пользовательским объектом.
Данные 3 класса объектов уже существовали в fetchmailconf . Каждый из них имел метод, который заставлял его выводить на экран GUI-панель редактирования для модификации своего экземпляра данных. Последняя проблема сводилась к некоторому преобразованию статических данных в Python-инициализаторе в действующие объекты.
Пример 9.2. Дамп Python-структуры для конфигурации fetchmail
fetchmailrc = {
'poll_interval':300,
"logfile":None,
"postmaster":"esr",
'bouncemail':TRUE,
"properties":None,
'invisible':FALSE,
'syslog' :FALSE,
# List of server entries begins here
# (Ниже начинается список серверов)
'servers': [
# Entry for site `imap.ccil.org' begins:
# (Начало записи для узла imap.ccil.org:)
{
"pollname":"imap.ccil.org",
'active':TRUE,
"via":None,
"protocol":"IMAP",
'port':0,
'timeout':300,
'dns':FALSE,
"aka":["snark.thyrsus.com","locke.ccil.org","ccil.org"],
'users': [
{
"remote":"esr",
"password":"masked_one",
'localnames':["esr"],
'fetchall':TRUE,
'keep':FALSE,
'flush':FALSE,
"mda":None,
'limit':0,
'warnings':3600,
}
' ]
}
'
# Entry for site `imap.netaxs.com' begins:
# (Начало записи для узла imap.netaxs.com:)
{
"pollname":"imap.netaxs.com",
'active':TRUE,
"via":None,
"protocol":"IMAP",
'port':0,
'timeout':300,
'dns':TRUE,
"aka":None,
'users': [
{
"remote":"esr",
"password":"masked_two",
'localnames':["esr"],
'fetchall':FALSE,
'keep':FALSE,
'flush':FALSE,
"mda":None,
'limit':0,
'warnings':3600,
}
' ]
}
'
]
}
Рассматривалась идея написания связующего уровня, который имел бы явную информацию о структуре всех 3 классов и использовал бы данную информацию для просмотра инициализатора при создании соответствующих объектов. Однако данная идея была отклонена, поскольку существовала вероятность добавления со временем новых членов класса, по мере создания новых функций в конфигурационном языке. Если бы код создания объектов был написан таким очевидным путем, то он также был бы хрупким и склонным к рассинхронизации при изменении определения классов либо структуры инициализатора, выводимой с помощью генератора отчетов --configdump
. Подобный подход приводит к бесконечному появлению ошибок.
Более надежным способом было бы использование создание программы, управляемой данными, т.е. кода, который анализировал бы форму и члены инициализатора, опрашивал бы определения классов об их членах, а затем согласовывал бы оба набора.
Программисты, работающие с Lisp и Java, называют данную методику интроспекцией (introspection) . В некоторых других объектно-ориентированных языках она называется программированием метаклассов (metaclass hacking) и, как правило, считается "черной магией", понятной только "посвященным". В большинстве объектно- ориентированных языков данная методика не поддерживается вообще, а в тех языках, где она поддерживается (среди них Perl и Java), она часто сложна и ненадежна. Однако в языке Python средства интроспекции и программирования метаклассов исключительно доступны.
Читать дальше