1
# -*- test-case-name: twisted.python.test.test_util -*-
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
3
# See LICENSE for details.
5
import os, sys, hmac, errno, new, inspect, warnings
11
from os import setgroups, getgroups
13
setgroups = getgroups = None
14
from UserDict import UserDict
17
class InsensitiveDict:
18
"""Dictionary, that has case-insensitive keys.
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().
26
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66315 originally
27
contributed by Sami Hangaslammi.
30
def __init__(self, dict=None, preserve=1):
31
"""Create an empty dictionary, or update from 'dict'."""
33
self.preserve=preserve
37
def __delitem__(self, key):
38
k=self._lowerOrReturn(key)
41
def _lowerOrReturn(self, key):
42
if isinstance(key, str) or isinstance(key, unicode):
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]
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)
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)
64
def _doPreserve(self, key):
65
if not self.preserve and (isinstance(key, str)
66
or isinstance(key, unicode)):
72
"""List of keys in their original case."""
73
return list(self.iterkeys())
77
return list(self.itervalues())
80
"""List of (key,value) pairs."""
81
return list(self.iteritems())
83
def get(self, key, default=None):
84
"""Retrieve value associated with 'key' or return default value
85
if 'key' doesn't exist."""
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):
98
def update(self, dict):
99
"""Copy (key,value) pairs from 'dict'."""
100
for k,v in dict.items():
104
"""String representation of the dictionary."""
105
items = ", ".join([("%r: %r" % (k,v)) for k,v in self.items()])
106
return "InsensitiveDict({%s})" % items
109
for v in self.data.itervalues():
110
yield self._doPreserve(v[0])
112
def itervalues(self):
113
for v in self.data.itervalues():
117
for (k, v) in self.data.itervalues():
118
yield self._doPreserve(k), v
126
for k in self.keys():
130
return InsensitiveDict(self, self.preserve)
133
return len(self.data)
135
def __eq__(self, other):
136
for k,v in self.items():
137
if not (k in other) or not (other[k]==v):
139
return len(self)==len(other)
141
class OrderedDict(UserDict):
142
"""A UserDict that preserves insert order whenever possible."""
143
def __init__(self, dict=None, **kwargs):
147
if hasattr(dict,'keys'):
150
for k,v in dict: # sequence
155
return '{'+', '.join([('%r: %r' % item) for item in self.items()])+'}'
157
def __setitem__(self, key, value):
158
if not self.has_key(key):
159
self._order.append(key)
160
UserDict.__setitem__(self, key, value)
163
return self.__class__(self)
165
def __delitem__(self, key):
166
UserDict.__delitem__(self, key)
167
self._order.remove(key)
170
for item in self._order:
171
yield (item, self[item])
174
return list(self.iteritems())
176
def itervalues(self):
177
for item in self._order:
181
return list(self.itervalues())
184
return iter(self._order)
187
return list(self._order)
190
key = self._order[-1]
195
def setdefault(self, item, default):
196
if self.has_key(item):
202
for k, v in d.items():
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.
212
if not dct.has_key(k): result.append(k)
216
def padTo(n, seq, default=None):
217
"""Pads a sequence out to n elements,
219
filling in with a default value if it is not long enough.
221
If the input sequence is longer than n, raises ValueError.
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.
229
raise ValueError, "%d elements is more than %d." % (len(seq), n)
231
blank = [default] * n
233
blank[:len(seq)] = list(seq)
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])
247
sys.path.extend(getPluginDirs())
249
def sibpath(path, sibling):
250
"""Return the path to a sibling of a file in the filesystem.
252
This is useful in conjunction with the special __file__ attribute
253
that Python provides for modules, so modules can load associated
256
return os.path.join(os.path.dirname(os.path.abspath(path)), sibling)
259
def _getpass(prompt):
260
"""Helper to turn IOErrors into KeyboardInterrupts"""
263
return getpass.getpass(prompt)
265
if e.errno == errno.EINTR:
266
raise KeyboardInterrupt
269
raise KeyboardInterrupt
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.
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.
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.
287
isaTTY = hasattr(sys.stdin, 'isatty') and sys.stdin.isatty()
294
old = sys.stdin, sys.stdout
295
sys.stdin = sys.stdout = open('/dev/tty', 'r+')
297
raise RuntimeError("Cannot obtain a TTY")
299
password = sys.stdin.readline()
300
if password[-1] == '\n':
301
password = password[:-1]
305
try1 = _getpass(prompt)
308
try2 = _getpass(confirmPrompt)
312
sys.stderr.write(mismatchMessage + "\n")
316
sys.stdin, sys.stdout = old
321
warnings.warn('twisted.python.util.dict is deprecated. Use __builtin__.dict instead')
322
return __builtin__.dict(*a, **k)
325
sys.stdout.write(' '.join(map(str, a))+'\n')
328
# This does not belong here
329
# But where does it belong?
332
return ''.join([chr(ord(c) ^ b) for c in s])
334
def keyed_md5(secret, challenge):
336
Create the keyed MD5 string for the given secret and challenge.
339
"keyed_md5() is deprecated. Use the stdlib module hmac instead.",
340
DeprecationWarning, stacklevel=2
342
return hmac.HMAC(secret, challenge).hexdigest()
344
def makeStatBar(width, maxPosition, doneChar = '=', undoneChar = '-', currentChar = '>'):
345
"""Creates a function that will return a string representing a progress bar.
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)
356
if result == last[0]:
361
statBar.__doc__ = """statBar(position, force = 0) -> '[%s%s%s]'-style progress bar
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)
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__)
378
k = reflect.qual(type(se))
379
print 'method %s of %s at %s' % (
380
frame.f_code.co_name, k, id(se)
383
print 'function %s in %s, line %s' % (
384
frame.f_code.co_name,
385
frame.f_code.co_filename,
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'])
393
If not found, return None
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
399
candidate=join(parents)+os.sep
402
if not exists("%s%s" % (candidate, f)):
407
if not isdir("%s%s" % (candidate, d)):
410
if allpresent: return candidate
417
A limited-size line-based log, useful for logging line-based
418
protocols such as SMTP.
420
When the log fills up, old entries drop off the end.
422
def __init__(self, size=10):
424
Create a new log, with size lines of storage (default 10).
425
A log size of 0 (or less) means an infinite log.
429
self.log = [None]*size
432
def append(self,line):
434
self.log[:-1] = self.log[1:]
437
self.log.append(line)
440
return '\n'.join(filter(None,self.log))
442
def __getitem__(self, item):
443
return filter(None,self.log)[item]
447
self.log = [None]*self.size
449
def raises(exception, f, *args, **kwargs):
450
"""Determine whether the given call raises the given exception"""
457
class IntervalDifferential:
459
Given a list of intervals, generate the amount of time to sleep between
462
For example, given 7, 11 and 13, the three (infinite) sequences::
468
will be generated, merged, and used to produce::
470
(7, 0) (4, 1) (2, 2) (1, 0) (7, 0) (1, 1) (4, 2) (2, 0) (5, 1) (2, 0)
472
New intervals may be added or removed as iteration proceeds using the
476
def __init__(self, intervals, default=60):
478
@type intervals: C{list} of C{int}, C{long}, or C{float} param
479
@param intervals: The intervals between instants.
481
@type default: C{int}, C{long}, or C{float}
482
@param default: The duration to generate if the intervals list
485
self.intervals = intervals[:]
486
self.default = default
489
return _IntervalDifferentialIterator(self.intervals, self.default)
491
class _IntervalDifferentialIterator:
492
def __init__(self, i, d):
494
self.intervals = [[e, e, n] for (e, n) in zip(i, range(len(i)))]
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
508
def addInterval(self, i):
510
delay = self.intervals[0][0] - self.intervals[0][1]
511
self.intervals.append([delay + i, i, len(self.intervals)])
512
self.intervals.sort()
514
self.intervals.append([i, i, 0])
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:
525
raise ValueError, "Specified interval not in IntervalDifferential"
530
Set showAttributes to a sequence of strings naming attributes, OR
531
sequences of (attributeName, displayName, formatCharacter)
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)))
540
r.append((' %s=' + attr[2]) % (attr[1], getattr(self, attr[0])))
548
compareAttributes = ()
549
def __eq__(self, other):
550
if not self.compareAttributes:
552
if isinstance(self, other.__class__):
554
[getattr(self, name) for name in self.compareAttributes] ==
555
[getattr(other, name) for name in self.compareAttributes])
556
return NotImplemented
559
def __ne__(self, other):
560
result = self.__eq__(other)
561
if result is NotImplemented:
568
L2 = [(key(e), i, e) for (i, e) in zip(range(len(list)), list)]
570
return [e[2] for e in L2]
573
from twisted.python._initgroups import initgroups as _c_initgroups
579
if pwd is None or grp is None or setgroups is None or getgroups is None:
580
def initgroups(uid, primaryGid):
584
Underlying platform support require to manipulate groups is missing.
587
# Fallback to the inefficient Python version
588
def _setgroups_until_success(l):
590
# NASTY NASTY HACK (but glibc does it so it must be okay):
591
# In case sysconfig didn't give the right answer, find the limit
592
# on max groups by just looping, trying to set fewer and fewer
593
# groups each time until it succeeds.
597
# This exception comes from python itself restricting
598
# number of groups allowed.
604
if e.errno == errno.EINVAL and len(l) > 1:
605
# This comes from the OS saying too many groups
613
def initgroups(uid, primaryGid):
615
Initializes the group access list.
617
If the C extension is present, we're calling it, which in turn calls
620
If not, this is done by reading the group database /etc/group and using
621
all groups of which C{uid} is a member. The additional group
622
C{primaryGid} is also added to the list.
624
If the given user is a member of more than C{NGROUPS}, arbitrary
625
groups will be silently discarded to bring the number below that
629
@param uid: The UID for which to look up group information.
631
@type primaryGid: C{int} or C{NoneType}
632
@param primaryGid: If provided, an additional GID to include when
635
if _c_initgroups is not None:
636
return _c_initgroups(pwd.getpwuid(uid)[0], primaryGid)
638
# Try to get the maximum number of groups
639
max_groups = os.sysconf("SC_NGROUPS_MAX")
641
# No predefined limit
644
username = pwd.getpwuid(uid)[0]
646
if primaryGid is not None:
648
for groupname, password, gid, userlist in grp.getgrall():
649
if username in userlist:
651
if len(l) == max_groups:
652
break # No more groups, ignore any more
654
_setgroups_until_success(l)
656
# We might be able to remove this code now that we
657
# don't try to setgid/setuid even when not asked to.
658
if e.errno == errno.EPERM:
659
for g in getgroups():
667
def switchUID(uid, gid, euid=False):
681
class SubclassableCStringIO(object):
682
"""A wrapper around cStringIO to allow for subclassing"""
685
def __init__(self, *a, **kw):
686
from cStringIO import StringIO
687
self.__csio = StringIO(*a, **kw)
690
return self.__csio.__iter__()
693
return self.__csio.next()
696
return self.__csio.close()
699
return self.__csio.isatty()
701
def seek(self, pos, mode=0):
702
return self.__csio.seek(pos, mode)
705
return self.__csio.tell()
707
def read(self, n=-1):
708
return self.__csio.read(n)
710
def readline(self, length=None):
711
return self.__csio.readline(length)
713
def readlines(self, sizehint=0):
714
return self.__csio.readlines(sizehint)
716
def truncate(self, size=None):
717
return self.__csio.truncate(size)
720
return self.__csio.write(s)
722
def writelines(self, list):
723
return self.__csio.writelines(list)
726
return self.__csio.flush()
729
return self.__csio.getvalue()
731
def moduleMovedForSplit(origModuleName, newModuleName, moduleDesc,
732
projectName, projectURL, globDict):
734
No-op function; only present for backwards compatibility. There is no
735
reason to call this function.
738
"moduleMovedForSplit is deprecated since Twisted 9.0.",
739
DeprecationWarning, stacklevel=2)
742
def untilConcludes(f, *a, **kw):
746
except (IOError, OSError), e:
747
if e.args[0] == errno.EINTR:
753
def setIDFunction(idFunction):
755
Change the function used by L{unsignedID} to determine the integer id value
756
of an object. This is largely useful for testing to give L{unsignedID}
757
deterministic, easily-controlled behavior.
759
@param idFunction: A function with the signature of L{id}.
760
@return: The previous function being used by L{unsignedID}.
763
oldIDFunction = _idFunction
764
_idFunction = idFunction
768
# A value about twice as large as any Python int, to which negative values
769
# from id() will be added, moving them into a range which should begin just
770
# above where positive values from id() leave off.
771
_HUGEINT = (sys.maxint + 1L) * 2L
774
Return the id of an object as an unsigned number so that its hex
775
representation makes sense.
777
This is mostly necessary in Python 2.4 which implements L{id} to sometimes
778
return a negative value. Python 2.3 shares this behavior, but also
779
implements hex and the %x format specifier to represent negative values as
780
though they were positive ones, obscuring the behavior of L{id}. Python
781
2.5's implementation of L{id} always returns positive values.
783
rval = _idFunction(obj)
789
def mergeFunctionMetadata(f, g):
791
Overwrite C{g}'s name and docstring with values from C{f}. Update
792
C{g}'s instance dictionary with C{f}'s.
794
To use this function safely you must use the return value. In Python 2.3,
795
L{mergeFunctionMetadata} will create a new function. In later versions of
796
Python, C{g} will be mutated and returned.
798
@return: A function that has C{g}'s behavior and metadata merged from
802
g.__name__ = f.__name__
805
merged = new.function(
806
g.func_code, g.func_globals,
807
f.__name__, inspect.getargspec(g)[-1],
814
merged.__doc__ = f.__doc__
815
except (TypeError, AttributeError):
818
merged.__dict__.update(g.__dict__)
819
merged.__dict__.update(f.__dict__)
820
except (TypeError, AttributeError):
822
merged.__module__ = f.__module__
826
def nameToLabel(mname):
828
Convert a string like a variable name into a slightly more human-friendly
829
string with spaces and capitalized letters.
832
@param mname: The name to convert to a label. This must be a string
833
which could be used as a Python identifier. Strings which do not take
834
this form will result in unpredictable behavior.
842
if letter.isupper() == lastWasUpper:
846
# breaking a word OR beginning a word
854
# we're processing the lowercase letter after the acronym-then-capital
856
firstLetter = word[-1]
857
labelList.append(lastWord)
858
word = firstLetter + letter
860
# definitely breaking: lower to upper
861
labelList.append(word)
863
lastWasUpper = letter.isupper()
865
labelList[0] = labelList[0].capitalize()
867
return mname.capitalize()
868
labelList.append(word)
869
return ' '.join(labelList)
873
def uidFromString(uidString):
875
Convert a user identifier, as a string, into an integer UID.
878
@param uid: A string giving the base-ten representation of a UID or the
879
name of a user which can be converted to a UID via L{pwd.getpwnam}.
882
@return: The integer UID corresponding to the given string.
884
@raise ValueError: If the user name is supplied and L{pwd} is not
888
return int(uidString)
892
return pwd.getpwnam(uidString)[2]
896
def gidFromString(gidString):
898
Convert a group identifier, as a string, into an integer GID.
901
@param uid: A string giving the base-ten representation of a GID or the
902
name of a group which can be converted to a GID via L{grp.getgrnam}.
905
@return: The integer GID corresponding to the given string.
907
@raise ValueError: If the group name is supplied and L{grp} is not
911
return int(gidString)
915
return grp.getgrnam(gidString)[2]
919
def runAsEffectiveUser(euid, egid, function, *args, **kwargs):
921
Run the given function wrapped with seteuid/setegid calls.
923
This will try to minimize the number of seteuid/setegid calls, comparing
924
current and wanted permissions
926
@param euid: effective UID used to call the function.
929
@type egid: effective GID used to call the function.
932
@param function: the function run with the specific permission.
933
@type function: any callable
935
@param *args: arguments passed to C{function}
936
@param **kwargs: keyword arguments passed to C{function}
938
uid, gid = os.geteuid(), os.getegid()
939
if uid == euid and gid == egid:
940
return function(*args, **kwargs)
942
if uid != 0 and (uid != euid or gid != egid):
946
if euid != 0 and (euid != uid or gid != egid):
949
return function(*args, **kwargs)
951
if euid != 0 and (uid != euid or gid != egid):
955
if uid != 0 and (uid != euid or gid != egid):
961
"uniquify", "padTo", "getPluginDirs", "addPluginDir", "sibpath",
962
"getPassword", "dict", "println", "keyed_md5", "makeStatBar",
963
"OrderedDict", "InsensitiveDict", "spewer", "searchupwards", "LineLog",
964
"raises", "IntervalDifferential", "FancyStrMixin", "FancyEqMixin",
965
"dsu", "switchUID", "SubclassableCStringIO", "moduleMovedForSplit",
966
"unsignedID", "mergeFunctionMetadata", "nameToLabel", "uidFromString",
967
"gidFromString", "runAsEffectiveUser", "moduleMovedForSplit",