~soren/nova/iptables-security-groups

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/util.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.python.test.test_util -*-
 
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
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: # 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 __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
# XXX
 
328
# This does not belong here
 
329
# But where does it belong?
 
330
 
 
331
def str_xor(s, b):
 
332
    return ''.join([chr(ord(c) ^ b) for c in s])
 
333
 
 
334
def keyed_md5(secret, challenge):
 
335
    """
 
336
    Create the keyed MD5 string for the given secret and challenge.
 
337
    """
 
338
    warnings.warn(
 
339
        "keyed_md5() is deprecated.  Use the stdlib module hmac instead.",
 
340
        DeprecationWarning, stacklevel=2
 
341
    )
 
342
    return hmac.HMAC(secret, challenge).hexdigest()
 
343
 
 
344
def makeStatBar(width, maxPosition, doneChar = '=', undoneChar = '-', currentChar = '>'):
 
345
    """Creates a function that will return a string representing a progress bar.
 
346
    """
 
347
    aValue = width / float(maxPosition)
 
348
    def statBar(position, force = 0, last = ['']):
 
349
        assert len(last) == 1, "Don't mess with the last parameter."
 
350
        done = int(aValue * position)
 
351
        toDo = width - done - 2
 
352
        result = "[%s%s%s]" % (doneChar * done, currentChar, undoneChar * toDo)
 
353
        if force:
 
354
            last[0] = result
 
355
            return result
 
356
        if result == last[0]:
 
357
            return ''
 
358
        last[0] = result
 
359
        return result
 
360
 
 
361
    statBar.__doc__ = """statBar(position, force = 0) -> '[%s%s%s]'-style progress bar
 
362
 
 
363
    returned string is %d characters long, and the range goes from 0..%d.
 
364
    The 'position' argument is where the '%s' will be drawn.  If force is false,
 
365
    '' will be returned instead if the resulting progress bar is identical to the
 
366
    previously returned progress bar.
 
367
""" % (doneChar * 3, currentChar, undoneChar * 3, width, maxPosition, currentChar)
 
368
    return statBar
 
369
 
 
370
def spewer(frame, s, ignored):
 
371
    """A trace function for sys.settrace that prints every function or method call."""
 
372
    from twisted.python import reflect
 
373
    if frame.f_locals.has_key('self'):
 
374
        se = frame.f_locals['self']
 
375
        if hasattr(se, '__class__'):
 
376
            k = reflect.qual(se.__class__)
 
377
        else:
 
378
            k = reflect.qual(type(se))
 
379
        print 'method %s of %s at %s' % (
 
380
            frame.f_code.co_name, k, id(se)
 
381
        )
 
382
    else:
 
383
        print 'function %s in %s, line %s' % (
 
384
            frame.f_code.co_name,
 
385
            frame.f_code.co_filename,
 
386
            frame.f_lineno)
 
387
 
 
388
def searchupwards(start, files=[], dirs=[]):
 
389
    """Walk upwards from start, looking for a directory containing
 
390
    all files and directories given as arguments::
 
391
    >>> searchupwards('.', ['foo.txt'], ['bar', 'bam'])
 
392
 
 
393
    If not found, return None
 
394
    """
 
395
    start=os.path.abspath(start)
 
396
    parents=start.split(os.sep)
 
397
    exists=os.path.exists; join=os.sep.join; isdir=os.path.isdir
 
398
    while len(parents):
 
399
        candidate=join(parents)+os.sep
 
400
        allpresent=1
 
401
        for f in files:
 
402
            if not exists("%s%s" % (candidate, f)):
 
403
                allpresent=0
 
404
                break
 
405
        if allpresent:
 
406
            for d in dirs:
 
407
                if not isdir("%s%s" % (candidate, d)):
 
408
                    allpresent=0
 
409
                    break
 
410
        if allpresent: return candidate
 
411
        parents.pop(-1)
 
412
    return None
 
413
 
 
414
 
 
415
class LineLog:
 
416
    """
 
417
    A limited-size line-based log, useful for logging line-based
 
418
    protocols such as SMTP.
 
419
 
 
420
    When the log fills up, old entries drop off the end.
 
421
    """
 
422
    def __init__(self, size=10):
 
423
        """
 
424
        Create a new log, with size lines of storage (default 10).
 
425
        A log size of 0 (or less) means an infinite log.
 
426
        """
 
427
        if size < 0:
 
428
            size = 0
 
429
        self.log = [None]*size
 
430
        self.size = size
 
431
 
 
432
    def append(self,line):
 
433
        if self.size:
 
434
            self.log[:-1] = self.log[1:]
 
435
            self.log[-1] = line
 
436
        else:
 
437
            self.log.append(line)
 
438
 
 
439
    def str(self):
 
440
        return '\n'.join(filter(None,self.log))
 
441
 
 
442
    def __getitem__(self, item):
 
443
        return filter(None,self.log)[item]
 
444
 
 
445
    def clear(self):
 
446
        """Empty the log"""
 
447
        self.log = [None]*self.size
 
448
 
 
449
def raises(exception, f, *args, **kwargs):
 
450
    """Determine whether the given call raises the given exception"""
 
451
    try:
 
452
        f(*args, **kwargs)
 
453
    except exception:
 
454
        return 1
 
455
    return 0
 
456
 
 
457
class IntervalDifferential:
 
458
    """
 
459
    Given a list of intervals, generate the amount of time to sleep between
 
460
    \"instants\".
 
461
 
 
462
    For example, given 7, 11 and 13, the three (infinite) sequences::
 
463
 
 
464
        7 14 21 28 35 ...
 
465
        11 22 33 44 ...
 
466
        13 26 39 52 ...
 
467
 
 
468
    will be generated, merged, and used to produce::
 
469
 
 
470
        (7, 0) (4, 1) (2, 2) (1, 0) (7, 0) (1, 1) (4, 2) (2, 0) (5, 1) (2, 0)
 
471
 
 
472
    New intervals may be added or removed as iteration proceeds using the
 
473
    proper methods.
 
474
    """
 
475
 
 
476
    def __init__(self, intervals, default=60):
 
477
        """
 
478
        @type intervals: C{list} of C{int}, C{long}, or C{float} param
 
479
        @param intervals: The intervals between instants.
 
480
 
 
481
        @type default: C{int}, C{long}, or C{float}
 
482
        @param default: The duration to generate if the intervals list
 
483
        becomes empty.
 
484
        """
 
485
        self.intervals = intervals[:]
 
486
        self.default = default
 
487
 
 
488
    def __iter__(self):
 
489
        return _IntervalDifferentialIterator(self.intervals, self.default)
 
490
 
 
491
class _IntervalDifferentialIterator:
 
492
    def __init__(self, i, d):
 
493
 
 
494
        self.intervals = [[e, e, n] for (e, n) in zip(i, range(len(i)))]
 
495
        self.default = d
 
496
        self.last = 0
 
497
 
 
498
    def next(self):
 
499
        if not self.intervals:
 
500
            return (self.default, None)
 
501
        last, index = self.intervals[0][0], self.intervals[0][2]
 
502
        self.intervals[0][0] += self.intervals[0][1]
 
503
        self.intervals.sort()
 
504
        result = last - self.last
 
505
        self.last = last
 
506
        return result, index
 
507
 
 
508
    def addInterval(self, i):
 
509
        if self.intervals:
 
510
            delay = self.intervals[0][0] - self.intervals[0][1]
 
511
            self.intervals.append([delay + i, i, len(self.intervals)])
 
512
            self.intervals.sort()
 
513
        else:
 
514
            self.intervals.append([i, i, 0])
 
515
 
 
516
    def removeInterval(self, interval):
 
517
        for i in range(len(self.intervals)):
 
518
            if self.intervals[i][1] == interval:
 
519
                index = self.intervals[i][2]
 
520
                del self.intervals[i]
 
521
                for i in self.intervals:
 
522
                    if i[2] > index:
 
523
                        i[2] -= 1
 
524
                return
 
525
        raise ValueError, "Specified interval not in IntervalDifferential"
 
526
 
 
527
 
 
528
class FancyStrMixin:
 
529
    """
 
530
    Set showAttributes to a sequence of strings naming attributes, OR
 
531
    sequences of (attributeName, displayName, formatCharacter)
 
532
    """
 
533
    showAttributes = ()
 
534
    def __str__(self):
 
535
        r = ['<', hasattr(self, 'fancybasename') and self.fancybasename or self.__class__.__name__]
 
536
        for attr in self.showAttributes:
 
537
            if isinstance(attr, str):
 
538
                r.append(' %s=%r' % (attr, getattr(self, attr)))
 
539
            else:
 
540
                r.append((' %s=' + attr[2]) % (attr[1], getattr(self, attr[0])))
 
541
        r.append('>')
 
542
        return ''.join(r)
 
543
    __repr__ = __str__
 
544
 
 
545
 
 
546
 
 
547
class FancyEqMixin:
 
548
    compareAttributes = ()
 
549
    def __eq__(self, other):
 
550
        if not self.compareAttributes:
 
551
            return self is other
 
552
        if isinstance(self, other.__class__):
 
553
            return (
 
554
                [getattr(self, name) for name in self.compareAttributes] ==
 
555
                [getattr(other, name) for name in self.compareAttributes])
 
556
        return NotImplemented
 
557
 
 
558
 
 
559
    def __ne__(self, other):
 
560
        result = self.__eq__(other)
 
561
        if result is NotImplemented:
 
562
            return result
 
563
        return not result
 
564
 
 
565
 
 
566
 
 
567
def dsu(list, key):
 
568
    L2 = [(key(e), i, e) for (i, e) in zip(range(len(list)), list)]
 
569
    L2.sort()
 
570
    return [e[2] for e in L2]
 
571
 
 
572
try:
 
573
    from twisted.python._initgroups import initgroups as _c_initgroups
 
574
except ImportError:
 
575
    _c_initgroups = None
 
576
 
 
577
 
 
578
 
 
579
if pwd is None or grp is None or setgroups is None or getgroups is None:
 
580
    def initgroups(uid, primaryGid):
 
581
        """
 
582
        Do nothing.
 
583
 
 
584
        Underlying platform support require to manipulate groups is missing.
 
585
        """
 
586
else:
 
587
    # Fallback to the inefficient Python version
 
588
    def _setgroups_until_success(l):
 
589
        while(1):
 
590
            # NASTY NASTY HACK (but glibc does it so it must be okay):
 
591
            # In case sysconfig didn't give the right answer, find the limit
 
592
            # on max groups by just looping, trying to set fewer and fewer
 
593
            # groups each time until it succeeds.
 
594
            try:
 
595
                setgroups(l)
 
596
            except ValueError:
 
597
                # This exception comes from python itself restricting
 
598
                # number of groups allowed.
 
599
                if len(l) > 1:
 
600
                    del l[-1]
 
601
                else:
 
602
                    raise
 
603
            except OSError, e:
 
604
                if e.errno == errno.EINVAL and len(l) > 1:
 
605
                    # This comes from the OS saying too many groups
 
606
                    del l[-1]
 
607
                else:
 
608
                    raise
 
609
            else:
 
610
                # Success, yay!
 
611
                return
 
612
 
 
613
    def initgroups(uid, primaryGid):
 
614
        """
 
615
        Initializes the group access list.
 
616
 
 
617
        If the C extension is present, we're calling it, which in turn calls
 
618
        initgroups(3).
 
619
        
 
620
        If not, this is done by reading the group database /etc/group and using
 
621
        all groups of which C{uid} is a member.  The additional group
 
622
        C{primaryGid} is also added to the list.
 
623
 
 
624
        If the given user is a member of more than C{NGROUPS}, arbitrary
 
625
        groups will be silently discarded to bring the number below that
 
626
        limit.
 
627
 
 
628
        @type uid: C{int}
 
629
        @param uid: The UID for which to look up group information.
 
630
 
 
631
        @type primaryGid: C{int} or C{NoneType}
 
632
        @param primaryGid: If provided, an additional GID to include when
 
633
            setting the groups.
 
634
        """
 
635
        if _c_initgroups is not None:
 
636
            return _c_initgroups(pwd.getpwuid(uid)[0], primaryGid)
 
637
        try:
 
638
            # Try to get the maximum number of groups
 
639
            max_groups = os.sysconf("SC_NGROUPS_MAX")
 
640
        except:
 
641
            # No predefined limit
 
642
            max_groups = 0
 
643
 
 
644
        username = pwd.getpwuid(uid)[0]
 
645
        l = []
 
646
        if primaryGid is not None:
 
647
            l.append(primaryGid)
 
648
        for groupname, password, gid, userlist in grp.getgrall():
 
649
            if username in userlist:
 
650
                l.append(gid)
 
651
                if len(l) == max_groups:
 
652
                    break # No more groups, ignore any more
 
653
        try:
 
654
            _setgroups_until_success(l)
 
655
        except OSError, e:
 
656
            # We might be able to remove this code now that we
 
657
            # don't try to setgid/setuid even when not asked to.
 
658
            if e.errno == errno.EPERM:
 
659
                for g in getgroups():
 
660
                    if g not in l:
 
661
                        raise
 
662
            else:
 
663
                raise
 
664
 
 
665
 
 
666
 
 
667
def switchUID(uid, gid, euid=False):
 
668
    if euid:
 
669
        setuid = os.seteuid
 
670
        setgid = os.setegid
 
671
    else:
 
672
        setuid = os.setuid
 
673
        setgid = os.setgid
 
674
    if gid is not None:
 
675
        setgid(gid)
 
676
    if uid is not None:
 
677
        initgroups(uid, gid)
 
678
        setuid(uid)
 
679
 
 
680
 
 
681
class SubclassableCStringIO(object):
 
682
    """A wrapper around cStringIO to allow for subclassing"""
 
683
    __csio = None
 
684
 
 
685
    def __init__(self, *a, **kw):
 
686
        from cStringIO import StringIO
 
687
        self.__csio = StringIO(*a, **kw)
 
688
 
 
689
    def __iter__(self):
 
690
        return self.__csio.__iter__()
 
691
 
 
692
    def next(self):
 
693
        return self.__csio.next()
 
694
 
 
695
    def close(self):
 
696
        return self.__csio.close()
 
697
 
 
698
    def isatty(self):
 
699
        return self.__csio.isatty()
 
700
 
 
701
    def seek(self, pos, mode=0):
 
702
        return self.__csio.seek(pos, mode)
 
703
 
 
704
    def tell(self):
 
705
        return self.__csio.tell()
 
706
 
 
707
    def read(self, n=-1):
 
708
        return self.__csio.read(n)
 
709
 
 
710
    def readline(self, length=None):
 
711
        return self.__csio.readline(length)
 
712
 
 
713
    def readlines(self, sizehint=0):
 
714
        return self.__csio.readlines(sizehint)
 
715
 
 
716
    def truncate(self, size=None):
 
717
        return self.__csio.truncate(size)
 
718
 
 
719
    def write(self, s):
 
720
        return self.__csio.write(s)
 
721
 
 
722
    def writelines(self, list):
 
723
        return self.__csio.writelines(list)
 
724
 
 
725
    def flush(self):
 
726
        return self.__csio.flush()
 
727
 
 
728
    def getvalue(self):
 
729
        return self.__csio.getvalue()
 
730
 
 
731
def moduleMovedForSplit(origModuleName, newModuleName, moduleDesc,
 
732
                        projectName, projectURL, globDict):
 
733
    """
 
734
    No-op function; only present for backwards compatibility.  There is no
 
735
    reason to call this function.
 
736
    """
 
737
    warnings.warn(
 
738
        "moduleMovedForSplit is deprecated since Twisted 9.0.",
 
739
        DeprecationWarning, stacklevel=2)
 
740
 
 
741
 
 
742
def untilConcludes(f, *a, **kw):
 
743
    while True:
 
744
        try:
 
745
            return f(*a, **kw)
 
746
        except (IOError, OSError), e:
 
747
            if e.args[0] == errno.EINTR:
 
748
                continue
 
749
            raise
 
750
 
 
751
_idFunction = id
 
752
 
 
753
def setIDFunction(idFunction):
 
754
    """
 
755
    Change the function used by L{unsignedID} to determine the integer id value
 
756
    of an object.  This is largely useful for testing to give L{unsignedID}
 
757
    deterministic, easily-controlled behavior.
 
758
 
 
759
    @param idFunction: A function with the signature of L{id}.
 
760
    @return: The previous function being used by L{unsignedID}.
 
761
    """
 
762
    global _idFunction
 
763
    oldIDFunction = _idFunction
 
764
    _idFunction = idFunction
 
765
    return oldIDFunction
 
766
 
 
767
 
 
768
# A value about twice as large as any Python int, to which negative values
 
769
# from id() will be added, moving them into a range which should begin just
 
770
# above where positive values from id() leave off.
 
771
_HUGEINT = (sys.maxint + 1L) * 2L
 
772
def unsignedID(obj):
 
773
    """
 
774
    Return the id of an object as an unsigned number so that its hex
 
775
    representation makes sense.
 
776
 
 
777
    This is mostly necessary in Python 2.4 which implements L{id} to sometimes
 
778
    return a negative value.  Python 2.3 shares this behavior, but also
 
779
    implements hex and the %x format specifier to represent negative values as
 
780
    though they were positive ones, obscuring the behavior of L{id}.  Python
 
781
    2.5's implementation of L{id} always returns positive values.
 
782
    """
 
783
    rval = _idFunction(obj)
 
784
    if rval < 0:
 
785
        rval += _HUGEINT
 
786
    return rval
 
787
 
 
788
 
 
789
def mergeFunctionMetadata(f, g):
 
790
    """
 
791
    Overwrite C{g}'s name and docstring with values from C{f}.  Update
 
792
    C{g}'s instance dictionary with C{f}'s.
 
793
 
 
794
    To use this function safely you must use the return value. In Python 2.3,
 
795
    L{mergeFunctionMetadata} will create a new function. In later versions of
 
796
    Python, C{g} will be mutated and returned.
 
797
 
 
798
    @return: A function that has C{g}'s behavior and metadata merged from
 
799
        C{f}.
 
800
    """
 
801
    try:
 
802
        g.__name__ = f.__name__
 
803
    except TypeError:
 
804
        try:
 
805
            merged = new.function(
 
806
                g.func_code, g.func_globals,
 
807
                f.__name__, inspect.getargspec(g)[-1],
 
808
                g.func_closure)
 
809
        except TypeError:
 
810
            pass
 
811
    else:
 
812
        merged = g
 
813
    try:
 
814
        merged.__doc__ = f.__doc__
 
815
    except (TypeError, AttributeError):
 
816
        pass
 
817
    try:
 
818
        merged.__dict__.update(g.__dict__)
 
819
        merged.__dict__.update(f.__dict__)
 
820
    except (TypeError, AttributeError):
 
821
        pass
 
822
    merged.__module__ = f.__module__
 
823
    return merged
 
824
 
 
825
 
 
826
def nameToLabel(mname):
 
827
    """
 
828
    Convert a string like a variable name into a slightly more human-friendly
 
829
    string with spaces and capitalized letters.
 
830
 
 
831
    @type mname: C{str}
 
832
    @param mname: The name to convert to a label.  This must be a string
 
833
    which could be used as a Python identifier.  Strings which do not take
 
834
    this form will result in unpredictable behavior.
 
835
 
 
836
    @rtype: C{str}
 
837
    """
 
838
    labelList = []
 
839
    word = ''
 
840
    lastWasUpper = False
 
841
    for letter in mname:
 
842
        if letter.isupper() == lastWasUpper:
 
843
            # Continuing a word.
 
844
            word += letter
 
845
        else:
 
846
            # breaking a word OR beginning a word
 
847
            if lastWasUpper:
 
848
                # could be either
 
849
                if len(word) == 1:
 
850
                    # keep going
 
851
                    word += letter
 
852
                else:
 
853
                    # acronym
 
854
                    # we're processing the lowercase letter after the acronym-then-capital
 
855
                    lastWord = word[:-1]
 
856
                    firstLetter = word[-1]
 
857
                    labelList.append(lastWord)
 
858
                    word = firstLetter + letter
 
859
            else:
 
860
                # definitely breaking: lower to upper
 
861
                labelList.append(word)
 
862
                word = letter
 
863
        lastWasUpper = letter.isupper()
 
864
    if labelList:
 
865
        labelList[0] = labelList[0].capitalize()
 
866
    else:
 
867
        return mname.capitalize()
 
868
    labelList.append(word)
 
869
    return ' '.join(labelList)
 
870
 
 
871
 
 
872
 
 
873
def uidFromString(uidString):
 
874
    """
 
875
    Convert a user identifier, as a string, into an integer UID.
 
876
 
 
877
    @type uid: C{str}
 
878
    @param uid: A string giving the base-ten representation of a UID or the
 
879
        name of a user which can be converted to a UID via L{pwd.getpwnam}.
 
880
 
 
881
    @rtype: C{int}
 
882
    @return: The integer UID corresponding to the given string.
 
883
 
 
884
    @raise ValueError: If the user name is supplied and L{pwd} is not
 
885
        available.
 
886
    """
 
887
    try:
 
888
        return int(uidString)
 
889
    except ValueError:
 
890
        if pwd is None:
 
891
            raise
 
892
        return pwd.getpwnam(uidString)[2]
 
893
 
 
894
 
 
895
 
 
896
def gidFromString(gidString):
 
897
    """
 
898
    Convert a group identifier, as a string, into an integer GID.
 
899
 
 
900
    @type uid: C{str}
 
901
    @param uid: A string giving the base-ten representation of a GID or the
 
902
        name of a group which can be converted to a GID via L{grp.getgrnam}.
 
903
 
 
904
    @rtype: C{int}
 
905
    @return: The integer GID corresponding to the given string.
 
906
 
 
907
    @raise ValueError: If the group name is supplied and L{grp} is not
 
908
        available.
 
909
    """
 
910
    try:
 
911
        return int(gidString)
 
912
    except ValueError:
 
913
        if grp is None:
 
914
            raise
 
915
        return grp.getgrnam(gidString)[2]
 
916
 
 
917
 
 
918
 
 
919
def runAsEffectiveUser(euid, egid, function, *args, **kwargs):
 
920
    """
 
921
    Run the given function wrapped with seteuid/setegid calls.
 
922
 
 
923
    This will try to minimize the number of seteuid/setegid calls, comparing
 
924
    current and wanted permissions
 
925
 
 
926
    @param euid: effective UID used to call the function.
 
927
    @type euid: C{int}
 
928
 
 
929
    @type egid: effective GID used to call the function.
 
930
    @param egid: C{int}
 
931
 
 
932
    @param function: the function run with the specific permission.
 
933
    @type function: any callable
 
934
 
 
935
    @param *args: arguments passed to C{function}
 
936
    @param **kwargs: keyword arguments passed to C{function}
 
937
    """
 
938
    uid, gid = os.geteuid(), os.getegid()
 
939
    if uid == euid and gid == egid:
 
940
        return function(*args, **kwargs)
 
941
    else:
 
942
        if uid != 0 and (uid != euid or gid != egid):
 
943
            os.seteuid(0)
 
944
        if gid != egid:
 
945
            os.setegid(egid)
 
946
        if euid != 0 and (euid != uid or gid != egid):
 
947
            os.seteuid(euid)
 
948
        try:
 
949
            return function(*args, **kwargs)
 
950
        finally:
 
951
            if euid != 0 and (uid != euid or gid != egid):
 
952
                os.seteuid(0)
 
953
            if gid != egid:
 
954
                os.setegid(gid)
 
955
            if uid != 0 and (uid != euid or gid != egid):
 
956
                os.seteuid(uid)
 
957
 
 
958
 
 
959
 
 
960
__all__ = [
 
961
    "uniquify", "padTo", "getPluginDirs", "addPluginDir", "sibpath",
 
962
    "getPassword", "dict", "println", "keyed_md5", "makeStatBar",
 
963
    "OrderedDict", "InsensitiveDict", "spewer", "searchupwards", "LineLog",
 
964
    "raises", "IntervalDifferential", "FancyStrMixin", "FancyEqMixin",
 
965
    "dsu", "switchUID", "SubclassableCStringIO", "moduleMovedForSplit",
 
966
    "unsignedID", "mergeFunctionMetadata", "nameToLabel", "uidFromString",
 
967
    "gidFromString", "runAsEffectiveUser", "moduleMovedForSplit",
 
968
]