Hello community, here is the log from the commit of package python-dephell-specifier for openSUSE:Factory checked in at 2019-09-13 15:03:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-dephell-specifier (Old) and /work/SRC/openSUSE:Factory/.python-dephell-specifier.new.7948 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-dephell-specifier" Fri Sep 13 15:03:35 2019 rev:2 rq:730640 version:0.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-dephell-specifier/python-dephell-specifier.changes 2019-08-13 13:26:59.389327985 +0200 +++ /work/SRC/openSUSE:Factory/.python-dephell-specifier.new.7948/python-dephell-specifier.changes 2019-09-13 15:05:06.657259382 +0200 @@ -1,0 +2,6 @@ +Fri Sep 13 09:01:42 UTC 2019 - Tomáš Chvátal <tchva...@suse.com> + +- Update to 0.2.1: + * no upstream changelog + +------------------------------------------------------------------- Old: ---- dephell_specifier-0.1.5.tar.gz New: ---- dephell_specifier-0.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-dephell-specifier.spec ++++++ --- /var/tmp/diff_new_pack.xemgOs/_old 2019-09-13 15:05:07.297259246 +0200 +++ /var/tmp/diff_new_pack.xemgOs/_new 2019-09-13 15:05:07.301259245 +0200 @@ -12,30 +12,31 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-dephell-specifier -Version: 0.1.5 +Version: 0.2.1 Release: 0 -License: MIT Summary: Dephell library for Python package version specifiers -Url: https://github.com/dephell/dephell_specifier +License: MIT Group: Development/Languages/Python -Source: https://files.pythonhosted.org/packages/source/d/dephell-specifier/dephell_specifier-%{version}.tar.gz -BuildRequires: python-rpm-macros +URL: https://github.com/dephell/dephell_specifier +Source: https://files.pythonhosted.org/packages/source/d/dephell_specifier/dephell_specifier-%{version}.tar.gz BuildRequires: %{python_module base >= 3.5} BuildRequires: %{python_module setuptools} -# SECTION test requirements -BuildRequires: %{python_module packaging} -# /SECTION BuildRequires: fdupes +BuildRequires: python-rpm-macros Requires: python-base >= 3.5 Requires: python-packaging BuildArch: noarch - +# SECTION test requirements +BuildRequires: %{python_module packaging >= 17.1} +BuildRequires: %{python_module pytest} +# /SECTION %python_subpackages %description @@ -51,6 +52,9 @@ %python_install %python_expand %fdupes %{buildroot}%{$python_sitelib} +%check +%pytest + %files %{python_files} %license LICENSE %doc README.md ++++++ dephell_specifier-0.1.5.tar.gz -> dephell_specifier-0.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/PKG-INFO new/dephell_specifier-0.2.1/PKG-INFO --- old/dephell_specifier-0.1.5/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell_specifier-0.2.1/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 -Name: dephell_specifier -Version: 0.1.5 +Name: dephell-specifier +Version: 0.2.1 Summary: Work with version specifiers. Author: orsinium Requires-Python: >=3.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/dephell_specifier/constants.py new/dephell_specifier-0.2.1/dephell_specifier/constants.py --- old/dephell_specifier-0.1.5/dephell_specifier/constants.py 2019-03-20 14:09:31.000000000 +0100 +++ new/dephell_specifier-0.2.1/dephell_specifier/constants.py 2019-07-07 17:57:48.000000000 +0200 @@ -12,3 +12,5 @@ PYTHONS_POPULAR = ('3.5', '3.6', '3.7') PYTHONS_UNRELEASED = ('3.8', '4.0') PYTHONS = PYTHONS_POPULAR + PYTHONS_DEPRECATED + PYTHONS_UNRELEASED + +OPERATOR_SYMBOLS = '!><=[]()~^,' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/dephell_specifier/range_specifier.py new/dephell_specifier-0.2.1/dephell_specifier/range_specifier.py --- old/dephell_specifier-0.1.5/dephell_specifier/range_specifier.py 2019-04-21 09:26:43.000000000 +0200 +++ new/dephell_specifier-0.2.1/dephell_specifier/range_specifier.py 2019-07-07 17:57:48.000000000 +0200 @@ -1,13 +1,20 @@ +import re +from typing import Set, List + # external from packaging.specifiers import InvalidSpecifier from packaging.version import LegacyVersion, parse, Version # app -from .constants import PYTHONS, JoinTypes +from .constants import PYTHONS, JoinTypes, OPERATOR_SYMBOLS from .git_specifier import GitSpecifier from .specifier import Specifier +REX_MAVEN_INTERVAL = re.compile(r'([\]\)])\,([\[\(])') +REX_TRIM_OPERATOR = re.compile(r'([{}])\s+'.format(re.escape(OPERATOR_SYMBOLS))) + + class RangeSpecifier: def __init__(self, spec=None): @@ -16,30 +23,33 @@ self.join_type = JoinTypes.AND return + # split `>2 || <1` on `>2` and `<1` subspecs = str(spec).split('||') if len(subspecs) > 1: self._specs = {type(self)(subspec) for subspec in subspecs} self.join_type = JoinTypes.OR return + # split `(,1),(2,)` on `(,1)` and `(2,)` + subspecs = REX_MAVEN_INTERVAL.sub(r'\1|\2', str(spec)).split('|') + if len(subspecs) > 1: + self._specs = {type(self)(subspec) for subspec in subspecs} + self.join_type = JoinTypes.OR + return + self._specs = self._parse(spec) self.join_type = JoinTypes.AND return - @staticmethod - def _parse(spec) -> set: - if not isinstance(spec, (list, tuple)): - spec = str(spec).split(',') + @classmethod + def _parse(cls, spec) -> Set[Specifier]: + spec = cls._split_specifier(spec) result = set() for constr in spec: - constr = constr.strip() - if constr in ('', '*'): + constr = cls._clean_constraint(constr) + if not constr: continue - constr = constr.replace('.x', '.*') - constr = constr.replace('.X', '.*') - - # https://docs.npmjs.com/misc/semver#advanced-range-syntax - + # parse npm's version range (`1.2.3 - 2.3.0`) if ' - ' in constr: if '.*' in constr: raise InvalidSpecifier('cannot mix ranges and starred notation') @@ -47,35 +57,116 @@ result.add(Specifier('>=' + left)) result.add(Specifier('<=' + right)) continue - + # parse mixed stars and operators like `<=1.2.*` + if constr[0] in '<>' and '.*' in constr: + result.add(cls._parse_star_and_operator(constr)) + continue + # parse npm-style semver specifiers if constr[0] in '~^': - version = parse(constr.lstrip('~^=').replace('.*', '.0')) - if isinstance(version, LegacyVersion): - raise InvalidSpecifier(constr) - parts = version.release + (0, 0) - parts = tuple(map(str, parts)) - left = '.'.join(parts[:3]) - - if constr[:2] == '~=': # ~=1.2 := >=1.2 <2.0; ~=1.2.2 := >=1.2.2 <1.3.0 - if len(version.release) == 1: - msg = '`~=` MUST NOT be used with a single segment version: ' - raise ValueError(msg + str(version)) - # https://www.python.org/dev/peps/pep-0440/#compatible-release - right = '.'.join(map(str, version.release[:3][:-1])) + '.*' - elif constr[0] == '^': # ^1.2.3 := >=1.2.3 <2.0.0 - # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - right = '.'.join([parts[0], '*']) - elif constr[0] == '~': # ~1.2.3 := >=1.2.3 <1.3.0 - # https://www.npmjs.com/package/semver#tilde-ranges-123-12-1 - right = '.'.join([parts[0], parts[1], '*']) - - result.add(Specifier('>=' + left)) - result.add(Specifier('==' + right)) + result.update(cls._parse_npm(constr)) continue - + # parse maven-style interval specifiers + if constr[0] in '[(' or constr[-1] in ')]': + result.update(cls._parse_maven(constr)) + continue + # parse classic python specifier result.add(Specifier(constr)) return result + @staticmethod + def _split_specifier(spec) -> List[str]: + if isinstance(spec, (list, tuple)): + return list(spec) + spec = str(spec) + + # pep-style comma-separated + if ',' in spec: + return spec.split(',') + + # single specifier + spec = REX_TRIM_OPERATOR.sub(r'\1', spec) + if ' ' not in spec: + return [spec] + + # npm-style space-separated + spec = spec.replace(' - ', '|').split() + spec = [constr.replace('|', ' - ') for constr in spec] + return spec + + @staticmethod + def _clean_constraint(constr: str) -> str: + constr = constr.strip() + if constr in ('', '*'): + return '' + constr = constr.replace('.x', '.*') + constr = constr.replace('.X', '.*') + constr = constr.replace('.*.*', '.*') + if constr.lstrip(OPERATOR_SYMBOLS).lower() in ('x', '*'): + return '' + + # add operator to constraint without operator + if ' - ' in constr: + return constr + if constr[0] not in OPERATOR_SYMBOLS and constr[-1] not in OPERATOR_SYMBOLS: + constr = '==' + constr + # replace `=` operator by `==` + if len(constr) > 1 and constr[0] == '=' and constr[1] not in OPERATOR_SYMBOLS: + constr = '==' + constr[1:] + return constr + + @staticmethod + def _parse_star_and_operator(constr: str) -> Specifier: + if constr[:2] in {'<', '>', '>='}: + return Specifier(constr.replace('.*', '.0')) + + version = parse(constr.lstrip(OPERATOR_SYMBOLS).rstrip('.*')) + parts = version.release[:-1] + (version.release[-1] + 1, ) + return Specifier(constr[:2] + '.'.join(map(str, parts))) + + @staticmethod + def _parse_maven(constr: str) -> Set[Specifier]: + if constr in '[]()': + return set() + if constr[0] == '[' and constr[-1] == ']': + return {Specifier('==' + constr[1:-1])} + if constr[0] == '[': + return {Specifier('>=' + constr[1:])} + if constr[0] == '(': + return {Specifier('>' + constr[1:])} + if constr[-1] == ']': + return {Specifier('<=' + constr[:-1])} + if constr[-1] == ')': + return {Specifier('<' + constr[:-1])} + raise ValueError('non maven constraint: {}'.format(constr)) + + @staticmethod + def _parse_npm(constr: str) -> Set[Specifier]: + version = parse(constr.lstrip(OPERATOR_SYMBOLS).replace('.*', '.0')) + if isinstance(version, LegacyVersion): + raise InvalidSpecifier(constr) + parts = version.release + (0, 0) + parts = tuple(map(str, parts)) + + if constr[:2] == '~=': # ~=1.2 := >=1.2 <2.0; ~=1.2.2 := >=1.2.2 <1.3.0 + if len(version.release) == 1: + msg = '`~=` MUST NOT be used with a single segment version: ' + raise ValueError(msg + str(version)) + # https://www.python.org/dev/peps/pep-0440/#compatible-release + right = '.'.join(map(str, version.release[:3][:-1])) + '.*' + elif constr[0] == '^': # ^1.2.3 := >=1.2.3 <2.0.0 + # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 + right = '.'.join([parts[0], '*']) + elif constr[0] == '~': # ~1.2.3 (or ~>1.2.3 for ruby) := >=1.2.3 <1.3.0 + # https://www.npmjs.com/package/semver#tilde-ranges-123-12-1 + # https://thoughtbot.com/blog/rubys-pessimistic-operator + if len(version.release) == 1: + right = '{}.*'.format(version.release[0]) + else: + right = '.'.join([parts[0], parts[1], '*']) + + left = '.'.join(parts[:3]) + return {Specifier('>=' + left), Specifier('==' + right)} + def attach_time(self, releases) -> bool: """Attach time to all specifiers if possible """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/dephell_specifier/specifier.py new/dephell_specifier-0.2.1/dephell_specifier/specifier.py --- old/dephell_specifier-0.1.5/dephell_specifier/specifier.py 2019-03-31 17:08:02.000000000 +0200 +++ new/dephell_specifier-0.2.1/dephell_specifier/specifier.py 2019-07-07 17:57:48.000000000 +0200 @@ -1,9 +1,10 @@ # built-in import operator +from typing import Any, Callable, Optional, Union # external from packaging import specifiers -from packaging.version import LegacyVersion, parse +from packaging.version import LegacyVersion, Version, parse # app from .utils import cached_property @@ -68,7 +69,7 @@ return True return False - def _check_version(self, version): + def _check_version(self, version) -> bool: """ https://www.python.org/dev/peps/pep-0440/ """ @@ -100,16 +101,20 @@ return self._spec.operator @property - def operation(self): + def operation(self) -> Optional[Callable[[Any, Any], bool]]: return OPERATIONS.get(self._spec.operator) + @property + def raw_version(self) -> str: + return self._spec.version + @cached_property - def version(self): - return parse(self._spec.version) + def version(self) -> Union[Version, LegacyVersion]: + return parse(self.raw_version) # magic methods - def __contains__(self, release): + def __contains__(self, release) -> bool: # compare version # check that this is Release without imports if not hasattr(release, 'time'): @@ -125,10 +130,10 @@ # compare release by version return self._check_version(version=release.version) - def __str__(self): + def __str__(self) -> str: return str(self._spec) - def __repr__(self): + def __repr__(self) -> str: return '{name}({spec})'.format( name=self.__class__.__name__, spec=str(self._spec), @@ -165,11 +170,11 @@ return NotImplemented - def __lt__(self, other): + def __lt__(self, other) -> bool: return self.version < other.version - def __eq__(self, other): + def __eq__(self, other) -> bool: return self.version == other.version and self.operator == other.operator - def __hash__(self): + def __hash__(self) -> int: return hash(self._spec) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/dephell_specifier.egg-info/PKG-INFO new/dephell_specifier-0.2.1/dephell_specifier.egg-info/PKG-INFO --- old/dephell_specifier-0.1.5/dephell_specifier.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell_specifier-0.2.1/dephell_specifier.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 -Name: dephell_specifier -Version: 0.1.5 +Name: dephell-specifier +Version: 0.2.1 Summary: Work with version specifiers. Author: orsinium Requires-Python: >=3.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/dephell_specifier.egg-info/requires.txt new/dephell_specifier-0.2.1/dephell_specifier.egg-info/requires.txt --- old/dephell_specifier-0.1.5/dephell_specifier.egg-info/requires.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell_specifier-0.2.1/dephell_specifier.egg-info/requires.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1 +1 @@ -packaging \ No newline at end of file +packaging>=17.1 \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/setup.py new/dephell_specifier-0.2.1/setup.py --- old/dephell_specifier-0.1.5/setup.py 2019-05-19 12:19:57.000000000 +0200 +++ new/dephell_specifier-0.2.1/setup.py 2019-07-07 19:54:08.000000000 +0200 @@ -13,17 +13,19 @@ readme = '' here = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(here, 'README.rst'), 'rb') as stream: - readme = stream.read().decode('utf8') +readme_path = os.path.join(here, 'README.rst') +if os.path.exists(readme_path): + with open(readme_path, 'rb') as stream: + readme = stream.read().decode('utf8') setup( long_description=readme, name='dephell_specifier', - version='0.1.5', + version='0.2.1', description='Work with version specifiers.', python_requires='>=3.5', author='orsinium', packages=['dephell_specifier'], package_data={}, - install_requires=['packaging'], + install_requires=['packaging>=17.1'], ) Binary files old/dephell_specifier-0.1.5/tests/__pycache__/__init__.cpython-37.pyc and new/dephell_specifier-0.2.1/tests/__pycache__/__init__.cpython-37.pyc differ Binary files old/dephell_specifier-0.1.5/tests/__pycache__/test_range_npm.cpython-37-PYTEST.pyc and new/dephell_specifier-0.2.1/tests/__pycache__/test_range_npm.cpython-37-PYTEST.pyc differ Binary files old/dephell_specifier-0.1.5/tests/__pycache__/test_range_specifier.cpython-37-PYTEST.pyc and new/dephell_specifier-0.2.1/tests/__pycache__/test_range_specifier.cpython-37-PYTEST.pyc differ Binary files old/dephell_specifier-0.1.5/tests/__pycache__/test_specifier.cpython-37-PYTEST.pyc and new/dephell_specifier-0.2.1/tests/__pycache__/test_specifier.cpython-37-PYTEST.pyc differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/tests/test_range_npm.py new/dephell_specifier-0.2.1/tests/test_range_npm.py --- old/dephell_specifier-0.1.5/tests/test_range_npm.py 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell_specifier-0.2.1/tests/test_range_npm.py 2019-07-07 17:20:10.000000000 +0200 @@ -0,0 +1,191 @@ +# external +import pytest + +# project +from dephell_specifier import RangeSpecifier + + +# https://github.com/npm/node-semver/blob/master/test/index.js +@pytest.mark.parametrize('spec, version', [ + # simple version + ('1.0.0 - 2.0.0', '1.2.3'), + ('^1.2.3+build', '1.2.3'), + ('^1.2.3+build', '1.3.0'), + ('1.2.3-pre+asdf - 2.4.3-pre+asdf', '1.2.3'), + ('1.2.3-pre+asdf - 2.4.3-pre+asdf', '1.2.3-pre.2'), + ('1.2.3-pre+asdf - 2.4.3-pre+asdf', '2.4.3-alpha'), + ('1.2.3+asdf - 2.4.3+asdf', '1.2.3'), + ('1.0.0', '1.0.0'), + + # open ranges + ('>=*', '0.2.4'), + ('', '1.0.0'), + ('>1.0.0', '1.1.0'), + ('<=2.0.0', '2.0.0'), + ('<=2.0.0', '1.9999.9999'), + ('<=2.0.0', '0.2.9'), + ('<2.0.0', '1.9999.9999'), + ('<2.0.0', '0.2.9'), + ('>= 1.0.0', '1.0.0'), + ('>= 1.0.0', '1.0.1'), + ('>= 1.0.0', '1.1.0'), + ('> 1.0.0', '1.0.1'), + ('> 1.0.0', '1.1.0'), + ('<= 2.0.0', '2.0.0'), + ('<= 2.0.0', '1.9999.9999'), + ('<= 2.0.0', '0.2.9'), + ('< 2.0.0', '1.9999.9999'), + ('<\t2.0.0', '0.2.9'), + ('>=0.1.97', '0.1.97'), + + # or's + ('0.1.20 || 1.2.4', '1.2.4'), + ('>=0.2.3 || <0.0.1', '0.0.0'), + ('>=0.2.3 || <0.0.1', '0.2.3'), + ('>=0.2.3 || <0.0.1', '0.2.4'), + ('||', '1.3.4'), + ('1.2.x || 2.x', '2.1.3'), + ('1.2.x || 2.x', '1.2.3'), + + # stars + ('2.x.x', '2.1.3'), + ('1.2.x', '1.2.3'), + ('x', '1.2.3'), + ('2.*.*', '2.1.3'), + ('1.2.*', '1.2.3'), + ('1.2.* || 2.*', '2.1.3'), + ('1.2.* || 2.*', '1.2.3'), + ('*', '1.2.3'), + # ('2', '2.1.2'), + # ('2.3', '2.3.1'), + + ('~0.0.1', '0.0.1'), + ('~0.0.1', '0.0.2'), + ('~x', '0.0.9'), + ('~2', '2.0.9'), + ('~2.4', '2.4.0'), + ('~2.4', '2.4.5'), + ('~>3.2.1', '3.2.2'), + ('~1', '1.2.3'), + ('~>1', '1.2.3'), + ('~> 1', '1.2.3'), + ('~1.0', '1.0.2'), + ('~ 1.0', '1.0.2'), + ('~ 1.0.3', '1.0.12'), + ('~ 1.0.3alpha', '1.0.12'), + + ('>=1', '1.0.0'), + ('>= 1', '1.0.0'), + ('<1.2', '1.1.1'), + ('< 1.2', '1.1.1'), + ('~v0.5.4-pre', '0.5.5'), + ('~v0.5.4-pre', '0.5.4'), + ('=0.7.x', '0.7.2'), + ('<=0.7.x', '0.7.2'), + ('>=0.7.x', '0.7.2'), + ('<=0.7.x', '0.6.2'), + + ('~1.2.1 >=1.2.3', '1.2.3'), + ('~1.2.1 =1.2.3', '1.2.3'), + ('~1.2.1 1.2.3', '1.2.3'), + ('~1.2.1 >=1.2.3 1.2.3', '1.2.3'), + ('~1.2.1 1.2.3 >=1.2.3', '1.2.3'), + ('~1.2.1 1.2.3', '1.2.3'), + ('>=1.2.1 1.2.3', '1.2.3'), + ('1.2.3 >=1.2.1', '1.2.3'), + ('>=1.2.3 >=1.2.1', '1.2.3'), + ('>=1.2.1 >=1.2.3', '1.2.3'), + + ('>=1.2', '1.2.8'), + ('^1.2.3', '1.8.1'), + ('^0.1.2', '0.1.2'), + ('^0.1', '0.1.2'), + ('^0.0.1', '0.0.1'), + ('^1.2', '1.4.2'), + ('^1.2 ^1', '1.4.2'), + # ('^1.2.3-alpha', '1.2.3-pre'), + # ('^1.2.0-alpha', '1.2.0-pre'), + # ('^0.0.1-alpha', '0.0.1-beta'), + # ('^0.0.1-alpha', '0.0.1'), + # ('^0.1.1-alpha', '0.1.1-beta'), + + # ('^x', '1.2.3'), + # ('x - 1.0.0', '0.9.7'), + # ('x - 1.x', '0.9.7'), + # ('1.0.0 - x', '1.9.7'), + # ('1.x - x', '1.9.7'), + # ('<=7.x', '7.9.9'), +]) +def test_included(spec, version): + assert version in RangeSpecifier(spec) + + +@pytest.mark.parametrize('spec, version', [ + ['1.0.0 - 2.0.0', '2.2.3'], + # ['1.2.3+asdf - 2.4.3+asdf', '1.2.3-pre.2'], + # ['1.2.3+asdf - 2.4.3+asdf', '2.4.3-alpha'], + ['^1.2.3+build', '2.0.0'], + ['^1.2.3+build', '1.2.0'], + ['^1.2.3', '1.2.3-pre'], + ['^1.2', '1.2.0-pre'], + # ['>1.2', '1.3.0-beta'], + # ['<=1.2.3', '1.2.3-beta'], + ['^1.2.3', '1.2.3-beta'], + ['=0.7.x', '0.7.0-asdf'], + ['>=0.7.x', '0.7.0-asdf'], + + ['1.0.0', '1.0.1'], + ['>=1.0.0', '0.0.0'], + ['>=1.0.0', '0.0.1'], + ['>=1.0.0', '0.1.0'], + ['>1.0.0', '0.0.1'], + ['>1.0.0', '0.1.0'], + ['<=2.0.0', '3.0.0'], + ['<=2.0.0', '2.9999.9999'], + ['<=2.0.0', '2.2.9'], + ['<2.0.0', '2.9999.9999'], + ['<2.0.0', '2.2.9'], + ['>=0.1.97', '0.1.93'], + ['0.1.20 || 1.2.4', '1.2.3'], + ['>=0.2.3 || <0.0.1', '0.0.3'], + ['>=0.2.3 || <0.0.1', '0.2.2'], + + ['2.x.x', '3.1.3'], + ['1.2.x', '1.3.3'], + ['1.2.x || 2.x', '3.1.3'], + ['1.2.x || 2.x', '1.1.3'], + ['2.*.*', '1.1.3'], + ['2.*.*', '3.1.3'], + ['1.2.*', '1.3.3'], + ['1.2.* || 2.*', '3.1.3'], + ['1.2.* || 2.*', '1.1.3'], + ['2', '1.1.2'], + ['2.3', '2.4.1'], + + ['~0.0.1', '0.1.0-alpha'], + ['~0.0.1', '0.1.0'], + ['~2.4', '2.5.0'], + ['~2.4', '2.3.9'], + ['~>3.2.1', '3.3.2'], + ['~>3.2.1', '3.2.0'], + ['~1', '0.2.3'], + ['~>1', '2.2.3'], + ['~1.0', '1.1.0'], + ['<1', '1.0.0'], + ['>=1.2', '1.1.1'], + ['~v0.5.4-beta', '0.5.4-alpha'], + ['=0.7.x', '0.8.2'], + ['>=0.7.x', '0.6.2'], + # ['<0.7.x', '0.7.2'], + ['<1.2.3', '1.2.3-beta'], + ['=1.2.3', '1.2.3-beta'], + # ['>1.2', '1.2.8'], + # ['^0.0.1', '0.0.2-alpha'], + # ['^0.0.1', '0.0.2'], + ['^1.2.3', '2.0.0-alpha'], + ['^1.2.3', '1.2.2'], + ['^1.2', '1.1.9'], + +]) +def test_excluded(spec, version): + assert version not in RangeSpecifier(spec) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/tests/test_range_specifier.py new/dephell_specifier-0.2.1/tests/test_range_specifier.py --- old/dephell_specifier-0.1.5/tests/test_range_specifier.py 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell_specifier-0.2.1/tests/test_range_specifier.py 2019-07-07 17:57:48.000000000 +0200 @@ -0,0 +1,289 @@ +# external +import pytest + +# project +from dephell_specifier import RangeSpecifier + + +@pytest.mark.parametrize('operator, mask', [ + ('<', [1, 0, 0]), + ('<=', [1, 1, 0]), + ('==', [0, 1, 0]), + ('===', [0, 1, 0]), + ('>=', [0, 1, 1]), + ('>', [0, 0, 1]), + ('!=', [1, 0, 1]), +]) +def test_simple(operator, mask): + versions = ('1.2.3', '1.3.2', '1.4.1') + spec = RangeSpecifier(operator + '1.3.2') + for version, ok in zip(versions, mask): + assert (version in spec) == ok + + +@pytest.mark.parametrize('version, spec, ok', [ + ('2.7', '<=3.4', True), + ('2.7.1', '<=3.4', True), + ('2.7.1rc1', '<=3.4', True), + ('2.7.15', '<=3.4', True), + ('2.7.15rc1', '<=3.4', True), + + ('2.7', '>=3.4', False), + ('2.7.1', '>=3.4', False), + ('2.7.1rc1', '>=3.4', False), + ('2.7.15', '>=3.4', False), + ('2.7.15rc1', '>=3.4', False), +]) +def test_cases(version, spec, ok): + assert (version in RangeSpecifier(spec)) is ok + + +@pytest.mark.parametrize('op1, op2, mask', [ + # left + ('<', '<', [1, 0, 0, 0, 0]), + ('<', '<=', [1, 0, 0, 0, 0]), + ('<=', '<', [1, 1, 0, 0, 0]), + ('<=', '<=', [1, 1, 0, 0, 0]), + ('==', '<', [0, 1, 0, 0, 0]), + ('==', '<=', [0, 1, 0, 0, 0]), + + # center + ('>=', '<', [0, 1, 1, 0, 0]), + ('>', '<', [0, 0, 1, 0, 0]), + ('>', '<=', [0, 0, 1, 1, 0]), + ('>=', '<=', [0, 1, 1, 1, 0]), + + # right + ('>=', '==', [0, 0, 0, 1, 0]), + ('>', '==', [0, 0, 0, 1, 0]), + ('>', '>=', [0, 0, 0, 1, 1]), + ('>=', '>=', [0, 0, 0, 1, 1]), + ('>=', '>', [0, 0, 0, 0, 1]), + ('>', '>', [0, 0, 0, 0, 1]), + + # incompat + ('<=', '>=', [0, 0, 0, 0, 0]), + ('<', '>=', [0, 0, 0, 0, 0]), + ('<=', '>', [0, 0, 0, 0, 0]), + ('<', '>', [0, 0, 0, 0, 0]), + ('==', '==', [0, 0, 0, 0, 0]), + ('==', '>', [0, 0, 0, 0, 0]), + ('==', '>=', [0, 0, 0, 0, 0]), + ('<', '==', [0, 0, 0, 0, 0]), + ('<=', '==', [0, 0, 0, 0, 0]), +]) +def test_range(op1, op2, mask): + versions = ('1.2.3', '1.3.2', '1.4.1', '1.5.1', '1.6.1') + spec = RangeSpecifier(op1 + '1.3.2,' + op2 + '1.5.1') + for version, ok in zip(versions, mask): + assert (version in spec) == ok + + +# ^1.2.3 := >=1.2.3 <2.0.0 +@pytest.mark.parametrize('specv, version, ok', [ + ('1.2.3', '1.2.3', True), + ('1.2.3', '1.2.4', True), + ('1.2.3', '1.3.1', True), + ('1.2.3', '1.3.0', True), + + ('1.2.3', '1.2.2', False), + ('1.2.3', '1.1.9', False), + ('1.2.3', '1.0.0', False), + ('1.2.3', '2.0.0', False), + ('1.2.3', '3.0.0', False), + + ('1.2.0', '1.2.0', True), + ('1.2.0', '1.2.1', True), + ('1.2.0', '1.3.2', True), + + ('1.2.0', '1.1.0', False), + ('1.2.0', '0.9.0', False), + ('1.2.0', '2.0.0', False), + + ('1.0.0', '1.0.0', True), + ('1.0.0', '1.0.1', True), + ('1.0.0', '1.1.0', True), + ('1.0.0', '2.0.0', False), + ('1.0.0', '0.9.0', False), + + ('1.0', '1.0.0', True), + ('1.0', '1.0.1', True), + ('1.0', '1.1.0', True), + ('1.0', '2.0.0', False), + ('1.0', '0.9.0', False), + + ('1', '1.0.0', True), + ('1', '1.0.1', True), + ('1', '1.1.0', True), + ('1', '2.0.0', False), + ('1', '0.9.0', False), +]) +def test_caret(specv, version, ok): + spec = RangeSpecifier('^' + specv) + assert (version in spec) is ok + + +# ~1.2.3 := >=1.2.3 <1.3.0 +@pytest.mark.parametrize('specv, version, ok', [ + ('1.2.3', '1.2.3', True), + ('1.2.3', '1.2.4', True), + + ('1.2.3', '1.3.1', False), + ('1.2.3', '1.3.0', False), + ('1.2.3', '1.2.2', False), + ('1.2.3', '1.1.9', False), + ('1.2.3', '1.0.0', False), + ('1.2.3', '2.0.0', False), + ('1.2.3', '3.0.0', False), + + ('1.2.0', '1.2.0', True), + ('1.2.0', '1.2.1', True), + + ('1.2.0', '1.3.2', False), + ('1.2.0', '1.1.0', False), + ('1.2.0', '0.9.0', False), + ('1.2.0', '2.0.0', False), + + ('1.0.0', '1.0.0', True), + ('1.0.0', '1.0.1', True), + ('1.0.0', '1.1.0', False), + ('1.0.0', '2.0.0', False), + ('1.0.0', '0.9.0', False), + + ('1.0', '1.0.0', True), + ('1.0', '1.0.1', True), + ('1.0', '1.1.0', False), + ('1.0', '2.0.0', False), + ('1.0', '0.9.0', False), + + ('1', '1.0.0', True), + ('1', '1.0.1', True), + ('1', '1.1.0', True), + ('1', '2.0.0', False), + ('1', '0.9.0', False), +]) +def test_tilda(specv, version, ok): + spec = RangeSpecifier('~' + specv) + assert (version in spec) is ok + + # Ruby's pessimistic operator (~>) has the same behavior + spec = RangeSpecifier('~>' + specv) + assert (version in spec) is ok + + +# ~=1.2.3 := >=1.2.3 <1.3.0 +# ~=1.2 := >=1.2 <2.0 +@pytest.mark.parametrize('specv, version, ok', [ + ('1.2.3', '1.2.3', True), + ('1.2.3', '1.2.4', True), + + ('1.2.3', '1.3.1', False), + ('1.2.3', '1.3.0', False), + ('1.2.3', '1.2.2', False), + ('1.2.3', '1.1.9', False), + ('1.2.3', '1.0.0', False), + ('1.2.3', '2.0.0', False), + ('1.2.3', '3.0.0', False), + + ('1.2.0', '1.2.0', True), + ('1.2.0', '1.2.1', True), + + ('1.2.0', '1.3.2', False), + ('1.2.0', '1.1.0', False), + ('1.2.0', '0.9.0', False), + ('1.2.0', '2.0.0', False), + + ('1.0.0', '1.0.0', True), + ('1.0.0', '1.0.1', True), + ('1.0.0', '1.1.0', False), + ('1.0.0', '2.0.0', False), + ('1.0.0', '0.9.0', False), + + ('1.0', '1.0.0', True), + ('1.0', '1.0.1', True), + ('1.0', '1.1.0', True), + ('1.0', '2.0.0', False), + ('1.0', '0.9.0', False), +]) +def test_compat(specv, version, ok): + spec = RangeSpecifier('~=' + specv) + assert (version in spec) is ok + + +@pytest.mark.parametrize('version, ok', [ + ('2.7', True), + ('2.7.1', True), + ('2.7.6', True), + + ('2.8', False), + ('2.8.0', False), + ('3.0', False), + + ('3.2', True), + ('3.2.1', True), + ('3.3', True), + ('3.7', True), + + ('4.0', False), +]) +def test_or(version, ok): + spec = RangeSpecifier('~2.7 || ^3.2') + assert (version in spec) is ok + + +@pytest.mark.parametrize('spec, marker', [ + ('>=2.7', 'm >= "2.7"'), + ('>=2.7,<3.4', 'm >= "2.7" and m < "3.4"'), + ('>=2.7 || >=3.4', 'm >= "2.7" or m >= "3.4"'), +]) +def test_to_marker(spec, marker): + assert RangeSpecifier(spec).to_marker('m') == marker + + +@pytest.mark.parametrize('left, right, expected', [ + ('>=2.7', '<=3.4', '>=2.7,<=3.4'), + ('>=2.7', '>=3.4,<=3.7', '>=2.7,>=3.4,<=3.7'), + ('==2.7 || >=3.4', '<=3.7', '==2.7,<=3.7 || >=3.4,<=3.7'), + ('==2.7 || >=3.4', '!=3.6,<=3.7', '==2.7,!=3.6,,<=3.7 || >=3.4,!=3.6,,<=3.7'), + ('<=3.7', '==2.7 || >=3.4', '==2.7,<=3.7 || >=3.4,<=3.7'), + ('<=3.7 || !=3.6', '==2.7 || >=3.4', '<=3.7,==2.7 || <=3.7,>=3.4 || !=3.6,==2.7 || !=3.6,>=3.4'), +]) +def test_merging(left, right, expected): + spec = RangeSpecifier(left) + RangeSpecifier(right) + assert spec == RangeSpecifier(expected) + + +@pytest.mark.parametrize('spec, expected', [ + ('>=2.7', '>=2.7'), + ('>=2.7,<3.4', '>=2.7,<3.4'), + ('==2.7.* || >=3.4', '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*'), + ('==2.7.* || >=3.4,<3.8', '>=2.7,<3.8,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*'), +]) +def test_peppify_python(spec, expected): + new = RangeSpecifier(spec).peppify() + assert str(new) == str(RangeSpecifier(expected)) + + +@pytest.mark.parametrize('spec, expected', [ + ('[1.0]', '==1.0'), + + # closed intervals + ('[1.2,1.3]', '>=1.2,<=1.3'), + ('[1.0,2.0)', '>=1.0,<2.0'), + ('(1.0,2.0]', '>1.0,<=2.0'), + ('(1.0,2.0)', '>1.0,<2.0'), + + # open intervals + ('[1.5,)', '>=1.5'), + ('(,1.5]', '<=1.5'), + ('(1.5,)', '>1.5'), + ('(,1.5)', '<1.5'), + + # or-chaining of intervals + ('(,1.0],[1.2,)', '<=1.0 || >=1.2'), + ('(,1.0),[1.2,)', '<1.0 || >=1.2'), + ('(,1.0],(1.2,)', '<=1.0 || >1.2'), + ('(,1.0),(1.2,)', '<1.0 || >1.2'), +]) +def test_intervals(spec, expected): + assert str(RangeSpecifier(spec)) == str(RangeSpecifier(expected)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dephell_specifier-0.1.5/tests/test_specifier.py new/dephell_specifier-0.2.1/tests/test_specifier.py --- old/dephell_specifier-0.1.5/tests/test_specifier.py 1970-01-01 01:00:00.000000000 +0100 +++ new/dephell_specifier-0.2.1/tests/test_specifier.py 2019-03-20 14:12:54.000000000 +0100 @@ -0,0 +1,59 @@ +import pytest +from dephell_specifier import Specifier + + +@pytest.mark.parametrize('left, right, result', [ + # left + ('<1.2', '<1.4', '<1.2'), + ('<1.2', '<=1.4', '<1.2'), + ('<=1.2', '<1.4', '<=1.2'), + ('<=1.2', '<=1.4', '<=1.2'), + + # swap is not important + ('<1.4', '<1.2', '<1.2'), + ('<=1.4', '<1.2', '<1.2'), + ('<1.4', '<=1.2', '<=1.2'), + ('<=1.4', '<=1.2', '<=1.2'), + + # right + ('>1.2', '>1.4', '>1.4'), + ('>=1.2', '>1.4', '>1.4'), + ('>1.2', '>=1.4', '>=1.4'), + ('>=1.2', '>=1.4', '>=1.4'), + + # equal + ('==1.2', '<1.4', '==1.2'), + ('==1.2', '<=1.4', '==1.2'), + ('>=1.2', '==1.4', '==1.4'), + ('>1.2', '==1.4', '==1.4'), + + # common version + ('==1.2', '==1.2', '==1.2'), + ('<=1.2', '==1.2', '==1.2'), + ('>=1.2', '==1.2', '==1.2'), + ('<=1.2', '>=1.2', '==1.2'), + + # empty interval + ('<=1.2', '>=1.4', None), + ('<=1.2', '>1.4', None), + ('<1.2', '>=1.4', None), + ('==1.2', '>=1.4', None), + ('==1.2', '>1.4', None), + ('<=1.2', '==1.4', None), + ('<1.2', '==1.4', None), + + # closed interval + ('>=1.2', '<=1.4', None), + ('>1.2', '<1.4', None), + ('>=1.2', '<1.4', None), + ('>1.2', '<=1.4', None), +]) +def test_merge(left, right, result): + ls = Specifier(left) + rs = Specifier(right) + if result is None: + with pytest.raises(TypeError): + ls + rs + else: + merged = ls + rs + assert str(merged) == result