/[gentoo-src]/portage/pym/portage_core.py
Gentoo

Contents of /portage/pym/portage_core.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (show annotations) (download) (as text)
Wed Oct 29 16:19:40 2003 UTC (11 years ago) by carpaski
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +1 -1 lines
File MIME type: text/x-python
FILE REMOVED
These aren't used.

1 # Copyright 1999-200 Gentoo Technologies, Inc.
2 # Distributed under the terms of the GNU General Public License, v2 or later
3 # Author: Daniel Robbins <drobbins@gentoo.org>
4 # $Header: /home/cvsroot/gentoo-src/portage/pym/portage_core.py,v 1.5 2001/11/18 07:35:44 drobbins Exp $
5
6 import string
7 import re
8
9 endversion={"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1}
10
11
12 class eid:
13 """
14 An eid is an ebuild/package id, consisting of a category, package, version
15 and revision. If validate is set to 1 (the default), the init code will
16 validate the input. If set to zero, the input is assumed correct, but an
17 exception will be raised if it is not.
18
19 An eid, or ebuild id, is an object that stores category, package, version
20 and revision information. eids can be valid or invalid. If validate is
21 set to 1 (the default), the init code will validate the input. If set to
22 zero, the input is assumed correct, but an exception will be raised if it
23 is not. If invalid, the eid will set an internal variable called "error"
24 to a human-readable error message describing what is wrong with the eid.
25 Note that eids aren't tied to a particular ebuild file or package database
26 entry; instead they're used by Portage to talk about particular
27 category/package-version-revs in the abstract. Because all the information
28 about a particular ebuild/db entry/.tbz2 package is stored in a single
29 object, a lot of redundancy is eliminated. Also, the eid methods make good
30 use of caching in order to optimize performance for version comparisons,
31 etc.
32
33 Example use:
34
35 >>> from portage_core import *
36 >>> a=eid("sys-apps/foo-1.0")
37 >>> a.valid
38 1
39 >>> a.version
40 '1.0'
41 >>> a.revision
42 '0'
43 >>> a.category
44 'sys-apps'
45 >>> b=eid("sys-apps/foo-1.1")
46 >>> a>b
47 0
48 >>> a<b
49 1
50 >>> c=eid("sys-bad/packagename-1.0p3")
51 >>> c.valid
52 0
53 >>> c.error
54 'sys-bad/packagename-1.0p3: Invalid ending version part'
55
56 (NOTE: the version string should be "1.0_p3", not "1.0p3".)
57
58 Data Definitions:
59
60 eid.repr: A human-readable represenation of the eid data, i.e. "sys-apps/foo-1.0-r1"
61 eid.category: category, i.e. "sys-apps"
62 eid.package: package, i.e. "foo"
63 eid.version: version, i.e. "1.0"
64 eid.revision: revision, i.e. "1"
65 eid.valid: boolean specifying whether this is a valid eid, i.e. 1
66 eid.error: a human-readable error message relating to a non-true eid.valid, i.e. "Invalid version part"
67 eid.cmp: cached eid comparison data
68
69 MORE TESTS:
70
71 >>> a=eid("sys-apps/foo-3.0")
72 >>> b=eid("sys-apps/foo-1.0_rc6")
73 >>> b.debug(0), b.valid, a>b, a<b
74 ([None, 'sys-apps', 'foo', '1.0_rc6', '0', 1, 'sys-apps/foo-1.0_rc6', None], 1, 1, 0)
75 >>> b=eid("sys-apps/foo-4.0_pre12")
76 >>> b.debug(0), b.valid, a>b, a<b
77 ([None, 'sys-apps', 'foo', '4.0_pre12', '0', 1, 'sys-apps/foo-4.0_pre12', None], 1, 0, 1)
78 >>> b=eid("sys-apps/foo-4.0")
79 >>> b.debug(0), b.valid, a>b, a<b
80 ([None, 'sys-apps', 'foo', '4.0', '0', 1, 'sys-apps/foo-4.0', None], 1, 0, 1)
81 >>> b=eid("sys-apps/foo-9.12.13")
82 >>> b.debug(0), b.valid, a>b, a<b
83 ([None, 'sys-apps', 'foo', '9.12.13', '0', 1, 'sys-apps/foo-9.12.13', None], 1, 0, 1)
84 >>> b=eid("sys-apps/foo-0.9.10")
85 >>> b.debug(0), b.valid, a>b, a<b
86 ([None, 'sys-apps', 'foo', '0.9.10', '0', 1, 'sys-apps/foo-0.9.10', None], 1, 1, 0)
87 >>> b=eid("sys-apps/foo-1.0.9-r1")
88 >>> b.debug(0), b.valid, a>b, a<b
89 ([None, 'sys-apps', 'foo', '1.0.9', '1', 1, 'sys-apps/foo-1.0.9-r1', None], 1, 1, 0)
90 >>> b=eid("sys-apps/foo-3.0")
91 >>> b.debug(0), b.valid, a>b, a<b
92 ([None, 'sys-apps', 'foo', '3.0', '0', 1, 'sys-apps/foo-3.0', None], 1, 0, 0)
93 >>> b=eid("sys-apps/foo-3.0_alpha1")
94 >>> b.debug(0), b.valid, a>b, a<b
95 ([None, 'sys-apps', 'foo', '3.0_alpha1', '0', 1, 'sys-apps/foo-3.0_alpha1', None], 1, 1, 0)
96 >>> b=eid("sys-apps/foo-3.0_beta")
97 >>> b.debug(0), b.valid, a>b, a<b
98 ([None, 'sys-apps', 'foo', '3.0_beta', '0', 1, 'sys-apps/foo-3.0_beta', None], 1, 1, 0)
99 >>> b=eid("sys-apps/foo-3.0_rc1")
100 >>> b.debug(0), b.valid, a>b, a<b
101 ([None, 'sys-apps', 'foo', '3.0_rc1', '0', 1, 'sys-apps/foo-3.0_rc1', None], 1, 1, 0)
102 >>> b=eid("sys-apps/foo-3.0_pre1")
103 >>> b.debug(0), b.valid, a>b, a<b
104 ([None, 'sys-apps', 'foo', '3.0_pre1', '0', 1, 'sys-apps/foo-3.0_pre1', None], 1, 1, 0)
105 >>> b=eid("sys-apps/foo-3.0_p3")
106 >>> b.debug(0), b.valid, a>b, a<b
107 ([None, 'sys-apps', 'foo', '3.0_p3', '0', 1, 'sys-apps/foo-3.0_p3', None], 1, 0, 1)
108 >>> b=eid("sys-apps/foo-3.0a")
109 >>> b.debug(0), b.valid, a>b, a<b
110 ([None, 'sys-apps', 'foo', '3.0a', '0', 1, 'sys-apps/foo-3.0a', None], 1, 0, 1)
111 """
112
113 pattern=re.compile(
114 '^(\w+-\w+)/' # category
115 '([^/]+?)' # name
116 '-(\d+(?:\.\d+)*[a-z]*)' # version, eg 1.23.4a
117 '(_(?:alpha|beta|pre|rc|p)\d*)?' # special suffix
118 '(?:-r(\d+))?$') # revision, eg r12
119
120 def __init__(self,mystring=None,validate=1):
121 "initialization; optional assignment; optional validation of input (otherwise assumed correct)"
122 self.repr=mystring
123 self.valid=1
124 self.cmp=None
125 self.error=None
126
127 if not mystring:
128 self.invalidate()
129 return
130 match=eid.pattern.search(mystring)
131 if match:
132 (self.category, self.package, v1, v2, rev) = match.groups()
133 self.version = v1 + (v2 or '')
134 self.revision = rev or '0'
135 return
136
137 # parse error -- try to figure out what's wrong with a looser regexp
138 match=re.compile(
139 '^(?:(.*)/)?' # category
140 '([^/]*?)' # name
141 '(?:-([^-_]*?))?' # version, eg 1.23.4a
142 '(_?(?:alpha|beta|pre|rc|p)\d*)?' # special suffix
143 '(?:-r(.*))?$' # revision, eg r12
144 ).search(mystring)
145 if not match:
146 # no good -- even the loose regexp failed
147 self.invalidate("Unparseable")
148 return
149 (cat, pkg, v1, v2, rev) = match.groups()
150
151 # the loose regexp worked; now try to find which part is wrong
152 if not cat: # check category
153 self.invalidate("Missing category")
154 elif len(string.split(cat, "/")) > 1:
155 self.invalidate("More than on \"/\"")
156 elif len(string.split(cat, "-")) != 2:
157 self.invalidate("Expected exactly 1 \"-\" in category")
158 elif not pkg: # check package name
159 self.invalidate("Missing package name")
160 elif not v1: # check version
161 self.invalidate("Missing version")
162 elif not re.compile('^\d+(?:\.\d+)*[a-z]*$').search(v1):
163 self.invalidate("Invalid version number")
164 elif v2 and v2[0] != '_':
165 self.invalidate("Invalid ending version part")
166 elif rev != None and not re.compile('^\d+').search(rev):
167 self.invalidate("Invalid revision number")
168 else:
169 self.invalidate("Miscellaneous error")
170
171 def invalidate(self,error=None):
172 "make this an invalid eid"
173 if error:
174 if self.repr:
175 #record string representation for later reference
176 self.error=self.repr+": "+error
177 else:
178 self.error=error
179 else:
180 self.error=None
181 self.cmp=None
182 self.category=None
183 self.package=None
184 self.version=None
185 self.revision=None
186 self.valid=0
187 self.repr=None
188
189 def debug(self, verbose=1):
190 "internal debug function"
191 if verbose:
192 print
193 print "DEBUG EID"
194 out=[]
195 for x in ["error","category","package","version","revision","valid","repr","cmp"]:
196 try:
197 exec("y=self." + x)
198 except:
199 y="(undefined)"
200 out.append(y)
201 if verbose:
202 print x, y
203 return out
204
205 def __cmp__(self,other):
206 "comparison operator code"
207 if self.cmp==None:
208 self.cmp=self.gencmp()
209 if other.cmp==None:
210 other.cmp=other.gencmp()
211 mycmp=self.cmp[:]
212 othercmp=other.cmp[:]
213 while(len(mycmp)<len(othercmp)):
214 mycmp.append([0,0,0])
215 while(len(mycmp)>len(othercmp)):
216 othercmp.append([0,0,0])
217 for x in range(0,len(mycmp)-1):
218 for y in range(0,3):
219 myret=mycmp[x][y]-othercmp[x][y]
220 if myret!=0:
221 return myret
222 return 0
223
224 def gencmp(self):
225 "internal function used to generate comparison lists"
226 cmplist=[]
227 splitversion=string.split(self.version,".")
228 for x in splitversion[:-1]:
229 cmplist.append([string.atoi(x),0,0])
230 a=string.split(splitversion[-1],"_")
231 match=0
232 p1=0
233 p2=0
234 if len(a)==2:
235 pos=len(a[1])
236 number=string.atoi(a[0])
237 if a[1][-1] in string.digits:
238 pos=0
239 while a[1][pos-1] in string.digits:
240 pos=pos-1
241 for x in endversion.keys():
242 if a[1][0:pos]==x:
243 match=1
244 #p1 stores the numerical weight of _alpha, _beta, etc.
245 p1=endversion[x]
246 try:
247 p2=string.atoi(a[1][len(x):])
248 except:
249 p2=0
250 cmplist.append([number,p1,p2])
251 cmplist.append([string.atoi(self.revision),0,0])
252 return cmplist
253 if not match:
254 #normal number or number with letter at end
255 if self.version[-1] not in string.digits:
256 #letter at end
257 p1=ord(self.version[-1])
258 number=string.atoi(splitversion[-1][0:-1])
259 else:
260 number=string.atoi(splitversion[-1])
261 cmplist.append([number,p1,p2])
262 cmplist.append([string.atoi(self.revision),0,0])
263 return cmplist
264
265 def similar(self,other):
266 "are we talking about the same category and package (but possibly different versions/revs)?"
267 if self.valid and other.valid:
268 if (self.category==other.category) and (self.package==other.package):
269 return 1
270 return 0
271
272 class eidset:
273 def __init__(self):
274 self.db={"category":{},"catpkg":{},"rep":{}}
275
276 def add(self,myeid):
277 "add an eid, indexing by category, catpkg and rep"
278 if not myeid.valid:
279 return
280 if not self.db["category"].has_key(myeid.category):
281 self.db["category"][myeid.category]=[]
282 self.db["category"][myeid.category].append(myeid)
283 mykey=myeid.category+"/"+myeid.package
284 if not self.db["catpkg"].has_key(mykey):
285 self.db["catpkg"][mykey]=[]
286 self.db["catpkg"][mykey].append(myeid)
287 self.db["rep"][myeid.rep]=myeid
288
289
290
291 """new dep ideas:
292
293 we introduce the concept of "hard deps" and "soft deps". By default, apps use
294 "hard deps". Hard deps are used to decribe relationships where the ebuild
295 depends on a specific version/rev of a package currently installed, and becomes
296 intrinsically tied to it. soft deps specify a dependency on a set of packages.
297 As long as one of the set is currently installed, the package will run fine.
298
299 Syntax ideas:
300
301 =sys-apps/foo-1.0 (hard dep)
302 'sys-apps/man (soft dep, depends on any version of man)
303 sys-apps/man (hard dep, depends on the specific version of man installed right now)
304 'foo/bar (soft dep, requires foo/bar and foo/oni to be installed
305 '{foo/bar >=1.0 <2.0} (soft dep using new set syntax, depending on any foo/bar 1.0 or greater, but less than 2.0)
306 '{foo/bar 1.*} (same as above)
307 '{foo/bar >=1.2 <2.0} (more specific)
308 '{foo/bar ~1.0 ~1.3} (any revision of 1.0 or 1.3)
309 {foo/bar >=4.0} a *hard* dependency on any ebuild
310 Questions: do we need to be able to "*hard*" depend on a specific version *and*
311 rev of a package?
312
313 Tenative answer: yes. The hard dependency should depend on whatever rev is installed,
314 and if a new rev is installed, the package dependent on it should be rebuilt too.
315
316 Do we need to only be able to hard-depend on a specific version of a package?
317
318 Tenative answer: no. hard deps are used to link multiple packages into
319 a logical whole. A hard dep is like concrete, forming an amalgam meta-package.
320
321 Why we are doing this: By specifying hard and soft deps, we provide the necessary
322 info that Portage needs to carefully rebuild/upgrade the system. We can now track
323 how packages *currently* are relying on one another... this is something that we
324 aren't doing yet, and is required for a nice, fast "emerge update" implementation.
325
326 Question: will this additional informatin allow portage to determine if a particular
327 package is currently being depended upon by anything on the running system?
328
329 Answer: yes, it will. Whee!
330 """
331
332 def _test():
333 import doctest, portage_core
334 return doctest.testmod(portage_core)
335
336 if __name__ == "__main__":
337 _test()

  ViewVC Help
Powered by ViewVC 1.1.20