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
(file_name not in self.contents_dictionary
or self.contents_dictionary[file_name]['type'] != 'dir')):
self.add_dir(os.path.dirname(file_name))
contents_item = OrderedDict({'type': 'dir'})
self.contents_dictionary[file_name] = contents_item
self.add_dir(os.path.dirname(file_name))
contents_item = OrderedDict({'type': 'dir'})
self.contents_dictionary[file_name] = contents_item
def add_sym(self, file_name, target_path=None):
'''Метод для добавления в CONTENTS символьных ссылок.'''

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

@ -7,14 +7,14 @@ import site
from calculate.vars.datavars import Variable, Namespace, HashVariable,\
TableVariable, IniCreated, DefaultValue
from calculate.vars.alt_datavars import NamespaceNode, VariableNode,\
StringType, ListType, IntegerType,\
FloatType, IniType
ListType, IntegerType,\
FloatType, IniType, TableType
from calculate.utils.gentoo import ProfileWalker
from calculate.utils.fs import readFile
from calculate.utils.files import list_directory
from pyparsing import Literal, Word, ZeroOrMore, Group, Optional, restOfLine,\
empty, printables, OneOrMore, lineno, line, col, SkipTo,\
LineEnd, Combine
LineEnd, Combine, nums
from enum import Enum
@ -46,17 +46,27 @@ class CalculateIniParser:
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='=-+')
# non_comma = Word(printables+'\t', excludeChars=',')
clear_section = lbrack.suppress() + Group(empty) + rbrack.suppress()
row_index = lbrack.suppress() + Word(nums) + rbrack.suppress()
section_start = Group(OneOrMore(lbrack.suppress() + section_name
+ rbrack.suppress())
+ (clear_section | ~lbrack())
+ LineEnd().suppress())
namespace_start = Group(OneOrMore(section_name)
+ (clear_section | ~lbrack)
+ LineEnd().suppress())
table_start = Group(OneOrMore(section_name)
+ (row_index | clear_section | ~lbrack)
+ LineEnd().suppress())
section_start = (namespace_start('namespace') |
table_start('table'))
# Если содержимое ini-файла не предваряется заголовком секции,
# значит эта строка ошибочна.
@ -80,6 +90,38 @@ class CalculateIniParser:
| 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":
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):
'''Метод вызываемый парсером, если обнаружена некорректная строка,
предназначен для получения некорректной строки и ее дальнейшего
@ -95,36 +137,21 @@ class CalculateIniParser:
self._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:
'''Класс, предназначенный для наполнения Namespace объекта переменными
из calculate.ini файла.'''
def __init__(self):
available_sections = {'custom'}
def __init__(self, restrict_creation=True):
self.ini_parser = CalculateIniParser()
self.restricted = restrict_creation
self.modify_only = False
def error(self, 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 файла и добавления всех его
переменных в указанное пространство имен.'''
self.namespace = namespace
@ -136,68 +163,152 @@ class NamespaceIniFiller:
def _line_processor(self, start_section=None,
clear_section=None,
start_table=None,
clear_table=None,
define_key=None,
error=None, **kwargs):
if start_section is not None:
self.start_section(*start_section)
elif clear_section is not None:
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)
for error in self.ini_parser.errors:
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
for section in sections:
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]
def clear_section(self, sections):
def clear_section(self, sections: list) -> None:
'''Метод для очистки пространства имен.'''
current_namespace = self.namespace
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
current_namespace = current_namespace.namespaces[section]
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.source = value
def define_variable(self, key, value):
def define_variable(self, key: str, value: str) -> None:
'''Метод для создания переменных в calculate.ini файле.'''
print("define variable: '{}' with value: '{}'".format(key, value))
VariableNode(key, self.current_namespace, variable_type=IniType,
source=value)
if not self.modify_only:
VariableNode(key, self.current_namespace, variable_type=IniType,
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_value = variable.get_value()
if variable.variable_type is IniType:
value_list = value.split(',')
variable_list = variable_value.split(',')
for item in value_list:
if item not in variable_list:
variable_list.append(item.strip())
variable_value = ','.join(variable_list)
elif variable.variable_type is ListType:
value_list = value.split(',')
for item in value_list:
if item not in variable_value:
variable_value.append(item.strip())
elif variable.variable_type is IntegerType:
variable_value += int(value)
elif variable.variable_type is FloatType:
variable_value += float(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_value = variable.get_value()
@ -225,24 +336,8 @@ class NamespaceIniFiller:
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):
'''Метод для добавления ошибки в лог.'''
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):
Namespace.reset()
datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller(restrict_creation=False)
calculate_ini_text = """
[os]
test = 123
@ -486,7 +488,6 @@ test = 123
shortname = CLD
fullname = Calculate Linux Desktop
"""
namespace_filler = NamespaceIniFiller()
namespace_filler.fill(datavars, calculate_ini_text)
assert datavars.os.test == '123'
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):
Namespace.reset()
datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller()
namespace_filler = NamespaceIniFiller(restrict_creation=False)
first_ini_text = """
[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):
Namespace.reset()
datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller()
namespace_filler = NamespaceIniFiller(restrict_creation=False)
first_ini_text = """
[os]
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):
Namespace.reset()
datavars = Namespace.datavars
namespace_filler = NamespaceIniFiller()
namespace_filler = NamespaceIniFiller(restrict_creation=False)
first_ini_text = """
[os]
test = 123,345,asdf,qwer,zxcv
@ -565,3 +566,71 @@ test -= asdf,zxcv
namespace_filler.fill(datavars, third_ini_text)
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