Commit 1a7cff66 authored by PJ Eby's avatar PJ Eby

Add a simple version parser that combines the pre-release smarts of

distutils' StrictVersion, with the flexibility of LooseVersion.  It also
deals heuristically with common concepts like alpha/beta/candidate/rc
and pre/preview, as well as '-' and 'pl' branching schemes.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041000
parent 6d3753cc
......@@ -17,7 +17,7 @@ __all__ = [
'register_loader_type', 'get_provider', 'IResourceProvider',
'ResourceManager', 'iter_distributions', 'require', 'resource_string',
'resource_stream', 'resource_filename', 'set_extraction_path',
'cleanup_resources', 'parse_requirements', # 'glob_resources'
'cleanup_resources', 'parse_requirements', 'parse_version'# 'glob_resources'
]
import sys, os, zipimport, time, re
......@@ -508,8 +508,20 @@ DISTRO = re.compile(r"\s*(\w+)").match # Distribution name
VERSION = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|\.)+)").match # version info
COMMA = re.compile(r"\s*,").match # comma between items
component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c'}.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':
yield part.zfill(8) # pad for numeric comparison
else:
yield '*'+part
yield '*final' # ensure that alpha/beta/candidate are before final
......@@ -519,12 +531,41 @@ COMMA = re.compile(r"\s*,").match # comma between items
def parse_version(s):
"""Convert a version string to a 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.
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 > "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".
Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that
come before "final" alphabetically) are assumed to be pre-release versions,
and so 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.
"""
parts = []
for part in _parse_version_parts(s.lower()):
if part.startswith('*'):
# remove trailing zeros from each series of numeric parts
while parts and parts[-1]=='00000000':
parts.pop()
parts.append(part)
return tuple(parts)
......
......@@ -31,3 +31,61 @@ class ParseTests(TestCase):
self.assertRaises(ValueError,lambda:list(parse_requirements("x\\")))
self.assertRaises(ValueError,lambda:list(parse_requirements("x==2 q")))
def testVersionOrdering(self):
def c(s1,s2):
p1, p2 = parse_version(s1),parse_version(s2)
self.failUnless(p1<p2, (s1,s2,p1,p2))
c('2.1','2.1.1')
c('2a1','2b0')
c('2a1','2.1')
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.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('0.4', '4.0')
c('0.0.4', '0.4.0')
c('0pl1', '0.4pl1')
def testVersionEquality(self):
def c(s1,s2):
p1, p2 = parse_version(s1),parse_version(s2)
self.assertEqual(p1,p2, (s1,s2,p1,p2))
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('0pre1', '0.0c1')
c('0.0.0preview1', '0c1')
c('0.0c1', '0rc1')
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