aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonald Stufft <donald@stufft.io>2014-12-13 18:36:50 -0500
committerDonald Stufft <donald@stufft.io>2014-12-13 18:36:50 -0500
commit18fc31b1516b4493ff1925497d4a6b8bd0110809 (patch)
treec2574b5e00a5bafd6e05838b4a79a4433796288d
parentb3203c3fd58476f0bead1436bf83ef05d3288d26 (diff)
downloadexternal_python_setuptools-18fc31b1516b4493ff1925497d4a6b8bd0110809.tar.gz
external_python_setuptools-18fc31b1516b4493ff1925497d4a6b8bd0110809.tar.bz2
external_python_setuptools-18fc31b1516b4493ff1925497d4a6b8bd0110809.zip
Restore iterating over Version objects for compat with buildout
-rw-r--r--pkg_resources.py71
-rw-r--r--setuptools/tests/test_resources.py19
2 files changed, 88 insertions, 2 deletions
diff --git a/pkg_resources.py b/pkg_resources.py
index a3eef28f..e1109608 100644
--- a/pkg_resources.py
+++ b/pkg_resources.py
@@ -79,8 +79,75 @@ 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
+
+class _SetuptoolsVersionMixin(object):
+ def __iter__(self):
+ 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 old_parse_version(s):
+ 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)
+
+ # Warn for use of this function
+ warnings.warn(
+ "You have iterated over the result of "
+ "pkg_resources.parse_version. This is a legacy behavior which is "
+ "inconsistent with the new version class introduced in setuptools "
+ "8.0. That class should be used directly instead of attempting to "
+ "iterate over the result.",
+ RuntimeWarning,
+ stacklevel=1,
+ )
+
+ for part in old_parse_version(str(self)):
+ yield part
+
+
+class SetuptoolsVersion(_SetuptoolsVersionMixin, packaging.version.Version):
+ pass
+
+
+class SetuptoolsLegacyVersion(_SetuptoolsVersionMixin,
+ packaging.version.LegacyVersion):
+ pass
+
+
+def parse_version(v):
+ try:
+ return SetuptoolsVersion(v)
+ except packaging.version.InvalidVersion:
+ return SetuptoolsLegacyVersion(v)
_state_vars = {}
diff --git a/setuptools/tests/test_resources.py b/setuptools/tests/test_resources.py
index 356e1ed4..23872e5d 100644
--- a/setuptools/tests/test_resources.py
+++ b/setuptools/tests/test_resources.py
@@ -488,6 +488,25 @@ class ParseTests(TestCase):
for v2 in torture[p+1:]:
c(v2,v1)
+ def testVersionBuildout(self):
+ """
+ Buildout has a function in it's bootstrap.py that inspected the return
+ value of parse_version. The new parse_version returns a Version class
+ which needs to support this behavior, at least for now.
+ """
+ def buildout(parsed_version):
+ _final_parts = '*final-', '*final'
+
+ def _final_version(parsed_version):
+ for part in parsed_version:
+ if (part[:1] == '*') and (part not in _final_parts):
+ return False
+ return True
+ return _final_version(parsed_version)
+
+ self.assertTrue(buildout(parse_version("1.0")))
+ self.assertFalse(buildout(parse_version("1.0a1")))
+
class ScriptHeaderTests(TestCase):
non_ascii_exe = '/Users/José/bin/python'