|
|
|
@ -44,6 +44,8 @@ class Variables(MutableMapping):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def resolve_or_missing(context, key, missing=missing, env={}):
|
|
|
|
|
'''Переопределение функции из для поиска значений переменных из jinja2.
|
|
|
|
|
Ищет переменные в datavars.'''
|
|
|
|
|
datavars = context.parent['__datavars__']
|
|
|
|
|
if key in context.vars:
|
|
|
|
|
return context.vars[key]
|
|
|
|
@ -58,6 +60,7 @@ def resolve_or_missing(context, key, missing=missing, env={}):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CalculateContext(Context):
|
|
|
|
|
'''Класс контекста позволяющий искать значения datavars и сохранять их.'''
|
|
|
|
|
_env_set = set()
|
|
|
|
|
|
|
|
|
|
def resolve(self, key):
|
|
|
|
@ -80,20 +83,20 @@ class CalculateContext(Context):
|
|
|
|
|
env=self._env_set)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConditionFailed(TemplateSyntaxError):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Parameters(MutableMapping):
|
|
|
|
|
'''Класс для хранения параметров и условий, взятых из шаблона, и передачи
|
|
|
|
|
'''Класс для хранения параметров, взятых из шаблона, и передачи
|
|
|
|
|
их шаблонизатору.'''
|
|
|
|
|
def __init__(self, parameters_dictionary={}, condition=True):
|
|
|
|
|
def __init__(self, parameters_dictionary={}):
|
|
|
|
|
self.__parameters = parameters_dictionary
|
|
|
|
|
self.__condition = condition
|
|
|
|
|
|
|
|
|
|
def set_parameters(self, *args, **kwargs):
|
|
|
|
|
parameters = dict(*args, **kwargs)
|
|
|
|
|
self.__parameters.update(parameters)
|
|
|
|
|
|
|
|
|
|
def set_condition(self, condition):
|
|
|
|
|
self.__condition = self.__condition and condition
|
|
|
|
|
|
|
|
|
|
def __getitem__(self, name):
|
|
|
|
|
return self.__parameters[name]
|
|
|
|
|
|
|
|
|
@ -110,12 +113,7 @@ class Parameters(MutableMapping):
|
|
|
|
|
return len(self.__parameters)
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return '<Parameters: {0}, condition={1}>'.format(self.__parameters,
|
|
|
|
|
self.__condition)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def condition(self):
|
|
|
|
|
return self.__condition
|
|
|
|
|
return '<Parameters: {0}>'.format(self.__parameters)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def parameters(self):
|
|
|
|
@ -123,7 +121,9 @@ class Parameters(MutableMapping):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CalculateExtension(Extension):
|
|
|
|
|
'''Класс расширения для jinja2, поддерживающий теги calculate-шаблонов.'''
|
|
|
|
|
_parameters_set = set()
|
|
|
|
|
_datavars = Variables()
|
|
|
|
|
|
|
|
|
|
def __init__(self, environment):
|
|
|
|
|
self.tags = {'calculate', 'save', 'set_var'}
|
|
|
|
@ -134,6 +134,8 @@ class CalculateExtension(Extension):
|
|
|
|
|
self.parse_methods = {'calculate': self.parse_calculate,
|
|
|
|
|
'save': self.parse_save}
|
|
|
|
|
|
|
|
|
|
self.environment = environment
|
|
|
|
|
|
|
|
|
|
def parse(self, parser):
|
|
|
|
|
self.parser = parser
|
|
|
|
|
self.stream = parser.stream
|
|
|
|
@ -141,6 +143,8 @@ class CalculateExtension(Extension):
|
|
|
|
|
return [self.parse_methods[tag_token]()]
|
|
|
|
|
|
|
|
|
|
def parse_save(self):
|
|
|
|
|
'''Метод для разбора тега save, сохраняющего значение указанной
|
|
|
|
|
переменной datavars.'''
|
|
|
|
|
lineno = next(self.stream).lineno
|
|
|
|
|
target_file = nodes.Const('', lineno=lineno)
|
|
|
|
|
|
|
|
|
@ -175,9 +179,10 @@ class CalculateExtension(Extension):
|
|
|
|
|
lineno=lineno)
|
|
|
|
|
|
|
|
|
|
def parse_calculate(self):
|
|
|
|
|
'''Метод для разбора тега calculate, содержащего значения параметров и
|
|
|
|
|
условия выполнения шаблона.'''
|
|
|
|
|
pairs_list = []
|
|
|
|
|
expect_comma_flag = False
|
|
|
|
|
conditions = nodes.Const(True)
|
|
|
|
|
|
|
|
|
|
lineno = next(self.stream).lineno
|
|
|
|
|
|
|
|
|
@ -185,17 +190,22 @@ class CalculateExtension(Extension):
|
|
|
|
|
if expect_comma_flag:
|
|
|
|
|
self.stream.expect('comma')
|
|
|
|
|
|
|
|
|
|
if self.stream.current.type == 'name':
|
|
|
|
|
if (self.stream.current.value in self._parameters_set and
|
|
|
|
|
self.stream.look().type != 'dot'):
|
|
|
|
|
pairs_list.append(self.get_parameter_node())
|
|
|
|
|
else:
|
|
|
|
|
conditions = nodes.And(
|
|
|
|
|
self.parser.parse_expression(
|
|
|
|
|
with_condexpr=True
|
|
|
|
|
),
|
|
|
|
|
conditions
|
|
|
|
|
)
|
|
|
|
|
if (self.stream.current.type == 'name'
|
|
|
|
|
and self.stream.current.value in self._parameters_set
|
|
|
|
|
and self.stream.look().type != 'dot'
|
|
|
|
|
and self.stream.look().type not in
|
|
|
|
|
self.CONDITION_TOKENS_TYPES):
|
|
|
|
|
# разбираем параметр.
|
|
|
|
|
pairs_list.append(self.get_parameter_node())
|
|
|
|
|
elif (self.stream.current.type == 'name'
|
|
|
|
|
or self.stream.current.type == 'lparen'):
|
|
|
|
|
# разбираем условие. Если условие False -- кидаем исключение.
|
|
|
|
|
condition_result = self.get_condition_result()
|
|
|
|
|
if not condition_result:
|
|
|
|
|
raise ConditionFailed(
|
|
|
|
|
'Condition is failed',
|
|
|
|
|
lineno=self.stream.current.lineno
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
raise TemplateSyntaxError('Name is expected in calculate tag.',
|
|
|
|
|
lineno=self.stream.current.lineno)
|
|
|
|
@ -203,13 +213,47 @@ class CalculateExtension(Extension):
|
|
|
|
|
dictionary_node = nodes.Dict(pairs_list)
|
|
|
|
|
save_node = self.call_method('save_parameters',
|
|
|
|
|
[dictionary_node,
|
|
|
|
|
conditions,
|
|
|
|
|
nodes.ContextReference()],
|
|
|
|
|
lineno=lineno)
|
|
|
|
|
return nodes.Output([save_node], lineno=lineno)
|
|
|
|
|
|
|
|
|
|
def get_condition_result(self):
|
|
|
|
|
'''Метод для разбора условий из тега calculate.'''
|
|
|
|
|
condition_list = []
|
|
|
|
|
# собираем исходный код условия из токенов.
|
|
|
|
|
# вероятно, следует придумать лучший способ.
|
|
|
|
|
while (self.stream.current.type != 'block_end' and
|
|
|
|
|
self.stream.current.type != 'comma'):
|
|
|
|
|
if self.stream.current.type == 'string':
|
|
|
|
|
condition_list.append("'{}'".format(
|
|
|
|
|
self.stream.current.value
|
|
|
|
|
))
|
|
|
|
|
elif self.stream.current.type == 'dot':
|
|
|
|
|
self.stream.skip(1)
|
|
|
|
|
if self.stream.current.type == 'name':
|
|
|
|
|
next_name = '.' + self.stream.current.value
|
|
|
|
|
else:
|
|
|
|
|
raise TemplateSyntaxError(
|
|
|
|
|
'Variable name is not correct.',
|
|
|
|
|
lineno=self.stream.current.lineno
|
|
|
|
|
)
|
|
|
|
|
condition_list[-1] = condition_list[-1] + next_name
|
|
|
|
|
else:
|
|
|
|
|
condition_list.append(
|
|
|
|
|
str(self.stream.current.value)
|
|
|
|
|
)
|
|
|
|
|
self.stream.skip(1)
|
|
|
|
|
|
|
|
|
|
condition = ' '.join(condition_list)
|
|
|
|
|
|
|
|
|
|
# компилируем исходный код условия и получаем результат его вычисления.
|
|
|
|
|
cond_expr = self.environment.compile_expression(condition)
|
|
|
|
|
condition_result = cond_expr(__datavars__=self._datavars)
|
|
|
|
|
return condition_result
|
|
|
|
|
|
|
|
|
|
def save_variable(self, variable_name, right_value, target_file, context):
|
|
|
|
|
'''Временный метод для сохранения значений переменных.'''
|
|
|
|
|
'''Метод для сохранения значений переменных указанных в теге save.'''
|
|
|
|
|
# временная реализация.
|
|
|
|
|
datavars = context.parent['__datavars__']
|
|
|
|
|
module_name = variable_name[0]
|
|
|
|
|
namespaces = variable_name[1:-1]
|
|
|
|
@ -228,6 +272,7 @@ class CalculateExtension(Extension):
|
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
|
def get_parameter_node(self):
|
|
|
|
|
'''Метод для разбра параметров, содержащихся в теге calculate.'''
|
|
|
|
|
lineno = self.stream.current.lineno
|
|
|
|
|
parameter_name = self.stream.expect('name').value
|
|
|
|
|
parameter_name_node = nodes.Const(parameter_name, lineno=lineno)
|
|
|
|
@ -235,6 +280,8 @@ class CalculateExtension(Extension):
|
|
|
|
|
parameter_value = self.stream.current.value
|
|
|
|
|
parameter_rvalue = self.parser.parse_expression(with_condexpr=True)
|
|
|
|
|
if parameter_name == 'env':
|
|
|
|
|
# если параметр env -- обновляем множенство значений env
|
|
|
|
|
# контекста вo время парсинга.
|
|
|
|
|
env_names = parameter_value.split(',')
|
|
|
|
|
for name in env_names:
|
|
|
|
|
CalculateContext._env_set.add(name.strip())
|
|
|
|
@ -242,18 +289,19 @@ class CalculateExtension(Extension):
|
|
|
|
|
parameter_rvalue = nodes.Const(True, lineno=lineno)
|
|
|
|
|
return nodes.Pair(parameter_name_node, parameter_rvalue)
|
|
|
|
|
|
|
|
|
|
def save_parameters(cls, parameters_dictionary, compare_result, context):
|
|
|
|
|
def save_parameters(cls, parameters_dictionary, context):
|
|
|
|
|
'''Метод для сохранения значений параметров.'''
|
|
|
|
|
context.parent['__parameters__'].set_parameters(parameters_dictionary)
|
|
|
|
|
context.parent['__parameters__'].set_condition(compare_result)
|
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateEngine():
|
|
|
|
|
class TemplateEngine:
|
|
|
|
|
def __init__(self, directory_path='/',
|
|
|
|
|
parameters_set=set(),
|
|
|
|
|
env_set=set(),
|
|
|
|
|
datavars_module=Variables()):
|
|
|
|
|
CalculateExtension._parameters_set = parameters_set
|
|
|
|
|
CalculateExtension._datavars = datavars_module
|
|
|
|
|
|
|
|
|
|
self._datavars_module = datavars_module
|
|
|
|
|
self._parameters_object = Parameters()
|
|
|
|
@ -264,32 +312,30 @@ class TemplateEngine():
|
|
|
|
|
self.environment.context_class = CalculateContext
|
|
|
|
|
|
|
|
|
|
def change_directory(self, directory_path):
|
|
|
|
|
'''Метод для смены директории в загрузчике.'''
|
|
|
|
|
self.environment.loader = FileSystemLoader(directory_path)
|
|
|
|
|
|
|
|
|
|
def process_template(self, template_path, env=set()):
|
|
|
|
|
'''Метод для обработки файла шаблона, расположенного по указанному
|
|
|
|
|
пути.'''
|
|
|
|
|
CalculateContext._env_set = env
|
|
|
|
|
template = self.environment.get_template(template_path)
|
|
|
|
|
self._parameters_object = Parameters(parameters_dictionary={},
|
|
|
|
|
condition=True)
|
|
|
|
|
self._parameters_object = Parameters(parameters_dictionary={})
|
|
|
|
|
self._template_text = template.render(
|
|
|
|
|
__datavars__=self._datavars_module,
|
|
|
|
|
__parameters__=self._parameters_object
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def process_template_from_string(self, string, env=set()):
|
|
|
|
|
'''Метод для обработки текста шаблона.'''
|
|
|
|
|
CalculateContext._env_set = env
|
|
|
|
|
template = self.environment.from_string(string)
|
|
|
|
|
self._parameters_object = Parameters(parameters_dictionary={},
|
|
|
|
|
condition=True)
|
|
|
|
|
self._parameters_object = Parameters(parameters_dictionary={})
|
|
|
|
|
self._template_text = template.render(
|
|
|
|
|
__datavars__=self._datavars_module,
|
|
|
|
|
__parameters__=self._parameters_object
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def condition(self):
|
|
|
|
|
return self._parameters_object.condition
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def parameters(self):
|
|
|
|
|
return self._parameters_object.parameters
|
|
|
|
|