root / trunk / twisted / python / util.py

Revision 26967, 28.4 kB (checked in by exarkun, 1 month ago)

Merge unsigned-id-delayed-call-3791

Author: drake, exarkun
Reviewer: exarkun, therve
Fixes: #3791

Add twisted.python.util.setIDFunction to allow the behavior of unsignedID
to be controlled. Then use this to test that unsignedID is being used in
DelayedCall and AMP to construct the repr string.

Line 
1 # -*- test-case-name: twisted.python.test.test_util -*-
2 # Copyright (c) 2001-2009 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 import os, sys, hmac, errno, new, inspect, warnings
6 try:
7     import pwd, grp
8 except ImportError:
9     pwd = grp = None
10 try:
11     from os import setgroups, getgroups
12 except ImportError:
13     setgroups = getgroups = None
14 from UserDict import UserDict
15
16
17 class InsensitiveDict:
18     """Dictionary, that has case-insensitive keys.
19
20     Normally keys are retained in their original form when queried with
21     .keys() or .items().  If initialized with preserveCase=0, keys are both
22     looked up in lowercase and returned in lowercase by .keys() and .items().
23     """
24     """
25     Modified recipe at
26     http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66315 originally
27     contributed by Sami Hangaslammi.
28     """
29
30     def __init__(self, dict=None, preserve=1):
31         """Create an empty dictionary, or update from 'dict'."""
32         self.data = {}
33         self.preserve=preserve
34         if dict:
35             self.update(dict)
36
37     def __delitem__(self, key):
38         k=self._lowerOrReturn(key)
39         del self.data[k]
40
41     def _lowerOrReturn(self, key):
42         if isinstance(key, str) or isinstance(key, unicode):
43             return key.lower()
44         else:
45             return key
46
47     def __getitem__(self, key):
48         """Retrieve the value associated with 'key' (in any case)."""
49         k = self._lowerOrReturn(key)
50         return self.data[k][1]
51
52     def __setitem__(self, key, value):
53         """Associate 'value' with 'key'. If 'key' already exists, but
54         in different case, it will be replaced."""
55         k = self._lowerOrReturn(key)
56         self.data[k] = (key, value)
57
58     def has_key(self, key):
59         """Case insensitive test whether 'key' exists."""
60         k = self._lowerOrReturn(key)
61         return self.data.has_key(k)
62     __contains__=has_key
63
64     def _doPreserve(self, key):
65         if not self.preserve and (isinstance(key, str)
66                                   or isinstance(key, unicode)):
67             return key.lower()
68         else:
69             return key
70
71     def keys(self):
72         """List of keys in their original case."""
73         return list(self.iterkeys())
74
75     def values(self):
76         """List of values."""
77         return list(self.itervalues())
78
79     def items(self):
80         """List of (key,value) pairs."""
81         return list(self.iteritems())
82
83     def get(self, key, default=None):
84         """Retrieve value associated with 'key' or return default value
85         if 'key' doesn't exist."""
86         try:
87             return self[key]
88         except KeyError:
89             return default
90
91     def setdefault(self, key, default):
92         """If 'key' doesn't exists, associate it with the 'default' value.
93         Return value associated with 'key'."""
94         if not self.has_key(key):
95             self[key] = default
96         return self[key]
97
98     def update(self, dict):
99         """Copy (key,value) pairs from 'dict'."""
100         for k,v in dict.items():
101             self[k] = v
102
103     def __repr__(self):
104         """String representation of the dictionary."""
105         items = ", ".join([("%r: %r" % (k,v)) for k,v in self.items()])
106         return "InsensitiveDict({%s})" % items
107
108     def iterkeys(self):
109         for v in self.data.itervalues():
110             yield self._doPreserve(v[0])
111
112     def itervalues(self):
113         for v in self.data.itervalues():
114             yield v[1]
115
116     def iteritems(self):
117         for (k, v) in self.data.itervalues():
118             yield self._doPreserve(k), v
119
120     def popitem(self):
121         i=self.items()[0]
122         del self[i[0]]
123         return i
124
125     def clear(self):
126         for k in self.keys():
127             del self[k]
128
129     def copy(self):
130         return InsensitiveDict(self, self.preserve)
131
132     def __len__(self):
133         return len(self.data)
134
135     def __eq__(self, other):
136         for k,v in self.items():
137             if not (k in other) or not (other[k]==v):
138                 return 0
139         return len(self)==len(other)
140
141 class OrderedDict(UserDict):
142     """A UserDict that preserves insert order whenever possible."""
143     def __init__(self, dict=None, **kwargs):
144         self._order = []
145         self.data = {}
146         if dict is not None:
147             if hasattr(dict,'keys'):
148                 self.update(dict)
149             else:
150                 for k,v in dict: # sequence
151                     self[k] = v
152         if len(kwargs):
153             self.update(kwargs)
154     def __repr__(self):
155         return '{'+', '.join([('%r: %r' % item) for item in self.items()])+'}'
156
157     def __setitem__(self, key, value):
158         if not self.has_key(key):
159             self._order.append(key)
160         UserDict.__setitem__(self, key, value)
161
162     def copy(self):
163         return self.__class__(self)
164
165     def __delitem__(self, key):
166         UserDict.__delitem__(self, key)
167         self._order.remove(key)
168
169     def iteritems(self):
170         for item in self._order:
171             yield (item, self[item])
172
173     def items(self):
174         return list(self.iteritems())
175
176     def itervalues(self):
177         for item in self._order:
178             yield self[item]
179
180     def values(self):
181         return list(self.itervalues())
182
183     def iterkeys(self):
184         return iter(self._order)
185
186     def keys(self):
187         return list(self._order)
188
189     def popitem(self):
190         key = self._order[-1]
191         value = self[key]
192         del self[key]
193         return (key, value)
194
195     def setdefault(self, item, default):
196         if self.has_key(item):
197             return self[item]
198         self[item] = default
199         return default
200
201     def update(self, d):
202         for k, v in d.items():
203             self[k] = v
204
205 def uniquify(lst):
206     """Make the elements of a list unique by inserting them into a dictionary.
207     This must not change the order of the input lst.
208     """
209     dct = {}
210     result = []
211     for k in lst:
212         if not dct.has_key(k): result.append(k)
213         dct[k] = 1
214     return result
215
216 def padTo(n, seq, default=None):
217     """Pads a sequence out to n elements,
218
219     filling in with a default value if it is not long enough.
220
221     If the input sequence is longer than n, raises ValueError.
222
223     Details, details:
224     This returns a new list; it does not extend the original sequence.
225     The new list contains the values of the original sequence, not copies.
226     """
227
228     if len(seq) > n:
229         raise ValueError, "%d elements is more than %d." % (len(seq), n)
230
231     blank = [default] * n
232
233     blank[:len(seq)] = list(seq)
234
235     return blank
236
237 def getPluginDirs():
238     import twisted
239     systemPlugins = os.path.join(os.path.dirname(os.path.dirname(
240                             os.path.abspath(twisted.__file__))), 'plugins')
241     userPlugins = os.path.expanduser("~/TwistedPlugins")
242     confPlugins = os.path.expanduser("~/.twisted")
243     allPlugins = filter(os.path.isdir, [systemPlugins, userPlugins, confPlugins])
244     return allPlugins
245
246 def addPluginDir():
247     sys.path.extend(getPluginDirs())
248
249 def sibpath(path, sibling):
250     """Return the path to a sibling of a file in the filesystem.
251
252     This is useful in conjunction with the special __file__ attribute
253     that Python provides for modules, so modules can load associated
254     resource files.
255     """
256     return os.path.join(os.path.dirname(os.path.abspath(path)), sibling)
257
258
259 def _getpass(prompt):
260     """Helper to turn IOErrors into KeyboardInterrupts"""
261     import getpass
262     try:
263         return getpass.getpass(prompt)
264     except IOError, e:
265         if e.errno == errno.EINTR:
266             raise KeyboardInterrupt
267         raise
268     except EOFError:
269         raise KeyboardInterrupt
270
271 def getPassword(prompt = 'Password: ', confirm = 0, forceTTY = 0,
272                 confirmPrompt = 'Confirm password: ',
273                 mismatchMessage = "Passwords don't match."):
274     """Obtain a password by prompting or from stdin.
275
276     If stdin is a terminal, prompt for a new password, and confirm (if
277     C{confirm} is true) by asking again to make sure the user typed the same
278     thing, as keystrokes will not be echoed.
279
280     If stdin is not a terminal, and C{forceTTY} is not true, read in a line
281     and use it as the password, less the trailing newline, if any.  If
282     C{forceTTY} is true, attempt to open a tty and prompt for the password
283     using it.  Raise a RuntimeError if this is not possible.
284
285     @returns: C{str}
286     """
287     isaTTY = hasattr(sys.stdin, 'isatty') and sys.stdin.isatty()
288
289     old = None
290     try:
291         if not isaTTY:
292             if forceTTY:
293                 try:
294                     old = sys.stdin, sys.stdout
295                     sys.stdin = sys.stdout = open('/dev/tty', 'r+')
296                 except:
297                     raise RuntimeError("Cannot obtain a TTY")
298             else:
299                 password = sys.stdin.readline()
300                 if password[-1] == '\n':
301                     password = password[:-1]
302                 return password
303
304         while 1:
305             try1 = _getpass(prompt)
306             if not confirm:
307                 return try1
308             try2 = _getpass(confirmPrompt)
309             if try1 == try2:
310                 return try1
311             else:
312                 sys.stderr.write(mismatchMessage + "\n")
313     finally:
314         if old:
315             sys.stdin.close()
316             sys.stdin, sys.stdout = old
317
318
319 def dict(*a, **k):
320     import __builtin__
321     warnings.warn('twisted.python.util.dict is deprecated.  Use __builtin__.dict instead')
322     return __builtin__.dict(*a, **k)
323
324 def println(*a):
325     sys.stdout.write(' '.join(map(str, a))+'\n')
326
327 # XXX
328 # This does not belong here
329 # But where does it belong?
330
331 def str_xor(s, b):
332     return ''.join([chr(ord(c) ^ b) for c in s])
333
334 def keyed_md5(secret, challenge):
335     """
336     Create the keyed MD5 string for the given secret and challenge.
337     """
338     warnings.warn(
339         "keyed_md5() is deprecated.  Use the stdlib module hmac instead.",
340         DeprecationWarning, stacklevel=2
341     )
342     return hmac.HMAC(secret, challenge).hexdigest()
343
344 def makeStatBar(width, maxPosition, doneChar = '=', undoneChar = '-', currentChar = '>'):
345     """Creates a function that will return a string representing a progress bar.
346     """
347     aValue = width / float(maxPosition)
348     def statBar(position, force = 0, last = ['']):
349         assert len(last) == 1, "Don't mess with the last parameter."
350         done = int(aValue * position)
351         toDo = width - done - 2
352         result = "[%s%s%s]" % (doneChar * done, currentChar, undoneChar * toDo)
353         if force:
354             last[0] = result
355             return result
356         if result == last[0]:
357             return ''
358         last[0] = result
359         return result
360
361     statBar.__doc__ = """statBar(position, force = 0) -> '[%s%s%s]'-style progress bar
362
363     returned string is %d characters long, and the range goes from 0..%d.
364     The 'position' argument is where the '%s' will be drawn.  If force is false,
365     '' will be returned instead if the resulting progress bar is identical to the
366     previously returned progress bar.
367 """ % (doneChar * 3, currentChar, undoneChar * 3, width, maxPosition, currentChar)
368     return statBar
369
370 def spewer(frame, s, ignored):
371     """A trace function for sys.settrace that prints every function or method call."""
372     from twisted.python import reflect
373     if frame.f_locals.has_key('self'):
374         se = frame.f_locals['self']
375         if hasattr(se, '__class__'):
376             k = reflect.qual(se.__class__)
377         else:
378             k = reflect.qual(type(se))
379         print 'method %s of %s at %s' % (
380             frame.f_code.co_name, k, id(se)
381         )
382     else:
383         print 'function %s in %s, line %s' % (
384             frame.f_code.co_name,
385             frame.f_code.co_filename,
386             frame.f_lineno)
387
388 def searchupwards(start, files=[], dirs=[]):
389     """Walk upwards from start, looking for a directory containing
390     all files and directories given as arguments::
391     >>> searchupwards('.', ['foo.txt'], ['bar', 'bam'])
392
393     If not found, return None
394     """
395     start=os.path.abspath(start)
396     parents=start.split(os.sep)
397     exists=os.path.exists; join=os.sep.join; isdir=os.path.isdir
398     while len(parents):
399         candidate=join(parents)+os.sep
400         allpresent=1
401         for f in files:
402             if not exists("%s%s" % (candidate, f)):
403                 allpresent=0
404                 break
405         if allpresent:
406             for d in dirs:
407                 if not isdir("%s%s" % (candidate, d)):
408                     allpresent=0
409                     break
410         if allpresent: return candidate
411         parents.pop(-1)
412     return None
413
414
415 class LineLog:
416     """
417     A limited-size line-based log, useful for logging line-based
418     protocols such as SMTP.
419
420     When the log fills up, old entries drop off the end.
421     """
422     def __init__(self, size=10):
423         """
424         Create a new log, with size lines of storage (default 10).
425         A log size of 0 (or less) means an infinite log.
426         """
427         if size < 0:
428             size = 0
429         self.log = [None]*size
430         self.size = size
431
432     def append(self,line):
433         if self.size:
434             self.log[:-1] = self.log[1:]
435             self.log[-1] = line
436         else:
437             self.log.append(line)
438
439     def str(self):
440         return '\n'.join(filter(None,self.log))
441
442     def __getitem__(self, item):
443         return filter(None,self.log)[item]
444
445     def clear(self):
446         """Empty the log"""
447         self.log = [None]*self.size
448
449 def raises(exception, f, *args, **kwargs):
450     """Determine whether the given call raises the given exception"""
451     try:
452         f(*args, **kwargs)
453     except exception:
454         return 1
455     return 0
456
457 class IntervalDifferential:
458     """
459     Given a list of intervals, generate the amount of time to sleep between
460     \"instants\".
461
462     For example, given 7, 11 and 13, the three (infinite) sequences::
463
464         7 14 21 28 35 ...
465         11 22 33 44 ...
466         13 26 39 52 ...
467
468     will be generated, merged, and used to produce::
469
470         (7, 0) (4, 1) (2, 2) (1, 0) (7, 0) (1, 1) (4, 2) (2, 0) (5, 1) (2, 0)
471
472     New intervals may be added or removed as iteration proceeds using the
473     proper methods.
474     """
475
476     def __init__(self, intervals, default=60):
477         """
478         @type intervals: C{list} of C{int}, C{long}, or C{float} param
479         @param intervals: The intervals between instants.
480
481         @type default: C{int}, C{long}, or C{float}
482         @param default: The duration to generate if the intervals list
483         becomes empty.
484         """
485         self.intervals = intervals[:]
486         self.default = default
487
488     def __iter__(self):
489         return _IntervalDifferentialIterator(self.intervals, self.default)
490
491 class _IntervalDifferentialIterator:
492     def __init__(self, i, d):
493
494         self.intervals = [[e, e, n] for (e, n) in zip(i, range(len(i)))]
495         self.default = d
496         self.last = 0
497
498     def next(self):
499         if not self.intervals:
500             return (self.default, None)
501         last, index = self.intervals[0][0], self.intervals[0][2]
502         self.intervals[0][0] += self.intervals[0][1]
503         self.intervals.sort()
504         result = last - self.last
505         self.last = last
506         return result, index
507
508     def addInterval(self, i):
509         if self.intervals:
510             delay = self.intervals[0][0] - self.intervals[0][1]
511             self.intervals.append([delay + i, i, len(self.intervals)])
512             self.intervals.sort()
513         else:
514             self.intervals.append([i, i, 0])
515
516     def removeInterval(self, interval):
517         for i in range(len(self.intervals)):
518             if self.intervals[i][1] == interval:
519                 index = self.intervals[i][2]
520                 del self.intervals[i]
521                 for i in self.intervals:
522                     if i[2] > index:
523                         i[2] -= 1
524                 return
525         raise ValueError, "Specified interval not in IntervalDifferential"
526
527
528 class FancyStrMixin:
529     """
530     Set showAttributes to a sequence of strings naming attributes, OR
531     sequences of (attributeName, displayName, formatCharacter)
532     """
533     showAttributes = ()
534     def __str__(self):
535         r = ['<', hasattr(self, 'fancybasename') and self.fancybasename or self.__class__.__name__]
536         for attr in self.showAttributes:
537             if isinstance(attr, str):
538                 r.append(' %s=%r' % (attr, getattr(self, attr)))
539             else:
540                 r.append((' %s=' + attr[2]) % (attr[1], getattr(self, attr[0])))
541         r.append('>')
542         return ''.join(r)
543     __repr__ = __str__
544
545
546
547 class FancyEqMixin:
548     compareAttributes = ()
549     def __eq__(self, other):
550         if not self.compareAttributes:
551             return self is other
552         if isinstance(self, other.__class__):
553             return (
554                 [getattr(self, name) for name in self.compareAttributes] ==
555                 [getattr(other, name) for name in self.compareAttributes])
556         return NotImplemented
557
558
559     def __ne__(self, other):
560         result = self.__eq__(other)
561         if result is NotImplemented:
562             return result
563         return not result
564
565
566
567 def dsu(list, key):
568     L2 = [(key(e), i, e) for (i, e) in zip(range(len(list)), list)]
569     L2.sort()
570     return [e[2] for e in L2]
571
572 if pwd is None or grp is None or setgroups is None or getgroups is None:
573     def initgroups(uid, primaryGid):
574         """
575         Do nothing.
576
577         Underlying platform support require to manipulate groups is missing.
578         """
579 else:
580     def _setgroups_until_success(l):
581         while(1):
582             # NASTY NASTY HACK (but glibc does it so it must be okay):
583             # In case sysconfig didn't give the right answer, find the limit
584             # on max groups by just looping, trying to set fewer and fewer
585             # groups each time until it succeeds.
586             try:
587                 setgroups(l)
588             except ValueError:
589                 # This exception comes from python itself restricting
590                 # number of groups allowed.
591                 if len(l) > 1:
592                     del l[-1]
593                 else:
594                     raise
595             except OSError, e:
596                 if e.errno == errno.EINVAL and len(l) > 1:
597                     # This comes from the OS saying too many groups
598                     del l[-1]
599                 else:
600                     raise
601             else:
602                 # Success, yay!
603                 return
604
605     def initgroups(uid, primaryGid):
606         """
607         Initializes the group access list.
608
609         This is done by reading the group database /etc/group and using all
610         groups of which C{uid} is a member.  The additional group
611         C{primaryGid} is also added to the list.
612
613         If the given user is a member of more than C{NGROUPS}, arbitrary
614         groups will be silently discarded to bring the number below that
615         limit.
616
617         @type uid: C{int}
618         @param uid: The UID for which to look up group information.
619
620         @type primaryGid: C{int} or C{NoneType}
621         @param primaryGid: If provided, an additional GID to include when
622             setting the groups.
623         """
624         try:
625             # Try to get the maximum number of groups
626             max_groups = os.sysconf("SC_NGROUPS_MAX")
627         except:
628             # No predefined limit
629             max_groups = 0
630
631         username = pwd.getpwuid(uid)[0]
632         l = []
633         if primaryGid is not None:
634             l.append(primaryGid)
635         for groupname, password, gid, userlist in grp.getgrall():
636             if username in userlist:
637                 l.append(gid)
638                 if len(l) == max_groups:
639                     break # No more groups, ignore any more
640         try:
641             _setgroups_until_success(l)
642         except OSError, e:
643             # We might be able to remove this code now that we
644             # don't try to setgid/setuid even when not asked to.
645             if e.errno == errno.EPERM:
646                 for g in getgroups():
647                     if g not in l:
648                         raise
649             else:
650                 raise
651
652
653
654 def switchUID(uid, gid, euid=False):
655     if euid:
656         setuid = os.seteuid
657         setgid = os.setegid
658     else:
659         setuid = os.setuid
660         setgid = os.setgid
661     if gid is not None:
662         setgid(gid)
663     if uid is not None:
664         initgroups(uid, gid)
665         setuid(uid)
666
667
668 class SubclassableCStringIO(object):
669     """A wrapper around cStringIO to allow for subclassing"""
670     __csio = None
671
672     def __init__(self, *a, **kw):
673         from cStringIO import StringIO
674         self.__csio = StringIO(*a, **kw)
675
676     def __iter__(self):
677         return self.__csio.__iter__()
678
679     def next(self):
680         return self.__csio.next()
681
682     def close(self):
683         return self.__csio.close()
684
685     def isatty(self):
686         return self.__csio.isatty()
687
688     def seek(self, pos, mode=0):
689         return self.__csio.seek(pos, mode)
690
691     def tell(self):
692         return self.__csio.tell()
693
694     def read(self, n=-1):
695         return self.__csio.read(n)
696
697     def readline(self, length=None):
698         return self.__csio.readline(length)
699
700     def readlines(self, sizehint=0):
701         return self.__csio.readlines(sizehint)
702
703     def truncate(self, size=None):
704         return self.__csio.truncate(size)
705
706     def write(self, s):
707         return self.__csio.write(s)
708
709     def writelines(self, list):
710         return self.__csio.writelines(list)
711
712     def flush(self):
713         return self.__csio.flush()
714
715     def getvalue(self):
716         return self.__csio.getvalue()
717
718 def moduleMovedForSplit(origModuleName, newModuleName, moduleDesc,
719                         projectName, projectURL, globDict):
720     """
721     No-op function; only present for backwards compatibility.  There is no
722     reason to call this function.
723     """
724     warnings.warn(
725         "moduleMovedForSplit is deprecated since Twisted 9.0.",
726         DeprecationWarning, stacklevel=2)
727
728
729 def untilConcludes(f, *a, **kw):
730     while True:
731         try:
732             return f(*a, **kw)
733         except (IOError, OSError), e:
734             if e.args[0] == errno.EINTR:
735                 continue
736             raise
737
738 _idFunction = id
739
740 def setIDFunction(idFunction):
741     """
742     Change the function used by L{unsignedID} to determine the integer id value
743     of an object.  This is largely useful for testing to give L{unsignedID}
744     deterministic, easily-controlled behavior.
745
746     @param idFunction: A function with the signature of L{id}.
747     @return: The previous function being used by L{unsignedID}.
748     """
749     global _idFunction
750     oldIDFunction = _idFunction
751     _idFunction = idFunction
752     return oldIDFunction
753
754
755 # A value about twice as large as any Python int, to which negative values
756 # from id() will be added, moving them into a range which should begin just
757 # above where positive values from id() leave off.
758 _HUGEINT = (sys.maxint + 1L) * 2L
759 def unsignedID(obj):
760     """
761     Return the id of an object as an unsigned number so that its hex
762     representation makes sense.
763
764     This is mostly necessary in Python 2.4 which implements L{id} to sometimes
765     return a negative value.  Python 2.3 shares this behavior, but also
766     implements hex and the %x format specifier to represent negative values as
767     though they were positive ones, obscuring the behavior of L{id}.  Python
768     2.5's implementation of L{id} always returns positive values.
769     """
770     rval = _idFunction(obj)
771     if rval < 0:
772         rval += _HUGEINT
773     return rval
774
775
776 def mergeFunctionMetadata(f, g):
777     """
778     Overwrite C{g}'s name and docstring with values from C{f}.  Update
779     C{g}'s instance dictionary with C{f}'s.
780
781     To use this function safely you must use the return value. In Python 2.3,
782     L{mergeFunctionMetadata} will create a new function. In later versions of
783     Python, C{g} will be mutated and returned.
784
785     @return: A function that has C{g}'s behavior and metadata merged from
786         C{f}.
787     """
788     try:
789         g.__name__ = f.__name__
790     except TypeError:
791         try:
792             merged = new.function(
793                 g.func_code, g.func_globals,
794                 f.__name__, inspect.getargspec(g)[-1],
795                 g.func_closure)
796         except TypeError:
797             pass
798     else:
799         merged = g
800     try:
801         merged.__doc__ = f.__doc__
802     except (TypeError, AttributeError):
803         pass
804     try:
805         merged.__dict__.update(g.__dict__)
806         merged.__dict__.update(f.__dict__)
807     except (TypeError, AttributeError):
808         pass
809     merged.__module__ = f.__module__
810     return merged
811
812
813 def nameToLabel(mname):
814     """
815     Convert a string like a variable name into a slightly more human-friendly
816     string with spaces and capitalized letters.
817
818     @type mname: C{str}
819     @param mname: The name to convert to a label.  This must be a string
820     which could be used as a Python identifier.  Strings which do not take
821     this form will result in unpredictable behavior.
822
823     @rtype: C{str}
824     """
825     labelList = []
826     word = ''
827     lastWasUpper = False
828     for letter in mname:
829         if letter.isupper() == lastWasUpper:
830             # Continuing a word.
831             word += letter
832         else:
833             # breaking a word OR beginning a word
834             if lastWasUpper:
835                 # could be either
836                 if len(word) == 1:
837                     # keep going
838                     word += letter
839                 else:
840                     # acronym
841                     # we're processing the lowercase letter after the acronym-then-capital
842                     lastWord = word[:-1]
843                     firstLetter = word[-1]
844                     labelList.append(lastWord)
845                     word = firstLetter + letter
846             else:
847                 # definitely breaking: lower to upper
848                 labelList.append(word)
849                 word = letter
850         lastWasUpper = letter.isupper()
851     if labelList:
852         labelList[0] = labelList[0].capitalize()
853     else:
854         return mname.capitalize()
855     labelList.append(word)
856     return ' '.join(labelList)
857
858
859
860 def uidFromString(uidString):
861     """
862     Convert a user identifier, as a string, into an integer UID.
863
864     @type uid: C{str}
865     @param uid: A string giving the base-ten representation of a UID or the
866         name of a user which can be converted to a UID via L{pwd.getpwnam}.
867
868     @rtype: C{int}
869     @return: The integer UID corresponding to the given string.
870
871     @raise ValueError: If the user name is supplied and L{pwd} is not
872         available.
873     """
874     try:
875         return int(uidString)
876     except ValueError:
877         if pwd is None:
878             raise
879         return pwd.getpwnam(uidString)[2]
880
881
882
883 def gidFromString(gidString):
884     """
885     Convert a group identifier, as a string, into an integer GID.
886
887     @type uid: C{str}
888     @param uid: A string giving the base-ten representation of a GID or the
889         name of a group which can be converted to a GID via L{grp.getgrnam}.
890
891     @rtype: C{int}
892     @return: The integer GID corresponding to the given string.
893
894     @raise ValueError: If the group name is supplied and L{grp} is not
895         available.
896     """
897     try:
898         return int(gidString)
899     except ValueError:
900         if grp is None:
901             raise
902         return grp.getgrnam(gidString)[2]
903
904
905
906 def runAsEffectiveUser(euid, egid, function, *args, **kwargs):
907     """
908     Run the given function wrapped with seteuid/setegid calls.
909
910     This will try to minimize the number of seteuid/setegid calls, comparing
911     current and wanted permissions
912
913     @param euid: effective UID used to call the function.
914     @type euid: C{int}
915
916     @type egid: effective GID used to call the function.
917     @param egid: C{int}
918
919     @param function: the function run with the specific permission.
920     @type function: any callable
921
922     @param *args: arguments passed to C{function}
923     @param **kwargs: keyword arguments passed to C{function}
924     """
925     uid, gid = os.geteuid(), os.getegid()
926     if uid == euid and gid == egid:
927         return function(*args, **kwargs)
928     else:
929         if uid != 0 and (uid != euid or gid != egid):
930             os.seteuid(0)
931         if gid != egid:
932             os.setegid(egid)
933         if euid != 0 and (euid != uid or gid != egid):
934             os.seteuid(euid)
935         try:
936             return function(*args, **kwargs)
937         finally:
938             if euid != 0 and (uid != euid or gid != egid):
939                 os.seteuid(0)
940             if gid != egid:
941                 os.setegid(gid)
942             if uid != 0 and (uid != euid or gid != egid):
943                 os.seteuid(uid)
944
945
946
947 __all__ = [
948     "uniquify", "padTo", "getPluginDirs", "addPluginDir", "sibpath",
949     "getPassword", "dict", "println", "keyed_md5", "makeStatBar",
950     "OrderedDict", "InsensitiveDict", "spewer", "searchupwards", "LineLog",
951     "raises", "IntervalDifferential", "FancyStrMixin", "FancyEqMixin",
952     "dsu", "switchUID", "SubclassableCStringIO", "moduleMovedForSplit",
953     "unsignedID", "mergeFunctionMetadata", "nameToLabel", "uidFromString",
954     "gidFromString", "runAsEffectiveUser", "moduleMovedForSplit",
955 ]
Note: See TracBrowser for help on using the browser.