summaryrefslogtreecommitdiff
path: root/passlib/utils/__init__.py
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-04-17 23:14:51 -0400
committerEli Collins <elic@assurancetechnologies.com>2012-04-17 23:14:51 -0400
commit64ab6fc89b497efa9169f11d55251e417c4db0ba (patch)
treeb3f6f5dc27b87a6bc90cb3686fa98239ee8ff053 /passlib/utils/__init__.py
parent8eb4c4d3b58eec6802c698ddbf357b2fd243a68c (diff)
parentcd029846fdc0c3d7ffc7f53caad4579e7e0e8725 (diff)
downloadpasslib-ironpython-support-dev.tar.gz
Merge from defaultironpython-support-dev
Diffstat (limited to 'passlib/utils/__init__.py')
-rw-r--r--passlib/utils/__init__.py183
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):