diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2012-04-17 23:14:51 -0400 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2012-04-17 23:14:51 -0400 |
commit | 64ab6fc89b497efa9169f11d55251e417c4db0ba (patch) | |
tree | b3f6f5dc27b87a6bc90cb3686fa98239ee8ff053 /passlib/utils/__init__.py | |
parent | 8eb4c4d3b58eec6802c698ddbf357b2fd243a68c (diff) | |
parent | cd029846fdc0c3d7ffc7f53caad4579e7e0e8725 (diff) | |
download | passlib-ironpython-support-dev.tar.gz |
Merge from defaultironpython-support-dev
Diffstat (limited to 'passlib/utils/__init__.py')
-rw-r--r-- | passlib/utils/__init__.py | 183 |
1 files changed, 106 insertions, 77 deletions
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py index 8eecec3..4ba4be8 100644 --- a/passlib/utils/__init__.py +++ b/passlib/utils/__init__.py @@ -2,6 +2,7 @@ #============================================================================= #imports #============================================================================= +from passlib.utils.compat import PYPY, JYTHON, IRONPYTHON #core from base64 import b64encode, b64decode from codecs import lookup as _lookup_codec @@ -11,17 +12,24 @@ import math import os import sys import random -from passlib.utils.compat import PYPY, IRONPYTHON, JYTHON -_ipy_missing_stringprep = False -if IRONPYTHON: +if JYTHON or IRONPYTHON: + # Jython 2.5.2 lacks stringprep module - + # see http://bugs.jython.org/issue1758320 + # + # IronPython also lacks stringprep module try: import stringprep except ImportError: - _ipy_missing_stringprep = True + stringprep = None + if JYTHON: + _stringprep_missing_reason = "not present under Jython" + else: + _stringprep_missing_reason = "not present under IronPython" else: import stringprep import time -import unicodedata +if stringprep: + import unicodedata from warnings import warn #site #pkg @@ -87,10 +95,6 @@ __all__ = [ # constants #================================================================================= -# Python VM identification -PYPY = hasattr(sys, "pypy_version_info") -JYTHON = sys.platform.startswith('java') - # bitsize of system architecture (32 or 64) sys_bits = int(math.log(sys.maxsize if PY3 else sys.maxint, 2) + 1.5) @@ -134,30 +138,46 @@ class classproperty(object): "py3 compatible alias" return self.im_func -def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True): +def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True, + replacement=None, _is_method=False): """decorator to deprecate a function. :arg msg: optional msg, default chosen if omitted :kwd deprecated: release where function was first deprecated :kwd removed: release where function will be removed + :kwd replacement: name/instructions for replacement function. :kwd updoc: add notice to docstring (default ``True``) """ if msg is None: - msg = "the function %(mod)s.%(name)s() is deprecated" + if _is_method: + msg = "the method %(mod)s.%(klass)s.%(name)s() is deprecated" + else: + msg = "the function %(mod)s.%(name)s() is deprecated" if deprecated: msg += " as of Passlib %(deprecated)s" if removed: msg += ", and will be removed in Passlib %(removed)s" + if replacement: + msg += ", use %s instead" % replacement msg += "." def build(func): - final = msg % dict( + kwds = dict( mod=func.__module__, name=func.__name__, deprecated=deprecated, removed=removed, - ) + ) + if _is_method: + state = [None] + else: + state = [msg % kwds] def wrapper(*args, **kwds): - warn(final, DeprecationWarning, stacklevel=2) + text = state[0] + if text is None: + klass = args[0].__class__ + kwds.update(klass=klass.__name__, mod=klass.__module__) + text = state[0] = msg % kwds + warn(text, DeprecationWarning, stacklevel=2) return func(*args, **kwds) update_wrapper(wrapper, func) if updoc and (deprecated or removed) and wrapper.__doc__: @@ -170,50 +190,63 @@ def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True): return wrapper return build -def relocated_function(target, msg=None, name=None, deprecated=None, mod=None, - removed=None, updoc=True): - """constructor to create alias for relocated function. +def deprecated_method(msg=None, deprecated=None, removed=None, updoc=True, + replacement=None): + """decorator to deprecate a method. - :arg target: import path to target :arg msg: optional msg, default chosen if omitted :kwd deprecated: release where function was first deprecated :kwd removed: release where function will be removed + :kwd replacement: name/instructions for replacement method. :kwd updoc: add notice to docstring (default ``True``) """ - target_mod, target_name = target.rsplit(".",1) - if mod is None: - import inspect - mod = inspect.currentframe(1).f_globals["__name__"] - if not name: - name = target_name - if msg is None: - msg = ("the function %(mod)s.%(name)s() has been moved to " - "%(target_mod)s.%(target_name)s(), the old location is deprecated") - if deprecated: - msg += " as of Passlib %(deprecated)s" - if removed: - msg += ", and will be removed in Passlib %(removed)s" - msg += "." - msg %= dict( - mod=mod, - name=name, - target_mod=target_mod, - target_name=target_name, - deprecated=deprecated, - removed=removed, - ) - state = [None] - def wrapper(*args, **kwds): - warn(msg, DeprecationWarning, stacklevel=2) - func = state[0] - if func is None: - module = __import__(target_mod, fromlist=[target_name], level=0) - func = state[0] = getattr(module, target_name) - return func(*args, **kwds) - wrapper.__module__ = mod - wrapper.__name__ = name - wrapper.__doc__ = msg - return wrapper + return deprecated_function(msg, deprecated, removed, updoc, replacement, + _is_method=True) + +##def relocated_function(target, msg=None, name=None, deprecated=None, mod=None, +## removed=None, updoc=True): +## """constructor to create alias for relocated function. +## +## :arg target: import path to target +## :arg msg: optional msg, default chosen if omitted +## :kwd deprecated: release where function was first deprecated +## :kwd removed: release where function will be removed +## :kwd updoc: add notice to docstring (default ``True``) +## """ +## target_mod, target_name = target.rsplit(".",1) +## if mod is None: +## import inspect +## mod = inspect.currentframe(1).f_globals["__name__"] +## if not name: +## name = target_name +## if msg is None: +## msg = ("the function %(mod)s.%(name)s() has been moved to " +## "%(target_mod)s.%(target_name)s(), the old location is deprecated") +## if deprecated: +## msg += " as of Passlib %(deprecated)s" +## if removed: +## msg += ", and will be removed in Passlib %(removed)s" +## msg += "." +## msg %= dict( +## mod=mod, +## name=name, +## target_mod=target_mod, +## target_name=target_name, +## deprecated=deprecated, +## removed=removed, +## ) +## state = [None] +## def wrapper(*args, **kwds): +## warn(msg, DeprecationWarning, stacklevel=2) +## func = state[0] +## if func is None: +## module = __import__(target_mod, fromlist=[target_name], level=0) +## func = state[0] = getattr(module, target_name) +## return func(*args, **kwds) +## wrapper.__module__ = mod +## wrapper.__name__ = name +## wrapper.__doc__ = msg +## return wrapper class memoized_property(object): """decorator which invokes method once, then replaces attr with result""" @@ -317,7 +350,7 @@ def consteq(left, right): return result == 0 @deprecated_function(deprecated="1.6", removed="1.8") -def splitcomma(source, sep=","): +def splitcomma(source, sep=","): # pragma: no cover """split comma-separated string into list of elements, stripping whitespace and discarding empty elements. """ @@ -350,6 +383,11 @@ def saslprep(source, errname="value"): :returns: normalized unicode string + + .. note:: + + Due to a missing :mod:`!stringprep` module, this feature + is not available on Jython. """ # saslprep - http://tools.ietf.org/html/rfc4013 # stringprep - http://tools.ietf.org/html/rfc3454 @@ -439,12 +477,12 @@ def saslprep(source, errname="value"): return data -# implement stub for ironpython -if _ipy_missing_stringprep: +# replace saslprep() with stub when stringprep is missing +if stringprep is None: def saslprep(source, errname="value"): - "ironpython stub for saslprep()" - raise NotImplementedError("saslprep() requires the stdlib 'stringprep' " - "module, which is not available under IronPython") + "stub for saslprep()" + raise NotImplementedError("saslprep() support requires the 'stringprep' " + "module, which is " + _stringprep_missing_reason) #============================================================================= # bytes helpers @@ -480,7 +518,7 @@ def render_bytes(source, *args): # NOTE: deprecating bytes<->int in favor of just using struct module. @deprecated_function(deprecated="1.6", removed="1.8") -def bytes_to_int(value): +def bytes_to_int(value): # pragma: no cover "decode string of bytes as single big-endian integer" from passlib.utils.compat import byte_elem_value out = 0 @@ -489,7 +527,7 @@ def bytes_to_int(value): return out @deprecated_function(deprecated="1.6", removed="1.8") -def int_to_bytes(value, count): +def int_to_bytes(value, count): # pragma: no cover "encodes integer into single big-endian byte string" assert value < (1<<(8*count)), "value too large for %d bytes: %d" % (count, value) return join_byte_values( @@ -592,15 +630,15 @@ def to_unicode(source, source_encoding="utf-8", errname="value"): * returns unicode strings unchanged. * returns bytes strings decoded using *source_encoding* """ + assert source_encoding if isinstance(source, unicode): return source elif isinstance(source, bytes): - assert source_encoding return source.decode(source_encoding) else: raise ExpectedStringError(source, errname) -if PY3 or IRONPYTHON: +if PY3: def to_native_str(source, encoding="utf-8", errname="value"): if isinstance(source, bytes): return source.decode(encoding) @@ -639,7 +677,7 @@ add_doc(to_native_str, """) @deprecated_function(deprecated="1.6", removed="1.7") -def to_hash_str(source, encoding="ascii"): +def to_hash_str(source, encoding="ascii"): # pragma: no cover "deprecated, use to_native_str() instead" return to_native_str(source, encoding, errname="hash") @@ -713,7 +751,7 @@ class Base64Engine(object): if isinstance(charmap, unicode): charmap = charmap.encode("latin-1") elif not isinstance(charmap, bytes): - raise TypeError("charmap must be unicode/bytes string") + raise ExpectedStringError(charmap, "charmap") if len(charmap) != 64: raise ValueError("charmap must be 64 characters in length") if len(set(charmap)) != 64: @@ -1160,8 +1198,7 @@ class Base64Engine(object): :returns: a string of length ``int(ceil(bits/6.0))``. """ - if value < 0: - raise ValueError("value cannot be negative") + assert value >= 0, "caller did not sanitize input" pad = -bits % 6 bits += pad if self.big: @@ -1313,22 +1350,14 @@ else: if isinstance(secret, bytes): # Python 3's crypt() only accepts unicode, which is then # encoding using utf-8 before passing to the C-level crypt(). - # so we have to decode the secret, but also check that it - # re-encodes to the original sequence of bytes... otherwise - # the call to crypt() will digest the wrong value. + # so we have to decode the secret. orig = secret try: secret = secret.decode("utf-8") except UnicodeDecodeError: return None - if secret.encode("utf-8") != orig: - # just in case original encoding wouldn't be reproduced - # during call to os_crypt. not sure if/how this could - # happen, but being paranoid. - from passlib.exc import PasslibRuntimeWarning - warn("utf-8 password didn't re-encode correctly!", - PasslibRuntimeWarning) - return None + assert secret.encode("utf-8") == orig, \ + "utf-8 spec says this can't happen!" if _NULL in secret: raise ValueError("null character in secret") if isinstance(hash, bytes): |