New version of the calculate ini parser is tested. Development of the new version of the datavars module is started.

packages
Иванов Денис 4 years ago
parent f8f1bad832
commit 6fdcf0744b

@ -0,0 +1,48 @@
import re
class VariableType:
'''Базовый класс для типов.'''
def check(self, value: str) -> bool:
pass
class StringType(VariableType):
pass
class IntegerType(VariableType):
integer_pattern = re.compile(r"^-?\d+$")
def check(self, value) -> bool:
return self.integer_pattern.match(value)
class FloatType(VariableType):
float_pattern = re.compile(r"^-?\d+(\.\d+)?$")
def check(self, value) -> bool:
return self.integer_pattern.match(value)
class BooleanType(VariableType):
pass
class ListType(VariableType):
pass
class ReadonlyType(VariableType):
pass
class Variable:
def __init__(self, variable_type: VariableType):
self.var_type = variable_type
class Namespace:
def __init__(self, name=''):
self._name = name
self.variables = dict()

@ -1,4 +1,3 @@
from calculate.utils.calculateini import CalculateIniParser
import re
import sys
import os
@ -10,6 +9,108 @@ from calculate.vars.datavars import Variable, Namespace, HashVariable,\
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, Dict, Optional,\
restOfLine, empty, printables, OneOrMore, oneOf, nums,\
lineno, line, col, Keyword, SkipTo, LineEnd, Combine
from enum import Enum
class Define(Enum):
assign = 0
append = 1
remove = 2
class CalculateIniParser:
'''Класс парсера calculate.ini файлов.'''
def __init__(self):
self._errors = []
self.operations = {"=": Define.assign,
"+=": Define.append,
"-=": Define.remove}
lbrack = Literal("[")
rbrack = Literal("]")
# comma = Literal(",").suppress()
comment_symbol = Literal(';') | Literal('#')
# Define = self.Define
value_operation = (Literal("=") | Combine(Literal("+") + Literal("="))
| Combine(Literal("-") + Literal("=")))
comment = comment_symbol + Optional(restOfLine)
section_name = Word(printables+'\t', excludeChars='[]')
value_name = Word(printables+'\t', excludeChars='=-+')
# non_comma = Word(printables+'\t', excludeChars=',')
clear_section = lbrack.suppress() + Group(empty) + rbrack.suppress()
section_start = Group(OneOrMore(lbrack.suppress() + section_name
+ rbrack.suppress())
+ (clear_section | ~lbrack())
+ LineEnd().suppress())
# Если содержимое ini-файла не предваряется заголовком секции,
# значит эта строка ошибочна.
unexpected = Group(~section_start + SkipTo(LineEnd(),
include=True))("error")
unexpected.setParseAction(self._unexpected_token)
key_value = (~lbrack + value_name
+ value_operation + empty
+ restOfLine + LineEnd().suppress())
def strip_key_value(tokens):
tokens[0] = tokens[0].strip()
tokens[1] = tokens[1].strip()
key_value.setParseAction(strip_key_value)
self.ini_section_parser = (section_start
+ Group(ZeroOrMore(
Group(key_value | unexpected)))
| unexpected)
self.ini_section_parser.ignore(comment)
def _unexpected_token(self, string, location, tokens):
'''Метод вызываемый парсером, если обнаружена некорректная строка,
предназначен для получения некорректной строки и ее дальнейшего
разбора.'''
print('UNEXPECTED TOKEN')
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
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:
@ -39,7 +140,8 @@ class NamespaceIniFiller:
self.clear_section(*clear_section)
elif define_key is not None:
self.define_key(*define_key)
elif error is not None:
for error in self.ini_parser.errors:
self.set_error(*error)
def start_section(self, sections):
@ -80,7 +182,6 @@ class NamespaceIniFiller:
self.change_value(key, ",".join(line))
def define_key(self, section, key, value, optype):
Define = CalculateIniParser.Define
if optype == Define.assign:
if key not in self.current_namespace:
self.define_variable(key, value)

File diff suppressed because it is too large Load Diff

@ -1,7 +1,6 @@
import pytest
from mock import call
from calculate.utils.calculateini import CalculateIniParser
Define = CalculateIniParser.Define
from calculate.vars.vars_loader import CalculateIniParser, Define
@pytest.mark.vars
@ -13,118 +12,112 @@ class TestCalculateIni:
# assert ns.varPath is None
def test_section_values(self, mocker):
cip = CalculateIniParser()
spy_section = mocker.spy(cip, 'start_section')
spy_def_key = mocker.spy(cip, 'define_key')
spy_error = mocker.spy(cip, 'parse_error')
ini_parser = CalculateIniParser()
cip.parse("[section]\n"
"varval1 = value1\n")
input_text = ("[section]\n"
"varval1 = value1\n")
spy_error.assert_not_called()
spy_section.assert_has_calls([call(['section'])])
spy_def_key.assert_has_calls([
call(['section'], 'varval1', 'value1', Define.assign),
])
parsed_lines = [{'start_section': (['section'],)},
{'define_key': (['section'], 'varval1',
'value1', Define.assign)},
]
parse_result = list(ini_parser.parse(input_text))
for parsed_line, result_line in zip(parsed_lines, parse_result):
assert parsed_line == result_line
def test_simple_calculate_ini_with_comments(self, mocker):
cip = CalculateIniParser()
spy_section = mocker.spy(cip, 'start_section')
spy_def_key = mocker.spy(cip, 'define_key')
spy_error = mocker.spy(cip, 'parse_error')
cip.parse("[section]\n"
"varval1 = value1\n"
"# some comment\n"
"varval2 += value2\n"
"varval3 -= value3\n")
spy_error.assert_not_called()
spy_section.assert_has_calls([call(['section'])])
spy_def_key.assert_has_calls([
call(['section'], 'varval1', 'value1', Define.assign),
call(['section'], 'varval2', 'value2', Define.append),
call(['section'], 'varval3', 'value3', Define.remove),
])
ini_parser = CalculateIniParser()
input_text = ("[section]\n"
"varval1 = value1\n"
"# some comment\n"
"varval2 += value2\n"
"varval3 -= value3\n")
parsed_lines = [{'start_section': (['section'],)},
{'define_key': (['section'], 'varval1',
'value1', Define.assign)},
{'define_key': (['section'], 'varval2',
'value2', Define.append)},
{'define_key': (['section'], 'varval3',
'value3', Define.remove)},
]
parse_result = list(ini_parser.parse(input_text))
for parsed_line, result_line in zip(parsed_lines, parse_result):
assert parsed_line == result_line
def test_some_complex_section_calculate_ini(self, mocker):
cip = CalculateIniParser()
spy_section = mocker.spy(cip, 'start_section')
spy_def_key = mocker.spy(cip, 'define_key')
spy_error = mocker.spy(cip, 'parse_error')
cip.parse("[section][sub]\n"
"varval1 = value1\n"
"varval2 = value2\n"
"[section][sub2]\n"
"varval1 = value1\n"
"\n"
"[section2]\n"
"varval1 = value1\n")
spy_error.assert_not_called()
spy_section.assert_has_calls([call(['section', 'sub'])])
spy_section.assert_has_calls([call(['section', 'sub2'])])
spy_section.assert_has_calls([call(['section2'])])
spy_def_key.assert_has_calls([
call(['section', 'sub'], 'varval1', 'value1', Define.assign),
call(['section', 'sub'], 'varval2', 'value2', Define.assign),
call(['section', 'sub2'], 'varval1', 'value1', Define.assign),
call(['section2'], 'varval1', 'value1', Define.assign),
])
ini_parser = CalculateIniParser()
input_text = ("[section][sub]\n"
"varval1 = value1\n"
"varval2 = value2\n"
"[section][sub2]\n"
"varval1 = value1\n"
"\n"
"[section2]\n"
"varval1 = value1\n")
parsed_lines = [{'start_section': (['section', 'sub'],)},
{'define_key': (['section', 'sub'],
'varval1', 'value1', Define.assign)},
{'define_key': (['section', 'sub'],
'varval2', 'value2', Define.assign)},
{'start_section': (['section', 'sub2'],)},
{'define_key': (['section', 'sub2'],
'varval1', 'value1', Define.assign)},
{'start_section': (['section2'],)},
{'define_key': (['section2'], 'varval1',
'value1', Define.assign)}]
parse_result = list(ini_parser.parse(input_text))
for parsed_line, result_line in zip(parsed_lines, parse_result):
assert parsed_line == result_line
def test_error(self, mocker):
cip = CalculateIniParser()
spy_section = mocker.spy(cip, 'start_section')
spy_def_key = mocker.spy(cip, 'define_key')
spy_error = mocker.spy(cip, 'parse_error')
cip.parse("[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")
# проверяем, что расозналась только одна секция
spy_section.assert_has_calls([call(['section', 'sub2'])])
# проверяем, что значение из криво определённой section2 попало
# в section.sub2
spy_def_key.assert_has_calls([
call(['section', 'sub2'], 'varval1', 'value1', Define.assign),
call(['section', 'sub2'], 'varval4', 'value4', Define.assign),
])
# проверяем, все нераспознанные строки попали в ошибки
# криво объявленная первая секция
# её переменные
# кривая переменная eee
# криво объявленная section2
spy_error.assert_has_calls([
call('[section', 1, 1),
call('varval1 = value1', 2, 1),
call('varval2 = value2', 3, 1),
call('eee', 6, 1),
call('[section2', 8, 1),
call('[section][][sub]', 11, 1),
call('[section][][sub][]', 12, 1),
])
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': (['section', 'sub2'],
'varval1', 'value1', Define.assign)},
{'define_key': (['section', 'sub2'],
'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),
]
parse_result = list(ini_parser.parse(input_text))
for error, parsed_error in zip(errors, ini_parser.errors):
assert parsed_error == error
for parsed_line, result_line in zip(parsed_lines, parse_result):
assert parsed_line == result_line
def test_clear_section(self, mocker):
cip = CalculateIniParser()
spy_section = mocker.spy(cip, 'start_section')
spy_def_key = mocker.spy(cip, 'define_key')
spy_error = mocker.spy(cip, 'parse_error')
spy_clear_sec = mocker.spy(cip, 'clear_section')
cip.parse("[section][test][]\n")
spy_error.assert_not_called()
spy_def_key.assert_not_called()
spy_section.assert_not_called()
ini_parser = CalculateIniParser()
spy_clear_sec.assert_has_calls([call(['section', 'test'])])
parse_result = next(ini_parser.parse("[section][test][]\n"))
assert parse_result == {'clear_section': (['section', 'test'],)}

@ -0,0 +1,6 @@
import pytest
@pytest.mark.alt_vars
class TestNamespace:
pass

@ -476,7 +476,6 @@ class TestNamespace:
assert test3.get_value() == "ZXC [234]"
test2.set_value("567")
assert test3.get_value() == "ZXC [567]"
assert False
def test_wrong_choice_varaible(self):
class TestVar1(Variable):

Loading…
Cancel
Save