1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 """
42 Provides general-purpose utilities.
43
44 @sort: AbsolutePathList, ObjectTypeList, RestrictedContentList, RegexMatchList,
45 RegexList, _Vertex, DirectedGraph, PathResolverSingleton,
46 sortDict, convertSize, getUidGid, changeOwnership, splitCommandLine,
47 resolveCommand, executeCommand, calculateFileAge, encodePath, nullDevice,
48 deriveDayOfWeek, isStartOfWeek, buildNormalizedPath,
49 ISO_SECTOR_SIZE, BYTES_PER_SECTOR,
50 BYTES_PER_KBYTE, BYTES_PER_MBYTE, BYTES_PER_GBYTE, KBYTES_PER_MBYTE, MBYTES_PER_GBYTE,
51 SECONDS_PER_MINUTE, MINUTES_PER_HOUR, HOURS_PER_DAY, SECONDS_PER_DAY,
52 UNIT_BYTES, UNIT_KBYTES, UNIT_MBYTES, UNIT_GBYTES, UNIT_SECTORS
53
54 @var ISO_SECTOR_SIZE: Size of an ISO image sector, in bytes.
55 @var BYTES_PER_SECTOR: Number of bytes (B) per ISO sector.
56 @var BYTES_PER_KBYTE: Number of bytes (B) per kilobyte (kB).
57 @var BYTES_PER_MBYTE: Number of bytes (B) per megabyte (MB).
58 @var BYTES_PER_GBYTE: Number of bytes (B) per megabyte (GB).
59 @var KBYTES_PER_MBYTE: Number of kilobytes (kB) per megabyte (MB).
60 @var MBYTES_PER_GBYTE: Number of megabytes (MB) per gigabyte (GB).
61 @var SECONDS_PER_MINUTE: Number of seconds per minute.
62 @var MINUTES_PER_HOUR: Number of minutes per hour.
63 @var HOURS_PER_DAY: Number of hours per day.
64 @var SECONDS_PER_DAY: Number of seconds per day.
65 @var UNIT_BYTES: Constant representing the byte (B) unit for conversion.
66 @var UNIT_KBYTES: Constant representing the kilobyte (kB) unit for conversion.
67 @var UNIT_MBYTES: Constant representing the megabyte (MB) unit for conversion.
68 @var UNIT_GBYTES: Constant representing the gigabyte (GB) unit for conversion.
69 @var UNIT_SECTORS: Constant representing the ISO sector unit for conversion.
70
71 @author: Kenneth J. Pronovici <pronovic@ieee.org>
72 """
73
74
75
76
77
78
79 import sys
80 import math
81 import os
82 import re
83 import time
84 import logging
85 import string
86 from subprocess import Popen, STDOUT, PIPE
87
88 try:
89 import pwd
90 import grp
91 _UID_GID_AVAILABLE = True
92 except ImportError:
93 _UID_GID_AVAILABLE = False
94
95 from CedarBackup2.release import VERSION, DATE
96
97
98
99
100
101
102 logger = logging.getLogger("CedarBackup2.log.util")
103 outputLogger = logging.getLogger("CedarBackup2.output")
104
105 ISO_SECTOR_SIZE = 2048.0
106 BYTES_PER_SECTOR = ISO_SECTOR_SIZE
107
108 BYTES_PER_KBYTE = 1024.0
109 KBYTES_PER_MBYTE = 1024.0
110 MBYTES_PER_GBYTE = 1024.0
111 BYTES_PER_MBYTE = BYTES_PER_KBYTE * KBYTES_PER_MBYTE
112 BYTES_PER_GBYTE = BYTES_PER_MBYTE * MBYTES_PER_GBYTE
113
114 SECONDS_PER_MINUTE = 60.0
115 MINUTES_PER_HOUR = 60.0
116 HOURS_PER_DAY = 24.0
117 SECONDS_PER_DAY = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY
118
119 UNIT_BYTES = 0
120 UNIT_KBYTES = 1
121 UNIT_MBYTES = 2
122 UNIT_GBYTES = 4
123 UNIT_SECTORS = 3
124
125 MTAB_FILE = "/etc/mtab"
126
127 MOUNT_COMMAND = [ "mount", ]
128 UMOUNT_COMMAND = [ "umount", ]
129
130 DEFAULT_LANGUAGE = "C"
131 LANG_VAR = "LANG"
132 LOCALE_VARS = [ "LC_ADDRESS", "LC_ALL", "LC_COLLATE",
133 "LC_CTYPE", "LC_IDENTIFICATION",
134 "LC_MEASUREMENT", "LC_MESSAGES",
135 "LC_MONETARY", "LC_NAME", "LC_NUMERIC",
136 "LC_PAPER", "LC_TELEPHONE", "LC_TIME", ]
144
145 """
146 Class representing an "unordered list".
147
148 An "unordered list" is a list in which only the contents matter, not the
149 order in which the contents appear in the list.
150
151 For instance, we might be keeping track of set of paths in a list, because
152 it's convenient to have them in that form. However, for comparison
153 purposes, we would only care that the lists contain exactly the same
154 contents, regardless of order.
155
156 I have come up with two reasonable ways of doing this, plus a couple more
157 that would work but would be a pain to implement. My first method is to
158 copy and sort each list, comparing the sorted versions. This will only work
159 if two lists with exactly the same members are guaranteed to sort in exactly
160 the same order. The second way would be to create two Sets and then compare
161 the sets. However, this would lose information about any duplicates in
162 either list. I've decided to go with option #1 for now. I'll modify this
163 code if I run into problems in the future.
164
165 We override the original C{__eq__}, C{__ne__}, C{__ge__}, C{__gt__},
166 C{__le__} and C{__lt__} list methods to change the definition of the various
167 comparison operators. In all cases, the comparison is changed to return the
168 result of the original operation I{but instead comparing sorted lists}.
169 This is going to be quite a bit slower than a normal list, so you probably
170 only want to use it on small lists.
171 """
172
174 """
175 Definition of C{==} operator for this class.
176 @param other: Other object to compare to.
177 @return: True/false depending on whether C{self == other}.
178 """
179 if other is None:
180 return False
181 selfSorted = self[:]
182 otherSorted = other[:]
183 selfSorted.sort()
184 otherSorted.sort()
185 return selfSorted.__eq__(otherSorted)
186
188 """
189 Definition of C{!=} operator for this class.
190 @param other: Other object to compare to.
191 @return: True/false depending on whether C{self != other}.
192 """
193 if other is None:
194 return True
195 selfSorted = self[:]
196 otherSorted = other[:]
197 selfSorted.sort()
198 otherSorted.sort()
199 return selfSorted.__ne__(otherSorted)
200
202 """
203 Definition of S{>=} operator for this class.
204 @param other: Other object to compare to.
205 @return: True/false depending on whether C{self >= other}.
206 """
207 if other is None:
208 return True
209 selfSorted = self[:]
210 otherSorted = other[:]
211 selfSorted.sort()
212 otherSorted.sort()
213 return selfSorted.__ge__(otherSorted)
214
216 """
217 Definition of C{>} operator for this class.
218 @param other: Other object to compare to.
219 @return: True/false depending on whether C{self > other}.
220 """
221 if other is None:
222 return True
223 selfSorted = self[:]
224 otherSorted = other[:]
225 selfSorted.sort()
226 otherSorted.sort()
227 return selfSorted.__gt__(otherSorted)
228
230 """
231 Definition of S{<=} operator for this class.
232 @param other: Other object to compare to.
233 @return: True/false depending on whether C{self <= other}.
234 """
235 if other is None:
236 return False
237 selfSorted = self[:]
238 otherSorted = other[:]
239 selfSorted.sort()
240 otherSorted.sort()
241 return selfSorted.__le__(otherSorted)
242
244 """
245 Definition of C{<} operator for this class.
246 @param other: Other object to compare to.
247 @return: True/false depending on whether C{self < other}.
248 """
249 if other is None:
250 return False
251 selfSorted = self[:]
252 otherSorted = other[:]
253 selfSorted.sort()
254 otherSorted.sort()
255 return selfSorted.__lt__(otherSorted)
256
263
264 """
265 Class representing a list of absolute paths.
266
267 This is an unordered list.
268
269 We override the C{append}, C{insert} and C{extend} methods to ensure that
270 any item added to the list is an absolute path.
271
272 Each item added to the list is encoded using L{encodePath}. If we don't do
273 this, we have problems trying certain operations between strings and unicode
274 objects, particularly for "odd" filenames that can't be encoded in standard
275 ASCII.
276 """
277
279 """
280 Overrides the standard C{append} method.
281 @raise ValueError: If item is not an absolute path.
282 """
283 if not os.path.isabs(item):
284 raise ValueError("Not an absolute path: [%s]" % item)
285 list.append(self, encodePath(item))
286
287 - def insert(self, index, item):
288 """
289 Overrides the standard C{insert} method.
290 @raise ValueError: If item is not an absolute path.
291 """
292 if not os.path.isabs(item):
293 raise ValueError("Not an absolute path: [%s]" % item)
294 list.insert(self, index, encodePath(item))
295
297 """
298 Overrides the standard C{insert} method.
299 @raise ValueError: If any item is not an absolute path.
300 """
301 for item in seq:
302 if not os.path.isabs(item):
303 raise ValueError("Not an absolute path: [%s]" % item)
304 for item in seq:
305 list.append(self, encodePath(item))
306
313
314 """
315 Class representing a list containing only objects with a certain type.
316
317 This is an unordered list.
318
319 We override the C{append}, C{insert} and C{extend} methods to ensure that
320 any item added to the list matches the type that is requested. The
321 comparison uses the built-in C{isinstance}, which should allow subclasses of
322 of the requested type to be added to the list as well.
323
324 The C{objectName} value will be used in exceptions, i.e. C{"Item must be a
325 CollectDir object."} if C{objectName} is C{"CollectDir"}.
326 """
327
328 - def __init__(self, objectType, objectName):
329 """
330 Initializes a typed list for a particular type.
331 @param objectType: Type that the list elements must match.
332 @param objectName: Short string containing the "name" of the type.
333 """
334 super(ObjectTypeList, self).__init__()
335 self.objectType = objectType
336 self.objectName = objectName
337
339 """
340 Overrides the standard C{append} method.
341 @raise ValueError: If item does not match requested type.
342 """
343 if not isinstance(item, self.objectType):
344 raise ValueError("Item must be a %s object." % self.objectName)
345 list.append(self, item)
346
347 - def insert(self, index, item):
348 """
349 Overrides the standard C{insert} method.
350 @raise ValueError: If item does not match requested type.
351 """
352 if not isinstance(item, self.objectType):
353 raise ValueError("Item must be a %s object." % self.objectName)
354 list.insert(self, index, item)
355
357 """
358 Overrides the standard C{insert} method.
359 @raise ValueError: If item does not match requested type.
360 """
361 for item in seq:
362 if not isinstance(item, self.objectType):
363 raise ValueError("All items must be %s objects." % self.objectName)
364 list.extend(self, seq)
365
366
367
368
369
370
371 -class RestrictedContentList(UnorderedList):
372
373 """
374 Class representing a list containing only object with certain values.
375
376 This is an unordered list.
377
378 We override the C{append}, C{insert} and C{extend} methods to ensure that
379 any item added to the list is among the valid values. We use a standard
380 comparison, so pretty much anything can be in the list of valid values.
381
382 The C{valuesDescr} value will be used in exceptions, i.e. C{"Item must be
383 one of values in VALID_ACTIONS"} if C{valuesDescr} is C{"VALID_ACTIONS"}.
384
385 @note: This class doesn't make any attempt to trap for nonsensical
386 arguments. All of the values in the values list should be of the same type
387 (i.e. strings). Then, all list operations also need to be of that type
388 (i.e. you should always insert or append just strings). If you mix types --
389 for instance lists and strings -- you will likely see AttributeError
390 exceptions or other problems.
391 """
392
393 - def __init__(self, valuesList, valuesDescr, prefix=None):
394 """
395 Initializes a list restricted to containing certain values.
396 @param valuesList: List of valid values.
397 @param valuesDescr: Short string describing list of values.
398 @param prefix: Prefix to use in error messages (None results in prefix "Item")
399 """
400 super(RestrictedContentList, self).__init__()
401 self.prefix = "Item"
402 if prefix is not None: self.prefix = prefix
403 self.valuesList = valuesList
404 self.valuesDescr = valuesDescr
405
406 - def append(self, item):
407 """
408 Overrides the standard C{append} method.
409 @raise ValueError: If item is not in the values list.
410 """
411 if item not in self.valuesList:
412 raise ValueError("%s must be one of the values in %s." % (self.prefix, self.valuesDescr))
413 list.append(self, item)
414
415 - def insert(self, index, item):
416 """
417 Overrides the standard C{insert} method.
418 @raise ValueError: If item is not in the values list.
419 """
420 if item not in self.valuesList:
421 raise ValueError("%s must be one of the values in %s." % (self.prefix, self.valuesDescr))
422 list.insert(self, index, item)
423
424 - def extend(self, seq):
425 """
426 Overrides the standard C{insert} method.
427 @raise ValueError: If item is not in the values list.
428 """
429 for item in seq:
430 if item not in self.valuesList:
431 raise ValueError("%s must be one of the values in %s." % (self.prefix, self.valuesDescr))
432 list.extend(self, seq)
433
440
441 """
442 Class representing a list containing only strings that match a regular expression.
443
444 If C{emptyAllowed} is passed in as C{False}, then empty strings are
445 explicitly disallowed, even if they happen to match the regular expression.
446 (C{None} values are always disallowed, since string operations are not
447 permitted on C{None}.)
448
449 This is an unordered list.
450
451 We override the C{append}, C{insert} and C{extend} methods to ensure that
452 any item added to the list matches the indicated regular expression.
453
454 @note: If you try to put values that are not strings into the list, you will
455 likely get either TypeError or AttributeError exceptions as a result.
456 """
457
458 - def __init__(self, valuesRegex, emptyAllowed=True, prefix=None):
459 """
460 Initializes a list restricted to containing certain values.
461 @param valuesRegex: Regular expression that must be matched, as a string
462 @param emptyAllowed: Indicates whether empty or None values are allowed.
463 @param prefix: Prefix to use in error messages (None results in prefix "Item")
464 """
465 super(RegexMatchList, self).__init__()
466 self.prefix = "Item"
467 if prefix is not None: self.prefix = prefix
468 self.valuesRegex = valuesRegex
469 self.emptyAllowed = emptyAllowed
470 self.pattern = re.compile(self.valuesRegex)
471
473 """
474 Overrides the standard C{append} method.
475 @raise ValueError: If item is None
476 @raise ValueError: If item is empty and empty values are not allowed
477 @raise ValueError: If item does not match the configured regular expression
478 """
479 if item is None or (not self.emptyAllowed and item == ""):
480 raise ValueError("%s cannot be empty." % self.prefix)
481 if not self.pattern.search(item):
482 raise ValueError("%s is not valid: [%s]" % (self.prefix, item))
483 list.append(self, item)
484
485 - def insert(self, index, item):
486 """
487 Overrides the standard C{insert} method.
488 @raise ValueError: If item is None
489 @raise ValueError: If item is empty and empty values are not allowed
490 @raise ValueError: If item does not match the configured regular expression
491 """
492 if item is None or (not self.emptyAllowed and item == ""):
493 raise ValueError("%s cannot be empty." % self.prefix)
494 if not self.pattern.search(item):
495 raise ValueError("%s is not valid [%s]" % (self.prefix, item))
496 list.insert(self, index, item)
497
499 """
500 Overrides the standard C{insert} method.
501 @raise ValueError: If any item is None
502 @raise ValueError: If any item is empty and empty values are not allowed
503 @raise ValueError: If any item does not match the configured regular expression
504 """
505 for item in seq:
506 if item is None or (not self.emptyAllowed and item == ""):
507 raise ValueError("%s cannot be empty." % self.prefix)
508 if not self.pattern.search(item):
509 raise ValueError("%s is not valid: [%s]" % (self.prefix, item))
510 list.extend(self, seq)
511
512
513
514
515
516
517 -class RegexList(UnorderedList):
518
519 """
520 Class representing a list of valid regular expression strings.
521
522 This is an unordered list.
523
524 We override the C{append}, C{insert} and C{extend} methods to ensure that
525 any item added to the list is a valid regular expression.
526 """
527
529 """
530 Overrides the standard C{append} method.
531 @raise ValueError: If item is not an absolute path.
532 """
533 try:
534 re.compile(item)
535 except re.error:
536 raise ValueError("Not a valid regular expression: [%s]" % item)
537 list.append(self, item)
538
539 - def insert(self, index, item):
540 """
541 Overrides the standard C{insert} method.
542 @raise ValueError: If item is not an absolute path.
543 """
544 try:
545 re.compile(item)
546 except re.error:
547 raise ValueError("Not a valid regular expression: [%s]" % item)
548 list.insert(self, index, item)
549
551 """
552 Overrides the standard C{insert} method.
553 @raise ValueError: If any item is not an absolute path.
554 """
555 for item in seq:
556 try:
557 re.compile(item)
558 except re.error:
559 raise ValueError("Not a valid regular expression: [%s]" % item)
560 for item in seq:
561 list.append(self, item)
562
563
564
565
566
567
568 -class _Vertex(object):
569
570 """
571 Represents a vertex (or node) in a directed graph.
572 """
573
575 """
576 Constructor.
577 @param name: Name of this graph vertex.
578 @type name: String value.
579 """
580 self.name = name
581 self.endpoints = []
582 self.state = None
583
585
586 """
587 Represents a directed graph.
588
589 A graph B{G=(V,E)} consists of a set of vertices B{V} together with a set
590 B{E} of vertex pairs or edges. In a directed graph, each edge also has an
591 associated direction (from vertext B{v1} to vertex B{v2}). A C{DirectedGraph}
592 object provides a way to construct a directed graph and execute a depth-
593 first search.
594
595 This data structure was designed based on the graphing chapter in
596 U{The Algorithm Design Manual<http://www2.toki.or.id/book/AlgDesignManual/>},
597 by Steven S. Skiena.
598
599 This class is intended to be used by Cedar Backup for dependency ordering.
600 Because of this, it's not quite general-purpose. Unlike a "general" graph,
601 every vertex in this graph has at least one edge pointing to it, from a
602 special "start" vertex. This is so no vertices get "lost" either because
603 they have no dependencies or because nothing depends on them.
604 """
605
606 _UNDISCOVERED = 0
607 _DISCOVERED = 1
608 _EXPLORED = 2
609
611 """
612 Directed graph constructor.
613
614 @param name: Name of this graph.
615 @type name: String value.
616 """
617 if name is None or name == "":
618 raise ValueError("Graph name must be non-empty.")
619 self._name = name
620 self._vertices = {}
621 self._startVertex = _Vertex(None)
622
624 """
625 Official string representation for class instance.
626 """
627 return "DirectedGraph(%s)" % self.name
628
630 """
631 Informal string representation for class instance.
632 """
633 return self.__repr__()
634
636 """
637 Definition of equals operator for this class.
638 @param other: Other object to compare to.
639 @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
640 """
641
642 if other is None:
643 return 1
644 if self.name != other.name:
645 if self.name < other.name:
646 return -1
647 else:
648 return 1
649 if self._vertices != other._vertices:
650 if self._vertices < other._vertices:
651 return -1
652 else:
653 return 1
654 return 0
655
657 """
658 Property target used to get the graph name.
659 """
660 return self._name
661
662 name = property(_getName, None, None, "Name of the graph.")
663
665 """
666 Creates a named vertex.
667 @param name: vertex name
668 @raise ValueError: If the vertex name is C{None} or empty.
669 """
670 if name is None or name == "":
671 raise ValueError("Vertex name must be non-empty.")
672 vertex = _Vertex(name)
673 self._startVertex.endpoints.append(vertex)
674 self._vertices[name] = vertex
675
677 """
678 Adds an edge with an associated direction, from C{start} vertex to C{finish} vertex.
679 @param start: Name of start vertex.
680 @param finish: Name of finish vertex.
681 @raise ValueError: If one of the named vertices is unknown.
682 """
683 try:
684 startVertex = self._vertices[start]
685 finishVertex = self._vertices[finish]
686 startVertex.endpoints.append(finishVertex)
687 except KeyError, e:
688 raise ValueError("Vertex [%s] could not be found." % e)
689
691 """
692 Implements a topological sort of the graph.
693
694 This method also enforces that the graph is a directed acyclic graph,
695 which is a requirement of a topological sort.
696
697 A directed acyclic graph (or "DAG") is a directed graph with no directed
698 cycles. A topological sort of a DAG is an ordering on the vertices such
699 that all edges go from left to right. Only an acyclic graph can have a
700 topological sort, but any DAG has at least one topological sort.
701
702 Since a topological sort only makes sense for an acyclic graph, this
703 method throws an exception if a cycle is found.
704
705 A depth-first search only makes sense if the graph is acyclic. If the
706 graph contains any cycles, it is not possible to determine a consistent
707 ordering for the vertices.
708
709 @note: If a particular vertex has no edges, then its position in the
710 final list depends on the order in which the vertices were created in the
711 graph. If you're using this method to determine a dependency order, this
712 makes sense: a vertex with no dependencies can go anywhere (and will).
713
714 @return: Ordering on the vertices so that all edges go from left to right.
715
716 @raise ValueError: If a cycle is found in the graph.
717 """
718 ordering = []
719 for key in self._vertices:
720 vertex = self._vertices[key]
721 vertex.state = self._UNDISCOVERED
722 for key in self._vertices:
723 vertex = self._vertices[key]
724 if vertex.state == self._UNDISCOVERED:
725 self._topologicalSort(self._startVertex, ordering)
726 return ordering
727
729 """
730 Recursive depth first search function implementing topological sort.
731 @param vertex: Vertex to search
732 @param ordering: List of vertices in proper order
733 """
734 vertex.state = self._DISCOVERED
735 for endpoint in vertex.endpoints:
736 if endpoint.state == self._UNDISCOVERED:
737 self._topologicalSort(endpoint, ordering)
738 elif endpoint.state != self._EXPLORED:
739 raise ValueError("Cycle found in graph (found '%s' while searching '%s')." % (vertex.name, endpoint.name))
740 if vertex.name is not None:
741 ordering.insert(0, vertex.name)
742 vertex.state = self._EXPLORED
743
750
751 """
752 Singleton used for resolving executable paths.
753
754 Various functions throughout Cedar Backup (including extensions) need a way
755 to resolve the path of executables that they use. For instance, the image
756 functionality needs to find the C{mkisofs} executable, and the Subversion
757 extension needs to find the C{svnlook} executable. Cedar Backup's original
758 behavior was to assume that the simple name (C{"svnlook"} or whatever) was
759 available on the caller's C{$PATH}, and to fail otherwise. However, this
760 turns out to be less than ideal, since for instance the root user might not
761 always have executables like C{svnlook} in its path.
762
763 One solution is to specify a path (either via an absolute path or some sort
764 of path insertion or path appending mechanism) that would apply to the
765 C{executeCommand()} function. This is not difficult to implement, but it
766 seem like kind of a "big hammer" solution. Besides that, it might also
767 represent a security flaw (for instance, I prefer not to mess with root's
768 C{$PATH} on the application level if I don't have to).
769
770 The alternative is to set up some sort of configuration for the path to
771 certain executables, i.e. "find C{svnlook} in C{/usr/local/bin/svnlook}" or
772 whatever. This PathResolverSingleton aims to provide a good solution to the
773 mapping problem. Callers of all sorts (extensions or not) can get an
774 instance of the singleton. Then, they call the C{lookup} method to try and
775 resolve the executable they are looking for. Through the C{lookup} method,
776 the caller can also specify a default to use if a mapping is not found.
777 This way, with no real effort on the part of the caller, behavior can neatly
778 degrade to something equivalent to the current behavior if there is no
779 special mapping or if the singleton was never initialized in the first
780 place.
781
782 Even better, extensions automagically get access to the same resolver
783 functionality, and they don't even need to understand how the mapping
784 happens. All extension authors need to do is document what executables
785 their code requires, and the standard resolver configuration section will
786 meet their needs.
787
788 The class should be initialized once through the constructor somewhere in
789 the main routine. Then, the main routine should call the L{fill} method to
790 fill in the resolver's internal structures. Everyone else who needs to
791 resolve a path will get an instance of the class using L{getInstance} and
792 will then just call the L{lookup} method.
793
794 @cvar _instance: Holds a reference to the singleton
795 @ivar _mapping: Internal mapping from resource name to path.
796 """
797
798 _instance = None
799
801 """Helper class to provide a singleton factory method."""
810
811 getInstance = _Helper()
812
819
820 - def lookup(self, name, default=None):
821 """
822 Looks up name and returns the resolved path associated with the name.
823 @param name: Name of the path resource to resolve.
824 @param default: Default to return if resource cannot be resolved.
825 @return: Resolved path associated with name, or default if name can't be resolved.
826 """
827 value = default
828 if name in self._mapping.keys():
829 value = self._mapping[name]
830 logger.debug("Resolved command [%s] to [%s].", name, value)
831 return value
832
833 - def fill(self, mapping):
834 """
835 Fills in the singleton's internal mapping from name to resource.
836 @param mapping: Mapping from resource name to path.
837 @type mapping: Dictionary mapping name to path, both as strings.
838 """
839 self._mapping = { }
840 for key in mapping.keys():
841 self._mapping[key] = mapping[key]
842
843
844
845
846
847
848 -class Pipe(Popen):
849 """
850 Specialized pipe class for use by C{executeCommand}.
851
852 The L{executeCommand} function needs a specialized way of interacting
853 with a pipe. First, C{executeCommand} only reads from the pipe, and
<