Gentoo Archives: gentoo-commits

From: "Zac Medico (zmedico)" <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] portage r15461 - in main/trunk/pym/portage: . dep
Date: Thu, 25 Feb 2010 20:48:13
Message-Id: E1Nkkcy-0004B5-Gv@stork.gentoo.org
1 Author: zmedico
2 Date: 2010-02-25 20:48:08 +0000 (Thu, 25 Feb 2010)
3 New Revision: 15461
4
5 Added:
6 main/trunk/pym/portage/dep/
7 main/trunk/pym/portage/dep/__init__.py
8 Removed:
9 main/trunk/pym/portage/dep.py
10 Log:
11 Move dep.py to dep/__init__.py, for splitting into smaller files.
12
13
14 Copied: main/trunk/pym/portage/dep/__init__.py (from rev 15457, main/trunk/pym/portage/dep.py)
15 ===================================================================
16 --- main/trunk/pym/portage/dep/__init__.py (rev 0)
17 +++ main/trunk/pym/portage/dep/__init__.py 2010-02-25 20:48:08 UTC (rev 15461)
18 @@ -0,0 +1,1203 @@
19 +# deps.py -- Portage dependency resolution functions
20 +# Copyright 2003-2004 Gentoo Foundation
21 +# Distributed under the terms of the GNU General Public License v2
22 +# $Id$
23 +
24 +__all__ = [
25 + 'Atom', 'best_match_to_list', 'cpvequal',
26 + 'dep_getcpv', 'dep_getkey', 'dep_getslot',
27 + 'dep_getusedeps', 'dep_opconvert', 'flatten',
28 + 'get_operator', 'isjustname', 'isspecific',
29 + 'isvalidatom', 'match_from_list', 'match_to_list',
30 + 'paren_enclose', 'paren_normalize', 'paren_reduce',
31 + 'remove_slot', 'strip_empty', 'use_reduce'
32 +]
33 +
34 +# DEPEND SYNTAX:
35 +#
36 +# 'use?' only affects the immediately following word!
37 +# Nesting is the only legal way to form multiple '[!]use?' requirements.
38 +#
39 +# Where: 'a' and 'b' are use flags, and 'z' is a depend atom.
40 +#
41 +# "a? z" -- If 'a' in [use], then b is valid.
42 +# "a? ( z )" -- Syntax with parenthesis.
43 +# "a? b? z" -- Deprecated.
44 +# "a? ( b? z )" -- Valid
45 +# "a? ( b? ( z ) ) -- Valid
46 +#
47 +
48 +import re, sys
49 +import warnings
50 +from itertools import chain
51 +import portage.exception
52 +from portage.exception import InvalidData, InvalidAtom
53 +from portage.localization import _
54 +from portage.versions import catpkgsplit, catsplit, \
55 + pkgcmp, pkgsplit, ververify, _cp, _cpv
56 +import portage.cache.mappings
57 +
58 +if sys.hexversion >= 0x3000000:
59 + basestring = str
60 +
61 +def cpvequal(cpv1, cpv2):
62 + """
63 +
64 + @param cpv1: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1"
65 + @type cpv1: String
66 + @param cpv2: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1"
67 + @type cpv2: String
68 + @rtype: Boolean
69 + @returns:
70 + 1. True if cpv1 = cpv2
71 + 2. False Otherwise
72 + 3. Throws PortageException if cpv1 or cpv2 is not a CPV
73 +
74 + Example Usage:
75 + >>> from portage.dep import cpvequal
76 + >>> cpvequal("sys-apps/portage-2.1","sys-apps/portage-2.1")
77 + >>> True
78 +
79 + """
80 +
81 + split1 = catpkgsplit(cpv1)
82 + split2 = catpkgsplit(cpv2)
83 +
84 + if not split1 or not split2:
85 + raise portage.exception.PortageException(_("Invalid data '%s, %s', parameter was not a CPV") % (cpv1, cpv2))
86 +
87 + if split1[0] != split2[0]:
88 + return False
89 +
90 + return (pkgcmp(split1[1:], split2[1:]) == 0)
91 +
92 +def strip_empty(myarr):
93 + """
94 + Strip all empty elements from an array
95 +
96 + @param myarr: The list of elements
97 + @type myarr: List
98 + @rtype: Array
99 + @return: The array with empty elements removed
100 + """
101 + return [x for x in myarr if x]
102 +
103 +_paren_whitespace_re = re.compile(r'\S(\(|\))|(\(|\))\S')
104 +
105 +def paren_reduce(mystr,tokenize=1):
106 + """
107 + Take a string and convert all paren enclosed entities into sublists, optionally
108 + futher splitting the list elements by spaces.
109 +
110 + Example usage:
111 + >>> paren_reduce('foobar foo ( bar baz )',1)
112 + ['foobar', 'foo', ['bar', 'baz']]
113 + >>> paren_reduce('foobar foo ( bar baz )',0)
114 + ['foobar foo ', [' bar baz ']]
115 +
116 + @param mystr: The string to reduce
117 + @type mystr: String
118 + @param tokenize: Split on spaces to produces further list breakdown
119 + @type tokenize: Integer
120 + @rtype: Array
121 + @return: The reduced string in an array
122 + """
123 + global _dep_check_strict, _paren_whitespace_re
124 + if _dep_check_strict:
125 + m = _paren_whitespace_re.search(mystr)
126 + if m is not None:
127 + raise portage.exception.InvalidDependString(
128 + _("missing space by parenthesis: '%s'") % m.group(0))
129 + mylist = []
130 + while mystr:
131 + left_paren = mystr.find("(")
132 + has_left_paren = left_paren != -1
133 + right_paren = mystr.find(")")
134 + has_right_paren = right_paren != -1
135 + if not has_left_paren and not has_right_paren:
136 + freesec = mystr
137 + subsec = None
138 + tail = ""
139 + elif mystr[0] == ")":
140 + return [mylist,mystr[1:]]
141 + elif has_left_paren and not has_right_paren:
142 + raise portage.exception.InvalidDependString(
143 + _("missing right parenthesis: '%s'") % mystr)
144 + elif has_left_paren and left_paren < right_paren:
145 + freesec,subsec = mystr.split("(",1)
146 + sublist = paren_reduce(subsec, tokenize=tokenize)
147 + if len(sublist) != 2:
148 + raise portage.exception.InvalidDependString(
149 + _("malformed syntax: '%s'") % mystr)
150 + subsec, tail = sublist
151 + else:
152 + subsec,tail = mystr.split(")",1)
153 + if tokenize:
154 + subsec = strip_empty(subsec.split(" "))
155 + return [mylist+subsec,tail]
156 + return mylist+[subsec],tail
157 + if not isinstance(tail, basestring):
158 + raise portage.exception.InvalidDependString(
159 + _("malformed syntax: '%s'") % mystr)
160 + mystr = tail
161 + if freesec:
162 + if tokenize:
163 + mylist = mylist + strip_empty(freesec.split(" "))
164 + else:
165 + mylist = mylist + [freesec]
166 + if subsec is not None:
167 + mylist = mylist + [subsec]
168 + return mylist
169 +
170 +class paren_normalize(list):
171 + """Take a dependency structure as returned by paren_reduce or use_reduce
172 + and generate an equivalent structure that has no redundant lists."""
173 + def __init__(self, src):
174 + list.__init__(self)
175 + self._zap_parens(src, self)
176 +
177 + def _zap_parens(self, src, dest, disjunction=False):
178 + if not src:
179 + return dest
180 + i = iter(src)
181 + for x in i:
182 + if isinstance(x, basestring):
183 + if x == '||':
184 + x = self._zap_parens(next(i), [], disjunction=True)
185 + if len(x) == 1:
186 + dest.append(x[0])
187 + else:
188 + dest.append("||")
189 + dest.append(x)
190 + elif x.endswith("?"):
191 + dest.append(x)
192 + dest.append(self._zap_parens(next(i), []))
193 + else:
194 + dest.append(x)
195 + else:
196 + if disjunction:
197 + x = self._zap_parens(x, [])
198 + if len(x) == 1:
199 + dest.append(x[0])
200 + else:
201 + dest.append(x)
202 + else:
203 + self._zap_parens(x, dest)
204 + return dest
205 +
206 +def paren_enclose(mylist):
207 + """
208 + Convert a list to a string with sublists enclosed with parens.
209 +
210 + Example usage:
211 + >>> test = ['foobar','foo',['bar','baz']]
212 + >>> paren_enclose(test)
213 + 'foobar foo ( bar baz )'
214 +
215 + @param mylist: The list
216 + @type mylist: List
217 + @rtype: String
218 + @return: The paren enclosed string
219 + """
220 + mystrparts = []
221 + for x in mylist:
222 + if isinstance(x, list):
223 + mystrparts.append("( "+paren_enclose(x)+" )")
224 + else:
225 + mystrparts.append(x)
226 + return " ".join(mystrparts)
227 +
228 +# This is just for use by emerge so that it can enable a backward compatibility
229 +# mode in order to gracefully deal with installed packages that have invalid
230 +# atoms or dep syntax. For backward compatibility with api consumers, strict
231 +# behavior will be explicitly enabled as necessary.
232 +_dep_check_strict = False
233 +
234 +def use_reduce(deparray, uselist=[], masklist=[], matchall=0, excludeall=[]):
235 + """
236 + Takes a paren_reduce'd array and reduces the use? conditionals out
237 + leaving an array with subarrays
238 +
239 + @param deparray: paren_reduce'd list of deps
240 + @type deparray: List
241 + @param uselist: List of use flags
242 + @type uselist: List
243 + @param masklist: List of masked flags
244 + @type masklist: List
245 + @param matchall: Resolve all conditional deps unconditionally. Used by repoman
246 + @type matchall: Integer
247 + @rtype: List
248 + @return: The use reduced depend array
249 + """
250 + # Quick validity checks
251 + for x, y in enumerate(deparray):
252 + if y == '||':
253 + if len(deparray) - 1 == x or not isinstance(deparray[x+1], list):
254 + raise portage.exception.InvalidDependString(_('%(dep)s missing atom list in "%(deparray)s"') % {"dep": deparray[x], "deparray": paren_enclose(deparray)})
255 + if deparray and deparray[-1] and deparray[-1][-1] == "?":
256 + raise portage.exception.InvalidDependString(_('Conditional without target in "%s"') % paren_enclose(deparray))
257 +
258 + global _dep_check_strict
259 +
260 + mydeparray = deparray[:]
261 + rlist = []
262 + while mydeparray:
263 + head = mydeparray.pop(0)
264 +
265 + if not isinstance(head, basestring):
266 + additions = use_reduce(head, uselist, masklist, matchall, excludeall)
267 + if additions:
268 + rlist.append(additions)
269 + elif rlist and rlist[-1] == "||":
270 + #XXX: Currently some DEPEND strings have || lists without default atoms.
271 + # raise portage.exception.InvalidDependString("No default atom(s) in \""+paren_enclose(deparray)+"\"")
272 + rlist.append([])
273 +
274 + else:
275 + if head[-1:] == "?": # Use reduce next group on fail.
276 + # Pull any other use conditions and the following atom or list into a separate array
277 + newdeparray = [head]
278 + while isinstance(newdeparray[-1], basestring) and \
279 + newdeparray[-1][-1:] == "?":
280 + if mydeparray:
281 + newdeparray.append(mydeparray.pop(0))
282 + else:
283 + raise ValueError(_("Conditional with no target."))
284 +
285 + # Deprecation checks
286 + warned = 0
287 + if len(newdeparray[-1]) == 0:
288 + sys.stderr.write(_("Note: Empty target in string. (Deprecated)\n"))
289 + warned = 1
290 + if len(newdeparray) != 2:
291 + sys.stderr.write(_("Note: Nested use flags without parenthesis (Deprecated)\n"))
292 + warned = 1
293 + if warned:
294 + sys.stderr.write(" --> "+" ".join(map(str,[head]+newdeparray))+"\n")
295 +
296 + # Check that each flag matches
297 + ismatch = True
298 + missing_flag = False
299 + for head in newdeparray[:-1]:
300 + head = head[:-1]
301 + if not head:
302 + missing_flag = True
303 + break
304 + if head.startswith("!"):
305 + head_key = head[1:]
306 + if not head_key:
307 + missing_flag = True
308 + break
309 + if not matchall and head_key in uselist or \
310 + head_key in excludeall:
311 + ismatch = False
312 + break
313 + elif head not in masklist:
314 + if not matchall and head not in uselist:
315 + ismatch = False
316 + break
317 + else:
318 + ismatch = False
319 + if missing_flag:
320 + raise portage.exception.InvalidDependString(
321 + _('Conditional without flag: "') + \
322 + paren_enclose([head+"?", newdeparray[-1]])+"\"")
323 +
324 + # If they all match, process the target
325 + if ismatch:
326 + target = newdeparray[-1]
327 + if isinstance(target, list):
328 + additions = use_reduce(target, uselist, masklist, matchall, excludeall)
329 + if additions:
330 + rlist.append(additions)
331 + elif not _dep_check_strict:
332 + # The old deprecated behavior.
333 + rlist.append(target)
334 + else:
335 + raise portage.exception.InvalidDependString(
336 + _("Conditional without parenthesis: '%s?'") % head)
337 +
338 + else:
339 + rlist += [head]
340 +
341 + return rlist
342 +
343 +def dep_opconvert(deplist):
344 + """
345 + Iterate recursively through a list of deps, if the
346 + dep is a '||' or '&&' operator, combine it with the
347 + list of deps that follows..
348 +
349 + Example usage:
350 + >>> test = ["blah", "||", ["foo", "bar", "baz"]]
351 + >>> dep_opconvert(test)
352 + ['blah', ['||', 'foo', 'bar', 'baz']]
353 +
354 + @param deplist: A list of deps to format
355 + @type mydep: List
356 + @rtype: List
357 + @return:
358 + The new list with the new ordering
359 + """
360 +
361 + retlist = []
362 + x = 0
363 + while x != len(deplist):
364 + if isinstance(deplist[x], list):
365 + retlist.append(dep_opconvert(deplist[x]))
366 + elif deplist[x] == "||" or deplist[x] == "&&":
367 + retlist.append([deplist[x]] + dep_opconvert(deplist[x+1]))
368 + x += 1
369 + else:
370 + retlist.append(deplist[x])
371 + x += 1
372 + return retlist
373 +
374 +def flatten(mylist):
375 + """
376 + Recursively traverse nested lists and return a single list containing
377 + all non-list elements that are found.
378 +
379 + Example usage:
380 + >>> flatten([1, [2, 3, [4]]])
381 + [1, 2, 3, 4]
382 +
383 + @param mylist: A list containing nested lists and non-list elements.
384 + @type mylist: List
385 + @rtype: List
386 + @return: A single list containing only non-list elements.
387 + """
388 + newlist = []
389 + for x in mylist:
390 + if isinstance(x, list):
391 + newlist.extend(flatten(x))
392 + else:
393 + newlist.append(x)
394 + return newlist
395 +
396 +class _use_dep(object):
397 +
398 + __slots__ = ("__weakref__", "conditional",
399 + "disabled", "enabled", "tokens", "required")
400 +
401 + _conditionals_class = portage.cache.mappings.slot_dict_class(
402 + ("disabled", "enabled", "equal", "not_equal"), prefix="")
403 +
404 + _valid_use_re = re.compile(r'^[^-?!=][^?!=]*$')
405 +
406 + def __init__(self, use):
407 + enabled_flags = []
408 + disabled_flags = []
409 + conditional = self._conditionals_class()
410 + for k in conditional.allowed_keys:
411 + conditional[k] = []
412 +
413 + for x in use:
414 + last_char = x[-1:]
415 + first_char = x[:1]
416 +
417 + if "?" == last_char:
418 + if "!" == first_char:
419 + conditional.disabled.append(
420 + self._validate_flag(x, x[1:-1]))
421 + else:
422 + conditional.enabled.append(
423 + self._validate_flag(x, x[:-1]))
424 +
425 + elif "=" == last_char:
426 + if "!" == first_char:
427 + conditional.not_equal.append(
428 + self._validate_flag(x, x[1:-1]))
429 + else:
430 + conditional.equal.append(
431 + self._validate_flag(x, x[:-1]))
432 +
433 + else:
434 + if "-" == first_char:
435 + disabled_flags.append(self._validate_flag(x, x[1:]))
436 + else:
437 + enabled_flags.append(self._validate_flag(x, x))
438 +
439 + self.tokens = use
440 + if not isinstance(self.tokens, tuple):
441 + self.tokens = tuple(self.tokens)
442 +
443 + self.required = frozenset(chain(
444 + enabled_flags,
445 + disabled_flags,
446 + *conditional.values()
447 + ))
448 +
449 + self.enabled = frozenset(enabled_flags)
450 + self.disabled = frozenset(disabled_flags)
451 + self.conditional = None
452 +
453 + for v in conditional.values():
454 + if v:
455 + for k, v in conditional.items():
456 + conditional[k] = frozenset(v)
457 + self.conditional = conditional
458 + break
459 +
460 + def _validate_flag(self, token, flag):
461 + if self._valid_use_re.match(flag) is None:
462 + raise InvalidAtom(_("Invalid use dep: '%s'") % (token,))
463 + return flag
464 +
465 + def __bool__(self):
466 + return bool(self.tokens)
467 +
468 + if sys.hexversion < 0x3000000:
469 + __nonzero__ = __bool__
470 +
471 + def __str__(self):
472 + if not self.tokens:
473 + return ""
474 + return "[%s]" % (",".join(self.tokens),)
475 +
476 + def __repr__(self):
477 + return "portage.dep._use_dep(%s)" % repr(self.tokens)
478 +
479 + def evaluate_conditionals(self, use):
480 + """
481 + Create a new instance with conditionals evaluated.
482 +
483 + Conditional evaluation behavior:
484 +
485 + parent state conditional result
486 +
487 + x x? x
488 + -x x?
489 + x !x?
490 + -x !x? -x
491 +
492 + x x= x
493 + -x x= -x
494 + x !x= -x
495 + -x !x= x
496 +
497 + Conditional syntax examples:
498 +
499 + Compact Form Equivalent Expanded Form
500 +
501 + foo[bar?] bar? ( foo[bar] ) !bar? ( foo )
502 + foo[!bar?] bar? ( foo ) !bar? ( foo[-bar] )
503 + foo[bar=] bar? ( foo[bar] ) !bar? ( foo[-bar] )
504 + foo[!bar=] bar? ( foo[-bar] ) !bar? ( foo[bar] )
505 +
506 + """
507 + tokens = []
508 +
509 + conditional = self.conditional
510 + tokens.extend(self.enabled)
511 + tokens.extend("-" + x for x in self.disabled)
512 + tokens.extend(x for x in conditional.enabled if x in use)
513 + tokens.extend("-" + x for x in conditional.disabled if x not in use)
514 +
515 + tokens.extend(x for x in conditional.equal if x in use)
516 + tokens.extend("-" + x for x in conditional.equal if x not in use)
517 + tokens.extend("-" + x for x in conditional.not_equal if x in use)
518 + tokens.extend(x for x in conditional.not_equal if x not in use)
519 +
520 + return _use_dep(tokens)
521 +
522 + def _eval_qa_conditionals(self, use_mask, use_force):
523 + """
524 + For repoman, evaluate all possible combinations within the constraints
525 + of the given use.force and use.mask settings. The result may seem
526 + ambiguous in the sense that the same flag can be in both the enabled
527 + and disabled sets, but this is useful within the context of how its
528 + intended to be used by repoman. It is assumed that the caller has
529 + already ensured that there is no intersection between the given
530 + use_mask and use_force sets when necessary.
531 + """
532 + tokens = []
533 +
534 + conditional = self.conditional
535 + tokens.extend(self.enabled)
536 + tokens.extend("-" + x for x in self.disabled)
537 + tokens.extend(x for x in conditional.enabled if x not in use_mask)
538 + tokens.extend("-" + x for x in conditional.disabled if x not in use_force)
539 +
540 + tokens.extend(x for x in conditional.equal if x not in use_mask)
541 + tokens.extend("-" + x for x in conditional.equal if x not in use_force)
542 + tokens.extend("-" + x for x in conditional.not_equal if x not in use_mask)
543 + tokens.extend(x for x in conditional.not_equal if x not in use_force)
544 +
545 + return _use_dep(tokens)
546 +
547 +if sys.hexversion < 0x3000000:
548 + _atom_base = unicode
549 +else:
550 + _atom_base = str
551 +
552 +class Atom(_atom_base):
553 +
554 + """
555 + For compatibility with existing atom string manipulation code, this
556 + class emulates most of the str methods that are useful with atoms.
557 + """
558 +
559 + class _blocker(object):
560 + __slots__ = ("overlap",)
561 +
562 + class _overlap(object):
563 + __slots__ = ("forbid",)
564 +
565 + def __init__(self, forbid=False):
566 + self.forbid = forbid
567 +
568 + def __init__(self, forbid_overlap=False):
569 + self.overlap = self._overlap(forbid=forbid_overlap)
570 +
571 + def __init__(self, s):
572 + if isinstance(s, Atom):
573 + # This is an efficiency assertion, to ensure that the Atom
574 + # constructor is not called redundantly.
575 + raise TypeError(_("Expected %s, got %s") % \
576 + (_atom_base, type(s)))
577 +
578 + _atom_base.__init__(s)
579 +
580 + if "!" == s[:1]:
581 + blocker = self._blocker(forbid_overlap=("!" == s[1:2]))
582 + if blocker.overlap.forbid:
583 + s = s[2:]
584 + else:
585 + s = s[1:]
586 + else:
587 + blocker = False
588 + self.__dict__['blocker'] = blocker
589 + m = _atom_re.match(s)
590 + if m is None:
591 + raise InvalidAtom(self)
592 +
593 + if m.group('op') is not None:
594 + base = _atom_re.groupindex['op']
595 + op = m.group(base + 1)
596 + cpv = m.group(base + 2)
597 + cp = m.group(base + 3)
598 + if m.group(base + 4) is not None:
599 + raise InvalidAtom(self)
600 + elif m.group('star') is not None:
601 + base = _atom_re.groupindex['star']
602 + op = '=*'
603 + cpv = m.group(base + 1)
604 + cp = m.group(base + 2)
605 + if m.group(base + 3) is not None:
606 + raise InvalidAtom(self)
607 + elif m.group('simple') is not None:
608 + op = None
609 + cpv = cp = m.group(_atom_re.groupindex['simple'] + 1)
610 + if m.group(_atom_re.groupindex['simple'] + 2) is not None:
611 + raise InvalidAtom(self)
612 + else:
613 + raise AssertionError(_("required group not found in atom: '%s'") % self)
614 + self.__dict__['cp'] = cp
615 + self.__dict__['cpv'] = cpv
616 + self.__dict__['slot'] = m.group(_atom_re.groups - 1)
617 + self.__dict__['operator'] = op
618 +
619 + use_str = m.group(_atom_re.groups)
620 + if use_str is not None:
621 + use = _use_dep(dep_getusedeps(s))
622 + without_use = Atom(m.group('without_use'))
623 + else:
624 + use = None
625 + without_use = self
626 +
627 + self.__dict__['use'] = use
628 + self.__dict__['without_use'] = without_use
629 +
630 + def __setattr__(self, name, value):
631 + raise AttributeError("Atom instances are immutable",
632 + self.__class__, name, value)
633 +
634 + def intersects(self, other):
635 + """
636 + Atoms with different cpv, operator or use attributes cause this method
637 + to return False even though there may actually be some intersection.
638 + TODO: Detect more forms of intersection.
639 + @param other: The package atom to match
640 + @type other: Atom
641 + @rtype: Boolean
642 + @return: True if this atom and the other atom intersect,
643 + False otherwise.
644 + """
645 + if not isinstance(other, Atom):
646 + raise TypeError("expected %s, got %s" % \
647 + (Atom, type(other)))
648 +
649 + if self == other:
650 + return True
651 +
652 + if self.cp != other.cp or \
653 + self.use != other.use or \
654 + self.operator != other.operator or \
655 + self.cpv != other.cpv:
656 + return False
657 +
658 + if self.slot is None or \
659 + other.slot is None or \
660 + self.slot == other.slot:
661 + return True
662 +
663 + return False
664 +
665 + def evaluate_conditionals(self, use):
666 + """
667 + Create an atom instance with any USE conditionals evaluated.
668 + @param use: The set of enabled USE flags
669 + @type use: set
670 + @rtype: Atom
671 + @return: an atom instance with any USE conditionals evaluated
672 + """
673 + if not (self.use and self.use.conditional):
674 + return self
675 + atom = remove_slot(self)
676 + if self.slot:
677 + atom += ":%s" % self.slot
678 + atom += str(self.use.evaluate_conditionals(use))
679 + return Atom(atom)
680 +
681 + def __copy__(self):
682 + """Immutable, so returns self."""
683 + return self
684 +
685 + def __deepcopy__(self, memo=None):
686 + """Immutable, so returns self."""
687 + memo[id(self)] = self
688 + return self
689 +
690 +def get_operator(mydep):
691 + """
692 + Return the operator used in a depstring.
693 +
694 + Example usage:
695 + >>> from portage.dep import *
696 + >>> get_operator(">=test-1.0")
697 + '>='
698 +
699 + @param mydep: The dep string to check
700 + @type mydep: String
701 + @rtype: String
702 + @return: The operator. One of:
703 + '~', '=', '>', '<', '=*', '>=', or '<='
704 + """
705 + if isinstance(mydep, Atom):
706 + return mydep.operator
707 + try:
708 + return Atom(mydep).operator
709 + except InvalidAtom:
710 + pass
711 +
712 + # Fall back to legacy code for backward compatibility.
713 + warnings.warn(_("%s is deprecated, use %s instead") % \
714 + ('portage.dep.get_operator()', 'portage.dep.Atom.operator'),
715 + DeprecationWarning)
716 + operator = None
717 + if mydep:
718 + mydep = remove_slot(mydep)
719 + if not mydep:
720 + return None
721 + if mydep[0] == "~":
722 + operator = "~"
723 + elif mydep[0] == "=":
724 + if mydep[-1] == "*":
725 + operator = "=*"
726 + else:
727 + operator = "="
728 + elif mydep[0] in "><":
729 + if len(mydep) > 1 and mydep[1] == "=":
730 + operator = mydep[0:2]
731 + else:
732 + operator = mydep[0]
733 + else:
734 + operator = None
735 +
736 + return operator
737 +
738 +def dep_getcpv(mydep):
739 + """
740 + Return the category-package-version with any operators/slot specifications stripped off
741 +
742 + Example usage:
743 + >>> dep_getcpv('>=media-libs/test-3.0')
744 + 'media-libs/test-3.0'
745 +
746 + @param mydep: The depstring
747 + @type mydep: String
748 + @rtype: String
749 + @return: The depstring with the operator removed
750 + """
751 + if isinstance(mydep, Atom):
752 + return mydep.cpv
753 + try:
754 + return Atom(mydep).cpv
755 + except InvalidAtom:
756 + pass
757 +
758 + # Fall back to legacy code for backward compatibility.
759 + warnings.warn(_("%s is deprecated, use %s instead") % \
760 + ('portage.dep.dep_getcpv()', 'portage.dep.Atom.cpv'),
761 + DeprecationWarning, stacklevel=2)
762 + mydep_orig = mydep
763 + if mydep:
764 + mydep = remove_slot(mydep)
765 + if mydep and mydep[0] == "*":
766 + mydep = mydep[1:]
767 + if mydep and mydep[-1] == "*":
768 + mydep = mydep[:-1]
769 + if mydep and mydep[0] == "!":
770 + if mydep[1:2] == "!":
771 + mydep = mydep[2:]
772 + else:
773 + mydep = mydep[1:]
774 + if mydep[:2] in [">=", "<="]:
775 + mydep = mydep[2:]
776 + elif mydep[:1] in "=<>~":
777 + mydep = mydep[1:]
778 + return mydep
779 +
780 +def dep_getslot(mydep):
781 + """
782 + Retrieve the slot on a depend.
783 +
784 + Example usage:
785 + >>> dep_getslot('app-misc/test:3')
786 + '3'
787 +
788 + @param mydep: The depstring to retrieve the slot of
789 + @type mydep: String
790 + @rtype: String
791 + @return: The slot
792 + """
793 + slot = getattr(mydep, "slot", False)
794 + if slot is not False:
795 + return slot
796 + colon = mydep.find(":")
797 + if colon != -1:
798 + bracket = mydep.find("[", colon)
799 + if bracket == -1:
800 + return mydep[colon+1:]
801 + else:
802 + return mydep[colon+1:bracket]
803 + return None
804 +
805 +def remove_slot(mydep):
806 + """
807 + Removes dep components from the right side of an atom:
808 + * slot
809 + * use
810 + * repo
811 + """
812 + colon = mydep.find(":")
813 + if colon != -1:
814 + mydep = mydep[:colon]
815 + else:
816 + bracket = mydep.find("[")
817 + if bracket != -1:
818 + mydep = mydep[:bracket]
819 + return mydep
820 +
821 +def dep_getusedeps( depend ):
822 + """
823 + Pull a listing of USE Dependencies out of a dep atom.
824 +
825 + Example usage:
826 + >>> dep_getusedeps('app-misc/test:3[foo,-bar]')
827 + ('foo', '-bar')
828 +
829 + @param depend: The depstring to process
830 + @type depend: String
831 + @rtype: List
832 + @return: List of use flags ( or [] if no flags exist )
833 + """
834 + use_list = []
835 + open_bracket = depend.find('[')
836 + # -1 = failure (think c++ string::npos)
837 + comma_separated = False
838 + bracket_count = 0
839 + while( open_bracket != -1 ):
840 + bracket_count += 1
841 + if bracket_count > 1:
842 + raise InvalidAtom(_("USE Dependency with more "
843 + "than one set of brackets: %s") % (depend,))
844 + close_bracket = depend.find(']', open_bracket )
845 + if close_bracket == -1:
846 + raise InvalidAtom(_("USE Dependency with no closing bracket: %s") % depend )
847 + use = depend[open_bracket + 1: close_bracket]
848 + # foo[1:1] may return '' instead of None, we don't want '' in the result
849 + if not use:
850 + raise InvalidAtom(_("USE Dependency with "
851 + "no use flag ([]): %s") % depend )
852 + if not comma_separated:
853 + comma_separated = "," in use
854 +
855 + if comma_separated and bracket_count > 1:
856 + raise InvalidAtom(_("USE Dependency contains a mixture of "
857 + "comma and bracket separators: %s") % depend )
858 +
859 + if comma_separated:
860 + for x in use.split(","):
861 + if x:
862 + use_list.append(x)
863 + else:
864 + raise InvalidAtom(_("USE Dependency with no use "
865 + "flag next to comma: %s") % depend )
866 + else:
867 + use_list.append(use)
868 +
869 + # Find next use flag
870 + open_bracket = depend.find( '[', open_bracket+1 )
871 + return tuple(use_list)
872 +
873 +# \w is [a-zA-Z0-9_]
874 +
875 +# 2.1.3 A slot name may contain any of the characters [A-Za-z0-9+_.-].
876 +# It must not begin with a hyphen or a dot.
877 +_slot = r'([\w+][\w+.-]*)'
878 +_slot_re = re.compile('^' + _slot + '$', re.VERBOSE)
879 +
880 +_use = r'\[.*\]'
881 +_op = r'([=~]|[><]=?)'
882 +
883 +_atom_re = re.compile('^(?P<without_use>(?:' +
884 + '(?P<op>' + _op + _cpv + ')|' +
885 + '(?P<star>=' + _cpv + r'\*)|' +
886 + '(?P<simple>' + _cp + '))(:' + _slot + ')?)(' + _use + ')?$', re.VERBOSE)
887 +
888 +def isvalidatom(atom, allow_blockers=False):
889 + """
890 + Check to see if a depend atom is valid
891 +
892 + Example usage:
893 + >>> isvalidatom('media-libs/test-3.0')
894 + False
895 + >>> isvalidatom('>=media-libs/test-3.0')
896 + True
897 +
898 + @param atom: The depend atom to check against
899 + @type atom: String or Atom
900 + @rtype: Boolean
901 + @return: One of the following:
902 + 1) False if the atom is invalid
903 + 2) True if the atom is valid
904 + """
905 + try:
906 + if not isinstance(atom, Atom):
907 + atom = Atom(atom)
908 + if not allow_blockers and atom.blocker:
909 + return False
910 + return True
911 + except InvalidAtom:
912 + return False
913 +
914 +def isjustname(mypkg):
915 + """
916 + Checks to see if the atom is only the package name (no version parts).
917 +
918 + Example usage:
919 + >>> isjustname('=media-libs/test-3.0')
920 + False
921 + >>> isjustname('media-libs/test')
922 + True
923 +
924 + @param mypkg: The package atom to check
925 + @param mypkg: String or Atom
926 + @rtype: Integer
927 + @return: One of the following:
928 + 1) False if the package string is not just the package name
929 + 2) True if it is
930 + """
931 + try:
932 + if not isinstance(mypkg, Atom):
933 + mypkg = Atom(mypkg)
934 + return mypkg == mypkg.cp
935 + except InvalidAtom:
936 + pass
937 +
938 + for x in mypkg.split('-')[-2:]:
939 + if ververify(x):
940 + return False
941 + return True
942 +
943 +def isspecific(mypkg):
944 + """
945 + Checks to see if a package is in =category/package-version or
946 + package-version format.
947 +
948 + Example usage:
949 + >>> isspecific('media-libs/test')
950 + False
951 + >>> isspecific('=media-libs/test-3.0')
952 + True
953 +
954 + @param mypkg: The package depstring to check against
955 + @type mypkg: String
956 + @rtype: Boolean
957 + @return: One of the following:
958 + 1) False if the package string is not specific
959 + 2) True if it is
960 + """
961 + try:
962 + if not isinstance(mypkg, Atom):
963 + mypkg = Atom(mypkg)
964 + return mypkg != mypkg.cp
965 + except InvalidAtom:
966 + pass
967 +
968 + # Fall back to legacy code for backward compatibility.
969 + return not isjustname(mypkg)
970 +
971 +def dep_getkey(mydep):
972 + """
973 + Return the category/package-name of a depstring.
974 +
975 + Example usage:
976 + >>> dep_getkey('=media-libs/test-3.0')
977 + 'media-libs/test'
978 +
979 + @param mydep: The depstring to retrieve the category/package-name of
980 + @type mydep: String
981 + @rtype: String
982 + @return: The package category/package-name
983 + """
984 + if isinstance(mydep, Atom):
985 + return mydep.cp
986 + try:
987 + return Atom(mydep).cp
988 + except InvalidAtom:
989 + try:
990 + atom = Atom('=' + mydep)
991 + except InvalidAtom:
992 + pass
993 + else:
994 + warnings.warn(_("invalid input to %s: '%s', use %s instead") % \
995 + ('portage.dep.dep_getkey()', mydep, 'portage.cpv_getkey()'),
996 + DeprecationWarning, stacklevel=2)
997 + return atom.cp
998 +
999 + # Fall back to legacy code for backward compatibility.
1000 + warnings.warn(_("%s is deprecated, use %s instead") % \
1001 + ('portage.dep.dep_getkey()', 'portage.dep.Atom.cp'),
1002 + DeprecationWarning, stacklevel=2)
1003 + mydep = dep_getcpv(mydep)
1004 + if mydep and isspecific(mydep):
1005 + mysplit = catpkgsplit(mydep)
1006 + if not mysplit:
1007 + return mydep
1008 + return mysplit[0] + "/" + mysplit[1]
1009 + else:
1010 + return mydep
1011 +
1012 +def match_to_list(mypkg, mylist):
1013 + """
1014 + Searches list for entries that matches the package.
1015 +
1016 + @param mypkg: The package atom to match
1017 + @type mypkg: String
1018 + @param mylist: The list of package atoms to compare against
1019 + @param mylist: List
1020 + @rtype: List
1021 + @return: A unique list of package atoms that match the given package atom
1022 + """
1023 + return [ x for x in set(mylist) if match_from_list(x, [mypkg]) ]
1024 +
1025 +def best_match_to_list(mypkg, mylist):
1026 + """
1027 + Returns the most specific entry that matches the package given.
1028 +
1029 + @param mypkg: The package atom to check
1030 + @type mypkg: String
1031 + @param mylist: The list of package atoms to check against
1032 + @type mylist: List
1033 + @rtype: String
1034 + @return: The package atom which best matches given the following ordering:
1035 + - =cpv 6
1036 + - ~cpv 5
1037 + - =cpv* 4
1038 + - cp:slot 3
1039 + - >cpv 2
1040 + - <cpv 2
1041 + - >=cpv 2
1042 + - <=cpv 2
1043 + - cp 1
1044 + """
1045 + operator_values = {'=':6, '~':5, '=*':4,
1046 + '>':2, '<':2, '>=':2, '<=':2, None:1}
1047 + maxvalue = 0
1048 + bestm = None
1049 + for x in match_to_list(mypkg, mylist):
1050 + if dep_getslot(x) is not None:
1051 + if maxvalue < 3:
1052 + maxvalue = 3
1053 + bestm = x
1054 + op_val = operator_values[x.operator]
1055 + if op_val > maxvalue:
1056 + maxvalue = op_val
1057 + bestm = x
1058 + return bestm
1059 +
1060 +def match_from_list(mydep, candidate_list):
1061 + """
1062 + Searches list for entries that matches the package.
1063 +
1064 + @param mydep: The package atom to match
1065 + @type mydep: String
1066 + @param candidate_list: The list of package atoms to compare against
1067 + @param candidate_list: List
1068 + @rtype: List
1069 + @return: A list of package atoms that match the given package atom
1070 + """
1071 +
1072 + if not candidate_list:
1073 + return []
1074 +
1075 + from portage.util import writemsg
1076 + if "!" == mydep[:1]:
1077 + mydep = mydep[1:]
1078 + if not isinstance(mydep, Atom):
1079 + mydep = Atom(mydep)
1080 +
1081 + mycpv = mydep.cpv
1082 + mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific
1083 + slot = mydep.slot
1084 +
1085 + if not mycpv_cps:
1086 + cat, pkg = catsplit(mycpv)
1087 + ver = None
1088 + rev = None
1089 + else:
1090 + cat, pkg, ver, rev = mycpv_cps
1091 + if mydep == mycpv:
1092 + raise KeyError(_("Specific key requires an operator"
1093 + " (%s) (try adding an '=')") % (mydep))
1094 +
1095 + if ver and rev:
1096 + operator = mydep.operator
1097 + if not operator:
1098 + writemsg(_("!!! Invalid atom: %s\n") % mydep, noiselevel=-1)
1099 + return []
1100 + else:
1101 + operator = None
1102 +
1103 + mylist = []
1104 +
1105 + if operator is None:
1106 + for x in candidate_list:
1107 + cp = getattr(x, "cp", None)
1108 + if cp is None:
1109 + mysplit = catpkgsplit(remove_slot(x))
1110 + if mysplit is not None:
1111 + cp = mysplit[0] + '/' + mysplit[1]
1112 + if cp != mycpv:
1113 + continue
1114 + mylist.append(x)
1115 +
1116 + elif operator == "=": # Exact match
1117 + for x in candidate_list:
1118 + xcpv = getattr(x, "cpv", None)
1119 + if xcpv is None:
1120 + xcpv = remove_slot(x)
1121 + if not cpvequal(xcpv, mycpv):
1122 + continue
1123 + mylist.append(x)
1124 +
1125 + elif operator == "=*": # glob match
1126 + # XXX: Nasty special casing for leading zeros
1127 + # Required as =* is a literal prefix match, so can't
1128 + # use vercmp
1129 + mysplit = catpkgsplit(mycpv)
1130 + myver = mysplit[2].lstrip("0")
1131 + if not myver or not myver[0].isdigit():
1132 + myver = "0"+myver
1133 + mycpv = mysplit[0]+"/"+mysplit[1]+"-"+myver
1134 + for x in candidate_list:
1135 + xs = getattr(x, "cpv_split", None)
1136 + if xs is None:
1137 + xs = catpkgsplit(remove_slot(x))
1138 + myver = xs[2].lstrip("0")
1139 + if not myver or not myver[0].isdigit():
1140 + myver = "0"+myver
1141 + xcpv = xs[0]+"/"+xs[1]+"-"+myver
1142 + if xcpv.startswith(mycpv):
1143 + mylist.append(x)
1144 +
1145 + elif operator == "~": # version, any revision, match
1146 + for x in candidate_list:
1147 + xs = getattr(x, "cpv_split", None)
1148 + if xs is None:
1149 + xs = catpkgsplit(remove_slot(x))
1150 + if xs is None:
1151 + raise InvalidData(x)
1152 + if not cpvequal(xs[0]+"/"+xs[1]+"-"+xs[2], mycpv_cps[0]+"/"+mycpv_cps[1]+"-"+mycpv_cps[2]):
1153 + continue
1154 + if xs[2] != ver:
1155 + continue
1156 + mylist.append(x)
1157 +
1158 + elif operator in [">", ">=", "<", "<="]:
1159 + mysplit = ["%s/%s" % (cat, pkg), ver, rev]
1160 + for x in candidate_list:
1161 + xs = getattr(x, "cpv_split", None)
1162 + if xs is None:
1163 + xs = catpkgsplit(remove_slot(x))
1164 + xcat, xpkg, xver, xrev = xs
1165 + xs = ["%s/%s" % (xcat, xpkg), xver, xrev]
1166 + try:
1167 + result = pkgcmp(xs, mysplit)
1168 + except ValueError: # pkgcmp may return ValueError during int() conversion
1169 + writemsg(_("\nInvalid package name: %s\n") % x, noiselevel=-1)
1170 + raise
1171 + if result is None:
1172 + continue
1173 + elif operator == ">":
1174 + if result > 0:
1175 + mylist.append(x)
1176 + elif operator == ">=":
1177 + if result >= 0:
1178 + mylist.append(x)
1179 + elif operator == "<":
1180 + if result < 0:
1181 + mylist.append(x)
1182 + elif operator == "<=":
1183 + if result <= 0:
1184 + mylist.append(x)
1185 + else:
1186 + raise KeyError(_("Unknown operator: %s") % mydep)
1187 + else:
1188 + raise KeyError(_("Unknown operator: %s") % mydep)
1189 +
1190 + if slot is not None:
1191 + candidate_list = mylist
1192 + mylist = []
1193 + for x in candidate_list:
1194 + xslot = getattr(x, "slot", False)
1195 + if xslot is False:
1196 + xslot = dep_getslot(x)
1197 + if xslot is not None and xslot != slot:
1198 + continue
1199 + mylist.append(x)
1200 +
1201 + if mydep.use:
1202 + candidate_list = mylist
1203 + mylist = []
1204 + for x in candidate_list:
1205 + use = getattr(x, "use", None)
1206 + if use is not None:
1207 + regex = x.iuse.regex
1208 + missing_iuse = False
1209 + for y in mydep.use.required:
1210 + if regex.match(y) is None:
1211 + missing_iuse = True
1212 + break
1213 + if missing_iuse:
1214 + continue
1215 + if mydep.use.enabled.difference(use.enabled):
1216 + continue
1217 + if mydep.use.disabled.intersection(use.enabled):
1218 + continue
1219 + mylist.append(x)
1220 +
1221 + return mylist
1222
1223 Deleted: main/trunk/pym/portage/dep.py
1224 ===================================================================
1225 --- main/trunk/pym/portage/dep.py 2010-02-25 20:42:04 UTC (rev 15460)
1226 +++ main/trunk/pym/portage/dep.py 2010-02-25 20:48:08 UTC (rev 15461)
1227 @@ -1,1203 +0,0 @@
1228 -# deps.py -- Portage dependency resolution functions
1229 -# Copyright 2003-2004 Gentoo Foundation
1230 -# Distributed under the terms of the GNU General Public License v2
1231 -# $Id$
1232 -
1233 -__all__ = [
1234 - 'Atom', 'best_match_to_list', 'cpvequal',
1235 - 'dep_getcpv', 'dep_getkey', 'dep_getslot',
1236 - 'dep_getusedeps', 'dep_opconvert', 'flatten',
1237 - 'get_operator', 'isjustname', 'isspecific',
1238 - 'isvalidatom', 'match_from_list', 'match_to_list',
1239 - 'paren_enclose', 'paren_normalize', 'paren_reduce',
1240 - 'remove_slot', 'strip_empty', 'use_reduce'
1241 -]
1242 -
1243 -# DEPEND SYNTAX:
1244 -#
1245 -# 'use?' only affects the immediately following word!
1246 -# Nesting is the only legal way to form multiple '[!]use?' requirements.
1247 -#
1248 -# Where: 'a' and 'b' are use flags, and 'z' is a depend atom.
1249 -#
1250 -# "a? z" -- If 'a' in [use], then b is valid.
1251 -# "a? ( z )" -- Syntax with parenthesis.
1252 -# "a? b? z" -- Deprecated.
1253 -# "a? ( b? z )" -- Valid
1254 -# "a? ( b? ( z ) ) -- Valid
1255 -#
1256 -
1257 -import re, sys
1258 -import warnings
1259 -from itertools import chain
1260 -import portage.exception
1261 -from portage.exception import InvalidData, InvalidAtom
1262 -from portage.localization import _
1263 -from portage.versions import catpkgsplit, catsplit, \
1264 - pkgcmp, pkgsplit, ververify, _cp, _cpv
1265 -import portage.cache.mappings
1266 -
1267 -if sys.hexversion >= 0x3000000:
1268 - basestring = str
1269 -
1270 -def cpvequal(cpv1, cpv2):
1271 - """
1272 -
1273 - @param cpv1: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1"
1274 - @type cpv1: String
1275 - @param cpv2: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1"
1276 - @type cpv2: String
1277 - @rtype: Boolean
1278 - @returns:
1279 - 1. True if cpv1 = cpv2
1280 - 2. False Otherwise
1281 - 3. Throws PortageException if cpv1 or cpv2 is not a CPV
1282 -
1283 - Example Usage:
1284 - >>> from portage.dep import cpvequal
1285 - >>> cpvequal("sys-apps/portage-2.1","sys-apps/portage-2.1")
1286 - >>> True
1287 -
1288 - """
1289 -
1290 - split1 = catpkgsplit(cpv1)
1291 - split2 = catpkgsplit(cpv2)
1292 -
1293 - if not split1 or not split2:
1294 - raise portage.exception.PortageException(_("Invalid data '%s, %s', parameter was not a CPV") % (cpv1, cpv2))
1295 -
1296 - if split1[0] != split2[0]:
1297 - return False
1298 -
1299 - return (pkgcmp(split1[1:], split2[1:]) == 0)
1300 -
1301 -def strip_empty(myarr):
1302 - """
1303 - Strip all empty elements from an array
1304 -
1305 - @param myarr: The list of elements
1306 - @type myarr: List
1307 - @rtype: Array
1308 - @return: The array with empty elements removed
1309 - """
1310 - return [x for x in myarr if x]
1311 -
1312 -_paren_whitespace_re = re.compile(r'\S(\(|\))|(\(|\))\S')
1313 -
1314 -def paren_reduce(mystr,tokenize=1):
1315 - """
1316 - Take a string and convert all paren enclosed entities into sublists, optionally
1317 - futher splitting the list elements by spaces.
1318 -
1319 - Example usage:
1320 - >>> paren_reduce('foobar foo ( bar baz )',1)
1321 - ['foobar', 'foo', ['bar', 'baz']]
1322 - >>> paren_reduce('foobar foo ( bar baz )',0)
1323 - ['foobar foo ', [' bar baz ']]
1324 -
1325 - @param mystr: The string to reduce
1326 - @type mystr: String
1327 - @param tokenize: Split on spaces to produces further list breakdown
1328 - @type tokenize: Integer
1329 - @rtype: Array
1330 - @return: The reduced string in an array
1331 - """
1332 - global _dep_check_strict, _paren_whitespace_re
1333 - if _dep_check_strict:
1334 - m = _paren_whitespace_re.search(mystr)
1335 - if m is not None:
1336 - raise portage.exception.InvalidDependString(
1337 - _("missing space by parenthesis: '%s'") % m.group(0))
1338 - mylist = []
1339 - while mystr:
1340 - left_paren = mystr.find("(")
1341 - has_left_paren = left_paren != -1
1342 - right_paren = mystr.find(")")
1343 - has_right_paren = right_paren != -1
1344 - if not has_left_paren and not has_right_paren:
1345 - freesec = mystr
1346 - subsec = None
1347 - tail = ""
1348 - elif mystr[0] == ")":
1349 - return [mylist,mystr[1:]]
1350 - elif has_left_paren and not has_right_paren:
1351 - raise portage.exception.InvalidDependString(
1352 - _("missing right parenthesis: '%s'") % mystr)
1353 - elif has_left_paren and left_paren < right_paren:
1354 - freesec,subsec = mystr.split("(",1)
1355 - sublist = paren_reduce(subsec, tokenize=tokenize)
1356 - if len(sublist) != 2:
1357 - raise portage.exception.InvalidDependString(
1358 - _("malformed syntax: '%s'") % mystr)
1359 - subsec, tail = sublist
1360 - else:
1361 - subsec,tail = mystr.split(")",1)
1362 - if tokenize:
1363 - subsec = strip_empty(subsec.split(" "))
1364 - return [mylist+subsec,tail]
1365 - return mylist+[subsec],tail
1366 - if not isinstance(tail, basestring):
1367 - raise portage.exception.InvalidDependString(
1368 - _("malformed syntax: '%s'") % mystr)
1369 - mystr = tail
1370 - if freesec:
1371 - if tokenize:
1372 - mylist = mylist + strip_empty(freesec.split(" "))
1373 - else:
1374 - mylist = mylist + [freesec]
1375 - if subsec is not None:
1376 - mylist = mylist + [subsec]
1377 - return mylist
1378 -
1379 -class paren_normalize(list):
1380 - """Take a dependency structure as returned by paren_reduce or use_reduce
1381 - and generate an equivalent structure that has no redundant lists."""
1382 - def __init__(self, src):
1383 - list.__init__(self)
1384 - self._zap_parens(src, self)
1385 -
1386 - def _zap_parens(self, src, dest, disjunction=False):
1387 - if not src:
1388 - return dest
1389 - i = iter(src)
1390 - for x in i:
1391 - if isinstance(x, basestring):
1392 - if x == '||':
1393 - x = self._zap_parens(next(i), [], disjunction=True)
1394 - if len(x) == 1:
1395 - dest.append(x[0])
1396 - else:
1397 - dest.append("||")
1398 - dest.append(x)
1399 - elif x.endswith("?"):
1400 - dest.append(x)
1401 - dest.append(self._zap_parens(next(i), []))
1402 - else:
1403 - dest.append(x)
1404 - else:
1405 - if disjunction:
1406 - x = self._zap_parens(x, [])
1407 - if len(x) == 1:
1408 - dest.append(x[0])
1409 - else:
1410 - dest.append(x)
1411 - else:
1412 - self._zap_parens(x, dest)
1413 - return dest
1414 -
1415 -def paren_enclose(mylist):
1416 - """
1417 - Convert a list to a string with sublists enclosed with parens.
1418 -
1419 - Example usage:
1420 - >>> test = ['foobar','foo',['bar','baz']]
1421 - >>> paren_enclose(test)
1422 - 'foobar foo ( bar baz )'
1423 -
1424 - @param mylist: The list
1425 - @type mylist: List
1426 - @rtype: String
1427 - @return: The paren enclosed string
1428 - """
1429 - mystrparts = []
1430 - for x in mylist:
1431 - if isinstance(x, list):
1432 - mystrparts.append("( "+paren_enclose(x)+" )")
1433 - else:
1434 - mystrparts.append(x)
1435 - return " ".join(mystrparts)
1436 -
1437 -# This is just for use by emerge so that it can enable a backward compatibility
1438 -# mode in order to gracefully deal with installed packages that have invalid
1439 -# atoms or dep syntax. For backward compatibility with api consumers, strict
1440 -# behavior will be explicitly enabled as necessary.
1441 -_dep_check_strict = False
1442 -
1443 -def use_reduce(deparray, uselist=[], masklist=[], matchall=0, excludeall=[]):
1444 - """
1445 - Takes a paren_reduce'd array and reduces the use? conditionals out
1446 - leaving an array with subarrays
1447 -
1448 - @param deparray: paren_reduce'd list of deps
1449 - @type deparray: List
1450 - @param uselist: List of use flags
1451 - @type uselist: List
1452 - @param masklist: List of masked flags
1453 - @type masklist: List
1454 - @param matchall: Resolve all conditional deps unconditionally. Used by repoman
1455 - @type matchall: Integer
1456 - @rtype: List
1457 - @return: The use reduced depend array
1458 - """
1459 - # Quick validity checks
1460 - for x, y in enumerate(deparray):
1461 - if y == '||':
1462 - if len(deparray) - 1 == x or not isinstance(deparray[x+1], list):
1463 - raise portage.exception.InvalidDependString(_('%(dep)s missing atom list in "%(deparray)s"') % {"dep": deparray[x], "deparray": paren_enclose(deparray)})
1464 - if deparray and deparray[-1] and deparray[-1][-1] == "?":
1465 - raise portage.exception.InvalidDependString(_('Conditional without target in "%s"') % paren_enclose(deparray))
1466 -
1467 - global _dep_check_strict
1468 -
1469 - mydeparray = deparray[:]
1470 - rlist = []
1471 - while mydeparray:
1472 - head = mydeparray.pop(0)
1473 -
1474 - if not isinstance(head, basestring):
1475 - additions = use_reduce(head, uselist, masklist, matchall, excludeall)
1476 - if additions:
1477 - rlist.append(additions)
1478 - elif rlist and rlist[-1] == "||":
1479 - #XXX: Currently some DEPEND strings have || lists without default atoms.
1480 - # raise portage.exception.InvalidDependString("No default atom(s) in \""+paren_enclose(deparray)+"\"")
1481 - rlist.append([])
1482 -
1483 - else:
1484 - if head[-1:] == "?": # Use reduce next group on fail.
1485 - # Pull any other use conditions and the following atom or list into a separate array
1486 - newdeparray = [head]
1487 - while isinstance(newdeparray[-1], basestring) and \
1488 - newdeparray[-1][-1:] == "?":
1489 - if mydeparray:
1490 - newdeparray.append(mydeparray.pop(0))
1491 - else:
1492 - raise ValueError(_("Conditional with no target."))
1493 -
1494 - # Deprecation checks
1495 - warned = 0
1496 - if len(newdeparray[-1]) == 0:
1497 - sys.stderr.write(_("Note: Empty target in string. (Deprecated)\n"))
1498 - warned = 1
1499 - if len(newdeparray) != 2:
1500 - sys.stderr.write(_("Note: Nested use flags without parenthesis (Deprecated)\n"))
1501 - warned = 1
1502 - if warned:
1503 - sys.stderr.write(" --> "+" ".join(map(str,[head]+newdeparray))+"\n")
1504 -
1505 - # Check that each flag matches
1506 - ismatch = True
1507 - missing_flag = False
1508 - for head in newdeparray[:-1]:
1509 - head = head[:-1]
1510 - if not head:
1511 - missing_flag = True
1512 - break
1513 - if head.startswith("!"):
1514 - head_key = head[1:]
1515 - if not head_key:
1516 - missing_flag = True
1517 - break
1518 - if not matchall and head_key in uselist or \
1519 - head_key in excludeall:
1520 - ismatch = False
1521 - break
1522 - elif head not in masklist:
1523 - if not matchall and head not in uselist:
1524 - ismatch = False
1525 - break
1526 - else:
1527 - ismatch = False
1528 - if missing_flag:
1529 - raise portage.exception.InvalidDependString(
1530 - _('Conditional without flag: "') + \
1531 - paren_enclose([head+"?", newdeparray[-1]])+"\"")
1532 -
1533 - # If they all match, process the target
1534 - if ismatch:
1535 - target = newdeparray[-1]
1536 - if isinstance(target, list):
1537 - additions = use_reduce(target, uselist, masklist, matchall, excludeall)
1538 - if additions:
1539 - rlist.append(additions)
1540 - elif not _dep_check_strict:
1541 - # The old deprecated behavior.
1542 - rlist.append(target)
1543 - else:
1544 - raise portage.exception.InvalidDependString(
1545 - _("Conditional without parenthesis: '%s?'") % head)
1546 -
1547 - else:
1548 - rlist += [head]
1549 -
1550 - return rlist
1551 -
1552 -def dep_opconvert(deplist):
1553 - """
1554 - Iterate recursively through a list of deps, if the
1555 - dep is a '||' or '&&' operator, combine it with the
1556 - list of deps that follows..
1557 -
1558 - Example usage:
1559 - >>> test = ["blah", "||", ["foo", "bar", "baz"]]
1560 - >>> dep_opconvert(test)
1561 - ['blah', ['||', 'foo', 'bar', 'baz']]
1562 -
1563 - @param deplist: A list of deps to format
1564 - @type mydep: List
1565 - @rtype: List
1566 - @return:
1567 - The new list with the new ordering
1568 - """
1569 -
1570 - retlist = []
1571 - x = 0
1572 - while x != len(deplist):
1573 - if isinstance(deplist[x], list):
1574 - retlist.append(dep_opconvert(deplist[x]))
1575 - elif deplist[x] == "||" or deplist[x] == "&&":
1576 - retlist.append([deplist[x]] + dep_opconvert(deplist[x+1]))
1577 - x += 1
1578 - else:
1579 - retlist.append(deplist[x])
1580 - x += 1
1581 - return retlist
1582 -
1583 -def flatten(mylist):
1584 - """
1585 - Recursively traverse nested lists and return a single list containing
1586 - all non-list elements that are found.
1587 -
1588 - Example usage:
1589 - >>> flatten([1, [2, 3, [4]]])
1590 - [1, 2, 3, 4]
1591 -
1592 - @param mylist: A list containing nested lists and non-list elements.
1593 - @type mylist: List
1594 - @rtype: List
1595 - @return: A single list containing only non-list elements.
1596 - """
1597 - newlist = []
1598 - for x in mylist:
1599 - if isinstance(x, list):
1600 - newlist.extend(flatten(x))
1601 - else:
1602 - newlist.append(x)
1603 - return newlist
1604 -
1605 -class _use_dep(object):
1606 -
1607 - __slots__ = ("__weakref__", "conditional",
1608 - "disabled", "enabled", "tokens", "required")
1609 -
1610 - _conditionals_class = portage.cache.mappings.slot_dict_class(
1611 - ("disabled", "enabled", "equal", "not_equal"), prefix="")
1612 -
1613 - _valid_use_re = re.compile(r'^[^-?!=][^?!=]*$')
1614 -
1615 - def __init__(self, use):
1616 - enabled_flags = []
1617 - disabled_flags = []
1618 - conditional = self._conditionals_class()
1619 - for k in conditional.allowed_keys:
1620 - conditional[k] = []
1621 -
1622 - for x in use:
1623 - last_char = x[-1:]
1624 - first_char = x[:1]
1625 -
1626 - if "?" == last_char:
1627 - if "!" == first_char:
1628 - conditional.disabled.append(
1629 - self._validate_flag(x, x[1:-1]))
1630 - else:
1631 - conditional.enabled.append(
1632 - self._validate_flag(x, x[:-1]))
1633 -
1634 - elif "=" == last_char:
1635 - if "!" == first_char:
1636 - conditional.not_equal.append(
1637 - self._validate_flag(x, x[1:-1]))
1638 - else:
1639 - conditional.equal.append(
1640 - self._validate_flag(x, x[:-1]))
1641 -
1642 - else:
1643 - if "-" == first_char:
1644 - disabled_flags.append(self._validate_flag(x, x[1:]))
1645 - else:
1646 - enabled_flags.append(self._validate_flag(x, x))
1647 -
1648 - self.tokens = use
1649 - if not isinstance(self.tokens, tuple):
1650 - self.tokens = tuple(self.tokens)
1651 -
1652 - self.required = frozenset(chain(
1653 - enabled_flags,
1654 - disabled_flags,
1655 - *conditional.values()
1656 - ))
1657 -
1658 - self.enabled = frozenset(enabled_flags)
1659 - self.disabled = frozenset(disabled_flags)
1660 - self.conditional = None
1661 -
1662 - for v in conditional.values():
1663 - if v:
1664 - for k, v in conditional.items():
1665 - conditional[k] = frozenset(v)
1666 - self.conditional = conditional
1667 - break
1668 -
1669 - def _validate_flag(self, token, flag):
1670 - if self._valid_use_re.match(flag) is None:
1671 - raise InvalidAtom(_("Invalid use dep: '%s'") % (token,))
1672 - return flag
1673 -
1674 - def __bool__(self):
1675 - return bool(self.tokens)
1676 -
1677 - if sys.hexversion < 0x3000000:
1678 - __nonzero__ = __bool__
1679 -
1680 - def __str__(self):
1681 - if not self.tokens:
1682 - return ""
1683 - return "[%s]" % (",".join(self.tokens),)
1684 -
1685 - def __repr__(self):
1686 - return "portage.dep._use_dep(%s)" % repr(self.tokens)
1687 -
1688 - def evaluate_conditionals(self, use):
1689 - """
1690 - Create a new instance with conditionals evaluated.
1691 -
1692 - Conditional evaluation behavior:
1693 -
1694 - parent state conditional result
1695 -
1696 - x x? x
1697 - -x x?
1698 - x !x?
1699 - -x !x? -x
1700 -
1701 - x x= x
1702 - -x x= -x
1703 - x !x= -x
1704 - -x !x= x
1705 -
1706 - Conditional syntax examples:
1707 -
1708 - Compact Form Equivalent Expanded Form
1709 -
1710 - foo[bar?] bar? ( foo[bar] ) !bar? ( foo )
1711 - foo[!bar?] bar? ( foo ) !bar? ( foo[-bar] )
1712 - foo[bar=] bar? ( foo[bar] ) !bar? ( foo[-bar] )
1713 - foo[!bar=] bar? ( foo[-bar] ) !bar? ( foo[bar] )
1714 -
1715 - """
1716 - tokens = []
1717 -
1718 - conditional = self.conditional
1719 - tokens.extend(self.enabled)
1720 - tokens.extend("-" + x for x in self.disabled)
1721 - tokens.extend(x for x in conditional.enabled if x in use)
1722 - tokens.extend("-" + x for x in conditional.disabled if x not in use)
1723 -
1724 - tokens.extend(x for x in conditional.equal if x in use)
1725 - tokens.extend("-" + x for x in conditional.equal if x not in use)
1726 - tokens.extend("-" + x for x in conditional.not_equal if x in use)
1727 - tokens.extend(x for x in conditional.not_equal if x not in use)
1728 -
1729 - return _use_dep(tokens)
1730 -
1731 - def _eval_qa_conditionals(self, use_mask, use_force):
1732 - """
1733 - For repoman, evaluate all possible combinations within the constraints
1734 - of the given use.force and use.mask settings. The result may seem
1735 - ambiguous in the sense that the same flag can be in both the enabled
1736 - and disabled sets, but this is useful within the context of how its
1737 - intended to be used by repoman. It is assumed that the caller has
1738 - already ensured that there is no intersection between the given
1739 - use_mask and use_force sets when necessary.
1740 - """
1741 - tokens = []
1742 -
1743 - conditional = self.conditional
1744 - tokens.extend(self.enabled)
1745 - tokens.extend("-" + x for x in self.disabled)
1746 - tokens.extend(x for x in conditional.enabled if x not in use_mask)
1747 - tokens.extend("-" + x for x in conditional.disabled if x not in use_force)
1748 -
1749 - tokens.extend(x for x in conditional.equal if x not in use_mask)
1750 - tokens.extend("-" + x for x in conditional.equal if x not in use_force)
1751 - tokens.extend("-" + x for x in conditional.not_equal if x not in use_mask)
1752 - tokens.extend(x for x in conditional.not_equal if x not in use_force)
1753 -
1754 - return _use_dep(tokens)
1755 -
1756 -if sys.hexversion < 0x3000000:
1757 - _atom_base = unicode
1758 -else:
1759 - _atom_base = str
1760 -
1761 -class Atom(_atom_base):
1762 -
1763 - """
1764 - For compatibility with existing atom string manipulation code, this
1765 - class emulates most of the str methods that are useful with atoms.
1766 - """
1767 -
1768 - class _blocker(object):
1769 - __slots__ = ("overlap",)
1770 -
1771 - class _overlap(object):
1772 - __slots__ = ("forbid",)
1773 -
1774 - def __init__(self, forbid=False):
1775 - self.forbid = forbid
1776 -
1777 - def __init__(self, forbid_overlap=False):
1778 - self.overlap = self._overlap(forbid=forbid_overlap)
1779 -
1780 - def __init__(self, s):
1781 - if isinstance(s, Atom):
1782 - # This is an efficiency assertion, to ensure that the Atom
1783 - # constructor is not called redundantly.
1784 - raise TypeError(_("Expected %s, got %s") % \
1785 - (_atom_base, type(s)))
1786 -
1787 - _atom_base.__init__(s)
1788 -
1789 - if "!" == s[:1]:
1790 - blocker = self._blocker(forbid_overlap=("!" == s[1:2]))
1791 - if blocker.overlap.forbid:
1792 - s = s[2:]
1793 - else:
1794 - s = s[1:]
1795 - else:
1796 - blocker = False
1797 - self.__dict__['blocker'] = blocker
1798 - m = _atom_re.match(s)
1799 - if m is None:
1800 - raise InvalidAtom(self)
1801 -
1802 - if m.group('op') is not None:
1803 - base = _atom_re.groupindex['op']
1804 - op = m.group(base + 1)
1805 - cpv = m.group(base + 2)
1806 - cp = m.group(base + 3)
1807 - if m.group(base + 4) is not None:
1808 - raise InvalidAtom(self)
1809 - elif m.group('star') is not None:
1810 - base = _atom_re.groupindex['star']
1811 - op = '=*'
1812 - cpv = m.group(base + 1)
1813 - cp = m.group(base + 2)
1814 - if m.group(base + 3) is not None:
1815 - raise InvalidAtom(self)
1816 - elif m.group('simple') is not None:
1817 - op = None
1818 - cpv = cp = m.group(_atom_re.groupindex['simple'] + 1)
1819 - if m.group(_atom_re.groupindex['simple'] + 2) is not None:
1820 - raise InvalidAtom(self)
1821 - else:
1822 - raise AssertionError(_("required group not found in atom: '%s'") % self)
1823 - self.__dict__['cp'] = cp
1824 - self.__dict__['cpv'] = cpv
1825 - self.__dict__['slot'] = m.group(_atom_re.groups - 1)
1826 - self.__dict__['operator'] = op
1827 -
1828 - use_str = m.group(_atom_re.groups)
1829 - if use_str is not None:
1830 - use = _use_dep(dep_getusedeps(s))
1831 - without_use = Atom(m.group('without_use'))
1832 - else:
1833 - use = None
1834 - without_use = self
1835 -
1836 - self.__dict__['use'] = use
1837 - self.__dict__['without_use'] = without_use
1838 -
1839 - def __setattr__(self, name, value):
1840 - raise AttributeError("Atom instances are immutable",
1841 - self.__class__, name, value)
1842 -
1843 - def intersects(self, other):
1844 - """
1845 - Atoms with different cpv, operator or use attributes cause this method
1846 - to return False even though there may actually be some intersection.
1847 - TODO: Detect more forms of intersection.
1848 - @param other: The package atom to match
1849 - @type other: Atom
1850 - @rtype: Boolean
1851 - @return: True if this atom and the other atom intersect,
1852 - False otherwise.
1853 - """
1854 - if not isinstance(other, Atom):
1855 - raise TypeError("expected %s, got %s" % \
1856 - (Atom, type(other)))
1857 -
1858 - if self == other:
1859 - return True
1860 -
1861 - if self.cp != other.cp or \
1862 - self.use != other.use or \
1863 - self.operator != other.operator or \
1864 - self.cpv != other.cpv:
1865 - return False
1866 -
1867 - if self.slot is None or \
1868 - other.slot is None or \
1869 - self.slot == other.slot:
1870 - return True
1871 -
1872 - return False
1873 -
1874 - def evaluate_conditionals(self, use):
1875 - """
1876 - Create an atom instance with any USE conditionals evaluated.
1877 - @param use: The set of enabled USE flags
1878 - @type use: set
1879 - @rtype: Atom
1880 - @return: an atom instance with any USE conditionals evaluated
1881 - """
1882 - if not (self.use and self.use.conditional):
1883 - return self
1884 - atom = remove_slot(self)
1885 - if self.slot:
1886 - atom += ":%s" % self.slot
1887 - atom += str(self.use.evaluate_conditionals(use))
1888 - return Atom(atom)
1889 -
1890 - def __copy__(self):
1891 - """Immutable, so returns self."""
1892 - return self
1893 -
1894 - def __deepcopy__(self, memo=None):
1895 - """Immutable, so returns self."""
1896 - memo[id(self)] = self
1897 - return self
1898 -
1899 -def get_operator(mydep):
1900 - """
1901 - Return the operator used in a depstring.
1902 -
1903 - Example usage:
1904 - >>> from portage.dep import *
1905 - >>> get_operator(">=test-1.0")
1906 - '>='
1907 -
1908 - @param mydep: The dep string to check
1909 - @type mydep: String
1910 - @rtype: String
1911 - @return: The operator. One of:
1912 - '~', '=', '>', '<', '=*', '>=', or '<='
1913 - """
1914 - if isinstance(mydep, Atom):
1915 - return mydep.operator
1916 - try:
1917 - return Atom(mydep).operator
1918 - except InvalidAtom:
1919 - pass
1920 -
1921 - # Fall back to legacy code for backward compatibility.
1922 - warnings.warn(_("%s is deprecated, use %s instead") % \
1923 - ('portage.dep.get_operator()', 'portage.dep.Atom.operator'),
1924 - DeprecationWarning)
1925 - operator = None
1926 - if mydep:
1927 - mydep = remove_slot(mydep)
1928 - if not mydep:
1929 - return None
1930 - if mydep[0] == "~":
1931 - operator = "~"
1932 - elif mydep[0] == "=":
1933 - if mydep[-1] == "*":
1934 - operator = "=*"
1935 - else:
1936 - operator = "="
1937 - elif mydep[0] in "><":
1938 - if len(mydep) > 1 and mydep[1] == "=":
1939 - operator = mydep[0:2]
1940 - else:
1941 - operator = mydep[0]
1942 - else:
1943 - operator = None
1944 -
1945 - return operator
1946 -
1947 -def dep_getcpv(mydep):
1948 - """
1949 - Return the category-package-version with any operators/slot specifications stripped off
1950 -
1951 - Example usage:
1952 - >>> dep_getcpv('>=media-libs/test-3.0')
1953 - 'media-libs/test-3.0'
1954 -
1955 - @param mydep: The depstring
1956 - @type mydep: String
1957 - @rtype: String
1958 - @return: The depstring with the operator removed
1959 - """
1960 - if isinstance(mydep, Atom):
1961 - return mydep.cpv
1962 - try:
1963 - return Atom(mydep).cpv
1964 - except InvalidAtom:
1965 - pass
1966 -
1967 - # Fall back to legacy code for backward compatibility.
1968 - warnings.warn(_("%s is deprecated, use %s instead") % \
1969 - ('portage.dep.dep_getcpv()', 'portage.dep.Atom.cpv'),
1970 - DeprecationWarning, stacklevel=2)
1971 - mydep_orig = mydep
1972 - if mydep:
1973 - mydep = remove_slot(mydep)
1974 - if mydep and mydep[0] == "*":
1975 - mydep = mydep[1:]
1976 - if mydep and mydep[-1] == "*":
1977 - mydep = mydep[:-1]
1978 - if mydep and mydep[0] == "!":
1979 - if mydep[1:2] == "!":
1980 - mydep = mydep[2:]
1981 - else:
1982 - mydep = mydep[1:]
1983 - if mydep[:2] in [">=", "<="]:
1984 - mydep = mydep[2:]
1985 - elif mydep[:1] in "=<>~":
1986 - mydep = mydep[1:]
1987 - return mydep
1988 -
1989 -def dep_getslot(mydep):
1990 - """
1991 - Retrieve the slot on a depend.
1992 -
1993 - Example usage:
1994 - >>> dep_getslot('app-misc/test:3')
1995 - '3'
1996 -
1997 - @param mydep: The depstring to retrieve the slot of
1998 - @type mydep: String
1999 - @rtype: String
2000 - @return: The slot
2001 - """
2002 - slot = getattr(mydep, "slot", False)
2003 - if slot is not False:
2004 - return slot
2005 - colon = mydep.find(":")
2006 - if colon != -1:
2007 - bracket = mydep.find("[", colon)
2008 - if bracket == -1:
2009 - return mydep[colon+1:]
2010 - else:
2011 - return mydep[colon+1:bracket]
2012 - return None
2013 -
2014 -def remove_slot(mydep):
2015 - """
2016 - Removes dep components from the right side of an atom:
2017 - * slot
2018 - * use
2019 - * repo
2020 - """
2021 - colon = mydep.find(":")
2022 - if colon != -1:
2023 - mydep = mydep[:colon]
2024 - else:
2025 - bracket = mydep.find("[")
2026 - if bracket != -1:
2027 - mydep = mydep[:bracket]
2028 - return mydep
2029 -
2030 -def dep_getusedeps( depend ):
2031 - """
2032 - Pull a listing of USE Dependencies out of a dep atom.
2033 -
2034 - Example usage:
2035 - >>> dep_getusedeps('app-misc/test:3[foo,-bar]')
2036 - ('foo', '-bar')
2037 -
2038 - @param depend: The depstring to process
2039 - @type depend: String
2040 - @rtype: List
2041 - @return: List of use flags ( or [] if no flags exist )
2042 - """
2043 - use_list = []
2044 - open_bracket = depend.find('[')
2045 - # -1 = failure (think c++ string::npos)
2046 - comma_separated = False
2047 - bracket_count = 0
2048 - while( open_bracket != -1 ):
2049 - bracket_count += 1
2050 - if bracket_count > 1:
2051 - raise InvalidAtom(_("USE Dependency with more "
2052 - "than one set of brackets: %s") % (depend,))
2053 - close_bracket = depend.find(']', open_bracket )
2054 - if close_bracket == -1:
2055 - raise InvalidAtom(_("USE Dependency with no closing bracket: %s") % depend )
2056 - use = depend[open_bracket + 1: close_bracket]
2057 - # foo[1:1] may return '' instead of None, we don't want '' in the result
2058 - if not use:
2059 - raise InvalidAtom(_("USE Dependency with "
2060 - "no use flag ([]): %s") % depend )
2061 - if not comma_separated:
2062 - comma_separated = "," in use
2063 -
2064 - if comma_separated and bracket_count > 1:
2065 - raise InvalidAtom(_("USE Dependency contains a mixture of "
2066 - "comma and bracket separators: %s") % depend )
2067 -
2068 - if comma_separated:
2069 - for x in use.split(","):
2070 - if x:
2071 - use_list.append(x)
2072 - else:
2073 - raise InvalidAtom(_("USE Dependency with no use "
2074 - "flag next to comma: %s") % depend )
2075 - else:
2076 - use_list.append(use)
2077 -
2078 - # Find next use flag
2079 - open_bracket = depend.find( '[', open_bracket+1 )
2080 - return tuple(use_list)
2081 -
2082 -# \w is [a-zA-Z0-9_]
2083 -
2084 -# 2.1.3 A slot name may contain any of the characters [A-Za-z0-9+_.-].
2085 -# It must not begin with a hyphen or a dot.
2086 -_slot = r'([\w+][\w+.-]*)'
2087 -_slot_re = re.compile('^' + _slot + '$', re.VERBOSE)
2088 -
2089 -_use = r'\[.*\]'
2090 -_op = r'([=~]|[><]=?)'
2091 -
2092 -_atom_re = re.compile('^(?P<without_use>(?:' +
2093 - '(?P<op>' + _op + _cpv + ')|' +
2094 - '(?P<star>=' + _cpv + r'\*)|' +
2095 - '(?P<simple>' + _cp + '))(:' + _slot + ')?)(' + _use + ')?$', re.VERBOSE)
2096 -
2097 -def isvalidatom(atom, allow_blockers=False):
2098 - """
2099 - Check to see if a depend atom is valid
2100 -
2101 - Example usage:
2102 - >>> isvalidatom('media-libs/test-3.0')
2103 - False
2104 - >>> isvalidatom('>=media-libs/test-3.0')
2105 - True
2106 -
2107 - @param atom: The depend atom to check against
2108 - @type atom: String or Atom
2109 - @rtype: Boolean
2110 - @return: One of the following:
2111 - 1) False if the atom is invalid
2112 - 2) True if the atom is valid
2113 - """
2114 - try:
2115 - if not isinstance(atom, Atom):
2116 - atom = Atom(atom)
2117 - if not allow_blockers and atom.blocker:
2118 - return False
2119 - return True
2120 - except InvalidAtom:
2121 - return False
2122 -
2123 -def isjustname(mypkg):
2124 - """
2125 - Checks to see if the atom is only the package name (no version parts).
2126 -
2127 - Example usage:
2128 - >>> isjustname('=media-libs/test-3.0')
2129 - False
2130 - >>> isjustname('media-libs/test')
2131 - True
2132 -
2133 - @param mypkg: The package atom to check
2134 - @param mypkg: String or Atom
2135 - @rtype: Integer
2136 - @return: One of the following:
2137 - 1) False if the package string is not just the package name
2138 - 2) True if it is
2139 - """
2140 - try:
2141 - if not isinstance(mypkg, Atom):
2142 - mypkg = Atom(mypkg)
2143 - return mypkg == mypkg.cp
2144 - except InvalidAtom:
2145 - pass
2146 -
2147 - for x in mypkg.split('-')[-2:]:
2148 - if ververify(x):
2149 - return False
2150 - return True
2151 -
2152 -def isspecific(mypkg):
2153 - """
2154 - Checks to see if a package is in =category/package-version or
2155 - package-version format.
2156 -
2157 - Example usage:
2158 - >>> isspecific('media-libs/test')
2159 - False
2160 - >>> isspecific('=media-libs/test-3.0')
2161 - True
2162 -
2163 - @param mypkg: The package depstring to check against
2164 - @type mypkg: String
2165 - @rtype: Boolean
2166 - @return: One of the following:
2167 - 1) False if the package string is not specific
2168 - 2) True if it is
2169 - """
2170 - try:
2171 - if not isinstance(mypkg, Atom):
2172 - mypkg = Atom(mypkg)
2173 - return mypkg != mypkg.cp
2174 - except InvalidAtom:
2175 - pass
2176 -
2177 - # Fall back to legacy code for backward compatibility.
2178 - return not isjustname(mypkg)
2179 -
2180 -def dep_getkey(mydep):
2181 - """
2182 - Return the category/package-name of a depstring.
2183 -
2184 - Example usage:
2185 - >>> dep_getkey('=media-libs/test-3.0')
2186 - 'media-libs/test'
2187 -
2188 - @param mydep: The depstring to retrieve the category/package-name of
2189 - @type mydep: String
2190 - @rtype: String
2191 - @return: The package category/package-name
2192 - """
2193 - if isinstance(mydep, Atom):
2194 - return mydep.cp
2195 - try:
2196 - return Atom(mydep).cp
2197 - except InvalidAtom:
2198 - try:
2199 - atom = Atom('=' + mydep)
2200 - except InvalidAtom:
2201 - pass
2202 - else:
2203 - warnings.warn(_("invalid input to %s: '%s', use %s instead") % \
2204 - ('portage.dep.dep_getkey()', mydep, 'portage.cpv_getkey()'),
2205 - DeprecationWarning, stacklevel=2)
2206 - return atom.cp
2207 -
2208 - # Fall back to legacy code for backward compatibility.
2209 - warnings.warn(_("%s is deprecated, use %s instead") % \
2210 - ('portage.dep.dep_getkey()', 'portage.dep.Atom.cp'),
2211 - DeprecationWarning, stacklevel=2)
2212 - mydep = dep_getcpv(mydep)
2213 - if mydep and isspecific(mydep):
2214 - mysplit = catpkgsplit(mydep)
2215 - if not mysplit:
2216 - return mydep
2217 - return mysplit[0] + "/" + mysplit[1]
2218 - else:
2219 - return mydep
2220 -
2221 -def match_to_list(mypkg, mylist):
2222 - """
2223 - Searches list for entries that matches the package.
2224 -
2225 - @param mypkg: The package atom to match
2226 - @type mypkg: String
2227 - @param mylist: The list of package atoms to compare against
2228 - @param mylist: List
2229 - @rtype: List
2230 - @return: A unique list of package atoms that match the given package atom
2231 - """
2232 - return [ x for x in set(mylist) if match_from_list(x, [mypkg]) ]
2233 -
2234 -def best_match_to_list(mypkg, mylist):
2235 - """
2236 - Returns the most specific entry that matches the package given.
2237 -
2238 - @param mypkg: The package atom to check
2239 - @type mypkg: String
2240 - @param mylist: The list of package atoms to check against
2241 - @type mylist: List
2242 - @rtype: String
2243 - @return: The package atom which best matches given the following ordering:
2244 - - =cpv 6
2245 - - ~cpv 5
2246 - - =cpv* 4
2247 - - cp:slot 3
2248 - - >cpv 2
2249 - - <cpv 2
2250 - - >=cpv 2
2251 - - <=cpv 2
2252 - - cp 1
2253 - """
2254 - operator_values = {'=':6, '~':5, '=*':4,
2255 - '>':2, '<':2, '>=':2, '<=':2, None:1}
2256 - maxvalue = 0
2257 - bestm = None
2258 - for x in match_to_list(mypkg, mylist):
2259 - if dep_getslot(x) is not None:
2260 - if maxvalue < 3:
2261 - maxvalue = 3
2262 - bestm = x
2263 - op_val = operator_values[x.operator]
2264 - if op_val > maxvalue:
2265 - maxvalue = op_val
2266 - bestm = x
2267 - return bestm
2268 -
2269 -def match_from_list(mydep, candidate_list):
2270 - """
2271 - Searches list for entries that matches the package.
2272 -
2273 - @param mydep: The package atom to match
2274 - @type mydep: String
2275 - @param candidate_list: The list of package atoms to compare against
2276 - @param candidate_list: List
2277 - @rtype: List
2278 - @return: A list of package atoms that match the given package atom
2279 - """
2280 -
2281 - if not candidate_list:
2282 - return []
2283 -
2284 - from portage.util import writemsg
2285 - if "!" == mydep[:1]:
2286 - mydep = mydep[1:]
2287 - if not isinstance(mydep, Atom):
2288 - mydep = Atom(mydep)
2289 -
2290 - mycpv = mydep.cpv
2291 - mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific
2292 - slot = mydep.slot
2293 -
2294 - if not mycpv_cps:
2295 - cat, pkg = catsplit(mycpv)
2296 - ver = None
2297 - rev = None
2298 - else:
2299 - cat, pkg, ver, rev = mycpv_cps
2300 - if mydep == mycpv:
2301 - raise KeyError(_("Specific key requires an operator"
2302 - " (%s) (try adding an '=')") % (mydep))
2303 -
2304 - if ver and rev:
2305 - operator = mydep.operator
2306 - if not operator:
2307 - writemsg(_("!!! Invalid atom: %s\n") % mydep, noiselevel=-1)
2308 - return []
2309 - else:
2310 - operator = None
2311 -
2312 - mylist = []
2313 -
2314 - if operator is None:
2315 - for x in candidate_list:
2316 - cp = getattr(x, "cp", None)
2317 - if cp is None:
2318 - mysplit = catpkgsplit(remove_slot(x))
2319 - if mysplit is not None:
2320 - cp = mysplit[0] + '/' + mysplit[1]
2321 - if cp != mycpv:
2322 - continue
2323 - mylist.append(x)
2324 -
2325 - elif operator == "=": # Exact match
2326 - for x in candidate_list:
2327 - xcpv = getattr(x, "cpv", None)
2328 - if xcpv is None:
2329 - xcpv = remove_slot(x)
2330 - if not cpvequal(xcpv, mycpv):
2331 - continue
2332 - mylist.append(x)
2333 -
2334 - elif operator == "=*": # glob match
2335 - # XXX: Nasty special casing for leading zeros
2336 - # Required as =* is a literal prefix match, so can't
2337 - # use vercmp
2338 - mysplit = catpkgsplit(mycpv)
2339 - myver = mysplit[2].lstrip("0")
2340 - if not myver or not myver[0].isdigit():
2341 - myver = "0"+myver
2342 - mycpv = mysplit[0]+"/"+mysplit[1]+"-"+myver
2343 - for x in candidate_list:
2344 - xs = getattr(x, "cpv_split", None)
2345 - if xs is None:
2346 - xs = catpkgsplit(remove_slot(x))
2347 - myver = xs[2].lstrip("0")
2348 - if not myver or not myver[0].isdigit():
2349 - myver = "0"+myver
2350 - xcpv = xs[0]+"/"+xs[1]+"-"+myver
2351 - if xcpv.startswith(mycpv):
2352 - mylist.append(x)
2353 -
2354 - elif operator == "~": # version, any revision, match
2355 - for x in candidate_list:
2356 - xs = getattr(x, "cpv_split", None)
2357 - if xs is None:
2358 - xs = catpkgsplit(remove_slot(x))
2359 - if xs is None:
2360 - raise InvalidData(x)
2361 - if not cpvequal(xs[0]+"/"+xs[1]+"-"+xs[2], mycpv_cps[0]+"/"+mycpv_cps[1]+"-"+mycpv_cps[2]):
2362 - continue
2363 - if xs[2] != ver:
2364 - continue
2365 - mylist.append(x)
2366 -
2367 - elif operator in [">", ">=", "<", "<="]:
2368 - mysplit = ["%s/%s" % (cat, pkg), ver, rev]
2369 - for x in candidate_list:
2370 - xs = getattr(x, "cpv_split", None)
2371 - if xs is None:
2372 - xs = catpkgsplit(remove_slot(x))
2373 - xcat, xpkg, xver, xrev = xs
2374 - xs = ["%s/%s" % (xcat, xpkg), xver, xrev]
2375 - try:
2376 - result = pkgcmp(xs, mysplit)
2377 - except ValueError: # pkgcmp may return ValueError during int() conversion
2378 - writemsg(_("\nInvalid package name: %s\n") % x, noiselevel=-1)
2379 - raise
2380 - if result is None:
2381 - continue
2382 - elif operator == ">":
2383 - if result > 0:
2384 - mylist.append(x)
2385 - elif operator == ">=":
2386 - if result >= 0:
2387 - mylist.append(x)
2388 - elif operator == "<":
2389 - if result < 0:
2390 - mylist.append(x)
2391 - elif operator == "<=":
2392 - if result <= 0:
2393 - mylist.append(x)
2394 - else:
2395 - raise KeyError(_("Unknown operator: %s") % mydep)
2396 - else:
2397 - raise KeyError(_("Unknown operator: %s") % mydep)
2398 -
2399 - if slot is not None:
2400 - candidate_list = mylist
2401 - mylist = []
2402 - for x in candidate_list:
2403 - xslot = getattr(x, "slot", False)
2404 - if xslot is False:
2405 - xslot = dep_getslot(x)
2406 - if xslot is not None and xslot != slot:
2407 - continue
2408 - mylist.append(x)
2409 -
2410 - if mydep.use:
2411 - candidate_list = mylist
2412 - mylist = []
2413 - for x in candidate_list:
2414 - use = getattr(x, "use", None)
2415 - if use is not None:
2416 - regex = x.iuse.regex
2417 - missing_iuse = False
2418 - for y in mydep.use.required:
2419 - if regex.match(y) is None:
2420 - missing_iuse = True
2421 - break
2422 - if missing_iuse:
2423 - continue
2424 - if mydep.use.enabled.difference(use.enabled):
2425 - continue
2426 - if mydep.use.disabled.intersection(use.enabled):
2427 - continue
2428 - mylist.append(x)
2429 -
2430 - return mylist