summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2016-11-22 16:49:46 -0500
committerEli Collins <elic@assurancetechnologies.com>2016-11-22 16:49:46 -0500
commit4f03b94b1c26468400c4839c0932f411e6667fe7 (patch)
treeb48d34e6f534ed9a47b18a0ca2ccef4e73376534
parent659c6afdcb85aa5715e18c4bd2c93f0cdc6de17e (diff)
downloadpasslib-4f03b94b1c26468400c4839c0932f411e6667fe7.tar.gz
passlib.utils: relocated a bunch of properties & decorators to .utils.decor
-rw-r--r--passlib/apache.py3
-rw-r--r--passlib/context.py5
-rw-r--r--passlib/crypto/digest.py3
-rw-r--r--passlib/ext/django/utils.py2
-rw-r--r--passlib/handlers/ldap_digests.py3
-rw-r--r--passlib/handlers/scrypt.py3
-rw-r--r--passlib/ifc.py3
-rw-r--r--passlib/pwd.py3
-rw-r--r--passlib/tests/test_crypto_scrypt.py3
-rw-r--r--passlib/tests/test_ext_django.py2
-rw-r--r--passlib/tests/test_utils.py6
-rw-r--r--passlib/tests/utils.py3
-rw-r--r--passlib/totp.py3
-rw-r--r--passlib/utils/__init__.py182
-rw-r--r--passlib/utils/decor.py191
-rw-r--r--passlib/utils/des.py2
-rw-r--r--passlib/utils/handlers.py5
-rw-r--r--passlib/utils/pbkdf2.py2
18 files changed, 228 insertions, 196 deletions
diff --git a/passlib/apache.py b/passlib/apache.py
index e2db7ca..41a4315 100644
--- a/passlib/apache.py
+++ b/passlib/apache.py
@@ -14,7 +14,8 @@ from passlib import exc, registry
from passlib.context import CryptContext
from passlib.exc import ExpectedStringError
from passlib.hash import htdigest
-from passlib.utils import render_bytes, to_bytes, deprecated_method, is_ascii_codec
+from passlib.utils import render_bytes, to_bytes, is_ascii_codec
+from passlib.utils.decor import deprecated_method
from passlib.utils.compat import join_bytes, unicode, BytesIO, PY3
# local
__all__ = [
diff --git a/passlib/context.py b/passlib/context.py
index 4926bb0..98cee6c 100644
--- a/passlib/context.py
+++ b/passlib/context.py
@@ -13,8 +13,8 @@ from warnings import warn
# pkg
from passlib.exc import ExpectedStringError, ExpectedTypeError, PasslibConfigWarning
from passlib.registry import get_crypt_handler, _validate_handler_name
-from passlib.utils import (handlers as uh, to_bytes, deprecated_method,
- to_unicode, splitcomma, memoized_property,
+from passlib.utils import (handlers as uh, to_bytes,
+ to_unicode, splitcomma,
as_bool, timer, rng, getrandstr, BASE64_CHARS,
)
from passlib.utils.compat import (iteritems, num_types, irange,
@@ -22,6 +22,7 @@ from passlib.utils.compat import (iteritems, num_types, irange,
NativeStringIO, BytesIO,
unicode_or_bytes_types, native_string_types,
)
+from passlib.utils.decor import deprecated_method, memoized_property
# local
__all__ = [
'CryptContext',
diff --git a/passlib/crypto/digest.py b/passlib/crypto/digest.py
index 2d8006a..18dce85 100644
--- a/passlib/crypto/digest.py
+++ b/passlib/crypto/digest.py
@@ -32,8 +32,9 @@ except ImportError:
# pkg
from passlib import exc
from passlib.utils import join_bytes, to_native_str, join_byte_values, to_bytes, \
- SequenceMixin, memoized_property
+ SequenceMixin
from passlib.utils.compat import irange, int_types, unicode_or_bytes_types, PY3
+from passlib.utils.decor import memoized_property
# local
__all__ = [
# hash utils
diff --git a/passlib/ext/django/utils.py b/passlib/ext/django/utils.py
index 6ed202c..33c8785 100644
--- a/passlib/ext/django/utils.py
+++ b/passlib/ext/django/utils.py
@@ -19,8 +19,8 @@ except ImportError:
from passlib import exc, registry
from passlib.context import CryptContext
from passlib.exc import PasslibRuntimeWarning
-from passlib.utils import memoized_property
from passlib.utils.compat import get_method_function, iteritems, OrderedDict
+from passlib.utils.decor import memoized_property
# local
__all__ = [
"DJANGO_VERSION",
diff --git a/passlib/handlers/ldap_digests.py b/passlib/handlers/ldap_digests.py
index 791c37e..4356f07 100644
--- a/passlib/handlers/ldap_digests.py
+++ b/passlib/handlers/ldap_digests.py
@@ -11,8 +11,9 @@ import re
# site
# pkg
from passlib.handlers.misc import plaintext
-from passlib.utils import unix_crypt_schemes, classproperty, to_unicode
+from passlib.utils import unix_crypt_schemes, to_unicode
from passlib.utils.compat import uascii_to_str, unicode, u
+from passlib.utils.decor import classproperty
import passlib.utils.handlers as uh
# local
__all__ = [
diff --git a/passlib/handlers/scrypt.py b/passlib/handlers/scrypt.py
index f7364b7..1d0d893 100644
--- a/passlib/handlers/scrypt.py
+++ b/passlib/handlers/scrypt.py
@@ -8,8 +8,9 @@ import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.crypto import scrypt as _scrypt
-from passlib.utils import h64, to_bytes, classproperty, b64s_decode, b64s_encode
+from passlib.utils import h64, to_bytes, b64s_decode, b64s_encode
from passlib.utils.compat import u, bascii_to_str, suppress_cause
+from passlib.utils.decor import classproperty
import passlib.utils.handlers as uh
# local
__all__ = [
diff --git a/passlib/ifc.py b/passlib/ifc.py
index b376d52..606fd05 100644
--- a/passlib/ifc.py
+++ b/passlib/ifc.py
@@ -7,8 +7,7 @@ import logging; log = logging.getLogger(__name__)
import sys
# site
# pkg
-from passlib.utils.compat import unicode
-from passlib.utils import deprecated_method
+from passlib.utils.decor import deprecated_method
# local
__all__ = [
"PasswordHash",
diff --git a/passlib/pwd.py b/passlib/pwd.py
index 1410817..274666a 100644
--- a/passlib/pwd.py
+++ b/passlib/pwd.py
@@ -14,7 +14,8 @@ import os
# pkg
from passlib import exc
from passlib.utils.compat import PY2, irange, itervalues, int_types
-from passlib.utils import rng, getrandstr, to_unicode, memoized_property
+from passlib.utils import rng, getrandstr, to_unicode
+from passlib.utils.decor import memoized_property
# local
__all__ = [
"genword", "default_charsets",
diff --git a/passlib/tests/test_crypto_scrypt.py b/passlib/tests/test_crypto_scrypt.py
index 9a3d4c4..06b8b29 100644
--- a/passlib/tests/test_crypto_scrypt.py
+++ b/passlib/tests/test_crypto_scrypt.py
@@ -11,8 +11,9 @@ import warnings
# site
# pkg
from passlib import exc
-from passlib.utils import classproperty, getrandbytes
+from passlib.utils import getrandbytes
from passlib.utils.compat import PYPY, u, bascii_to_str
+from passlib.utils.decor import classproperty
from passlib.tests.utils import TestCase, skipUnless, TEST_MODE, hb
# subject
from passlib.crypto import scrypt as scrypt_mod
diff --git a/passlib/tests/test_ext_django.py b/passlib/tests/test_ext_django.py
index 0598fd0..e8ae175 100644
--- a/passlib/tests/test_ext_django.py
+++ b/passlib/tests/test_ext_django.py
@@ -15,7 +15,7 @@ from passlib.ext.django.utils import (
DJANGO_VERSION, MIN_DJANGO_VERSION, DjangoTranslator,
)
from passlib.utils.compat import iteritems, get_method_function, u
-from passlib.utils import memoized_property
+from passlib.utils.decor import memoized_property
# tests
from passlib.tests.utils import TestCase, TEST_MODE, handler_derived_from
from passlib.tests.test_handlers import get_handler_case, conditionally_available_hashes
diff --git a/passlib/tests/test_utils.py b/passlib/tests/test_utils.py
index b49d48c..d6a4555 100644
--- a/passlib/tests/test_utils.py
+++ b/passlib/tests/test_utils.py
@@ -34,7 +34,7 @@ class MiscTest(TestCase):
self.assertTrue('irange' in dir(compat))
def test_classproperty(self):
- from passlib.utils import classproperty
+ from passlib.utils.decor import classproperty
class test(object):
xvar = 1
@@ -47,7 +47,7 @@ class MiscTest(TestCase):
self.assertIs(prop.im_func, prop.__func__)
def test_deprecated_function(self):
- from passlib.utils import deprecated_function
+ from passlib.utils.decor import deprecated_function
# NOTE: not comprehensive, just tests the basic behavior
@deprecated_function(deprecated="1.6", removed="1.8")
@@ -65,7 +65,7 @@ class MiscTest(TestCase):
self.assertEqual(test_func(1,2), (1,2))
def test_memoized_property(self):
- from passlib.utils import memoized_property
+ from passlib.utils.decor import memoized_property
class dummy(object):
counter = 0
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index bd72652..d974a41 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -26,9 +26,10 @@ from passlib.exc import MissingBackendError
import passlib.registry as registry
from passlib.tests.backports import TestCase as _TestCase, skip, skipIf, skipUnless, SkipTest
from passlib.utils import has_rounds_info, has_salt_info, rounds_cost_values, \
- classproperty, rng as sys_rng, getrandstr, is_ascii_safe, to_native_str, \
+ rng as sys_rng, getrandstr, is_ascii_safe, to_native_str, \
repeat_string, tick, batch
from passlib.utils.compat import iteritems, irange, u, unicode, PY2
+from passlib.utils.decor import classproperty
import passlib.utils.handlers as uh
# local
__all__ = [
diff --git a/passlib/totp.py b/passlib/totp.py
index 547e1ae..a2e91e1 100644
--- a/passlib/totp.py
+++ b/passlib/totp.py
@@ -35,10 +35,11 @@ except ImportError:
# pkg
from passlib import exc
from passlib.exc import TokenError, MalformedTokenError, InvalidTokenError, UsedTokenError
-from passlib.utils import (to_unicode, to_bytes, consteq, memoized_property, hybrid_method,
+from passlib.utils import (to_unicode, to_bytes, consteq,
getrandbytes, rng, SequenceMixin, xor_bytes, getrandstr, BASE64_CHARS)
from passlib.utils.compat import (u, unicode, native_string_types, bascii_to_str, int_types, num_types,
irange, byte_elem_value, UnicodeIO, suppress_cause)
+from passlib.utils.decor import hybrid_method, memoized_property
from passlib.crypto.digest import lookup_hash, compile_hmac, pbkdf2_hmac
from passlib.hash import pbkdf2_sha256
# local
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index a9485e7..701557d 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -34,6 +34,14 @@ import types
from warnings import warn
# site
# pkg
+from passlib.utils.decor import (
+ # [remove these aliases in 2.0]
+ deprecated_function,
+ deprecated_method,
+ memoized_property,
+ classproperty,
+ hybrid_method,
+)
from passlib.exc import ExpectedStringError
from passlib.utils.compat import (add_doc, join_bytes, join_byte_values,
join_byte_elems, irange, imap, PY3, u,
@@ -48,12 +56,6 @@ __all__ = [
'unix_crypt_schemes',
'rounds_cost_values',
- # decorators
- "classproperty",
-## "deprecated_function",
-## "relocated_function",
-## "memoized_class_property",
-
# unicode helpers
'consteq',
'saslprep',
@@ -125,174 +127,8 @@ _USPACE = u(" ")
MAX_PASSWORD_SIZE = int(os.environ.get("PASSLIB_MAX_PASSWORD_SIZE") or 4096)
#=============================================================================
-# decorators and meta helpers
+# type helpers
#=============================================================================
-class classproperty(object):
- """Function decorator which acts like a combination of classmethod+property (limited to read-only properties)"""
-
- def __init__(self, func):
- self.im_func = func
-
- def __get__(self, obj, cls):
- return self.im_func(cls)
-
- @property
- def __func__(self):
- """py3 compatible alias"""
- return self.im_func
-
-def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,
- replacement=None, _is_method=False,
- func_module=None):
- """decorator to deprecate a function.
-
- :arg msg: optional msg, default chosen if omitted
- :kwd deprecated: version when function was first deprecated
- :kwd removed: version when function will be removed
- :kwd replacement: alternate name / instructions for replacing this function.
- :kwd updoc: add notice to docstring (default ``True``)
- """
- if msg is None:
- 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):
- is_classmethod = _is_method and isinstance(func, classmethod)
- if is_classmethod:
- # NOTE: PY26 doesn't support "classmethod().__func__" directly...
- func = func.__get__(None, type).__func__
- opts = dict(
- mod=func_module or func.__module__,
- name=func.__name__,
- deprecated=deprecated,
- removed=removed,
- )
- if _is_method:
- def wrapper(*args, **kwds):
- tmp = opts.copy()
- klass = args[0] if is_classmethod else args[0].__class__
- tmp.update(klass=klass.__name__, mod=klass.__module__)
- warn(msg % tmp, DeprecationWarning, stacklevel=2)
- return func(*args, **kwds)
- else:
- text = msg % opts
- def wrapper(*args, **kwds):
- warn(text, DeprecationWarning, stacklevel=2)
- return func(*args, **kwds)
- update_wrapper(wrapper, func)
- if updoc and (deprecated or removed) and \
- wrapper.__doc__ and ".. deprecated::" not in wrapper.__doc__:
- txt = deprecated or ''
- if removed or replacement:
- txt += "\n "
- if removed:
- txt += "and will be removed in version %s" % (removed,)
- if replacement:
- if removed:
- txt += ", "
- txt += "use %s instead" % replacement
- txt += "."
- if not wrapper.__doc__.strip(" ").endswith("\n"):
- wrapper.__doc__ += "\n"
- wrapper.__doc__ += "\n.. deprecated:: %s\n" % (txt,)
- if is_classmethod:
- wrapper = classmethod(wrapper)
- return wrapper
- return build
-
-def deprecated_method(msg=None, deprecated=None, removed=None, updoc=True,
- replacement=None):
- """decorator to deprecate a method.
-
- :arg msg: optional msg, default chosen if omitted
- :kwd deprecated: version when method was first deprecated
- :kwd removed: version when method will be removed
- :kwd replacement: alternate name / instructions for replacing this method.
- :kwd updoc: add notice to docstring (default ``True``)
- """
- return deprecated_function(msg, deprecated, removed, updoc, replacement,
- _is_method=True)
-
-class memoized_property(object):
- """decorator which invokes method once, then replaces attr with result"""
- def __init__(self, func):
- self.__func__ = func
- self.__name__ = func.__name__
- self.__doc__ = func.__doc__
-
- def __get__(self, obj, cls):
- if obj is None:
- return self
- value = self.__func__(obj)
- setattr(obj, self.__name__, value)
- return value
-
- if not PY3:
-
- @property
- def im_func(self):
- """py2 alias"""
- return self.__func__
-
- def clear_cache(self, obj):
- """
- class-level helper to clear stored value (if any).
-
- usage: :samp:`type(self).{attr}.clear_cache(self)`
- """
- obj.__dict__.pop(self.__name__, None)
-
- def peek_cache(self, obj, default=None):
- """
- class-level helper to peek at stored value
-
- usage: :samp:`value = type(self).{attr}.clear_cache(self)`
- """
- return obj.__dict__.get(self.__name__, default)
-
-# works but not used
-##class memoized_class_property(object):
-## """function decorator which calls function as classmethod,
-## and replaces itself with result for current and all future invocations.
-## """
-## def __init__(self, func):
-## self.im_func = func
-##
-## def __get__(self, obj, cls):
-## func = self.im_func
-## value = func(cls)
-## setattr(cls, func.__name__, value)
-## return value
-##
-## @property
-## def __func__(self):
-## "py3 compatible alias"
-
-class hybrid_method(object):
- """
- decorator which invokes function with class if called as class method,
- and with object if called at instance level.
- """
-
- def __init__(self, func):
- self.func = func
- update_wrapper(self, func)
-
- def __get__(self, obj, cls):
- if obj is None:
- obj = cls
- if PY3:
- return types.MethodType(self.func, obj)
- else:
- return types.MethodType(self.func, obj, cls)
class SequenceMixin(object):
"""
diff --git a/passlib/utils/decor.py b/passlib/utils/decor.py
index 9319c2e..9041d5d 100644
--- a/passlib/utils/decor.py
+++ b/passlib/utils/decor.py
@@ -6,15 +6,62 @@ passlib.utils.decor -- helper decorators & properties
#=============================================================================
# core
from __future__ import absolute_import, division, print_function
-from functools import wraps
+import logging
+log = logging.getLogger(__name__)
+from functools import wraps, update_wrapper
+import types
+from warnings import warn
# site
# pkg
+from passlib.utils.compat import PY3
# local
__all__ = [
- "memoize_single_value"
+ "classproperty",
+ "hybrid_method",
+
+ "memoize_single_value",
+ "memoized_property",
+
+ "deprecated_function",
+ "deprecated_method",
]
#=============================================================================
+# class-level decorators
+#=============================================================================
+class classproperty(object):
+ """Function decorator which acts like a combination of classmethod+property (limited to read-only properties)"""
+
+ def __init__(self, func):
+ self.im_func = func
+
+ def __get__(self, obj, cls):
+ return self.im_func(cls)
+
+ @property
+ def __func__(self):
+ """py3 compatible alias"""
+ return self.im_func
+
+class hybrid_method(object):
+ """
+ decorator which invokes function with class if called as class method,
+ and with object if called at instance level.
+ """
+
+ def __init__(self, func):
+ self.func = func
+ update_wrapper(self, func)
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ obj = cls
+ if PY3:
+ return types.MethodType(self.func, obj)
+ else:
+ return types.MethodType(self.func, obj, cls)
+
+#=============================================================================
# memoization
#=============================================================================
@@ -41,6 +88,146 @@ def memoize_single_value(func):
return wrapper
+class memoized_property(object):
+ """
+ decorator which invokes method once, then replaces attr with result
+ """
+ def __init__(self, func):
+ self.__func__ = func
+ self.__name__ = func.__name__
+ self.__doc__ = func.__doc__
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ value = self.__func__(obj)
+ setattr(obj, self.__name__, value)
+ return value
+
+ if not PY3:
+
+ @property
+ def im_func(self):
+ """py2 alias"""
+ return self.__func__
+
+ def clear_cache(self, obj):
+ """
+ class-level helper to clear stored value (if any).
+
+ usage: :samp:`type(self).{attr}.clear_cache(self)`
+ """
+ obj.__dict__.pop(self.__name__, None)
+
+ def peek_cache(self, obj, default=None):
+ """
+ class-level helper to peek at stored value
+
+ usage: :samp:`value = type(self).{attr}.clear_cache(self)`
+ """
+ return obj.__dict__.get(self.__name__, default)
+
+# works but not used
+##class memoized_class_property(object):
+## """function decorator which calls function as classmethod,
+## and replaces itself with result for current and all future invocations.
+## """
+## def __init__(self, func):
+## self.im_func = func
+##
+## def __get__(self, obj, cls):
+## func = self.im_func
+## value = func(cls)
+## setattr(cls, func.__name__, value)
+## return value
+##
+## @property
+## def __func__(self):
+## "py3 compatible alias"
+
+#=============================================================================
+# deprecation
+#=============================================================================
+def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,
+ replacement=None, _is_method=False,
+ func_module=None):
+ """decorator to deprecate a function.
+
+ :arg msg: optional msg, default chosen if omitted
+ :kwd deprecated: version when function was first deprecated
+ :kwd removed: version when function will be removed
+ :kwd replacement: alternate name / instructions for replacing this function.
+ :kwd updoc: add notice to docstring (default ``True``)
+ """
+ if msg is None:
+ 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):
+ is_classmethod = _is_method and isinstance(func, classmethod)
+ if is_classmethod:
+ # NOTE: PY26 doesn't support "classmethod().__func__" directly...
+ func = func.__get__(None, type).__func__
+ opts = dict(
+ mod=func_module or func.__module__,
+ name=func.__name__,
+ deprecated=deprecated,
+ removed=removed,
+ )
+ if _is_method:
+ def wrapper(*args, **kwds):
+ tmp = opts.copy()
+ klass = args[0] if is_classmethod else args[0].__class__
+ tmp.update(klass=klass.__name__, mod=klass.__module__)
+ warn(msg % tmp, DeprecationWarning, stacklevel=2)
+ return func(*args, **kwds)
+ else:
+ text = msg % opts
+ def wrapper(*args, **kwds):
+ warn(text, DeprecationWarning, stacklevel=2)
+ return func(*args, **kwds)
+ update_wrapper(wrapper, func)
+ if updoc and (deprecated or removed) and \
+ wrapper.__doc__ and ".. deprecated::" not in wrapper.__doc__:
+ txt = deprecated or ''
+ if removed or replacement:
+ txt += "\n "
+ if removed:
+ txt += "and will be removed in version %s" % (removed,)
+ if replacement:
+ if removed:
+ txt += ", "
+ txt += "use %s instead" % replacement
+ txt += "."
+ if not wrapper.__doc__.strip(" ").endswith("\n"):
+ wrapper.__doc__ += "\n"
+ wrapper.__doc__ += "\n.. deprecated:: %s\n" % (txt,)
+ if is_classmethod:
+ wrapper = classmethod(wrapper)
+ return wrapper
+ return build
+
+def deprecated_method(msg=None, deprecated=None, removed=None, updoc=True,
+ replacement=None):
+ """decorator to deprecate a method.
+
+ :arg msg: optional msg, default chosen if omitted
+ :kwd deprecated: version when method was first deprecated
+ :kwd removed: version when method will be removed
+ :kwd replacement: alternate name / instructions for replacing this method.
+ :kwd updoc: add notice to docstring (default ``True``)
+ """
+ return deprecated_function(msg, deprecated, removed, updoc, replacement,
+ _is_method=True)
+
#=============================================================================
# eof
#=============================================================================
diff --git a/passlib/utils/des.py b/passlib/utils/des.py
index 62adac4..034bfc4 100644
--- a/passlib/utils/des.py
+++ b/passlib/utils/des.py
@@ -14,7 +14,7 @@ warn("the 'passlib.utils.des' module has been relocated to 'passlib.crypto.des'
#=============================================================================
# relocated functions
#=============================================================================
-from passlib.utils import deprecated_function
+from passlib.utils.decor import deprecated_function
from passlib.crypto.des import expand_des_key, des_encrypt_block, des_encrypt_int_block
expand_des_key = deprecated_function(deprecated="1.7", removed="1.8",
diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py
index d9c2c1e..876edec 100644
--- a/passlib/utils/handlers.py
+++ b/passlib/utils/handlers.py
@@ -17,14 +17,15 @@ from passlib.exc import MissingBackendError, PasslibConfigWarning, \
from passlib.ifc import PasswordHash
from passlib.registry import get_crypt_handler
from passlib.utils import (
- classproperty, consteq, getrandstr, getrandbytes,
+ consteq, getrandstr, getrandbytes,
BASE64_CHARS, HASH64_CHARS, rng, to_native_str,
- is_crypt_handler, to_unicode, deprecated_method,
+ is_crypt_handler, to_unicode,
MAX_PASSWORD_SIZE, accepts_keyword, as_bool,
update_mixin_classes)
from passlib.utils.compat import join_byte_values, irange, u, native_string_types, \
uascii_to_str, join_unicode, unicode, str_to_uascii, \
join_unicode, unicode_or_bytes_types, PY2, int_types
+from passlib.utils.decor import classproperty, deprecated_method
# local
__all__ = [
# helpers for implementing MCF handlers
diff --git a/passlib/utils/pbkdf2.py b/passlib/utils/pbkdf2.py
index 3a6aff9..273143b 100644
--- a/passlib/utils/pbkdf2.py
+++ b/passlib/utils/pbkdf2.py
@@ -12,7 +12,7 @@ import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.exc import ExpectedTypeError
-from passlib.utils import deprecated_function
+from passlib.utils.decor import deprecated_function
from passlib.utils.compat import native_string_types
from passlib.crypto.digest import norm_hash_name, lookup_hash, pbkdf1 as _pbkdf1, pbkdf2_hmac, compile_hmac
# local