Hello community,

here is the log from the commit of package python-packaging for 
openSUSE:Factory checked in at 2018-03-26 12:32:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-packaging (Old)
 and      /work/SRC/openSUSE:Factory/.python-packaging.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-packaging"

Mon Mar 26 12:32:04 2018 rev:9 rq:590422 version:17.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-packaging/python-packaging.changes        
2017-05-17 10:46:13.039506671 +0200
+++ /work/SRC/openSUSE:Factory/.python-packaging.new/python-packaging.changes   
2018-03-26 12:32:11.396155035 +0200
@@ -1,0 +2,12 @@
+Thu Mar 22 20:37:57 UTC 2018 - [email protected]
+
+- Update to version 17.1
+  * Fix utils.canonicalize_version when supplying non PEP 440 versions.
+- Update to version 17.0
+  * Drop support for python 2.6, 3.2, and 3.3.
+  * Define minimal pyparsing version to 2.0.2 (#91).
+  * Add epoch, release, pre, dev, and post attributes to Version and 
LegacyVersion (#34).
+  * Add Version().is_devrelease and LegacyVersion().is_devrelease to make it 
easy to determine if a release is a development release.
+  * Add utils.canonicalize_version to canonicalize version strings or Version 
instances (#121).
+
+-------------------------------------------------------------------

Old:
----
  packaging-16.8.tar.gz

New:
----
  packaging-17.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-packaging.spec ++++++
--- /var/tmp/diff_new_pack.sFJX7G/_old  2018-03-26 12:32:13.700072322 +0200
+++ /var/tmp/diff_new_pack.sFJX7G/_new  2018-03-26 12:32:13.704072178 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-packaging
 #
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -20,7 +20,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-packaging
-Version:        16.8
+Version:        17.1
 Release:        0
 Summary:        Core utilities for Python packages
 License:        Apache-2.0
@@ -28,7 +28,7 @@
 Url:            https://github.com/pypa/packaging
 Source:         
https://pypi.io/packages/source/p/packaging/packaging-%{version}.tar.gz
 BuildRequires:  %{python_module base}
-BuildRequires:  %{python_module pyparsing}
+BuildRequires:  %{python_module pyparsing >= 2.0.2}
 BuildRequires:  %{python_module six}
 BuildRequires:  python-rpm-macros
 # do not add setuptools dependency, this is now a dependency
@@ -43,9 +43,8 @@
 #   File "/usr/lib/python2.7/site-packages/packaging/requirements.py", line 
59, in <module>
 #    MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")
 # TypeError: __call__() takes exactly 2 arguments (1 given)
-Requires:       python-pyparsing >= 2.2.0
+Requires:       python-pyparsing >= 2.0.2
 Requires:       python-six
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildArch:      noarch
 %python_subpackages
 

++++++ packaging-16.8.tar.gz -> packaging-17.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/CHANGELOG.rst 
new/packaging-17.1/CHANGELOG.rst
--- old/packaging-16.8/CHANGELOG.rst    2016-10-29 17:54:54.000000000 +0200
+++ new/packaging-17.1/CHANGELOG.rst    2018-02-28 18:51:13.000000000 +0100
@@ -1,6 +1,29 @@
 Changelog
 ---------
 
+17.1 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions.
+
+
+17.0 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Drop support for python 2.6, 3.2, and 3.3.
+
+* Define minimal pyparsing version to 2.0.2 (:issue:`91`).
+
+* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to
+  ``Version`` and ``LegacyVersion`` (:issue:`34`).
+
+* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to
+  make it easy to determine if a release is a development release.
+
+* Add ``utils.canonicalize_version`` to canonicalize version strings or
+  ``Version`` instances (:issue:`121`).
+
+
 16.8 - 2016-10-29
 ~~~~~~~~~~~~~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/PKG-INFO new/packaging-17.1/PKG-INFO
--- old/packaging-16.8/PKG-INFO 2016-10-29 17:55:00.000000000 +0200
+++ new/packaging-17.1/PKG-INFO 2018-02-28 18:51:44.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: packaging
-Version: 16.8
+Version: 17.1
 Summary: Core utilities for Python packages
 Home-page: https://github.com/pypa/packaging
 Author: Donald Stufft and individual contributors
@@ -41,6 +41,29 @@
         Changelog
         ---------
         
+        17.1 - 2017-02-28
+        ~~~~~~~~~~~~~~~~~
+        
+        * Fix ``utils.canonicalize_version`` when supplying non PEP 440 
versions.
+        
+        
+        17.0 - 2017-02-28
+        ~~~~~~~~~~~~~~~~~
+        
+        * Drop support for python 2.6, 3.2, and 3.3.
+        
+        * Define minimal pyparsing version to 2.0.2 (`#91 
<https://github.com/pypa/packaging/issues/91>`__).
+        
+        * Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` 
attributes to
+          ``Version`` and ``LegacyVersion`` (`#34 
<https://github.com/pypa/packaging/issues/34>`__).
+        
+        * Add ``Version().is_devrelease`` and 
``LegacyVersion().is_devrelease`` to
+          make it easy to determine if a release is a development release.
+        
+        * Add ``utils.canonicalize_version`` to canonicalize version strings or
+          ``Version`` instances (`#121 
<https://github.com/pypa/packaging/issues/121>`__).
+        
+        
         16.8 - 2016-10-29
         ~~~~~~~~~~~~~~~~~
         
@@ -204,14 +227,15 @@
         * Initial release.
         
 Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/docs/conf.py 
new/packaging-17.1/docs/conf.py
--- old/packaging-16.8/docs/conf.py     2016-10-29 17:54:54.000000000 +0200
+++ new/packaging-17.1/docs/conf.py     2018-02-28 14:57:14.000000000 +0100
@@ -135,7 +135,7 @@
 
 # Example configuration for intersphinx: refer to the Python standard library.
 intersphinx_mapping = {
-    "http://docs.python.org/": None,
+    "https://docs.python.org/": None,
 }
 
 epub_theme = "epub"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/docs/development/getting-started.rst 
new/packaging-17.1/docs/development/getting-started.rst
--- old/packaging-16.8/docs/development/getting-started.rst     2016-10-29 
17:54:54.000000000 +0200
+++ new/packaging-17.1/docs/development/getting-started.rst     2018-02-28 
14:57:14.000000000 +0100
@@ -37,11 +37,12 @@
 
     $ tox
     ...
-    ERROR:   py26: InterpreterNotFound: python2.6
      py27: commands succeeded
     ERROR:   pypy: InterpreterNotFound: pypy
-    ERROR:   py32: InterpreterNotFound: python3.2
-     py33: commands succeeded
+    ERROR:   py34: InterpreterNotFound: python3.4
+    ERROR:   py35: InterpreterNotFound: python3.5
+     py36: commands succeeded
+    ERROR:   py37: InterpreterNotFound: python3.7
      docs: commands succeeded
      pep8: commands succeeded
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/packaging-16.8/docs/development/submitting-patches.rst 
new/packaging-17.1/docs/development/submitting-patches.rst
--- old/packaging-16.8/docs/development/submitting-patches.rst  2016-10-29 
17:54:54.000000000 +0200
+++ new/packaging-17.1/docs/development/submitting-patches.rst  2018-02-28 
14:57:14.000000000 +0100
@@ -75,6 +75,6 @@
 * Use Sphinx parameter/attribute documentation `syntax`_.
 
 
-.. _`Write comments as complete sentences.`: 
http://nedbatchelder.com/blog/201401/comments_should_be_sentences.html
+.. _`Write comments as complete sentences.`: 
https://nedbatchelder.com/blog/201401/comments_should_be_sentences.html
 .. _`syntax`: http://sphinx-doc.org/domains.html#info-field-lists
 .. _`Studies have shown`: 
http://www.ibm.com/developerworks/rational/library/11-proven-practices-for-peer-review/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/docs/requirements.rst 
new/packaging-17.1/docs/requirements.rst
--- old/packaging-16.8/docs/requirements.rst    2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/docs/requirements.rst    2018-02-28 14:57:14.000000000 
+0100
@@ -71,7 +71,7 @@
 
     .. attribute:: extras
 
-      A list of extras that the requirement specifies.
+      A set of extras that the requirement specifies.
 
     .. attribute:: specifier
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/docs/utils.rst 
new/packaging-17.1/docs/utils.rst
--- old/packaging-16.8/docs/utils.rst   2016-10-29 17:54:54.000000000 +0200
+++ new/packaging-17.1/docs/utils.rst   2018-02-28 14:57:14.000000000 +0100
@@ -26,3 +26,16 @@
         'oslo-concurrency'
         >>> canonicalize_name("requests")
         'requests'
+
+.. function:: canonicalize_version(version)
+
+    This function takes a string representing a package version (or a
+    ``Version`` instance), and returns the normalized form of it.
+
+    :param str version: The version to normalize.
+
+    .. doctest::
+
+        >>> from packaging.utils import canonicalize_version
+        >>> canonicalize_version('1.4.0.0.0')
+        '1.4'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/docs/version.rst 
new/packaging-17.1/docs/version.rst
--- old/packaging-16.8/docs/version.rst 2016-10-29 17:54:54.000000000 +0200
+++ new/packaging-17.1/docs/version.rst 2018-02-28 14:57:14.000000000 +0100
@@ -21,6 +21,12 @@
     <Version('1.0')>
     >>> v1 < v2
     True
+    >>> v1.epoch
+    0
+    >>> v1.release
+    (1, 0)
+    >>> v1.pre
+    ('a', 5)
     >>> v1.is_prerelease
     True
     >>> v2.is_prerelease
@@ -29,8 +35,11 @@
     Traceback (most recent call last):
         ...
     InvalidVersion: Invalid version: 'french toast'
+    >>> Version("1.0").post
     >>> Version("1.0").is_postrelease
     False
+    >>> Version("1.0.post0").post
+    0
     >>> Version("1.0.post0").is_postrelease
     True
 
@@ -66,15 +75,50 @@
         instance. The base version is the public version of the project without
         any pre or post release markers.
 
+    .. attribute:: epoch
+
+        An integer giving the version epoch of this :class:`Version` instance
+
+    .. attribute:: release
+
+        A tuple of integers giving the components of the release segment of
+        this :class:`Version` instance; that is, the ``1.2.3`` part of the
+        version number, including trailing zeroes but not including the epoch
+        or any prerelease/development/postrelease suffixes
+
     .. attribute:: local
 
         A string representing the local version portion of this ``Version()``
         if it has one, or ``None`` otherwise.
 
+    .. attribute:: pre
+
+        If this :class:`Version` instance represents a prerelease, this
+        attribute will be a pair of the prerelease phase (the string ``"a"``,
+        ``"b"``, or ``"rc"``) and the prerelease number (an integer).  If this
+        instance is not a prerelease, the attribute will be `None`.
+
     .. attribute:: is_prerelease
 
         A boolean value indicating whether this :class:`Version` instance
-        represents a prerelease or a final release.
+        represents a prerelease and/or development release.
+
+    .. attribute:: dev
+
+        If this :class:`Version` instance represents a development release,
+        this attribute will be the development release number (an integer);
+        otherwise, it will be `None`.
+
+    .. attribute:: is_devrelease
+
+        A boolean value indicating whether this :class:`Version` instance
+        represents a development release.
+
+    .. attribute:: post
+
+        If this :class:`Version` instance represents a postrelease, this
+        attribute will be the postrelease number (an integer); otherwise, it
+        will be `None`.
 
     .. attribute:: is_postrelease
 
@@ -106,18 +150,57 @@
         :class:`LegacyVersion` instance. This will always be the entire version
         string.
 
+    .. attribute:: epoch
+
+        This will always be ``-1`` since without `PEP 440`_ we do not have the
+        concept of version epochs.  The value reflects the fact that
+        :class:`LegacyVersion` instances always compare less than
+        :class:`Version` instances.
+
+    .. attribute:: release
+
+        This will always be ``None`` since without `PEP 440`_ we do not have
+        the concept of a release segment or its components.  It exists
+        primarily to allow a :class:`LegacyVersion` to be used as a stand in
+        for a :class:`Version`.
+
     .. attribute:: local
 
         This will always be ``None`` since without `PEP 440`_ we do not have
         the concept of a local version. It exists primarily to allow a
         :class:`LegacyVersion` to be used as a stand in for a :class:`Version`.
 
+    .. attribute:: pre
+
+        This will always be ``None`` since without `PEP 440`_ we do not have
+        the concept of a prerelease. It exists primarily to allow a
+        :class:`LegacyVersion` to be used as a stand in for a :class:`Version`.
+
     .. attribute:: is_prerelease
 
         A boolean value indicating whether this :class:`LegacyVersion`
-        represents a prerelease or a final release. Since without `PEP 440`_
-        there is no concept of pre or final releases this will always be
-        `False` and exists for compatibility with :class:`Version`.
+        represents a prerelease and/or development release.  Since without
+        `PEP 440`_ there is no concept of pre or dev releases this will
+        always be `False` and exists for compatibility with :class:`Version`.
+
+    .. attribute:: dev
+
+        This will always be ``None`` since without `PEP 440`_ we do not have
+        the concept of a development release. It exists primarily to allow a
+        :class:`LegacyVersion` to be used as a stand in for a :class:`Version`.
+
+    .. attribute:: is_devrelease
+
+        A boolean value indicating whether this :class:`LegacyVersion`
+        represents a development release.  Since without `PEP 440`_ there is
+        no concept of dev releases this will always be `False` and exists for
+        compatibility with :class:`Version`.
+
+    .. attribute:: post
+
+        This will always be ``None`` since without `PEP 440`_ we do not have
+        the concept of a postrelease. It exists primarily to allow a
+        :class:`LegacyVersion` to be used as a stand in for a :class:`Version`.
 
     .. attribute:: is_postrelease
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging/__about__.py 
new/packaging-17.1/packaging/__about__.py
--- old/packaging-16.8/packaging/__about__.py   2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/packaging/__about__.py   2018-02-28 18:51:28.000000000 
+0100
@@ -12,7 +12,7 @@
 __summary__ = "Core utilities for Python packages"
 __uri__ = "https://github.com/pypa/packaging";
 
-__version__ = "16.8"
+__version__ = "17.1"
 
 __author__ = "Donald Stufft and individual contributors"
 __email__ = "[email protected]"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging/_structures.py 
new/packaging-17.1/packaging/_structures.py
--- old/packaging-16.8/packaging/_structures.py 2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/packaging/_structures.py 2018-02-28 14:57:14.000000000 
+0100
@@ -33,6 +33,7 @@
     def __neg__(self):
         return NegativeInfinity
 
+
 Infinity = Infinity()
 
 
@@ -65,4 +66,5 @@
     def __neg__(self):
         return Infinity
 
+
 NegativeInfinity = NegativeInfinity()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging/requirements.py 
new/packaging-17.1/packaging/requirements.py
--- old/packaging-16.8/packaging/requirements.py        2016-10-29 
17:54:54.000000000 +0200
+++ new/packaging-17.1/packaging/requirements.py        2018-02-28 
14:57:14.000000000 +0100
@@ -60,8 +60,8 @@
 MARKER_EXPR.setParseAction(
     lambda s, l, t: Marker(s[t._original_start:t._original_end])
 )
-MARKER_SEPERATOR = SEMICOLON
-MARKER = MARKER_SEPERATOR + MARKER_EXPR
+MARKER_SEPARATOR = SEMICOLON
+MARKER = MARKER_SEPARATOR + MARKER_EXPR
 
 VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER)
 URL_AND_MARKER = URL + Optional(MARKER)
@@ -70,6 +70,9 @@
     NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
 
 REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
+# pyparsing isn't thread safe during initialization, so we do it eagerly, see
+# issue #104
+REQUIREMENT.parseString("x[]")
 
 
 class Requirement(object):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging/specifiers.py 
new/packaging-17.1/packaging/specifiers.py
--- old/packaging-16.8/packaging/specifiers.py  2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/packaging/specifiers.py  2018-02-28 14:57:14.000000000 
+0100
@@ -198,7 +198,7 @@
                         (prereleases or self.prereleases)):
                     found_prereleases.append(version)
                 # Either this is not a prerelease, or we should have been
-                # accepting prereleases from the begining.
+                # accepting prereleases from the beginning.
                 else:
                     yielded = True
                     yield version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging/utils.py 
new/packaging-17.1/packaging/utils.py
--- old/packaging-16.8/packaging/utils.py       2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/packaging/utils.py       2018-02-28 18:49:49.000000000 
+0100
@@ -5,6 +5,8 @@
 
 import re
 
+from .version import InvalidVersion, Version
+
 
 _canonicalize_regex = re.compile(r"[-_.]+")
 
@@ -12,3 +14,50 @@
 def canonicalize_name(name):
     # This is taken from PEP 503.
     return _canonicalize_regex.sub("-", name).lower()
+
+
+def canonicalize_version(version):
+    """
+    This is very similar to Version.__str__, but has one subtle differences
+    with the way it handles the release segment.
+    """
+
+    try:
+        version = Version(version)
+    except InvalidVersion:
+        # Legacy versions cannot be normalized
+        return version
+
+    parts = []
+
+    # Epoch
+    if version.epoch != 0:
+        parts.append("{0}!".format(version.epoch))
+
+    # Release segment
+    # NB: This strips trailing '.0's to normalize
+    parts.append(
+        re.sub(
+            r'(\.0)+$',
+            '',
+            ".".join(str(x) for x in version.release)
+        )
+    )
+
+    # Pre-release
+    if version.pre is not None:
+        parts.append("".join(str(x) for x in version.pre))
+
+    # Post-release
+    if version.post is not None:
+        parts.append(".post{0}".format(version.post))
+
+    # Development release
+    if version.dev is not None:
+        parts.append(".dev{0}".format(version.dev))
+
+    # Local version segment
+    if version.local is not None:
+        parts.append("+{0}".format(version.local))
+
+    return "".join(parts)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging/version.py 
new/packaging-17.1/packaging/version.py
--- old/packaging-16.8/packaging/version.py     2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/packaging/version.py     2018-02-28 14:57:14.000000000 
+0100
@@ -90,6 +90,26 @@
         return self._version
 
     @property
+    def epoch(self):
+        return -1
+
+    @property
+    def release(self):
+        return None
+
+    @property
+    def pre(self):
+        return None
+
+    @property
+    def post(self):
+        return None
+
+    @property
+    def dev(self):
+        return None
+
+    @property
     def local(self):
         return None
 
@@ -101,6 +121,10 @@
     def is_postrelease(self):
         return False
 
+    @property
+    def is_devrelease(self):
+        return False
+
 
 _legacy_version_component_re = re.compile(
     r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE,
@@ -154,6 +178,7 @@
 
     return epoch, parts
 
+
 # Deliberately not anchored to the start and end of the string, to make it
 # easier for 3rd party code to reuse
 VERSION_PATTERN = r"""
@@ -237,33 +262,58 @@
         parts = []
 
         # Epoch
-        if self._version.epoch != 0:
-            parts.append("{0}!".format(self._version.epoch))
+        if self.epoch != 0:
+            parts.append("{0}!".format(self.epoch))
 
         # Release segment
-        parts.append(".".join(str(x) for x in self._version.release))
+        parts.append(".".join(str(x) for x in self.release))
 
         # Pre-release
-        if self._version.pre is not None:
-            parts.append("".join(str(x) for x in self._version.pre))
+        if self.pre is not None:
+            parts.append("".join(str(x) for x in self.pre))
 
         # Post-release
-        if self._version.post is not None:
-            parts.append(".post{0}".format(self._version.post[1]))
+        if self.post is not None:
+            parts.append(".post{0}".format(self.post))
 
         # Development release
-        if self._version.dev is not None:
-            parts.append(".dev{0}".format(self._version.dev[1]))
+        if self.dev is not None:
+            parts.append(".dev{0}".format(self.dev))
 
         # Local version segment
-        if self._version.local is not None:
-            parts.append(
-                "+{0}".format(".".join(str(x) for x in self._version.local))
-            )
+        if self.local is not None:
+            parts.append("+{0}".format(self.local))
 
         return "".join(parts)
 
     @property
+    def epoch(self):
+        return self._version.epoch
+
+    @property
+    def release(self):
+        return self._version.release
+
+    @property
+    def pre(self):
+        return self._version.pre
+
+    @property
+    def post(self):
+        return self._version.post[1] if self._version.post else None
+
+    @property
+    def dev(self):
+        return self._version.dev[1] if self._version.dev else None
+
+    @property
+    def local(self):
+        if self._version.local:
+            return ".".join(str(x) for x in self._version.local)
+        else:
+            return None
+
+    @property
     def public(self):
         return str(self).split("+", 1)[0]
 
@@ -272,27 +322,25 @@
         parts = []
 
         # Epoch
-        if self._version.epoch != 0:
-            parts.append("{0}!".format(self._version.epoch))
+        if self.epoch != 0:
+            parts.append("{0}!".format(self.epoch))
 
         # Release segment
-        parts.append(".".join(str(x) for x in self._version.release))
+        parts.append(".".join(str(x) for x in self.release))
 
         return "".join(parts)
 
     @property
-    def local(self):
-        version_string = str(self)
-        if "+" in version_string:
-            return version_string.split("+", 1)[1]
-
-    @property
     def is_prerelease(self):
-        return bool(self._version.dev or self._version.pre)
+        return self.dev is not None or self.pre is not None
 
     @property
     def is_postrelease(self):
-        return bool(self._version.post)
+        return self.post is not None
+
+    @property
+    def is_devrelease(self):
+        return self.dev is not None
 
 
 def _parse_letter_version(letter, number):
@@ -326,7 +374,7 @@
         return letter, int(number)
 
 
-_local_version_seperators = re.compile(r"[\._-]")
+_local_version_separators = re.compile(r"[\._-]")
 
 
 def _parse_local_version(local):
@@ -336,7 +384,7 @@
     if local is not None:
         return tuple(
             part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_seperators.split(local)
+            for part in _local_version_separators.split(local)
         )
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging.egg-info/PKG-INFO 
new/packaging-17.1/packaging.egg-info/PKG-INFO
--- old/packaging-16.8/packaging.egg-info/PKG-INFO      2016-10-29 
17:55:00.000000000 +0200
+++ new/packaging-17.1/packaging.egg-info/PKG-INFO      2018-02-28 
18:51:44.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: packaging
-Version: 16.8
+Version: 17.1
 Summary: Core utilities for Python packages
 Home-page: https://github.com/pypa/packaging
 Author: Donald Stufft and individual contributors
@@ -41,6 +41,29 @@
         Changelog
         ---------
         
+        17.1 - 2017-02-28
+        ~~~~~~~~~~~~~~~~~
+        
+        * Fix ``utils.canonicalize_version`` when supplying non PEP 440 
versions.
+        
+        
+        17.0 - 2017-02-28
+        ~~~~~~~~~~~~~~~~~
+        
+        * Drop support for python 2.6, 3.2, and 3.3.
+        
+        * Define minimal pyparsing version to 2.0.2 (`#91 
<https://github.com/pypa/packaging/issues/91>`__).
+        
+        * Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` 
attributes to
+          ``Version`` and ``LegacyVersion`` (`#34 
<https://github.com/pypa/packaging/issues/34>`__).
+        
+        * Add ``Version().is_devrelease`` and 
``LegacyVersion().is_devrelease`` to
+          make it easy to determine if a release is a development release.
+        
+        * Add ``utils.canonicalize_version`` to canonicalize version strings or
+          ``Version`` instances (`#121 
<https://github.com/pypa/packaging/issues/121>`__).
+        
+        
         16.8 - 2016-10-29
         ~~~~~~~~~~~~~~~~~
         
@@ -204,14 +227,15 @@
         * Initial release.
         
 Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/packaging.egg-info/requires.txt 
new/packaging-17.1/packaging.egg-info/requires.txt
--- old/packaging-16.8/packaging.egg-info/requires.txt  2016-10-29 
17:55:00.000000000 +0200
+++ new/packaging-17.1/packaging.egg-info/requires.txt  2018-02-28 
18:51:44.000000000 +0100
@@ -1,2 +1,2 @@
-pyparsing
+pyparsing>=2.0.2
 six
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/setup.py new/packaging-17.1/setup.py
--- old/packaging-16.8/setup.py 2016-10-29 17:54:54.000000000 +0200
+++ new/packaging-17.1/setup.py 2018-02-28 14:57:14.000000000 +0100
@@ -50,9 +50,15 @@
     author=about["__author__"],
     author_email=about["__email__"],
 
-    install_requires=["pyparsing", "six"],
+    python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
+
+    install_requires=[
+        "pyparsing>=2.0.2",  # Needed to avoid issue #91
+        "six",
+    ],
 
     classifiers=[
+        "Development Status :: 5 - Production/Stable",
         "Intended Audience :: Developers",
 
         "License :: OSI Approved :: Apache Software License",
@@ -60,12 +66,11 @@
 
         "Programming Language :: Python",
         "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 2.6",
         "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.2",
-        "Programming Language :: Python :: 3.3",
         "Programming Language :: Python :: 3.4",
+        "Programming Language :: Python :: 3.5",
+        "Programming Language :: Python :: 3.6",
     ],
 
     packages=[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/tests/test_utils.py 
new/packaging-17.1/tests/test_utils.py
--- old/packaging-16.8/tests/test_utils.py      2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/tests/test_utils.py      2018-02-28 18:49:49.000000000 
+0100
@@ -5,7 +5,7 @@
 
 import pytest
 
-from packaging.utils import canonicalize_name
+from packaging.utils import canonicalize_name, canonicalize_version
 
 
 @pytest.mark.parametrize(
@@ -25,3 +25,23 @@
 )
 def test_canonicalize_name(name, expected):
     assert canonicalize_name(name) == expected
+
+
[email protected](
+    ("version", "expected"),
+    [
+        ('1.4.0', '1.4'),
+        ('1.40.0', '1.40'),
+        ('1.4.0.0.00.000.0000', '1.4'),
+        ('1.0', '1'),
+        ('1.0+abc', '1+abc'),
+        ('1.0.dev0', '1.dev0'),
+        ('1.0.post0', '1.post0'),
+        ('1.0a0', '1a0'),
+        ('1.0rc0', '1rc0'),
+        ('100!0.0', '100!0'),
+        ('1.0.1-test7', '1.0.1-test7'),  # LegacyVersion is unchanged
+    ]
+)
+def test_canonicalize_version(version, expected):
+    assert canonicalize_version(version) == expected
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/tests/test_version.py 
new/packaging-17.1/tests/test_version.py
--- old/packaging-16.8/tests/test_version.py    2016-10-29 17:54:54.000000000 
+0200
+++ new/packaging-17.1/tests/test_version.py    2018-02-28 14:57:14.000000000 
+0100
@@ -264,6 +264,7 @@
         ("version", "public"),
         [
             ("1.0", "1.0"),
+            ("1.0.dev0", "1.0.dev0"),
             ("1.0.dev6", "1.0.dev6"),
             ("1.0a1", "1.0a1"),
             ("1.0a1.post5", "1.0a1.post5"),
@@ -300,6 +301,7 @@
         ("version", "base_version"),
         [
             ("1.0", "1.0"),
+            ("1.0.dev0", "1.0"),
             ("1.0.dev6", "1.0"),
             ("1.0a1", "1.0"),
             ("1.0a1.post5", "1.0"),
@@ -333,9 +335,84 @@
         assert Version(version).base_version == base_version
 
     @pytest.mark.parametrize(
+        ("version", "epoch"),
+        [
+            ("1.0", 0),
+            ("1.0.dev0", 0),
+            ("1.0.dev6", 0),
+            ("1.0a1", 0),
+            ("1.0a1.post5", 0),
+            ("1.0a1.post5.dev6", 0),
+            ("1.0rc4", 0),
+            ("1.0.post5", 0),
+            ("1!1.0", 1),
+            ("1!1.0.dev6", 1),
+            ("1!1.0a1", 1),
+            ("1!1.0a1.post5", 1),
+            ("1!1.0a1.post5.dev6", 1),
+            ("1!1.0rc4", 1),
+            ("1!1.0.post5", 1),
+            ("1.0+deadbeef", 0),
+            ("1.0.dev6+deadbeef", 0),
+            ("1.0a1+deadbeef", 0),
+            ("1.0a1.post5+deadbeef", 0),
+            ("1.0a1.post5.dev6+deadbeef", 0),
+            ("1.0rc4+deadbeef", 0),
+            ("1.0.post5+deadbeef", 0),
+            ("1!1.0+deadbeef", 1),
+            ("1!1.0.dev6+deadbeef", 1),
+            ("1!1.0a1+deadbeef", 1),
+            ("1!1.0a1.post5+deadbeef", 1),
+            ("1!1.0a1.post5.dev6+deadbeef", 1),
+            ("1!1.0rc4+deadbeef", 1),
+            ("1!1.0.post5+deadbeef", 1),
+        ],
+    )
+    def test_version_epoch(self, version, epoch):
+        assert Version(version).epoch == epoch
+
+    @pytest.mark.parametrize(
+        ("version", "release"),
+        [
+            ("1.0", (1, 0)),
+            ("1.0.dev0", (1, 0)),
+            ("1.0.dev6", (1, 0)),
+            ("1.0a1", (1, 0)),
+            ("1.0a1.post5", (1, 0)),
+            ("1.0a1.post5.dev6", (1, 0)),
+            ("1.0rc4", (1, 0)),
+            ("1.0.post5", (1, 0)),
+            ("1!1.0", (1, 0)),
+            ("1!1.0.dev6", (1, 0)),
+            ("1!1.0a1", (1, 0)),
+            ("1!1.0a1.post5", (1, 0)),
+            ("1!1.0a1.post5.dev6", (1, 0)),
+            ("1!1.0rc4", (1, 0)),
+            ("1!1.0.post5", (1, 0)),
+            ("1.0+deadbeef", (1, 0)),
+            ("1.0.dev6+deadbeef", (1, 0)),
+            ("1.0a1+deadbeef", (1, 0)),
+            ("1.0a1.post5+deadbeef", (1, 0)),
+            ("1.0a1.post5.dev6+deadbeef", (1, 0)),
+            ("1.0rc4+deadbeef", (1, 0)),
+            ("1.0.post5+deadbeef", (1, 0)),
+            ("1!1.0+deadbeef", (1, 0)),
+            ("1!1.0.dev6+deadbeef", (1, 0)),
+            ("1!1.0a1+deadbeef", (1, 0)),
+            ("1!1.0a1.post5+deadbeef", (1, 0)),
+            ("1!1.0a1.post5.dev6+deadbeef", (1, 0)),
+            ("1!1.0rc4+deadbeef", (1, 0)),
+            ("1!1.0.post5+deadbeef", (1, 0)),
+        ],
+    )
+    def test_version_release(self, version, release):
+        assert Version(version).release == release
+
+    @pytest.mark.parametrize(
         ("version", "local"),
         [
             ("1.0", None),
+            ("1.0.dev0", None),
             ("1.0.dev6", None),
             ("1.0a1", None),
             ("1.0a1.post5", None),
@@ -369,8 +446,46 @@
         assert Version(version).local == local
 
     @pytest.mark.parametrize(
+        ("version", "pre"),
+        [
+            ("1.0", None),
+            ("1.0.dev0", None),
+            ("1.0.dev6", None),
+            ("1.0a1", ('a', 1)),
+            ("1.0a1.post5", ('a', 1)),
+            ("1.0a1.post5.dev6", ('a', 1)),
+            ("1.0rc4", ('rc', 4)),
+            ("1.0.post5", None),
+            ("1!1.0", None),
+            ("1!1.0.dev6", None),
+            ("1!1.0a1", ('a', 1)),
+            ("1!1.0a1.post5", ('a', 1)),
+            ("1!1.0a1.post5.dev6", ('a', 1)),
+            ("1!1.0rc4", ('rc', 4)),
+            ("1!1.0.post5", None),
+            ("1.0+deadbeef", None),
+            ("1.0.dev6+deadbeef", None),
+            ("1.0a1+deadbeef", ('a', 1)),
+            ("1.0a1.post5+deadbeef", ('a', 1)),
+            ("1.0a1.post5.dev6+deadbeef", ('a', 1)),
+            ("1.0rc4+deadbeef", ('rc', 4)),
+            ("1.0.post5+deadbeef", None),
+            ("1!1.0+deadbeef", None),
+            ("1!1.0.dev6+deadbeef", None),
+            ("1!1.0a1+deadbeef", ('a', 1)),
+            ("1!1.0a1.post5+deadbeef", ('a', 1)),
+            ("1!1.0a1.post5.dev6+deadbeef", ('a', 1)),
+            ("1!1.0rc4+deadbeef", ('rc', 4)),
+            ("1!1.0.post5+deadbeef", None),
+        ],
+    )
+    def test_version_pre(self, version, pre):
+        assert Version(version).pre == pre
+
+    @pytest.mark.parametrize(
         ("version", "expected"),
         [
+            ("1.0.dev0", True),
             ("1.0.dev1", True),
             ("1.0a1.dev1", True),
             ("1.0b1.dev1", True),
@@ -398,6 +513,117 @@
         assert Version(version).is_prerelease is expected
 
     @pytest.mark.parametrize(
+        ("version", "dev"),
+        [
+            ("1.0", None),
+            ("1.0.dev0", 0),
+            ("1.0.dev6", 6),
+            ("1.0a1", None),
+            ("1.0a1.post5", None),
+            ("1.0a1.post5.dev6", 6),
+            ("1.0rc4", None),
+            ("1.0.post5", None),
+            ("1!1.0", None),
+            ("1!1.0.dev6", 6),
+            ("1!1.0a1", None),
+            ("1!1.0a1.post5", None),
+            ("1!1.0a1.post5.dev6", 6),
+            ("1!1.0rc4", None),
+            ("1!1.0.post5", None),
+            ("1.0+deadbeef", None),
+            ("1.0.dev6+deadbeef", 6),
+            ("1.0a1+deadbeef", None),
+            ("1.0a1.post5+deadbeef", None),
+            ("1.0a1.post5.dev6+deadbeef", 6),
+            ("1.0rc4+deadbeef", None),
+            ("1.0.post5+deadbeef", None),
+            ("1!1.0+deadbeef", None),
+            ("1!1.0.dev6+deadbeef", 6),
+            ("1!1.0a1+deadbeef", None),
+            ("1!1.0a1.post5+deadbeef", None),
+            ("1!1.0a1.post5.dev6+deadbeef", 6),
+            ("1!1.0rc4+deadbeef", None),
+            ("1!1.0.post5+deadbeef", None),
+        ],
+    )
+    def test_version_dev(self, version, dev):
+        assert Version(version).dev == dev
+
+    @pytest.mark.parametrize(
+        ("version", "expected"),
+        [
+            ("1.0", False),
+            ("1.0.dev0", True),
+            ("1.0.dev6", True),
+            ("1.0a1", False),
+            ("1.0a1.post5", False),
+            ("1.0a1.post5.dev6", True),
+            ("1.0rc4", False),
+            ("1.0.post5", False),
+            ("1!1.0", False),
+            ("1!1.0.dev6", True),
+            ("1!1.0a1", False),
+            ("1!1.0a1.post5", False),
+            ("1!1.0a1.post5.dev6", True),
+            ("1!1.0rc4", False),
+            ("1!1.0.post5", False),
+            ("1.0+deadbeef", False),
+            ("1.0.dev6+deadbeef", True),
+            ("1.0a1+deadbeef", False),
+            ("1.0a1.post5+deadbeef", False),
+            ("1.0a1.post5.dev6+deadbeef", True),
+            ("1.0rc4+deadbeef", False),
+            ("1.0.post5+deadbeef", False),
+            ("1!1.0+deadbeef", False),
+            ("1!1.0.dev6+deadbeef", True),
+            ("1!1.0a1+deadbeef", False),
+            ("1!1.0a1.post5+deadbeef", False),
+            ("1!1.0a1.post5.dev6+deadbeef", True),
+            ("1!1.0rc4+deadbeef", False),
+            ("1!1.0.post5+deadbeef", False),
+        ],
+    )
+    def test_version_is_devrelease(self, version, expected):
+        assert Version(version).is_devrelease is expected
+
+    @pytest.mark.parametrize(
+        ("version", "post"),
+        [
+            ("1.0", None),
+            ("1.0.dev0", None),
+            ("1.0.dev6", None),
+            ("1.0a1", None),
+            ("1.0a1.post5", 5),
+            ("1.0a1.post5.dev6", 5),
+            ("1.0rc4", None),
+            ("1.0.post5", 5),
+            ("1!1.0", None),
+            ("1!1.0.dev6", None),
+            ("1!1.0a1", None),
+            ("1!1.0a1.post5", 5),
+            ("1!1.0a1.post5.dev6", 5),
+            ("1!1.0rc4", None),
+            ("1!1.0.post5", 5),
+            ("1.0+deadbeef", None),
+            ("1.0.dev6+deadbeef", None),
+            ("1.0a1+deadbeef", None),
+            ("1.0a1.post5+deadbeef", 5),
+            ("1.0a1.post5.dev6+deadbeef", 5),
+            ("1.0rc4+deadbeef", None),
+            ("1.0.post5+deadbeef", 5),
+            ("1!1.0+deadbeef", None),
+            ("1!1.0.dev6+deadbeef", None),
+            ("1!1.0a1+deadbeef", None),
+            ("1!1.0a1.post5+deadbeef", 5),
+            ("1!1.0a1.post5.dev6+deadbeef", 5),
+            ("1!1.0rc4+deadbeef", None),
+            ("1!1.0.post5+deadbeef", 5),
+        ],
+    )
+    def test_version_post(self, version, post):
+        assert Version(version).post == post
+
+    @pytest.mark.parametrize(
         ("version", "expected"),
         [
             ("1.0.dev1", False),
@@ -531,14 +757,38 @@
         assert LegacyVersion(version).base_version == version
 
     @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
+    def test_legacy_version_epoch(self, version):
+        assert LegacyVersion(version).epoch == -1
+
+    @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
+    def test_legacy_version_release(self, version):
+        assert LegacyVersion(version).release is None
+
+    @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
     def test_legacy_version_local(self, version):
         assert LegacyVersion(version).local is None
 
     @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
+    def test_legacy_version_pre(self, version):
+        assert LegacyVersion(version).pre is None
+
+    @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
     def test_legacy_version_is_prerelease(self, version):
         assert not LegacyVersion(version).is_prerelease
 
     @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
+    def test_legacy_version_dev(self, version):
+        assert LegacyVersion(version).dev is None
+
+    @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
+    def test_legacy_version_is_devrelease(self, version):
+        assert not LegacyVersion(version).is_devrelease
+
+    @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
+    def test_legacy_version_post(self, version):
+        assert LegacyVersion(version).post is None
+
+    @pytest.mark.parametrize("version", VERSIONS + LEGACY_VERSIONS)
     def test_legacy_version_is_postrelease(self, version):
         assert not LegacyVersion(version).is_postrelease
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/packaging-16.8/tox.ini new/packaging-17.1/tox.ini
--- old/packaging-16.8/tox.ini  2016-10-29 17:54:54.000000000 +0200
+++ new/packaging-17.1/tox.ini  2018-02-28 14:57:14.000000000 +0100
@@ -1,33 +1,26 @@
 [tox]
-envlist = py26,py27,pypy,py32,py33,py34,docs,pep8,py2pep8
+envlist = py27,pypy,py34,py35,py36,py37,docs,pep8,py2pep8
 
 [testenv]
 deps =
     coverage
     pretend
     pytest
+    https://github.com/pypa/pip/archive/master.zip#egg=pip
+# The --ignore-installed install options is needed to install pip url
+# (needed to issue #95). This can be removed once pip 10.0 is out.
+install_command =
+    pip install {opts} {packages} --ignore-installed
 commands =
     python -m coverage run --source packaging/ -m pytest --strict {posargs}
     python -m coverage report -m --fail-under 100
-install_command =
-    pip install --find-links https://wheels.caremad.io/ {opts} {packages}
-
-# Python 2.6 doesn't support python -m on a package.
-[testenv:py26]
-commands =
-    python -m coverage.__main__ run --source packaging/ -m pytest --strict 
{posargs}
-    python -m coverage.__main__ report -m --fail-under 100
-
-# coverage.py doesn't support Python 3.2 anymore.
-[testenv:py32]
-commands =
-    py.test --strict {posargs}
 
 [testenv:pypy]
 commands =
     py.test --capture=no --strict {posargs}
 
 [testenv:docs]
+basepython = python3.6
 deps =
     sphinx
     sphinx_rtd_theme
@@ -37,14 +30,14 @@
     sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
 
 [testenv:pep8]
-basepython = python3.2
+basepython = python3.4
 deps =
     flake8
     pep8-naming
 commands = flake8 .
 
 [testenv:py2pep8]
-basepython = python2.6
+basepython = python2.7
 deps =
     flake8
     pep8-naming


Reply via email to