# vim: fileencoding=utf-8 # from .base_format import BaseFormat, FormatError from collections import OrderedDict import re try: from lxml.etree.ElementTree import fromstring except ImportError: from xml.etree.ElementTree import fromstring class PatchFormat(BaseFormat): FORMAT = 'patch' FORMAT_PARAMETERS = {'multiline', 'dotall', 'comment'} def __init__(self, document_text: str, multiline=False, dotall=False, comment_symbol=''): processing_methods = OrderedDict() super().__init__(processing_methods) self._multiline_flag = multiline self._dotall_flag = dotall self._parsed_patch = None self._document_to_patch = '' self._FLAG_VALUES = {'True': True, 'False': False, 'true': True, 'false': False, '1': True, '0': False} self._XML_ROOT_LINE = '\ {0}' 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) except Exception: 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 if self._parse_patch is None: return False else: if not self._patch_document(document_to_patch): 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': raise FormatError('incorrect text of the template') while True: for patch_tag in PATCH_DOCUMENT_TAGS: patch_element = next(patch_iterator, None) if patch_element is None: if patch_tag == 'text': raise FormatError('last Text ' 'object is missed.') else: break if patch_element.tag == patch_tag: if patch_element.text is not None: element_text = patch_element.text.strip() if element_text == '': raise FormatError( ("Error: Incorrect text of the " "template: <{0}>%s").format( patch_tag )) else: raise FormatError("Error: Incorrect text of the " "template: <{0}>").format( patch_tag ) if patch_tag == 'reg': dotall = patch_element.attrib.get('dotall', False) regex_flags = 0 if 'multiline' in patch_element.attrib: multiline = patch_element.attrib['multiline'] if multiline not in self._FLAG_VALUES: raise FormatError('invalid multiline value') else: multiline = self._FLAG_VALUES[multiline] # Если глобально флаг MULTILINE включен, но в # атрибутах тэга этот флаг присутствует со # значением False -- для этого регулярного # выражения этот флаг также будет False. multiline_global = self._multiline_flag & multiline else: multiline = False multiline_global = self._multiline_flag if multiline_global or multiline: regex_flags |= re.MULTILINE if 'dotall' in patch_element.attrib: dotall = patch_element.attrib['dotall'] if dotall not in self._FLAG_VALUES: raise FormatError('invalid dotall value') else: dotall = self._FLAG_VALUES[dotall] # Если глобально флаг DOTALL включен, но в # атрибутах тэга этот флаг присутствует со # значением False -- для этого регулярного # выражения этот флаг также будет False. dotall_global = self._dotall_flag & dotall else: dotall = False dotall_global = self._dotall_flag if dotall_global or dotall: regex_flags |= re.DOTALL regex_expression = re.compile(element_text, regex_flags) else: text_for_replace = element_text else: if patch_element.tag in PATCH_DOCUMENT_TAGS: error_message = '<{0}> is expected, <{1}> instead.'.\ format(patch_tag, patch_element.tag) else: error_message = 'unknown tag: {0}'.format( patch_element.tag ) raise ("incorrect text of the template: {}".format( error_message)) else: self._document_to_patch = re.sub(regex_expression, text_for_replace, self._document_to_patch) continue return True