FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Gentoo > Gentoo Portage Developer

 
 
LinkBack Thread Tools
 
Old 07-18-2008, 09:41 AM
Ali Polatel
 
Default New objects cpv, pv and version to be used instead of raw strings.

Hi,
Attached patch adds objects cpv, pv and version to portage.versions. This is
meant as a thin layer over functions vercmp(), pkgcmp(), pkgsplit() and
catpkgsplit().
Using these objects instead of the mentioned functions allows us to write
cleaner code and remove deprecated stuff like:
list.sort(pkgcmp)
which won't exist in py3k.

Please comment.

---
pym/portage/versions.py | 292 +++++++++++++++++++++++++++++++++++++++++------
1 files changed, 256 insertions(+), 36 deletions(-)

diff --git a/pym/portage/versions.py b/pym/portage/versions.py
index 261fa9d..20b0d4f 100644
--- a/pym/portage/versions.py
+++ b/pym/portage/versions.py
@@ -4,6 +4,7 @@
# $Id$

import re
+import warnings

ver_regexp = re.compile("^(cvs.)?(d+)((.d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)d*)*)(-r(d+))?$")
suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(d*)$")
@@ -12,6 +13,205 @@ endversion_keys = ["pre", "p", "alpha", "beta", "rc"]

from portage.exception import InvalidData

+# builtin all() is new in Python-2.5
+# TODO Move compatibility stuff to a new module portage.compat
+# and import from it like from portage.compat import all
+from sys import hexversion
+if hexversion < 0x02050000:
+ def all(iterable):
+ for i in iterable:
+ if not bool(i):
+ return False
+ return True
+del hexversion
+
+def needs_version(func):
+ """Decorator for functions that require non-keyword arguments of type version."""
+ def func_proxy(*args, **kwargs):
+ if not all([isinstance(arg, version) for arg in args]):
+ raise TypeError("Not all non-keyword arguments are of type version")
+ return func(*args, **kwargs)
+ func_proxy.__doc__ = func.__doc__
+ return func_proxy
+
+def needs_pv(func):
+ """Decorator for functions that require non-keyword arguments of type pv."""
+ def func_proxy(*args, **kwargs):
+ if not all([isinstance(arg, pv) for arg in args]):
+ raise TypeError("Not all non-keyword arguments are of type pv")
+ return func(*args, **kwargs)
+ func_proxy.__doc__ = func.__doc__
+ return func_proxy
+
+def needs_cpv(func):
+ """Decorator for functions that require non-keyword arguments of type cpv."""
+ def func_proxy(*args, **kwargs):
+ if not all([isinstance(arg, cpv) for arg in args]):
+ raise TypeError("Not all non-keyword arguments are of type cpv")
+ return func(*args, **kwargs)
+ func_proxy.__doc__ = func.__doc__
+ return func_proxy
+
+class version(str):
+ """Represents a package version"""
+
+ __hash = None
+ __parts = ()
+ __str = ""
+
+ def __new__(cls, value):
+ m = ver_regexp.match(value)
+ if m is None:
+ raise TypeError("Syntax error in version: %s" % value)
+ else:
+ new_ver = str.__new__(cls, m.groups())
+ new_ver.__hash = hash(m.groups()) + hash(value)
+ new_ver.__parts = m.groups()
+ new_ver.__str = value
+
+ return new_ver
+
+ def __str__(self):
+ return self.__str
+
+ def __repr__(self):
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
+ id(self), self.__str)
+
+ def __hash__(self):
+ return self.__hash
+
+ def __getitem__(self, i):
+ return self.__parts[i]
+
+ def __getslice__(self, i, j):
+ return self.__parts[i:j]
+
+ def __len__(self):
+ return len(self.__parts)
+
+ @needs_version
+ def __cmp__(self, y):
+ return vercmp(self, y)
+
+ @needs_version
+ def __eq__(self, y):
+ return vercmp(self, y) == 0
+
+ @needs_version
+ def __ne__(self, y):
+ return vercmp(self, y) != 0
+
+ @needs_version
+ def __lt__(self, y):
+ return vercmp(self, y) < 0
+
+ @needs_version
+ def __le__(self, y):
+ return vercmp(self, y) <= 0
+
+ @needs_version
+ def __gt__(self, y):
+ return vercmp(self, y) > 0
+
+ @needs_version
+ def __ge__(self, y):
+ return vercmp(self, y) >= 0
+
+class pv(str):
+ """Represents a pv"""
+
+ __hash = None
+ __parts = ()
+ __str = ""
+
+ def __new__(cls, value):
+ parts = pkgsplit(value)
+ if parts is None:
+ # Ideally a TypeError should be raised here.
+ # But to fit code using this easily, fail silently.
+ return None
+ else:
+ new_pv = str.__new__(cls, parts)
+ new_pv.__hash = hash(parts) + hash(value)
+ new_pv.__parts = (parts[0], version("-".join(parts[1:])))
+ new_pv.__str = value
+
+ return new_pv
+
+ def __str__(self):
+ return self.__str
+
+ def __repr__(self):
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
+ id(self), self.__str)
+
+ def __hash__(self):
+ return self.__hash
+
+ def __getitem__(self, i):
+ return self.__parts[i]
+
+ def __getslice__(self, i, j):
+ return self.__parts[i:j]
+
+ def __len__(self):
+ return len(self.__parts)
+
+ @needs_pv
+ def __cmp__(self, y):
+ if self.__parts[0] != y.__parts[0]:
+ return None
+ else:
+ return cmp(self[1], y[1])
+
+class cpv(str):
+ """Represents a cpv"""
+
+ __hash = None
+ __parts = ()
+ __str = ""
+
+ def __new__(cls, value):
+ parts = catpkgsplit(value)
+ if parts is None:
+ # Ideally a TypeError should be raised here.
+ # But to fit code using this easily, fail silently.
+ return None
+ else:
+ new_cpv = str.__new__(cls, parts)
+ new_cpv.__hash = hash(parts) + hash(value)
+ new_cpv.__parts = (parts[0], pv("-".join(parts[1:])))
+ new_cpv.__str = value
+
+ return new_cpv
+
+ def __str__(self):
+ return self.__str
+
+ def __repr__(self):
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
+ id(self), self.__str)
+
+ def __hash__(self):
+ return self.__hash
+
+ def __getitem__(self, i):
+ return self.__parts[i]
+
+ def __getslice__(self, i, j):
+ return self.__parts[i:j]
+
+ def __len__(self):
+ return len(self.__parts)
+
+ @needs_cpv
+ def __cmp__(self, y):
+ if self[0] != y[0]:
+ return None
+ else:
+ return cmp(self[1], y[1])
+
def ververify(myver, silent=1):
if ver_regexp.match(myver):
return 1
@@ -45,43 +245,63 @@ def vercmp(ver1, ver2, silent=1):
4. None if ver1 or ver2 are invalid (see ver_regexp in portage.versions.py)
"""

- if ver1 == ver2:
- return 0
- mykey=ver1+":"+ver2
- try:
- return vercmp_cache[mykey]
- except KeyError:
- pass
- match1 = ver_regexp.match(ver1)
- match2 = ver_regexp.match(ver2)
-
- # checking that the versions are valid
- if not match1 or not match1.groups():
- if not silent:
- print "!!! syntax error in version: %s" % ver1
- return None
- if not match2 or not match2.groups():
- if not silent:
- print "!!! syntax error in version: %s" % ver2
- return None
+ if isinstance(ver1, version) and isinstance(ver2, version):
+ if ver1._str == ver2._str:
+ return 0
+ mykey = ver1._str+":"+ver2._str
+ if mykey in vercmp_cache:
+ return vercmp_cache[mykey]
+
+ group1 = ver1[:]
+ group2 = ver2[:]
+ elif isinstance(ver1, str) and isinstance(ver2, str):
+ ## Backwards compatibility
+ msg = "vercmp(str,str) is deprecated use portage.version object instead"
+ warnings.warn(msg, DeprecationWarning)
+
+ if ver1 == ver2:
+ return 0
+ mykey=ver1+":"+ver2
+ try:
+ return vercmp_cache[mykey]
+ except KeyError:
+ pass
+ match1 = ver_regexp.match(ver1)
+ match2 = ver_regexp.match(ver2)
+
+ # checking that the versions are valid
+ if not match1 or not match1.groups():
+ if not silent:
+ print "!!! syntax error in version: %s" % ver1
+ return None
+ if not match2 or not match2.groups():
+ if not silent:
+ print "!!! syntax error in version: %s" % ver2
+ return None
+
+ group1 = match1.groups()
+ group2 = match2.groups()
+ else:
+ raise TypeError(
+ "Arguments aren't of type str,str or version,version")

# shortcut for cvs ebuilds (new style)
- if match1.group(1) and not match2.group(1):
+ if group1[0] and not group2[0]:
vercmp_cache[mykey] = 1
return 1
- elif match2.group(1) and not match1.group(1):
+ elif group2[0] and not group1[0]:
vercmp_cache[mykey] = -1
return -1

# building lists of the version parts before the suffix
# first part is simple
- list1 = [int(match1.group(2))]
- list2 = [int(match2.group(2))]
+ list1 = [int(group1[1])]
+ list2 = [int(group2[1])]

# this part would greatly benefit from a fixed-length version pattern
- if len(match1.group(3)) or len(match2.group(3)):
- vlist1 = match1.group(3)[1:].split(".")
- vlist2 = match2.group(3)[1:].split(".")
+ if len(group1[2]) or len(group2[2]):
+ vlist1 = group1[2][1:].split(".")
+ vlist2 = group2[2][1:].split(".")
for i in range(0, max(len(vlist1), len(vlist2))):
# Implcit .0 is given a value of -1, so that 1.0.0 > 1.0, since it
# would be ambiguous if two versions that aren't literally equal
@@ -111,10 +331,10 @@ def vercmp(ver1, ver2, silent=1):
list2.append(int(vlist2[i].ljust(max_len, "0")))

# and now the final letter
- if len(match1.group(5)):
- list1.append(ord(match1.group(5)))
- if len(match2.group(5)):
- list2.append(ord(match2.group(5)))
+ if len(group1[4]):
+ list1.append(ord(group1[4]))
+ if len(group2[4]):
+ list2.append(ord(group2[4]))

for i in range(0, max(len(list1), len(list2))):
if len(list1) <= i:
@@ -128,8 +348,8 @@ def vercmp(ver1, ver2, silent=1):
return list1[i] - list2[i]

# main version is equal, so now compare the _suffix part
- list1 = match1.group(6).split("_")[1:]
- list2 = match2.group(6).split("_")[1:]
+ list1 = group1[5].split("_")[1:]
+ list2 = group2[5].split("_")[1:]

for i in range(0, max(len(list1), len(list2))):
# Implicit _p0 is given a value of -1, so that 1 < 1_p0
@@ -154,12 +374,12 @@ def vercmp(ver1, ver2, silent=1):
return r1 - r2

# the suffix part is equal to, so finally check the revision
- if match1.group(10):
- r1 = int(match1.group(10))
+ if group1[9]:
+ r1 = int(group1[9])
else:
r1 = 0
- if match2.group(10):
- r2 = int(match2.group(10))
+ if group2[9]:
+ r2 = int(group2[9])
else:
r2 = 0
vercmp_cache[mykey] = r1 - r2
--
1.5.6.2

--
gentoo-portage-dev@lists.gentoo.org mailing list
 
Old 07-18-2008, 11:29 AM
René 'Necoro' Neumann
 
Default New objects cpv, pv and version to be used instead of raw strings.

On Fri, 18 Jul 2008 12:41:52 +0300, Ali Polatel <hawking@gentoo.org> wrote:
> Hi,
> Attached patch adds objects cpv, pv and version to portage.versions. This
> is
> meant as a thin layer over functions vercmp(), pkgcmp(), pkgsplit() and
> catpkgsplit().
> Using these objects instead of the mentioned functions allows us to write
> cleaner code and remove deprecated stuff like:
> list.sort(pkgcmp)
> which won't exist in py3k.
>
> Please comment.

Is there a reason, why you are using "__new__" instead of "__init__"?

Regards,
René

--
gentoo-portage-dev@lists.gentoo.org mailing list
 
Old 07-24-2008, 12:12 AM
Ali Polatel
 
Default New objects cpv, pv and version to be used instead of raw strings.

Thanks for the comments.

Version 2, updates:
* Use "import sys" instead of "from sys import hexversion"
* Use super() to call functions from parent class.
* Fix initialization, used to pass m.groups() to parent object which is a
leftover from older versions where I used to subclass list instead of str.
Removed __str__ functions and __str which are useless as well.
* Added readonly attributes cvs, main_version and revision to version, pv and
cpv, package to pv, cpv and category to cpv. I think this is a better way than
making __parts available so it's guaranteed that there'll be no unwanted change
in internal state.
* version uses __init__ now. pv and cpv still use __new__ because to fit code
easily they return None when the argument isn't a valid PV or CPV instead of
raising TypeError.

Please comment.

---
pym/portage/versions.py | 332 +++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 296 insertions(+), 36 deletions(-)

diff --git a/pym/portage/versions.py b/pym/portage/versions.py
index 261fa9d..507d7a1 100644
--- a/pym/portage/versions.py
+++ b/pym/portage/versions.py
@@ -4,6 +4,7 @@
# $Id$

import re
+import warnings

ver_regexp = re.compile("^(cvs.)?(d+)((.d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)d*)*)(-r(d+))?$")
suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(d*)$")
@@ -12,6 +13,245 @@ endversion_keys = ["pre", "p", "alpha", "beta", "rc"]

from portage.exception import InvalidData

+# builtin all() is new in Python-2.5
+# TODO Move compatibility stuff to a new module portage.compat
+# and import from it like from portage.compat import all
+import sys
+if sys.hexversion < 0x02050000:
+ def all(iterable):
+ for i in iterable:
+ if not bool(i):
+ return False
+ return True
+
+def needs_version(func):
+ """Decorator for functions that require non-keyword arguments of type version."""
+ def func_proxy(*args, **kwargs):
+ if not all([isinstance(arg, version) for arg in args]):
+ raise TypeError("Not all non-keyword arguments are of type version")
+ return func(*args, **kwargs)
+ func_proxy.__doc__ = func.__doc__
+ return func_proxy
+
+def needs_pv(func):
+ """Decorator for functions that require non-keyword arguments of type pv."""
+ def func_proxy(*args, **kwargs):
+ if not all([isinstance(arg, pv) for arg in args]):
+ raise TypeError("Not all non-keyword arguments are of type pv")
+ return func(*args, **kwargs)
+ func_proxy.__doc__ = func.__doc__
+ return func_proxy
+
+def needs_cpv(func):
+ """Decorator for functions that require non-keyword arguments of type cpv."""
+ def func_proxy(*args, **kwargs):
+ if not all([isinstance(arg, cpv) for arg in args]):
+ raise TypeError("Not all non-keyword arguments are of type cpv")
+ return func(*args, **kwargs)
+ func_proxy.__doc__ = func.__doc__
+ return func_proxy
+
+class version(str):
+ """Represents a package version"""
+
+ __hash = None
+ __parts = ()
+
+ def __init__(self, value):
+ m = ver_regexp.match(value)
+ if m is None:
+ raise TypeError("Syntax error in version: %s" % value)
+ else:
+ super(version, self).__init__(value)
+ self.__hash = hash(m.groups()) + hash(value)
+ self.__parts = m.groups()
+
+ def __repr__(self):
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
+ id(self), self)
+
+ def __hash__(self):
+ return self.__hash
+
+ def __getitem__(self, i):
+ return self.__parts[i]
+
+ def __getslice__(self, i, j):
+ return self.__parts[i:j]
+
+ def __len__(self):
+ return len(self.__parts)
+
+ @needs_version
+ def __cmp__(self, y):
+ return vercmp(self, y)
+
+ @needs_version
+ def __eq__(self, y):
+ return vercmp(self, y) == 0
+
+ @needs_version
+ def __ne__(self, y):
+ return vercmp(self, y) != 0
+
+ @needs_version
+ def __lt__(self, y):
+ return vercmp(self, y) < 0
+
+ @needs_version
+ def __le__(self, y):
+ return vercmp(self, y) <= 0
+
+ @needs_version
+ def __gt__(self, y):
+ return vercmp(self, y) > 0
+
+ @needs_version
+ def __ge__(self, y):
+ return vercmp(self, y) >= 0
+
+ @property
+ def cvs(self):
+ return self.__parts[0]
+
+ @property
+ def main_version(self):
+ mv = self.__parts[1:3]
+ mv += self.__parts[4:6]
+ return "".join(mv)
+
+ @property
+ def revision(self):
+ return self.__parts[8]
+
+class pv(str):
+ """Represents a pv"""
+
+ __hash = None
+ __parts = ()
+
+ def __new__(cls, value):
+ parts = pkgsplit(value)
+ if parts is None:
+ # Ideally a TypeError should be raised here.
+ # But to fit code using this easily, fail silently.
+ return None
+ else:
+ new_pv = super(pv, cls).__new__(cls, value)
+ new_pv.__hash = hash(parts) + hash(value)
+ new_pv.__parts = (parts[0], version("-".join(parts[1:])))
+
+ return new_pv
+
+ def __repr__(self):
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
+ id(self), self)
+
+ def __hash__(self):
+ return self.__hash
+
+ def __getitem__(self, i):
+ return self.__parts[i]
+
+ def __getslice__(self, i, j):
+ return self.__parts[i:j]
+
+ def __len__(self):
+ return len(self.__parts)
+
+ @needs_pv
+ def __cmp__(self, y):
+ if self.__parts[0] != y.__parts[0]:
+ return None
+ else:
+ return cmp(self[1], y[1])
+
+ @property
+ def package(self):
+ return self.__parts[0]
+
+ @property
+ def version(self):
+ return self.__parts[1]
+
+ @property
+ def cvs(self):
+ return self.__parts[1].cvs
+
+ @property
+ def main_version(self):
+ return self.__parts[1].main_version
+
+ @property
+ def revision(self):
+ return self.__parts[1].revision
+
+class cpv(str):
+ """Represents a cpv"""
+
+ __hash = None
+ __parts = ()
+
+ def __new__(cls, value):
+ parts = catpkgsplit(value)
+ if parts is None:
+ # Ideally a TypeError should be raised here.
+ # But to fit code using this easily, fail silently.
+ return None
+ else:
+ new_cpv = super(cpv, cls).__new__(cls, value)
+ new_cpv.__hash = hash(parts) + hash(value)
+ new_cpv.__parts = (parts[0], pv("-".join(parts[1:])))
+
+ return new_cpv
+
+ def __repr__(self):
+ return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
+ id(self), self)
+
+ def __hash__(self):
+ return self.__hash
+
+ def __getitem__(self, i):
+ return self.__parts[i]
+
+ def __getslice__(self, i, j):
+ return self.__parts[i:j]
+
+ def __len__(self):
+ return len(self.__parts)
+
+ @needs_cpv
+ def __cmp__(self, y):
+ if self[0] != y[0]:
+ return None
+ else:
+ return cmp(self[1], y[1])
+
+ @property
+ def category(self):
+ return self.__parts[0]
+
+ @property
+ def package(self):
+ return self.__parts[1]
+
+ @property
+ def version(self):
+ return self.__parts[1].version
+
+ @property
+ def cvs(self):
+ return self.__parts[1].cvs
+
+ @property
+ def main_version(self):
+ return self.__parts[1].main_version
+
+ @property
+ def revision(self):
+ return self.__parts[1].revision
+
def ververify(myver, silent=1):
if ver_regexp.match(myver):
return 1
@@ -45,43 +285,63 @@ def vercmp(ver1, ver2, silent=1):
4. None if ver1 or ver2 are invalid (see ver_regexp in portage.versions.py)
"""

- if ver1 == ver2:
- return 0
- mykey=ver1+":"+ver2
- try:
- return vercmp_cache[mykey]
- except KeyError:
- pass
- match1 = ver_regexp.match(ver1)
- match2 = ver_regexp.match(ver2)
-
- # checking that the versions are valid
- if not match1 or not match1.groups():
- if not silent:
- print "!!! syntax error in version: %s" % ver1
- return None
- if not match2 or not match2.groups():
- if not silent:
- print "!!! syntax error in version: %s" % ver2
- return None
+ if isinstance(ver1, version) and isinstance(ver2, version):
+ if ver1._str == ver2._str:
+ return 0
+ mykey = ver1._str+":"+ver2._str
+ if mykey in vercmp_cache:
+ return vercmp_cache[mykey]
+
+ group1 = ver1[:]
+ group2 = ver2[:]
+ elif isinstance(ver1, str) and isinstance(ver2, str):
+ ## Backwards compatibility
+ msg = "vercmp(str,str) is deprecated use portage.version object instead"
+ warnings.warn(msg, DeprecationWarning)
+
+ if ver1 == ver2:
+ return 0
+ mykey=ver1+":"+ver2
+ try:
+ return vercmp_cache[mykey]
+ except KeyError:
+ pass
+ match1 = ver_regexp.match(ver1)
+ match2 = ver_regexp.match(ver2)
+
+ # checking that the versions are valid
+ if not match1 or not match1.groups():
+ if not silent:
+ print "!!! syntax error in version: %s" % ver1
+ return None
+ if not match2 or not match2.groups():
+ if not silent:
+ print "!!! syntax error in version: %s" % ver2
+ return None
+
+ group1 = match1.groups()
+ group2 = match2.groups()
+ else:
+ raise TypeError(
+ "Arguments aren't of type str,str or version,version")

# shortcut for cvs ebuilds (new style)
- if match1.group(1) and not match2.group(1):
+ if group1[0] and not group2[0]:
vercmp_cache[mykey] = 1
return 1
- elif match2.group(1) and not match1.group(1):
+ elif group2[0] and not group1[0]:
vercmp_cache[mykey] = -1
return -1

# building lists of the version parts before the suffix
# first part is simple
- list1 = [int(match1.group(2))]
- list2 = [int(match2.group(2))]
+ list1 = [int(group1[1])]
+ list2 = [int(group2[1])]

# this part would greatly benefit from a fixed-length version pattern
- if len(match1.group(3)) or len(match2.group(3)):
- vlist1 = match1.group(3)[1:].split(".")
- vlist2 = match2.group(3)[1:].split(".")
+ if len(group1[2]) or len(group2[2]):
+ vlist1 = group1[2][1:].split(".")
+ vlist2 = group2[2][1:].split(".")
for i in range(0, max(len(vlist1), len(vlist2))):
# Implcit .0 is given a value of -1, so that 1.0.0 > 1.0, since it
# would be ambiguous if two versions that aren't literally equal
@@ -111,10 +371,10 @@ def vercmp(ver1, ver2, silent=1):
list2.append(int(vlist2[i].ljust(max_len, "0")))

# and now the final letter
- if len(match1.group(5)):
- list1.append(ord(match1.group(5)))
- if len(match2.group(5)):
- list2.append(ord(match2.group(5)))
+ if len(group1[4]):
+ list1.append(ord(group1[4]))
+ if len(group2[4]):
+ list2.append(ord(group2[4]))

for i in range(0, max(len(list1), len(list2))):
if len(list1) <= i:
@@ -128,8 +388,8 @@ def vercmp(ver1, ver2, silent=1):
return list1[i] - list2[i]

# main version is equal, so now compare the _suffix part
- list1 = match1.group(6).split("_")[1:]
- list2 = match2.group(6).split("_")[1:]
+ list1 = group1[5].split("_")[1:]
+ list2 = group2[5].split("_")[1:]

for i in range(0, max(len(list1), len(list2))):
# Implicit _p0 is given a value of -1, so that 1 < 1_p0
@@ -154,12 +414,12 @@ def vercmp(ver1, ver2, silent=1):
return r1 - r2

# the suffix part is equal to, so finally check the revision
- if match1.group(10):
- r1 = int(match1.group(10))
+ if group1[9]:
+ r1 = int(group1[9])
else:
r1 = 0
- if match2.group(10):
- r2 = int(match2.group(10))
+ if group2[9]:
+ r2 = int(group2[9])
else:
r2 = 0
vercmp_cache[mykey] = r1 - r2
--
Regards,
Ali Polatel
 
Old 07-24-2008, 11:11 AM
René 'Necoro' Neumann
 
Default New objects cpv, pv and version to be used instead of raw strings.

For the decorators: I'm not sure if this is overkill: But in Python2.5
there is the "functools.wraps"-decorator, that takes care that the wrapped
function looks the same like the original one ...

This would look like:

def decorator (f):
@wraps(f)
def func_proxy(...):
# something
return func_proxy

And when we are not using 2.5, your current approach could be used (though
enhanced a bit to copy more data of the wrapped func):

def wraps (f):
"""Emulate functools.wraps for <python-2.5"""
def w_deco(func):
func.__doc__ = f.__doc__
func.__dict__ = f.__dict__
func.__name__ = f.__name__
return func
return w_deco

On Thu, 24 Jul 2008 03:12:50 +0300, Ali Polatel <hawking@gentoo.org> wrote:
> Thanks for the comments.
>
> Version 2, updates:
> * Use "import sys" instead of "from sys import hexversion"
> * Use super() to call functions from parent class.
> * Fix initialization, used to pass m.groups() to parent object which is a
> leftover from older versions where I used to subclass list instead of
str.
> Removed __str__ functions and __str which are useless as well.
> * Added readonly attributes cvs, main_version and revision to version, pv
> and
> cpv, package to pv, cpv and category to cpv. I think this is a better way
> than
> making __parts available so it's guaranteed that there'll be no unwanted
> change
> in internal state.
> * version uses __init__ now. pv and cpv still use __new__ because to fit
> code
> easily they return None when the argument isn't a valid PV or CPV instead
> of
> raising TypeError.
>
> Please comment.
>
> ---
> pym/portage/versions.py | 332
> +++++++++++++++++++++++++++++++++++++++++-----
> 1 files changed, 296 insertions(+), 36 deletions(-)
>
> diff --git a/pym/portage/versions.py b/pym/portage/versions.py
> index 261fa9d..507d7a1 100644
> --- a/pym/portage/versions.py
> +++ b/pym/portage/versions.py
> @@ -4,6 +4,7 @@
> # $Id$
>
> import re
> +import warnings
>
> ver_regexp =
>
re.compile("^(cvs.)?(d+)((.d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)d*)*)(-r(d+))?$")
> suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(d*)$")
> @@ -12,6 +13,245 @@ endversion_keys = ["pre", "p", "alpha", "beta", "rc"]
>
> from portage.exception import InvalidData
>
> +# builtin all() is new in Python-2.5
> +# TODO Move compatibility stuff to a new module portage.compat
> +# and import from it like from portage.compat import all
> +import sys
> +if sys.hexversion < 0x02050000:
> + def all(iterable):
> + for i in iterable:
> + if not bool(i):
> + return False
> + return True
> +
> +def needs_version(func):
> + """Decorator for functions that require non-keyword arguments of type
> version."""
> + def func_proxy(*args, **kwargs):
> + if not all([isinstance(arg, version) for arg in args]):
> + raise TypeError("Not all non-keyword arguments are of type version")
> + return func(*args, **kwargs)
> + func_proxy.__doc__ = func.__doc__
> + return func_proxy
> +
> +def needs_pv(func):
> + """Decorator for functions that require non-keyword arguments of type
> pv."""
> + def func_proxy(*args, **kwargs):
> + if not all([isinstance(arg, pv) for arg in args]):
> + raise TypeError("Not all non-keyword arguments are of type pv")
> + return func(*args, **kwargs)
> + func_proxy.__doc__ = func.__doc__
> + return func_proxy
> +
> +def needs_cpv(func):
> + """Decorator for functions that require non-keyword arguments of type
> cpv."""
> + def func_proxy(*args, **kwargs):
> + if not all([isinstance(arg, cpv) for arg in args]):
> + raise TypeError("Not all non-keyword arguments are of type cpv")
> + return func(*args, **kwargs)
> + func_proxy.__doc__ = func.__doc__
> + return func_proxy
> +
> +class version(str):
> + """Represents a package version"""
> +
> + __hash = None
> + __parts = ()
> +
> + def __init__(self, value):
> + m = ver_regexp.match(value)
> + if m is None:
> + raise TypeError("Syntax error in version: %s" % value)
> + else:
> + super(version, self).__init__(value)
> + self.__hash = hash(m.groups()) + hash(value)
> + self.__parts = m.groups()
> +
> + def __repr__(self):
> + return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
> + id(self), self)
> +
> + def __hash__(self):
> + return self.__hash
> +
> + def __getitem__(self, i):
> + return self.__parts[i]
> +
> + def __getslice__(self, i, j):
> + return self.__parts[i:j]
> +
> + def __len__(self):
> + return len(self.__parts)
> +
> + @needs_version
> + def __cmp__(self, y):
> + return vercmp(self, y)
> +
> + @needs_version
> + def __eq__(self, y):
> + return vercmp(self, y) == 0
> +
> + @needs_version
> + def __ne__(self, y):
> + return vercmp(self, y) != 0
> +
> + @needs_version
> + def __lt__(self, y):
> + return vercmp(self, y) < 0
> +
> + @needs_version
> + def __le__(self, y):
> + return vercmp(self, y) <= 0
> +
> + @needs_version
> + def __gt__(self, y):
> + return vercmp(self, y) > 0
> +
> + @needs_version
> + def __ge__(self, y):
> + return vercmp(self, y) >= 0
> +
> + @property
> + def cvs(self):
> + return self.__parts[0]
> +
> + @property
> + def main_version(self):
> + mv = self.__parts[1:3]
> + mv += self.__parts[4:6]
> + return "".join(mv)
> +
> + @property
> + def revision(self):
> + return self.__parts[8]
> +
> +class pv(str):
> + """Represents a pv"""
> +
> + __hash = None
> + __parts = ()
> +
> + def __new__(cls, value):
> + parts = pkgsplit(value)
> + if parts is None:
> + # Ideally a TypeError should be raised here.
> + # But to fit code using this easily, fail silently.
> + return None
> + else:
> + new_pv = super(pv, cls).__new__(cls, value)
> + new_pv.__hash = hash(parts) + hash(value)
> + new_pv.__parts = (parts[0], version("-".join(parts[1:])))
> +
> + return new_pv
> +
> + def __repr__(self):
> + return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
> + id(self), self)
> +
> + def __hash__(self):
> + return self.__hash
> +
> + def __getitem__(self, i):
> + return self.__parts[i]
> +
> + def __getslice__(self, i, j):
> + return self.__parts[i:j]
> +
> + def __len__(self):
> + return len(self.__parts)
> +
> + @needs_pv
> + def __cmp__(self, y):
> + if self.__parts[0] != y.__parts[0]:
> + return None
> + else:
> + return cmp(self[1], y[1])
> +
> + @property
> + def package(self):
> + return self.__parts[0]
> +
> + @property
> + def version(self):
> + return self.__parts[1]
> +
> + @property
> + def cvs(self):
> + return self.__parts[1].cvs
> +
> + @property
> + def main_version(self):
> + return self.__parts[1].main_version
> +
> + @property
> + def revision(self):
> + return self.__parts[1].revision
> +
> +class cpv(str):
> + """Represents a cpv"""
> +
> + __hash = None
> + __parts = ()
> +
> + def __new__(cls, value):
> + parts = catpkgsplit(value)
> + if parts is None:
> + # Ideally a TypeError should be raised here.
> + # But to fit code using this easily, fail silently.
> + return None
> + else:
> + new_cpv = super(cpv, cls).__new__(cls, value)
> + new_cpv.__hash = hash(parts) + hash(value)
> + new_cpv.__parts = (parts[0], pv("-".join(parts[1:])))
> +
> + return new_cpv
> +
> + def __repr__(self):
> + return "<%s object at 0x%x: %s>" % (self.__class__.__name__,
> + id(self), self)
> +
> + def __hash__(self):
> + return self.__hash
> +
> + def __getitem__(self, i):
> + return self.__parts[i]
> +
> + def __getslice__(self, i, j):
> + return self.__parts[i:j]
> +
> + def __len__(self):
> + return len(self.__parts)
> +
> + @needs_cpv
> + def __cmp__(self, y):
> + if self[0] != y[0]:
> + return None
> + else:
> + return cmp(self[1], y[1])
> +
> + @property
> + def category(self):
> + return self.__parts[0]
> +
> + @property
> + def package(self):
> + return self.__parts[1]
> +
> + @property
> + def version(self):
> + return self.__parts[1].version
> +
> + @property
> + def cvs(self):
> + return self.__parts[1].cvs
> +
> + @property
> + def main_version(self):
> + return self.__parts[1].main_version
> +
> + @property
> + def revision(self):
> + return self.__parts[1].revision
> +
> def ververify(myver, silent=1):
> if ver_regexp.match(myver):
> return 1
> @@ -45,43 +285,63 @@ def vercmp(ver1, ver2, silent=1):
> 4. None if ver1 or ver2 are invalid (see ver_regexp in
> portage.versions.py)
> """
>
> - if ver1 == ver2:
> - return 0
> - mykey=ver1+":"+ver2
> - try:
> - return vercmp_cache[mykey]
> - except KeyError:
> - pass
> - match1 = ver_regexp.match(ver1)
> - match2 = ver_regexp.match(ver2)
> -
> - # checking that the versions are valid
> - if not match1 or not match1.groups():
> - if not silent:
> - print "!!! syntax error in version: %s" % ver1
> - return None
> - if not match2 or not match2.groups():
> - if not silent:
> - print "!!! syntax error in version: %s" % ver2
> - return None
> + if isinstance(ver1, version) and isinstance(ver2, version):
> + if ver1._str == ver2._str:
> + return 0
> + mykey = ver1._str+":"+ver2._str
> + if mykey in vercmp_cache:
> + return vercmp_cache[mykey]
> +
> + group1 = ver1[:]
> + group2 = ver2[:]
> + elif isinstance(ver1, str) and isinstance(ver2, str):
> + ## Backwards compatibility
> + msg = "vercmp(str,str) is deprecated use portage.version object
> instead"
> + warnings.warn(msg, DeprecationWarning)
> +
> + if ver1 == ver2:
> + return 0
> + mykey=ver1+":"+ver2
> + try:
> + return vercmp_cache[mykey]
> + except KeyError:
> + pass
> + match1 = ver_regexp.match(ver1)
> + match2 = ver_regexp.match(ver2)
> +
> + # checking that the versions are valid
> + if not match1 or not match1.groups():
> + if not silent:
> + print "!!! syntax error in version: %s" % ver1
> + return None
> + if not match2 or not match2.groups():
> + if not silent:
> + print "!!! syntax error in version: %s" % ver2
> + return None
> +
> + group1 = match1.groups()
> + group2 = match2.groups()
> + else:
> + raise TypeError(
> + "Arguments aren't of type str,str or version,version")
>
> # shortcut for cvs ebuilds (new style)
> - if match1.group(1) and not match2.group(1):
> + if group1[0] and not group2[0]:
> vercmp_cache[mykey] = 1
> return 1
> - elif match2.group(1) and not match1.group(1):
> + elif group2[0] and not group1[0]:
> vercmp_cache[mykey] = -1
> return -1
>
> # building lists of the version parts before the suffix
> # first part is simple
> - list1 = [int(match1.group(2))]
> - list2 = [int(match2.group(2))]
> + list1 = [int(group1[1])]
> + list2 = [int(group2[1])]
>
> # this part would greatly benefit from a fixed-length version pattern
> - if len(match1.group(3)) or len(match2.group(3)):
> - vlist1 = match1.group(3)[1:].split(".")
> - vlist2 = match2.group(3)[1:].split(".")
> + if len(group1[2]) or len(group2[2]):
> + vlist1 = group1[2][1:].split(".")
> + vlist2 = group2[2][1:].split(".")
> for i in range(0, max(len(vlist1), len(vlist2))):
> # Implcit .0 is given a value of -1, so that 1.0.0 > 1.0, since it
> # would be ambiguous if two versions that aren't literally equal
> @@ -111,10 +371,10 @@ def vercmp(ver1, ver2, silent=1):
> list2.append(int(vlist2[i].ljust(max_len, "0")))
>
> # and now the final letter
> - if len(match1.group(5)):
> - list1.append(ord(match1.group(5)))
> - if len(match2.group(5)):
> - list2.append(ord(match2.group(5)))
> + if len(group1[4]):
> + list1.append(ord(group1[4]))
> + if len(group2[4]):
> + list2.append(ord(group2[4]))
>
> for i in range(0, max(len(list1), len(list2))):
> if len(list1) <= i:
> @@ -128,8 +388,8 @@ def vercmp(ver1, ver2, silent=1):
> return list1[i] - list2[i]
>
> # main version is equal, so now compare the _suffix part
> - list1 = match1.group(6).split("_")[1:]
> - list2 = match2.group(6).split("_")[1:]
> + list1 = group1[5].split("_")[1:]
> + list2 = group2[5].split("_")[1:]
>
> for i in range(0, max(len(list1), len(list2))):
> # Implicit _p0 is given a value of -1, so that 1 < 1_p0
> @@ -154,12 +414,12 @@ def vercmp(ver1, ver2, silent=1):
> return r1 - r2
>
> # the suffix part is equal to, so finally check the revision
> - if match1.group(10):
> - r1 = int(match1.group(10))
> + if group1[9]:
> + r1 = int(group1[9])
> else:
> r1 = 0
> - if match2.group(10):
> - r2 = int(match2.group(10))
> + if group2[9]:
> + r2 = int(group2[9])
> else:
> r2 = 0
> vercmp_cache[mykey] = r1 - r2
> --
> Regards,
> Ali Polatel
 
Old 07-27-2008, 03:00 AM
Zac Medico
 
Default New objects cpv, pv and version to be used instead of raw strings.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ali Polatel wrote:
> Thanks for the comments.

I haven't been able to finish reviewing your patch yet but I just
want you to know that it's still in my queue.

Zac
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)

iEYEARECAAYFAkiL5MwACgkQ/ejvha5XGaMSzACgyuAEQHMNFOjiubmTQhyuZzFa
EusAoM97a0BCfByh/gEU6Sj6X6kx9MCt
=vJa6
-----END PGP SIGNATURE-----
 

Thread Tools




All times are GMT. The time now is 05:05 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright ©2007 - 2008, www.linux-archive.org