Comments and exceptions are added for all format modules.

packages
Иванов Денис 4 years ago
parent 9f8723f2c7
commit 803b9c5ae8

@ -38,17 +38,20 @@ class BaseFormat():
self._line_timer = 0
def _lines_to_dictionary(self, document_lines):
'''Основной метод для парсинга документа. Принимает список строк,
к каждой строке применяет парсеры, определенные для некоторого формата.
Первый парсер, которому удается разобрать строку используется для
формирования словаря.'''
# print('Lines processing...')
for line in document_lines:
# print(self._line_timer, '\t', line)
for processing_method in self._processing_methods:
processing_method(line)
if self._fatal_error_flag:
# Действия если файл невозможно разобрать.
try:
processing_method(line)
except FormatError as error:
self._document_dictionary = OrderedDict()
raise FormatError('line {} is not correct.'.
format(self._line_timer))
raise FormatError("can not parse line: {}, reason: {}".
format(line, str(error)))
if self._is_match():
if self._is_ready_to_update():
@ -57,27 +60,34 @@ class BaseFormat():
else:
# Действия если не удалось разобрать строку.
self._document_dictionary = OrderedDict()
raise FormatError('line {} is not correct.'.
format(self._line_timer))
self._line_timer += 1
raise FormatError('can not parse line: {}'.
format(line))
if self._need_finish:
self._finish_method()
def _parse_xml_to_dictionary(self, xml_document_text):
'''Метод для парсинга xml файлов.
Файлы xml предварительно не разбиваются на строки, а разбираются с
помощью модуля lxml. Перевод в словарь осуществляется методами формата,
рекурсивно вызывающимися в зависимости от типа тега.'''
root = fromstring(xml_document_text)
self._document_dictionary = self._processing_methods[root.tag](root)
def print_dictionary(self):
'''Метод для отладки.'''
pprint(self._document_dictionary)
def join_template(self, template):
'''Метод запускающий наложение шаблона.'''
self._join(self._document_dictionary,
template._document_dictionary,
self._join_before)
def _get_list_of_logic_lines(self, text):
'''Метод разбивающий документ на список логических строк -- то есть
учитывающий при разбиении возможность разбиение одной строки на
несколько с помощью бэкслеша. В некоторых форматах переопределен.'''
list_of_lines = []
lines_to_join = []
for line in text.splitlines():
@ -94,6 +104,8 @@ class BaseFormat():
return list_of_lines
def _join(self, original, template, join_before):
'''Основной метод для наложения шаблонов путем объединения их словарей
выполняемого рекурсивно.'''
if template == OrderedDict():
return
if join_before:
@ -205,6 +217,8 @@ class BaseFormat():
original.move_to_end(key_value, last=False)
def make_template(self, template):
'''Метод для запуска генерации шаблонов путем сравнения пары исходных
файлов.'''
full_diff, set_to_check = self.compare_dictionaries(
self._document_dictionary,
template._document_dictionary
@ -214,6 +228,8 @@ class BaseFormat():
return template_object
def compare_dictionaries(self, dict_1, dict_2):
'''Основной метод для генерации шаблонов путем сравнения пары исходных
файлов. Работает рекурсивно.'''
to_remove_dictionary = OrderedDict()
to_add_dictionary = OrderedDict()
to_replace_dictionary = OrderedDict()
@ -289,6 +305,8 @@ class BaseFormat():
@property
def document_text(self):
'''Метод для получения текста документа. Использует jinja2 для
рендеринга документа.'''
file_loader = PackageLoader('calculate.templates.format',
self.TEMPLATES_DIRECTORY)
formats_environment = Environment(loader=file_loader,
@ -303,12 +321,20 @@ class BaseFormat():
return document_text
def _finish_method(self):
'''Метод для выполнения заключительных действий парсинга.
Переопределяется в форматах. Вызывается при self._need_finish = True'''
pass
def _is_ready_to_update(self):
'''Метод для проверки флага self._ready_to_update, указывающего, что
сформированная форматом секция документа, находящаяся в
self._item_to_add, может быть добавлена в словарь документа.'''
is_ready, self._ready_to_update = self._ready_to_update, False
return is_ready
def _is_match(self):
'''Метод для проверки флага self._is_match, указывающего что текущий
парсер, использованный форматом, смог распарсить строку и использовать
другие парсеры не нужно.'''
is_match, self._match = self._match, False
return is_match

@ -11,6 +11,9 @@ from pyparsing import originalTextFor, OneOrMore, Word, alphanums, Literal,\
class BINDFormat(BaseFormat):
'''Класс формата BIND. В отличие от большинства других форматов обрабатывает
документ не построчно, а напрямую применяя парсер ко всему тексту
документа.'''
FORMAT = 'bind'
EXECUTABLE = False
@ -36,6 +39,9 @@ class BINDFormat(BaseFormat):
self._parse_text(document_text)
def _initialize_parser(self):
'''Метод для инициализации парсеров. Для данного формата парсеры
инициализируется при создании экземпляра формата, поскольку настройка
парсеров зависит от того, включен ли флаг ignore_comments.'''
left_brace = Literal('{')
right_brace = Literal('}')
semicolon = Literal(';')
@ -211,13 +217,20 @@ class BINDFormat(BaseFormat):
self._document_parser = OneOrMore(block_item)
def _parse_text(self, text):
'''Метод для запуска разбора документа.'''
parsing_result = self._document_parser.parseString(text, parseAll=True)
list_of_elements = parsing_result.asList()
# На выходе парсера получаем список словарей секций, который затем
# преобразуем в словарь документа.
for part in list_of_elements:
self._join_dictionary(self._document_dictionary,
part)
def _join_dictionary(self, out_dictionary, dictionary_to_add):
'''Метод для преобразования результата парсинга в итоговый словарь.
Работает рекурсивно. Умеет объединять секции с одинаковыми названиями.
'''
for key in dictionary_to_add:
if dictionary_to_add == OrderedDict():
return
@ -230,6 +243,8 @@ class BINDFormat(BaseFormat):
out_dictionary[key] = dictionary_to_add[key]
def _add_plain_line(self, current_parse):
'''Метод используемый в парсере простых строк, состоящих только из
имени параметра и опционально из его значения.'''
name = tuple(current_parse.name.asList())
if not current_parse.parameter == '':
value = current_parse.parameter
@ -241,14 +256,17 @@ class BINDFormat(BaseFormat):
return OrderedDict({name: [value]})
def _add_include_line(self, current_parse):
'''Метод используемый в парсере директивы include.'''
name = current_parse.asList()
return OrderedDict({tuple(name): ['']})
def _add_ipline(self, current_parse):
'''Метод используемый в парсере ip адресов.'''
ip_value = current_parse.IP
return OrderedDict({tuple(ip_value): ['']})
def _add_inet_specline(self, current_parse):
'''Метод используемый в парсере директивы inet_spec.'''
# Удаляем пробельные символы из второго параметра директивы.
current_parse.name.parameter = current_parse.name.parameter.strip()
block_name = tuple(current_parse.name.asList())
@ -261,6 +279,9 @@ class BINDFormat(BaseFormat):
return OrderedDict({block_name: content})
def _add_param_block(self, current_parse):
'''Метод используемый в парсере блоков параметров. Использует
рекурсивный метод self._join_dictionary для построения словаря блока
параметров. Учитывает возможность наличия вложенных блоков.'''
block_name = tuple(current_parse.name.asList())
block_content = current_parse.content.asList()
content = OrderedDict({'#': []})
@ -270,6 +291,9 @@ class BINDFormat(BaseFormat):
return OrderedDict({block_name: content})
def _add_comments_to_paramline(self, current_parse):
'''Метод используемый в парсере директив вместе с относящимися к ним
комментариями. Закрепляет комментарии за данным параметром в итоговом
словаре.'''
[parameter] = current_parse.value.asList()
comments = current_parse.comments
if parameter == OrderedDict():
@ -288,6 +312,9 @@ class BINDFormat(BaseFormat):
return parameter
def _add_comments_to_block(self, current_parse):
'''Метод используемый в парсере блоков директив вместе с относящимися
к ним комментариями. Закрепляет комментарии за данным параметром в
итоговом словаре.'''
[value] = current_parse.value
[block_name] = value
block = value[block_name]
@ -301,6 +328,9 @@ class BINDFormat(BaseFormat):
return value
def _create_comment_list(self, current_parse):
'''Метод используемый в парсере комментариев. Формирует из обнаруженных
комментариев список, который затем закрепляется за блоком или
параметром.'''
comments_list = []
comments = current_parse.asList()
for comment in comments:

@ -12,10 +12,15 @@ class CompizFormat(BaseFormat):
EXECUTABLE = False
_initialized = False
_comment_symbol = ''
def __init__(self, document_text: str, ignore_comments=False,
join_before=False, comment_symbol=''):
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_section_line,
self._parse_parameter_line,
@ -31,8 +36,6 @@ class CompizFormat(BaseFormat):
self._current_section_name = ''
self._last_comments_list = []
if not self._initialized or comment_symbol != self._comment_symbol:
self._initialize_parser(comment_symbol)
if document_text == '':
self._document_dictionary = OrderedDict()
@ -41,7 +44,8 @@ class CompizFormat(BaseFormat):
self._lines_to_dictionary(document_lines)
@classmethod
def _initialize_parser(cls, comment_symbol):
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
section_name = originalTextFor(
OneOrMore(Word(alphanums+'_'))
)
@ -54,8 +58,8 @@ class CompizFormat(BaseFormat):
+ Literal(']').suppress())('section_name')
parameter_name = originalTextFor(
OneOrMore(Word(printables,
excludeChars='='))
OneOrMore(Word(printables,
excludeChars='='))
)
parameter_value = Word(printables)
@ -71,24 +75,16 @@ class CompizFormat(BaseFormat):
+ parameter_name('name')
+ restOfLine.suppress())('parameter_name')
if not comment_symbol:
cls._comment_line = originalTextFor(
Literal('#')
+ ZeroOrMore(Word(
printables
+ pyparsing_unicode.alphanums))
)('comment')
else:
cls._comment_line = originalTextFor(
(Literal('#') | Literal(comment_symbol))
+ ZeroOrMore(Word(
printables
+ pyparsing_unicode.alphanums))
)('comment')
cls._comment_line = originalTextFor(
Literal('#')
+ ZeroOrMore(Word(
printables
+ pyparsing_unicode.alphanums))
)('comment')
cls._initialized = True
def _parse_section_line(self, line):
'''Метод для парсинга строк содержащих имя секции.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._section_line.parseString(line)
@ -117,6 +113,7 @@ class CompizFormat(BaseFormat):
return
def _parse_parameter_line(self, line):
'''Метод для парсинга строк содержащих параметр.'''
try:
parsing_result = self._parameter_line.parseString(line)
self._match = True
@ -132,6 +129,9 @@ class CompizFormat(BaseFormat):
return
def _parse_to_delete_line(self, line):
'''Метод для парсинга строк, подлежащих удалению, т.е. для которых
указано имя параметра со знаком !, и опционально присутствует значение
параметра.'''
try:
parsing_result = self._parameter_to_delete.parseString(line)
self._match = True
@ -148,6 +148,7 @@ class CompizFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для парсинга строк содержащих комментарий.'''
try:
parsing_result = self._comment_line.parseString(line)
self._match = True
@ -158,6 +159,8 @@ class CompizFormat(BaseFormat):
return
def _finish_method(self):
'''Метод для завершения парсинга. В данном случае добавляет в итоговый
словарь последнюю разобранную секцию.'''
if self._current_section_name in self._document_dictionary.keys():
self._document_dictionary[self._current_section_name].update(
self._current_section

@ -13,6 +13,12 @@ class ContentsFormat(BaseFormat):
_initialized = False
def __new__(cls, *args, **kwargs):
'''Метод для инициализации парсеров.'''
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False,
@ -25,6 +31,11 @@ class ContentsFormat(BaseFormat):
self._ignore_comments = ignore_comments
self._join_before = join_before
# флаг для указания режима, в котором работает формат.
# Если True => формат работает для наложения шаблонов;
# Если False => формат работает для получения словаря, используемого
# модулем package.
self._template_parser_flag = template_parser
if not self._initialized:
@ -67,6 +78,7 @@ class ContentsFormat(BaseFormat):
cls._initialized = True
def _parse_sym_line(self, line):
'''Метод для разбора строк типа sym.'''
try:
parsing_result = self.sym_line.parseString(line)
self._match = True
@ -88,6 +100,7 @@ class ContentsFormat(BaseFormat):
return
def _parse_dir_line(self, line):
'''Метод для разбора строк типа dir.'''
try:
parsing_result = self.dir_line.parseString(line)
self._match = True
@ -105,6 +118,7 @@ class ContentsFormat(BaseFormat):
return
def _parse_obj_line(self, line):
'''Метод для разбора строк типа obj.'''
try:
parsing_result = self.obj_line.parseString(line)
self._match = True
@ -127,6 +141,10 @@ class ContentsFormat(BaseFormat):
@property
def document_text(self):
'''Метод для получения текста результирующего документа.
Представляет собой переопределение соответствующего метода базового
класса, посколько в данном случае нужно учитывать режим работы формата.
'''
file_loader = PackageLoader('calculate.templates.format',
self.TEMPLATES_DIRECTORY)
formats_environment = Environment(loader=file_loader,

@ -10,7 +10,7 @@ class DiffFormat(BaseFormat):
FORMAT = 'diff'
EXECUTABLE = True
def __init__(self, document_text: str, comment_symbol=''):
def __init__(self, document_text: str):
self._patch_text = document_text
self._root_path = ''
self._last_level = 0
@ -19,18 +19,19 @@ class DiffFormat(BaseFormat):
self._changed_files_list = []
def execute_format(self, root_path):
print
'''Метод для запуска работы формата.'''
if path.exists(root_path):
self._root_path = root_path
else:
raise FormatError('Root path does not exist.')
raise FormatError('root path does not exist')
if self._patch_text:
return self._patch_document()
else:
raise FormatError('Empty patch file.')
raise FormatError('empty patch file')
def _patch_document(self):
'''Метод, производящий наложение патча путем запуска процесса patch.'''
for level in range(0, 4):
patch_dry_run = Process('patch', '--dry-run',
'-p{}'.format(level), cwd=self._root_path)
@ -44,19 +45,16 @@ class DiffFormat(BaseFormat):
if patch_dry_run.success():
return ''
else:
raise FormatError('Correction failed.')
raise FormatError('correction failed')
self._last_level = level
patch_run = Process('patch', '-p{}'.format(level), cwd=self._root_path)
patch_run.write(self._patch_text)
if patch_run.success():
print('patch run is successful...')
for line in patch_run:
if line.startswith('patching file'):
self._changed_files_list.append(line[13:].strip())
return patch_run.read()
else:
print('patch run is no successful...')
print(patch_run.read_error())
return ''

@ -16,8 +16,14 @@ class DovecotFormat(BaseFormat):
_initialized = False
_comment_symbol = ''
def __init__(self, document_text: str, ignore_comments=False,
join_before=False, comment_symbol=''):
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_section_start_line,
self._parse_include_line,
@ -35,8 +41,6 @@ class DovecotFormat(BaseFormat):
self._current_section_name = ''
self._last_comments_list = []
if not self._initialized or comment_symbol != self._comment_symbol:
self._initialize_parser(comment_symbol)
if document_text == '':
self._document_dictionary = OrderedDict()
@ -45,28 +49,19 @@ class DovecotFormat(BaseFormat):
self._lines_to_dictionary(document_lines)
@classmethod
def _initialize_parser(cls, comment_symbol=''):
def _initialize_parser(cls):
# Знаки пунктуации и действий.
left_brace = Literal('{')
right_brace = Literal('}')
action_symbols = (Literal('!') | Literal('-'))
if not comment_symbol:
cls._comment_line_parser = originalTextFor(
Literal('#')
+ ZeroOrMore(Word(
printables
+ pyparsing_unicode.alphanums)
)
)('comment')
else:
cls._comment_line_parser = originalTextFor(
(Literal('#') | Literal(comment_symbol))
+ ZeroOrMore(Word(
printables
+ pyparsing_unicode.alphanums)
)
)('comment')
cls._comment_line_parser = originalTextFor(
Literal('#')
+ ZeroOrMore(Word(
printables
+ pyparsing_unicode.alphanums)
)
)('comment')
# Для парсинга строк с началом секций.
section = Word(alphas, alphanums+'-_', excludeChars='{}')
@ -96,9 +91,10 @@ class DovecotFormat(BaseFormat):
)('value'))
# Для парсинга строк с параметрами, подлежащими удалению.
cls._parameter_to_delete_parser = (action_symbols('action')
+ parameter_name
+ Optional(Literal('=')).suppress()
cls._parameter_to_delete_parser = (
action_symbols('action')
+ parameter_name
+ Optional(Literal('=')).suppress()
)
# Для парсинга строк, содержащих директиву !include.
@ -107,7 +103,8 @@ class DovecotFormat(BaseFormat):
include_line_plain = (Optional(~action_symbols, default='')('action')
+ include('keyword') + Word(printables)('value'))
include_line_to_delete = (action_symbols('action') + include('keyword')
include_line_to_delete = (action_symbols('action')
+ include('keyword')
+ Word(printables)('value'))
cls._include_line_parser = (include_line_plain |

@ -9,7 +9,7 @@ class JSONFormat(BaseFormat):
FORMAT = 'json'
def __init__(self, document_text: str, ignore_comments=False,
join_before=False, comment_symbol=''):
join_before=False):
processing_methods = []
super().__init__(processing_methods)
self._ignore_comments = ignore_comments
@ -22,10 +22,14 @@ class JSONFormat(BaseFormat):
self._text_to_dictionary(document_text)
def _text_to_dictionary(self, json_file_text):
'''Метод для получения словаря документа, переопределяющий метод
базового класса.'''
self._document_dictionary = json.loads(json_file_text,
object_pairs_hook=OrderedDict)
@property
def document_text(self):
'''Метод для получения текста документа, переопределяющий метод
базового класса.'''
json_file_text = json.dumps(self._document_dictionary, indent=4)
return json_file_text

@ -12,8 +12,14 @@ class KDEFormat(BaseFormat):
_initialized = False
def __init__(self, document_text: str, ignore_comments=False,
join_before=False, comment_symbol=''):
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_section_line,
self._parse_parameter_line,
@ -29,8 +35,6 @@ class KDEFormat(BaseFormat):
self._current_section_name = ''
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -40,6 +44,7 @@ class KDEFormat(BaseFormat):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
action_symbols = (Literal('!') | Literal('-'))
section_name_part_content = originalTextFor((OneOrMore(
Word(alphanums+':'))))
@ -79,6 +84,7 @@ class KDEFormat(BaseFormat):
cls._initialized = True
def _parse_section_line(self, line):
'''Метод для парсинга строк содержащих название секции.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._section_line.parseString(line)
@ -107,6 +113,7 @@ class KDEFormat(BaseFormat):
return
def _parse_parameter_line(self, line):
'''Метод для парсинга строк содержащих обычный параметр.'''
try:
parsing_result = self._parameter_line.parseString(line)
self._match = True
@ -122,6 +129,9 @@ class KDEFormat(BaseFormat):
return
def _parse_to_delete_line(self, line):
'''Метод для парсинга строк, подлежащих удалению, т.е. для которых
указано имя параметра со знаком !, и опционально присутствует значение
параметра.'''
try:
parsing_result = self._parameter_to_delete.parseString(line)
self._match = True
@ -138,6 +148,7 @@ class KDEFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для парсинга строк содержащих комментарий.'''
try:
parsing_result = self._comment_line.parseString(line)
self._match = True
@ -148,6 +159,8 @@ class KDEFormat(BaseFormat):
return
def _finish_method(self):
'''Метод для завершения парсинга. В данном случае добавляет в итоговый
словарь последнюю разобранную секцию.'''
if self._current_section_name in self._document_dictionary.keys():
self._document_dictionary[self._current_section_name].update(
self._current_section

@ -12,8 +12,14 @@ class KernelFormat(BaseFormat):
_initialized = False
def __init__(self, document_text: str, ignore_comments=False,
join_before=False, comment_symbol=''):
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_parameter_line,
self._parse_to_delete_line]
@ -24,8 +30,6 @@ class KernelFormat(BaseFormat):
self._comments_processing = True
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -35,6 +39,7 @@ class KernelFormat(BaseFormat):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
parameter_name = Word(alphanums+'_')('parameter_name')
parameter_value = originalTextFor(
@ -63,6 +68,7 @@ class KernelFormat(BaseFormat):
cls._initialized = True
def _parse_parameter_line(self, line):
'''Метод для парсинга строк содержащих параметр.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._parameter_line.parseString(line)
@ -80,6 +86,9 @@ class KernelFormat(BaseFormat):
return
def _parse_to_delete_line(self, line):
'''Метод для парсинга строк, подлежащих удалению, т.е. для которых
указано имя параметра со знаком !, и опционально присутствует значение
параметра.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._parameter_to_delete.parseString(line)
@ -99,6 +108,7 @@ class KernelFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для парсинга строк содержащих комментарий.'''
try:
result = self._comment_line.parseString(line)
self._match = True

@ -13,10 +13,14 @@ class LDAPFormat(BaseFormat):
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False,
comment_symbol=''):
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_type_line,
self._parse_access_line,
@ -43,8 +47,6 @@ class LDAPFormat(BaseFormat):
self._current_type = ('', 'global')
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -54,6 +56,7 @@ class LDAPFormat(BaseFormat):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
cls._comment_line = originalTextFor(
Literal('#')
+ ZeroOrMore(Word(printables
@ -215,6 +218,9 @@ class LDAPFormat(BaseFormat):
cls._initialized = True
def _get_list_of_logic_lines(self, text):
'''Метод для разбиения исходного документа на список логических строк,
то есть с учетом того, что строка ldap файла начинающаяся с отступа
является продолжением предыдущей.'''
list_of_lines = []
lines_to_join = []
for line in text.splitlines():
@ -239,6 +245,8 @@ class LDAPFormat(BaseFormat):
return list_of_lines
def _parse_type_line(self, line):
'''Метод для парсинга строк с объявлением областей backend или database
'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._type_line.parseString(line)
@ -273,8 +281,7 @@ class LDAPFormat(BaseFormat):
def _parse_notunique_line(self, line):
'''Метод для парсинга строк c директивами неуникальными для секции.
Аргументы: line -- строка, которую нужно распарсить.
'''
Их приходится парсить полностью как ключ словаря.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._not_unique_parser.parseString(line)
@ -293,9 +300,7 @@ class LDAPFormat(BaseFormat):
def _parse_access_line(self, line):
'''Метод для парсинга строк содержащих конструкцию
access to <what> by <who>|<access level>|<control>.
Аргументы: line -- строка, которую нужно распарсить.
'''
access to <what> by <who>|<access level>|<control>.'''
try:
parsing_result = self._access_line_parser.parseString(line)
self._match = True
@ -335,9 +340,7 @@ class LDAPFormat(BaseFormat):
def _parse_access_line_to_delete(self, line):
'''Метод для парсинга строк, предписывающих удаление конструкций
access to, если указано только ее название и значение What.
Аргументы: line -- строка, которую нужно распарсить.
'''
access to, если указано только ее название и значение What.'''
try:
parsing_result = self._access_line_to_delete_parser.parseString(
line
@ -357,9 +360,7 @@ class LDAPFormat(BaseFormat):
def _parse_syncrepl_line(self, line):
'''Метод для парсинга строк содержащих конструкцию syncrepl
rep=<ReplicaID>.
Аргументы: line -- строка, которую нужно распарсить.
'''
rep=<ReplicaID>.'''
try:
parsing_result = self._syncrepl_line_parser.parseString(line)
self._match = True
@ -398,9 +399,7 @@ class LDAPFormat(BaseFormat):
def _parse_syncrepl_line_to_delete(self, line):
'''Метод для парсинга строк, предписывающих удаление конструкций
syncrepl rid=<ReplicaID>, если указано только ее название и значение
ReplicaID.
Аргументы: line -- строка, которую нужно распарсить.
'''
ReplicaID.'''
try:
parsing_result = self._syncrepl_line_to_delete_parser.parseString(
line
@ -419,9 +418,7 @@ class LDAPFormat(BaseFormat):
return
def _parse_index_line(self, line):
'''Метод для парсинга строк с директивами index.
Аргументы: line -- строка, которую нужно распарсить.
'''
'''Метод для парсинга строк с директивами index.'''
try:
parsing_result = self._index_line_parser.parseString(line)
self._match = True
@ -438,9 +435,7 @@ class LDAPFormat(BaseFormat):
def _parse_index_line_to_delete(self, line):
'''Метод для парсинга строк, предписывающих удаление директив index,
если указано только из имя, но отсутвует значение.
Аргументы: line -- строка, которую нужно распарсить.
'''
если указано только из имя, но отсутвует значение.'''
try:
parsing_result = self._index_line_to_delete_parser.parseString(
line
@ -460,9 +455,7 @@ class LDAPFormat(BaseFormat):
def _parse_plain_directive_line(self, line):
'''Метод для парсинга строк с простыми уникальными для секции
директивами.
Аргументы: line -- строка, которую нужно распарсить.
'''
директивами.'''
try:
parsing_result = self._directive_line_parser.parseString(line)
self._match = True
@ -478,9 +471,7 @@ class LDAPFormat(BaseFormat):
def _parse_plain_directive_line_to_delete(self, line):
'''Метод для парсинга строк, предписывающих удаление простых уникальных
директив, если указано только их имя, но отсутствует значение.
Аргументы: line -- строка, которую нужно распарсить.
'''
директив, если указано только их имя, но отсутствует значение.'''
try:
parsing_result = self._directive_line_to_delete_parser.parseString(
line
@ -501,8 +492,7 @@ class LDAPFormat(BaseFormat):
def _parse_comment_line(self, line):
'''Метод для парсинга строк с комментариями и добавления их в список
комментариев _last_comments_list, предназначенный для сбора
комментариев и последующего их присваивания параметрам и секциям.
Аргументы: line -- строка, которую нужно распарсить.'''
комментариев и последующего их присваивания параметрам и секциям.'''
try:
parsing_result = self._comment_line.parseString(line)
self._match = True
@ -522,6 +512,8 @@ class LDAPFormat(BaseFormat):
return
def _finish_method(self):
'''Метод для завершения парсинга. В данном случае добавляет в итоговый
словарь последнюю разобранную область.'''
self._item_to_add = OrderedDict()
if self._current_type in self._document_dictionary.keys():
self._document_dictionary[self._current_type].update(

@ -12,10 +12,14 @@ class OpenRCFormat(BaseFormat):
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False,
comment_symbol=''):
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_parameter_line,
self._parse_to_delete_line]
@ -25,8 +29,6 @@ class OpenRCFormat(BaseFormat):
self._comments_processing = True
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -36,6 +38,7 @@ class OpenRCFormat(BaseFormat):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
parameter_name = Word(printables, excludeChars='=')
parameter_value = originalTextFor(OneOrMore(Word(printables)))
@ -61,6 +64,7 @@ class OpenRCFormat(BaseFormat):
cls._initialized = True
def _parse_parameter_line(self, line):
'''Метод для парсинга строк содержащих параметр.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._parameter_line.parseString(line)
@ -79,6 +83,9 @@ class OpenRCFormat(BaseFormat):
return
def _parse_to_delete_line(self, line):
'''Метод для парсинга строк, подлежащих удалению, т.е. для которых
указано имя параметра со знаком !, и опционально присутствует значение
параметра.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._parameter_to_delete.parseString(line)
@ -98,6 +105,7 @@ class OpenRCFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для парсинга строк содержащих комментарий.'''
try:
result = self._comment_line.parseString(line)
self._match = True

@ -1,6 +1,6 @@
# vim: fileencoding=utf-8
#
from .base_format import BaseFormat
from .base_format import BaseFormat, FormatError
from collections import OrderedDict
import re
try:
@ -33,21 +33,19 @@ class PatchFormat(BaseFormat):
self._XML_ROOT_LINE = '<?xml version="1.0" encoding="utf-8"?>\
<patch>{0}</patch>'
if not self._parse_patch(document_text):
# Какая-то обработка ошибки.
print('Error: Can not parse patch document.')
self._parse_patch(document_text)
def _parse_patch(self, patch_text):
'''Метод, составляющий из текста шаблона xml документ и разбирающий его
с помощью lxml.'''
xml_patch = self._XML_ROOT_LINE.format(patch_text.strip())
try:
self._parsed_patch = fromstring(xml_patch)
return True
except Exception:
# Какая-то обработка ошибки.
print('Error: Incorrect text of the template.')
return False
raise FormatError('can not parse patch document')
def execute_format(self, document_to_patch):
'''Метод для запуска наложения патча.'''
if not document_to_patch.strip() == '':
self._document_to_patch = document_to_patch
@ -55,24 +53,23 @@ class PatchFormat(BaseFormat):
return False
else:
if not self._patch_document(document_to_patch):
error_message = 'Error: Can not run patch.'
print(error_message)
return False
raise FormatError('Error: Can not run patch.')
else:
after_patch = self._document_to_patch
self._document_to_patch = ''
# Пока что возвращает результат наложения шаблона, это временно
return after_patch
def _patch_document(self, document_to_patch):
'''Метод, обходящий теги шаблона и использующий указанные в нем
регулярные выражения.'''
patch_iterator = self._parsed_patch.getiterator()
PATCH_DOCUMENT_TAGS = ('reg', 'text')
patch_element = next(patch_iterator, False)
if not patch_element or not patch_element.tag == 'patch':
print('Error: Incorrect text of the template.')
return False
raise FormatError('incorrect text of the template')
while True:
for patch_tag in PATCH_DOCUMENT_TAGS:
@ -80,9 +77,8 @@ class PatchFormat(BaseFormat):
if patch_element is None:
if patch_tag == 'text':
print('Error: Last <text>Text</text> '
'object is missed.')
return False
raise FormatError('last <text>Text</text> '
'object is missed.')
else:
break
@ -90,19 +86,16 @@ class PatchFormat(BaseFormat):
if patch_element.text is not None:
element_text = patch_element.text.strip()
if element_text == '':
error_message = 'Error: Incorrect text of the \
template: <{0}>%s</{0}>'.format(
raise FormatError(
("Error: Incorrect text of the "
"template: <{0}>%s</{0}>").format(
patch_tag
)
print(error_message)
return False
))
else:
error_message = 'Error: Incorrect text of the \
template: <{0}></{0}>'.format(
raise FormatError("Error: Incorrect text of the "
"template: <{0}></{0}>").format(
patch_tag
)
print(error_message)
return False
)
if patch_tag == 'reg':
dotall = patch_element.attrib.get('dotall', False)
@ -111,10 +104,7 @@ class PatchFormat(BaseFormat):
if 'multiline' in patch_element.attrib:
multiline = patch_element.attrib['multiline']
if multiline not in self._FLAG_VALUES:
error_message = ('Error: Invalid multiline '
'value.')
print(error_message)
return False
raise FormatError('invalid multiline value')
else:
multiline = self._FLAG_VALUES[multiline]
@ -133,9 +123,7 @@ class PatchFormat(BaseFormat):
if 'dotall' in patch_element.attrib:
dotall = patch_element.attrib['dotall']
if dotall not in self._FLAG_VALUES:
error_message = 'Error: Invalid dotall value.'
print(error_message)
return False
raise FormatError('invalid dotall value')
else:
dotall = self._FLAG_VALUES[dotall]
@ -157,21 +145,15 @@ class PatchFormat(BaseFormat):
text_for_replace = element_text
else:
if patch_element.tag in PATCH_DOCUMENT_TAGS:
error_message = 'Error: <{0}> is expected, \
<{1}> instead.'.format(
patch_tag,
patch_element.tag
)
print(error_message)
error_message = '<{0}> is expected, <{1}> instead.'.\
format(patch_tag,
patch_element.tag)
else:
error_message = 'Error: Unknown tag: {0}'.format(
error_message = 'unknown tag: {0}'.format(
patch_element.tag
)
print(error_message)
# Какая-то обработка ошибки.
error_message = 'Error: Incorrect text of the template.'
print(error_message)
return False
)
raise ("incorrect text of the template: {}".format(
error_message))
else:
self._document_to_patch = re.sub(regex_expression,
text_for_replace,

@ -12,10 +12,14 @@ class PostfixFormat(BaseFormat):
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False,
comment_symbol=''):
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_parameter_line,
self._parse_to_delete_line]
@ -25,8 +29,6 @@ class PostfixFormat(BaseFormat):
self._comments_processing = True
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -36,6 +38,7 @@ class PostfixFormat(BaseFormat):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
parameter_name = Word(alphanums+'_')
parameter_value = originalTextFor(OneOrMore(Word(printables)))
@ -61,6 +64,7 @@ class PostfixFormat(BaseFormat):
)('comment')
def _parse_parameter_line(self, line):
'''Метод для парсинга строк содержащих параметр.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._parameter_line.parseString(line)
@ -78,6 +82,9 @@ class PostfixFormat(BaseFormat):
return
def _parse_to_delete_line(self, line):
'''Метод для парсинга строк, подлежащих удалению, т.е. для которых
указано имя параметра со знаком !, и опционально присутствует значение
параметра.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._parameter_to_delete.parseString(line)
@ -96,6 +103,7 @@ class PostfixFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для парсинга строк содержащих комментарий.'''
try:
parsing_result = self._comment_line.parseString(line)
self._match = True

@ -12,6 +12,11 @@ class ProcmailFormat(BaseFormat):
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=True,
@ -25,8 +30,6 @@ class ProcmailFormat(BaseFormat):
self._comments_processing = True
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -36,6 +39,7 @@ class ProcmailFormat(BaseFormat):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
parameter_name = Word(alphanums+'_.')
parameter_value = originalTextFor(OneOrMore(Word(printables)))
@ -64,6 +68,7 @@ class ProcmailFormat(BaseFormat):
cls._initialized = True
def _parse_parameter_line(self, line):
'''Метод для парсинга строк содержащих параметр.'''
try:
parsing_result = self._parameter_line.parseString(line)
self._match = True
@ -80,6 +85,9 @@ class ProcmailFormat(BaseFormat):
return
def _parse_to_delete_line(self, line):
'''Метод для парсинга строк, подлежащих удалению, т.е. для которых
указано имя параметра со знаком !, и опционально присутствует значение
параметра.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._parameter_to_delete.parseString(line)
@ -98,6 +106,7 @@ class ProcmailFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для парсинга строк содержащих комментарий.'''
try:
parsing_result = self._comment_line.parseString(line)
self._match = True

@ -1,6 +1,6 @@
# vim: fileencoding=utf-8
#
from .base_format import BaseFormat
from .base_format import BaseFormat, FormatError
from jinja2 import Environment, PackageLoader
from collections import OrderedDict
from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\
@ -9,10 +9,21 @@ from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\
class ProFTPDFormat(BaseFormat):
'''Класс формата ProFTPD. В отличие от других форматов, при разборе
вложенных на несколько уровней блоков, в итоговый словарь добавляются не
вложенные на несколько уровней словари, а отдельные объекты этих блоков,
содержащие в названии последовательность имен тегов, к которым они
относятся.
'''
FORMAT = 'proftpd'
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False,
@ -36,8 +47,6 @@ class ProFTPDFormat(BaseFormat):
self._actions_stack = []
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -47,6 +56,7 @@ class ProFTPDFormat(BaseFormat):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсеров.'''
left_angle_bracket = Literal('<')
right_angle_bracket = Literal('>')
slash = Literal('/')
@ -200,6 +210,7 @@ class ProFTPDFormat(BaseFormat):
cls._initialized = True
def _parse_section_start_line(self, line):
'''Метод для разбора тега открывающего секцию.'''
try:
parsing_result = self._section_start_parser.parseString(line)
self._match = True
@ -212,6 +223,7 @@ class ProFTPDFormat(BaseFormat):
return
def _parse_section_end_line(self, line):
'''Метод для разбора тега закрывающего секцию.'''
try:
parsing_result = self._section_end_parser.parseString(line)
self._match = True
@ -221,14 +233,14 @@ class ProFTPDFormat(BaseFormat):
directive = tuple(parsing_result.directive)
if not current_section[1] != directive:
# Здесь будет кидаться исключение.
self._fatal_error_flag = True
return
raise FormatError("incorrect end tag </{}>, expected </{}>".
format(directive, current_section))
except ParseException:
return
def _parse_plain_directive_line(self, line):
'''Метод для разбора строк состоящих из имени параметра и его значения.
'''
try:
parsing_result = self._plain_directive_parser.parseString(line)
self._match = True
@ -258,6 +270,8 @@ class ProFTPDFormat(BaseFormat):
return
def _parse_to_delete_plain_directive_line(self, line):
'''Метод для разбора строк с параметрами, подлежащими удалению, то есть
содержащих имя параметра с символом ! и, опционально, его значение.'''
try:
parsing_result = self._delete_plain_directive_parser.parseString(
line
@ -283,6 +297,9 @@ class ProFTPDFormat(BaseFormat):
return
def _parse_single_key_directive_line(self, line):
'''Метод для разбора строк с директивами, состоящих лишь из одного
имени директивы.
'''
try:
parsing_result = self._single_key_directive_parser.parseString(
line
@ -314,6 +331,9 @@ class ProFTPDFormat(BaseFormat):
return
def _parse_double_key_directive_line(self, line):
'''Метод для разбора директив, добавление в словарь которых возможно
посредством формирования ключа из названия директивы и последующего за
ней значения.'''
try:
parsing_result = self._double_key_directive_parser.parseString(
line
@ -345,6 +365,8 @@ class ProFTPDFormat(BaseFormat):
return
def _parse_to_delete_double_key_directive_line(self, line):
'''Метод для разбора директив, подлежащих удалению, ключи которых
формируются из названия директивы и последующего за ней значения.'''
try:
parsing_result = self._delete_double_key_directive_parser.\
parseString(line)
@ -369,6 +391,9 @@ class ProFTPDFormat(BaseFormat):
return
def _parse_full_key_directive_line(self, line):
'''Метод для разбора строк с директивами, из имен которых невозможно
составить уникальный ключ для итогового словаря. Такие директивы
разбираем полностью как ключ для пустого значения в словаре.'''
try:
parsing_result = self._full_key_directive_parser.parseString(line)
self._match = True
@ -398,6 +423,7 @@ class ProFTPDFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для разбора строк, содержащих комментарии.'''
try:
result = self._comment_line.parseString(line)
self._match = True

@ -10,10 +10,14 @@ class SambaFormat(BaseFormat):
FORMAT = 'samba'
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str,
ignore_comments=False,
join_before=False,
comment_symbol=''):
join_before=False):
processing_methods = [self._parse_comment_line,
self._parse_section_line,
self._parse_parameter_line,
@ -30,8 +34,6 @@ class SambaFormat(BaseFormat):
self._join_before = join_before
self._last_comments_list = []
if not self._initialized:
self._initialize_parser()
if document_text == '':
self._document_dictionary = OrderedDict()
@ -80,6 +82,7 @@ class SambaFormat(BaseFormat):
cls._initialized = True
def _parse_section_line(self, line):
'''Метод для парсинга строк содержащих имя секции.'''
try:
self._item_to_add = OrderedDict()
parsing_result = self._section_line.parseString(line)
@ -107,6 +110,7 @@ class SambaFormat(BaseFormat):
return
def _parse_parameter_line(self, line):
'''Метод для парсинга строк содержащих параметр.'''
try:
parsing_result = self._parameter_line.parseString(line)
self._match = True
@ -123,6 +127,9 @@ class SambaFormat(BaseFormat):
return
def _parse_to_delete_line(self, line):
'''Метод для парсинга строк, подлежащих удалению, т.е. для которых
указано имя параметра со знаком !, и опционально присутствует значение
параметра.'''
try:
parsing_result = self._parameter_to_delete.parseString(line)
self._match = True
@ -140,6 +147,7 @@ class SambaFormat(BaseFormat):
return
def _parse_comment_line(self, line):
'''Метод для парсинга строк содержащих комментарий.'''
try:
parsing_result = self._comment_line.parseString(line)
self._match = True
@ -150,6 +158,8 @@ class SambaFormat(BaseFormat):
return
def _finish_method(self):
'''Метод для завершения парсинга. В данном случае добавляет в итоговый
словарь последнюю разобранную секцию.'''
self._item_to_add = OrderedDict()
if self._current_section_name in self._document_dictionary.keys():
self._document_dictionary[self._current_section_name].update(

@ -14,6 +14,13 @@ except ImportError:
class XMLGConfFormat(BaseFormat):
FORMAT = 'xml_gconf'
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str):
processing_methods = OrderedDict({'gconf': self._gconf,
'entry': self._entry,
@ -30,14 +37,18 @@ class XMLGConfFormat(BaseFormat):
self._parse_xml_to_dictionary(document_text)
def _initialize_parser(self):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсерa.'''
action_symbols = (Literal('!') | Literal('-'))
name = originalTextFor(OneOrMore(Word(printables)))
self._node_name = Optional(action_symbols)('action') + name('name')
cls._node_name = Optional(action_symbols)('action') + name('name')
cls._initialized = True
def _entry(self, xml_element):
'''Метод для парсинга тега entry.'''
try:
element_items = OrderedDict(xml_element.attrib)
@ -71,6 +82,7 @@ class XMLGConfFormat(BaseFormat):
return OrderedDict()
def _gconf(self, xml_element):
'''Метод для парсинга тега gconf.'''
output_dictionary = OrderedDict()
element_name = ('', xml_element.tag)
for child in xml_element:
@ -81,6 +93,7 @@ class XMLGConfFormat(BaseFormat):
return OrderedDict({element_name: output_dictionary})
def _dir(self, xml_element):
'''Метод для парсинга тега dir.'''
output_dictionary = OrderedDict()
try:
parsing_result = self._node_name.parseString(
@ -101,6 +114,7 @@ class XMLGConfFormat(BaseFormat):
return OrderedDict({element_name: output_dictionary})
def _longdesc(self, xml_element):
'''Метод для парсинга тега longdesc.'''
element_name = ('', 'longdesc')
description = xml_element.text
@ -112,6 +126,7 @@ class XMLGConfFormat(BaseFormat):
return OrderedDict({element_name: ''})
def _local_schema(self, xml_element):
'''Метод для парсинга тега local_schema.'''
output_dictionary = OrderedDict()
try:
element_name = ('', xml_element.tag,
@ -129,6 +144,7 @@ class XMLGConfFormat(BaseFormat):
return OrderedDict({element_name: output_dictionary})
def _stringvalue(self, xml_element):
'''Метод для парсинга тега stringvalue.'''
element_name = ('', 'stringvalue')
value = xml_element.text
@ -140,6 +156,7 @@ class XMLGConfFormat(BaseFormat):
return OrderedDict({element_name: ''})
def _default(self, xml_element):
'''Уже не акутальный метод, вместо него теперь _unknown'''
output_dictionary = OrderedDict()
element_name = ('', xml_element.tag, *xml_element.items())
@ -151,6 +168,7 @@ class XMLGConfFormat(BaseFormat):
return OrderedDict({element_name: output_dictionary})
def _li(self, xml_element):
'''Метод для разбора элементов с тегом li.'''
child = next(iter(xml_element))
list_element = self._processing_methods.get(child.tag,
@ -166,6 +184,8 @@ class XMLGConfFormat(BaseFormat):
@property
def document_text(self):
'''Метод для получения исходного текста документа. Использует
рекурсивный метод _build_section.'''
gconf_header = next(iter(self._document_dictionary))
root = Element('gconf')
@ -180,6 +200,9 @@ class XMLGConfFormat(BaseFormat):
return xml_document
def _build_section(self, current_element, dictionary):
'''Метод для перевода словаря xml-документа обратно в текст документа.
Для этого рекурсивно строит дерево xml-документа пригодное работы lxml.
'''
for dict_element in dictionary.keys():
element_tag = dict_element[1]
element_attributes = OrderedDict({key: value for key, value in

@ -14,6 +14,13 @@ except ImportError:
class XMLXfceFormat(BaseFormat):
FORMAT = 'xml_xfce'
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._initialized:
cls._initialize_parser()
return super().__new__(cls)
def __init__(self, document_text: str, ignore_comments=False):
processing_methods = OrderedDict({'channel': self._channel,
'property': self._property,
@ -27,19 +34,22 @@ class XMLXfceFormat(BaseFormat):
else:
self._parse_xml_to_dictionary(document_text)
def _initialize_parser(self):
@classmethod
def _initialize_parser(cls):
'''Метод для инициализации парсерa.'''
action_symbols = (Literal('!') | Literal('-'))
name = originalTextFor(OneOrMore(Word(printables)))
self._node_name = Optional(action_symbols)('action') + name('name')
cls._node_name = Optional(action_symbols)('action') + name('name')
# Кортежи с названиями атрибутов различных элементов.
self._ELEMENT_ATTRIBUTES = ('tag', 'name', 'type', 'value')
self._CHANNEL_ATTRIBUTES = ('tag', 'name', 'version')
self._VALUE_ATTRIBUTES = ('tag', 'type', 'value')
cls._ELEMENT_ATTRIBUTES = ('tag', 'name', 'type', 'value')
cls._CHANNEL_ATTRIBUTES = ('tag', 'name', 'version')
cls._VALUE_ATTRIBUTES = ('tag', 'type', 'value')
def _property(self, xml_element):
'''Метод для парсинга тега property.'''
try:
parsing_result = self._node_name.parseString(
xml_element.attrib['name']
@ -77,6 +87,7 @@ class XMLXfceFormat(BaseFormat):
return OrderedDict({element_name: output})
def _value(self, xml_element):
'''Метод для парсинга тега value.'''
try:
value = (xml_element.tag,
xml_element.attrib['type'],
@ -87,6 +98,7 @@ class XMLXfceFormat(BaseFormat):
return value
def _channel(self, xml_element):
'''Метод для парсинга тега channel.'''
output_dictionary = OrderedDict()
try:
parsing_result = self._node_name.parseString(
@ -114,6 +126,8 @@ class XMLXfceFormat(BaseFormat):
@property
def document_text(self):
'''Метод для получения исходного текста документа. Использует
рекурсивный метод _build_section.'''
channel = next(iter(self._document_dictionary.keys()))
channel_head = OrderedDict(
{key: value for key, value in
@ -132,6 +146,9 @@ class XMLXfceFormat(BaseFormat):
return xml_document
def _build_section(self, current_element, dictionary):
'''Метод для перевода словаря xml-документа обратно в текст документа.
Для этого рекурсивно строит дерево xml-документа пригодное работы lxml.
'''
for dict_element in dictionary.keys():
element_head = OrderedDict({key: value for key, value in
zip(self._ELEMENT_ATTRIBUTES,

@ -139,45 +139,6 @@ class TestParsingMethods:
compiz_object = CompizFormat(document_text)
assert compiz_object._document_dictionary == result
def test_if_comment_parameter_is_set_for_template__format_object_will_parse_comments_with_comment_symbol_from_this_parameter(self):
document_text = '''
# Comment
[Added Associations]
@ Comment1
application/illustrator=zzz-gimp.desktop
application/pdf=evince.desktop;
@ Comment2
# Comment3
application/rtf=libreoffice-writer.desktop;
[Other Section]
@Comment
!application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop;
'''
section_1 = OrderedDict({'#': ['# Comment'],
('', 'application/illustrator'):
['@ Comment1',
'zzz-gimp.desktop'],
('', 'application/pdf'):
['evince.desktop;'],
('', 'application/rtf'):
['@ Comment2',
'# Comment3',
'libreoffice-writer.desktop;']})
section_2 = OrderedDict({('!', 'application/vnd.oasis.opendocument.spreadsheet'):
['@Comment',
"calculate-calc.desktop;"]})
result = OrderedDict({('', 'Added Associations'): section_1,
('', 'Other Section'): section_2})
compiz_object = CompizFormat(document_text, comment_symbol='@')
assert compiz_object._document_dictionary == result
def test_if_the_ignore_comments_flag_is_set__the_parser_ignores_all_comments(self):
document_text = '''
# Comment

Loading…
Cancel
Save