| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 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: |
|---|
| 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 |
|
|---|
| 328 |
|
|---|
| 329 |
|
|---|
| 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 |
|
|---|
| 583 |
|
|---|
| 584 |
|
|---|
| 585 |
|
|---|
| 586 |
try: |
|---|
| 587 |
setgroups(l) |
|---|
| 588 |
except ValueError: |
|---|
| 589 |
|
|---|
| 590 |
|
|---|
| 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 |
|
|---|
| 598 |
del l[-1] |
|---|
| 599 |
else: |
|---|
| 600 |
raise |
|---|
| 601 |
else: |
|---|
| 602 |
|
|---|
| 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 |
|
|---|
| 626 |
max_groups = os.sysconf("SC_NGROUPS_MAX") |
|---|
| 627 |
except: |
|---|
| 628 |
|
|---|
| 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 |
|---|
| 640 |
try: |
|---|
| 641 |
_setgroups_until_success(l) |
|---|
| 642 |
except OSError, e: |
|---|
| 643 |
|
|---|
| 644 |
|
|---|
| 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 |
|
|---|
| 756 |
|
|---|
| 757 |
|
|---|
| 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 |
|
|---|
| 831 |
word += letter |
|---|
| 832 |
else: |
|---|
| 833 |
|
|---|
| 834 |
if lastWasUpper: |
|---|
| 835 |
|
|---|
| 836 |
if len(word) == 1: |
|---|
| 837 |
|
|---|
| 838 |
word += letter |
|---|
| 839 |
else: |
|---|
| 840 |
|
|---|
| 841 |
|
|---|
| 842 |
lastWord = word[:-1] |
|---|
| 843 |
firstLetter = word[-1] |
|---|
| 844 |
labelList.append(lastWord) |
|---|
| 845 |
word = firstLetter + letter |
|---|
| 846 |
else: |
|---|
| 847 |
|
|---|
| 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 |
] |
|---|