| 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 | """ |
|---|
| 569 | decorate-sort-undecorate (aka "Schwartzian transform") |
|---|
| 570 | |
|---|
| 571 | DEPRECATED. Use the built-in C{sorted()} instead. |
|---|
| 572 | """ |
|---|
| 573 | warnings.warn(("dsu is deprecated since Twisted 10.1. " |
|---|
| 574 | "Use the built-in sorted() instead."), DeprecationWarning, stacklevel=2) |
|---|
| 575 | L2 = [(key(e), i, e) for (i, e) in zip(range(len(list)), list)] |
|---|
| 576 | L2.sort() |
|---|
| 577 | return [e[2] for e in L2] |
|---|
| 578 | |
|---|
| 579 | |
|---|
| 580 | try: |
|---|
| 581 | from twisted.python._initgroups import initgroups as _c_initgroups |
|---|
| 582 | except ImportError: |
|---|
| 583 | _c_initgroups = None |
|---|
| 584 | |
|---|
| 585 | |
|---|
| 586 | |
|---|
| 587 | if pwd is None or grp is None or setgroups is None or getgroups is None: |
|---|
| 588 | def initgroups(uid, primaryGid): |
|---|
| 589 | """ |
|---|
| 590 | Do nothing. |
|---|
| 591 | |
|---|
| 592 | Underlying platform support require to manipulate groups is missing. |
|---|
| 593 | """ |
|---|
| 594 | else: |
|---|
| 595 | |
|---|
| 596 | def _setgroups_until_success(l): |
|---|
| 597 | while(1): |
|---|
| 598 | |
|---|
| 599 | |
|---|
| 600 | |
|---|
| 601 | |
|---|
| 602 | try: |
|---|
| 603 | setgroups(l) |
|---|
| 604 | except ValueError: |
|---|
| 605 | |
|---|
| 606 | |
|---|
| 607 | if len(l) > 1: |
|---|
| 608 | del l[-1] |
|---|
| 609 | else: |
|---|
| 610 | raise |
|---|
| 611 | except OSError, e: |
|---|
| 612 | if e.errno == errno.EINVAL and len(l) > 1: |
|---|
| 613 | |
|---|
| 614 | del l[-1] |
|---|
| 615 | else: |
|---|
| 616 | raise |
|---|
| 617 | else: |
|---|
| 618 | |
|---|
| 619 | return |
|---|
| 620 | |
|---|
| 621 | def initgroups(uid, primaryGid): |
|---|
| 622 | """ |
|---|
| 623 | Initializes the group access list. |
|---|
| 624 | |
|---|
| 625 | If the C extension is present, we're calling it, which in turn calls |
|---|
| 626 | initgroups(3). |
|---|
| 627 | |
|---|
| 628 | If not, this is done by reading the group database /etc/group and using |
|---|
| 629 | all groups of which C{uid} is a member. The additional group |
|---|
| 630 | C{primaryGid} is also added to the list. |
|---|
| 631 | |
|---|
| 632 | If the given user is a member of more than C{NGROUPS}, arbitrary |
|---|
| 633 | groups will be silently discarded to bring the number below that |
|---|
| 634 | limit. |
|---|
| 635 | |
|---|
| 636 | @type uid: C{int} |
|---|
| 637 | @param uid: The UID for which to look up group information. |
|---|
| 638 | |
|---|
| 639 | @type primaryGid: C{int} or C{NoneType} |
|---|
| 640 | @param primaryGid: If provided, an additional GID to include when |
|---|
| 641 | setting the groups. |
|---|
| 642 | """ |
|---|
| 643 | if _c_initgroups is not None: |
|---|
| 644 | return _c_initgroups(pwd.getpwuid(uid)[0], primaryGid) |
|---|
| 645 | try: |
|---|
| 646 | |
|---|
| 647 | max_groups = os.sysconf("SC_NGROUPS_MAX") |
|---|
| 648 | except: |
|---|
| 649 | |
|---|
| 650 | max_groups = 0 |
|---|
| 651 | |
|---|
| 652 | username = pwd.getpwuid(uid)[0] |
|---|
| 653 | l = [] |
|---|
| 654 | if primaryGid is not None: |
|---|
| 655 | l.append(primaryGid) |
|---|
| 656 | for groupname, password, gid, userlist in grp.getgrall(): |
|---|
| 657 | if username in userlist: |
|---|
| 658 | l.append(gid) |
|---|
| 659 | if len(l) == max_groups: |
|---|
| 660 | break |
|---|
| 661 | try: |
|---|
| 662 | _setgroups_until_success(l) |
|---|
| 663 | except OSError, e: |
|---|
| 664 | |
|---|
| 665 | |
|---|
| 666 | if e.errno == errno.EPERM: |
|---|
| 667 | for g in getgroups(): |
|---|
| 668 | if g not in l: |
|---|
| 669 | raise |
|---|
| 670 | else: |
|---|
| 671 | raise |
|---|
| 672 | |
|---|
| 673 | |
|---|
| 674 | |
|---|
| 675 | def switchUID(uid, gid, euid=False): |
|---|
| 676 | if euid: |
|---|
| 677 | setuid = os.seteuid |
|---|
| 678 | setgid = os.setegid |
|---|
| 679 | else: |
|---|
| 680 | setuid = os.setuid |
|---|
| 681 | setgid = os.setgid |
|---|
| 682 | if gid is not None: |
|---|
| 683 | setgid(gid) |
|---|
| 684 | if uid is not None: |
|---|
| 685 | initgroups(uid, gid) |
|---|
| 686 | setuid(uid) |
|---|
| 687 | |
|---|
| 688 | |
|---|
| 689 | class SubclassableCStringIO(object): |
|---|
| 690 | """A wrapper around cStringIO to allow for subclassing""" |
|---|
| 691 | __csio = None |
|---|
| 692 | |
|---|
| 693 | def __init__(self, *a, **kw): |
|---|
| 694 | from cStringIO import StringIO |
|---|
| 695 | self.__csio = StringIO(*a, **kw) |
|---|
| 696 | |
|---|
| 697 | def __iter__(self): |
|---|
| 698 | return self.__csio.__iter__() |
|---|
| 699 | |
|---|
| 700 | def next(self): |
|---|
| 701 | return self.__csio.next() |
|---|
| 702 | |
|---|
| 703 | def close(self): |
|---|
| 704 | return self.__csio.close() |
|---|
| 705 | |
|---|
| 706 | def isatty(self): |
|---|
| 707 | return self.__csio.isatty() |
|---|
| 708 | |
|---|
| 709 | def seek(self, pos, mode=0): |
|---|
| 710 | return self.__csio.seek(pos, mode) |
|---|
| 711 | |
|---|
| 712 | def tell(self): |
|---|
| 713 | return self.__csio.tell() |
|---|
| 714 | |
|---|
| 715 | def read(self, n=-1): |
|---|
| 716 | return self.__csio.read(n) |
|---|
| 717 | |
|---|
| 718 | def readline(self, length=None): |
|---|
| 719 | return self.__csio.readline(length) |
|---|
| 720 | |
|---|
| 721 | def readlines(self, sizehint=0): |
|---|
| 722 | return self.__csio.readlines(sizehint) |
|---|
| 723 | |
|---|
| 724 | def truncate(self, size=None): |
|---|
| 725 | return self.__csio.truncate(size) |
|---|
| 726 | |
|---|
| 727 | def write(self, s): |
|---|
| 728 | return self.__csio.write(s) |
|---|
| 729 | |
|---|
| 730 | def writelines(self, list): |
|---|
| 731 | return self.__csio.writelines(list) |
|---|
| 732 | |
|---|
| 733 | def flush(self): |
|---|
| 734 | return self.__csio.flush() |
|---|
| 735 | |
|---|
| 736 | def getvalue(self): |
|---|
| 737 | return self.__csio.getvalue() |
|---|
| 738 | |
|---|
| 739 | def moduleMovedForSplit(origModuleName, newModuleName, moduleDesc, |
|---|
| 740 | projectName, projectURL, globDict): |
|---|
| 741 | """ |
|---|
| 742 | No-op function; only present for backwards compatibility. There is no |
|---|
| 743 | reason to call this function. |
|---|
| 744 | """ |
|---|
| 745 | warnings.warn( |
|---|
| 746 | "moduleMovedForSplit is deprecated since Twisted 9.0.", |
|---|
| 747 | DeprecationWarning, stacklevel=2) |
|---|
| 748 | |
|---|
| 749 | |
|---|
| 750 | def untilConcludes(f, *a, **kw): |
|---|
| 751 | while True: |
|---|
| 752 | try: |
|---|
| 753 | return f(*a, **kw) |
|---|
| 754 | except (IOError, OSError), e: |
|---|
| 755 | if e.args[0] == errno.EINTR: |
|---|
| 756 | continue |
|---|
| 757 | raise |
|---|
| 758 | |
|---|
| 759 | _idFunction = id |
|---|
| 760 | |
|---|
| 761 | def setIDFunction(idFunction): |
|---|
| 762 | """ |
|---|
| 763 | Change the function used by L{unsignedID} to determine the integer id value |
|---|
| 764 | of an object. This is largely useful for testing to give L{unsignedID} |
|---|
| 765 | deterministic, easily-controlled behavior. |
|---|
| 766 | |
|---|
| 767 | @param idFunction: A function with the signature of L{id}. |
|---|
| 768 | @return: The previous function being used by L{unsignedID}. |
|---|
| 769 | """ |
|---|
| 770 | global _idFunction |
|---|
| 771 | oldIDFunction = _idFunction |
|---|
| 772 | _idFunction = idFunction |
|---|
| 773 | return oldIDFunction |
|---|
| 774 | |
|---|
| 775 | |
|---|
| 776 | |
|---|
| 777 | |
|---|
| 778 | |
|---|
| 779 | _HUGEINT = (sys.maxint + 1L) * 2L |
|---|
| 780 | def unsignedID(obj): |
|---|
| 781 | """ |
|---|
| 782 | Return the id of an object as an unsigned number so that its hex |
|---|
| 783 | representation makes sense. |
|---|
| 784 | |
|---|
| 785 | This is mostly necessary in Python 2.4 which implements L{id} to sometimes |
|---|
| 786 | return a negative value. Python 2.3 shares this behavior, but also |
|---|
| 787 | implements hex and the %x format specifier to represent negative values as |
|---|
| 788 | though they were positive ones, obscuring the behavior of L{id}. Python |
|---|
| 789 | 2.5's implementation of L{id} always returns positive values. |
|---|
| 790 | """ |
|---|
| 791 | rval = _idFunction(obj) |
|---|
| 792 | if rval < 0: |
|---|
| 793 | rval += _HUGEINT |
|---|
| 794 | return rval |
|---|
| 795 | |
|---|
| 796 | |
|---|
| 797 | def mergeFunctionMetadata(f, g): |
|---|
| 798 | """ |
|---|
| 799 | Overwrite C{g}'s name and docstring with values from C{f}. Update |
|---|
| 800 | C{g}'s instance dictionary with C{f}'s. |
|---|
| 801 | |
|---|
| 802 | To use this function safely you must use the return value. In Python 2.3, |
|---|
| 803 | L{mergeFunctionMetadata} will create a new function. In later versions of |
|---|
| 804 | Python, C{g} will be mutated and returned. |
|---|
| 805 | |
|---|
| 806 | @return: A function that has C{g}'s behavior and metadata merged from |
|---|
| 807 | C{f}. |
|---|
| 808 | """ |
|---|
| 809 | try: |
|---|
| 810 | g.__name__ = f.__name__ |
|---|
| 811 | except TypeError: |
|---|
| 812 | try: |
|---|
| 813 | merged = new.function( |
|---|
| 814 | g.func_code, g.func_globals, |
|---|
| 815 | f.__name__, inspect.getargspec(g)[-1], |
|---|
| 816 | g.func_closure) |
|---|
| 817 | except TypeError: |
|---|
| 818 | pass |
|---|
| 819 | else: |
|---|
| 820 | merged = g |
|---|
| 821 | try: |
|---|
| 822 | merged.__doc__ = f.__doc__ |
|---|
| 823 | except (TypeError, AttributeError): |
|---|
| 824 | pass |
|---|
| 825 | try: |
|---|
| 826 | merged.__dict__.update(g.__dict__) |
|---|
| 827 | merged.__dict__.update(f.__dict__) |
|---|
| 828 | except (TypeError, AttributeError): |
|---|
| 829 | pass |
|---|
| 830 | merged.__module__ = f.__module__ |
|---|
| 831 | return merged |
|---|
| 832 | |
|---|
| 833 | |
|---|
| 834 | def nameToLabel(mname): |
|---|
| 835 | """ |
|---|
| 836 | Convert a string like a variable name into a slightly more human-friendly |
|---|
| 837 | string with spaces and capitalized letters. |
|---|
| 838 | |
|---|
| 839 | @type mname: C{str} |
|---|
| 840 | @param mname: The name to convert to a label. This must be a string |
|---|
| 841 | which could be used as a Python identifier. Strings which do not take |
|---|
| 842 | this form will result in unpredictable behavior. |
|---|
| 843 | |
|---|
| 844 | @rtype: C{str} |
|---|
| 845 | """ |
|---|
| 846 | labelList = [] |
|---|
| 847 | word = '' |
|---|
| 848 | lastWasUpper = False |
|---|
| 849 | for letter in mname: |
|---|
| 850 | if letter.isupper() == lastWasUpper: |
|---|
| 851 | |
|---|
| 852 | word += letter |
|---|
| 853 | else: |
|---|
| 854 | |
|---|
| 855 | if lastWasUpper: |
|---|
| 856 | |
|---|
| 857 | if len(word) == 1: |
|---|
| 858 | |
|---|
| 859 | word += letter |
|---|
| 860 | else: |
|---|
| 861 | |
|---|
| 862 | |
|---|
| 863 | lastWord = word[:-1] |
|---|
| 864 | firstLetter = word[-1] |
|---|
| 865 | labelList.append(lastWord) |
|---|
| 866 | word = firstLetter + letter |
|---|
| 867 | else: |
|---|
| 868 | |
|---|
| 869 | labelList.append(word) |
|---|
| 870 | word = letter |
|---|
| 871 | lastWasUpper = letter.isupper() |
|---|
| 872 | if labelList: |
|---|
| 873 | labelList[0] = labelList[0].capitalize() |
|---|
| 874 | else: |
|---|
| 875 | return mname.capitalize() |
|---|
| 876 | labelList.append(word) |
|---|
| 877 | return ' '.join(labelList) |
|---|
| 878 | |
|---|
| 879 | |
|---|
| 880 | |
|---|
| 881 | def uidFromString(uidString): |
|---|
| 882 | """ |
|---|
| 883 | Convert a user identifier, as a string, into an integer UID. |
|---|
| 884 | |
|---|
| 885 | @type uid: C{str} |
|---|
| 886 | @param uid: A string giving the base-ten representation of a UID or the |
|---|
| 887 | name of a user which can be converted to a UID via L{pwd.getpwnam}. |
|---|
| 888 | |
|---|
| 889 | @rtype: C{int} |
|---|
| 890 | @return: The integer UID corresponding to the given string. |
|---|
| 891 | |
|---|
| 892 | @raise ValueError: If the user name is supplied and L{pwd} is not |
|---|
| 893 | available. |
|---|
| 894 | """ |
|---|
| 895 | try: |
|---|
| 896 | return int(uidString) |
|---|
| 897 | except ValueError: |
|---|
| 898 | if pwd is None: |
|---|
| 899 | raise |
|---|
| 900 | return pwd.getpwnam(uidString)[2] |
|---|
| 901 | |
|---|
| 902 | |
|---|
| 903 | |
|---|
| 904 | def gidFromString(gidString): |
|---|
| 905 | """ |
|---|
| 906 | Convert a group identifier, as a string, into an integer GID. |
|---|
| 907 | |
|---|
| 908 | @type uid: C{str} |
|---|
| 909 | @param uid: A string giving the base-ten representation of a GID or the |
|---|
| 910 | name of a group which can be converted to a GID via L{grp.getgrnam}. |
|---|
| 911 | |
|---|
| 912 | @rtype: C{int} |
|---|
| 913 | @return: The integer GID corresponding to the given string. |
|---|
| 914 | |
|---|
| 915 | @raise ValueError: If the group name is supplied and L{grp} is not |
|---|
| 916 | available. |
|---|
| 917 | """ |
|---|
| 918 | try: |
|---|
| 919 | return int(gidString) |
|---|
| 920 | except ValueError: |
|---|
| 921 | if grp is None: |
|---|
| 922 | raise |
|---|
| 923 | return grp.getgrnam(gidString)[2] |
|---|
| 924 | |
|---|
| 925 | |
|---|
| 926 | |
|---|
| 927 | def runAsEffectiveUser(euid, egid, function, *args, **kwargs): |
|---|
| 928 | """ |
|---|
| 929 | Run the given function wrapped with seteuid/setegid calls. |
|---|
| 930 | |
|---|
| 931 | This will try to minimize the number of seteuid/setegid calls, comparing |
|---|
| 932 | current and wanted permissions |
|---|
| 933 | |
|---|
| 934 | @param euid: effective UID used to call the function. |
|---|
| 935 | @type euid: C{int} |
|---|
| 936 | |
|---|
| 937 | @type egid: effective GID used to call the function. |
|---|
| 938 | @param egid: C{int} |
|---|
| 939 | |
|---|
| 940 | @param function: the function run with the specific permission. |
|---|
| 941 | @type function: any callable |
|---|
| 942 | |
|---|
| 943 | @param *args: arguments passed to C{function} |
|---|
| 944 | @param **kwargs: keyword arguments passed to C{function} |
|---|
| 945 | """ |
|---|
| 946 | uid, gid = os.geteuid(), os.getegid() |
|---|
| 947 | if uid == euid and gid == egid: |
|---|
| 948 | return function(*args, **kwargs) |
|---|
| 949 | else: |
|---|
| 950 | if uid != 0 and (uid != euid or gid != egid): |
|---|
| 951 | os.seteuid(0) |
|---|
| 952 | if gid != egid: |
|---|
| 953 | os.setegid(egid) |
|---|
| 954 | if euid != 0 and (euid != uid or gid != egid): |
|---|
| 955 | os.seteuid(euid) |
|---|
| 956 | try: |
|---|
| 957 | return function(*args, **kwargs) |
|---|
| 958 | finally: |
|---|
| 959 | if euid != 0 and (uid != euid or gid != egid): |
|---|
| 960 | os.seteuid(0) |
|---|
| 961 | if gid != egid: |
|---|
| 962 | os.setegid(gid) |
|---|
| 963 | if uid != 0 and (uid != euid or gid != egid): |
|---|
| 964 | os.seteuid(uid) |
|---|
| 965 | |
|---|
| 966 | |
|---|
| 967 | |
|---|
| 968 | __all__ = [ |
|---|
| 969 | "uniquify", "padTo", "getPluginDirs", "addPluginDir", "sibpath", |
|---|
| 970 | "getPassword", "dict", "println", "keyed_md5", "makeStatBar", |
|---|
| 971 | "OrderedDict", "InsensitiveDict", "spewer", "searchupwards", "LineLog", |
|---|
| 972 | "raises", "IntervalDifferential", "FancyStrMixin", "FancyEqMixin", |
|---|
| 973 | "dsu", "switchUID", "SubclassableCStringIO", "moduleMovedForSplit", |
|---|
| 974 | "unsignedID", "mergeFunctionMetadata", "nameToLabel", "uidFromString", |
|---|
| 975 | "gidFromString", "runAsEffectiveUser", "moduleMovedForSplit", |
|---|
| 976 | ] |
|---|