~ubuntu-branches/ubuntu/vivid/moin/vivid

« back to all changes in this revision

Viewing changes to MoinMoin/support/passlib/utils/compat.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2014-01-07 21:33:21 UTC
  • mfrom: (0.1.34 sid)
  • Revision ID: package-import@ubuntu.com-20140107213321-574mr13z2oebjgms
Tags: 1.9.7-1ubuntu1
* Merge with Debian; remaining changes:
* debian/control:
  - remove python-xml from Suggests field, the package isn't in
    sys.path any more.
  - demote fckeditor from Recommends to Suggests; the code was previously
    embedded in moin, but it was also disabled, so there's no reason for us
    to pull this in by default currently. Note: fckeditor has a number of
    security problems and so this change probably needs to be carried
    indefinitely.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""passlib.utils.compat - python 2/3 compatibility helpers"""
 
2
#=============================================================================
 
3
# figure out what we're running
 
4
#=============================================================================
 
5
 
 
6
#------------------------------------------------------------------------
 
7
# python version
 
8
#------------------------------------------------------------------------
 
9
import sys
 
10
PY2 = sys.version_info < (3,0)
 
11
PY3 = sys.version_info >= (3,0)
 
12
PY_MAX_25 = sys.version_info < (2,6) # py 2.5 or earlier
 
13
PY27 = sys.version_info[:2] == (2,7) # supports last 2.x release
 
14
PY_MIN_32 = sys.version_info >= (3,2) # py 3.2 or later
 
15
 
 
16
#------------------------------------------------------------------------
 
17
# python implementation
 
18
#------------------------------------------------------------------------
 
19
PYPY = hasattr(sys, "pypy_version_info")
 
20
JYTHON = sys.platform.startswith('java')
 
21
 
 
22
#------------------------------------------------------------------------
 
23
# capabilities
 
24
#------------------------------------------------------------------------
 
25
 
 
26
# __dir__() added in py2.6
 
27
SUPPORTS_DIR_METHOD = not PY_MAX_25 and not (PYPY and sys.pypy_version_info < (1,6))
 
28
 
 
29
#=============================================================================
 
30
# common imports
 
31
#=============================================================================
 
32
import logging; log = logging.getLogger(__name__)
 
33
if PY3:
 
34
    import builtins
 
35
else:
 
36
    import __builtin__ as builtins
 
37
 
 
38
def add_doc(obj, doc):
 
39
    """add docstring to an object"""
 
40
    obj.__doc__ = doc
 
41
 
 
42
#=============================================================================
 
43
# the default exported vars
 
44
#=============================================================================
 
45
__all__ = [
 
46
    # python versions
 
47
    'PY2', 'PY3', 'PY_MAX_25', 'PY27', 'PY_MIN_32',
 
48
 
 
49
    # io
 
50
    'BytesIO', 'StringIO', 'NativeStringIO', 'SafeConfigParser',
 
51
    'print_',
 
52
 
 
53
    # type detection
 
54
##    'is_mapping',
 
55
    'callable',
 
56
    'int_types',
 
57
    'num_types',
 
58
    'base_string_types',
 
59
 
 
60
    # unicode/bytes types & helpers
 
61
    'u', 'b',
 
62
    'unicode', 'bytes',
 
63
    'uascii_to_str', 'bascii_to_str',
 
64
    'str_to_uascii', 'str_to_bascii',
 
65
    'join_unicode', 'join_bytes',
 
66
    'join_byte_values', 'join_byte_elems',
 
67
    'byte_elem_value',
 
68
    'iter_byte_values',
 
69
 
 
70
    # iteration helpers
 
71
    'irange', #'lrange',
 
72
    'imap', 'lmap',
 
73
    'iteritems', 'itervalues',
 
74
    'next',
 
75
 
 
76
    # introspection
 
77
    'exc_err', 'get_method_function', 'add_doc',
 
78
]
 
79
 
 
80
# begin accumulating mapping of lazy-loaded attrs,
 
81
# 'merged' into module at bottom
 
82
_lazy_attrs = dict()
 
83
 
 
84
#=============================================================================
 
85
# unicode & bytes types
 
86
#=============================================================================
 
87
if PY3:
 
88
    unicode = str
 
89
    bytes = builtins.bytes
 
90
 
 
91
    def u(s):
 
92
        assert isinstance(s, str)
 
93
        return s
 
94
 
 
95
    def b(s):
 
96
        assert isinstance(s, str)
 
97
        return s.encode("latin-1")
 
98
 
 
99
    base_string_types = (unicode, bytes)
 
100
 
 
101
else:
 
102
    unicode = builtins.unicode
 
103
    bytes = str if PY_MAX_25 else builtins.bytes
 
104
 
 
105
    def u(s):
 
106
        assert isinstance(s, str)
 
107
        return s.decode("unicode_escape")
 
108
 
 
109
    def b(s):
 
110
        assert isinstance(s, str)
 
111
        return s
 
112
 
 
113
    base_string_types = basestring
 
114
 
 
115
#=============================================================================
 
116
# unicode & bytes helpers
 
117
#=============================================================================
 
118
# function to join list of unicode strings
 
119
join_unicode = u('').join
 
120
 
 
121
# function to join list of byte strings
 
122
join_bytes = b('').join
 
123
 
 
124
if PY3:
 
125
    def uascii_to_str(s):
 
126
        assert isinstance(s, unicode)
 
127
        return s
 
128
 
 
129
    def bascii_to_str(s):
 
130
        assert isinstance(s, bytes)
 
131
        return s.decode("ascii")
 
132
 
 
133
    def str_to_uascii(s):
 
134
        assert isinstance(s, str)
 
135
        return s
 
136
 
 
137
    def str_to_bascii(s):
 
138
        assert isinstance(s, str)
 
139
        return s.encode("ascii")
 
140
 
 
141
    join_byte_values = join_byte_elems = bytes
 
142
 
 
143
    def byte_elem_value(elem):
 
144
        assert isinstance(elem, int)
 
145
        return elem
 
146
 
 
147
    def iter_byte_values(s):
 
148
        assert isinstance(s, bytes)
 
149
        return s
 
150
 
 
151
    def iter_byte_chars(s):
 
152
        assert isinstance(s, bytes)
 
153
        # FIXME: there has to be a better way to do this
 
154
        return (bytes([c]) for c in s)
 
155
 
 
156
else:
 
157
    def uascii_to_str(s):
 
158
        assert isinstance(s, unicode)
 
159
        return s.encode("ascii")
 
160
 
 
161
    def bascii_to_str(s):
 
162
        assert isinstance(s, bytes)
 
163
        return s
 
164
 
 
165
    def str_to_uascii(s):
 
166
        assert isinstance(s, str)
 
167
        return s.decode("ascii")
 
168
 
 
169
    def str_to_bascii(s):
 
170
        assert isinstance(s, str)
 
171
        return s
 
172
 
 
173
    def join_byte_values(values):
 
174
        return join_bytes(chr(v) for v in values)
 
175
 
 
176
    join_byte_elems = join_bytes
 
177
 
 
178
    byte_elem_value = ord
 
179
 
 
180
    def iter_byte_values(s):
 
181
        assert isinstance(s, bytes)
 
182
        return (ord(c) for c in s)
 
183
 
 
184
    def iter_byte_chars(s):
 
185
        assert isinstance(s, bytes)
 
186
        return s
 
187
 
 
188
add_doc(uascii_to_str, "helper to convert ascii unicode -> native str")
 
189
add_doc(bascii_to_str, "helper to convert ascii bytes -> native str")
 
190
add_doc(str_to_uascii, "helper to convert ascii native str -> unicode")
 
191
add_doc(str_to_bascii, "helper to convert ascii native str -> bytes")
 
192
 
 
193
# join_byte_values -- function to convert list of ordinal integers to byte string.
 
194
 
 
195
# join_byte_elems --  function to convert list of byte elements to byte string;
 
196
#                 i.e. what's returned by ``b('a')[0]``...
 
197
#                 this is b('a') under PY2, but 97 under PY3.
 
198
 
 
199
# byte_elem_value -- function to convert byte element to integer -- a noop under PY3
 
200
 
 
201
add_doc(iter_byte_values, "iterate over byte string as sequence of ints 0-255")
 
202
add_doc(iter_byte_chars, "iterate over byte string as sequence of 1-byte strings")
 
203
 
 
204
#=============================================================================
 
205
# numeric
 
206
#=============================================================================
 
207
if PY3:
 
208
    int_types = (int,)
 
209
    num_types = (int, float)
 
210
else:
 
211
    int_types = (int, long)
 
212
    num_types = (int, long, float)
 
213
 
 
214
#=============================================================================
 
215
# iteration helpers
 
216
#
 
217
# irange - range iterable / view (xrange under py2, range under py3)
 
218
# lrange - range list (range under py2, list(range()) under py3)
 
219
#
 
220
# imap - map to iterator
 
221
# lmap - map to list
 
222
#=============================================================================
 
223
if PY3:
 
224
    irange = range
 
225
    ##def lrange(*a,**k):
 
226
    ##    return list(range(*a,**k))
 
227
 
 
228
    def lmap(*a, **k):
 
229
        return list(map(*a,**k))
 
230
    imap = map
 
231
 
 
232
    def iteritems(d):
 
233
        return d.items()
 
234
    def itervalues(d):
 
235
        return d.values()
 
236
 
 
237
    next_method_attr = "__next__"
 
238
 
 
239
else:
 
240
    irange = xrange
 
241
    ##lrange = range
 
242
 
 
243
    lmap = map
 
244
    from itertools import imap
 
245
 
 
246
    def iteritems(d):
 
247
        return d.iteritems()
 
248
    def itervalues(d):
 
249
        return d.itervalues()
 
250
 
 
251
    next_method_attr = "next"
 
252
 
 
253
if PY_MAX_25:
 
254
    _undef = object()
 
255
    def next(itr, default=_undef):
 
256
        "compat wrapper for next()"
 
257
        if default is _undef:
 
258
            return itr.next()
 
259
        try:
 
260
            return itr.next()
 
261
        except StopIteration:
 
262
            return default
 
263
else:
 
264
    next = builtins.next
 
265
 
 
266
#=============================================================================
 
267
# typing
 
268
#=============================================================================
 
269
##def is_mapping(obj):
 
270
##    # non-exhaustive check, enough to distinguish from lists, etc
 
271
##    return hasattr(obj, "items")
 
272
 
 
273
if (3,0) <= sys.version_info < (3,2):
 
274
    # callable isn't dead, it's just resting
 
275
    from collections import Callable
 
276
    def callable(obj):
 
277
        return isinstance(obj, Callable)
 
278
else:
 
279
    callable = builtins.callable
 
280
 
 
281
#=============================================================================
 
282
# introspection
 
283
#=============================================================================
 
284
def exc_err():
 
285
    "return current error object (to avoid try/except syntax change)"
 
286
    return sys.exc_info()[1]
 
287
 
 
288
if PY3:
 
289
    method_function_attr = "__func__"
 
290
else:
 
291
    method_function_attr = "im_func"
 
292
 
 
293
def get_method_function(func):
 
294
    "given (potential) method, return underlying function"
 
295
    return getattr(func, method_function_attr, func)
 
296
 
 
297
#=============================================================================
 
298
# input/output
 
299
#=============================================================================
 
300
if PY3:
 
301
    _lazy_attrs = dict(
 
302
        BytesIO="io.BytesIO",
 
303
        UnicodeIO="io.StringIO",
 
304
        NativeStringIO="io.StringIO",
 
305
        SafeConfigParser="configparser.SafeConfigParser",
 
306
    )
 
307
    if sys.version_info >= (3,2):
 
308
        # py32 renamed this, removing old ConfigParser
 
309
        _lazy_attrs["SafeConfigParser"] = "configparser.ConfigParser"
 
310
 
 
311
    print_ = getattr(builtins, "print")
 
312
 
 
313
else:
 
314
    _lazy_attrs = dict(
 
315
        BytesIO="cStringIO.StringIO",
 
316
        UnicodeIO="StringIO.StringIO",
 
317
        NativeStringIO="cStringIO.StringIO",
 
318
        SafeConfigParser="ConfigParser.SafeConfigParser",
 
319
    )
 
320
 
 
321
    def print_(*args, **kwds):
 
322
        """The new-style print function."""
 
323
        # extract kwd args
 
324
        fp = kwds.pop("file", sys.stdout)
 
325
        sep = kwds.pop("sep", None)
 
326
        end = kwds.pop("end", None)
 
327
        if kwds:
 
328
            raise TypeError("invalid keyword arguments")
 
329
 
 
330
        # short-circuit if no target
 
331
        if fp is None:
 
332
            return
 
333
 
 
334
        # use unicode or bytes ?
 
335
        want_unicode = isinstance(sep, unicode) or isinstance(end, unicode) or \
 
336
                       any(isinstance(arg, unicode) for arg in args)
 
337
 
 
338
        # pick default end sequence
 
339
        if end is None:
 
340
            end = u("\n") if want_unicode else "\n"
 
341
        elif not isinstance(end, base_string_types):
 
342
            raise TypeError("end must be None or a string")
 
343
 
 
344
        # pick default separator
 
345
        if sep is None:
 
346
            sep = u(" ") if want_unicode else " "
 
347
        elif not isinstance(sep, base_string_types):
 
348
            raise TypeError("sep must be None or a string")
 
349
 
 
350
        # write to buffer
 
351
        first = True
 
352
        write = fp.write
 
353
        for arg in args:
 
354
            if first:
 
355
                first = False
 
356
            else:
 
357
                write(sep)
 
358
            if not isinstance(arg, basestring):
 
359
                arg = str(arg)
 
360
            write(arg)
 
361
        write(end)
 
362
 
 
363
#=============================================================================
 
364
# lazy overlay module
 
365
#=============================================================================
 
366
from types import ModuleType
 
367
 
 
368
def _import_object(source):
 
369
    "helper to import object from module; accept format `path.to.object`"
 
370
    modname, modattr = source.rsplit(".",1)
 
371
    mod = __import__(modname, fromlist=[modattr], level=0)
 
372
    return getattr(mod, modattr)
 
373
 
 
374
class _LazyOverlayModule(ModuleType):
 
375
    """proxy module which overlays original module,
 
376
    and lazily imports specified attributes.
 
377
 
 
378
    this is mainly used to prevent importing of resources
 
379
    that are only needed by certain password hashes,
 
380
    yet allow them to be imported from a single location.
 
381
 
 
382
    used by :mod:`passlib.utils`, :mod:`passlib.utils.crypto`,
 
383
    and :mod:`passlib.utils.compat`.
 
384
    """
 
385
 
 
386
    @classmethod
 
387
    def replace_module(cls, name, attrmap):
 
388
        orig = sys.modules[name]
 
389
        self = cls(name, attrmap, orig)
 
390
        sys.modules[name] = self
 
391
        return self
 
392
 
 
393
    def __init__(self, name, attrmap, proxy=None):
 
394
        ModuleType.__init__(self, name)
 
395
        self.__attrmap = attrmap
 
396
        self.__proxy = proxy
 
397
        self.__log = logging.getLogger(name)
 
398
 
 
399
    def __getattr__(self, attr):
 
400
        proxy = self.__proxy
 
401
        if proxy and hasattr(proxy, attr):
 
402
            return getattr(proxy, attr)
 
403
        attrmap = self.__attrmap
 
404
        if attr in attrmap:
 
405
            source = attrmap[attr]
 
406
            if callable(source):
 
407
                value = source()
 
408
            else:
 
409
                value = _import_object(source)
 
410
            setattr(self, attr, value)
 
411
            self.__log.debug("loaded lazy attr %r: %r", attr, value)
 
412
            return value
 
413
        raise AttributeError("'module' object has no attribute '%s'" % (attr,))
 
414
 
 
415
    def __repr__(self):
 
416
        proxy = self.__proxy
 
417
        if proxy:
 
418
            return repr(proxy)
 
419
        else:
 
420
            return ModuleType.__repr__(self)
 
421
 
 
422
    def __dir__(self):
 
423
        attrs = set(dir(self.__class__))
 
424
        attrs.update(self.__dict__)
 
425
        attrs.update(self.__attrmap)
 
426
        proxy = self.__proxy
 
427
        if proxy is not None:
 
428
            attrs.update(dir(proxy))
 
429
        return list(attrs)
 
430
 
 
431
# replace this module with overlay that will lazily import attributes.
 
432
_LazyOverlayModule.replace_module(__name__, _lazy_attrs)
 
433
 
 
434
#=============================================================================
 
435
# eof
 
436
#=============================================================================