Merge genscripts revision 191
[gentoolkit.git] / pym / gentoolkit / cpv.py
1 #!/usr/bin/python
2 #
3 # Copyright 2009-2010 Gentoo Foundation
4 #
5 # Licensed under the GNU General Public License, v2
6 #
7 # $Header$
8
9 """Provides attributes and methods for a category/package-version string."""
10
11 __all__ = ('CPV',)
12
13 # =======
14 # Imports
15 # =======
16
17 from portage.versions import catpkgsplit, vercmp
18
19 from gentoolkit import errors
20
21 # =======
22 # Classes
23 # =======
24
25 class CPV(object):
26         """Provides methods on a category/package-version string.
27
28         Will also correctly split just a package or package-version string.
29
30         Example usage:
31                 >>> from gentoolkit.cpv import CPV
32                 >>> cpv = CPV('sys-apps/portage-2.2-r1')
33                 >>> cpv.category, cpv.name, cpv.fullversion
34                 ('sys-apps', 'portage', '2.2-r1')
35                 >>> str(cpv)
36                 'sys-apps/portage-2.2-r1'
37                 >>> # An 'rc' (release candidate) version is less than non 'rc' version:
38                 ... CPV('sys-apps/portage-2') > CPV('sys-apps/portage-2_rc10')
39                 True
40         """
41
42         def __init__(self, cpv):
43                 self.cpv = cpv
44
45                 values = split_cpv(cpv)
46                 self.category = values[0]
47                 self.name = values[1]
48                 self.version = values[2]
49                 self.revision = values[3]
50                 del values
51
52                 if not self.name:
53                         raise errors.GentoolkitInvalidCPV(cpv)
54
55                 sep = '/' if self.category else ''
56                 self.cp = sep.join((self.category, self.name))
57
58                 sep = '-' if self.revision else ''
59                 self.fullversion = sep.join((self.version, self.revision))
60                 del sep
61
62         def __eq__(self, other):
63                 if not isinstance(other, self.__class__):
64                         return False
65                 return self.cpv == other.cpv
66
67         def __ne__(self, other):
68                 return not self == other
69
70         def __lt__(self, other):
71                 if not isinstance(other, self.__class__):
72                         raise TypeError("other isn't of %s type, is %s" % (
73                                 self.__class__, other.__class__)
74                         )
75
76                 if self.category != other.category:
77                         return self.category < other.category
78                 elif self.name != other.name:
79                         return self.name < other.name
80                 else:
81                         # FIXME: this cmp() hack is for vercmp not using -1,0,1
82                         # See bug 266493; this was fixed in portage-2.2_rc31
83                         #return vercmp(self.fullversion, other.fullversion)
84                         result = cmp(vercmp(self.fullversion, other.fullversion), 0)
85                         if result == -1:
86                                 return True
87                         else:
88                                 return False
89
90         def __gt__(self, other):
91                 if not isinstance(other, self.__class__):
92                         raise TypeError("other isn't of %s type, is %s" % (
93                                 self.__class__, other.__class__)
94                         )
95                 return not self <= other
96
97         def __le__(self, other):
98                 if not isinstance(other, self.__class__):
99                         raise TypeError("other isn't of %s type, is %s" % (
100                                 self.__class__, other.__class__)
101                         )
102                 return self < other or self == other
103
104         def __ge__(self, other):
105                 if not isinstance(other, self.__class__):
106                         raise TypeError("other isn't of %s type, is %s" % (
107                                 self.__class__, other.__class__)
108                         )
109                 return self > other or self == other
110
111         def __repr__(self):
112                 return "<%s %r>" % (self.__class__.__name__, str(self))
113
114         def __str__(self):
115                 return self.cpv
116
117
118 # =========
119 # Functions
120 # =========
121
122 def split_cpv(cpv):
123         """Split a cpv into category, name, version and revision.
124
125         Inlined from helpers because of circular imports.
126
127         @todo: this function is slow and accepts some crazy things for cpv
128         @type cpv: str
129         @param cpv: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex
130         @rtype: tuple
131         @return: (category, pkg_name, version, revision)
132                 Each tuple element is a string or empty string ("").
133         """
134
135         result = catpkgsplit(cpv)
136
137         if result:
138                 result = list(result)
139                 if result[0] == 'null':
140                         result[0] = ''
141                 if result[3] == 'r0':
142                         result[3] = ''
143         else:
144                 result = cpv.split("/")
145                 if len(result) == 1:
146                         result = ['', cpv, '', '']
147                 else:
148                         result = result + ['', '']
149
150         return tuple(result)
151
152 # vim: set ts=4 sw=4 tw=79: