summaryrefslogtreecommitdiff
path: root/passlib/utils/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'passlib/utils/__init__.py')
-rw-r--r--passlib/utils/__init__.py185
1 files changed, 69 insertions, 116 deletions
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index 6147886..94833ad 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -61,11 +61,10 @@ from passlib.utils.decor import (
hybrid_method,
)
from passlib.exc import ExpectedStringError, ExpectedTypeError
-from passlib.utils.compat import (add_doc, join_bytes, join_byte_values,
- join_byte_elems, irange, imap, PY3, u,
- join_unicode, unicode, byte_elem_value, nextgetter,
- unicode_or_str, unicode_or_bytes_types,
- get_method_function, suppress_cause, PYPY)
+from passlib.utils.compat import (add_doc, join_bytes,
+ join_unicode,
+ unicode_or_bytes,
+ get_method_function, PYPY)
# local
__all__ = [
# constants
@@ -113,7 +112,7 @@ __all__ = [
#=============================================================================
# bitsize of system architecture (32 or 64)
-sys_bits = int(math.log(sys.maxsize if PY3 else sys.maxint, 2) + 1.5)
+sys_bits = int(math.log(sys.maxsize, 2) + 1.5)
# list of hashes algs supported by crypt() on at least one OS.
# XXX: move to .registry for passlib 2.0?
@@ -128,13 +127,10 @@ unix_crypt_schemes = [
# list of rounds_cost constants
rounds_cost_values = [ "linear", "log2" ]
-# legacy import, will be removed in 1.8
-from passlib.exc import MissingBackendError
-
# internal helpers
_BEMPTY = b''
-_UEMPTY = u("")
-_USPACE = u(" ")
+_UEMPTY = u""
+_USPACE = u" "
# maximum password size which passlib will allow; see exc.PasswordSizeError
MAX_PASSWORD_SIZE = int(os.environ.get("PASSLIB_MAX_PASSWORD_SIZE") or 4096)
@@ -169,30 +165,23 @@ class SequenceMixin(object):
def __ne__(self, other):
return not self.__eq__(other)
-if PY3:
- # getargspec() is deprecated, use this under py3.
- # even though it's a lot more awkward to get basic info :|
-
- _VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD
- _VAR_ANY_SET = set([_VAR_KEYWORD, inspect.Parameter.VAR_POSITIONAL])
+# getargspec() is deprecated, use this under py3.
+# even though it's a lot more awkward to get basic info :|
- def accepts_keyword(func, key):
- """test if function accepts specified keyword"""
- params = inspect.signature(get_method_function(func)).parameters
- if not params:
- return False
- arg = params.get(key)
- if arg and arg.kind not in _VAR_ANY_SET:
- return True
- # XXX: annoying what we have to do to determine if VAR_KWDS in use.
- return params[list(params)[-1]].kind == _VAR_KEYWORD
+_VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD
+_VAR_ANY_SET = {_VAR_KEYWORD, inspect.Parameter.VAR_POSITIONAL}
-else:
+def accepts_keyword(func, key):
+ """test if function accepts specified keyword"""
+ params = inspect.signature(get_method_function(func)).parameters
+ if not params:
+ return False
+ arg = params.get(key)
+ if arg and arg.kind not in _VAR_ANY_SET:
+ return True
+ # XXX: annoying what we have to do to determine if VAR_KWDS in use.
+ return params[list(params)[-1]].kind == _VAR_KEYWORD
- def accepts_keyword(func, key):
- """test if function accepts specified keyword"""
- spec = inspect.getargspec(get_method_function(func))
- return key in spec.args or spec.keywords is not None
def update_mixin_classes(target, add=None, remove=None, append=False,
before=None, after=None, dryrun=False):
@@ -333,16 +322,16 @@ def consteq(left, right):
# http://bugs.python.org/issue14955
# validate types
- if isinstance(left, unicode):
- if not isinstance(right, unicode):
- raise TypeError("inputs must be both unicode or both bytes")
- is_py3_bytes = False
+ if isinstance(left, str):
+ if not isinstance(right, str):
+ raise TypeError("inputs must be both str or both bytes")
+ is_bytes = False
elif isinstance(left, bytes):
if not isinstance(right, bytes):
- raise TypeError("inputs must be both unicode or both bytes")
- is_py3_bytes = PY3
+ raise TypeError("inputs must be both str or both bytes")
+ is_bytes = True
else:
- raise TypeError("inputs must be both unicode or both bytes")
+ raise TypeError("inputs must be both str or both bytes")
# do size comparison.
# NOTE: the double-if construction below is done deliberately, to ensure
@@ -361,8 +350,7 @@ def consteq(left, right):
result = 1
# run constant-time string comparision
- # TODO: use izip instead (but first verify it's faster than zip for this case)
- if is_py3_bytes:
+ if is_bytes:
for l,r in zip(tmp, right):
result |= l ^ r
else:
@@ -442,8 +430,8 @@ def saslprep(source, param="value"):
# validate type
# XXX: support bytes (e.g. run through want_unicode)?
# might be easier to just integrate this into cryptcontext.
- if not isinstance(source, unicode):
- raise TypeError("input must be unicode string, not %s" %
+ if not isinstance(source, str):
+ raise TypeError("input must be string, not %s" %
(type(source),))
# mapping stage
@@ -557,19 +545,11 @@ def render_bytes(source, *args):
else arg for arg in args)
return result.encode("latin-1")
-if PY3:
- # new in py32
- def bytes_to_int(value):
- return int.from_bytes(value, 'big')
- def int_to_bytes(value, count):
- return value.to_bytes(count, 'big')
-else:
- # XXX: can any of these be sped up?
- from binascii import hexlify, unhexlify
- def bytes_to_int(value):
- return int(hexlify(value),16)
- def int_to_bytes(value, count):
- return unhexlify(('%%0%dx' % (count<<1)) % value)
+def bytes_to_int(value):
+ return int.from_bytes(value, 'big')
+
+def int_to_bytes(value, count):
+ return value.to_bytes(count, 'big')
add_doc(bytes_to_int, "decode byte string as single big-endian integer")
add_doc(int_to_bytes, "encode integer as single big-endian byte string")
@@ -595,14 +575,14 @@ def utf8_repeat_string(source, size):
_BNULL = b"\x00"
-_UNULL = u("\x00")
+_UNULL = u"\x00"
def right_pad_string(source, size, pad=None):
"""right-pad or truncate <source> string, so it has length <size>"""
cur = len(source)
if size > cur:
if pad is None:
- pad = _UNULL if isinstance(source, unicode) else _BNULL
+ pad = _UNULL if isinstance(source, str) else _BNULL
return source+pad*(size-cur)
else:
return source[:size]
@@ -648,7 +628,7 @@ def utf8_truncate(source, index):
# loop until we find non-continuation byte
while index < end:
- if byte_elem_value(source[index]) & 0xC0 != 0x80:
+ if source[index] & 0xC0 != 0x80:
# found single-char byte, or start-char byte.
break
# else: found continuation byte.
@@ -694,7 +674,7 @@ def is_same_codec(left, right):
return _lookup_codec(left).name == _lookup_codec(right).name
_B80 = b'\x80'[0]
-_U80 = u('\x80')
+_U80 = u'\x80'
def is_ascii_safe(source):
"""Check if string (bytes or unicode) contains only 7-bit ascii"""
r = _B80 if isinstance(source, bytes) else _U80
@@ -717,7 +697,7 @@ def to_bytes(source, encoding="utf-8", param="value", source_encoding=None):
the source will be transcoded from *source_encoding* to *encoding*
(via unicode).
- :raises TypeError: if source is not unicode or bytes.
+ :raises TypeError: if source is not str or bytes.
:returns:
* unicode strings will be encoded using *encoding*, and returned.
@@ -732,7 +712,7 @@ def to_bytes(source, encoding="utf-8", param="value", source_encoding=None):
return source.decode(source_encoding).encode(encoding)
else:
return source
- elif isinstance(source, unicode):
+ elif isinstance(source, str):
return source.encode(encoding)
else:
raise ExpectedStringError(source, param)
@@ -749,42 +729,34 @@ def to_unicode(source, encoding="utf-8", param="value"):
:param param:
optional name of variable/noun to reference when raising errors.
- :raises TypeError: if source is not unicode or bytes.
+ :raises TypeError: if source is not str or bytes.
:returns:
* returns unicode strings unchanged.
* returns bytes strings decoded using *encoding*
"""
assert encoding
- if isinstance(source, unicode):
+ if isinstance(source, str):
return source
elif isinstance(source, bytes):
return source.decode(encoding)
else:
raise ExpectedStringError(source, param)
-if PY3:
- def to_native_str(source, encoding="utf-8", param="value"):
- if isinstance(source, bytes):
- return source.decode(encoding)
- elif isinstance(source, unicode):
- return source
- else:
- raise ExpectedStringError(source, param)
-else:
- def to_native_str(source, encoding="utf-8", param="value"):
- if isinstance(source, bytes):
- return source
- elif isinstance(source, unicode):
- return source.encode(encoding)
- else:
- raise ExpectedStringError(source, param)
+
+def to_native_str(source, encoding="utf-8", param="value"):
+ if isinstance(source, bytes):
+ return source.decode(encoding)
+ elif isinstance(source, str):
+ return source
+ else:
+ raise ExpectedStringError(source, param)
+
add_doc(to_native_str,
- """Take in unicode or bytes, return native string.
+ """Take in str or bytes, returns str.
- Python 2: encodes unicode using specified encoding, leaves bytes alone.
- Python 3: leaves unicode alone, decodes bytes using specified encoding.
+ leaves str alone, decodes bytes using specified encoding.
:raises TypeError: if source is not unicode or bytes.
@@ -816,7 +788,7 @@ def as_bool(value, none=None, param="boolean"):
recognizes strings such as "true", "false"
"""
assert none in [True, False, None]
- if isinstance(value, unicode_or_bytes_types):
+ if isinstance(value, unicode_or_bytes):
clean = value.lower().strip()
if clean in _true_set:
return True
@@ -840,7 +812,7 @@ def is_safe_crypt_input(value):
"""
UT helper --
test if value is safe to pass to crypt.crypt();
- under PY3, can't pass non-UTF8 bytes to crypt.crypt.
+ since PY3 won't let us pass non-UTF8 bytes to crypt.crypt.
"""
if crypt_accepts_bytes or not isinstance(value, bytes):
return True
@@ -883,9 +855,9 @@ else:
# returning NULL / None. examples include ":", ":0", "*0", etc.
# safe_crypt() returns None for any string starting with one of the
# chars in this string...
- _invalid_prefixes = u("*:!")
+ _invalid_prefixes = u"*:!"
- if PY3:
+ if True: # legacy block from PY3 compat
# * pypy3 (as of v7.3.1) has a crypt which accepts bytes, or ASCII-only unicode.
# * whereas CPython3 (as of v3.9) has a crypt which doesn't take bytes,
@@ -905,11 +877,11 @@ else:
if crypt_accepts_bytes:
# PyPy3 -- all bytes accepted, but unicode encoded to ASCII,
# so handling that ourselves.
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
if _BNULL in secret:
raise ValueError("null character in secret")
- if isinstance(hash, unicode):
+ if isinstance(hash, str):
hash = hash.encode("ascii")
else:
# CPython3's crypt() doesn't take bytes, only unicode; unicode which is then
@@ -945,27 +917,7 @@ else:
if not result or result[0] in _invalid_prefixes:
return None
return result
- else:
- #: see feature-detection in PY3 fork above
- crypt_accepts_bytes = True
-
- # Python 2 crypt handler
- def safe_crypt(secret, hash):
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- if _NULL in secret:
- raise ValueError("null character in secret")
- if isinstance(hash, unicode):
- hash = hash.encode("ascii")
- with _safe_crypt_lock:
- result = _crypt(secret, hash)
- if not result:
- return None
- result = result.decode("ascii")
- if result[0] in _invalid_prefixes:
- return None
- return result
add_doc(safe_crypt, """Wrapper around stdlib's crypt.
@@ -1006,9 +958,9 @@ def test_crypt(secret, hash):
# safe_crypt() always returns unicode, which means that for py3,
# 'hash' can't be bytes, or "== hash" will never be True.
# under py2 unicode & str(bytes) will compare fine;
- # so just enforcing "unicode_or_str" limitation
- assert isinstance(hash, unicode_or_str), \
- "hash must be unicode_or_str, got %s" % type(hash)
+ # so just enforcing "str" limitation
+ assert isinstance(hash, str), \
+ "hash must be str, got %s" % type(hash)
assert hash, "hash must be non-empty"
return safe_crypt(secret, hash) == hash
@@ -1055,7 +1007,7 @@ def genseed(value=None):
# this method throws error for e.g. SystemRandom instances,
# so fall back to extracting 4k of state
value = value.getrandbits(1 << 15)
- text = u("%s %s %s %.15f %.15f %s") % (
+ text = u"%s %s %s %.15f %.15f %s" % (
# if caller specified a seed value, mix it in
value,
@@ -1106,7 +1058,8 @@ def getrandbytes(rng, count):
yield value & 0xff
value >>= 3
i += 1
- return join_byte_values(helper())
+ return bytes(helper())
+
def getrandstr(rng, charset, count):
"""return string containing *count* number of chars/bytes, whose elements are drawn from specified charset, using specified rng"""
@@ -1132,10 +1085,10 @@ def getrandstr(rng, charset, count):
value //= letters
i += 1
- if isinstance(charset, unicode):
+ if isinstance(charset, str):
return join_unicode(helper())
else:
- return join_byte_elems(helper())
+ return bytes(helper())
_52charset = '2346789ABCDEFGHJKMNPQRTUVWXYZabcdefghjkmnpqrstuvwxyz'