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