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


Reply via email to