Commit 59f12c01 authored by Jason R. Coombs's avatar Jason R. Coombs

Merge with 8.0b1 (use packaging for version specifiers)

parents 3bc1dafb 2439ceb8
......@@ -160,3 +160,4 @@ bc6655b4acf205dd9f25c702955645656077398a 6.0.1
01271e84e5125fcc4f0f368a6e21116a5722953c 6.0.2
7ea80190d494a766c6356fce85c844703964b6cc 6.1
df26609c2f614f5fc9110342e4003ee8bd95cf84 7.0
850a5c155c48b6ecfbb83b961586ea359b561522 8.0b1
......@@ -2,6 +2,19 @@
CHANGES
=======
---
8.0
---
* Implement `PEP 440 <http://legacy.python.org/dev/peps/pep-0440/>`_ within
pkg_resources and setuptools. This change
deprecates some version numbers such that they will no longer be installable
without using the ``===`` escape hatch. See `the changes to test_resources
<https://bitbucket.org/pypa/setuptools/commits/dcd552da643c4448056de84c73d56da6d70769d5#chg-setuptools/tests/test_resources.py>`_
for specific examples of version numbers and specifiers that are no longer
supported. Setuptools now "vendors" the `packaging
<https://github.com/pypa/packaging>`_ library.
---
7.0
---
......
empty:
exit 1
update-vendored:
rm -rf setuptools/_vendor/packaging
pip install -r setuptools/_vendor/vendored.txt -t setuptools/_vendor/
rm -rf setuptools/_vendor/*.{egg,dist}-info
......@@ -594,7 +594,7 @@ Requirements Parsing
requirement ::= project_name versionspec? extras?
versionspec ::= comparison version (',' comparison version)*
comparison ::= '<' | '<=' | '!=' | '==' | '>=' | '>'
comparison ::= '<' | '<=' | '!=' | '==' | '>=' | '>' | '~=' | '==='
extras ::= '[' extralist? ']'
extralist ::= identifier (',' identifier)*
project_name ::= identifier
......@@ -646,13 +646,10 @@ Requirements Parsing
The ``Requirement`` object's version specifiers (``.specs``) are internally
sorted into ascending version order, and used to establish what ranges of
versions are acceptable. Adjacent redundant conditions are effectively
consolidated (e.g. ``">1, >2"`` produces the same results as ``">1"``, and
``"<2,<3"`` produces the same results as``"<3"``). ``"!="`` versions are
consolidated (e.g. ``">1, >2"`` produces the same results as ``">2"``, and
``"<2,<3"`` produces the same results as``"<2"``). ``"!="`` versions are
excised from the ranges they fall within. The version being tested for
acceptability is then checked for membership in the resulting ranges.
(Note that providing conflicting conditions for the same version (e.g.
``"<2,>=2"`` or ``"==2,!=2"``) is meaningless and may therefore produce
bizarre results when compared with actual version number(s).)
``__eq__(other_requirement)``
A requirement compares equal to another requirement if they have
......@@ -681,10 +678,7 @@ Requirements Parsing
``specs``
A list of ``(op,version)`` tuples, sorted in ascending parsed-version
order. The `op` in each tuple is a comparison operator, represented as
a string. The `version` is the (unparsed) version number. The relative
order of tuples containing the same version numbers is undefined, since
having more than one operator for a given version is either redundant or
self-contradictory.
a string. The `version` is the (unparsed) version number.
Entry Points
......@@ -967,7 +961,7 @@ version
``ValueError`` is raised.
parsed_version
The ``parsed_version`` is a tuple representing a "parsed" form of the
The ``parsed_version`` is an object representing a "parsed" form of the
distribution's ``version``. ``dist.parsed_version`` is a shortcut for
calling ``parse_version(dist.version)``. It is used to compare or sort
distributions by version. (See the `Parsing Utilities`_ section below for
......@@ -1541,40 +1535,12 @@ Parsing Utilities
-----------------
``parse_version(version)``
Parse a project's version string, returning a value that can be used to
compare versions by chronological order. Semantically, the format is a
rough cross between distutils' ``StrictVersion`` and ``LooseVersion``
classes; if you give it versions that would work with ``StrictVersion``,
then they will compare the same way. Otherwise, comparisons are more like
a "smarter" form of ``LooseVersion``. It is *possible* to create
pathological version coding schemes that will fool this parser, but they
should be very rare in practice.
The returned value will be a tuple of strings. Numeric portions of the
version are padded to 8 digits so they will compare numerically, but
without relying on how numbers compare relative to strings. Dots are
dropped, but dashes are retained. Trailing zeros between alpha segments
or dashes are suppressed, so that e.g. "2.4.0" is considered the same as
"2.4". Alphanumeric parts are lower-cased.
The algorithm assumes that strings like "-" and any alpha string that
alphabetically follows "final" represents a "patch level". So, "2.4-1"
is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is
considered newer than "2.4-1", which in turn is newer than "2.4".
Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that
come before "final" alphabetically) are assumed to be pre-release versions,
so that the version "2.4" is considered newer than "2.4a1". Any "-"
characters preceding a pre-release indicator are removed. (In versions of
setuptools prior to 0.6a9, "-" characters were not removed, leading to the
unintuitive result that "0.2-rc1" was considered a newer version than
"0.2".)
Finally, to handle miscellaneous cases, the strings "pre", "preview", and
"rc" are treated as if they were "c", i.e. as though they were release
candidates, and therefore are not as new as a version string that does not
contain them. And the string "dev" is treated as if it were an "@" sign;
that is, a version coming before even "a" or "alpha".
Parsed a project's version string as defined by PEP 440. The returned
value will be an object that represents the version. These objects may
be compared to each other and sorted. The sorting algorithm is as defined
by PEP 440 with the addition that any version which is not a valid PEP 440
version will be considered less than any valid PEP 440 version and the
invalid versions will continue sorting using the original algorithm.
.. _yield_lines():
......@@ -1629,10 +1595,12 @@ Parsing Utilities
See ``to_filename()``.
``safe_version(version)``
Similar to ``safe_name()`` except that spaces in the input become dots, and
dots are allowed to exist in the output. As with ``safe_name()``, if you
are generating a filename from this you should replace any "-" characters
in the output with underscores.
This will return the normalized form of any PEP 440 version, if the version
string is not PEP 440 compatible than it is similar to ``safe_name()``
except that spaces in the input become dots, and dots are allowed to exist
in the output. As with ``safe_name()``, if you are generating a filename
from this you should replace any "-" characters in the output with
underscores.
``safe_extra(extra)``
Return a "safe" form of an extra's name, suitable for use in a requirement
......
......@@ -36,7 +36,7 @@ try:
except ImportError:
USER_SITE = None
DEFAULT_VERSION = "7.1"
DEFAULT_VERSION = "8.0"
DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
def _python_cmd(*args):
......
......@@ -14,6 +14,8 @@ The package resource API is designed to work with normal filesystem packages,
method.
"""
from __future__ import absolute_import
import sys
import os
import io
......@@ -73,6 +75,13 @@ try:
except ImportError:
pass
import setuptools._vendor.packaging.version
import setuptools._vendor.packaging.specifiers
packaging = setuptools._vendor.packaging
# For compatibility, expose packaging.version.parse as parse_version
parse_version = packaging.version.parse
_state_vars = {}
......@@ -1156,11 +1165,13 @@ def safe_name(name):
def safe_version(version):
"""Convert an arbitrary string to a standard version string
Spaces become dots, and all other non-alphanumeric characters become
dashes, with runs of multiple dashes condensed to a single dash.
"""
Convert an arbitrary string to a standard version string
"""
try:
# normalize the version
return str(packaging.version.Version(version))
except packaging.version.InvalidVersion:
version = version.replace(' ','.')
return re.sub('[^A-Za-z0-9.]+', '-', version)
......@@ -2080,7 +2091,7 @@ CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match
# Distribution or extra
DISTRO = re.compile(r"\s*((\w|[-.])+)").match
# ver. info
VERSION = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|[-.])+)").match
VERSION = re.compile(r"\s*(<=?|>=?|===?|!=|~=)\s*((\w|[-.*_!+])+)").match
# comma between items
COMMA = re.compile(r"\s*,").match
OBRACKET = re.compile(r"\s*\[").match
......@@ -2092,67 +2103,6 @@ EGG_NAME = re.compile(
re.VERBOSE | re.IGNORECASE
).match
component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get
def _parse_version_parts(s):
for part in component_re.split(s):
part = replace(part, part)
if not part or part=='.':
continue
if part[:1] in '0123456789':
# pad for numeric comparison
yield part.zfill(8)
else:
yield '*'+part
# ensure that alpha/beta/candidate are before final
yield '*final'
def parse_version(s):
"""Convert a version string to a chronologically-sortable key
This is a rough cross between distutils' StrictVersion and LooseVersion;
if you give it versions that would work with StrictVersion, then it behaves
the same; otherwise it acts like a slightly-smarter LooseVersion. It is
*possible* to create pathological version coding schemes that will fool
this parser, but they should be very rare in practice.
The returned value will be a tuple of strings. Numeric portions of the
version are padded to 8 digits so they will compare numerically, but
without relying on how numbers compare relative to strings. Dots are
dropped, but dashes are retained. Trailing zeros between alpha segments
or dashes are suppressed, so that e.g. "2.4.0" is considered the same as
"2.4". Alphanumeric parts are lower-cased.
The algorithm assumes that strings like "-" and any alpha string that
alphabetically follows "final" represents a "patch level". So, "2.4-1"
is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is
considered newer than "2.4-1", which in turn is newer than "2.4".
Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that
come before "final" alphabetically) are assumed to be pre-release versions,
so that the version "2.4" is considered newer than "2.4a1".
Finally, to handle miscellaneous cases, the strings "pre", "preview", and
"rc" are treated as if they were "c", i.e. as though they were release
candidates, and therefore are not as new as a version string that does not
contain them, and "dev" is replaced with an '@' so that it sorts lower than
than any other pre-release tag.
"""
parts = []
for part in _parse_version_parts(s.lower()):
if part.startswith('*'):
# remove '-' before a prerelease tag
if part < '*final':
while parts and parts[-1] == '*final-':
parts.pop()
# remove trailing zeros from each series of numeric parts
while parts and parts[-1]=='00000000':
parts.pop()
parts.append(part)
return tuple(parts)
class EntryPoint(object):
"""Object representing an advertised importable object"""
......@@ -2305,7 +2255,7 @@ class Distribution(object):
@property
def hashcmp(self):
return (
getattr(self, 'parsed_version', ()),
self.parsed_version,
self.precedence,
self.key,
_remove_md5_fragment(self.location),
......@@ -2351,11 +2301,10 @@ class Distribution(object):
@property
def parsed_version(self):
try:
if not hasattr(self, "_parsed_version"):
self._parsed_version = parse_version(self.version)
return self._parsed_version
except AttributeError:
self._parsed_version = pv = parse_version(self.version)
return pv
@property
def version(self):
......@@ -2460,7 +2409,12 @@ class Distribution(object):
def as_requirement(self):
"""Return a ``Requirement`` that matches this distribution exactly"""
return Requirement.parse('%s==%s' % (self.project_name, self.version))
if isinstance(self.parsed_version, packaging.version.Version):
spec = "%s==%s" % (self.project_name, self.parsed_version)
else:
spec = "%s===%s" % (self.project_name, self.parsed_version)
return Requirement.parse(spec)
def load_entry_point(self, group, name):
"""Return the `name` entry point of `group` or raise ImportError"""
......@@ -2712,7 +2666,7 @@ def parse_requirements(strs):
line, p, specs = scan_list(VERSION, LINE_END, line, p, (1, 2),
"version spec")
specs = [(op, safe_version(val)) for op, val in specs]
specs = [(op, val) for op, val in specs]
yield Requirement(project_name, specs, extras)
......@@ -2721,26 +2675,23 @@ class Requirement:
"""DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
self.unsafe_name, project_name = project_name, safe_name(project_name)
self.project_name, self.key = project_name, project_name.lower()
index = [
(parse_version(v), state_machine[op], op, v)
for op, v in specs
]
index.sort()
self.specs = [(op, ver) for parsed, trans, op, ver in index]
self.index, self.extras = index, tuple(map(safe_extra, extras))
self.specifier = packaging.specifiers.SpecifierSet(
",".join(["".join([x, y]) for x, y in specs])
)
self.specs = specs
self.extras = tuple(map(safe_extra, extras))
self.hashCmp = (
self.key,
tuple((op, parsed) for parsed, trans, op, ver in index),
self.specifier,
frozenset(self.extras),
)
self.__hash = hash(self.hashCmp)
def __str__(self):
specs = ','.join([''.join(s) for s in self.specs])
extras = ','.join(self.extras)
if extras:
extras = '[%s]' % extras
return '%s%s%s' % (self.project_name, extras, specs)
return '%s%s%s' % (self.project_name, extras, self.specifier)
def __eq__(self, other):
return (
......@@ -2752,29 +2703,13 @@ class Requirement:
if isinstance(item, Distribution):
if item.key != self.key:
return False
# only get if we need it
if self.index:
item = item.parsed_version
elif isinstance(item, string_types):
item = parse_version(item)
last = None
# -1, 0, 1
compare = lambda a, b: (a > b) - (a < b)
for parsed, trans, op, ver in self.index:
# Indexing: 0, 1, -1
action = trans[compare(item, parsed)]
if action == 'F':
return False
elif action == 'T':
return True
elif action == '+':
last = True
elif action == '-' or last is None:
last = False
# no rules encountered
if last is None:
last = True
return last
item = item.version
# Allow prereleases always in order to match the previous behavior of
# this method. In the future this should be smarter and follow PEP 440
# more accurately.
return self.specifier.contains(item, prereleases=True)
def __hash__(self):
return self.__hash
......@@ -2790,16 +2725,6 @@ class Requirement:
raise ValueError("Expected only one requirement", s)
raise ValueError("No requirements found", s)
state_machine = {
# =><
'<': '--T',
'<=': 'T-T',
'>': 'F+F',
'>=': 'T+F',
'==': 'T..',
'!=': 'F++',
}
def _get_mro(cls):
"""Get an mro for a type or classic class"""
......
# Copyright 2014 Donald Stufft
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
__all__ = [
"__title__", "__summary__", "__uri__", "__version__", "__author__",
"__email__", "__license__", "__copyright__",
]
__title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__version__ = "14.3"
__author__ = "Donald Stufft"
__email__ = "donald@stufft.io"
__license__ = "Apache License, Version 2.0"
__copyright__ = "Copyright 2014 %s" % __author__
# Copyright 2014 Donald Stufft
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
from .__about__ import (
__author__, __copyright__, __email__, __license__, __summary__, __title__,
__uri__, __version__
)
__all__ = [
"__title__", "__summary__", "__uri__", "__version__", "__author__",
"__email__", "__license__", "__copyright__",
]
# Copyright 2014 Donald Stufft
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import sys
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
# flake8: noqa
if PY3:
string_types = str,
else:
string_types = basestring,
def with_metaclass(meta, *bases):
"""
Create a base class with a metaclass.
"""
# This requires a bit of explanation: the basic idea is to make a dummy
# metaclass for one level of class instantiation that replaces itself with
# the actual metaclass.
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
# Copyright 2014 Donald Stufft
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
class Infinity(object):
def __repr__(self):
return "Infinity"
def __hash__(self):
return hash(repr(self))
def __lt__(self, other):
return False
def __le__(self, other):
return False
def __eq__(self, other):
return isinstance(other, self.__class__)
def __ne__(self, other):
return not isinstance(other, self.__class__)
def __gt__(self, other):
return True
def __ge__(self, other):
return True
def __neg__(self):
return NegativeInfinity
Infinity = Infinity()
class NegativeInfinity(object):
def __repr__(self):
return "-Infinity"
def __hash__(self):
return hash(repr(self))
def __lt__(self, other):
return True
def __le__(self, other):
return True
def __eq__(self, other):
return isinstance(other, self.__class__)
def __ne__(self, other):
return not isinstance(other, self.__class__)
def __gt__(self, other):
return False
def __ge__(self, other):
return False
def __neg__(self):
return Infinity
NegativeInfinity = NegativeInfinity()
This diff is collapsed.
This diff is collapsed.
......@@ -10,6 +10,13 @@ import os
import re
import sys
try:
import packaging.version
except ImportError:
# fallback to vendored version
import setuptools._vendor.packaging.version
packaging = setuptools._vendor.packaging
from setuptools import Command
from setuptools.command.sdist import sdist
from setuptools.compat import basestring, PY3, StringIO
......@@ -68,10 +75,15 @@ class egg_info(Command):
self.vtags = self.tags()
self.egg_version = self.tagged_version()
parsed_version = parse_version(self.egg_version)
try:
is_version = isinstance(parsed_version, packaging.version.Version)
spec = (
"%s==%s" if is_version else "%s===%s"
)
list(
parse_requirements('%s==%s' % (self.egg_name,
self.egg_version))
parse_requirements(spec % (self.egg_name, self.egg_version))
)
except ValueError:
raise distutils.errors.DistutilsOptionError(
......
......@@ -13,11 +13,19 @@ from distutils.core import Distribution as _Distribution
from distutils.errors import (DistutilsOptionError, DistutilsPlatformError,
DistutilsSetupError)
try:
import packaging.version
except ImportError:
# fallback to vendored version
import setuptools._vendor.packaging.version
packaging = setuptools._vendor.packaging
from setuptools.depends import Require
from setuptools.compat import basestring, PY2
from setuptools import windows_support
import pkg_resources
def _get_unpatched(cls):
"""Protect against re-patching the distutils if reloaded
......@@ -269,6 +277,27 @@ class Distribution(_Distribution):
# Some people apparently take "version number" too literally :)
self.metadata.version = str(self.metadata.version)
if self.metadata.version is not None:
try:
ver = packaging.version.Version(self.metadata.version)
normalized_version = str(ver)
if self.metadata.version != normalized_version:
warnings.warn(
"The version specified requires normalization, "
"consider using '%s' instead of '%s'." % (
normalized_version,
self.metadata.version,
)
)
self.metadata.version = normalized_version
except (packaging.version.InvalidVersion, TypeError):
warnings.warn(
"The version specified (%r) is an invalid version, this "
"may not work as expected with newer versions of "
"setuptools, pip, and PyPI. Please see PEP 440 for more "
"details." % self.metadata.version
)
def parse_command_line(self):
"""Process features after parsing command line options"""
result = _Distribution.parse_command_line(self)
......
......@@ -155,7 +155,7 @@ class TestSvnDummy(environment.ZippedEnvironment):
infile.close()
del infile
self.assertTrue("Version: 0.1.1-r1\n" in read_contents)
self.assertTrue("Version: 0.1.1.post1\n" in read_contents)
@skipIf(not test_svn._svn_check, "No SVN to text, in the first place")
def test_no_tags(self):
......
......@@ -8,6 +8,10 @@ import tempfile
import shutil
from unittest import TestCase
import setuptools._vendor.packaging.version
import setuptools._vendor.packaging.specifiers
packaging = setuptools._vendor.packaging
import pkg_resources
from pkg_resources import (parse_requirements, VersionConflict, parse_version,
Distribution, EntryPoint, Requirement, safe_version, safe_name,
......@@ -103,7 +107,7 @@ class DistroTests(TestCase):
def checkFooPkg(self,d):
self.assertEqual(d.project_name, "FooPkg")
self.assertEqual(d.key, "foopkg")
self.assertEqual(d.version, "1.3-1")
self.assertEqual(d.version, "1.3.post1")
self.assertEqual(d.py_version, "2.4")
self.assertEqual(d.platform, "win32")
self.assertEqual(d.parsed_version, parse_version("1.3-1"))
......@@ -120,9 +124,9 @@ class DistroTests(TestCase):
self.assertEqual(d.platform, None)
def testDistroParse(self):
d = dist_from_fn("FooPkg-1.3_1-py2.4-win32.egg")
d = dist_from_fn("FooPkg-1.3.post1-py2.4-win32.egg")
self.checkFooPkg(d)
d = dist_from_fn("FooPkg-1.3_1-py2.4-win32.egg-info")
d = dist_from_fn("FooPkg-1.3.post1-py2.4-win32.egg-info")
self.checkFooPkg(d)
def testDistroMetadata(self):
......@@ -330,24 +334,15 @@ class RequirementsTests(TestCase):
self.assertTrue(twist11 not in r)
self.assertTrue(twist12 in r)
def testAdvancedContains(self):
r, = parse_requirements("Foo>=1.2,<=1.3,==1.9,>2.0,!=2.5,<3.0,==4.5")
for v in ('1.2','1.2.2','1.3','1.9','2.0.1','2.3','2.6','3.0c1','4.5'):
self.assertTrue(v in r, (v,r))
for v in ('1.2c1','1.3.1','1.5','1.9.1','2.0','2.5','3.0','4.0'):
self.assertTrue(v not in r, (v,r))
def testOptionsAndHashing(self):
r1 = Requirement.parse("Twisted[foo,bar]>=1.2")
r2 = Requirement.parse("Twisted[bar,FOO]>=1.2")
r3 = Requirement.parse("Twisted[BAR,FOO]>=1.2.0")
self.assertEqual(r1,r2)
self.assertEqual(r1,r3)
self.assertEqual(r1.extras, ("foo","bar"))
self.assertEqual(r2.extras, ("bar","foo")) # extras are normalized
self.assertEqual(hash(r1), hash(r2))
self.assertEqual(
hash(r1), hash(("twisted", ((">=",parse_version("1.2")),),
hash(r1), hash(("twisted", packaging.specifiers.SpecifierSet(">=1.2"),
frozenset(["foo","bar"])))
)
......@@ -420,7 +415,7 @@ class ParseTests(TestCase):
self.assertNotEqual(safe_name("peak.web"), "peak-web")
def testSafeVersion(self):
self.assertEqual(safe_version("1.2-1"), "1.2-1")
self.assertEqual(safe_version("1.2-1"), "1.2.post1")
self.assertEqual(safe_version("1.2 alpha"), "1.2.alpha")
self.assertEqual(safe_version("2.3.4 20050521"), "2.3.4.20050521")
self.assertEqual(safe_version("Money$$$Maker"), "Money-Maker")
......@@ -454,12 +449,12 @@ class ParseTests(TestCase):
c('0.4', '0.4.0')
c('0.4.0.0', '0.4.0')
c('0.4.0-0', '0.4-0')
c('0pl1', '0.0pl1')
c('0post1', '0.0post1')
c('0pre1', '0.0c1')
c('0.0.0preview1', '0c1')
c('0.0c1', '0-rc1')
c('1.2a1', '1.2.a.1')
c('1.2...a', '1.2a')
c('1.2.a', '1.2a')
def testVersionOrdering(self):
def c(s1,s2):
......@@ -472,16 +467,14 @@ class ParseTests(TestCase):
c('2.3a1', '2.3')
c('2.1-1', '2.1-2')
c('2.1-1', '2.1.1')
c('2.1', '2.1pl4')
c('2.1', '2.1post4')
c('2.1a0-20040501', '2.1')
c('1.1', '02.1')
c('A56','B27')
c('3.2', '3.2.pl0')
c('3.2-1', '3.2pl1')
c('3.2pl1', '3.2pl1-1')
c('3.2', '3.2.post0')
c('3.2post1', '3.2post2')
c('0.4', '4.0')
c('0.0.4', '0.4.0')
c('0pl1', '0.4pl1')
c('0post1', '0.4post1')
c('2.1.0-rc1','2.1.0')
c('2.1dev','2.1a0')
......
__version__ = '7.1'
__version__ = '8.0'
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment