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 |