You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
7.5 KiB

4 years ago
# vim: fileencoding=utf-8
#
from .base_format import BaseFormat, FormatError
4 years ago
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='', join_before=False):
4 years ago
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 = '<?xml version="1.0" encoding="utf-8"?>\
<patch>{0}</patch>'
self._parse_patch(document_text)
4 years ago
def _parse_patch(self, patch_text):
'''Метод, составляющий из текста шаблона xml документ и разбирающий его
с помощью lxml.'''
4 years ago
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')
4 years ago
def execute_format(self, document_to_patch):
'''Метод для запуска наложения патча.'''
4 years ago
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.')
4 years ago
else:
after_patch = self._document_to_patch
self._document_to_patch = ''
# Пока что возвращает результат наложения шаблона, это временно
4 years ago
return after_patch
def _patch_document(self, document_to_patch):
'''Метод, обходящий теги шаблона и использующий указанные в нем
регулярные выражения.'''
4 years ago
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')
4 years ago
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>Text</text> '
'object is missed.')
4 years ago
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</{0}>").format(
4 years ago
patch_tag
))
4 years ago
else:
raise FormatError("Error: Incorrect text of the "
"template: <{0}></{0}>").format(
4 years ago
patch_tag
)
4 years ago
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')
4 years ago
else:
multiline = self._FLAG_VALUES[multiline]
# Если глобально флаг MULTILINE включен, но в
# атрибутах тэга <reg> этот флаг присутствует со
# значением 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')
4 years ago
else:
dotall = self._FLAG_VALUES[dotall]
# Если глобально флаг DOTALL включен, но в
# атрибутах тэга <reg> этот флаг присутствует со
# значением 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)
4 years ago
else:
error_message = 'unknown tag: {0}'.format(
4 years ago
patch_element.tag
)
raise ("incorrect text of the template: {}".format(
error_message))
4 years ago
else:
self._document_to_patch = re.sub(regex_expression,
text_for_replace,
self._document_to_patch)
continue
return True