Entry
Parsing sys.version
Jul 5th, 2000 09:58
Nathan Wallace, Hans Nowak, Snippet 9, Michael P. Reilly
"""
Packages: text.parsing
"""
"""
: What's the best way to test the Python version in a script? I'm
: thinking of a function like "require 5.004;" in Perl, something like
: this:
: def require_python_version(what_I_want):
: what_I_want = float(what_I_want)
: actual_version = some_mangling_of(sys.version)
: if actual_version < what_I_want:
: print "You lose, buster."
: sys.exit(1)
: Currently, I can test for 1.5 compliance as a string match with
:>>> hasattr(sys, 'version') and sys.version[:3] >= '1.5'
: but that might not necessarily work with every version somebody might have
: out there, or with future versions.
: For reference, my sys.version says:
: 1.5.1 (#1, May 29 1998, 12:50:41) [GCC 2.7.2.3]
: The double decimal point makes it unparseable as a number, unless you
: drop the second decimal and convert it to 1.51. But what to do about
: beta versions, which will sort alphabetically after their non-beta
: counterparts? Drop the second number by 1 if you see the word "beta" or
: the letter "b"?
I had a similar problem with CVS/RCS numbers. I created a class where
partial order comparisions can be performed. This is the same with
(many) release numbers, including Python (so far). You could possibly
use this as-is, but should take out the CVS/RCS specific idiosyncrasies.
Especially the distinction between a "branch" number and a "revision"
number.
"""
import string
class Revision:
def __init__(self, number):
if not number:
number = '1'
if hasattr(number, '__class__') and number.__class__ is Revision:
self.path = number.path[:]
self.type = number.type
self.number = number.number
elif type(number) in (type(()), type([])):
self.path = map(None, number)
self._join(number)
self._settype()
else:
self.number = self._normalize(str(number))
self._split(self.number)
self._settype()
def _split(self, number):
parts = map(string.atoi, string.splitfields(number, '.'))
self.path = parts
def _join(self, path):
parts = string.joinfields(map(str, path), '.')
self.number = parts
def _settype(self):
if (len(self.path) % 2) == 0:
self.type = 'r'
else:
self.type = 'b'
def _normalize(self, number):
pos = string.rfind(number, '.0.')
if pos == -1:
return number
else:
return number[:pos] + number[pos+2:] # keep one dot
def __len__(self):
"""shows the depth"""
return (len(self.path) + 1) / 2
def __hash__(self):
return hash(self.number)
def __cmp__(self, other):
if self.type != other.type:
raise TypeError, "Not the same type"
ps, po = self.path, other.path
ls, lo = len(ps), len(po)
minimum = min(ls, lo)
val = cmp(ps[:minimum], po[:minimum])
if val == 0:
val = cmp(ls, lo)
return val
def up(self):
return Revision(self.path[:-1])
def next(self):
p = self.path[:]
p[-1] = p[-1] + 1
return Revision(p)
def previous(self):
p = self.path[:]
p[-1] = p[-1] - 1
if p[-1] <= 0:
return self.up()
else:
return Revision(p)
def branch(self):
if self.type == 'b':
raise TypeError, "Invalid type"
return self.up()
def getpath(self):
if self.type == 'r':
list = (self,)
rev = self.up()
else:
list = ()
rev = self
for i in range(len(self)-1):
list = list + (rev,)
rev = rev.up().up()
list = list + (rev,) # root
return tuple(list)
def is_in(self, other):
if len(self) > len(other):
return 0
else:
return self.path == other.path[:len(self.path)]
def __str__(self):
return self.number
def __repr__(self):
if self.type == 'b':
type='branch'
else:
type='revision'
return '<%s %s>' % (type, self.number)
import sys
rv = Revision("1.5.1")
print rv