# vim: fileencoding=utf-8 # # ToDo: написать счетчик скобок для финальной оценки корректности # документа. # from .base_format import Format from collections import OrderedDict from pyparsing import originalTextFor, Literal, Word, printables, alphanums,\ ParseException, Regex, Group, Optional, alphas, lineEnd,\ lineStart, Keyword class DovecotFormat(Format): FORMAT = 'dovecot' EXECUTABLE = False _initialized = 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, template_path: str, ignore_comments: bool = False, join_before: bool = False, add_header: bool = False, already_changed: bool = False, **kwargs): processing_methods = [self._parse_comment_line, self._parse_section_start_line, self._parse_include_line, self._parse_section_end_line, self._parse_parameter_line, self._parse_parameter_to_delete_line] super().__init__(processing_methods) self._ignore_comments: bool = ignore_comments self._comments_processing: bool = True self._need_finish: bool = True self._join_before: bool = join_before self._section_stack: OrderedDict = OrderedDict() self._current_section_name: str = '' self._last_comments_list: list = [] self.header: str if add_header and not ignore_comments: self.header, document_text = self._get_header_and_document_text( document_text, template_path, already_changed=already_changed) else: self.header = '' document_text = document_text.strip() if document_text == '': self._document_dictionary = OrderedDict() else: document_lines = self._get_list_of_logic_lines(document_text) self._lines_to_dictionary(document_lines) @classmethod def _initialize_parser(cls): # Знаки пунктуации и действий. left_brace = Literal('{') right_brace = Literal('}') action_symbols = (Literal('!') | Literal('-')) cls._comment_line_parser = originalTextFor( Literal(cls.comment_symbol) + Regex(r'.*'))('comment') # Для парсинга строк с началом секций. section = Word(alphas, alphanums+'-_', excludeChars='{}') section_name = Word(printables, excludeChars='{}') cls._section_start_parser = Group(Optional(action_symbols, default='')('action') + section + Optional(section_name) + left_brace.suppress())('name') # Для парсинга строк, указывающих конец секций. cls._section_end_parser = lineStart() + right_brace + lineEnd() # Для парсинга строк, содержащих параметры. parameter_name = Word(alphas, alphanums+'_-', excludeChars='{}=') parameter_value = Regex(r'\S+(\s+\S+)*') cls._parameter_line_parser = (Group(Optional(action_symbols, default='')('action') + parameter_name)('name') + Literal('=').suppress() + originalTextFor( parameter_value )('value')) # Для парсинга строк с параметрами, подлежащими удалению. cls._parameter_to_delete_parser = ( action_symbols('action') + parameter_name + Optional(Literal('=')).suppress() ) # Для парсинга строк, содержащих директиву !include. include = Keyword('!include') | Keyword('!include_try') include_line_plain = (Optional(~action_symbols, default='')('action') + include('keyword') + Regex(r'\S+')('value')) include_line_to_delete = (action_symbols('action') + include('keyword') + Regex(r'\S+')('value')) cls._include_line_parser = (include_line_plain | include_line_to_delete) cls._initialized = True def _parse_section_start_line(self, line): try: parsing_result = self._section_start_parser.parseString(line) self._match = True section_name = tuple(parsing_result.name.asList()) if not self._last_comments_list == []: section_content = OrderedDict({'#': self._last_comments_list}) self._last_comments_list = [] else: section_content = OrderedDict() new_section = OrderedDict({section_name: section_content}) if self._section_stack == OrderedDict(): if section_name in self._document_dictionary: new_section = OrderedDict( {section_name: self._document_dictionary[section_name]} ) else: if section_name in \ self._section_stack[self._current_section_name]: new_section = OrderedDict({ section_name: self._section_stack[self._current_section_name] [section_name] }) else: self._section_stack[self._current_section_name].update( new_section ) self._section_stack.update(new_section) self._current_section_name = section_name except ParseException: return def _parse_section_end_line(self, line): try: self._section_end_parser.parseString(line) self._match = True last_section_name, last_section = self._section_stack.popitem() if self._section_stack == OrderedDict(): self._item_to_add = OrderedDict({last_section_name: last_section}) self._ready_to_update = True self._current_section_name = '' else: self._current_section_name = next(reversed( self._section_stack )) except ParseException: return def _parse_parameter_line(self, line): try: parsing_result = self._parameter_line_parser.parseString(line) self._match = True parameter_name = tuple(parsing_result.name.asList()) parameter_value = (self._last_comments_list + [parsing_result.value.strip()]) self._last_comments_list = [] parameter = OrderedDict({parameter_name: parameter_value}) if self._section_stack == OrderedDict(): self._item_to_add = parameter self._ready_to_update = True else: self._section_stack[self._current_section_name].update( parameter ) except ParseException: return def _parse_parameter_to_delete_line(self, line): try: parsing_result = self._parameter_to_delete_parser.parseString(line) self._match = True parameter_name = tuple(parsing_result.asList()) parameter_value = self._last_comments_list + [''] self._last_comments_list = [] parameter = OrderedDict({parameter_name: parameter_value}) if self._section_stack == OrderedDict(): self._item_to_add = parameter self._ready_to_update = True else: self._section_stack[self._current_section_name].update( parameter ) except ParseException: return def _parse_include_line(self, line): try: parsing_result = self._include_line_parser.parseString(line) self._match = True parameter_name = tuple(parsing_result.asList()) if parsing_result.action == '-': return parameter_value = self._last_comments_list + [''] self._last_comments_list = [] include_item = OrderedDict({parameter_name: parameter_value}) if self._section_stack == OrderedDict(): self._item_to_add = include_item self._ready_to_update = True else: self._section_stack[self._current_section_name].update( include_item ) except ParseException: return def _parse_comment_line(self, line): try: parsing_result = self._comment_line_parser.parseString(line) self._match = True if not self._ignore_comments: self._last_comments_list.append(parsing_result.comment) except ParseException: return