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.

203 lines
8.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# vim: fileencoding=utf-8
#
from .base_format import Format, FormatError
from ..template_engine import ParametersContainer
from collections import OrderedDict
import re
try:
from lxml.etree.ElementTree import fromstring
except ImportError:
from xml.etree.ElementTree import fromstring
class RegexFormat(Format):
FORMAT = 'regex'
EXECUTABLE = False
FORMAT_PARAMETERS = {'multiline', 'dotall', 'comment'}
comment_symbol = '#'
def __init__(self, document_text: str,
template_path,
ignore_comments=False,
join_before=False,
add_header=False,
already_changed=False,
parameters=ParametersContainer(),
**kwargs):
processing_methods = OrderedDict()
super().__init__(processing_methods)
self._multiline_flag = parameters.multiline
self._dotall_flag = parameters.dotall
if parameters.comment is not False:
self.comment_symbol = parameters.comment
self._parsed_patch = None
if add_header and not ignore_comments:
self.header, self._document_text, self.shebang =\
self._get_header_and_document_text(
document_text,
template_path,
already_changed=already_changed,
check_shebang=True)
else:
self.header = ''
self.shebang = ''
self._document_text = document_text
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>'
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 join_template(self, template):
tmp_multiline = template._multiline_flag
tmp_dotall = template._dotall_flag
template._multiline_flag = self._multiline_flag
template._dotall_flag = self._dotall_flag
self._document_text = template._join(self._document_text)
template._multiline_flag = tmp_multiline
template._dotall_flag = tmp_dotall
def _join(self, input_text):
'''Метод для запуска наложения патча.'''
self._parse_patch(self._document_text)
if not input_text.strip() == '':
self._document_to_patch = input_text
else:
return input_text
if not self._patch_document(input_text):
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.iter()
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)
# Предупреждение если отсутствует последний <text>bla</text>
if patch_element is None:
if patch_tag == 'text':
raise FormatError('last <text>Text</text> '
'object is missed.')
else:
break
if patch_element.tag == patch_tag:
# Если <reg> тег пустой -- это ошибка.
if patch_element.text is not None:
element_text = patch_element.text
if patch_tag == "reg" and element_text.strip() == '':
raise FormatError(
("Error: Incorrect text of the "
"template: <{0}>%s</{0}>").format(
patch_tag
))
elif patch_tag == "reg":
raise FormatError("Error: Incorrect text of the "
"template: <{0}></{0}>".format(
patch_tag))
else:
element_text = ''
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 включен, но в
# атрибутах тэга <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')
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)
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
@property
def document_text(self):
return '{}{}{}'.format(self.shebang, self.header, self._document_text)