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 calculate.utils.tools import Singleton
import hashlib
import operator
class PackageError(Exception):
@ -42,169 +41,233 @@ class PackageNotFound(Exception):
class Version:
_suffix_order = {'alpha': 0, 'beta': 1, 'pre': 2,
'rc': 3, 'no': 4, 'p': 5}
'''Класс для работы со значениями версий.'''
def __init__(self, version_value=None):
if version_value is None:
self._version_string = '-1'
self._version_value = [-1]
self._string = '-1'
self._value = [-1]
self._literal = None
self._suffix = [(4, 0)]
self._revision = 0
elif isinstance(version_value, Version):
self._version_string = version_value._version_string
self._version_value = version_value._version_value
self._string = version_value._string
self._value = version_value._value
self._literal = version_value._literal
self._suffix = version_value._suffix
self._revision = version_value._revision
else:
value = self._get_version_value(version_value)
if not value:
if value is None:
raise VersionError(
"Can't initialize Version object using '{0}'"
" value with type {1}".format(version_value,
type(version_value)))
if isinstance(version_value, str):
self._version_string = version_value.strip('-')
else:
self._version_string = str(version_value)
self._version_value = value
self._string = value['string']
self._value = value['value']
self._literal = value['literal']
self._suffix = value['suffix']
self._revision = value['revision']
def _get_version_value(self, 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):
version_value = [str(version)]
version_value = {'string': str(int),
'value': [version],
'literal': '',
'suffix': [(4, 0)],
'revision': 0}
elif isinstance(version, float):
version_value = []
version_list = []
version = str(version).split('.')
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):
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):
'''Вспомогательный метод для реализации различных операций сравнения.
'''
version_value = self._version_value[:]
# Парсим ревизию.
if parse_result['revision'] is not None:
version_value['revision'] = int(
parse_result['revision'].strip('-r'))
else:
version_value['revision'] = 0
else:
return
other_value_length = len(other_value)
version_value_length = len(version_value)
return version_value
if other_value_length < version_value_length:
for counter in range(version_value_length - other_value_length):
other_value.append(0)
elif version_value_length < other_value_length:
for counter in range(other_value_length - version_value_length):
version_value.append(0)
def _compare_lists(self, lversion, rversion, filler=0):
'''Метод для сравнения двух списков, даже если если они не одинаковы.
Возвращает 0, если списки равны, 1 если lversion больше, -1 если
lversion меньше.'''
if lversion == rversion:
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:
continue
if compare_operator(lvalue, rvalue):
return True
else:
return False
else:
if (compare_operator != operator.lt and
compare_operator != operator.gt and
compare_operator != operator.ne):
return True
if lvalue > rvalue:
return 1
else:
return False
return -1
return 0
def __lt__(self, other):
'''Перегрузка x < y.'''
other_value = self._get_version_value(other)
if not other_value:
other_version = self._get_version_value(other)
if other_version is None:
raise VersionError(
"Unable to compare Version object with the '{0}'"
" 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):
'''Перегрузка x <= y.'''
other_value = self._get_version_value(other)
if not other_value:
other_version = self._get_version_value(other)
if other_version is None:
raise VersionError(
"Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other)))
return self._use_compare_operation(operator.le, other_value)
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._revision <= other_version['revision']
def __eq__(self, other):
'''Перегрузка x == y.'''
other_value = self._get_version_value(other)
if not other_value:
other_version = self._get_version_value(other)
if other_version is None:
raise VersionError(
"Unable to compare Version object with the '{0}'"
" value of '{1}' type".format(other, type(other)))
return self._use_compare_operation(operator.eq, other_value)
cmp_res = self._compare_lists(self._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):
'''Перегрузка x != y.'''
other_value = self._get_version_value(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)
return not self.__eq__(other)
def __gt__(self, other):
'''Перегрузка x > y.'''
other_value = self._get_version_value(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)
return not self.__le__(other)
def __ge__(self, other):
'''Перегрузка x >= y.'''
other_value = self._get_version_value(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)
return not self.__lt__(other)
def __hash__(self):
return hash(self._version_string)
return hash(self._string)
def __repr__(self):
return '<Version: {}>'.format(self._version_string)
return '<Version: {}>'.format(self._string)
def __str__(self):
return self._version_string
return self._string
def __bool__(self):
if self._version_value == [-1]:
if self._value == [-1]:
return False
else:
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):
def __init__(self):
@ -328,15 +391,13 @@ class PackageAtomName:
def __eq__(self, other):
if isinstance(other, PackageAtomName):
return (self._package_directory ==
other._package_directory)
return self._package_directory == other._package_directory
else:
return False
def __ne__(self, other):
if isinstance(other, PackageAtomName):
return (self._package_directory !=
other._package_directory)
return self._package_directory != other._package_directory
else:
return False
@ -361,8 +422,14 @@ NonePackage = PackageAtomName({'pkg_path': None, 'version': None})
class PackageAtomParser:
'''Класс для парсинга параметра 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 =\
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/]*)/
{0}
@ -372,6 +439,7 @@ class PackageAtomParser:
atom_regex = re.compile(atom_name_pattern, re.VERBOSE)
package_name_regex = re.compile(package_name_pattern)
version_regex = re.compile(_version_pattern)
def __init__(self, pkg_path='/var/db/pkg',
chroot_path='/'):
@ -443,7 +511,7 @@ class PackageAtomParser:
# Используем glob-паттерн для поиска.
if self._atom_dictionary['version'] is not None:
full_name = self._atom_dictionary['name'] + '-' +\
self._atom_dictionary['version']._version_string
self._atom_dictionary['version']._string
else:
full_name = self._atom_dictionary['name']

@ -22,20 +22,35 @@ class TestContents:
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):
version_1 = Version('3.7.2-r1')
version_1 = Version('3.9.0_beta')
version_2 = Version('3.9')
version_3 = Version('3.9.0')
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):
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
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_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):
version_1 = Version('13.0')
@ -45,6 +60,43 @@ class TestContents:
version_1 = Version('13.1.0')
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):
result = OrderedDict({
'/usr':
@ -240,24 +292,3 @@ sym /etc/test_dir_2/symlink -> file_2.cfg {}
with pytest.raises(PackageNotFound):
file_package = package_atom.get_file_package('/etc/shadow')
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