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

Contents of /portage/pym/portage_syntax.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.11 - (show annotations) (download) (as text)
Tue May 10 15:50:26 2005 UTC (9 years, 4 months ago) by jstubbs
Branch: MAIN
CVS Tags: HEAD
Branch point for: portage_2_1
Changes since 1.10: +325 -245 lines
File MIME type: text/x-python
Redesigned and rewrote DependSpec

1 import re
2
3 from copy import *
4
5
6 pkg_regexp = re.compile("^[a-zA-Z0-9]([-_+a-zA-Z0-9]*[+a-zA-Z0-9])?$")
7 ver_regexp = re.compile("^(cvs\\.)?(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?$")
8 suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
9 suffix_value = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1}
10
11 class CPV(object):
12
13 """
14 Attributes
15
16 str category
17 str package
18 str key (cat/pkg)
19 str version
20 int revision
21
22 Methods
23
24 int __hash__()
25 str __repr__()
26 int __cmp__(CPV)
27 """
28
29 def __init__(self, cpvstr):
30 if not isinstance(cpvstr, str):
31 raise ValueError(cpvstr)
32 self.__dict__["cpvstr"] = cpvstr
33 self.__dict__["hash"] = hash(cpvstr)
34
35 def __hash__(self):
36 return self.hash
37
38 def __repr__(self):
39 return self.cpvstr
40
41 def __setattr__(self, name, value):
42 raise Exception()
43
44 def __getattr__(self, name):
45
46 if name == "category":
47 myparts = self.cpvstr.split("/")
48 if len(myparts) >= 2:
49 if not pkg_regexp.match(myparts[0]):
50 raise ValueError(self.cpvstr)
51 self.__dict__["category"] = myparts[0]
52 else:
53 self.__dict__["category"] = None
54 return self.category
55
56 if name == "package":
57 if self.category:
58 myparts = self.cpvstr[len(self.category)+1:].split("-")
59 else:
60 myparts = self.cpvstr.split("-")
61 if ver_regexp.match(myparts[0]):
62 raise ValueError(self.cpvstr)
63 pos = 1
64 while pos < len(myparts) and not ver_regexp.match(myparts[pos]):
65 pos += 1
66 pkgname = "-".join(myparts[:pos])
67 if not pkg_regexp.match(pkgname):
68 raise ValueError(self.cpvstr)
69 self.__dict__["package"] = pkgname
70 return self.package
71
72 if name == "key":
73 if self.category:
74 self.__dict__["key"] = self.category +"/"+ self.package
75 else:
76 self.__dict__["key"] = self.package
77 return self.key
78
79 if name == "version" or name == "revision":
80 if self.category:
81 myparts = self.cpvstr[len(self.category+self.package)+2:].split("-")
82 else:
83 myparts = self.cpvstr[len(self.package)+1:].split("-")
84
85 if not myparts[0]:
86 self.__dict__["version"] = None
87 self.__dict__["revision"] = None
88
89 else:
90 if myparts[-1][0] == "r" and myparts[-1][1:].isdigit():
91 self.__dict__["revision"] = int(myparts[-1][1:])
92 myparts = myparts[:-1]
93 else:
94 self.__dict__["revision"] = 0
95
96 for x in myparts:
97 if not ver_regexp.match(x):
98 raise ValueError(self.mycpv)
99
100 self.__dict__["version"] = "-".join(myparts)
101
102 if name == "version":
103 return self.version
104 else:
105 return self.revision
106
107 raise AttributeError(name)
108
109 def __cmp__(self, other):
110
111 if self.cpvstr == other.cpvstr:
112 return 0
113
114 if self.category and other.category and self.category != other.category:
115 return cmp(self.category, other.category)
116
117 if self.package and other.package and self.package != other.package:
118 return cmp(self.package, other.package)
119
120 if self.version != other.version:
121
122 if self.version is None:
123 raise ValueError(self)
124
125 if other.version is None:
126 raise ValueError(other)
127
128 match1 = ver_regexp.match(self.version)
129 match2 = ver_regexp.match(other.version)
130
131 # shortcut for cvs ebuilds (new style)
132 if match1.group(1) and not match2.group(1):
133 return 1
134 elif match2.group(1) and not match1.group(1):
135 return -1
136
137 # building lists of the version parts before the suffix
138 # first part is simple
139 list1 = [int(match1.group(2))]
140 list2 = [int(match2.group(2))]
141
142 # this part would greatly benefit from a fixed-length version pattern
143 if len(match1.group(3)) or len(match2.group(3)):
144 vlist1 = match1.group(3)[1:].split(".")
145 vlist2 = match2.group(3)[1:].split(".")
146 for i in range(0, max(len(vlist1), len(vlist2))):
147 if len(vlist1) <= i or len(vlist1[i]) == 0:
148 list1.append(0)
149 list2.append(int(vlist2[i]))
150 elif len(vlist2) <= i or len(vlist2[i]) == 0:
151 list1.append(int(vlist1[i]))
152 list2.append(0)
153 # Let's make life easy and use integers unless we're forced to use floats
154 elif (vlist1[i][0] != "0" and vlist2[i][0] != "0"):
155 list1.append(int(vlist1[i]))
156 list2.append(int(vlist2[i]))
157 # now we have to use floats so 1.02 compares correctly against 1.1
158 else:
159 list1.append(float("0."+vlist1[i]))
160 list2.append(float("0."+vlist2[i]))
161
162 # and now the final letter
163 if len(match1.group(5)):
164 list1.append(ord(match1.group(5)))
165 if len(match2.group(5)):
166 list2.append(ord(match2.group(5)))
167
168 for i in range(0, max(len(list1), len(list2))):
169 if len(list1) <= i:
170 return -1
171 elif len(list2) <= i:
172 return 1
173 elif list1[i] != list2[i]:
174 return list1[i] - list2[i]
175
176 # main version is equal, so now compare the _suffix part
177 list1 = match1.group(6).split("_")[1:]
178 list2 = match2.group(6).split("_")[1:]
179
180 for i in range(0, max(len(list1), len(list2))):
181 if len(list1) <= i:
182 s1 = ("p","0")
183 else:
184 s1 = suffix_regexp.match(list1[i]).groups()
185 if len(list2) <= i:
186 s2 = ("p","0")
187 else:
188 s2 = suffix_regexp.match(list2[i]).groups()
189 if s1[0] != s2[0]:
190 return suffix_value[s1[0]] - suffix_value[s2[0]]
191 if s1[1] != s2[1]:
192 # it's possible that the s(1|2)[1] == ''
193 # in such a case, fudge it.
194 try: r1 = int(s1[1])
195 except ValueError: r1 = 0
196 try: r2 = int(s2[1])
197 except ValueError: r2 = 0
198 return r1 - r2
199
200 return cmp(self.revision, other.revision)
201
202
203 class Atom(object):
204
205 """
206 Attributes
207
208 bool blocks
209 str operator
210 bool glob_match
211 CPV cpv
212
213 Methods
214 int __hash__()
215 str __str__()
216 str __repr__()
217 bool match(CPV)
218 """
219
220 def __init__(self, atomstr):
221 if not isinstance(atomstr, str):
222 raise ValueError(atomstr)
223 self.__dict__["atomstr"] = atomstr
224 self.__dict__["hash"] = hash(atomstr)
225
226 def __hash__(self):
227 return self.hash
228
229 def __str__(self):
230 return self.atomstr
231
232 def __repr__(self):
233 return "Atom('" + self.atomstr + "')"
234
235 def __setattr__(self, name, value):
236 raise Exception()
237
238 def __eq__(self, other):
239 if isinstance(other, Atom):
240 return hash(self) == other.hash
241 return False
242
243 def __copy__(self):
244 return self
245
246 def __getattr__(self, name):
247
248 if "operator" not in self.__dict__:
249
250 myatom = self.atomstr
251
252 if myatom[0] == "!":
253 self.__dict__["blocks"] = True
254 myatom = myatom[1:]
255 else:
256 self.__dict__["blocks"] = False
257
258 if myatom[0:2] in ["<=", ">="]:
259 self.__dict__["operator"] = myatom[0:2]
260 myatom = myatom[2:]
261 elif myatom[0] in ["<", ">", "=", "~"]:
262 self.__dict__["operator"] = myatom[0]
263 myatom = myatom[1:]
264 else:
265 self.__dict__["operator"] = None
266
267 if myatom[-1] == "*":
268 self.__dict__["glob_match"] = True
269 myatom = myatom[:-1]
270 else:
271 self.__dict__["glob_match"] = False
272
273 self.__dict__["cpv"] = CPV(myatom)
274
275 if self.operator != "=" and self.glob_match:
276 raise ValueError(self.atomstr)
277
278 if self.operator and not self.cpv.version:
279 raise ValueError(self.atomstr)
280
281 if not self.operator and self.cpv.version:
282 raise ValueError(self.atomstr)
283
284 if self.operator == "~" and self.cpv.revision:
285 raise ValueError(self.atomstr)
286
287 if self.glob_match and self.cpv.revision:
288 raise ValueError(self.atomstr)
289
290 if not self.__dict__.has_key(name):
291 return self.cpv.__getattr__(name)
292
293 return self.__dict__[name]
294
295 def match(self, cpv):
296
297 if self.cpv.category and cpv.category and self.cpv.category != cpv.category:
298 return False
299
300 if self.cpv.package and cpv.package and self.cpv.package != cpv.package:
301 return False
302
303 if not self.operator:
304 return True
305
306 if self.operator == "=":
307 if self.glob_match and cpv.version.startswith(self.cpv.version):
308 return True
309 if self.cpv.version != cpv.version:
310 return False
311 if self.cpv.revision != cpv.revision:
312 return False
313 return True
314
315 if self.operator == "~" and self.cpv.version == cpv.version:
316 return True
317
318 diff = cmp(self.cpv, cpv)
319
320 if not diff:
321 if self.operator == "<=" or self.operator == ">=":
322 return True
323 else:
324 return False
325
326 if diff > 0:
327 if self.operator[0] == "<":
328 return True
329 else:
330 return False
331
332 #if diff < 0:
333 if self.operator[0] == ">":
334 return True
335 #else:
336 return False
337
338 def with_key(self, key):
339 return Atom(self.atomstr.replace(self.cpv.key, key))
340
341 def intersects(self, atom):
342 if self == atom:
343 return True
344 if self.cpv.key != atom.cpv.key:
345 return False
346 if self.blocks != atom.blocks:
347 return False
348 if not self.operator or not atom.operator:
349 return True
350 if self.cpv == other.cpv:
351 if self.operator == atom.operator:
352 return True
353 if self.operator == "<":
354 return (atom.operator[0] == "<")
355 if self.operator == ">":
356 return (other.operator[0] == ">" or other.operator == "~")
357 if self.operator == "=":
358 return (other.operator != "<" and other.operator != ">")
359 if self.operator == "~" or self.operator == ">=":
360 return (other.operator != "<")
361 return (other.operator != ">")
362 elif self.cpv.version == other.cpv.version:
363 if self.cpv > other.cpv:
364 if self.operator == "=" and other.operator == "~":
365 return True
366 elif self.operator == "~" and other.operator == "=":
367 return True
368 if self.operator in ["=","~"] and other.operator in ["=","~"]:
369 return False
370 if self.cpv > other.cpv:
371 if self.operator in ["<","<="]:
372 return True
373 if other.operator in [">",">="]:
374 return True
375 return False
376 if self.operator in [">",">="]:
377 return True
378 if other.operator in ["<","<="]:
379 return True
380 return False
381
382 def encapsulates(self, atom):
383 if not self.intersects(atom):
384 return False
385
386 if self.operator and not atom.operator:
387 return False
388 if not self.operator:
389 return True
390
391 if self.cpv == atom.cpv:
392 if self.operator == other.operator:
393 return True
394 if other.operator == "=":
395 return True
396 if self.operator == "<=" and other.operator == "<":
397 return True
398 if self.operator == ">=" and other.operator == ">":
399 return True
400 return False
401 elif self.cpv.version == other.cpv.version:
402 if self.cpv < other.cpv and self.operator == "~":
403 return true
404 if self.cpv > other.cpv:
405 if self.operator in ["<","<="] and other.operator not in [">",">="]:
406 return True
407 return False
408 if self.operator in [">",">="] and other.operator not in ["<","<="]:
409 return True
410 return False
411
412
413
414
415
416 class UseCondition(object):
417
418 _use_regex = re.compile("^!?[\\w-]+\?$")
419
420 def can_parse(cls, condition_str):
421 return (cls._use_regex.match(condition_str) is not None)
422 can_parse = classmethod(can_parse)
423
424 def __init__(self, condition_str):
425 condition_str = condition_str[:-1]
426 self.__dict__["_hash"] = hash(condition_str)
427 self.__dict__["negated"] = (condition_str[0] == "!")
428 if self.negated:
429 self.__dict__["flag"] = condition_str[1:]
430 else:
431 self.__dict__["flag"] = condition_str
432
433 def __setattr__(self, name, value):
434 raise TypeError("UseCondition has only read-only attributes (assign to "+name+")")
435
436 def __hash__(self):
437 return self._hash
438
439 def __eq__(self, other):
440 return (isinstance(other, UseCondition) and self._hash == other._hash)
441
442 def __copy__(self):
443 return self
444
445 def conflicts_with(self, other):
446 return (self.flag == other.flag and self.negated != other.negated)
447
448
449 class ParseError(Exception):
450 pass
451
452
453 class DependSpec(object):
454
455 def __init__(self, dependstr="", element_class=str):
456 dependstr = " ( ".join(dependstr.split("("))
457 dependstr = " ) ".join(dependstr.split(")"))
458 dependstr = " ".join(dependstr.split())
459 self.__dict__["_origstr"] = dependstr
460 self.__dict__["_str"] = None
461 self.__dict__["_element_class"] = element_class
462 self.__dict__["_needs_brackets"] = True
463 self.__dict__["_specials"] = []
464 self.__dict__["condition"] = None
465
466 def __copy__(self):
467 dependspec = self.__class__()
468 dependspec.__dict__["_element_class"] = self._element_class
469 dependspec.__dict__["_specials"] = self._specials[:]
470 dependspec.__dict__["condition"] = copy(self.condition)
471 dependspec.__dict__["_needs_brackets"] = self._needs_brackets
472 self._parsed
473 dependspec.__dict__["_elements"] = self._elements[:]
474 dependspec.__dict__["_parsed"] = True
475 return dependspec
476
477 def __setattr__(self, name, value):
478 raise TypeError("DependSpec has only read-only attributes (assign to "+name+")")
479
480 def __str__(self):
481 if self._str is not None:
482 return self._str
483 self._parsed
484 mystr = []
485 for element in self._elements:
486 x = str(element)
487 if x:
488 if isinstance(element, DependSpec) and element._needs_brackets:
489 x = "( "+x+" )"
490 mystr.append(x)
491 mystr = " ".join(mystr)
492 if self.condition:
493 mystr = str(self.condition)+" ( "+mystr+" )"
494 self.__dict__["_str"] = mystr
495 return mystr
496
497 def _needs_brackets(self):
498 return True
499
500 def __hash__(self):
501 return hash(str(self))
502
503 def __eq__(self, other):
504 return (isinstance(other, DependSpec) and str(self) == str(other))
505
506 def __getattr__(self, name):
507 if "_parsed" not in self.__dict__:
508 self._parse()
509 return self.__dict__[name]
510
511 def __getitem__(self, idx):
512 self._parsed
513 return self._elements[idx]
514
515 def __len__(self):
516 self._parsed
517 return len(self._elements)
518
519 def _parse(self):
520 dependstr = self._origstr
521 if dependstr.count("(") != dependstr.count(")"):
522 raise ParseError(dependstr)
523 self.__dict__["_elements"] = []
524 specials_found = []
525 condition = None
526 strlen = len(dependstr)
527 pos = 0
528 while pos != strlen:
529 if dependstr[pos] == " ":
530 pos += 1
531 continue
532 if dependstr[pos] == ")":
533 raise ParseError(dependstr)
534 if dependstr[pos] == "(":
535 pos += 1
536 bracket_count = 1
537 nextpos = pos
538 while bracket_count:
539 nextpos_d = {}
540 nextpos_d[dependstr.find("(", nextpos)] = True
541 nextpos_d[dependstr.find(")", nextpos)] = True
542 if -1 in nextpos_d:
543 del nextpos_d[-1]
544 nextpos = min(nextpos_d.keys())
545 if dependstr[nextpos] == "(":
546 bracket_count += 1
547 else:
548 bracket_count -= 1
549 nextpos += 1
550 element = self.__class__(dependstr[pos:nextpos-1])
551 element.__dict__["_element_class"] = self._element_class
552 if condition:
553 element.__dict__["condition"] = condition
554 condition = None
555 pos = nextpos
556 self._elements.append(element)
557 continue
558 nextpos_d = {strlen:True}
559 nextpos_d[dependstr.find(" ", pos)] = True
560 nextpos_d[dependstr.find("(", pos)] = True
561 nextpos_d[dependstr.find(")", pos)] = True
562 if -1 in nextpos_d:
563 del nextpos_d[-1]
564 nextpos = min(nextpos_d.keys())
565 element = dependstr[pos:nextpos]
566 if element in self._specials:
567 specials_found += [(element, len(self._elements))]
568 elif UseCondition.can_parse(element):
569 if condition:
570 raise ParseError(dependstr)
571 condition = UseCondition(element)
572 else:
573 if condition:
574 raise ParseError(dependstr)
575 self._elements.append(self._element_class(element))
576 pos = nextpos
577 if condition:
578 raise ParseError(dependstr)
579 for special in specials_found:
580 if special[1] == len(self._elements):
581 raise ParseError(dependstr)
582 try:
583 self._do_special(special[0], special[1])
584 except ParseError:
585 raise ParseError(dependstr)
586 self.__dict__["_parsed"] = True
587
588 def all_conditions(self):
589 cond_d = {}
590 if self.condition:
591 cond_d[self.condition] = True
592 yield self.condition
593
594 self._parsed
595 for element in self._elements:
596 if isinstance(element, DependSpec):
597 for cond in element.all_conditions():
598 if cond not in cond_d:
599 cond_d[cond] = True
600 yield cond
601
602 def with_only_conditions(self, conditions):
603 if self.condition and self.condition not in conditions:
604 return self.__class__()
605 self._parsed
606 dependspec = copy(self)
607 dependspec.__dict__["condition"] = None
608 for idx in range(len(dependspec._elements)):
609 if isinstance(dependspec._elements[idx], DependSpec):
610 dependspec._elements[idx] = dependspec._elements[idx].with_only_conditions(conditions)
611 return dependspec
612
613 def _can_combine_with(self, other):
614 return self.condition == other.condition
615
616 def compacted(self):
617 elements = []
618 element_d = {}
619 self._parsed
620 for element in self._elements:
621 if element in element_d:
622 continue
623 if isinstance(element, DependSpec):
624 element = element.compacted()
625 if not len(element._elements):
626 continue
627 if self._can_combine_with(element):
628 for element in element._elements:
629 if element in element_d:
630 continue
631 elements.append(element)
632 element_d[element] = True
633 else:
634 elements.append(element)
635 element_d[element] = True
636 else:
637 elements.append(element)
638 element_d[element] = True
639 if not elements:
640 return self.__class__()
641 dependspec = copy(self)
642 dependspec.__dict__["_elements"] = elements
643 return dependspec
644
645
646 class AtomDependSpec(DependSpec):
647
648 def create_from(atoms, preferential=False):
649 dependstr = []
650 for atom in atoms:
651 dependstr.append(str(atom))
652 dependstr = " ".join(dependstr)
653 if preferential:
654 dependstr = "|| ( "+dependstr+" )"
655 return AtomDependSpec(dependstr)
656 create_from = staticmethod(create_from)
657
658 def __init__(self, dependstr=""):
659 super(self.__class__, self).__init__(dependstr, element_class=Atom)
660 self.__dict__["preferential"] = False
661 self.__dict__["_specials"] = ["||"]
662
663 def __copy__(self):
664 atomdependspec = super(self.__class__, self).__copy__()
665 atomdependspec.__dict__["preferential"] = self.preferential
666 return atomdependspec
667
668 def __str__(self):
669 if self._str is not None:
670 return self._str
671 mystr = super(self.__class__, self).__str__()
672 if self.preferential:
673 mystr = "|| ( "+mystr+" )"
674 self.__dict__["_str"] = mystr
675 return mystr
676
677 def _do_special(self, special, idx):
678 if not isinstance(self._elements[idx], AtomDependSpec) or self._elements[idx].preferential:
679 raise ParseError()
680 self._elements[idx].__dict__["preferential"] = True
681 self._elements[idx].__dict__["_needs_brackets"] = False
682
683 def _can_combine_with(self, other):
684 if self.preferential != other.preferential:
685 return False
686 return super(self.__class__, self)._can_combine_with(other)
687
688 def compacted(self):
689 atomdependspec = super(self.__class__, self).compacted()
690 if atomdependspec.preferential and len(atomdependspec._elements) <= 1:
691 atomdependspec.__dict__["preferential"] = False
692 atomdependspec.__dict__["_needs_brackets"] = True
693 atomdependspec = atomdependspec.compacted()
694 return atomdependspec
695
696 def with_keys_transformed(self, key_map):
697 atomdependspec = copy(self)
698 atomdependspec._parsed
699 for x in range(len(atomdependspec._elements)):
700 if isinstance(atomdependspec._elements[x], AtomDependSpec):
701 atomdependspec._elements[x] = atomdependspec._elements[x].with_keys_transformed()
702 elif atomdependspec._elements[x].key in key_map:
703 elements = []
704 for newkey in key_map[atomdependspec._elements[x].key]:
705 elements.append(atomdependspec._elements[x].with_key(newkey))
706 atomdependspec._elements[x] = AtomDependSpec.create_from(elements, preferential=True)
707 atomdependspec.__dict__["_str"] = None
708 return atomdependspec
709
710 def combinations(self):
711 if not self._elements:
712 return []
713
714 if self.condition:
715 raise NotImplementedError()
716
717 combinations = []
718
719 if self.preferential:
720 for element in self._elements:
721 if isinstance(element, AtomDependSpec):
722 combinations += element.combinations()
723 else:
724 combinations += [[element]]
725 else:
726 singles = []
727 others = []
728 for element in self._elements:
729 if isinstance(element, AtomDependSpec):
730 others += [element.combinations()]
731 else:
732 singles += [element]
733 if others:
734 indexes = []
735 endindex = len(others)
736 for x in range(endindex):
737 indexes.append(0)
738 index = 0
739 while index != endindex:
740 if indexes[index] >= len(others[index]):
741 index += 1
742 if index == endindex:
743 continue
744 for x in range(index):
745 indexes[x] = 0
746 indexes[index] += 1
747 continue
748 else:
749 index = 0
750 newcomb = singles[:]
751 for x in range(endindex):
752 if others[x]:
753 newcomb.extend(others[x][indexes[x]])
754 combinations.append(newcomb)
755 indexes[index] += 1
756 else:
757 combinations = [singles]
758 return combinations

  ViewVC Help
Powered by ViewVC 1.1.20