Now versions can be checked using range of versions. Different suffixes and revisions syntax is available now for single version values and versions in ATOM-names. fixed #4

master
Иванов Денис 4 years ago
parent ae7f6f620d
commit 44fcfc72f6

@ -10,7 +10,6 @@ from pyparsing import Literal, Regex, Word, nums, alphanums,\
from jinja2 import PackageLoader, Environment from jinja2 import PackageLoader, Environment
from calculate.utils.tools import Singleton from calculate.utils.tools import Singleton
import hashlib import hashlib
import operator
class PackageError(Exception): class PackageError(Exception):
@ -42,169 +41,233 @@ class PackageNotFound(Exception):
class Version: class Version:
_suffix_order = {'alpha': 0, 'beta': 1, 'pre': 2,
'rc': 3, 'no': 4, 'p': 5}
'''Класс для работы со значениями версий.''' '''Класс для работы со значениями версий.'''
def __init__(self, version_value=None): def __init__(self, version_value=None):
if version_value is None: if version_value is None:
self._version_string = '-1' self._string = '-1'
self._version_value = [-1] self._value = [-1]
self._literal = None
self._suffix = [(4, 0)]
self._revision = 0
elif isinstance(version_value, Version): elif isinstance(version_value, Version):
self._version_string = version_value._version_string self._string = version_value._string
self._version_value = version_value._version_value self._value = version_value._value
self._literal = version_value._literal
self._suffix = version_value._suffix
self._revision = version_value._revision
else: else:
value = self._get_version_value(version_value) value = self._get_version_value(version_value)
if not value: if value is None:
raise VersionError( raise VersionError(
"Can't initialize Version object using '{0}'" "Can't initialize Version object using '{0}'"
" value with type {1}".format(version_value, " value with type {1}".format(version_value,
type(version_value))) type(version_value)))
if isinstance(version_value, str): self._string = value['string']
self._version_string = version_value.strip('-') self._value = value['value']
else: self._literal = value['literal']
self._version_string = str(version_value) self._suffix = value['suffix']
self._revision = value['revision']
self._version_value = value
def _get_version_value(self, version): def _get_version_value(self, version):
'''Вспомогательный метод для получения значения версии, представленного '''Вспомогательный метод для получения значения версии, представленного
в виде списка.''' в виде списка.'''
if isinstance(version, Version): if isinstance(version, Version):
return version._version_value version_value = {'string': version._string,
'value': version._value,
'literal': version._literal,
'suffix': version._suffix,
'revision': version._revision}
elif isinstance(version, int): elif isinstance(version, int):
version_value = [str(version)] version_value = {'string': str(int),
'value': [version],
'literal': '',
'suffix': [(4, 0)],
'revision': 0}
elif isinstance(version, float): elif isinstance(version, float):
version_value = [] version_list = []
version = str(version).split('.') version = str(version).split('.')
for version_part in version: for version_part in version:
version_value.append(int(version_part.strip())) version_list.append(int(version_part.strip()))
version_value = {'string': str(version),
'value': version_list,
'literal': '',
'suffix': (4, 0),
'revision': 0}
elif isinstance(version, str): elif isinstance(version, str):
version = version.strip('-')
version_value = []
if '-' in version:
version = version.split('-')[0]
if '_' in version:
version = version.split('_')[0]
for version_part in version.split('.'):
if version_part.isdigit():
version_part = int(version_part)
version_value.append(version_part)
else:
return False
else:
return False
return version_value parse_result = PackageAtomParser.version_regex.search(
version.strip('-'))
if not parse_result:
return
result_dict = parse_result.groupdict()
version_value = {'string': version}
version_list = []
for version_part in result_dict['value'].split('.'):
version_list.append(int(version_part.strip('-')))
version_value['value'] = version_list
# Парсим литерал, если он есть.
version_value['literal'] = result_dict['literal'] or ''
# Парсим всю совокупность имеющихся суффиксов.
print('result dict:', result_dict)
suffixes = result_dict['suffix']
print('suffixes:', suffixes)
suffix_list = []
if suffixes is not None:
suffixes = suffixes.strip('_')
suffixes = suffixes.split('_')
for suffix in suffixes:
result = re.search(r'([^\d]+)(\d+)?', suffix)
suffix_list.append((self._suffix_order[result.group(1)],
int(result.group(2) or 0)))
else:
suffix_list = [(self._suffix_order['no'], 0)]
version_value['suffix'] = suffix_list
def _use_compare_operation(self, compare_operator, other_value): # Парсим ревизию.
'''Вспомогательный метод для реализации различных операций сравнения. if parse_result['revision'] is not None:
''' version_value['revision'] = int(
version_value = self._version_value[:] parse_result['revision'].strip('-r'))
else:
version_value['revision'] = 0
else:
return
other_value_length = len(other_value) return version_value
version_value_length = len(version_value)
if other_value_length < version_value_length: def _compare_lists(self, lversion, rversion, filler=0):
for counter in range(version_value_length - other_value_length): '''Метод для сравнения двух списков, даже если если они не одинаковы.
other_value.append(0) Возвращает 0, если списки равны, 1 если lversion больше, -1 если
elif version_value_length < other_value_length: lversion меньше.'''
for counter in range(other_value_length - version_value_length): if lversion == rversion:
version_value.append(0) return 0
for lvalue, rvalue in zip(version_value, other_value): for index in range(0, max(len(lversion), len(rversion))):
lvalue = lversion[index] if len(lversion) > index else filler
rvalue = rversion[index] if len(rversion) > index else filler
if lvalue == rvalue: if lvalue == rvalue:
continue continue
if compare_operator(lvalue, rvalue): if lvalue > rvalue:
return True return 1
else:
return False
else:
if (compare_operator != operator.lt and
compare_operator != operator.gt and
compare_operator != operator.ne):
return True
else: else:
return False return -1
return 0
def __lt__(self, other): def __lt__(self, other):
'''Перегрузка x < y.''' '''Перегрузка x < y.'''
other_value = self._get_version_value(other) other_version = self._get_version_value(other)
if not other_value: if other_version is None:
raise VersionError( raise VersionError(
"Unable to compare Version object with the '{0}'" "Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other))) " value of '{1}' type".format(other, type(other)))
cmp_res = self._compare_lists(self._value, other_version['value'])
if cmp_res != 0:
return cmp_res < 0
if self._literal != other_version['literal']:
return self._literal < other_version['literal']
cmp_res = self._compare_lists(self._suffix,
other_version['suffix'],
filler=(4, 0))
if cmp_res != 0:
return cmp_res < 0
return self._use_compare_operation(operator.lt, other_value) return self._revision < other_version['revision']
def __le__(self, other): def __le__(self, other):
'''Перегрузка x <= y.''' '''Перегрузка x <= y.'''
other_value = self._get_version_value(other) other_version = self._get_version_value(other)
if not other_value: if other_version is None:
raise VersionError( raise VersionError(
"Unable to compare Version object with the '{0}'" "Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other))) " value of '{1}' type".format(other, type(other)))
cmp_res = self._compare_lists(self._value, other_version['value'])
return self._use_compare_operation(operator.le, other_value) if cmp_res != 0:
return cmp_res < 0
if self._literal != other_version['literal']:
return self._literal < other_version['literal']
cmp_res = self._compare_lists(self._suffix,
other_version['suffix'],
filler=(4, 0))
if cmp_res != 0:
return cmp_res < 0
return self._revision <= other_version['revision']
def __eq__(self, other): def __eq__(self, other):
'''Перегрузка x == y.''' '''Перегрузка x == y.'''
other_value = self._get_version_value(other) other_version = self._get_version_value(other)
if not other_value: if other_version is None:
raise VersionError( raise VersionError(
"Unable to compare Version object with the '{0}'" "Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other))) " value of '{1}' type".format(other, type(other)))
cmp_res = self._compare_lists(self._value,
return self._use_compare_operation(operator.eq, other_value) other_version['value'])
if cmp_res != 0:
return False
if self._literal != other_version['literal']:
return False
cmp_res = self._compare_lists(self._suffix,
other_version['suffix'],
filler=(4, 0))
if cmp_res != 0:
return False
return self._revision == other_version['revision']
def __ne__(self, other): def __ne__(self, other):
'''Перегрузка x != y.''' '''Перегрузка x != y.'''
other_value = self._get_version_value(other) return not self.__eq__(other)
if not other_value:
raise VersionError(
"Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other)))
return self._use_compare_operation(operator.ne, other_value)
def __gt__(self, other): def __gt__(self, other):
'''Перегрузка x > y.''' '''Перегрузка x > y.'''
other_value = self._get_version_value(other) return not self.__le__(other)
if not other_value:
raise VersionError(
"Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other)))
return self._use_compare_operation(operator.gt, other_value)
def __ge__(self, other): def __ge__(self, other):
'''Перегрузка x >= y.''' '''Перегрузка x >= y.'''
other_value = self._get_version_value(other) return not self.__lt__(other)
if not other_value:
raise VersionError(
"Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other)))
return self._use_compare_operation(operator.ge, other_value)
def __hash__(self): def __hash__(self):
return hash(self._version_string) return hash(self._string)
def __repr__(self): def __repr__(self):
return '<Version: {}>'.format(self._version_string) return '<Version: {}>'.format(self._string)
def __str__(self): def __str__(self):
return self._version_string return self._string
def __bool__(self): def __bool__(self):
if self._version_value == [-1]: if self._value == [-1]:
return False return False
else: else:
return True return True
def __rshift__(self, other: tuple) -> bool:
"Проверка нахождения значения переменной в указанном диапазоне."
if (not isinstance(other, tuple) or len(other) != 2
or not isinstance(other[0], str) or not isinstance(other[1], str)):
raise VersionError("Versions range must be tuple of two strings,"
f" not '{type(other)}'")
lequal = other[0].startswith('=')
lversion = Version(other[0].strip('='))
requal = other[0].startswith('=')
rversion = Version(other[1].strip('='))
return (((lequal and self >= lversion)
or (not lequal and self > lversion))
and ((requal and self <= rversion)
or (not requal and self < rversion)))
class ContentsParser(metaclass=Singleton): class ContentsParser(metaclass=Singleton):
def __init__(self): def __init__(self):
@ -328,15 +391,13 @@ class PackageAtomName:
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, PackageAtomName): if isinstance(other, PackageAtomName):
return (self._package_directory == return self._package_directory == other._package_directory
other._package_directory)
else: else:
return False return False
def __ne__(self, other): def __ne__(self, other):
if isinstance(other, PackageAtomName): if isinstance(other, PackageAtomName):
return (self._package_directory != return self._package_directory != other._package_directory
other._package_directory)
else: else:
return False return False
@ -361,8 +422,14 @@ NonePackage = PackageAtomName({'pkg_path': None, 'version': None})
class PackageAtomParser: class PackageAtomParser:
'''Класс для парсинга параметра package, его проверки, а также определения '''Класс для парсинга параметра package, его проверки, а также определения
принадлежности файла пакету.''' принадлежности файла пакету.'''
_value = r'(?P<value>\d+(\.\d+)*)'
_literal = r'(?P<literal>[a-z])?'
_suffix = r'(?P<suffix>(_(pre|p|beta|alpha|rc)(\d+)?)+)?'
_revision = r'(?P<revision>-r\d+)?'
_version_pattern = _value + _literal + _suffix + _revision
package_name_pattern =\ package_name_pattern =\
r'(?P<name>\D[\w\d]*(\-\D[\w\d]*)*)(?P<version>-\d[^\s,\[:]*)?' fr'(?P<name>\D[\w\d]*(\-\D[\w\d]*)*)(?P<version>-{_version_pattern})?'
atom_name_pattern = r'''(?P<category>[^\s/]*)/ atom_name_pattern = r'''(?P<category>[^\s/]*)/
{0} {0}
@ -372,6 +439,7 @@ class PackageAtomParser:
atom_regex = re.compile(atom_name_pattern, re.VERBOSE) atom_regex = re.compile(atom_name_pattern, re.VERBOSE)
package_name_regex = re.compile(package_name_pattern) package_name_regex = re.compile(package_name_pattern)
version_regex = re.compile(_version_pattern)
def __init__(self, pkg_path='/var/db/pkg', def __init__(self, pkg_path='/var/db/pkg',
chroot_path='/'): chroot_path='/'):
@ -443,7 +511,7 @@ class PackageAtomParser:
# Используем glob-паттерн для поиска. # Используем glob-паттерн для поиска.
if self._atom_dictionary['version'] is not None: if self._atom_dictionary['version'] is not None:
full_name = self._atom_dictionary['name'] + '-' +\ full_name = self._atom_dictionary['name'] + '-' +\
self._atom_dictionary['version']._version_string self._atom_dictionary['version']._string
else: else:
full_name = self._atom_dictionary['name'] full_name = self._atom_dictionary['name']

@ -22,20 +22,35 @@ class TestContents:
assert version_1 < version_2 assert version_1 < version_2
def test_if_two_Version_objects_compared_using_le_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True(self): def test_if_two_Version_objects_compared_using_le_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True(self):
version_1 = Version('3.7.2-r1') version_1 = Version('3.9.0_beta')
version_2 = Version('3.9') version_2 = Version('3.9')
version_3 = Version('3.9.0') version_3 = Version('3.9.0')
assert (version_1 <= version_2 and version_2 <= version_3) assert (version_1 <= version_2 and version_2 <= version_3)
def test_if_two_Version_objects_compared_using_lt_operation_both_versions_has_suffixes_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True(self):
version_1 = Version('13.9_alpha')
version_2 = Version('13.9.0_beta')
assert version_1 < version_2
def test_if_two_Version_objects_compared_using_gt_operation_and_the_left_version_value_is_less_than_the_right_version__the_result_of_the_comparing_would_be_True(self): def test_if_two_Version_objects_compared_using_gt_operation_and_the_left_version_value_is_less_than_the_right_version__the_result_of_the_comparing_would_be_True(self):
version_1 = Version('3.5.6-r1') version_1 = Version('3.5.6-r1')
version_2 = Version('2.5.6') version_2 = Version('2.5.6-r2')
assert version_1 > version_2
def test_if_two_Version_objects_compared_using_gt_operation_both_of_the_versions_have_revision_values_and_the_same_version_number_and_the_right_version_value_is_less_than_the_left_version__the_result_of_the_comparing_would_be_True(self):
version_1 = Version('3.5.6-r2')
version_2 = Version('3.5.6-r1')
assert version_1 > version_2
def test_if_two_Version_objects_compared_using_gt_operation_both_of_them_have_p_suffix_and_the_right_version_value_is_less_than_left_version__the_result_of_the_comparing_would_be_True(self):
version_1 = Version('2.5.6_p2-r1')
version_2 = Version('2.5.6_p1-r1')
assert version_1 > version_2 assert version_1 > version_2
def test_if_two_Version_objects_compared_using_ge_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True(self): def test_if_two_Version_objects_compared_using_ge_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True(self):
version_1 = Version('13.0') version_1 = Version('13.0')
version_2 = Version('12.7.6-r1') version_2 = Version('12.7.6-r1')
assert (version_1 >= version_2 and version_2 >= '12.7.6') assert version_1 >= version_2 and version_2 >= '12.7.6'
def test_if_two_Version_objects_compared_using_ne_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True(self): def test_if_two_Version_objects_compared_using_ne_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True(self):
version_1 = Version('13.0') version_1 = Version('13.0')
@ -45,6 +60,43 @@ class TestContents:
version_1 = Version('13.1.0') version_1 = Version('13.1.0')
assert (version_1 == '13.1') assert (version_1 == '13.1')
def test_if_version_object_is_checked_to_be_in_the_range_and_the_version_is_in_the_range_indeed__the_operation_returns_True(self):
version = Version('5.1.2')
assert version >> ('5.1.0', '6.1.12')
def test_if_version_object_is_checked_to_be_in_the_range_between_two_versions_values_the_first_one_is_included_and_the_version_is_in_this_range_indeed__the_operation_returns_True(self):
version = Version('5.1.2')
assert version >> ('=5.1.2', '6.1.12')
def test_i_am_too_lazy_to_come_up_with_the_name_for_this_test(self):
version = Version('5.1.2')
assert not version >> ('5.1.2', '6.1.12')
def test_the_same_situation(self):
version = Version('4.1')
assert not version >> ('5.1.2', '6.1.12')
def test_if_ContentsParser_is_used_for_parsing_of_a_correct_contents_file_text_and_then_is_used_to_get_text_of_the_source_text__the_source_text_and_the_parser_s_output_will_be_the_same(self):
parser = ContentsParser()
contents_text = '''dir /usr
dir /usr/bin
sym /usr/bin/rview -> vim 1573538053
sym /usr/bin/rvim -> vim 1573538053
obj /usr/bin/vim 30acc0f256e11c1ecdb1bd80b688d238 1573538056
sym /usr/bin/vimdiff -> vim 1573538053
dir /etc
dir /etc/test_dir_1
'''
output_dict = parser.parse(contents_text)
print('DICT:')
pprint(output_dict)
output_text = parser.render(output_dict)
print('TEXT:')
print(output_text)
assert output_text == contents_text
def test_if_PackageContents_object_initialized_by_existing_package__it_contains_dictionary_of_items_from_contents_file(self): def test_if_PackageContents_object_initialized_by_existing_package__it_contains_dictionary_of_items_from_contents_file(self):
result = OrderedDict({ result = OrderedDict({
'/usr': '/usr':
@ -240,24 +292,3 @@ sym /etc/test_dir_2/symlink -> file_2.cfg {}
with pytest.raises(PackageNotFound): with pytest.raises(PackageNotFound):
file_package = package_atom.get_file_package('/etc/shadow') file_package = package_atom.get_file_package('/etc/shadow')
print('package = {}'.format(file_package)) print('package = {}'.format(file_package))
def test_parser(self):
parser = ContentsParser()
contents_text = '''dir /usr
dir /usr/bin
sym /usr/bin/rview -> vim 1573538053
sym /usr/bin/rvim -> vim 1573538053
obj /usr/bin/vim 30acc0f256e11c1ecdb1bd80b688d238 1573538056
sym /usr/bin/vimdiff -> vim 1573538053
dir /etc
dir /etc/test_dir_1
'''
output_dict = parser.parse(contents_text)
print('DICT:')
pprint(output_dict)
output_text = parser.render(output_dict)
print('TEXT:')
print(output_text)
assert output_text == contents_text

Loading…
Cancel
Save