Support of a tables creation and modification is added in the NamespaceIniFiller. Creation the variables and namespaces using calculate.ini is available only in the custom section now.

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

@ -647,9 +647,9 @@ class Package:
if (file_name != '/' and if (file_name != '/' and
(file_name not in self.contents_dictionary (file_name not in self.contents_dictionary
or self.contents_dictionary[file_name]['type'] != 'dir')): or self.contents_dictionary[file_name]['type'] != 'dir')):
self.add_dir(os.path.dirname(file_name)) self.add_dir(os.path.dirname(file_name))
contents_item = OrderedDict({'type': 'dir'}) contents_item = OrderedDict({'type': 'dir'})
self.contents_dictionary[file_name] = contents_item self.contents_dictionary[file_name] = contents_item
def add_sym(self, file_name, target_path=None): def add_sym(self, file_name, target_path=None):
'''Метод для добавления в CONTENTS символьных ссылок.''' '''Метод для добавления в CONTENTS символьных ссылок.'''

@ -255,13 +255,20 @@ class Table:
" with type '{}'".format(row, " with type '{}'".format(row,
type(row))) type(row)))
def get_table(self): def get_table(self) -> List[dict]:
return self._rows return self._rows
def add_row(self, row: dict): def add_row(self, row: dict):
self._check_columns(row) self._check_columns(row)
self._rows.append(row) self._rows.append(row)
def change_row(self, row: dict, index: int) -> None:
self._check_columns(row)
self._rows[index] = row
def clear(self) -> None:
self._rows.clear()
def _check_columns(self, row: dict) -> None: def _check_columns(self, row: dict) -> None:
'''Метод для проверки наличия в хэше только тех полей, которые '''Метод для проверки наличия в хэше только тех полей, которые
соответствуют заданным для таблицы колонкам.''' соответствуют заданным для таблицы колонкам.'''
@ -291,6 +298,9 @@ class Table:
return key in self._values return key in self._values
return False return False
def __len__(self):
return len(self._rows)
class TableType(VariableType): class TableType(VariableType):
name = 'table' name = 'table'

@ -7,14 +7,14 @@ import site
from calculate.vars.datavars import Variable, Namespace, HashVariable,\ from calculate.vars.datavars import Variable, Namespace, HashVariable,\
TableVariable, IniCreated, DefaultValue TableVariable, IniCreated, DefaultValue
from calculate.vars.alt_datavars import NamespaceNode, VariableNode,\ from calculate.vars.alt_datavars import NamespaceNode, VariableNode,\
StringType, ListType, IntegerType,\ ListType, IntegerType,\
FloatType, IniType FloatType, IniType, TableType
from calculate.utils.gentoo import ProfileWalker from calculate.utils.gentoo import ProfileWalker
from calculate.utils.fs import readFile from calculate.utils.fs import readFile
from calculate.utils.files import list_directory from calculate.utils.files import list_directory
from pyparsing import Literal, Word, ZeroOrMore, Group, Optional, restOfLine,\ from pyparsing import Literal, Word, ZeroOrMore, Group, Optional, restOfLine,\
empty, printables, OneOrMore, lineno, line, col, SkipTo,\ empty, printables, OneOrMore, lineno, line, col, SkipTo,\
LineEnd, Combine LineEnd, Combine, nums
from enum import Enum from enum import Enum
@ -46,17 +46,27 @@ class CalculateIniParser:
comment = comment_symbol + Optional(restOfLine) comment = comment_symbol + Optional(restOfLine)
section_name = Word(printables+'\t', excludeChars='[]') section_name = (lbrack.suppress() + (~Word(nums)
+ Word(printables+'\t',
excludeChars='[]'))
+ rbrack.suppress())
value_name = Word(printables+'\t', excludeChars='=-+') value_name = Word(printables+'\t', excludeChars='=-+')
# non_comma = Word(printables+'\t', excludeChars=',') # non_comma = Word(printables+'\t', excludeChars=',')
clear_section = lbrack.suppress() + Group(empty) + rbrack.suppress() clear_section = lbrack.suppress() + Group(empty) + rbrack.suppress()
row_index = lbrack.suppress() + Word(nums) + rbrack.suppress()
section_start = Group(OneOrMore(lbrack.suppress() + section_name namespace_start = Group(OneOrMore(section_name)
+ rbrack.suppress()) + (clear_section | ~lbrack)
+ (clear_section | ~lbrack()) + LineEnd().suppress())
+ LineEnd().suppress())
table_start = Group(OneOrMore(section_name)
+ (row_index | clear_section | ~lbrack)
+ LineEnd().suppress())
section_start = (namespace_start('namespace') |
table_start('table'))
# Если содержимое ini-файла не предваряется заголовком секции, # Если содержимое ini-файла не предваряется заголовком секции,
# значит эта строка ошибочна. # значит эта строка ошибочна.
@ -80,6 +90,38 @@ class CalculateIniParser:
| unexpected) | unexpected)
self.ini_section_parser.ignore(comment) 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":
continue
section, defkeys = tokens
print('section type: {}'.format(section.getName()))
if section.getName() == 'namespace':
section_list = section.asList()
if section_list[-1] == []:
yield {'clear_section': (section_list[:-1], )}
else:
yield {'start_section': (section_list, )}
for defkey in defkeys:
if defkey.getName() == "error":
continue
yield {'define_key': (defkey[0], defkey[2],
self.operations[defkey[1]])}
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)}
def _unexpected_token(self, string, location, tokens): def _unexpected_token(self, string, location, tokens):
'''Метод вызываемый парсером, если обнаружена некорректная строка, '''Метод вызываемый парсером, если обнаружена некорректная строка,
предназначен для получения некорректной строки и ее дальнейшего предназначен для получения некорректной строки и ее дальнейшего
@ -95,36 +137,21 @@ class CalculateIniParser:
self._errors = [] self._errors = []
return errors return errors
def parse(self, data):
for tokens, start, end in self.ini_section_parser.scanString(data):
if tokens.getName() == "error":
continue
section, defkeys = tokens
section_list = section.asList()
if section_list[-1] == []:
yield {'clear_section': (section_list[:-1], )}
else:
yield {'start_section': (section.asList(), )}
for defkey in defkeys:
if defkey.getName() == "error":
continue
yield {'define_key': (section.asList(), defkey[0], defkey[2],
self.operations[defkey[1]])}
class NamespaceIniFiller: class NamespaceIniFiller:
'''Класс, предназначенный для наполнения Namespace объекта переменными '''Класс, предназначенный для наполнения Namespace объекта переменными
из calculate.ini файла.''' из calculate.ini файла.'''
def __init__(self): available_sections = {'custom'}
def __init__(self, restrict_creation=True):
self.ini_parser = CalculateIniParser() self.ini_parser = CalculateIniParser()
self.restricted = restrict_creation
self.modify_only = False
def error(self, lineno, error_message): def error(self, lineno, error_message):
self.errors.append(lineno, error_message) self.errors.append(lineno, error_message)
def fill(self, namespace, ini_file_text): def fill(self, namespace: NamespaceNode, ini_file_text: str) -> None:
'''Метод для разбора calculate.ini файла и добавления всех его '''Метод для разбора calculate.ini файла и добавления всех его
переменных в указанное пространство имен.''' переменных в указанное пространство имен.'''
self.namespace = namespace self.namespace = namespace
@ -136,68 +163,152 @@ class NamespaceIniFiller:
def _line_processor(self, start_section=None, def _line_processor(self, start_section=None,
clear_section=None, clear_section=None,
start_table=None,
clear_table=None,
define_key=None, define_key=None,
error=None, **kwargs): error=None, **kwargs):
if start_section is not None: if start_section is not None:
self.start_section(*start_section) self.start_section(*start_section)
elif clear_section is not None: elif clear_section is not None:
self.clear_section(*clear_section) 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: elif define_key is not None:
self.define_key(*define_key) self.define_key(*define_key)
for error in self.ini_parser.errors: for error in self.ini_parser.errors:
self.set_error(*error) self.set_error(*error)
def start_section(self, sections): def start_section(self, sections: str) -> None:
'''Метод для получения доступа и создания пространств имен.''' '''Метод для получения доступа и создания пространств имен.'''
if self.restricted:
self.modify_only = sections[0] not in self.available_sections
self.current_namespace = self.namespace self.current_namespace = self.namespace
for section in sections: for section in sections:
if section not in self.current_namespace.namespaces: if section not in self.current_namespace.namespaces:
self.current_namespace.add_namespace(NamespaceNode(section)) if not self.modify_only:
self.current_namespace.add_namespace(
NamespaceNode(section))
else:
self.current_namespace = None
return
self.current_namespace = self.current_namespace.namespaces[section] self.current_namespace = self.current_namespace.namespaces[section]
def clear_section(self, sections): def clear_section(self, sections: list) -> None:
'''Метод для очистки пространства имен.''' '''Метод для очистки пространства имен.'''
current_namespace = self.namespace current_namespace = self.namespace
for section in sections: for section in sections:
if section not in current_namespace.namespaces: if section in current_namespace.namespaces:
current_namespace = current_namespace.namespaces[section]
elif (section in current_namespace.variables and
current_namespace.variables[section].variable_type
is TableType):
table_variable = current_namespace.variables[section]
table_to_clear = table_variable.get_value()
table_to_clear.clear()
table_variable.source = table_to_clear
else:
return return
current_namespace = current_namespace.namespaces[section]
current_namespace.clear() current_namespace.clear()
def change_value(self, key, value): def start_table(self, sections: str, row) -> None:
'''Метод для создания и модификации таблиц.'''
if self.restricted:
self.modify_only = sections[0] not in self.available_sections
self.current_namespace = self.namespace
row_number = int(sections.pop())
table_name = sections.pop()
for section in sections:
if section not in self.current_namespace.namespaces:
if not self.modify_only:
self.current_namespace.add_namespace(
NamespaceNode(section))
else:
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:
table_variable = self.current_namespace.variables[table_name]
table = table_variable.get_value()
if len(table) < row_number:
table.change_row(row, row_number)
else:
table.add_row(row)
table_variable.source = table
def define_key(self, key: str, value: str, optype) -> None:
if self.current_namespace is None:
return
if optype == Define.assign:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.change_value(key, value)
elif optype == Define.append:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.append_value(key, value)
elif optype == Define.remove:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.remove_value(key, value)
def change_value(self, key: str, value: str) -> None:
'''Метод для изменения значения переменной.'''
variable = self.current_namespace[key] variable = self.current_namespace[key]
variable.source = value variable.source = value
def define_variable(self, key, value): def define_variable(self, key: str, value: str) -> None:
'''Метод для создания переменных в calculate.ini файле.''' '''Метод для создания переменных в calculate.ini файле.'''
print("define variable: '{}' with value: '{}'".format(key, value)) if not self.modify_only:
VariableNode(key, self.current_namespace, variable_type=IniType, VariableNode(key, self.current_namespace, variable_type=IniType,
source=value) source=value)
else:
# TODO Какая-то обработка ошибки.
pass
def append_value(self, key, value): def append_value(self, key: str, value: str) -> None:
'''Метод выполняющий действия возложенные на оператор +=.''' '''Метод выполняющий действия возложенные на оператор +=.'''
variable = self.current_namespace[key] variable = self.current_namespace[key]
variable_value = variable.get_value() variable_value = variable.get_value()
if variable.variable_type is IniType: if variable.variable_type is IniType:
value_list = value.split(',') value_list = value.split(',')
variable_list = variable_value.split(',') variable_list = variable_value.split(',')
for item in value_list: for item in value_list:
if item not in variable_list: if item not in variable_list:
variable_list.append(item.strip()) variable_list.append(item.strip())
variable_value = ','.join(variable_list) variable_value = ','.join(variable_list)
elif variable.variable_type is ListType: elif variable.variable_type is ListType:
value_list = value.split(',') value_list = value.split(',')
for item in value_list: for item in value_list:
if item not in variable_value: if item not in variable_value:
variable_value.append(item.strip()) variable_value.append(item.strip())
elif variable.variable_type is IntegerType: elif variable.variable_type is IntegerType:
variable_value += int(value) variable_value += int(value)
elif variable.variable_type is FloatType: elif variable.variable_type is FloatType:
variable_value += float(value) variable_value += float(value)
variable.source = variable_value variable.source = variable_value
def remove_value(self, key, value): def remove_value(self, key: str, value: str) -> None:
'''Метод выполняющий действия возложенные на оператор -=.''' '''Метод выполняющий действия возложенные на оператор -=.'''
variable = self.current_namespace[key] variable = self.current_namespace[key]
variable_value = variable.get_value() variable_value = variable.get_value()
@ -225,24 +336,8 @@ class NamespaceIniFiller:
variable.source = variable_value variable.source = variable_value
def define_key(self, section, key, value, optype):
if optype == Define.assign:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.change_value(key, value)
elif optype == Define.append:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.append_value(key, value)
elif optype == Define.remove:
if key not in self.current_namespace:
self.define_variable(key, value)
else:
self.remove_value(key, value)
def set_error(self, line, lineno, col): def set_error(self, line, lineno, col):
'''Метод для добавления ошибки в лог.'''
self.error(lineno, "Syntax error: {}".format(line)) self.error(lineno, "Syntax error: {}".format(line))

@ -478,6 +478,8 @@ class TestNamespace:
def test_if_calculate_ini_file_is_used_just_to_create_some_variables__the_namespace_filler_will_create_them(self): def test_if_calculate_ini_file_is_used_just_to_create_some_variables__the_namespace_filler_will_create_them(self):
Namespace.reset() Namespace.reset()
datavars = Namespace.datavars datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller(restrict_creation=False)
calculate_ini_text = """ calculate_ini_text = """
[os] [os]
test = 123 test = 123
@ -486,7 +488,6 @@ test = 123
shortname = CLD shortname = CLD
fullname = Calculate Linux Desktop fullname = Calculate Linux Desktop
""" """
namespace_filler = NamespaceIniFiller()
namespace_filler.fill(datavars, calculate_ini_text) namespace_filler.fill(datavars, calculate_ini_text)
assert datavars.os.test == '123' assert datavars.os.test == '123'
assert datavars.os.linux.shortname == 'CLD' assert datavars.os.linux.shortname == 'CLD'
@ -495,7 +496,7 @@ fullname = Calculate Linux Desktop
def test_if_a_calculate_ini_file_is_used_to_create_some_variables_and_an_other_one_is_used_to_update_value_of_the_variable__the_namespace_filler_creates_them_using_a_first_calculate_ini_and_then_changes_value_using_a_second_calculate_ini(self): def test_if_a_calculate_ini_file_is_used_to_create_some_variables_and_an_other_one_is_used_to_update_value_of_the_variable__the_namespace_filler_creates_them_using_a_first_calculate_ini_and_then_changes_value_using_a_second_calculate_ini(self):
Namespace.reset() Namespace.reset()
datavars = Namespace.datavars datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller() namespace_filler = NamespaceIniFiller(restrict_creation=False)
first_ini_text = """ first_ini_text = """
[os] [os]
@ -520,7 +521,7 @@ shortname = CLDX
def test_if_calculate_ini_file_creates_a_variable_and_then_uses_append_operation_to_append_any_value_to_the_created_variable__the_namespace_filler_appends_the_value_to_the_created_variable(self): def test_if_calculate_ini_file_creates_a_variable_and_then_uses_append_operation_to_append_any_value_to_the_created_variable__the_namespace_filler_appends_the_value_to_the_created_variable(self):
Namespace.reset() Namespace.reset()
datavars = Namespace.datavars datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller() namespace_filler = NamespaceIniFiller(restrict_creation=False)
first_ini_text = """ first_ini_text = """
[os] [os]
test = 123 test = 123
@ -542,7 +543,7 @@ test += asdf,qwer,zxcv
def test_if_calculate_ini_file_creates_a_variable_and_then_uses_remove_operation_to_remove_any_values_in_the_created_variable__the_namespace_filler_removes_the_values_from_the_created_variable(self): def test_if_calculate_ini_file_creates_a_variable_and_then_uses_remove_operation_to_remove_any_values_in_the_created_variable__the_namespace_filler_removes_the_values_from_the_created_variable(self):
Namespace.reset() Namespace.reset()
datavars = Namespace.datavars datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller() namespace_filler = NamespaceIniFiller(restrict_creation=False)
first_ini_text = """ first_ini_text = """
[os] [os]
test = 123,345,asdf,qwer,zxcv test = 123,345,asdf,qwer,zxcv
@ -565,3 +566,71 @@ test -= asdf,zxcv
namespace_filler.fill(datavars, third_ini_text) namespace_filler.fill(datavars, third_ini_text)
assert datavars.os.test == "123,qwer" assert datavars.os.test == "123,qwer"
def test_custom_and_not_custom_creation(self):
Namespace.reset()
datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller()
with Namespace('os'):
Variable('var_1', type=StringType, source='value_1')
Variable('var_2', type=StringType, source='value_2')
first_ini_text = """
[os]
var_1 = other_value
var_3 = value_3
[custom][test_1]
var_1 = value_1
[custom][test_2]
var_1 = value_1
"""
namespace_filler.fill(datavars, first_ini_text)
assert datavars.os.var_1 == "other_value"
assert 'var_3' not in datavars.os
assert datavars.custom.test_1.var_1 == "value_1"
assert datavars.custom.test_2.var_1 == "value_1"
def test_table_from_ini(self):
Namespace.reset()
datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller()
with Namespace('os'):
Variable('var_1', type=StringType, source='value_1')
Variable('var_2', type=StringType, source='value_2')
first_ini_text = """
[custom][test][0]
name = name_0
value = value_0
[custom][test][1]
name = name_1
value = value_1
[custom][test][2]
name = name_2
value = value_2
"""
namespace_filler.fill(datavars, first_ini_text)
assert datavars.custom.test.get_table() ==\
[{'name': 'name_0', 'value': 'value_0'},
{'name': 'name_1', 'value': 'value_1'},
{'name': 'name_2', 'value': 'value_2'}]
second_ini_text = """
[custom][test][]
[custom][test][0]
name = name_0
value = value_0
"""
namespace_filler.fill(datavars, second_ini_text)
assert datavars.custom.test.get_table() ==\
[{'name': 'name_0', 'value': 'value_0'}]

Loading…
Cancel
Save