Errors processing is almost done for the datavars module.

packages
Иванов Денис 4 years ago
parent a2ce6f87f7
commit 8f02dcd5e5

@ -1065,9 +1065,8 @@ class CalculateExtension(Extension):
VariableNode(variable_name, namespace,
variable_type=IniType, source=str(new_value))
else:
raise SaveError("can not create variable '{}' in the not"
" 'custom' namespace".
format('.'.join(variable)))
raise SaveError("can not create variable '{}' in not 'custom'"
" namespace".format('.'.join(variable)))
if target:
if namespace.variables[variable_name].variable_type is HashType:

@ -207,7 +207,7 @@ class TemplateWrapper:
self.format_class = ParametersProcessor.\
available_formats[self.parameters.format]
else:
# TODO Здесь будет детектор форматов.
# TODO Здесь будет детектор форматов. Когда-нибудь.
pass
# Если по этому пути что-то есть -- проверяем конфликты.

@ -142,26 +142,33 @@ class ColorPrint:
# Заглушка вместо модуля вывода.
class IOModule:
def __init__(self):
def __init__(self, save_messages=False):
self.console_output = ColorPrint()
self.save_messages = save_messages
self.messages = []
def set_error(self, message):
self.messages.append(('error', message))
if self.save_messages:
self.messages.append(('error', message))
self.console_output.print_error(message)
def set_warning(self, message):
self.messages.append(('warning', message))
if self.save_messages:
self.messages.append(('warning', message))
self.console_output.print_warning(message)
def set_info(self, message):
self.messages.append(('info', message))
if self.save_messages:
self.messages.append(('info', message))
self.console_output.print_info(message)
def set_success(self, message):
self.messages.append(('success', message))
if self.save_messages:
self.messages.append(('success', message))
self.console_output.print_ok(message)
def set_failure(self, message):
self.messages.append(('fail', message))
if self.save_messages:
self.messages.append(('fail', message))
self.console_output.print_not_ok(message)

@ -191,9 +191,12 @@ class Hash:
def __init__(self, values: dict, master_variable, parent=None):
self.fixed = master_variable.fixed
self._values = dict()
self._fields = set()
for key, value in values.items():
self._values.update({key: HashValue(key, value, master_variable,
self)})
if self.fixed:
self._fields.add(key)
self.master_variable = master_variable
self.row_index = None
@ -211,7 +214,7 @@ class Hash:
self._values[key].value = value
elif self.fixed:
raise VariableError("key '{}' is unavailable for fixed"
" hash, available keys: '{}'".
" hash, available keys: '{}'.".
format(key, ', '.join(self._fields)))
else:
self._values[key] = HashValue(key, value, self.master_variable,
@ -739,7 +742,7 @@ class NamespaceNode:
def get_fullname(self) -> str:
'''Метод для получения полного имени пространства имен.'''
if self.parent is not None:
if self.parent is not None and self.parent.name != '<root>':
return '{}.{}'.format(self.parent.get_fullname(), self.name)
else:
return self.name

@ -7,14 +7,14 @@ from jinja2 import Environment, FileSystemLoader
from calculate.variables.datavars import NamespaceNode, VariableNode,\
ListType, IntegerType,\
FloatType, IniType, TableType,\
Namespace, VariableError, HashType,\
Namespace, HashType,\
VariableNotFoundError
from calculate.utils.gentoo import ProfileWalker
from calculate.utils.files import read_file, FilesError
from calculate.utils.tools import Singleton
from calculate.utils.io_module import IOModule
from pyparsing import Literal, Word, ZeroOrMore, Group, Optional, restOfLine,\
empty, printables, OneOrMore, lineno, line, col, SkipTo,\
empty, printables, OneOrMore, lineno, line, SkipTo,\
LineEnd, Combine, nums
from enum import Enum
from contextlib import contextmanager
@ -36,8 +36,6 @@ class CalculateIniParser(metaclass=Singleton):
'''Класс парсера calculate.ini файлов.'''
def __init__(self):
self._errors = []
self.operations = {"=": Define.assign,
"+=": Define.append,
"-=": Define.remove}
@ -73,8 +71,12 @@ class CalculateIniParser(metaclass=Singleton):
+ (row_index | clear_section | ~lbrack)
+ LineEnd().suppress())
def add_lineno(string, location, tokens):
tokens.append(lineno(location, string))
section_start = (namespace_start('namespace') |
table_start('table'))
section_start.setParseAction(add_lineno)
# Если содержимое ini-файла не предваряется заголовком секции,
# значит эта строка ошибочна.
@ -86,63 +88,62 @@ class CalculateIniParser(metaclass=Singleton):
+ value_operation + empty
+ restOfLine + LineEnd().suppress())
def strip_key_value(tokens):
def process_key_value(string, location, tokens):
tokens[0] = tokens[0].strip()
tokens[1] = tokens[1].strip()
tokens.append(lineno(location, string))
key_value.setParseAction(strip_key_value)
key_value.setParseAction(process_key_value)
self.ini_section_parser = (section_start
+ Group(ZeroOrMore(
Group(key_value | unexpected)))
Group(key_value
| unexpected)))
| unexpected)
self.ini_section_parser.ignore(comment)
def parse(self, data: str):
for tokens, start, end in self.ini_section_parser.scanString(data):
if tokens.getName() == "error":
if tokens[1].strip():
yield({'error': (tokens[0], tokens[1])})
continue
section, defkeys = tokens
section, section_lineno, defkeys = tokens
if section.getName() == 'namespace':
section_list = section.asList()
if section_list[-1] == []:
yield {'clear_section': (section_list[:-1], )}
yield {'clear_section': (section_list[:-1],
section_lineno)}
else:
yield {'start_section': (section_list, )}
yield {'start_section': (section_list, section_lineno)}
for defkey in defkeys:
if defkey.getName() == "error":
if defkey[1].strip():
yield({'error': (defkey[0], defkey[1])})
continue
yield {'define_key': (defkey[0], defkey[2],
self.operations[defkey[1]])}
self.operations[defkey[1]],
defkey[3])}
else:
table_list = section.asList()
if table_list[-1] == []:
yield {'clear_table': (table_list[:-1], )}
else:
table_values = {}
for defkey in defkeys:
if defkey.getName() == "error":
continue
table_values.update({defkey[0]: defkey[2]})
yield {'start_table': (table_list, table_values)}
table_values = {}
for defkey in defkeys:
if defkey.getName() == "error":
if defkey[1].strip():
yield({'error': (defkey[0], defkey[1])})
continue
table_values.update({defkey[0]: defkey[2]})
yield {'start_table': (table_list, table_values,
section_lineno)}
def _unexpected_token(self, string, location, tokens):
'''Метод вызываемый парсером, если обнаружена некорректная строка,
предназначен для получения некорректной строки и ее дальнейшего
разбора.'''
error_line = line(location, string).strip()
if error_line:
self._errors.append((error_line, lineno(location, string),
col(location, string)))
@property
def errors(self):
errors = self._errors
self._errors = []
return errors
return [lineno(location, string), line(location, string)]
class NamespaceIniFiller:
@ -150,25 +151,21 @@ class NamespaceIniFiller:
из calculate.ini файла.'''
available_sections = {'custom'}
def __init__(self, restrict_creation=True, ouput=None):
def __init__(self, restrict_creation=True):
self.ini_parser = CalculateIniParser()
self._errors = []
# Флаги, определяющие возможность создания новых переменных и новых
# пространств имен в данном пространстве имен.
self.restricted = restrict_creation
self.modify_only = False
self.output = ouput
def error(self, lineno, error_message):
self.errors.append(lineno, error_message)
def fill(self, namespace: NamespaceNode, ini_file_text: str) -> None:
'''Метод для разбора calculate.ini файла и добавления всех его
переменных в указанное пространство имен.'''
self.namespace = namespace
self.current_namespace = self.namespace
self.errors = []
self._errors = []
for parsed_line in self.ini_parser.parse(ini_file_text):
self._line_processor(**parsed_line)
@ -176,7 +173,6 @@ class NamespaceIniFiller:
def _line_processor(self, start_section=None,
clear_section=None,
start_table=None,
clear_table=None,
define_key=None,
error=None, **kwargs):
'''Метод вызывающий обработку токенов, выдаваемых парсером в
@ -187,15 +183,12 @@ class NamespaceIniFiller:
self.clear_section(*clear_section)
elif start_table is not None:
self.start_table(*start_table)
elif clear_table is not None:
self.clear_table(*clear_table)
elif define_key is not None:
self.define_key(*define_key)
elif error is not None:
self._set_error(error[0], 'SyntaxError', error[1])
for error in self.ini_parser.errors:
self.set_error(*error)
def start_section(self, sections: str) -> None:
def start_section(self, sections: str, lineno) -> None:
'''Метод для получения доступа и создания пространств имен.'''
if self.restricted:
self.modify_only = sections[0] not in self.available_sections
@ -204,9 +197,12 @@ class NamespaceIniFiller:
for section in sections:
if isinstance(self.current_namespace, Datavars):
if section not in self.current_namespace:
# TODO Поменять на простой вывод.
raise VariableNotFoundError("variables package '{}' is not"
" found".format(section))
# TODO Поменять на логгирование.
self._set_error(lineno, 'VariableError',
"variables package '{}' is not found.".
format(section))
self.current_namespace = None
return
elif isinstance(self.current_namespace, NamespaceNode):
if section not in self.current_namespace.namespaces:
if (section in self.current_namespace.variables and
@ -216,26 +212,38 @@ class NamespaceIniFiller:
self.current_namespace = self.current_namespace.\
variables[section]
return
elif not self.modify_only:
self.current_namespace.add_namespace(
NamespaceNode(section))
else:
if not self.modify_only:
self.current_namespace.add_namespace(
NamespaceNode(section))
else:
self.current_namespace = None
return
# TODO Поменять на логгирование.
self._set_error(lineno, 'VariableError',
"can not create namespace '{}.{}' in"
" not 'custom' namespace.".format(
self.current_namespace.get_fullname(),
section))
self.current_namespace = None
return
self.current_namespace = self.current_namespace.namespaces[section]
def clear_section(self, sections: list) -> None:
def clear_section(self, sections: list, lineno) -> None:
'''Метод для очистки пространства имен.'''
if self.restricted:
self.modify_only = sections[0] not in self.available_sections
current_namespace = self.namespace
for section in sections:
if (isinstance(current_namespace, Datavars) and
section in current_namespace):
current_namespace = current_namespace[section]
if isinstance(current_namespace, Datavars):
if section in current_namespace:
current_namespace = current_namespace[section]
else:
self._set_error(lineno, 'VariableError',
"can not clear"" namespace '{}'. Variables"
" package '{}' is not found.".format(
".".join(sections),
section))
return
elif isinstance(current_namespace, NamespaceNode):
if section in current_namespace.namespaces:
current_namespace = current_namespace[section]
@ -248,11 +256,20 @@ class NamespaceIniFiller:
table_variable.source = table_to_clear
return
else:
self._set_error(lineno, 'VariableError',
"can not clear namespace '{}'. Namespace"
" is not found.".format(
".".join(sections)))
return
if not self.modify_only:
current_namespace.clear()
else:
self._set_error(lineno, 'VariableError',
"can not clear namespace '{}' from not 'custom'"
" namespace.".format(current_namespace.
get_fullname()))
def start_table(self, sections: str, row) -> None:
def start_table(self, sections: str, row, lineno) -> None:
'''Метод для создания и модификации таблиц.'''
if self.restricted:
self.modify_only = sections[0] not in self.available_sections
@ -265,15 +282,27 @@ class NamespaceIniFiller:
self.current_namespace.add_namespace(
NamespaceNode(section))
else:
self._set_error(lineno, 'VariableError',
"can not create table '{}.{}', namespace"
" '{}' is not found.".format(
".".join(sections),
table_name, section))
self.current_namespace = None
return
self.current_namespace = self.current_namespace.namespaces[section]
if table_name not in self.current_namespace.variables:
if not self.modify_only:
table_variable = VariableNode(table_name,
self.current_namespace,
variable_type=TableType,
source=[row])
else:
self._set_error(lineno, 'VariableError',
"can not create table '{}.{}' in not 'custom'"
" namespace.".format(self.current_namespace.
get_fullname(),
table_name))
else:
table_variable = self.current_namespace.variables[table_name]
table = table_variable.get_value()
@ -283,28 +312,28 @@ class NamespaceIniFiller:
table.add_row(row)
table_variable.source = table
def define_key(self, key: str, value: str, optype) -> None:
def define_key(self, key: str, value: str, optype, lineno) -> None:
'''Метод для создания и модификации переменных.'''
if self.current_namespace is None:
return
if (isinstance(self.current_namespace, VariableNode) and
self.current_namespace.variable_type is HashType):
self.update_hash(key, value, optype)
self.update_hash(key, value, optype, lineno)
else:
if optype == Define.assign:
if key not in self.current_namespace:
self.define_variable(key, value)
self.define_variable(key, value, lineno)
else:
self.change_value(key, value)
elif optype == Define.append:
if key not in self.current_namespace:
self.define_variable(key, value)
self.define_variable(key, value, lineno)
else:
self.append_value(key, value)
elif optype == Define.remove:
if key not in self.current_namespace:
self.define_variable(key, value)
self.define_variable(key, value, lineno)
else:
self.remove_value(key, value)
@ -313,15 +342,15 @@ class NamespaceIniFiller:
variable = self.current_namespace[key]
variable.source = value
def define_variable(self, key: str, value: str) -> None:
def define_variable(self, key: str, value: str, lineno) -> None:
'''Метод для создания переменных в calculate.ini файле.'''
if not self.modify_only:
VariableNode(key, self.current_namespace, variable_type=IniType,
source=value)
else:
# TODO Какая-то обработка ошибки.
self.output.set_error("Can not create variable '{}.{}' in not"
" 'custom' namespace".format(
self._set_error(lineno, 'VariableError',
"can not create variable '{}.{}' in not 'custom'"
" namespace.".format(
self.current_namespace.get_fullname(),
key))
@ -382,25 +411,31 @@ class NamespaceIniFiller:
variable.source = variable_value
def update_hash(self, key: str, value: str, optype):
def update_hash(self, key: str, value: str, optype, lineno):
'''Метод для изменения переменных хэшей через calculate.ini.'''
hash_to_update = self.current_namespace.get_value().get_hash()
if key not in hash_to_update:
# Если ключ отсутствует в хэше, то проверяем, является ли он
# фиксированным.
if self.current_namespace.fixed:
raise VariableError("key '{}' is unavailable for fixed"
" hash, available keys: '{}'".
format(key,
', '.join(self.current_namespace.
get_value()._fields)))
self._set_error(lineno, 'VariableError',
"key '{}' is unavailable for fixed hash '{}',"
" available keys: '{}'.".format(
key,
self.current_namespace.get_fullname(),
', '.join(self.current_namespace.
get_value()._fields)))
return
else:
hash_to_update.update({key: value})
elif optype == Define.assign:
hash_to_update.update({key: value})
elif optype == Define.append:
current_value = hash_to_update[key]
hash_to_update[key] = current_value + value
elif optype == Define.remove:
current_value = hash_to_update[key]
if (isinstance(current_value, int) or
@ -414,11 +449,18 @@ class NamespaceIniFiller:
if value_to_remove in current_value:
current_value.remove(value_to_remove)
hash_to_update[key] = ','.join(current_value)
self.current_namespace.source = hash_to_update
def set_error(self, line, lineno, col):
def _set_error(self, lineno, error_type, line):
'''Метод для добавления ошибки в лог.'''
self.error(lineno, "Syntax error: {}".format(line))
self._errors.append("{}:{}: {}".format(error_type, lineno, line))
@property
def errors(self):
errors = self._errors
self._errors = []
return errors
class VariableLoader:
@ -437,8 +479,10 @@ class VariableLoader:
def load_variables_package(self, package_name: str) -> None:
'''Метод для загрузки пакетов с переменными.'''
directory_path = os.path.join(self.variables_path, package_name)
package = '{}.{}'.format(self.variables_package, package_name)
package_namespace = NamespaceNode(package_name)
self.datavars.root.add_namespace(package_namespace)
self._fill_from_package(package_namespace, directory_path, package)
@ -471,7 +515,7 @@ class VariableLoader:
" variables.")
return
self._fill_from_ini(profile_path)
self._fill_from_profile_ini(profile_path)
def load_user_variables(self):
'''Метод для загрузки переменных из calculate.ini указанных в
@ -485,8 +529,15 @@ class VariableLoader:
return
for ini_file in env_order:
self.output.set_info("Loading variables from file: '{}'".format(
ini_file))
if ini_file in env_path:
self.fill_from_custom_ini(env_path[ini_file].value)
self.output.set_success("Variables from '{}' are loaded".
format(ini_file))
else:
self.output.set_warning("File '{}' is not found. Variables are"
" not loaded".format(ini_file))
def _fill_from_package(self, current_namespace: NamespaceNode,
directory_path: str, package: str) -> None:
@ -506,6 +557,7 @@ class VariableLoader:
Namespace.set_current_namespace(current_namespace)
# with self.test(file_name, current_namespace):
# importlib.import_module('{}.{}'.format(package, file_name))
spec = importlib.util.spec_from_file_location(
'{}.{}'.format(package, file_name),
file_node.path)
@ -520,7 +572,7 @@ class VariableLoader:
'{}.{}'.format(package,
directory_node.name))
def _fill_from_ini(self, profile_path):
def _fill_from_profile_ini(self, profile_path):
'''Метод для зaполнения переменных из ini-файла.'''
profile_walker = ProfileWalker(self.ini_basename,
self.repository_map)
@ -540,10 +592,23 @@ class VariableLoader:
def fill_from_custom_ini(self, file_path: str):
'''Метод для заполнения переменных из конкретного указанного файла.'''
self.output.set_info('Loading variables from file: {}'.format(
file_path))
if os.path.exists(file_path):
ini_file_text = read_file(file_path)
self.ini_filler.fill(self.datavars, ini_file_text)
parsing_errors = self.ini_filler.errors
if parsing_errors:
for error in parsing_errors:
self.output.set_error(error)
self.output.set_warning('Some variables was not loaded.')
else:
self.output.set_success('All variables are loaded.')
else:
self.output.set_error("Variables are not loaded. File '{}' does"
" not exist.".format(file_path))
@contextmanager
def test(self, file_name, namespace):
'''Контекстный менеджер для тестирования.'''
@ -723,3 +788,5 @@ class Datavars:
dict_to_save = self.variables_to_save[target]
target_path = target_paths[target].value
saver.save_to_ini(target_path, dict_to_save)
self.output.set_info("Variables for '{}' is saved in the"
" file: {}".format(target, target_path))

@ -480,7 +480,7 @@ class TestDirectoryProcessor:
def test_if_the_template_directory_have_no_the_action_parameter_value_and_append_parameter_is_not_skip__the_directory_processor_skips_this_template_branch_and_sets_warning(self):
datavars.main['cl_template_path'] = os.path.join(CHROOT_PATH,
'templates_11')
io_module = IOModule()
io_module = IOModule(save_messages=True)
warning_message = ("Action parameter is not set for template:"
" {0}").format(join_paths(
CHROOT_PATH,
@ -495,7 +495,7 @@ class TestDirectoryProcessor:
def test_if_the_template_has_two_root_directories_with_different_action_values_and_directory_processor_intialized_for_the_one_of_this_actions__the_directory_processor_skips_one_this_template_s_roots_and_processed_a_template_s_root_with_the_same_action_parameter_value(self):
datavars.main['cl_template_path'] = os.path.join(CHROOT_PATH,
'templates_12')
io_module = IOModule()
io_module = IOModule(save_messages=True)
warning_message = ("Action parameter value '{0}' does not match its"
" current value '{1}'. Template: {2}").format(
'update',
@ -546,7 +546,7 @@ class TestDirectoryProcessor:
).format(os.path.join(CHROOT_PATH,
'templates_15/root/dir_11'))
io_module = IOModule()
io_module = IOModule(save_messages=True)
directory_processor = DirectoryProcessor('install',
datavars_module=datavars,
output_module=io_module)

@ -15,8 +15,9 @@ class TestCalculateIni:
input_text = ("[section]\n"
"varval1 = value1\n")
parsed_lines = [{'start_section': (['section'],)},
{'define_key': ('varval1', 'value1', Define.assign)},
parsed_lines = [{'start_section': (['section'], 1)},
{'define_key': ('varval1', 'value1',
Define.assign, 2)},
]
parse_result = list(ini_parser.parse(input_text))
@ -32,10 +33,13 @@ class TestCalculateIni:
"varval2 += value2\n"
"varval3 -= value3\n")
parsed_lines = [{'start_section': (['section'],)},
{'define_key': ('varval1', 'value1', Define.assign)},
{'define_key': ('varval2', 'value2', Define.append)},
{'define_key': ('varval3', 'value3', Define.remove)},
parsed_lines = [{'start_section': (['section'], 1)},
{'define_key': ('varval1', 'value1',
Define.assign, 2)},
{'define_key': ('varval2', 'value2',
Define.append, 4)},
{'define_key': ('varval3', 'value3',
Define.remove, 5)},
]
parse_result = list(ini_parser.parse(input_text))
@ -54,13 +58,17 @@ class TestCalculateIni:
"[section2]\n"
"varval1 = value1\n")
parsed_lines = [{'start_section': (['section', 'sub'],)},
{'define_key': ('varval1', 'value1', Define.assign)},
{'define_key': ('varval2', 'value2', Define.assign)},
{'start_section': (['section', 'sub2'],)},
{'define_key': ('varval1', 'value1', Define.assign)},
{'start_section': (['section2'],)},
{'define_key': ('varval1', 'value1', Define.assign)}]
parsed_lines = [{'start_section': (['section', 'sub'], 1)},
{'define_key': ('varval1', 'value1',
Define.assign, 2)},
{'define_key': ('varval2', 'value2',
Define.assign, 3)},
{'start_section': (['section', 'sub2'], 4)},
{'define_key': ('varval1', 'value1',
Define.assign, 5)},
{'start_section': (['section2'], 7)},
{'define_key': ('varval1', 'value1',
Define.assign, 8)}]
parse_result = list(ini_parser.parse(input_text))
for parsed_line, result_line in zip(parsed_lines, parse_result):
@ -69,43 +77,51 @@ class TestCalculateIni:
def test_error(self):
ini_parser = CalculateIniParser()
input_text = ("[section\n"
"varval1 = value1\n"
"varval2 = value2\n"
"[section][sub2]\n"
"varval1 = value1\n"
"eee\n"
"\n"
"[section2\n"
"varval4 = value4\n"
"[section][]\n"
"[section][][sub]\n"
"[section][][sub][]\n")
parsed_lines = [{'start_section': (['section', 'sub2'],)},
{'define_key': ('varval1', 'value1', Define.assign)},
{'define_key': ('varval4', 'value4', Define.assign)},
{'clear_section': (['section'],)}
]
errors = [('[section', 1, 1),
('varval1 = value1', 2, 1),
('varval2 = value2', 3, 1),
('eee', 6, 1),
('[section2', 8, 1),
('[section][][sub]', 11, 1),
('[section][][sub][]', 12, 1),
input_text = '''[section
varval1 = value1
varval2 = value2
[section][sub2]
varval1 = value1
eee
[section2
varval4 = value4
[section][]
[section][][sub]
[section][][sub][]'''
lines = [{'start_section': (['section', 'sub2'], 4)},
{'define_key': ('varval1', 'value1', Define.assign, 5)},
{'define_key': ('varval4', 'value4', Define.assign, 9)},
{'clear_section': (['section'], 10)}
]
parsed_lines = []
errors = [(1, '[section'),
(2, 'varval1 = value1'),
(3, 'varval2 = value2'),
(6, 'eee'),
(8, '[section2'),
(11, '[section][][sub]'),
(12, '[section][][sub][]'),
]
parsed_errors = []
parse_result = list(ini_parser.parse(input_text))
for parsed_line in ini_parser.parse(input_text):
if next(iter(parsed_line)) == 'error':
parsed_errors.append(parsed_line['error'])
else:
parsed_lines.append(parsed_line)
assert len(parsed_errors) == len(errors)
for error, parsed_error in zip(errors, ini_parser.errors):
for error, parsed_error in zip(errors, parsed_errors):
assert parsed_error == error
for parsed_line, result_line in zip(parsed_lines, parse_result):
assert parsed_line == result_line
for line, parsed_line in zip(lines, parsed_lines):
assert parsed_line == line
def test_clear_section(self):
ini_parser = CalculateIniParser()
parse_result = next(ini_parser.parse("[section][test][]\n"))
assert parse_result == {'clear_section': (['section', 'test'],)}
assert parse_result == {'clear_section': (['section', 'test'], 1)}

@ -13,6 +13,7 @@ from calculate.templates.template_engine import TemplateEngine, FILE
from calculate.templates.template_processor import TemplateExecutor
from calculate.variables.loader import NamespaceIniFiller, Datavars
from calculate.utils.files import stderr_devnull, read_file
from calculate.utils.io_module import IOModule
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/variables/testfiles')
@ -790,7 +791,7 @@ var_2 = value_2
[custom][test][]
[os][]
"""
"""
namespace_filler.fill(datavars, second_ini_text)
assert datavars.os.var_1 == 'value_1'
@ -798,6 +799,40 @@ var_2 = value_2
assert 'var_1' not in datavars.custom.test.variables
assert 'var_2' not in datavars.custom.test.variables
def test_if_calculate_ini_file_contains_some_errors__the_NamespaceIniFiller_object_collect_all_error_messages_to_the_error_attribute(self):
Namespace.reset()
datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller()
input_text = '''[section
varval1 = value1
varval2 = value2
[section][sub2]
varval1 = value1
eee
[section2
varval4 = value4
[section][]
[section][][sub]
[section][][sub][]'''
errors = ['SyntaxError:1: [section',
'SyntaxError:2: varval1 = value1',
'SyntaxError:3: varval2 = value2',
("VariableError:4: can not create namespace '<root>.section'"
" in not 'custom' namespace."),
'SyntaxError:6: eee',
'SyntaxError:8: [section2',
("VariableError:10: can not clear namespace 'section'."
" Namespace is not found."),
'SyntaxError:11: [section][][sub]',
'SyntaxError:12: [section][][sub][]']
namespace_filler.fill(datavars, input_text)
for parsed_error, error in zip(namespace_filler.errors, errors):
assert parsed_error == error
def test_if_calculate_ini_file_is_used_for_changing_of_an_unfixed_hash_variable_created_using_the_variables_api_and_for_adding_of_the_new_key_in_it__the_NamespaceIniFiller_object_changes_hash_value_and_adds_a_new_key(self):
Namespace.reset()
datavars = Namespace.datavars
@ -850,8 +885,9 @@ key_2 = weird_value
key_3 = weird_value
"""
with pytest.raises(VariableError):
namespace_filler.fill(datavars, first_ini_text)
namespace_filler.fill(datavars, first_ini_text)
assert datavars.os.var_1.get_hash() == {'key_1': 'value_1',
'key_2': 'value_2'}
def test_if_calculate_ini_file_is_used_for_creation_of_the_table_variable_in_the_custom_namespace__the_NamespaceIniFiller_object_creates_table_in_the_custom_namespace(self):
Namespace.reset()
@ -875,7 +911,6 @@ value = value_1
name = name_2
value = value_2
"""
namespace_filler.fill(datavars, first_ini_text)
assert datavars.custom.test ==\
[{'name': 'name_0', 'value': 'value_0'},
@ -1359,6 +1394,34 @@ id_2 = 1349'''
read_file(datavars.system.env_path['system'].value)
# Теперь тестируем обработку ошибок.
def test_process_ini_errors(self):
io_module = IOModule(save_messages=True)
datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
'variables_12'),
io_module=io_module)
messages = [('info', ("Loading variables from file: /home/divanov/Home"
"/development/calculate-lib/tests/variables/testfiles/"
"ini_vars/calculate_8.ini")),
('error', 'SyntaxError:1: [section'),
('error', 'SyntaxError:2: varval1 = value1'),
('error', 'SyntaxError:3: varval2 = value2'),
('error', ("VariableError:4: variables package 'section'"
" is not found.")),
('error', 'SyntaxError:6: eee'),
('error', 'SyntaxError:8: [section2'),
('error', ("VariableError:10: can not clear namespace"
" 'section'. Variables package 'section' is not"
" found.")),
('error', 'SyntaxError:11: [section][][sub]'),
('error', 'SyntaxError:12: [section][][sub][]'),
('warning', 'Some variables was not loaded.')]
for message, datavars_message in zip(
messages,
io_module.messages[-len(messages):]):
assert message == datavars_message
assert False
def test_for_removing_testfiles(self):
shutil.rmtree(os.path.join(TESTFILES_PATH, 'gentoo'))

@ -0,0 +1,12 @@
[section
varval1 = value1
varval2 = value2
[section][sub2]
varval1 = value1
eee
[section2
varval4 = value4
[section][]
[section][][sub]
[section][][sub][]

@ -26,6 +26,9 @@ with Namespace('linux'):
source=Dependence('.subname', '.fullname', '.ver',
depend=get_title))
Variable('hashvar', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_0', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)

@ -0,0 +1,4 @@
from calculate.variables.datavars import Variable, StringType
Variable('chroot', type=StringType.readonly, source='/')

@ -0,0 +1,50 @@
from calculate.variables.datavars import Namespace, Variable, Dependence,\
StringType, HashType, TableType,\
ListType, IntegerType, FloatType
with Namespace('linux'):
Variable('shortname', source='', type=StringType)
Variable('ver', source='', type=StringType)
Variable('fullname', source='', type=StringType)
Variable('subname', source='', type=StringType)
Variable('arch', source='', type=StringType)
Variable('test_1', source=12, type=IntegerType)
Variable('test_2', source=1.2, type=FloatType)
def get_title(subname, fullname, ver):
if subname.value:
return '{} {} {}'.format(fullname.value, subname.value, ver.value)
else:
return '{} {}'.format(fullname.value, ver.value)
Variable('title', type=StringType,
source=Dependence('.subname', '.fullname', '.ver',
depend=get_title))
Variable('hashvar', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_0', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_1', source={'key1': 'value1',
'key2': 'value2'}, type=HashType)
Variable('hashvar_2', source={'id_1': 1349,
'id_2': 1575}, type=HashType)
Variable('calculate', type=StringType,
source=Dependence('.hashvar_0',
depend=lambda hashvar: "{} {}".format(
hashvar.value['value1'],
hashvar.value['value2'])))
Variable('tablevar', type=TableType, source=[{"dev": "/dev/sdb1",
"mount": "/"},
{"dev": "/dev/sdb2",
"mount": "/var/calculate"}])

@ -0,0 +1,57 @@
import os
from calculate.variables.datavars import Variable, Namespace, Dependence,\
StringType, TableType
'''
gentoo:
make_profile -> string
profile:
path -> string
name -> string
repositories[*]{name, path} -> table
config -> undefined
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/variables/testfiles')
# Путь до файла, указывающего на активный профиль
Variable('make_profile', type=StringType, source='/etc/portage/make.profile')
# Параметры текущего профиля.
with Namespace('profile'):
# Абсолютный путь до профиля
Variable('path', type=StringType,
source=os.path.join(TESTFILES_PATH,
"gentoo/repos/distros/profiles/CLD/amd64"))
def get_profile_name(path, repositories):
profile_path = path.value
if not profile_path:
return ""
for repository in repositories.value:
repository_path = repository['path']
repository_name = repository['name']
remove_part = os.path.normpath(os.path.join(repository_path,
"profiles"))
if profile_path.startswith(remove_part):
return "{}:{}".format(repository_name,
profile_path[len(remove_part) + 1:])
return profile_path
# Название профиля
Variable('name', type=StringType,
source=Dependence('.path', '..repositories',
depend=get_profile_name))
# Информация о репозиториях
# name: имя репозитория
# path: полный путь до репозитория
Variable('repositories', type=TableType,
source=[{'name': 'distros',
'path': os.path.join(TESTFILES_PATH,
"gentoo/repos/distros")},
{'name': 'calculate',
'path': os.path.join(TESTFILES_PATH,
"gentoo/repos/calculate")},
{'name': 'gentoo',
'path': os.path.join(TESTFILES_PATH,
"gentoo/portage")}])

@ -0,0 +1,19 @@
import os
from calculate.variables.datavars import Variable, ListType, HashType
'''
system:
env_order -> list
env_path -> hash
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/variables/testfiles')
# Список мест, где есть calculate.ini файлы.
Variable('env_order', type=ListType, source=['system', 'local'])
# Отображение множества мест, где есть calculate.ini файлы, на пути к ним.
Variable('env_path', type=HashType,
source={'system': os.path.join(TESTFILES_PATH,
'ini_vars/calculate_7.ini')})

@ -0,0 +1,4 @@
from calculate.variables.datavars import Variable, StringType
Variable('chroot', type=StringType.readonly, source='/')

@ -0,0 +1,50 @@
from calculate.variables.datavars import Namespace, Variable, Dependence,\
StringType, HashType, TableType,\
ListType, IntegerType, FloatType
with Namespace('linux'):
Variable('shortname', source='', type=StringType)
Variable('ver', source='', type=StringType)
Variable('fullname', source='', type=StringType)
Variable('subname', source='', type=StringType)
Variable('arch', source='', type=StringType)
Variable('test_1', source=12, type=IntegerType)
Variable('test_2', source=1.2, type=FloatType)
def get_title(subname, fullname, ver):
if subname.value:
return '{} {} {}'.format(fullname.value, subname.value, ver.value)
else:
return '{} {}'.format(fullname.value, ver.value)
Variable('title', type=StringType,
source=Dependence('.subname', '.fullname', '.ver',
depend=get_title))
Variable('hashvar', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_0', source={'value1': 'test1',
'value2': 'test2'}, type=HashType)
Variable('hashvar_1', source={'key1': 'value1',
'key2': 'value2'}, type=HashType)
Variable('hashvar_2', source={'id_1': 1349,
'id_2': 1575}, type=HashType)
Variable('calculate', type=StringType,
source=Dependence('.hashvar_0',
depend=lambda hashvar: "{} {}".format(
hashvar.value['value1'],
hashvar.value['value2'])))
Variable('tablevar', type=TableType, source=[{"dev": "/dev/sdb1",
"mount": "/"},
{"dev": "/dev/sdb2",
"mount": "/var/calculate"}])

@ -0,0 +1,57 @@
import os
from calculate.variables.datavars import Variable, Namespace, Dependence,\
StringType, TableType
'''
gentoo:
make_profile -> string
profile:
path -> string
name -> string
repositories[*]{name, path} -> table
config -> undefined
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/variables/testfiles')
# Путь до файла, указывающего на активный профиль
Variable('make_profile', type=StringType, source='/etc/portage/make.profile')
# Параметры текущего профиля.
with Namespace('profile'):
# Абсолютный путь до профиля
Variable('path', type=StringType,
source=os.path.join(TESTFILES_PATH,
"gentoo/repos/distros/profiles/CLD/amd64"))
def get_profile_name(path, repositories):
profile_path = path.value
if not profile_path:
return ""
for repository in repositories.value:
repository_path = repository['path']
repository_name = repository['name']
remove_part = os.path.normpath(os.path.join(repository_path,
"profiles"))
if profile_path.startswith(remove_part):
return "{}:{}".format(repository_name,
profile_path[len(remove_part) + 1:])
return profile_path
# Название профиля
Variable('name', type=StringType,
source=Dependence('.path', '..repositories',
depend=get_profile_name))
# Информация о репозиториях
# name: имя репозитория
# path: полный путь до репозитория
Variable('repositories', type=TableType,
source=[{'name': 'distros',
'path': os.path.join(TESTFILES_PATH,
"gentoo/repos/distros")},
{'name': 'calculate',
'path': os.path.join(TESTFILES_PATH,
"gentoo/repos/calculate")},
{'name': 'gentoo',
'path': os.path.join(TESTFILES_PATH,
"gentoo/portage")}])

@ -0,0 +1,19 @@
import os
from calculate.variables.datavars import Variable, ListType, HashType
'''
system:
env_order -> list
env_path -> hash
'''
TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/variables/testfiles')
# Список мест, где есть calculate.ini файлы.
Variable('env_order', type=ListType, source=['system', 'local'])
# Отображение множества мест, где есть calculate.ini файлы, на пути к ним.
Variable('env_path', type=HashType,
source={'system': os.path.join(TESTFILES_PATH,
'ini_vars/calculate_8.ini')})

@ -13,6 +13,8 @@ with Namespace('linux'):
Variable('arch', source='', type=StringType)
Variable('test', source='', type=StringType)
Variable('test_1', source=12, type=IntegerType)
Variable('test_2', source=1.2, type=FloatType)

Loading…
Cancel
Save