Repository: dephell/dephell_specifier

Issue:
Retrieving input out of Specifier('==1.0-1')
`str(Specifier('==1.0-1'))` is `==1.0-1` however 

`str(Specifier('==1.0-1').version)` is `1.0.post1`

To get `1.0-1` back out, I need to do `str(Specifier('==1.0-1')._spec.version)`

`1.0-1` is `1.0.post1` in equivalent in recent versions of Python libraries, but doesnt hold true in other packaging version systems.

I may be trying to use @dephell out of its intended scope, going a bit further even than https://github.com/dephell/dephell/issues/98 .  c.f. https://github.com/dephell/dephell_specifier/issues/3

I am hoping to use dephell for https://gitlab.com/coala/package_manager , specifically https://gitlab.com/coala/package_manager/issues/6 .  We support a rather large list of package managers ; c.f. https://gitlab.com/coala/package_manager/tree/master/dependency_management/requirements , but have not parsed the `version` field passed to our classes, until now.  It has been approximately a specifier, but also held discrete versions.  That is easy enough to handle using `dephell_specifier`.

With a few exceptions (fixable with https://github.com/dephell/dephell_specifier/issues/3), dephell_specifier seems to be quite suitable.

Except I need to be able to get the version back out, without Python-specific rewriting of the version string (i.e. the `-1' -> `.post`` transform), and ideally avoiding `_` underscore prefixed values.

Perhaps adding a property `raw_version` that returns `self._spec.version` ?

Discussion:

Patch:
diff --git a/dephell_specifier/constants.py b/dephell_specifier/constants.py
index 6895c4b..7060a13 100644
--- a/dephell_specifier/constants.py
+++ b/dephell_specifier/constants.py
@@ -13,4 +13,4 @@ PYTHONS_POPULAR = ('3.5', '3.6', '3.7')
 PYTHONS_UNRELEASED = ('3.8', '4.0')
 PYTHONS = PYTHONS_POPULAR + PYTHONS_DEPRECATED + PYTHONS_UNRELEASED
 
-OPERATOR_SYMBOLS = '><=[]()~^,'
+OPERATOR_SYMBOLS = '!><=[]()~^,'
diff --git a/dephell_specifier/range_specifier.py b/dephell_specifier/range_specifier.py
index 8f226a6..9e4dc56 100644
--- a/dephell_specifier/range_specifier.py
+++ b/dephell_specifier/range_specifier.py
@@ -31,7 +31,7 @@ class RangeSpecifier:
             return
 
         # split `(,1),(2,)` on `(,1)` and `(2,)`
-        subspecs = REX_MAVEN_INTERVAL.sub(r'\1|\2', spec).split('|')
+        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
diff --git a/dephell_specifier/specifier.py b/dephell_specifier/specifier.py
index 610a3e6..5a3b77b 100644
--- a/dephell_specifier/specifier.py
+++ b/dephell_specifier/specifier.py
@@ -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 @@ class Specifier:
                     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 @@ class Specifier:
         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 @@ class Specifier:
         # 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 @@ class Specifier:
 
         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)


Test patch:
diff --git a/tests/test_range_specifier.py b/tests/test_range_specifier.py
index b0b4c21..755bbed 100644
--- a/tests/test_range_specifier.py
+++ b/tests/test_range_specifier.py
@@ -12,6 +12,7 @@ from dephell_specifier import RangeSpecifier
     ('===', [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')