1 """passlib.utils.compat - python 2/3 compatibility helpers"""
2
3
4
5
6
7
8
9
10
11
12 import sys
13 PY2 = sys.version_info < (3,0)
14 PY3 = sys.version_info >= (3,0)
15 PY_MAX_25 = sys.version_info < (2,6)
16 PY27 = sys.version_info[:2] == (2,7)
17 PY_MIN_32 = sys.version_info >= (3,2)
18
19
20
21
22 PYPY = hasattr(sys, "pypy_version_info")
23 JYTHON = sys.platform.startswith('java')
24
25
26
27
28
29
30 SUPPORTS_DIR_METHOD = not PY_MAX_25 and not (PYPY and sys.pypy_version_info < (1,6))
31
32
33
34
35 import logging; log = logging.getLogger(__name__)
36 if PY3:
37 import builtins
38 else:
39 import __builtin__ as builtins
42 """add docstring to an object"""
43 obj.__doc__ = doc
44
45
46
47
48 __all__ = [
49
50 'PY2', 'PY3', 'PY_MAX_25', 'PY27', 'PY_MIN_32',
51
52
53 'BytesIO', 'StringIO', 'NativeStringIO', 'SafeConfigParser',
54 'print_',
55
56
57
58 'callable',
59 'int_types',
60 'num_types',
61 'base_string_types',
62
63
64 'u', 'b',
65 'unicode', 'bytes',
66 'uascii_to_str', 'bascii_to_str',
67 'str_to_uascii', 'str_to_bascii',
68 'join_unicode', 'join_bytes',
69 'join_byte_values', 'join_byte_elems',
70 'byte_elem_value',
71 'iter_byte_values',
72
73
74 'irange',
75 'imap', 'lmap',
76 'iteritems', 'itervalues',
77 'next',
78
79
80 'exc_err', 'get_method_function', 'add_doc',
81 ]
82
83
84
85 _lazy_attrs = dict()
86
87
88
89
90 if PY3:
91 unicode = str
92 bytes = builtins.bytes
95 assert isinstance(s, str)
96 return s
97
99 assert isinstance(s, str)
100 return s.encode("latin-1")
101
102 base_string_types = (unicode, bytes)
103
104 else:
105 unicode = builtins.unicode
106 bytes = str if PY_MAX_25 else builtins.bytes
109 assert isinstance(s, str)
110 return s.decode("unicode_escape")
111
113 assert isinstance(s, str)
114 return s
115
116 base_string_types = basestring
117
118
119
120
121
122 join_unicode = u('').join
123
124
125 join_bytes = b('').join
126
127 if PY3:
129 assert isinstance(s, unicode)
130 return s
131
133 assert isinstance(s, bytes)
134 return s.decode("ascii")
135
137 assert isinstance(s, str)
138 return s
139
141 assert isinstance(s, str)
142 return s.encode("ascii")
143
144 join_byte_values = join_byte_elems = bytes
147 assert isinstance(elem, int)
148 return elem
149
151 assert isinstance(s, bytes)
152 return s
153
155 assert isinstance(s, bytes)
156
157 return (bytes([c]) for c in s)
158
159 else:
161 assert isinstance(s, unicode)
162 return s.encode("ascii")
163
165 assert isinstance(s, bytes)
166 return s
167
169 assert isinstance(s, str)
170 return s.decode("ascii")
171
173 assert isinstance(s, str)
174 return s
175
178
179 join_byte_elems = join_bytes
180
181 byte_elem_value = ord
184 assert isinstance(s, bytes)
185 return (ord(c) for c in s)
186
188 assert isinstance(s, bytes)
189 return s
190
191 add_doc(uascii_to_str, "helper to convert ascii unicode -> native str")
192 add_doc(bascii_to_str, "helper to convert ascii bytes -> native str")
193 add_doc(str_to_uascii, "helper to convert ascii native str -> unicode")
194 add_doc(str_to_bascii, "helper to convert ascii native str -> bytes")
195
196
197
198
199
200
201
202
203
204 add_doc(iter_byte_values, "iterate over byte string as sequence of ints 0-255")
205 add_doc(iter_byte_chars, "iterate over byte string as sequence of 1-byte strings")
206
207
208
209
210 if PY3:
211 int_types = (int,)
212 num_types = (int, float)
213 else:
214 int_types = (int, long)
215 num_types = (int, long, float)
216
217
218
219
220
221
222
223
224
225
226 if PY3:
227 irange = range
228
229
230
231 - def lmap(*a, **k):
232 return list(map(*a,**k))
233 imap = map
239
240 next_method_attr = "__next__"
241
242 else:
243 irange = xrange
244
245
246 lmap = map
247 from itertools import imap
253
254 next_method_attr = "next"
255
256 if PY_MAX_25:
257 _undef = object()
259 "compat wrapper for next()"
260 if default is _undef:
261 return itr.next()
262 try:
263 return itr.next()
264 except StopIteration:
265 return default
266 else:
267 next = builtins.next
268
269
270
271
272
273
274
275
276 if (3,0) <= sys.version_info < (3,2):
277
278 from collections import Callable
280 return isinstance(obj, Callable)
281 else:
282 callable = builtins.callable
288 "return current error object (to avoid try/except syntax change)"
289 return sys.exc_info()[1]
290
291 if PY3:
292 method_function_attr = "__func__"
293 else:
294 method_function_attr = "im_func"
297 "given (potential) method, return underlying function"
298 return getattr(func, method_function_attr, func)
299
300
301
302
303 if PY3:
304 _lazy_attrs = dict(
305 BytesIO="io.BytesIO",
306 UnicodeIO="io.StringIO",
307 NativeStringIO="io.StringIO",
308 SafeConfigParser="configparser.SafeConfigParser",
309 )
310 if sys.version_info >= (3,2):
311
312 _lazy_attrs["SafeConfigParser"] = "configparser.ConfigParser"
313
314 print_ = getattr(builtins, "print")
315
316 else:
317 _lazy_attrs = dict(
318 BytesIO="cStringIO.StringIO",
319 UnicodeIO="StringIO.StringIO",
320 NativeStringIO="cStringIO.StringIO",
321 SafeConfigParser="ConfigParser.SafeConfigParser",
322 )
323
324 - def print_(*args, **kwds):
325 """The new-style print function."""
326
327 fp = kwds.pop("file", sys.stdout)
328 sep = kwds.pop("sep", None)
329 end = kwds.pop("end", None)
330 if kwds:
331 raise TypeError("invalid keyword arguments")
332
333
334 if fp is None:
335 return
336
337
338 want_unicode = isinstance(sep, unicode) or isinstance(end, unicode) or \
339 any(isinstance(arg, unicode) for arg in args)
340
341
342 if end is None:
343 end = u("\n") if want_unicode else "\n"
344 elif not isinstance(end, base_string_types):
345 raise TypeError("end must be None or a string")
346
347
348 if sep is None:
349 sep = u(" ") if want_unicode else " "
350 elif not isinstance(sep, base_string_types):
351 raise TypeError("sep must be None or a string")
352
353
354 first = True
355 write = fp.write
356 for arg in args:
357 if first:
358 first = False
359 else:
360 write(sep)
361 if not isinstance(arg, basestring):
362 arg = str(arg)
363 write(arg)
364 write(end)
365
366
367
368
369 from types import ModuleType
372 "helper to import object from module; accept format `path.to.object`"
373 modname, modattr = source.rsplit(".",1)
374 mod = __import__(modname, fromlist=[modattr], level=0)
375 return getattr(mod, modattr)
376
378 """proxy module which overlays original module,
379 and lazily imports specified attributes.
380
381 this is mainly used to prevent importing of resources
382 that are only needed by certain password hashes,
383 yet allow them to be imported from a single location.
384
385 used by :mod:`passlib.utils`, :mod:`passlib.utils.crypto`,
386 and :mod:`passlib.utils.compat`.
387 """
388
389 @classmethod
391 orig = sys.modules[name]
392 self = cls(name, attrmap, orig)
393 sys.modules[name] = self
394 return self
395
396 - def __init__(self, name, attrmap, proxy=None):
397 ModuleType.__init__(self, name)
398 self.__attrmap = attrmap
399 self.__proxy = proxy
400 self.__log = logging.getLogger(name)
401
417
424
433
434
435 _LazyOverlayModule.replace_module(__name__, _lazy_attrs)
436
437
438
439
440