summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--admin/bench_pbkdf2.py6
-rw-r--r--admin/benchmarks.py42
-rw-r--r--admin/plot_verify_timing.py1
-rw-r--r--admin/regen.py1
-rw-r--r--choose_rounds.py1
-rw-r--r--docs/history/1.6.rst4
-rw-r--r--docs/history/1.7.rst17
-rw-r--r--docs/history/1.8.rst94
-rw-r--r--docs/history/index.rst9
-rw-r--r--docs/index.rst4
-rw-r--r--docs/install.rst47
-rw-r--r--docs/lib/passlib.apache.rst4
-rw-r--r--docs/lib/passlib.context.rst36
-rw-r--r--docs/lib/passlib.hash.bcrypt.rst7
-rw-r--r--docs/lib/passlib.hash.unix_disabled.rst4
-rw-r--r--docs/lib/passlib.utils.pbkdf2.rst2
-rw-r--r--passlib/__init__.py2
-rw-r--r--passlib/_setup/stamp.py1
-rw-r--r--passlib/apache.py153
-rw-r--r--passlib/apps.py1
-rw-r--r--passlib/context.py749
-rw-r--r--passlib/crypto/_blowfish/__init__.py9
-rw-r--r--passlib/crypto/_blowfish/_gen_files.py9
-rw-r--r--passlib/crypto/_md4.py10
-rw-r--r--passlib/crypto/des.py17
-rw-r--r--passlib/crypto/digest.py67
-rw-r--r--passlib/crypto/scrypt/__init__.py5
-rw-r--r--passlib/crypto/scrypt/_builtin.py13
-rw-r--r--passlib/exc.py4
-rw-r--r--passlib/ext/django/utils.py30
-rw-r--r--passlib/handlers/argon2.py40
-rw-r--r--passlib/handlers/bcrypt.py197
-rw-r--r--passlib/handlers/cisco.py24
-rw-r--r--passlib/handlers/des_crypt.py57
-rw-r--r--passlib/handlers/digests.py7
-rw-r--r--passlib/handlers/django.py39
-rw-r--r--passlib/handlers/fshp.py21
-rw-r--r--passlib/handlers/ldap_digests.py37
-rw-r--r--passlib/handlers/md5_crypt.py11
-rw-r--r--passlib/handlers/misc.py74
-rw-r--r--passlib/handlers/mssql.py10
-rw-r--r--passlib/handlers/mysql.py14
-rw-r--r--passlib/handlers/oracle.py14
-rw-r--r--passlib/handlers/pbkdf2.py22
-rw-r--r--passlib/handlers/phpass.py15
-rw-r--r--passlib/handlers/postgres.py7
-rw-r--r--passlib/handlers/roundup.py7
-rw-r--r--passlib/handlers/scram.py23
-rw-r--r--passlib/handlers/scrypt.py23
-rw-r--r--passlib/handlers/sha1_crypt.py9
-rw-r--r--passlib/handlers/sha2_crypt.py30
-rw-r--r--passlib/handlers/sun_md5_crypt.py39
-rw-r--r--passlib/handlers/windows.py19
-rw-r--r--passlib/hash.py2
-rw-r--r--passlib/ifc.py23
-rw-r--r--passlib/pwd.py26
-rw-r--r--passlib/registry.py8
-rw-r--r--passlib/tests/backports.py67
-rw-r--r--passlib/tests/test_apache.py79
-rw-r--r--passlib/tests/test_apps.py1
-rw-r--r--passlib/tests/test_context.py71
-rw-r--r--passlib/tests/test_context_deprecated.py743
-rw-r--r--passlib/tests/test_crypto_builtin_md4.py24
-rw-r--r--passlib/tests/test_crypto_des.py1
-rw-r--r--passlib/tests/test_crypto_digest.py15
-rw-r--r--passlib/tests/test_crypto_scrypt.py13
-rw-r--r--passlib/tests/test_ext_django.py25
-rw-r--r--passlib/tests/test_ext_django_source.py13
-rw-r--r--passlib/tests/test_handlers.py112
-rw-r--r--passlib/tests/test_handlers_argon2.py7
-rw-r--r--passlib/tests/test_handlers_bcrypt.py77
-rw-r--r--passlib/tests/test_handlers_cisco.py6
-rw-r--r--passlib/tests/test_handlers_django.py15
-rw-r--r--passlib/tests/test_handlers_pbkdf2.py35
-rw-r--r--passlib/tests/test_handlers_scrypt.py4
-rw-r--r--passlib/tests/test_hosts.py1
-rw-r--r--passlib/tests/test_pwd.py2
-rw-r--r--passlib/tests/test_registry.py3
-rw-r--r--passlib/tests/test_totp.py41
-rw-r--r--passlib/tests/test_utils.py249
-rw-r--r--passlib/tests/test_utils_handlers.py182
-rw-r--r--passlib/tests/test_utils_md4.py2
-rw-r--r--passlib/tests/test_utils_pbkdf2.py62
-rw-r--r--passlib/tests/test_win32.py50
-rw-r--r--passlib/tests/tox_support.py5
-rw-r--r--passlib/tests/utils.py154
-rw-r--r--passlib/totp.py74
-rw-r--r--passlib/utils/__init__.py185
-rw-r--r--passlib/utils/binary.py88
-rw-r--r--passlib/utils/compat/__init__.py284
-rw-r--r--passlib/utils/compat/_ordered_dict.py242
-rw-r--r--passlib/utils/decor.py27
-rw-r--r--passlib/utils/des.py21
-rw-r--r--passlib/utils/handlers.py167
-rw-r--r--passlib/utils/pbkdf2.py19
-rw-r--r--passlib/win32.py68
-rw-r--r--setup.py10
-rw-r--r--tox.ini33
99 files changed, 1240 insertions, 4186 deletions
diff --git a/README b/README
index abd701f..1c2d54c 100644
--- a/README
+++ b/README
@@ -6,7 +6,7 @@ The Passlib Python Library
Welcome
=======
-Passlib is a password hashing library for Python 2 & 3, which provides
+Passlib is a password hashing library for Python 3, which provides
cross-platform implementations of over 30 password hashing algorithms, as well
as a framework for managing existing password hashes. It's designed to be useful
for a wide range of tasks, from verifying a hash found in /etc/shadow, to
diff --git a/admin/bench_pbkdf2.py b/admin/bench_pbkdf2.py
index 53a4d47..e04f46d 100644
--- a/admin/bench_pbkdf2.py
+++ b/admin/bench_pbkdf2.py
@@ -4,8 +4,6 @@ helper script to benchmark pbkdf2 implementations/backends
#=============================================================================
# init script env
#=============================================================================
-from __future__ import absolute_import, division, print_function, unicode_literals
-
# make sure passlib source dir is first in import path
import os, sys
os.chdir(os.path.abspath(os.path.join(__file__, *[".."]*2)))
@@ -25,7 +23,6 @@ except ImportError:
assert reload, "expected builtin reload()" # py2x
# site
# pkg
-from passlib.utils.compat import PY3
# local
#=============================================================================
@@ -126,9 +123,6 @@ def main():
import passlib.crypto.digest as digest_mod
for backend in ["from-bytes", "unpack", "hexlify"]:
name = "p/%s" % backend
- if backend == "from-bytes" and not PY3:
- na(name)
- continue
os.environ['PASSLIB_PBKDF2_BACKEND'] = backend
reload(digest_mod)
benchmark(name,
diff --git a/admin/benchmarks.py b/admin/benchmarks.py
index fb02b39..01e4bac 100644
--- a/admin/benchmarks.py
+++ b/admin/benchmarks.py
@@ -24,7 +24,6 @@ try:
except ImportError:
PasslibConfigWarning = None
import passlib.utils.handlers as uh
-from passlib.utils.compat import u, print_, unicode
from passlib.tests.utils import time_call
# local
@@ -78,10 +77,10 @@ class benchmark:
usec = int(secs * 1e6)
if usec < 1000:
return "%.*g usec" % (precision, usec)
- msec = usec / 1000
+ msec = usec // 1000
if msec < 1000:
return "%.*g msec" % (precision, msec)
- sec = msec / 1000
+ sec = msec // 1000
return "%.*g sec" % (precision, sec)
#=============================================================================
@@ -90,19 +89,15 @@ class benchmark:
sample_config_1p = os.path.join(root, "passlib", "tests", "sample_config_1s.cfg")
from passlib.context import CryptContext
-if hasattr(CryptContext, "from_path"):
- CryptPolicy = None
-else:
- from passlib.context import CryptPolicy
class BlankHandler(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
name = "blank"
- ident = u("$b$")
+ ident = u"$b$"
setting_kwds = ("rounds", "salt", "salt_size")
checksum_size = 1
min_salt_size = max_salt_size = 1
- salt_chars = u("a")
+ salt_chars = u"a"
min_rounds = 1000
max_rounds = 3000
@@ -117,14 +112,14 @@ class BlankHandler(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
return uh.render_mc3(self.ident, self.rounds, self.salt, self.checksum)
def _calc_checksum(self, secret):
- return unicode(secret[0:1])
+ return secret[0:1]
class AnotherHandler(BlankHandler):
name = "another"
- ident = u("$a$")
+ ident = u"$a$"
-SECRET = u("toomanysecrets")
-OTHER = u("setecastronomy")
+SECRET = u"toomanysecrets"
+OTHER = u"setecastronomy"
#=============================================================================
# CryptContext benchmarks
@@ -133,12 +128,8 @@ OTHER = u("setecastronomy")
def test_context_from_path():
"""test speed of CryptContext.from_path()"""
path = sample_config_1p
- if CryptPolicy:
- def helper():
- CryptPolicy.from_path(path)
- else:
- def helper():
- CryptContext.from_path(path)
+ def helper():
+ CryptContext.from_path(path)
return helper
@benchmark.constructor()
@@ -150,14 +141,9 @@ def test_context_update():
deprecated = [ "des_crypt" ],
sha512_crypt__min_rounds=4000,
)
- if CryptPolicy:
- policy=CryptPolicy.from_path(sample_config_1p)
- def helper():
- policy.replace(**kwds)
- else:
- ctx = CryptContext.from_path(sample_config_1p)
- def helper():
- ctx.copy(**kwds)
+ ctx = CryptContext.from_path(sample_config_1p)
+ def helper():
+ ctx.copy(**kwds)
return helper
@benchmark.constructor()
@@ -306,7 +292,7 @@ def main(*args):
if any(re.match(arg, k) for arg in args))
helper = benchmark.run(source, maxtime=2, bestof=3)
for name, secs, precision in helper:
- print_("%-50s %9s (%d)" % (name, benchmark.pptime(secs), precision))
+ print("%-50s %9s (%d)" % (name, benchmark.pptime(secs), precision))
if __name__ == "__main__":
import sys
diff --git a/admin/plot_verify_timing.py b/admin/plot_verify_timing.py
index 3f6255f..b34400b 100644
--- a/admin/plot_verify_timing.py
+++ b/admin/plot_verify_timing.py
@@ -3,7 +3,6 @@
small helper script used to compare timing of verify() & dummy_verify()
"""
# core
-from __future__ import absolute_import, division, print_function, unicode_literals
from argparse import ArgumentParser
import sys
from timeit import default_timer as tick
diff --git a/admin/regen.py b/admin/regen.py
index dc4b2fb..a461e3e 100644
--- a/admin/regen.py
+++ b/admin/regen.py
@@ -5,7 +5,6 @@ helper script which rebuilds all autogenerated bits of code w/in passlib.
# imports
#=============================================================================
# core
-from __future__ import absolute_import, division, print_function
import datetime
import re
import os
diff --git a/choose_rounds.py b/choose_rounds.py
index 80d8609..2949950 100644
--- a/choose_rounds.py
+++ b/choose_rounds.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import division, print_function
# core
import math
import logging; log = logging.getLogger(__name__)
diff --git a/docs/history/1.6.rst b/docs/history/1.6.rst
index 05b3d40..45ae322 100644
--- a/docs/history/1.6.rst
+++ b/docs/history/1.6.rst
@@ -280,7 +280,7 @@ CryptContext
* All new (and hopefully clearer) :ref:`tutorial <context-tutorial>`
and :ref:`reference <context-reference>` documentation.
- * The :class:`CryptPolicy` class and the :attr:`!CryptContext.policy` attribute have been deprecated.
+ * The :class:`!CryptPolicy` class and the :attr:`!CryptContext.policy` attribute have been deprecated.
This was a semi-internal class, which most applications
were not involved with at all, but to be conservative about
@@ -303,7 +303,7 @@ CryptContext
can now be set to the special string ``"auto"``; which will
automatically deprecate all schemes except for the default one.
- * The :ref:`min_verify_time <context-min-verify-time-option>` keyword
+ * The ``min_verify_time`` keyword
has been deprecated, will be ignored in release 1.7, and will be removed in release 1.8.
It was never very useful, and now complicates the internal code needlessly.
diff --git a/docs/history/1.7.rst b/docs/history/1.7.rst
index 28546a4..f1b9346 100644
--- a/docs/history/1.7.rst
+++ b/docs/history/1.7.rst
@@ -1,5 +1,3 @@
-.. _whats-new:
-
===========
Passlib 1.7
===========
@@ -91,6 +89,11 @@ Bugfixes
being run on systems lacking support for the hasher being tested.
This test now runs regardless of system support.
+Deprecations
+------------
+
+* Support for Python 2.x, 3.3, and 3.4 is deprecated; and will be dropped in Passlib 1.8.
+
Other Changes
-------------
@@ -184,11 +187,11 @@ Deprecations
Due to lack of ``pip`` and ``venv`` support, Passlib is no longer fully tested on Python
2.6 & 3.3. There are no known issues, and bugfixes against these versions will still be
accepted for the Passlib 1.7.x series.
- However, **Passlib 1.8 will drop support for Python 2.x & 3.3,** and require Python >= 3.4.
+ However, **Passlib 1.8 will drop support for Python 2.x, 3.3, & 3.4,** and require Python >= 3.5.
-* Support for Python 2.x & 3.3 is deprecated; and will be dropped in Passlib 1.8.
- *(2020-05-10: Updated to include all of Python 2.x; when 1.7.2 was released,
- only Python 2.6 / 3.3 support was deprecated)*
+* Support for Python 2.x, 3.3, and 3.4 is deprecated; and will be dropped in Passlib 1.8.
+ *(2020-10-06: Updated to include all of Python 2.x, 3.3, and 3.4; when 1.7.2 was released,
+ only Python 2.6 and 3.3 support was deprecated)*
* .. py:currentmodule:: passlib.hash
@@ -527,7 +530,7 @@ Changes in existing behavior:
Scheduled removal of features:
- * **[minor]** :mod:`passlib.context`: The :ref:`min_verify_time <context-min-verify-time-option>` keyword
+ * **[minor]** :mod:`passlib.context`: The ``min_verify_time`` keyword
that was deprecated in release 1.6, is now completely ignored.
Support will be removed entirely in release 1.8.
diff --git a/docs/history/1.8.rst b/docs/history/1.8.rst
new file mode 100644
index 0000000..9de5b7b
--- /dev/null
+++ b/docs/history/1.8.rst
@@ -0,0 +1,94 @@
+.. _whats-new:
+
+===========
+Passlib 1.8
+===========
+
+.. rst-class:: emphasize-children toc-always-open
+
+**1.8.0** (NOT YET RELEASED)
+============================
+
+Overview
+--------
+
+.. rst-class:: without-title
+
+.. warning::
+
+ **1.8 is under development,** and tenatively scheduled for release in late 2017.
+
+ See https://passlib.readthedocs.io/en/stable/history/1.7.html for the latest release.
+
+Requirements
+------------
+
+* **Passlib now requires Python >= 3.5.** As of this release, support for Python 2.x, 3.3,
+ and 3.4 has been dropped. If you need to use Passlib on an earlier version of Python,
+ please use the :doc:`1.7` series, which will be maintained in bugfix-only mode
+ for few more releases. (See :issue:`119` for rationale).
+
+Backwards Incompatibilities
+---------------------------
+
+The following previously-deprecated features were removed,
+though few of these should be in use, as they've been deprecated
+for a number of years / releases:
+
+ **passlib.apache:**
+
+ .. py:currentmodule:: passlib.apache
+
+ * :mod:`passlib.apache`: A number of deprecated options & methods were removed from
+ :class:`HtpasswdFile` and :class:`Htdigest`:
+
+ - Support for setting ``encoding=None`` removed, use ``return_unicode=True`` instead.
+ - ``autoload=False`` keyword removed, use ``new=True`` instead.
+ - :meth:`!load` method no longer supports ``force=False``, use :meth:`~HtpasswdFile.load_if_changed` instead.
+ - :meth:`!update` alias removed, use :meth:`~HtpasswdFile.set_password` instead.
+ - :meth:`!find` alias removed, use :meth:`~HtpasswdFile.get_hash` instead.
+ - :meth:`!verify` alias removed, use :meth:`~HtpasswdFile.check_password` instead.
+ - ``default`` keyword removed, use ``default_scheme`` instead.
+
+ **passlib.context:**
+
+ .. py:currentmodule:: passlib.context
+
+ * The :class:`!passlib.context.CryptPolicy` class was removed.
+ Code should be using the equivalent :meth:`~CryptContext` methods instead.
+
+ * Concurrent with that, :class:`CryptContext`'s ``policy`` keyword and attribute
+ were removed, along with :class:`LazyCryptContext`'s ``create_policy`` keyword.
+
+ * :meth:`!CryptContext.replace` alias removed, use :meth:`CryptContext.using` instead.
+
+ * :class:`CryptContext`'s ``min_verify_time`` and ``harden_verify`` keywords removed.
+
+ **passlib.hash:**
+
+ .. py:currentmodule:: passlib.hash
+
+ * :class:`!passlib.hash.unix_fallback` was removed, use :class:`~unix_disabled` instead.
+
+ **other modules:**
+
+ * In :mod:`passlib.ext.django`, support for ``CONFIG=None`` was dropped.
+
+ * The deprecated :mod:`!passlib.win32` module was removed, use :class:`passlib.hash.lmhash` hash instead.
+
+ **internal details:**
+
+ .. py:currentmodule:: passlib.hash
+
+ * The :meth:`!passlib.hash.nthash.raw_nthash` alias was removed, use :meth:`nthash.raw` instead.
+
+ * In :mod:`passlib.utils.handlers`: :class:`!StaticHandler` subclasses must now always implement
+ :meth:`!_calc_checksum`, the old genhash-based style is no longer supported or checked for.
+
+ * The deprecated :func:`passlib.utils.des.mdes_encrypt_int_block` method was removed.
+
+ * The :func:`passlib.utils.pbkdf2.norm_hash_name` alias was removed, use :func:`passlib.crypto.digest.norm_hash_name` instead.
+
+ .. py:currentmodule:: passlib.utils
+
+ * Many PY2 compatibility helper inside :mod:`!passlib.utils.compat` have been removed.
diff --git a/docs/history/index.rst b/docs/history/index.rst
index 90824c2..938f860 100644
--- a/docs/history/index.rst
+++ b/docs/history/index.rst
@@ -8,15 +8,14 @@ Release History
.. seealso::
- **For the latest release:** see :ref:`What's New <whats-new>` in Passlib 1.7
+ **For the latest release:** see :ref:`What's New <whats-new>` in Passlib 1.8
.. rst-class:: float-center without-title
-.. warning::
+.. toctree::
+ :maxdepth: 2
- **Passlib 1.8 will drop support for Python 2.x, 3.3, and 3.4**;
- and will require Python >= 3.5. The 1.7 series will be the last
- to support Python 2.7. (See :issue:`119` for rationale).
+ 1.8 Series <1.8>
.. toctree::
:maxdepth: 2
diff --git a/docs/index.rst b/docs/index.rst
index 45e63ba..20f0604 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -5,7 +5,7 @@
.. rst-class:: float-right
-.. seealso:: :ref:`What's new in Passlib 1.7.4 <whats-new>`
+.. seealso:: :ref:`What's new in Passlib 1.8 <whats-new>`
==========================================
Passlib |release| documentation
@@ -33,7 +33,7 @@ Passlib |release| documentation
Welcome
=======
-Passlib is a password hashing library for Python 2 & 3, which provides
+Passlib is a password hashing library for Python 3, which provides
cross-platform implementations of over 30 password hashing algorithms, as well
as a framework for managing existing password hashes. It's designed to be useful
for a wide range of tasks, from verifying a hash found in /etc/shadow, to
diff --git a/docs/install.rst b/docs/install.rst
index 2ebe119..f48af45 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -6,27 +6,28 @@ Installation
Supported Platforms
===================
-Passlib requires Python 2 (>= 2.6) or Python 3 (>= 3.3).
+Passlib requires Python 3.5 or newer.
It is known to work with the following Python implementations:
.. rst-class:: float-right without-title
.. warning::
- **Passlib 1.8 will drop support for Python 2.x, 3.3, and 3.4**;
- and will require Python >= 3.5. The 1.7 series will be the
- last to support Python 2. (See :issue:`119` for rationale).
+ **Passlib 1.8 dropped support for Python 2.x, 3.3, and 3.4**;
+ If you need support for Python 2.6 - 3.4, use the Passlib 1.7.x series.
-* CPython 2 -- v2.6 or newer.
-* CPython 3 -- v3.3 or newer.
-* PyPy -- v2.0 or newer.
+* CPython 3 -- v3.5 or newer.
* PyPy3 -- v5.3 or newer.
-* Jython -- v2.7 or newer.
Passlib should work with all operating systems and environments,
as it contains builtin fallbacks for almost all OS-dependant features.
Google App Engine is supported as well.
+.. versionchanged:: 1.8
+
+ Support for Python 2.x, 3.3, and 3.4 was dropped.
+ Jython no longer listed, until it has a Python 3 compatible for testing.
+
.. versionchanged:: 1.7
Support for Python 2.5, 3.0-3.2 was dropped.
@@ -36,25 +37,12 @@ Google App Engine is supported as well.
Optional Libraries
==================
-* `bcrypt <https://pypi.python.org/pypi/bcrypt>`_,
- `py-bcrypt <https://pypi.python.org/pypi/py-bcrypt>`_, or
- `bcryptor <https://bitbucket.org/ares/bcryptor/overview>`_
-
- .. rst-class:: float-right
+* `bcrypt <https://pypi.python.org/pypi/bcrypt>`_
- .. warning::
-
- Support for ``py-bcrypt`` and ``bcryptor`` will be dropped in Passlib 1.8,
- as these libraries are unmaintained.
-
- If any of these packages are installed, they will be used to provide
- support for the BCrypt hash algorithm.
- This is required if you want to handle BCrypt hashes,
- and your OS does not provide native BCrypt support
- via stdlib's :mod:`!crypt` (which includes pretty much all non-BSD systems).
-
- `bcrypt <https://pypi.python.org/pypi/bcrypt>`_ is currently the recommended
- option -- it's actively maintained, and compatible with both CPython and PyPy.
+ If installed, this will be used to handle :class:~passlib.hash.bcrypt` and
+ :class:`~passlib.hash.bcrypt_sha256` hashes. If your system lacks :func:`crypt.crypt()`
+ support for bcrypt hashes, this library is *required* in order for passlib to provide
+ bcrypt support.
Use ``pip install passlib[bcrypt]`` to get the recommended bcrypt setup.
@@ -85,6 +73,10 @@ Optional Libraries
If installed, this will be used to provide support for the :class:`~passlib.hash.scrypt`
hash algorithm. If not installed, a MUCH slower builtin reference implementation will be used.
+.. versionchanged:: 1.8
+
+ Dropped support for ``py-bcrypt`` and ``bcryptor`` backends.
+
.. versionchanged:: 1.7
Added fastpbkdf2, cryptography, argon2_cffi, argon2pure, and scrypt support.
@@ -129,8 +121,7 @@ algorithms using multiple external sources (if detected at runtime).
All unit tests are contained within the :mod:`passlib.tests` subpackage,
and are designed to be run using the
-`Nose <http://somethingaboutorange.com/mrl/projects/nose>`_ unit testing library
-(as well as the ``unittest2`` library under Python 2.6).
+`Nose <http://somethingaboutorange.com/mrl/projects/nose>`_ unit testing library.
Once Passlib and Nose have been installed, the main suite of tests may be run using::
diff --git a/docs/lib/passlib.apache.rst b/docs/lib/passlib.apache.rst
index b87eb63..969ead0 100644
--- a/docs/lib/passlib.apache.rst
+++ b/docs/lib/passlib.apache.rst
@@ -19,6 +19,10 @@ htpasswd and htdigest files; though the use of two helper classes.
These classes will now preserve blank lines and "#" comments when updating
htpasswd files; previous releases would throw a parse error.
+.. versionchanged:: 1.8
+
+ A number of methods deprecated since 1.6, have been removed.
+
.. index:: Apache; htpasswd
Htpasswd Files
diff --git a/docs/lib/passlib.context.rst b/docs/lib/passlib.context.rst
index 5ed7413..bec6f7e 100644
--- a/docs/lib/passlib.context.rst
+++ b/docs/lib/passlib.context.rst
@@ -166,36 +166,6 @@ Options which directly affect the behavior of the CryptContext instance:
.. versionadded:: 1.7
-.. _context-min-verify-time-option:
-
-``min_verify_time``
-
- If specified, unsuccessful :meth:`~CryptContext.verify`
- calls will be penalized, and take at least this may
- seconds before the method returns. May be an integer
- or fractional number of seconds.
-
- .. deprecated:: 1.6
- This option has not proved very useful, is ignored by 1.7,
- and will be removed in version 1.8.
-
- .. versionchanged:: 1.7
-
- Per deprecation roadmap above, this option is now ignored.
-
-.. _context-harden-verify-option:
-
-``harden_verify``
-
- Companion to ``min_verify_time``, currently ignored.
-
- .. versionadded:: 1.7
-
- .. deprecated:: 1.7.1
-
- This option is ignored by 1.7.1, and will be removed in 1.8
- along with ``min_verify_time``.
-
.. _context-algorithm-options:
Algorithm Options
@@ -537,9 +507,3 @@ if any invalid-but-correctable values are encountered
Other Helpers
=============
.. autoclass:: LazyCryptContext([schemes=None,] \*\*kwds [, onload=None])
-
-.. rst-class:: html-toggle
-
-The CryptPolicy Class (deprecated)
-==================================
-.. autoclass:: CryptPolicy
diff --git a/docs/lib/passlib.hash.bcrypt.rst b/docs/lib/passlib.hash.bcrypt.rst
index 436148c..3bb5865 100644
--- a/docs/lib/passlib.hash.bcrypt.rst
+++ b/docs/lib/passlib.hash.bcrypt.rst
@@ -50,17 +50,10 @@ Bcrypt Backends
.. rst-class:: float-center
-.. warning::
-
- Support for ``py-bcrypt`` and ``bcryptor`` will be dropped in Passlib 1.8,
- as these libraries are unmaintained.
-
This class will use the first available of five possible backends:
1. `bcrypt <https://pypi.python.org/pypi/bcrypt>`_, if installed.
-2. `py-bcrypt <https://pypi.python.org/pypi/py-bcrypt>`_, if installed (DEPRECATED)
-3. `bcryptor <https://bitbucket.org/ares/bcryptor/overview>`_, if installed (DEPRECATED).
4. stdlib's :func:`crypt.crypt()`, if the host OS supports BCrypt
(primarily BSD-derived systems).
5. A pure-python implementation of BCrypt, built into Passlib.
diff --git a/docs/lib/passlib.hash.unix_disabled.rst b/docs/lib/passlib.hash.unix_disabled.rst
index 8bdf9c3..2c68144 100644
--- a/docs/lib/passlib.hash.unix_disabled.rst
+++ b/docs/lib/passlib.hash.unix_disabled.rst
@@ -36,10 +36,6 @@ Interface
=========
.. autoclass:: unix_disabled()
-Deprecated Interface
-====================
-.. autoclass:: unix_fallback()
-
Deviations
==========
According to the Linux ``shadow`` man page, an empty string is treated
diff --git a/docs/lib/passlib.utils.pbkdf2.rst b/docs/lib/passlib.utils.pbkdf2.rst
index cfc503a..a72628a 100644
--- a/docs/lib/passlib.utils.pbkdf2.rst
+++ b/docs/lib/passlib.utils.pbkdf2.rst
@@ -32,6 +32,4 @@ PKCS#5 Key Derivation Functions
Helper Functions
================
-.. autofunction:: norm_hash_name
-
.. autofunction:: get_prf
diff --git a/passlib/__init__.py b/passlib/__init__.py
index 963bfcc..7b9e2cd 100644
--- a/passlib/__init__.py
+++ b/passlib/__init__.py
@@ -1,3 +1,3 @@
"""passlib - suite of password hashing & generation routines"""
-__version__ = '1.7.4'
+__version__ = '1.8.0.dev0'
diff --git a/passlib/_setup/stamp.py b/passlib/_setup/stamp.py
index f14c0d4..ebf6a11 100644
--- a/passlib/_setup/stamp.py
+++ b/passlib/_setup/stamp.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import absolute_import, division, print_function
# core
import datetime
from distutils.dist import Distribution
diff --git a/passlib/apache.py b/passlib/apache.py
index a75f2cf..80a8a91 100644
--- a/passlib/apache.py
+++ b/passlib/apache.py
@@ -3,8 +3,8 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
+from io import BytesIO
import logging; log = logging.getLogger(__name__)
import os
from warnings import warn
@@ -15,8 +15,7 @@ from passlib.context import CryptContext
from passlib.exc import ExpectedStringError
from passlib.hash import htdigest
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
+from passlib.utils.compat import join_bytes
# local
__all__ = [
'HtpasswdFile',
@@ -50,9 +49,9 @@ class _CommonFile(object):
# charset encoding used by file (defaults to utf-8)
encoding = None
- # whether users() and other public methods should return unicode or bytes?
- # (defaults to False under PY2, True under PY3)
- return_unicode = None
+ # whether users() and other public methods should return str or bytes?
+ # (defaults to True)
+ return_unicode = True
# if bound to local file, these will be set.
_path = None # local file path
@@ -76,7 +75,7 @@ class _CommonFile(object):
def from_string(cls, data, **kwds):
"""create new object from raw string.
- :type data: unicode or bytes
+ :type data: str or bytes
:arg data:
database to load, as single string.
@@ -107,17 +106,14 @@ class _CommonFile(object):
#===================================================================
# init
#===================================================================
- def __init__(self, path=None, new=False, autoload=True, autosave=False,
- encoding="utf-8", return_unicode=PY3,
+ # XXX: add a new() classmethod, ala TOTP.new()?
+
+ def __init__(self, path=None, new=False, autosave=False,
+ encoding="utf-8", return_unicode=True,
):
# set encoding
if not encoding:
- warn("``encoding=None`` is deprecated as of Passlib 1.6, "
- "and will cause a ValueError in Passlib 1.8, "
- "use ``return_unicode=False`` instead.",
- DeprecationWarning, stacklevel=2)
- encoding = "utf-8"
- return_unicode = False
+ raise TypeError("'encoding' is required")
elif not is_ascii_codec(encoding):
# htpasswd/htdigest files assumes 1-byte chars, and use ":" separator,
# so only ascii-compatible encodings are allowed.
@@ -131,11 +127,6 @@ class _CommonFile(object):
self._mtime = 0
# init db
- if not autoload:
- warn("``autoload=False`` is deprecated as of Passlib 1.6, "
- "and will be removed in Passlib 1.8, use ``new=True`` instead",
- DeprecationWarning, stacklevel=2)
- new = True
if path and not new:
self.load()
else:
@@ -181,33 +172,17 @@ class _CommonFile(object):
self.load()
return True
- def load(self, path=None, force=True):
+ def load(self, path=None):
"""Load state from local file.
If no path is specified, attempts to load from ``self.path``.
:type path: str
:arg path: local file to load from
-
- :type force: bool
- :param force:
- if ``force=False``, only load from ``self.path`` if file
- has changed since last load.
-
- .. deprecated:: 1.6
- This keyword will be removed in Passlib 1.8;
- Applications should use :meth:`load_if_changed` instead.
"""
if path is not None:
with open(path, "rb") as fh:
self._mtime = 0
self._load_lines(fh)
- elif not force:
- warn("%(name)s.load(force=False) is deprecated as of Passlib 1.6,"
- "and will be removed in Passlib 1.8; "
- "use %(name)s.load_if_changed() instead." %
- dict(name=self.__class__.__name__),
- DeprecationWarning, stacklevel=2)
- return self.load_if_changed()
elif self._path:
with open(self._path, "rb") as fh:
self._mtime = os.path.getmtime(self._path)
@@ -376,7 +351,7 @@ class _CommonFile(object):
:returns:
encoded identifer as bytes
"""
- if isinstance(value, unicode):
+ if isinstance(value, str):
value = value.encode(self.encoding)
elif not isinstance(value, bytes):
raise ExpectedStringError(value, param)
@@ -397,7 +372,7 @@ class _CommonFile(object):
(usually indicates wrong encoding set for file).
:returns:
- field as unicode or bytes, as appropriate.
+ field as str or bytes, as appropriate.
"""
assert isinstance(value, bytes), "expected value to be bytes"
if self.return_unicode:
@@ -563,7 +538,7 @@ class HtpasswdFile(_CommonFile):
.. versionadded:: 1.6
This feature was previously enabled by setting ``autoload=False``.
- That alias has been deprecated, and will be removed in Passlib 1.8
+ That alias was removed in Passlib 1.8
:type autosave: bool
:param autosave:
@@ -615,7 +590,7 @@ class HtpasswdFile(_CommonFile):
.. versionadded:: 1.6
This keyword was previously named ``default``. That alias
- has been deprecated, and will be removed in Passlib 1.8.
+ was removed in Passlib 1.8.
.. versionchanged:: 1.6.3
@@ -641,23 +616,6 @@ class HtpasswdFile(_CommonFile):
will probably not be usable by another application,
and particularly not by Apache.
- :param autoload:
- Set to ``False`` to prevent the constructor from automatically
- loaded the file from disk.
-
- .. deprecated:: 1.6
- This has been replaced by the *new* keyword.
- Instead of setting ``autoload=False``, you should use
- ``new=True``. Support for this keyword will be removed
- in Passlib 1.8.
-
- :param default:
- Change the default algorithm used to hash new passwords.
-
- .. deprecated:: 1.6
- This has been renamed to *default_scheme* for clarity.
- Support for this alias will be removed in Passlib 1.8.
-
Loading & Saving
================
.. automethod:: load
@@ -713,12 +671,6 @@ class HtpasswdFile(_CommonFile):
#===================================================================
def __init__(self, path=None, default_scheme=None, context=htpasswd_context,
**kwds):
- if 'default' in kwds:
- warn("``default`` is deprecated as of Passlib 1.6, "
- "and will be removed in Passlib 1.8, it has been renamed "
- "to ``default_scheem``.",
- DeprecationWarning, stacklevel=2)
- default_scheme = kwds.pop("default")
if default_scheme:
if default_scheme in _warn_no_bcrypt:
warn("HtpasswdFile: no bcrypt backends available, "
@@ -727,7 +679,7 @@ class HtpasswdFile(_CommonFile):
default_scheme = htpasswd_defaults.get(default_scheme, default_scheme)
context = context.copy(default=default_scheme)
self.context = context
- super(HtpasswdFile, self).__init__(path, **kwds)
+ super().__init__(path, **kwds)
def _parse_record(self, record, lineno):
# NOTE: should return (user, hash) tuple
@@ -772,24 +724,17 @@ class HtpasswdFile(_CommonFile):
.. versionchanged:: 1.6
This method was previously called ``update``, it was renamed
to prevent ambiguity with the dictionary method.
- The old alias is deprecated, and will be removed in Passlib 1.8.
+ The old alias was removed in Passlib 1.8.
"""
hash = self.context.hash(password)
return self.set_hash(user, hash)
- @deprecated_method(deprecated="1.6", removed="1.8",
- replacement="set_password")
- def update(self, user, password):
- """set password for user"""
- return self.set_password(user, password)
-
def get_hash(self, user):
"""Return hash stored for user, or ``None`` if user not found.
.. versionchanged:: 1.6
This method was previously named ``find``, it was renamed
- for clarity. The old name is deprecated, and will be removed
- in Passlib 1.8.
+ for clarity. The old name was removed in Passlib 1.8.
"""
try:
return self._records[self._encode_user(user)]
@@ -807,19 +752,13 @@ class HtpasswdFile(_CommonFile):
.. versionadded:: 1.7
"""
# assert self.context.identify(hash), "unrecognized hash format"
- if PY3 and isinstance(hash, str):
+ if isinstance(hash, str):
hash = hash.encode(self.encoding)
user = self._encode_user(user)
existing = self._set_record(user, hash)
self._autosave()
return existing
- @deprecated_method(deprecated="1.6", removed="1.8",
- replacement="get_hash")
- def find(self, user):
- """return hash for user"""
- return self.get_hash(user)
-
# XXX: rename to something more explicit, like delete_user()?
def delete(self, user):
"""Delete user's entry.
@@ -848,13 +787,13 @@ class HtpasswdFile(_CommonFile):
.. versionchanged:: 1.6
This method was previously called ``verify``, it was renamed
to prevent ambiguity with the :class:`!CryptContext` method.
- The old alias is deprecated, and will be removed in Passlib 1.8.
+ The old alias was removed in Passlib 1.8.
"""
user = self._encode_user(user)
hash = self._records.get(user)
if hash is None:
return None
- if isinstance(password, unicode):
+ if isinstance(password, str):
# NOTE: encoding password to match file, making the assumption
# that server will use same encoding to hash the password.
password = password.encode(self.encoding)
@@ -866,12 +805,6 @@ class HtpasswdFile(_CommonFile):
self._autosave()
return ok
- @deprecated_method(deprecated="1.6", removed="1.8",
- replacement="check_password")
- def verify(self, user, password):
- """verify password for user"""
- return self.check_password(user, password)
-
#===================================================================
# eoc
#===================================================================
@@ -928,7 +861,7 @@ class HtdigestFile(_CommonFile):
.. versionadded:: 1.6
This feature was previously enabled by setting ``autoload=False``.
- That alias has been deprecated, and will be removed in Passlib 1.8
+ That alias was removed in Passlib 1.8
:type autosave: bool
:param autosave:
@@ -949,16 +882,6 @@ class HtdigestFile(_CommonFile):
This is also exposed as a readonly instance attribute.
- :param autoload:
- Set to ``False`` to prevent the constructor from automatically
- loaded the file from disk.
-
- .. deprecated:: 1.6
- This has been replaced by the *new* keyword.
- Instead of setting ``autoload=False``, you should use
- ``new=True``. Support for this keyword will be removed
- in Passlib 1.8.
-
Loading & Saving
================
.. automethod:: load
@@ -1030,7 +953,7 @@ class HtdigestFile(_CommonFile):
#===================================================================
def __init__(self, path=None, default_realm=None, **kwds):
self.default_realm = default_realm
- super(HtdigestFile, self).__init__(path, **kwds)
+ super().__init__(path, **kwds)
def _parse_record(self, record, lineno):
result = record.rstrip().split(_BCOLON)
@@ -1121,12 +1044,6 @@ class HtdigestFile(_CommonFile):
hash = htdigest.hash(password, user, realm, encoding=self.encoding)
return self.set_hash(user, realm, hash)
- @deprecated_method(deprecated="1.6", removed="1.8",
- replacement="set_password")
- def update(self, user, realm, password):
- """set password for user"""
- return self.set_password(user, realm, password)
-
def get_hash(self, user, realm=None):
"""Return :class:`~passlib.hash.htdigest` hash stored for user.
@@ -1135,15 +1052,13 @@ class HtdigestFile(_CommonFile):
.. versionchanged:: 1.6
This method was previously named ``find``, it was renamed
- for clarity. The old name is deprecated, and will be removed
- in Passlib 1.8.
+ for clarity. The old name is was removed Passlib 1.8.
"""
key = self._encode_key(user, realm)
hash = self._records.get(key)
if hash is None:
return None
- if PY3:
- hash = hash.decode(self.encoding)
+ hash = hash.decode(self.encoding)
return hash
def set_hash(self, user, realm=None, hash=_UNSET):
@@ -1165,19 +1080,13 @@ class HtdigestFile(_CommonFile):
# called w/ two args - (user, hash), use default realm
realm, hash = None, realm
# assert htdigest.identify(hash), "unrecognized hash format"
- if PY3 and isinstance(hash, str):
+ if isinstance(hash, str):
hash = hash.encode(self.encoding)
key = self._encode_key(user, realm)
existing = self._set_record(key, hash)
self._autosave()
return existing
- @deprecated_method(deprecated="1.6", removed="1.8",
- replacement="get_hash")
- def find(self, user, realm):
- """return hash for user"""
- return self.get_hash(user, realm)
-
# XXX: rename to something more explicit, like delete_user()?
def delete(self, user, realm=None):
"""Delete user's entry for specified realm.
@@ -1227,7 +1136,7 @@ class HtdigestFile(_CommonFile):
.. versionchanged:: 1.6
This method was previously called ``verify``, it was renamed
to prevent ambiguity with the :class:`!CryptContext` method.
- The old alias is deprecated, and will be removed in Passlib 1.8.
+ The old alias was removed in Passlib 1.8.
"""
if password is _UNSET:
# called w/ two args - (user, password), use default realm
@@ -1240,12 +1149,6 @@ class HtdigestFile(_CommonFile):
return htdigest.verify(password, hash, user, realm,
encoding=self.encoding)
- @deprecated_method(deprecated="1.6", removed="1.8",
- replacement="check_password")
- def verify(self, user, realm, password):
- """verify password for user"""
- return self.check_password(user, realm, password)
-
#===================================================================
# eoc
#===================================================================
diff --git a/passlib/apps.py b/passlib/apps.py
index 682bbff..e8a2555 100644
--- a/passlib/apps.py
+++ b/passlib/apps.py
@@ -56,7 +56,6 @@ def _load_master_config():
# disabled handlers
'django_disabled',
'unix_disabled',
- 'unix_fallback',
]
for name in excluded:
schemes.remove(name)
diff --git a/passlib/context.py b/passlib/context.py
index bc3cbf5..21865a0 100644
--- a/passlib/context.py
+++ b/passlib/context.py
@@ -2,8 +2,9 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
+from configparser import ConfigParser
+from io import StringIO
import re
import logging; log = logging.getLogger(__name__)
import threading
@@ -19,17 +20,14 @@ from passlib.utils import (handlers as uh, to_bytes,
as_bool, timer, rng, getrandstr,
)
from passlib.utils.binary import BASE64_CHARS
-from passlib.utils.compat import (iteritems, num_types, irange,
- PY2, PY3, unicode, SafeConfigParser,
- NativeStringIO, BytesIO,
- unicode_or_bytes_types, native_string_types,
+from passlib.utils.compat import (num_types,
+ unicode_or_bytes,
)
from passlib.utils.decor import deprecated_method, memoized_property
# local
__all__ = [
'CryptContext',
'LazyCryptContext',
- 'CryptPolicy',
]
#=============================================================================
@@ -80,508 +78,6 @@ def _always_needs_update(hash, secret=None):
_global_settings = set(["truncate_error", "vary_rounds"])
#=============================================================================
-# crypt policy
-#=============================================================================
-_preamble = ("The CryptPolicy class has been deprecated as of "
- "Passlib 1.6, and will be removed in Passlib 1.8. ")
-
-class CryptPolicy(object):
- """
- .. deprecated:: 1.6
- This class has been deprecated, and will be removed in Passlib 1.8.
- All of its functionality has been rolled into :class:`CryptContext`.
-
- This class previously stored the configuration options for the
- CryptContext class. In the interest of interface simplification,
- all of this class' functionality has been rolled into the CryptContext
- class itself.
- The documentation for this class is now focused on documenting how to
- migrate to the new api. Additionally, where possible, the deprecation
- warnings issued by the CryptPolicy methods will list the replacement call
- that should be used.
-
- Constructors
- ============
- CryptPolicy objects can be constructed directly using any of
- the keywords accepted by :class:`CryptContext`. Direct uses of the
- :class:`!CryptPolicy` constructor should either pass the keywords
- directly into the CryptContext constructor, or to :meth:`CryptContext.update`
- if the policy object was being used to update an existing context object.
-
- In addition to passing in keywords directly,
- CryptPolicy objects can be constructed by the following methods:
-
- .. automethod:: from_path
- .. automethod:: from_string
- .. automethod:: from_source
- .. automethod:: from_sources
- .. automethod:: replace
-
- Introspection
- =============
- All of the informational methods provided by this class have been deprecated
- by identical or similar methods in the :class:`CryptContext` class:
-
- .. automethod:: has_schemes
- .. automethod:: schemes
- .. automethod:: iter_handlers
- .. automethod:: get_handler
- .. automethod:: get_options
- .. automethod:: handler_is_deprecated
- .. automethod:: get_min_verify_time
-
- Exporting
- =========
- .. automethod:: iter_config
- .. automethod:: to_dict
- .. automethod:: to_file
- .. automethod:: to_string
-
- .. note::
- CryptPolicy are immutable.
- Use the :meth:`replace` method to mutate existing instances.
-
- .. deprecated:: 1.6
- """
- #===================================================================
- # class methods
- #===================================================================
- @classmethod
- def from_path(cls, path, section="passlib", encoding="utf-8"):
- """create a CryptPolicy instance from a local file.
-
- .. deprecated:: 1.6
-
- Creating a new CryptContext from a file, which was previously done via
- ``CryptContext(policy=CryptPolicy.from_path(path))``, can now be
- done via ``CryptContext.from_path(path)``.
- See :meth:`CryptContext.from_path` for details.
-
- Updating an existing CryptContext from a file, which was previously done
- ``context.policy = CryptPolicy.from_path(path)``, can now be
- done via ``context.load_path(path)``.
- See :meth:`CryptContext.load_path` for details.
- """
- warn(_preamble +
- "Instead of ``CryptPolicy.from_path(path)``, "
- "use ``CryptContext.from_path(path)`` "
- " or ``context.load_path(path)`` for an existing CryptContext.",
- DeprecationWarning, stacklevel=2)
- return cls(_internal_context=CryptContext.from_path(path, section,
- encoding))
-
- @classmethod
- def from_string(cls, source, section="passlib", encoding="utf-8"):
- """create a CryptPolicy instance from a string.
-
- .. deprecated:: 1.6
-
- Creating a new CryptContext from a string, which was previously done via
- ``CryptContext(policy=CryptPolicy.from_string(data))``, can now be
- done via ``CryptContext.from_string(data)``.
- See :meth:`CryptContext.from_string` for details.
-
- Updating an existing CryptContext from a string, which was previously done
- ``context.policy = CryptPolicy.from_string(data)``, can now be
- done via ``context.load(data)``.
- See :meth:`CryptContext.load` for details.
- """
- warn(_preamble +
- "Instead of ``CryptPolicy.from_string(source)``, "
- "use ``CryptContext.from_string(source)`` or "
- "``context.load(source)`` for an existing CryptContext.",
- DeprecationWarning, stacklevel=2)
- return cls(_internal_context=CryptContext.from_string(source, section,
- encoding))
-
- @classmethod
- def from_source(cls, source, _warn=True):
- """create a CryptPolicy instance from some source.
-
- this method autodetects the source type, and invokes
- the appropriate constructor automatically. it attempts
- to detect whether the source is a configuration string, a filepath,
- a dictionary, or an existing CryptPolicy instance.
-
- .. deprecated:: 1.6
-
- Create a new CryptContext, which could previously be done via
- ``CryptContext(policy=CryptPolicy.from_source(source))``, should
- now be done using an explicit method: the :class:`CryptContext`
- constructor itself, :meth:`CryptContext.from_path`,
- or :meth:`CryptContext.from_string`.
-
- Updating an existing CryptContext, which could previously be done via
- ``context.policy = CryptPolicy.from_source(source)``, should
- now be done using an explicit method: :meth:`CryptContext.update`,
- or :meth:`CryptContext.load`.
- """
- if _warn:
- warn(_preamble +
- "Instead of ``CryptPolicy.from_source()``, "
- "use ``CryptContext.from_string(path)`` "
- " or ``CryptContext.from_path(source)``, as appropriate.",
- DeprecationWarning, stacklevel=2)
- if isinstance(source, CryptPolicy):
- return source
- elif isinstance(source, dict):
- return cls(_internal_context=CryptContext(**source))
- elif not isinstance(source, (bytes,unicode)):
- raise TypeError("source must be CryptPolicy, dict, config string, "
- "or file path: %r" % (type(source),))
- elif any(c in source for c in "\n\r\t") or not source.strip(" \t./;:"):
- return cls(_internal_context=CryptContext.from_string(source))
- else:
- return cls(_internal_context=CryptContext.from_path(source))
-
- @classmethod
- def from_sources(cls, sources, _warn=True):
- """create a CryptPolicy instance by merging multiple sources.
-
- each source is interpreted as by :meth:`from_source`,
- and the results are merged together.
-
- .. deprecated:: 1.6
- Instead of using this method to merge multiple policies together,
- a :class:`CryptContext` instance should be created, and then
- the multiple sources merged together via :meth:`CryptContext.load`.
- """
- if _warn:
- warn(_preamble +
- "Instead of ``CryptPolicy.from_sources()``, "
- "use the various CryptContext constructors "
- " followed by ``context.update()``.",
- DeprecationWarning, stacklevel=2)
- if len(sources) == 0:
- raise ValueError("no sources specified")
- if len(sources) == 1:
- return cls.from_source(sources[0], _warn=False)
- kwds = {}
- for source in sources:
- kwds.update(cls.from_source(source, _warn=False)._context.to_dict(resolve=True))
- return cls(_internal_context=CryptContext(**kwds))
-
- def replace(self, *args, **kwds):
- """create a new CryptPolicy, optionally updating parts of the
- existing configuration.
-
- .. deprecated:: 1.6
- Callers of this method should :meth:`CryptContext.update` or
- :meth:`CryptContext.copy` instead.
- """
- if self._stub_policy:
- warn(_preamble + # pragma: no cover -- deprecated & unused
- "Instead of ``context.policy.replace()``, "
- "use ``context.update()`` or ``context.copy()``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().replace()``, "
- "create a CryptContext instance and "
- "use ``context.update()`` or ``context.copy()``.",
- DeprecationWarning, stacklevel=2)
- sources = [ self ]
- if args:
- sources.extend(args)
- if kwds:
- sources.append(kwds)
- return CryptPolicy.from_sources(sources, _warn=False)
-
- #===================================================================
- # instance attrs
- #===================================================================
-
- # internal CryptContext we're wrapping to handle everything
- # until this class is removed.
- _context = None
-
- # flag indicating this is wrapper generated by the CryptContext.policy
- # attribute, rather than one created independantly by the application.
- _stub_policy = False
-
- #===================================================================
- # init
- #===================================================================
- def __init__(self, *args, **kwds):
- context = kwds.pop("_internal_context", None)
- if context:
- assert isinstance(context, CryptContext)
- self._context = context
- self._stub_policy = kwds.pop("_stub_policy", False)
- assert not (args or kwds), "unexpected args: %r %r" % (args,kwds)
- else:
- if args:
- if len(args) != 1:
- raise TypeError("only one positional argument accepted")
- if kwds:
- raise TypeError("cannot specify positional arg and kwds")
- kwds = args[0]
- warn(_preamble +
- "Instead of constructing a CryptPolicy instance, "
- "create a CryptContext directly, or use ``context.update()`` "
- "and ``context.load()`` to reconfigure existing CryptContext "
- "instances.",
- DeprecationWarning, stacklevel=2)
- self._context = CryptContext(**kwds)
-
- #===================================================================
- # public interface for examining options
- #===================================================================
- def has_schemes(self):
- """return True if policy defines *any* schemes for use.
-
- .. deprecated:: 1.6
- applications should use ``bool(context.schemes())`` instead.
- see :meth:`CryptContext.schemes`.
- """
- if self._stub_policy:
- warn(_preamble + # pragma: no cover -- deprecated & unused
- "Instead of ``context.policy.has_schemes()``, "
- "use ``bool(context.schemes())``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().has_schemes()``, "
- "create a CryptContext instance and "
- "use ``bool(context.schemes())``.",
- DeprecationWarning, stacklevel=2)
- return bool(self._context.schemes())
-
- def iter_handlers(self):
- """return iterator over handlers defined in policy.
-
- .. deprecated:: 1.6
- applications should use ``context.schemes(resolve=True))`` instead.
- see :meth:`CryptContext.schemes`.
- """
- if self._stub_policy:
- warn(_preamble +
- "Instead of ``context.policy.iter_handlers()``, "
- "use ``context.schemes(resolve=True)``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().iter_handlers()``, "
- "create a CryptContext instance and "
- "use ``context.schemes(resolve=True)``.",
- DeprecationWarning, stacklevel=2)
- return self._context.schemes(resolve=True, unconfigured=True)
-
- def schemes(self, resolve=False):
- """return list of schemes defined in policy.
-
- .. deprecated:: 1.6
- applications should use :meth:`CryptContext.schemes` instead.
- """
- if self._stub_policy:
- warn(_preamble + # pragma: no cover -- deprecated & unused
- "Instead of ``context.policy.schemes()``, "
- "use ``context.schemes()``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().schemes()``, "
- "create a CryptContext instance and "
- "use ``context.schemes()``.",
- DeprecationWarning, stacklevel=2)
- return list(self._context.schemes(resolve=resolve, unconfigured=True))
-
- def get_handler(self, name=None, category=None, required=False):
- """return handler as specified by name, or default handler.
-
- .. deprecated:: 1.6
- applications should use :meth:`CryptContext.handler` instead,
- though note that the ``required`` keyword has been removed,
- and the new method will always act as if ``required=True``.
- """
- if self._stub_policy:
- warn(_preamble +
- "Instead of ``context.policy.get_handler()``, "
- "use ``context.handler()``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().get_handler()``, "
- "create a CryptContext instance and "
- "use ``context.handler()``.",
- DeprecationWarning, stacklevel=2)
- # CryptContext.handler() doesn't support required=False,
- # so wrapping it in try/except
- try:
- return self._context.handler(name, category, unconfigured=True)
- except KeyError:
- if required:
- raise
- else:
- return None
-
- def get_min_verify_time(self, category=None):
- """get min_verify_time setting for policy.
-
- .. deprecated:: 1.6
- min_verify_time option will be removed entirely in passlib 1.8
-
- .. versionchanged:: 1.7
- this method now always returns the value automatically
- calculated by :meth:`CryptContext.min_verify_time`,
- any value specified by policy is ignored.
- """
- warn("get_min_verify_time() and min_verify_time option is deprecated and ignored, "
- "and will be removed in Passlib 1.8", DeprecationWarning,
- stacklevel=2)
- return 0
-
- def get_options(self, name, category=None):
- """return dictionary of options specific to a given handler.
-
- .. deprecated:: 1.6
- this method has no direct replacement in the 1.6 api, as there
- is not a clearly defined use-case. however, examining the output of
- :meth:`CryptContext.to_dict` should serve as the closest alternative.
- """
- # XXX: might make a public replacement, but need more study of the use cases.
- if self._stub_policy:
- warn(_preamble + # pragma: no cover -- deprecated & unused
- "``context.policy.get_options()`` will no longer be available.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "``CryptPolicy().get_options()`` will no longer be available.",
- DeprecationWarning, stacklevel=2)
- if hasattr(name, "name"):
- name = name.name
- return self._context._config._get_record_options_with_flag(name, category)[0]
-
- def handler_is_deprecated(self, name, category=None):
- """check if handler has been deprecated by policy.
-
- .. deprecated:: 1.6
- this method has no direct replacement in the 1.6 api, as there
- is not a clearly defined use-case. however, examining the output of
- :meth:`CryptContext.to_dict` should serve as the closest alternative.
- """
- # XXX: might make a public replacement, but need more study of the use cases.
- if self._stub_policy:
- warn(_preamble +
- "``context.policy.handler_is_deprecated()`` will no longer be available.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "``CryptPolicy().handler_is_deprecated()`` will no longer be available.",
- DeprecationWarning, stacklevel=2)
- if hasattr(name, "name"):
- name = name.name
- return self._context.handler(name, category).deprecated
-
- #===================================================================
- # serialization
- #===================================================================
-
- def iter_config(self, ini=False, resolve=False):
- """iterate over key/value pairs representing the policy object.
-
- .. deprecated:: 1.6
- applications should use :meth:`CryptContext.to_dict` instead.
- """
- if self._stub_policy:
- warn(_preamble + # pragma: no cover -- deprecated & unused
- "Instead of ``context.policy.iter_config()``, "
- "use ``context.to_dict().items()``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().iter_config()``, "
- "create a CryptContext instance and "
- "use ``context.to_dict().items()``.",
- DeprecationWarning, stacklevel=2)
- # hacked code that renders keys & values in manner that approximates
- # old behavior. context.to_dict() is much cleaner.
- context = self._context
- if ini:
- def render_key(key):
- return context._render_config_key(key).replace("__", ".")
- def render_value(value):
- if isinstance(value, (list,tuple)):
- value = ", ".join(value)
- return value
- resolve = False
- else:
- render_key = context._render_config_key
- render_value = lambda value: value
- return (
- (render_key(key), render_value(value))
- for key, value in context._config.iter_config(resolve)
- )
-
- def to_dict(self, resolve=False):
- """export policy object as dictionary of options.
-
- .. deprecated:: 1.6
- applications should use :meth:`CryptContext.to_dict` instead.
- """
- if self._stub_policy:
- warn(_preamble +
- "Instead of ``context.policy.to_dict()``, "
- "use ``context.to_dict()``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().to_dict()``, "
- "create a CryptContext instance and "
- "use ``context.to_dict()``.",
- DeprecationWarning, stacklevel=2)
- return self._context.to_dict(resolve)
-
- def to_file(self, stream, section="passlib"): # pragma: no cover -- deprecated & unused
- """export policy to file.
-
- .. deprecated:: 1.6
- applications should use :meth:`CryptContext.to_string` instead,
- and then write the output to a file as desired.
- """
- if self._stub_policy:
- warn(_preamble +
- "Instead of ``context.policy.to_file(stream)``, "
- "use ``stream.write(context.to_string())``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().to_file(stream)``, "
- "create a CryptContext instance and "
- "use ``stream.write(context.to_string())``.",
- DeprecationWarning, stacklevel=2)
- out = self._context.to_string(section=section)
- if PY2:
- out = out.encode("utf-8")
- stream.write(out)
-
- def to_string(self, section="passlib", encoding=None):
- """export policy to file.
-
- .. deprecated:: 1.6
- applications should use :meth:`CryptContext.to_string` instead.
- """
- if self._stub_policy:
- warn(_preamble + # pragma: no cover -- deprecated & unused
- "Instead of ``context.policy.to_string()``, "
- "use ``context.to_string()``.",
- DeprecationWarning, stacklevel=2)
- else:
- warn(_preamble +
- "Instead of ``CryptPolicy().to_string()``, "
- "create a CryptContext instance and "
- "use ``context.to_string()``.",
- DeprecationWarning, stacklevel=2)
- out = self._context.to_string(section=section)
- if encoding:
- out = out.encode(encoding)
- return out
-
- #===================================================================
- # eoc
- #===================================================================
-
-#=============================================================================
# _CryptConfig helper class
#=============================================================================
class _CryptConfig(object):
@@ -641,7 +137,7 @@ class _CryptConfig(object):
"""initialize .handlers and .schemes attributes"""
handlers = []
schemes = []
- if isinstance(data, native_string_types):
+ if isinstance(data, str):
data = splitcomma(data)
for elem in data or ():
# resolve elem -> handler & scheme
@@ -649,7 +145,7 @@ class _CryptConfig(object):
handler = elem
scheme = handler.name
_validate_handler_name(scheme)
- elif isinstance(elem, native_string_types):
+ elif isinstance(elem, str):
handler = get_crypt_handler(elem)
scheme = handler.name
else:
@@ -687,7 +183,7 @@ class _CryptConfig(object):
categories = set()
# load source config into internal storage
- for (cat, scheme, key), value in iteritems(source):
+ for (cat, scheme, key), value in source.items():
categories.add(cat)
explicit_scheme = scheme
if not cat and not scheme and key in _global_settings:
@@ -732,8 +228,6 @@ class _CryptConfig(object):
raise KeyError("'schemes' context option is not allowed "
"per category")
key, value = norm_context_option(cat, key, value)
- if key == "min_verify_time": # ignored in 1.7, to be removed in 1.8
- continue
# store in context_options
# map structure: context_options[key][category] = value
@@ -754,7 +248,7 @@ class _CryptConfig(object):
raise KeyError("%r option not allowed in CryptContext "
"configuration" % (key,))
# coerce strings for certain fields (e.g. min_rounds uses ints)
- if isinstance(value, native_string_types):
+ if isinstance(value, str):
func = _coerce_scheme_options.get(key)
if func:
value = func(value)
@@ -765,12 +259,12 @@ class _CryptConfig(object):
if key == "default":
if hasattr(value, "name"):
value = value.name
- elif not isinstance(value, native_string_types):
+ elif not isinstance(value, str):
raise ExpectedTypeError(value, "str", "default")
if schemes and value not in schemes:
raise KeyError("default scheme not found in policy")
elif key == "deprecated":
- if isinstance(value, native_string_types):
+ if isinstance(value, str):
value = splitcomma(value)
elif not isinstance(value, (list,tuple)):
raise ExpectedTypeError(value, "str or seq", "deprecated")
@@ -783,19 +277,11 @@ class _CryptConfig(object):
elif schemes:
# make sure list of deprecated schemes is subset of configured schemes
for scheme in value:
- if not isinstance(scheme, native_string_types):
+ if not isinstance(scheme, str):
raise ExpectedTypeError(value, "str", "deprecated element")
if scheme not in schemes:
raise KeyError("deprecated scheme not found "
"in policy: %r" % (scheme,))
- elif key == "min_verify_time":
- warn("'min_verify_time' was deprecated in Passlib 1.6, is "
- "ignored in 1.7, and will be removed in 1.8",
- DeprecationWarning)
- elif key == "harden_verify":
- warn("'harden_verify' is deprecated & ignored as of Passlib 1.7.1, "
- " and will be removed in 1.8",
- DeprecationWarning)
elif key != "schemes":
raise KeyError("unknown CryptContext keyword: %r" % (key,))
return key, value
@@ -1059,12 +545,9 @@ class _CryptConfig(object):
pass
# type check
- if category is not None and not isinstance(category, native_string_types):
- if PY2 and isinstance(category, unicode):
- # for compatibility with unicode-centric py2 apps
- return self.get_record(scheme, category.encode("utf-8"))
+ if category is not None and not isinstance(category, str):
raise ExpectedTypeError(category, "str or None", "category")
- if scheme is not None and not isinstance(scheme, native_string_types):
+ if scheme is not None and not isinstance(scheme, str):
raise ExpectedTypeError(scheme, "str or None", "scheme")
# if scheme=None,
@@ -1118,7 +601,7 @@ class _CryptConfig(object):
# unique identifiers will work properly in a CryptContext.
# XXX: if all handlers have a unique prefix (e.g. all are MCF / LDAP),
# could use dict-lookup to speed up this search.
- if not isinstance(hash, unicode_or_bytes_types):
+ if not isinstance(hash, unicode_or_bytes):
raise ExpectedStringError(hash, "hash")
# type check of category - handled by _get_record_list()
for record in self._get_record_list(category):
@@ -1255,7 +738,7 @@ class CryptContext(object):
def from_string(cls, source, section="passlib", encoding="utf-8"):
"""create new CryptContext instance from an INI-formatted string.
- :type source: unicode or bytes
+ :type source: str or bytes
:arg source:
string containing INI-formatted content.
@@ -1284,8 +767,8 @@ class CryptContext(object):
.. seealso:: :meth:`to_string`, the inverse of this constructor.
"""
- if not isinstance(source, unicode_or_bytes_types):
- raise ExpectedTypeError(source, "unicode or bytes", "source")
+ if not isinstance(source, unicode_or_bytes):
+ raise ExpectedTypeError(source, "str or bytes", "source")
self = cls(_autoload=False)
self.load(source, section=section, encoding=encoding)
return self
@@ -1346,7 +829,7 @@ class CryptContext(object):
.. versionadded:: 1.6
This method was previously named :meth:`!replace`. That alias
- has been deprecated, and will be removed in Passlib 1.8.
+ was removed in Passlib 1.8.
.. seealso:: :meth:`update`
"""
@@ -1365,100 +848,39 @@ class CryptContext(object):
"""
return self.copy(**kwds)
- def replace(self, **kwds):
- """deprecated alias of :meth:`copy`"""
- warn("CryptContext().replace() has been deprecated in Passlib 1.6, "
- "and will be removed in Passlib 1.8, "
- "it has been renamed to CryptContext().copy()",
- DeprecationWarning, stacklevel=2)
- return self.copy(**kwds)
-
#===================================================================
# init
#===================================================================
def __init__(self, schemes=None,
# keyword only...
- policy=_UNSET, # <-- deprecated
_autoload=True, **kwds):
# XXX: add ability to make flag certain contexts as immutable,
# e.g. the builtin passlib ones?
# XXX: add a name or import path for the contexts, to help out repr?
if schemes is not None:
kwds['schemes'] = schemes
- if policy is not _UNSET:
- warn("The CryptContext ``policy`` keyword has been deprecated as of Passlib 1.6, "
- "and will be removed in Passlib 1.8; please use "
- "``CryptContext.from_string()` or "
- "``CryptContext.from_path()`` instead.",
- DeprecationWarning)
- if policy is None:
- self.load(kwds)
- elif isinstance(policy, CryptPolicy):
- self.load(policy._context)
- self.update(kwds)
- else:
- raise TypeError("policy must be a CryptPolicy instance")
- elif _autoload:
+ if _autoload:
self.load(kwds)
else:
assert not kwds, "_autoload=False and kwds are mutually exclusive"
# XXX: would this be useful?
##def __str__(self):
- ## if PY3:
- ## return self.to_string()
- ## else:
- ## return self.to_string().encode("utf-8")
+ ## return self.to_string()
def __repr__(self):
return "<CryptContext at 0x%0x>" % id(self)
#===================================================================
- # deprecated policy object
- #===================================================================
- def _get_policy(self):
- # The CryptPolicy class has been deprecated, so to support any
- # legacy accesses, we create a stub policy object so .policy attr
- # will continue to work.
- #
- # the code waits until app accesses a specific policy object attribute
- # before issuing deprecation warning, so developer gets method-specific
- # suggestion for how to upgrade.
-
- # NOTE: making a copy of the context so the policy acts like a snapshot,
- # to retain the pre-1.6 behavior.
- return CryptPolicy(_internal_context=self.copy(), _stub_policy=True)
-
- def _set_policy(self, policy):
- warn("The CryptPolicy class and the ``context.policy`` attribute have "
- "been deprecated as of Passlib 1.6, and will be removed in "
- "Passlib 1.8; please use the ``context.load()`` and "
- "``context.update()`` methods instead.",
- DeprecationWarning, stacklevel=2)
- if isinstance(policy, CryptPolicy):
- self.load(policy._context)
- else:
- raise TypeError("expected CryptPolicy instance")
-
- policy = property(_get_policy, _set_policy,
- doc="[deprecated] returns CryptPolicy instance "
- "tied to this CryptContext")
-
- #===================================================================
# loading / updating configuration
#===================================================================
@staticmethod
def _parse_ini_stream(stream, section, filename):
"""helper read INI from stream, extract passlib section as dict"""
- # NOTE: this expects a unicode stream under py3,
- # and a utf-8 bytes stream under py2,
- # allowing the resulting dict to always use native strings.
- p = SafeConfigParser()
- if PY3:
- # python 3.2 deprecated readfp in favor of read_file
- p.read_file(stream, filename)
- else:
- p.readfp(stream, filename)
+ # NOTE: this expects a unicode stream,
+ # and resulting dict will always use native strings.
+ p = ConfigParser()
+ p.read_file(stream, filename)
# XXX: could change load() to accept list of items,
# and skip intermediate dict creation
return dict(p.items(section))
@@ -1474,22 +896,9 @@ class CryptContext(object):
.. versionadded:: 1.6
"""
- def helper(stream):
+ with open(path, "rt", encoding=encoding) as stream:
kwds = self._parse_ini_stream(stream, section, path)
- return self.load(kwds, update=update)
- if PY3:
- # decode to unicode, which load() expected under py3
- with open(path, "rt", encoding=encoding) as stream:
- return helper(stream)
- elif encoding in ["utf-8", "ascii"]:
- # keep as utf-8 bytes, which load() expects under py2
- with open(path, "rb") as stream:
- return helper(stream)
- else:
- # transcode to utf-8 bytes
- with open(path, "rb") as fh:
- tmp = fh.read().decode(encoding).encode("utf-8")
- return helper(BytesIO(tmp))
+ return self.load(kwds, update=update)
def load(self, source, update=False, section="passlib", encoding="utf-8"):
"""Load new configuration into CryptContext, replacing existing config.
@@ -1503,7 +912,7 @@ class CryptContext(object):
the key/value pairs will be interpreted the same
keywords for the :class:`CryptContext` class constructor.
- * a :class:`!unicode` or :class:`!bytes` string
+ * a :class:`!str` or :class:`!bytes` string
this will be interpreted as an INI-formatted file,
and appropriate key/value pairs will be loaded from
@@ -1556,13 +965,9 @@ class CryptContext(object):
# autodetect source type, convert to dict
#-----------------------------------------------------------
parse_keys = True
- if isinstance(source, unicode_or_bytes_types):
- if PY3:
- source = to_unicode(source, encoding, param="source")
- else:
- source = to_bytes(source, "utf-8", source_encoding=encoding,
- param="source")
- source = self._parse_ini_stream(NativeStringIO(source), section,
+ if isinstance(source, unicode_or_bytes):
+ source = to_unicode(source, encoding, param="source")
+ source = self._parse_ini_stream(StringIO(source), section,
"<string passed to CryptContext.load()>")
elif isinstance(source, CryptContext):
# extract dict directly from config, so it can be merged later
@@ -1580,8 +985,7 @@ class CryptContext(object):
#-----------------------------------------------------------
if parse_keys:
parse = self._parse_config_key
- source = dict((parse(key), value)
- for key, value in iteritems(source))
+ source = dict((parse(key), value) for key, value in source.items())
if update and self._config is not None:
# if updating, do nothing if source is empty,
if not source:
@@ -1610,7 +1014,7 @@ class CryptContext(object):
def _parse_config_key(ckey):
"""helper used to parse ``cat__scheme__option`` keys into a tuple"""
# split string into 1-3 parts
- assert isinstance(ckey, native_string_types)
+ assert isinstance(ckey, str)
parts = ckey.replace(".", "__").split("__")
count = len(parts)
if count == 1:
@@ -1669,8 +1073,7 @@ class CryptContext(object):
## return
##
## def strip_items(target, filter):
- ## keys = [key for key,value in iteritems(target)
- ## if filter(key,value)]
+ ## keys = [key for key,value in target.items() if filter(key,value)]
## for key in keys:
## del target[key]
##
@@ -1695,7 +1098,7 @@ class CryptContext(object):
## strip_items(deprecated, lambda k,v: k and v==cur)
##
## # remove redundant category options.
- ## for scheme, config in iteritems(scheme_options):
+ ## for scheme, config in scheme_options.items():
## if None in config:
## cur = config[None]
## strip_items(config, lambda k,v: k and v==cur)
@@ -1881,7 +1284,7 @@ class CryptContext(object):
else:
value = str(value)
- assert isinstance(value, native_string_types), \
+ assert isinstance(value, str), \
"expected string for key: %r %r" % (key, value)
# escape any percent signs.
@@ -1958,9 +1361,9 @@ class CryptContext(object):
.. seealso:: the :ref:`context-serialization-example` example in the tutorial.
"""
- parser = SafeConfigParser()
+ parser = ConfigParser()
self._write_to_parser(parser, section)
- buf = NativeStringIO()
+ buf = StringIO()
parser.write(buf)
unregistered = self._get_unregistered_handlers()
if unregistered:
@@ -1968,10 +1371,7 @@ class CryptContext(object):
"# NOTE: the %s handler(s) are not registered with Passlib,\n"
"# this string may not correctly reproduce the current configuration.\n\n"
) % ", ".join(repr(handler.name) for handler in unregistered))
- out = buf.getvalue()
- if not PY3:
- out = out.decode("utf-8")
- return out
+ return buf.getvalue()
# XXX: is this useful enough to enable?
##def write_to_path(self, path, section="passlib", update=False):
@@ -1987,23 +1387,6 @@ class CryptContext(object):
## fh.close()
#===================================================================
- # verify() hardening
- # NOTE: this entire feature has been disabled.
- # all contents of this section are NOOPs as of 1.7.1,
- # and will be removed in 1.8.
- #===================================================================
-
- mvt_estimate_max_samples = 20
- mvt_estimate_min_samples = 10
- mvt_estimate_max_time = 2
- mvt_estimate_resolution = 0.01
- harden_verify = None
- min_verify_time = 0
-
- def reset_min_verify_time(self):
- self._reset_dummy_verify()
-
- #===================================================================
# password hash api
#===================================================================
@@ -2023,7 +1406,7 @@ class CryptContext(object):
def _get_or_identify_record(self, hash, scheme=None, category=None):
"""return record based on scheme, or failing that, by identifying hash"""
if scheme:
- if not isinstance(hash, unicode_or_bytes_types):
+ if not isinstance(hash, unicode_or_bytes):
raise ExpectedStringError(hash, "hash")
return self._get_record(scheme, category)
else:
@@ -2060,7 +1443,7 @@ class CryptContext(object):
If so, the password should be re-hashed using :meth:`hash`
Otherwise, it will return ``False``.
- :type hash: unicode or bytes
+ :type hash: str or bytes
:arg hash:
The hash string to examine.
@@ -2084,7 +1467,7 @@ class CryptContext(object):
be used when determining if the hash needs to be updated
(e.g. is below the minimum rounds).
- :type secret: unicode, bytes, or None
+ :type secret: str, bytes, or None
:param secret:
Optional secret associated with the provided ``hash``.
This is not required, or even currently used for anything...
@@ -2166,7 +1549,7 @@ class CryptContext(object):
All registered algorithms will be checked, from first to last,
and whichever one positively identifies the hash first will be returned.
- :type hash: unicode or bytes
+ :type hash: str or bytes
:arg hash:
The hash string to test.
@@ -2204,7 +1587,7 @@ class CryptContext(object):
def hash(self, secret, scheme=None, category=None, **kwds):
"""run secret through selected algorithm, returning resulting hash.
- :type secret: unicode or bytes
+ :type secret: str or bytes
:arg secret:
the password to hash.
@@ -2243,7 +1626,7 @@ class CryptContext(object):
.. seealso:: the :ref:`context-basic-example` example in the tutorial
"""
- # XXX: could insert normalization to preferred unicode encoding here
+ # XXX: could insert normalization to preferred str encoding here
if scheme is not None:
# TODO: offer replacement alternative.
# ``context.handler(scheme).hash()`` would work,
@@ -2277,11 +1660,11 @@ class CryptContext(object):
(limited to the schemes configured for this context).
It will then check whether the password verifies against the hash.
- :type secret: unicode or bytes
+ :type secret: str or bytes
:arg secret:
the secret to verify
- :type hash: unicode or bytes
+ :type hash: str or bytes
:arg hash:
hash string to compare to
@@ -2357,11 +1740,11 @@ class CryptContext(object):
which wish to update deprecated hashes, and this call takes
care of all 3 steps efficiently.
- :type secret: unicode or bytes
+ :type secret: str or bytes
:arg secret:
the secret to verify
- :type secret: unicode or bytes
+ :type secret: str or bytes
:arg hash:
hash string to compare to.
@@ -2458,7 +1841,7 @@ class CryptContext(object):
"""
type(self)._dummy_hash.clear_cache(self)
- def dummy_verify(self, elapsed=0):
+ def dummy_verify(self):
"""
Helper that applications can call when user wasn't found,
in order to simulate time it would take to hash a password.
@@ -2466,12 +1849,6 @@ class CryptContext(object):
Runs verify() against a dummy hash, to simulate verification
of a real account password.
- :param elapsed:
-
- .. deprecated:: 1.7.1
-
- this option is ignored, and will be removed in passlib 1.8.
-
.. versionadded:: 1.7
"""
self.verify(self._dummy_secret, self._dummy_hash)
@@ -2567,12 +1944,6 @@ class LazyCryptContext(CryptContext):
.. versionadded:: 1.6
- :param create_policy:
-
- .. deprecated:: 1.6
- This option will be removed in Passlib 1.8,
- applications should use ``onload`` instead.
-
:param kwds:
All additional keywords are passed to CryptContext;
@@ -2596,11 +1967,9 @@ class LazyCryptContext(CryptContext):
"""
_lazy_kwds = None
- # NOTE: the way this class works changed in 1.6.
- # previously it just called _lazy_init() when ``.policy`` was
- # first accessed. now that is done whenever any of the public
- # attributes are accessed, and the class itself is changed
- # to a regular CryptContext, to remove the overhead once it's unneeded.
+ # NOTE: the way this class works is that whenever any of the public
+ # attributes are accessed, _lazy_init() is invoked, the class itself is changed
+ # to a regular CryptContext (to remove the overhead once it's unneeded)
def __init__(self, schemes=None, **kwds):
if schemes is not None:
@@ -2609,21 +1978,11 @@ class LazyCryptContext(CryptContext):
def _lazy_init(self):
kwds = self._lazy_kwds
- if 'create_policy' in kwds:
- warn("The CryptPolicy class, and LazyCryptContext's "
- "``create_policy`` keyword have been deprecated as of "
- "Passlib 1.6, and will be removed in Passlib 1.8; "
- "please use the ``onload`` keyword instead.",
- DeprecationWarning)
- create_policy = kwds.pop("create_policy")
- result = create_policy(**kwds)
- policy = CryptPolicy.from_source(result, _warn=False)
- kwds = policy._context.to_dict()
- elif 'onload' in kwds:
+ if 'onload' in kwds:
onload = kwds.pop("onload")
kwds = onload(**kwds)
del self._lazy_kwds
- super(LazyCryptContext, self).__init__(**kwds)
+ super().__init__(**kwds)
self.__class__ = CryptContext
def __getattribute__(self, attr):
diff --git a/passlib/crypto/_blowfish/__init__.py b/passlib/crypto/_blowfish/__init__.py
index 1aa1c85..3ee4590 100644
--- a/passlib/crypto/_blowfish/__init__.py
+++ b/passlib/crypto/_blowfish/__init__.py
@@ -56,7 +56,6 @@ import struct
# pkg
from passlib.utils import getrandbytes, rng
from passlib.utils.binary import bcrypt64
-from passlib.utils.compat import BytesIO, unicode, u, native_string_types
from passlib.crypto._blowfish.unrolled import BlowfishEngine
# local
__all__ = [
@@ -99,13 +98,13 @@ def raw_bcrypt(password, ident, salt, log_rounds):
#===================================================================
# parse ident
- assert isinstance(ident, native_string_types)
+ assert isinstance(ident, str)
add_null_padding = True
- if ident == u('2a') or ident == u('2y') or ident == u('2b'):
+ if ident == u'2a' or ident == u'2y' or ident == u'2b':
pass
- elif ident == u('2'):
+ elif ident == u'2':
add_null_padding = False
- elif ident == u('2x'):
+ elif ident == u'2x':
raise ValueError("crypt_blowfish's buggy '2x' hashes are not "
"currently supported")
else:
diff --git a/passlib/crypto/_blowfish/_gen_files.py b/passlib/crypto/_blowfish/_gen_files.py
index 2f92cf8..b1486e2 100644
--- a/passlib/crypto/_blowfish/_gen_files.py
+++ b/passlib/crypto/_blowfish/_gen_files.py
@@ -6,14 +6,13 @@
import os
import textwrap
# pkg
-from passlib.utils.compat import irange
# local
#=============================================================================
# helpers
#=============================================================================
def varlist(name, count):
- return ", ".join(name + str(x) for x in irange(count))
+ return ", ".join(name + str(x) for x in range(count))
def indent_block(block, padding):
@@ -30,7 +29,7 @@ BFSTR = """\
""".strip()
def render_encipher(write, indent=0):
- for i in irange(0, 15, 2):
+ for i in range(0, 15, 2):
write(indent, """\
# Feistel substitution on left word (round %(i)d)
r ^= %(left)s ^ p%(i1)d
@@ -74,7 +73,7 @@ def write_expand_function(write, indent=0):
# integrate key
#=============================================================
""")
- for i in irange(18):
+ for i in range(18):
write(indent+1, """\
p%(i)d = P[%(i)d] ^ key_words[%(i)d]
""", i=i)
@@ -99,7 +98,7 @@ def write_expand_function(write, indent=0):
""")
- for i in irange(2, 18, 2):
+ for i in range(2, 18, 2):
write(indent+1, """\
#------------------------------------------------
# update P[%(i)d] and P[%(i1)d]
diff --git a/passlib/crypto/_md4.py b/passlib/crypto/_md4.py
index bdc211f..e6ab63e 100644
--- a/passlib/crypto/_md4.py
+++ b/passlib/crypto/_md4.py
@@ -20,7 +20,7 @@ Implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html
from binascii import hexlify
import struct
# site
-from passlib.utils.compat import bascii_to_str, irange, PY3
+from passlib.utils.compat import bascii_to_str
# local
__all__ = ["md4"]
@@ -176,16 +176,12 @@ class md4(object):
state[a] = ((t<<s) & MASK_32) + (t>>(32-s))
# add back into original state
- for i in irange(4):
+ for i in range(4):
orig[i] = (orig[i]+state[i]) & MASK_32
def update(self, content):
if not isinstance(content, bytes):
- if PY3:
- raise TypeError("expected bytes")
- else:
- # replicate behavior of hashlib under py2
- content = content.encode("ascii")
+ raise TypeError("expected bytes")
buf = self._buf
if buf:
content = buf + content
diff --git a/passlib/crypto/des.py b/passlib/crypto/des.py
index 3f87aef..fc63ee0 100644
--- a/passlib/crypto/des.py
+++ b/passlib/crypto/des.py
@@ -47,8 +47,6 @@ The netbsd des-crypt implementation has some nice notes on how this all works -
import struct
# pkg
from passlib import exc
-from passlib.utils.compat import join_byte_values, byte_elem_value, \
- irange, irange, int_types
# local
__all__ = [
"expand_des_key",
@@ -606,14 +604,14 @@ def _unpack56(value):
## assert 0 <= value < 0x80, "value out of range"
## return (value<<1) | (0x9669 >> ((value ^ (value >> 4)) & 0xf)) & 1
-_EXPAND_ITER = irange(49,-7,-7)
+_EXPAND_ITER = range(49, -7, -7)
def expand_des_key(key):
"""convert DES from 7 bytes to 8 bytes (by inserting empty parity bits)"""
if isinstance(key, bytes):
if len(key) != 7:
raise ValueError("key must be 7 bytes in size")
- elif isinstance(key, int_types):
+ elif isinstance(key, int):
if key < 0 or key > INT_56_MASK:
raise ValueError("key must be 56-bit non-negative integer")
return _unpack64(expand_des_key(_pack56(key)))
@@ -624,9 +622,8 @@ def expand_des_key(key):
# but the parity bit would just be ignored in des_encrypt_block(),
# so not bothering to use it.
# XXX: could make parity-restoring optionally available via flag
- ##return join_byte_values(expand_7bit((key >> shift) & 0x7f)
- ## for shift in _EXPAND_ITER)
- return join_byte_values(((key>>shift) & 0x7f)<<1 for shift in _EXPAND_ITER)
+ ##return bytes(expand_7bit((key >> shift) & 0x7f) for shift in _EXPAND_ITER)
+ return bytes(((key >> shift) & 0x7f) << 1 for shift in _EXPAND_ITER)
def shrink_des_key(key):
"""convert DES key from 8 bytes to 7 bytes (by discarding the parity bits)"""
@@ -634,7 +631,7 @@ def shrink_des_key(key):
if len(key) != 8:
raise ValueError("key must be 8 bytes in size")
return _pack56(shrink_des_key(_unpack64(key)))
- elif isinstance(key, int_types):
+ elif isinstance(key, int):
if key < 0 or key > INT_64_MASK:
raise ValueError("key must be 64-bit non-negative integer")
else:
@@ -748,13 +745,13 @@ def des_encrypt_int_block(key, input, salt=0, rounds=1):
raise ValueError("salt must be 24-bit non-negative integer")
# validate & unpack key
- if not isinstance(key, int_types):
+ if not isinstance(key, int):
raise exc.ExpectedTypeError(key, "int", "key")
elif key < 0 or key > INT_64_MASK:
raise ValueError("key must be 64-bit non-negative integer")
# validate & unpack input
- if not isinstance(input, int_types):
+ if not isinstance(input, int):
raise exc.ExpectedTypeError(input, "int", "input")
elif input < 0 or input > INT_64_MASK:
raise ValueError("input must be 64-bit non-negative integer")
diff --git a/passlib/crypto/digest.py b/passlib/crypto/digest.py
index 90e0cad..c1b9769 100644
--- a/passlib/crypto/digest.py
+++ b/passlib/crypto/digest.py
@@ -5,7 +5,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import division
# core
import hashlib
import logging; log = logging.getLogger(__name__)
@@ -31,9 +30,8 @@ except ImportError:
_fast_pbkdf2_hmac = None
# pkg
from passlib import exc
-from passlib.utils import join_bytes, to_native_str, join_byte_values, to_bytes, \
- SequenceMixin, as_bool
-from passlib.utils.compat import irange, int_types, unicode_or_bytes_types, PY3, error_from
+from passlib.utils import join_bytes, to_native_str, to_bytes, SequenceMixin, as_bool
+from passlib.utils.compat import unicode_or_bytes
from passlib.utils.decor import memoized_property
# local
__all__ = [
@@ -126,7 +124,7 @@ def _gen_fallback_info():
not invoked at runtime.
"""
out = {}
- for alg in sorted(hashlib.algorithms_available | set(["md4"])):
+ for alg in sorted(hashlib.algorithms_available | {"md4"}):
info = lookup_hash(alg)
out[info.name] = (info.digest_size, info.block_size)
return out
@@ -298,7 +296,7 @@ def lookup_hash(digest, # *,
# resolve ``digest`` to ``const`` & ``name_record``
cache_by_name = True
- if isinstance(digest, unicode_or_bytes_types):
+ if isinstance(digest, unicode_or_bytes):
# normalize name
name_list = _get_hash_aliases(digest)
name = name_list[0]
@@ -584,7 +582,7 @@ mock_fips_mode = False
#: algorithms allowed under FIPS mode (subset of hashlib.algorithms_available);
#: per https://csrc.nist.gov/Projects/Hash-Functions FIPS 202 list.
-_fips_algorithms = set([
+_fips_algorithms = {
# FIPS 180-4 and FIPS 202
'sha1',
'sha224',
@@ -601,7 +599,7 @@ _fips_algorithms = set([
'sha3_512',
'shake_128',
'shake_256',
-])
+}
def _set_mock_fips_mode(enable=True):
@@ -622,8 +620,8 @@ if as_bool(os.environ.get("PASSLIB_MOCK_FIPS_MODE")):
#=============================================================================
#: translation tables used by compile_hmac()
-_TRANS_5C = join_byte_values((x ^ 0x5C) for x in irange(256))
-_TRANS_36 = join_byte_values((x ^ 0x36) for x in irange(256))
+_TRANS_5C = bytes((x ^ 0x5C) for x in range(256))
+_TRANS_36 = bytes((x ^ 0x36) for x in range(256))
def compile_hmac(digest, key, multipart=False):
"""
@@ -634,7 +632,7 @@ def compile_hmac(digest, key, multipart=False):
digest name or constructor.
:arg key:
- secret key as :class:`!bytes` or :class:`!unicode` (unicode will be encoded using utf-8).
+ secret key as :class:`!bytes` or :class:`!str` (str will be encoded using utf-8).
:param multipart:
request a multipart constructor instead (see return description).
@@ -713,11 +711,11 @@ def pbkdf1(digest, secret, salt, rounds, keylen=None):
:arg secret:
secret to use when generating the key.
- may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8).
+ may be :class:`!bytes` or :class:`str` (encoded using UTF-8).
:arg salt:
salt string to use when generating key.
- may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8).
+ may be :class:`!bytes` or :class:`str` (encoded using UTF-8).
:param rounds:
number of rounds to use to generate key.
@@ -742,7 +740,7 @@ def pbkdf1(digest, secret, salt, rounds, keylen=None):
salt = to_bytes(salt, param="salt")
# validate rounds
- if not isinstance(rounds, int_types):
+ if not isinstance(rounds, int):
raise exc.ExpectedTypeError(rounds, "int", "rounds")
if rounds < 1:
raise ValueError("rounds must be at least 1")
@@ -750,7 +748,7 @@ def pbkdf1(digest, secret, salt, rounds, keylen=None):
# validate keylen
if keylen is None:
keylen = digest_size
- elif not isinstance(keylen, int_types):
+ elif not isinstance(keylen, int):
raise exc.ExpectedTypeError(keylen, "int or None", "keylen")
elif keylen < 0:
raise ValueError("keylen must be at least 0")
@@ -760,7 +758,7 @@ def pbkdf1(digest, secret, salt, rounds, keylen=None):
# main pbkdf1 loop
block = secret + salt
- for _ in irange(rounds):
+ for _ in range(rounds):
block = const(block).digest()
return block[:keylen]
@@ -778,11 +776,11 @@ def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None):
:arg secret:
passphrase to use to generate key.
- may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8).
+ may be :class:`!bytes` or :class:`str` (encoded using UTF-8).
:arg salt:
salt string to use when generating key.
- may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8).
+ may be :class:`!bytes` or :class:`str` (encoded using UTF-8).
:param rounds:
number of rounds to use to generate key.
@@ -814,7 +812,7 @@ def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None):
digest_size = digest_info.digest_size
# validate rounds
- if not isinstance(rounds, int_types):
+ if not isinstance(rounds, int):
raise exc.ExpectedTypeError(rounds, "int", "rounds")
if rounds < 1:
raise ValueError("rounds must be at least 1")
@@ -822,7 +820,7 @@ def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None):
# validate keylen
if keylen is None:
keylen = digest_size
- elif not isinstance(keylen, int_types):
+ elif not isinstance(keylen, int):
raise exc.ExpectedTypeError(keylen, "int or None", "keylen")
elif keylen < 1:
# XXX: could allow keylen=0, but want to be compat w/ stdlib
@@ -866,7 +864,7 @@ def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None):
# assemble & return result
return join_bytes(
calc_block(keyed_hmac, keyed_hmac(salt + _pack_uint32(i)), rounds)
- for i in irange(1, block_count + 1)
+ for i in range(1, block_count + 1)
)[:keylen]
#-------------------------------------------------------------------------------------
@@ -876,7 +874,7 @@ def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None):
# NOTE: this env var is only present to support the admin/benchmark_pbkdf2 script
_force_backend = os.environ.get("PASSLIB_PBKDF2_BACKEND") or "any"
-if PY3 and _force_backend in ["any", "from-bytes"]:
+if _force_backend in ["any", "from-bytes"]:
from functools import partial
def _get_pbkdf2_looper(digest_size):
@@ -890,14 +888,19 @@ if PY3 and _force_backend in ["any", "from-bytes"]:
from_bytes = int.from_bytes
BIG = "big" # endianess doesn't matter, just has to be consistent
accum = from_bytes(digest, BIG)
- for _ in irange(rounds - 1):
+ for _ in range(rounds - 1):
digest = keyed_hmac(digest)
accum ^= from_bytes(digest, BIG)
return accum.to_bytes(digest_size, BIG)
_builtin_backend = "from-bytes"
-elif _force_backend in ["any", "unpack", "from-bytes"]:
+elif _force_backend in ["unpack"]:
+
+ # XXX: should run bench_pbkdf2() to verify;
+ # but think this can be removed now that we're always on python 3
+ # (the from_bytes method should always be faster)
+
from struct import Struct
from passlib.utils import sys_bits
@@ -912,7 +915,7 @@ elif _force_backend in ["any", "unpack", "from-bytes"]:
def helper(keyed_hmac, digest, rounds):
accum = digest
- for _ in irange(rounds - 1):
+ for _ in range(rounds - 1):
digest = keyed_hmac(digest)
accum ^= digest
return accum
@@ -969,8 +972,8 @@ elif _force_backend in ["any", "unpack", "from-bytes"]:
#
tdict = dict(
digest_size=digest_size,
- accum_vars=", ".join("acc_%d" % i for i in irange(count)),
- digest_vars=", ".join("dig_%d" % i for i in irange(count)),
+ accum_vars=", ".join("acc_%d" % i for i in range(count)),
+ digest_vars=", ".join("dig_%d" % i for i in range(count)),
)
# head of function
@@ -979,13 +982,13 @@ elif _force_backend in ["any", "unpack", "from-bytes"]:
" '''pbkdf2 loop helper for digest_size={digest_size}'''\n"
" unpack_digest = struct.unpack\n"
" {accum_vars} = unpack_digest(digest)\n"
- " for _ in irange(1, rounds):\n"
+ " for _ in range(1, rounds):\n"
" digest = keyed_hmac(digest)\n"
" {digest_vars} = unpack_digest(digest)\n"
).format(**tdict)
# xor digest
- for i in irange(count):
+ for i in range(count):
source += " acc_%d ^= dig_%d\n" % (i, i)
# return result
@@ -995,7 +998,7 @@ elif _force_backend in ["any", "unpack", "from-bytes"]:
# compile helper
#
code = compile(source, "<generated by passlib.crypto.digest._get_pbkdf2_looper()>", "exec")
- gdict = dict(irange=irange, struct=struct)
+ gdict = dict(struct=struct)
ldict = dict()
eval(code, gdict, ldict)
helper = ldict['helper']
@@ -1011,7 +1014,7 @@ elif _force_backend in ["any", "unpack", "from-bytes"]:
_builtin_backend = "unpack"
else:
- assert _force_backend in ["any", "hexlify"]
+ assert _force_backend in ["hexlify"]
# XXX: older & slower approach that used int(hexlify()),
# keeping it around for a little while just for benchmarking.
@@ -1025,7 +1028,7 @@ else:
def _pbkdf2_looper(keyed_hmac, digest, rounds):
hexlify = _hexlify
accum = int(hexlify(digest), 16)
- for _ in irange(rounds - 1):
+ for _ in range(rounds - 1):
digest = keyed_hmac(digest)
accum ^= int(hexlify(digest), 16)
return int_to_bytes(accum, len(digest))
diff --git a/passlib/crypto/scrypt/__init__.py b/passlib/crypto/scrypt/__init__.py
index c71873a..cd10632 100644
--- a/passlib/crypto/scrypt/__init__.py
+++ b/passlib/crypto/scrypt/__init__.py
@@ -6,7 +6,6 @@ XXX: add this module to public docs?
#==========================================================================
# imports
#==========================================================================
-from __future__ import absolute_import
# core
import logging; log = logging.getLogger(__name__)
from warnings import warn
@@ -108,10 +107,10 @@ def scrypt(secret, salt, n, r, p=1, keylen=32):
"""run SCrypt key derivation function using specified parameters.
:arg secret:
- passphrase string (unicode is encoded to bytes using utf-8).
+ passphrase string (str is encoded to bytes using utf-8).
:arg salt:
- salt string (unicode is encoded to bytes using utf-8).
+ salt string (str is encoded to bytes using utf-8).
:arg n:
integer 'N' parameter
diff --git a/passlib/crypto/scrypt/_builtin.py b/passlib/crypto/scrypt/_builtin.py
index e9bb305..c93e7a3 100644
--- a/passlib/crypto/scrypt/_builtin.py
+++ b/passlib/crypto/scrypt/_builtin.py
@@ -6,7 +6,6 @@
import operator
import struct
# pkg
-from passlib.utils.compat import izip
from passlib.crypto.digest import pbkdf2_hmac
from passlib.crypto.scrypt._salsa import salsa20
# local
@@ -170,7 +169,7 @@ class ScryptEngine(object):
i = 0
while i < n:
j = integerify(buffer) & n_mask
- result = tuple(a ^ b for a, b in izip(buffer, get_v_elem(j)))
+ result = tuple(a ^ b for a, b in zip(buffer, get_v_elem(j)))
bmix(result, buffer)
i += 1
@@ -179,7 +178,7 @@ class ScryptEngine(object):
# if not n_is_log_2:
# while i < n:
# j = integerify(buffer) % n
- # tmp = tuple(a^b for a,b in izip(buffer, get_v_elem(j)))
+ # tmp = tuple(a^b for a,b in zip(buffer, get_v_elem(j)))
# bmix(tmp,buffer)
# i += 1
@@ -225,15 +224,15 @@ class ScryptEngine(object):
j = 0
while j < half:
jn = j+16
- target[j:jn] = tmp = salsa20(a ^ b for a, b in izip(tmp, siter))
- target[half+j:half+jn] = tmp = salsa20(a ^ b for a, b in izip(tmp, siter))
+ target[j:jn] = tmp = salsa20(a ^ b for a, b in zip(tmp, siter))
+ target[half+j:half+jn] = tmp = salsa20(a ^ b for a, b in zip(tmp, siter))
j = jn
def _bmix_1(self, source, target):
"""special bmix() method optimized for ``r=1`` case"""
B = source[16:]
- target[:16] = tmp = salsa20(a ^ b for a, b in izip(B, iter(source)))
- target[16:] = salsa20(a ^ b for a, b in izip(tmp, B))
+ target[:16] = tmp = salsa20(a ^ b for a, b in zip(B, iter(source)))
+ target[16:] = salsa20(a ^ b for a, b in zip(tmp, B))
#=================================================================
# eoc
diff --git a/passlib/exc.py b/passlib/exc.py
index 755c7dc..29ef8af 100644
--- a/passlib/exc.py
+++ b/passlib/exc.py
@@ -310,8 +310,8 @@ def ExpectedTypeError(value, expected, param):
return TypeError("%s must be %s, not %s" % (param, expected, name))
def ExpectedStringError(value, param):
- """error message when param was supposed to be unicode or bytes"""
- return ExpectedTypeError(value, "unicode or bytes", param)
+ """error message when param was supposed to be str or bytes"""
+ return ExpectedTypeError(value, "str or bytes", param)
#------------------------------------------------------------------------
# hash/verify parameter errors
diff --git a/passlib/ext/django/utils.py b/passlib/ext/django/utils.py
index 2f8a2ef..d25a501 100644
--- a/passlib/ext/django/utils.py
+++ b/passlib/ext/django/utils.py
@@ -3,6 +3,7 @@
# imports
#=============================================================================
# core
+from collections import OrderedDict
from functools import update_wrapper, wraps
import logging; log = logging.getLogger(__name__)
import sys
@@ -19,7 +20,7 @@ except ImportError:
from passlib import exc, registry
from passlib.context import CryptContext
from passlib.exc import PasslibRuntimeWarning
-from passlib.utils.compat import get_method_function, iteritems, OrderedDict, unicode
+from passlib.utils.compat import get_method_function
from passlib.utils.decor import memoized_property
# local
__all__ = [
@@ -180,7 +181,7 @@ class DjangoTranslator(object):
#=============================================================================
def __init__(self, context=None, **kwds):
- super(DjangoTranslator, self).__init__(**kwds)
+ super().__init__(**kwds)
if context is not None:
self.context = context
@@ -467,7 +468,7 @@ class DjangoContextAdapter(DjangoTranslator):
# init parent, filling in default context object
if context is None:
context = CryptContext()
- super(DjangoContextAdapter, self).__init__(context=context, **kwds)
+ super().__init__(context=context, **kwds)
# setup user category
if get_user_category:
@@ -504,7 +505,7 @@ class DjangoContextAdapter(DjangoTranslator):
reset_hashers(setting="PASSWORD_HASHERS")
# reset internal caches
- super(DjangoContextAdapter, self).reset_hashers()
+ super().reset_hashers()
#=============================================================================
# django hashers helpers -- hasher lookup
@@ -821,13 +822,7 @@ class DjangoContextAdapter(DjangoTranslator):
config = getattr(settings, "PASSLIB_CONTEXT", _UNSET)
if config is _UNSET:
config = "passlib-default"
- if config is None:
- warn("setting PASSLIB_CONFIG=None is deprecated, "
- "and support will be removed in Passlib 1.8, "
- "use PASSLIB_CONFIG='disabled' instead.",
- DeprecationWarning)
- config = "disabled"
- elif not isinstance(config, (unicode, bytes, dict)):
+ if not isinstance(config, (str, bytes, dict)):
raise exc.ExpectedTypeError(config, "str or dict", "PASSLIB_CONFIG")
# load custom category func (if any)
@@ -1001,7 +996,7 @@ class _PasslibHasherWrapper(object):
]
if hasattr(handler, "parsehash"):
kwds = handler.parsehash(encoded, sanitize=mask_hash)
- for key, value in iteritems(kwds):
+ for key, value in kwds.items():
key = self._translate_kwds.get(key, key)
items.append((_(key), value))
return OrderedDict(items)
@@ -1030,7 +1025,6 @@ class _PasslibHasherWrapper(object):
##from passlib.registry import register_crypt_handler
##from passlib.utils import classproperty, to_native_str, to_unicode
-##from passlib.utils.compat import unicode
##
##
##class _HasherHandler(object):
@@ -1068,7 +1062,7 @@ class _PasslibHasherWrapper(object):
## @property
## def ident(self):
## # this should always be correct, as django relies on ident prefix.
-## return unicode(self.django_name + "$")
+## return self.django_name + "$"
##
## @property
## def identify(self, hash):
@@ -1085,7 +1079,7 @@ class _PasslibHasherWrapper(object):
## opts['iterations'] = kwds.pop("rounds")
## if kwds:
## raise TypeError("unexpected keyword arguments: %r" % list(kwds))
-## if isinstance(secret, unicode):
+## if isinstance(secret, str):
## secret = secret.encode("utf-8")
## if salt is None:
## salt = self.django_hasher.salt()
@@ -1094,7 +1088,7 @@ class _PasslibHasherWrapper(object):
## @property
## def verify(self, secret, hash):
## hash = to_native_str(hash, "utf-8", "hash")
-## if isinstance(secret, unicode):
+## if isinstance(secret, str):
## secret = secret.encode("utf-8")
## return self.django_hasher.verify(secret, hash)
##
@@ -1168,7 +1162,7 @@ class _PatchManager(object):
def check_all(self, strict=False):
"""run sanity check on all keys, issue warning if out of sync"""
same = self._is_same_value
- for path, (orig, expected) in iteritems(self._state):
+ for path, (orig, expected) in self._state.items():
if same(self._get_path(path), expected):
continue
msg = "another library has patched resource: %r" % path
@@ -1222,7 +1216,7 @@ class _PatchManager(object):
##def patch_many(self, **kwds):
## "override specified resources with new values"
- ## for path, value in iteritems(kwds):
+ ## for path, value in kwds.items():
## self.patch(path, value)
def monkeypatch(self, parent, name=None, enable=True, wrap=False):
diff --git a/passlib/handlers/argon2.py b/passlib/handlers/argon2.py
index 4a5691b..f0c2881 100644
--- a/passlib/handlers/argon2.py
+++ b/passlib/handlers/argon2.py
@@ -15,7 +15,6 @@ References
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement, absolute_import
# core
import logging
log = logging.getLogger(__name__)
@@ -30,7 +29,7 @@ from passlib import exc
from passlib.crypto.digest import MAX_UINT32
from passlib.utils import classproperty, to_bytes, render_bytes
from passlib.utils.binary import b64s_encode, b64s_decode
-from passlib.utils.compat import u, unicode, bascii_to_str, uascii_to_str, PY2
+from passlib.utils.compat import bascii_to_str
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -49,9 +48,9 @@ __all__ = [
#: argon2 type constants -- subclasses handle mapping these to backend-specific type constants.
#: (should be lowercase, to match representation in hash string)
-TYPE_I = u("i")
-TYPE_D = u("d")
-TYPE_ID = u("id") # new 2016-10-29; passlib 1.7.2 requires backends new enough for support
+TYPE_I = u"i"
+TYPE_D = u"d"
+TYPE_ID = u"id" # new 2016-10-29; passlib 1.7.2 requires backends new enough for support
#: list of all known types; first (supported) type will be used as default.
ALL_TYPES = (TYPE_ID, TYPE_I, TYPE_D)
@@ -284,7 +283,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
digest_size = checksum_size
# create variant
- subcls = super(_Argon2Common, cls).using(**kwds)
+ subcls = super().using(**kwds)
# set type
if type is not None:
@@ -293,7 +292,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
# set checksum size
relaxed = kwds.get("relaxed")
if digest_size is not None:
- if isinstance(digest_size, uh.native_string_types):
+ if isinstance(digest_size, str):
digest_size = int(digest_size)
# NOTE: this isn't *really* digest size minimum, but want to enforce secure minimum.
subcls.checksum_size = uh.norm_integer(subcls, digest_size, min=16, max=MAX_UINT32,
@@ -301,7 +300,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
# set memory cost
if memory_cost is not None:
- if isinstance(memory_cost, uh.native_string_types):
+ if isinstance(memory_cost, str):
memory_cost = int(memory_cost)
subcls.memory_cost = subcls._norm_memory_cost(memory_cost, relaxed=relaxed)
@@ -310,7 +309,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
# set max threads
if max_threads is not None:
- if isinstance(max_threads, uh.native_string_types):
+ if isinstance(max_threads, str):
max_threads = int(max_threads)
if max_threads < 1 and max_threads != -1:
raise ValueError("max_threads (%d) must be -1 (unlimited), or at least 1." %
@@ -394,9 +393,9 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
@classmethod
def from_string(cls, hash):
- # NOTE: assuming hash will be unicode, or use ascii-compatible encoding.
- # TODO: switch to working w/ str or unicode
- if isinstance(hash, unicode):
+ # NOTE: assuming hash will be str, or use ascii-compatible encoding.
+ # TODO: switch to working w/ str
+ if isinstance(hash, str):
hash = hash.encode("utf-8")
if not isinstance(hash, bytes):
raise exc.ExpectedStringError(hash, "hash")
@@ -434,7 +433,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
# NOTE: 'keyid' param currently not supported
return "$argon2%s$%sm=%d,t=%d,p=%d%s$%s$%s" % (
- uascii_to_str(self.type),
+ self.type,
vstr,
self.memory_cost,
self.rounds,
@@ -463,7 +462,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
self.checksum_size = len(checksum)
# call parent
- super(_Argon2Common, self).__init__(**kwds)
+ super().__init__(**kwds)
# init type
if type is None:
@@ -500,11 +499,8 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
@classmethod
def _norm_type(cls, value):
# type check
- if not isinstance(value, unicode):
- if PY2 and isinstance(value, bytes):
- value = value.decode('ascii')
- else:
- raise uh.exc.ExpectedTypeError(value, "str", "type")
+ if not isinstance(value, str):
+ raise uh.exc.ExpectedTypeError(value, "str", "type")
# check if type is valid
if value in ALL_TYPES_SET:
@@ -520,7 +516,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
@classmethod
def _norm_version(cls, version):
- if not isinstance(version, uh.int_types):
+ if not isinstance(version, int):
raise uh.exc.ExpectedTypeError(version, "integer", "version")
# minimum valid version
@@ -578,7 +574,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin,
return True
if self.checksum_size != cls.checksum_size:
return True
- return super(_Argon2Common, self)._calc_needs_update(**kwds)
+ return super()._calc_needs_update(**kwds)
#===================================================================
# backend loading
@@ -684,7 +680,7 @@ class _NoBackend(_Argon2Common):
self._stub_requires_backend()
# NOTE: have to use super() here so that we don't recursively
# call subclass's wrapped _calc_checksum
- return super(argon2, self)._calc_checksum(secret)
+ return super()._calc_checksum(secret)
#===================================================================
# eoc
diff --git a/passlib/handlers/bcrypt.py b/passlib/handlers/bcrypt.py
index b83b110..9a10f9e 100644
--- a/passlib/handlers/bcrypt.py
+++ b/passlib/handlers/bcrypt.py
@@ -10,7 +10,6 @@ TODO:
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement, absolute_import
# core
from base64 import b64encode
from hashlib import sha256
@@ -20,8 +19,6 @@ import logging; log = logging.getLogger(__name__)
from warnings import warn
# site
_bcrypt = None # dynamically imported by _load_backend_bcrypt()
-_pybcrypt = None # dynamically imported by _load_backend_pybcrypt()
-_bcryptor = None # dynamically imported by _load_backend_bcryptor()
# pkg
_builtin_bcrypt = None # dynamically imported by _load_backend_builtin()
from passlib.crypto.digest import compile_hmac
@@ -30,8 +27,6 @@ from passlib.utils import safe_crypt, repeat_string, to_bytes, parse_version, \
rng, getrandstr, test_crypt, to_unicode, \
utf8_truncate, utf8_repeat_string, crypt_accepts_bytes
from passlib.utils.binary import bcrypt64
-from passlib.utils.compat import get_unbound_method_function
-from passlib.utils.compat import u, uascii_to_str, unicode, str_to_uascii, PY3, error_from
import passlib.utils.handlers as uh
# local
@@ -42,11 +37,11 @@ __all__ = [
#=============================================================================
# support funcs & constants
#=============================================================================
-IDENT_2 = u("$2$")
-IDENT_2A = u("$2a$")
-IDENT_2X = u("$2x$")
-IDENT_2Y = u("$2y$")
-IDENT_2B = u("$2b$")
+IDENT_2 = u"$2$"
+IDENT_2A = u"$2a$"
+IDENT_2X = u"$2x$"
+IDENT_2Y = u"$2y$"
+IDENT_2B = u"$2b$"
_BNULL = b'\x00'
# reference hash of "test", used in various self-checks
@@ -122,8 +117,8 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
#--------------------
default_ident = IDENT_2B
ident_values = (IDENT_2, IDENT_2A, IDENT_2X, IDENT_2Y, IDENT_2B)
- ident_aliases = {u("2"): IDENT_2, u("2a"): IDENT_2A, u("2y"): IDENT_2Y,
- u("2b"): IDENT_2B}
+ ident_aliases = {u"2": IDENT_2, u"2a": IDENT_2A, u"2y": IDENT_2Y,
+ u"2b": IDENT_2B}
#--------------------
# HasSalt
@@ -171,9 +166,9 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
if ident == IDENT_2X:
raise ValueError("crypt_blowfish's buggy '2x' hashes are not "
"currently supported")
- rounds_str, data = tail.split(u("$"))
+ rounds_str, data = tail.split(u"$")
rounds = int(rounds_str)
- if rounds_str != u('%02d') % (rounds,):
+ if rounds_str != u'%02d' % (rounds,):
raise uh.exc.MalformedHashError(cls, "malformed cost field")
salt, chk = data[:22], data[22:]
return cls(
@@ -184,15 +179,15 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
)
def to_string(self):
- hash = u("%s%02d$%s%s") % (self.ident, self.rounds, self.salt, self.checksum)
- return uascii_to_str(hash)
+ hash = u"%s%02d$%s%s" % (self.ident, self.rounds, self.salt, self.checksum)
+ return hash
# NOTE: this should be kept separate from to_string()
# so that bcrypt_sha256() can still use it, while overriding to_string()
def _get_config(self, ident):
"""internal helper to prepare config string for backends"""
- config = u("%s%02d$%s") % (ident, self.rounds, self.salt)
- return uascii_to_str(config)
+ config = u"%s%02d$%s" % (ident, self.rounds, self.salt)
+ return config
#===================================================================
# migration
@@ -211,7 +206,7 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
# TODO: try to detect incorrect 8bit/wraparound hashes using kwds.get("secret")
# hand off to base implementation, so HasRounds can check rounds value.
- return super(_BcryptCommon, cls).needs_update(hash, **kwds)
+ return super().needs_update(hash, **kwds)
#===================================================================
# specialized salt generation - fixes passlib issue 25
@@ -229,12 +224,12 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
def _generate_salt(cls):
# generate random salt as normal,
# but repair last char so the padding bits always decode to zero.
- salt = super(_BcryptCommon, cls)._generate_salt()
+ salt = super()._generate_salt()
return bcrypt64.repair_unused(salt)
@classmethod
def _norm_salt(cls, salt, **kwds):
- salt = super(_BcryptCommon, cls)._norm_salt(salt, **kwds)
+ salt = super()._norm_salt(salt, **kwds)
assert salt is not None, "HasSalt didn't generate new salt!"
changed, salt = bcrypt64.check_repair_unused(salt)
if changed:
@@ -248,7 +243,7 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
return salt
def _norm_checksum(self, checksum, relaxed=False):
- checksum = super(_BcryptCommon, self)._norm_checksum(checksum, relaxed=relaxed)
+ checksum = super()._norm_checksum(checksum, relaxed=relaxed)
changed, checksum = bcrypt64.check_repair_unused(checksum)
if changed:
warn(
@@ -294,8 +289,6 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
verify = mixin_cls.verify
err_types = (ValueError, uh.exc.MissingBackendError)
- if _bcryptor:
- err_types += (_bcryptor.engine.SaltError,)
def safe_verify(secret, hash):
"""verify() wrapper which traps 'unknown identifier' errors"""
@@ -309,7 +302,6 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
# - InternalBackendError if crypt fails for unknown reason
# (trapped below so we can debug it)
# pybcrypt, bcrypt -- raises ValueError
- # bcryptor -- raises bcryptor.engine.SaltError
return NotImplemented
except uh.exc.InternalBackendError:
# _calc_checksum() code may also throw CryptBackendError
@@ -490,7 +482,7 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
def _norm_digest_args(cls, secret, ident, new=False):
# make sure secret is unicode
require_valid_utf8_bytes = cls._require_valid_utf8_bytes
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
elif require_valid_utf8_bytes:
# if backend requires utf8 bytes (os_crypt);
@@ -510,7 +502,7 @@ class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents,
cls._check_truncate_policy(secret)
# NOTE: especially important to forbid NULLs for bcrypt, since many
- # backends (bcryptor, bcrypt) happily accept them, and then
+ # backends (bcrypt) happily accept them, and then
# silently truncate the password at first NULL they encounter!
if _BNULL in secret:
raise uh.exc.NullPasswordError(cls)
@@ -591,7 +583,7 @@ class _NoBackend(_BcryptCommon):
self._stub_requires_backend()
# NOTE: have to use super() here so that we don't recursively
# call subclass's wrapped _calc_checksum, e.g. bcrypt_sha256._calc_checksum
- return super(bcrypt, self)._calc_checksum(secret)
+ return super()._calc_checksum(secret)
#===================================================================
# eoc
@@ -630,7 +622,7 @@ class _BcryptBackend(_BcryptCommon):
# # below method has a few edge cases where it chokes though.
# @classmethod
# def verify(cls, secret, hash):
- # if isinstance(hash, unicode):
+ # if isinstance(hash, str):
# hash = hash.encode("ascii")
# ident = hash[:hash.index(b"$", 1)+1].decode("ascii")
# if ident not in cls.ident_values:
@@ -650,7 +642,7 @@ class _BcryptBackend(_BcryptCommon):
# returns ascii bytes
secret, ident = self._prepare_digest_args(secret)
config = self._get_config(ident)
- if isinstance(config, unicode):
+ if isinstance(config, str):
config = config.encode("ascii")
hash = _bcrypt.hashpw(secret, config)
assert isinstance(hash, bytes)
@@ -658,113 +650,6 @@ class _BcryptBackend(_BcryptCommon):
raise uh.exc.CryptBackendError(self, config, hash, source="`bcrypt` package")
return hash[-31:].decode("ascii")
-#-----------------------------------------------------------------------
-# bcryptor backend
-#-----------------------------------------------------------------------
-class _BcryptorBackend(_BcryptCommon):
- """
- backend which uses 'bcryptor' package
- """
-
- @classmethod
- def _load_backend_mixin(mixin_cls, name, dryrun):
- # try to import bcryptor
- global _bcryptor
- try:
- import bcryptor as _bcryptor
- except ImportError: # pragma: no cover
- return False
-
- # deprecated as of 1.7.2
- if not dryrun:
- warn("Support for `bcryptor` is deprecated, and will be removed in Passlib 1.8; "
- "Please use `pip install bcrypt` instead", DeprecationWarning)
-
- return mixin_cls._finalize_backend_mixin(name, dryrun)
-
- def _calc_checksum(self, secret):
- # bcryptor behavior:
- # py2: unicode secret/hash encoded as ascii bytes before use,
- # bytes taken as-is; returns ascii bytes.
- # py3: not supported
- secret, ident = self._prepare_digest_args(secret)
- config = self._get_config(ident)
- hash = _bcryptor.engine.Engine(False).hash_key(secret, config)
- if not hash.startswith(config) or len(hash) != len(config) + 31:
- raise uh.exc.CryptBackendError(self, config, hash, source="bcryptor library")
- return str_to_uascii(hash[-31:])
-
-#-----------------------------------------------------------------------
-# pybcrypt backend
-#-----------------------------------------------------------------------
-class _PyBcryptBackend(_BcryptCommon):
- """
- backend which uses 'pybcrypt' package
- """
-
- #: classwide thread lock used for pybcrypt < 0.3
- _calc_lock = None
-
- @classmethod
- def _load_backend_mixin(mixin_cls, name, dryrun):
- # try to import pybcrypt
- global _pybcrypt
- if not _detect_pybcrypt():
- # not installed, or bcrypt installed instead
- return False
- try:
- import bcrypt as _pybcrypt
- except ImportError: # pragma: no cover
- # XXX: should we raise AssertionError here? (if get here, _detect_pybcrypt() is broken)
- return False
-
- # deprecated as of 1.7.2
- if not dryrun:
- warn("Support for `py-bcrypt` is deprecated, and will be removed in Passlib 1.8; "
- "Please use `pip install bcrypt` instead", DeprecationWarning)
-
- # determine pybcrypt version
- try:
- version = _pybcrypt._bcrypt.__version__
- except:
- log.warning("(trapped) error reading pybcrypt version", exc_info=True)
- version = "<unknown>"
- log.debug("detected 'pybcrypt' backend, version %r", version)
-
- # return calc function based on version
- vinfo = parse_version(version) or (0, 0)
- if vinfo < (0, 3):
- warn("py-bcrypt %s has a major security vulnerability, "
- "you should upgrade to py-bcrypt 0.3 immediately."
- % version, uh.exc.PasslibSecurityWarning)
- if mixin_cls._calc_lock is None:
- import threading
- mixin_cls._calc_lock = threading.Lock()
- mixin_cls._calc_checksum = get_unbound_method_function(mixin_cls._calc_checksum_threadsafe)
-
- return mixin_cls._finalize_backend_mixin(name, dryrun)
-
- def _calc_checksum_threadsafe(self, secret):
- # as workaround for pybcrypt < 0.3's concurrency issue,
- # we wrap everything in a thread lock. as long as bcrypt is only
- # used through passlib, this should be safe.
- with self._calc_lock:
- return self._calc_checksum_raw(secret)
-
- def _calc_checksum_raw(self, secret):
- # py-bcrypt behavior:
- # py2: unicode secret/hash encoded as ascii bytes before use,
- # bytes taken as-is; returns ascii bytes.
- # py3: unicode secret encoded as utf-8 bytes,
- # hash encoded as ascii bytes, returns ascii unicode.
- secret, ident = self._prepare_digest_args(secret)
- config = self._get_config(ident)
- hash = _pybcrypt.hashpw(secret, config)
- if not hash.startswith(config) or len(hash) != len(config) + 31:
- raise uh.exc.CryptBackendError(self, config, hash, source="pybcrypt library")
- return str_to_uascii(hash[-31:])
-
- _calc_checksum = _calc_checksum_raw
#-----------------------------------------------------------------------
# os crypt backend
@@ -810,14 +695,14 @@ class _OsCryptBackend(_BcryptCommon):
# instead of returning None? (would save re-detecting what went wrong)
# XXX: isn't secret ALWAYS bytes at this point?
#
- if PY3 and isinstance(secret, bytes):
+ if isinstance(secret, bytes):
try:
secret.decode("utf-8")
except UnicodeDecodeError:
- raise error_from(uh.exc.PasswordValueError(
+ raise uh.exc.PasswordValueError(
"python3 crypt.crypt() ony supports bytes passwords using UTF8; "
"passlib recommends running `pip install bcrypt` for general bcrypt support.",
- ), None)
+ ) from None
#
# else crypt() call failed for unknown reason.
@@ -943,7 +828,7 @@ class bcrypt(_NoBackend, _BcryptCommon):
# in order to load the appropriate backend.
#: list of potential backends
- backends = ("bcrypt", "pybcrypt", "bcryptor", "os_crypt", "builtin")
+ backends = ("bcrypt", "os_crypt", "builtin")
#: flag that this class's bases should be modified by SubclassBackendMixin
_backend_mixin_target = True
@@ -952,8 +837,6 @@ class bcrypt(_NoBackend, _BcryptCommon):
_backend_mixin_map = {
None: _NoBackend,
"bcrypt": _BcryptBackend,
- "pybcrypt": _PyBcryptBackend,
- "bcryptor": _BcryptorBackend,
"os_crypt": _OsCryptBackend,
"builtin": _BuiltinBackend,
}
@@ -965,7 +848,7 @@ class bcrypt(_NoBackend, _BcryptCommon):
#=============================================================================
# variants
#=============================================================================
-_UDOLLAR = u("$")
+_UDOLLAR = u"$"
# XXX: it might be better to have all the bcrypt variants share a common base class,
# and have the (django_)bcrypt_sha256 wrappers just proxy bcrypt instead of subclassing it.
@@ -983,17 +866,17 @@ class _wrapped_bcrypt(bcrypt):
# def hash(cls, secret, **kwds):
# # bypass bcrypt backend overriding this method
# # XXX: would wrapping bcrypt make this easier than subclassing it?
- # return super(_BcryptCommon, cls).hash(secret, **kwds)
+ # return super().hash(secret, **kwds)
#
# @classmethod
# def verify(cls, secret, hash):
# # bypass bcrypt backend overriding this method
- # return super(_BcryptCommon, cls).verify(secret, hash)
+ # return super().verify(secret, hash)
#
# @classmethod
# def genhash(cls, secret, hash):
# # bypass bcrypt backend overriding this method
- # return super(_BcryptCommon, cls).genhash(secret, hash)
+ # return super().genhash(secret, hash)
@classmethod
def _check_truncate_policy(cls, secret):
@@ -1050,7 +933,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
# class specific
#--------------------
- _supported_versions = set([1, 2])
+ _supported_versions = {1, 2}
#===================================================================
# instance attrs
@@ -1067,7 +950,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
@classmethod
def using(cls, version=None, **kwds):
- subcls = super(bcrypt_sha256, cls).using(**kwds)
+ subcls = super().using(**kwds)
if version is not None:
subcls.version = subcls._norm_version(version)
ident = subcls.default_ident
@@ -1093,7 +976,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
# XXX: we can't use .ident attr due to bcrypt code using it.
# working around that via prefix.
- prefix = u('$bcrypt-sha256$')
+ prefix = u'$bcrypt-sha256$'
#: current version 2 hash format
_v2_hash_re = re.compile(r"""(?x)
@@ -1152,8 +1035,8 @@ class bcrypt_sha256(_wrapped_bcrypt):
checksum=m.group("digest"),
)
- _v2_template = u("$bcrypt-sha256$v=2,t=%s,r=%d$%s$%s")
- _v1_template = u("$bcrypt-sha256$%s,%d$%s$%s")
+ _v2_template = u"$bcrypt-sha256$v=2,t=%s,r=%d$%s$%s"
+ _v1_template = u"$bcrypt-sha256$%s,%d$%s$%s"
def to_string(self):
if self.version == 1:
@@ -1161,7 +1044,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
else:
template = self._v2_template
hash = template % (self.ident.strip(_UDOLLAR), self.rounds, self.salt, self.checksum)
- return uascii_to_str(hash)
+ return hash
#===================================================================
# init
@@ -1170,7 +1053,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
def __init__(self, version=None, **kwds):
if version is not None:
self.version = self._norm_version(version)
- super(bcrypt_sha256, self).__init__(**kwds)
+ super().__init__(**kwds)
#===================================================================
# version
@@ -1193,7 +1076,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
# thus, have to use base64 (44 bytes) rather than hex (64 bytes).
# XXX: it's later come out that 55-72 may be ok, so later revision of bcrypt_sha256
# may switch to hex encoding, since it's simpler to implement elsewhere.
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
if self.version == 1:
@@ -1223,7 +1106,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
key = b64encode(digest)
# hand result off to normal bcrypt algorithm
- return super(bcrypt_sha256, self)._calc_checksum(key)
+ return super()._calc_checksum(key)
#===================================================================
# other
@@ -1232,7 +1115,7 @@ class bcrypt_sha256(_wrapped_bcrypt):
def _calc_needs_update(self, **kwds):
if self.version < type(self).version:
return True
- return super(bcrypt_sha256, self)._calc_needs_update(**kwds)
+ return super()._calc_needs_update(**kwds)
#===================================================================
# eoc
diff --git a/passlib/handlers/cisco.py b/passlib/handlers/cisco.py
index e715e1a..1b242e9 100644
--- a/passlib/handlers/cisco.py
+++ b/passlib/handlers/cisco.py
@@ -13,8 +13,6 @@ from warnings import warn
# pkg
from passlib.utils import right_pad_string, to_unicode, repeat_string, to_bytes
from passlib.utils.binary import h64
-from passlib.utils.compat import unicode, u, join_byte_values, \
- join_byte_elems, iter_byte_values, uascii_to_str
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -124,7 +122,7 @@ class cisco_pix(uh.HasUserContext, uh.StaticHandler):
# been observed when trying to actually *set* a non-ascii password
# via ASDM, and access via SSH seems to strip 8-bit chars.
#
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
#
@@ -186,7 +184,7 @@ class cisco_pix(uh.HasUserContext, uh.StaticHandler):
#
user = self.user
if user:
- if isinstance(user, unicode):
+ if isinstance(user, str):
user = user.encode("utf-8")
if not asa or len(secret) < 28:
secret += repeat_string(user, 4)
@@ -219,7 +217,7 @@ class cisco_pix(uh.HasUserContext, uh.StaticHandler):
# 16 bytes, which may have been a general 'char password[]'
# size limit under PIX
#
- digest = join_byte_elems(c for i, c in enumerate(digest) if (i + 1) & 3)
+ digest = bytes(c for i, c in enumerate(digest) if (i + 1) & 3)
#
# encode using Hash64
@@ -352,7 +350,7 @@ class cisco_type7(uh.GenericHandler):
#===================================================================
@classmethod
def using(cls, salt=None, **kwds):
- subcls = super(cisco_type7, cls).using(**kwds)
+ subcls = super().using(**kwds)
if salt is not None:
salt = subcls._norm_salt(salt, relaxed=kwds.get("relaxed"))
subcls._generate_salt = staticmethod(lambda: salt)
@@ -367,7 +365,7 @@ class cisco_type7(uh.GenericHandler):
return cls(salt=salt, checksum=hash[2:].upper())
def __init__(self, salt=None, **kwds):
- super(cisco_type7, self).__init__(**kwds)
+ super().__init__(**kwds)
if salt is not None:
salt = self._norm_salt(salt)
elif self.use_defaults:
@@ -400,12 +398,12 @@ class cisco_type7(uh.GenericHandler):
return uh.rng.randint(0, 15)
def to_string(self):
- return "%02d%s" % (self.salt, uascii_to_str(self.checksum))
+ return "%02d%s" % (self.salt, self.checksum)
def _calc_checksum(self, secret):
# XXX: no idea what unicode policy is, but all examples are
# 7-bit ascii compatible, so using UTF-8
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
return hexlify(self._cipher(secret, self.salt)).decode("ascii").upper()
@@ -423,16 +421,16 @@ class cisco_type7(uh.GenericHandler):
return raw.decode(encoding) if encoding else raw
# type7 uses a xor-based vingere variant, using the following secret key:
- _key = u("dsfd;kfoA,.iyewrkldJKDHSUBsgvca69834ncxv9873254k;fg87")
+ _key = u"dsfd;kfoA,.iyewrkldJKDHSUBsgvca69834ncxv9873254k;fg87"
@classmethod
- def _cipher(cls, data, salt):
+ def _cipher(cls, data: bytes, salt: int):
"""xor static key against data - encrypts & decrypts"""
key = cls._key
key_size = len(key)
- return join_byte_values(
+ return bytes(
value ^ ord(key[(salt + idx) % key_size])
- for idx, value in enumerate(iter_byte_values(data))
+ for idx, value in enumerate(data)
)
#=============================================================================
diff --git a/passlib/handlers/des_crypt.py b/passlib/handlers/des_crypt.py
index 68a4ca7..58707ee 100644
--- a/passlib/handlers/des_crypt.py
+++ b/passlib/handlers/des_crypt.py
@@ -10,7 +10,6 @@ from warnings import warn
# pkg
from passlib.utils import safe_crypt, test_crypt, to_unicode
from passlib.utils.binary import h64, h64big
-from passlib.utils.compat import byte_elem_value, u, uascii_to_str, unicode, suppress_cause
from passlib.crypto.des import des_encrypt_int_block
import passlib.utils.handlers as uh
# local
@@ -37,8 +36,8 @@ def _crypt_secret_to_key(secret):
# but des_encrypt_int_block() would just ignore them...
##return sum(expand_7bit(byte_elem_value(c) & 0x7f) << (56-i*8)
## for i, c in enumerate(secret[:8]))
- return sum((byte_elem_value(c) & 0x7f) << (57-i*8)
- for i, c in enumerate(secret[:8]))
+ return sum((c & 0x7f) << (57-i*8) for i, c in enumerate(secret[:8]))
+
def _raw_des_crypt(secret, salt):
"""pure-python backed for des_crypt"""
@@ -53,7 +52,7 @@ def _raw_des_crypt(secret, salt):
salt_value = h64.decode_int12(salt)
# gotta do something - no official policy since this predates unicode
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
assert isinstance(secret, bytes)
@@ -89,7 +88,7 @@ def _raw_bsdi_crypt(secret, rounds, salt):
salt_value = h64.decode_int24(salt)
# gotta do something - no official policy since this predates unicode
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
assert isinstance(secret, bytes)
@@ -171,11 +170,11 @@ class des_crypt(uh.TruncateMixin, uh.HasManyBackends, uh.HasSalt, uh.GenericHand
#===================================================================
# FORMAT: 2 chars of H64-encoded salt + 11 chars of H64-encoded checksum
- _hash_regex = re.compile(u(r"""
+ _hash_regex = re.compile(r"""
^
(?P<salt>[./a-z0-9]{2})
(?P<chk>[./a-z0-9]{11})?
- $"""), re.X|re.I)
+ $""", re.X|re.I)
@classmethod
def from_string(cls, hash):
@@ -184,8 +183,8 @@ class des_crypt(uh.TruncateMixin, uh.HasManyBackends, uh.HasSalt, uh.GenericHand
return cls(salt=salt, checksum=chk or None)
def to_string(self):
- hash = u("%s%s") % (self.salt, self.checksum)
- return uascii_to_str(hash)
+ hash = u"%s%s" % (self.salt, self.checksum)
+ return hash
#===================================================================
# digest calculation
@@ -297,13 +296,13 @@ class bsdi_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
#===================================================================
# parsing
#===================================================================
- _hash_regex = re.compile(u(r"""
+ _hash_regex = re.compile(r"""
^
_
(?P<rounds>[./a-z0-9]{4})
(?P<salt>[./a-z0-9]{4})
(?P<chk>[./a-z0-9]{11})?
- $"""), re.X|re.I)
+ $""", re.X|re.I)
@classmethod
def from_string(cls, hash):
@@ -319,9 +318,9 @@ class bsdi_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
)
def to_string(self):
- hash = u("_%s%s%s") % (h64.encode_int24(self.rounds).decode("ascii"),
+ hash = u"_%s%s%s" % (h64.encode_int24(self.rounds).decode("ascii"),
self.salt, self.checksum)
- return uascii_to_str(hash)
+ return hash
#===================================================================
# validation
@@ -333,7 +332,7 @@ class bsdi_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
@classmethod
def using(cls, **kwds):
- subcls = super(bsdi_crypt, cls).using(**kwds)
+ subcls = super().using(**kwds)
if not subcls.default_rounds & 1:
# issue warning if caller set an even 'rounds' value.
warn("bsdi_crypt rounds should be odd, as even rounds may reveal weak DES keys",
@@ -342,7 +341,7 @@ class bsdi_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
@classmethod
def _generate_rounds(cls):
- rounds = super(bsdi_crypt, cls)._generate_rounds()
+ rounds = super()._generate_rounds()
# ensure autogenerated rounds are always odd
# NOTE: doing this even for default_rounds so needs_update() doesn't get
# caught in a loop.
@@ -359,7 +358,7 @@ class bsdi_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
if not self.rounds & 1:
return True
# hand off to base implementation
- return super(bsdi_crypt, self)._calc_needs_update(**kwds)
+ return super()._calc_needs_update(**kwds)
#===================================================================
# backends
@@ -442,11 +441,11 @@ class bigcrypt(uh.HasSalt, uh.GenericHandler):
#===================================================================
# internal helpers
#===================================================================
- _hash_regex = re.compile(u(r"""
+ _hash_regex = re.compile(r"""
^
(?P<salt>[./a-z0-9]{2})
(?P<chk>([./a-z0-9]{11})+)?
- $"""), re.X|re.I)
+ $""", re.X|re.I)
@classmethod
def from_string(cls, hash):
@@ -458,11 +457,11 @@ class bigcrypt(uh.HasSalt, uh.GenericHandler):
return cls(salt=salt, checksum=chk)
def to_string(self):
- hash = u("%s%s") % (self.salt, self.checksum)
- return uascii_to_str(hash)
+ hash = u"%s%s" % (self.salt, self.checksum)
+ return hash
def _norm_checksum(self, checksum, relaxed=False):
- checksum = super(bigcrypt, self)._norm_checksum(checksum, relaxed=relaxed)
+ checksum = super()._norm_checksum(checksum, relaxed=relaxed)
if len(checksum) % 11:
raise uh.exc.InvalidHashError(self)
return checksum
@@ -471,7 +470,7 @@ class bigcrypt(uh.HasSalt, uh.GenericHandler):
# backend
#===================================================================
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
chk = _raw_des_crypt(secret, self.salt.encode("ascii"))
idx = 8
@@ -546,11 +545,11 @@ class crypt16(uh.TruncateMixin, uh.HasSalt, uh.GenericHandler):
#===================================================================
# internal helpers
#===================================================================
- _hash_regex = re.compile(u(r"""
+ _hash_regex = re.compile(r"""
^
(?P<salt>[./a-z0-9]{2})
(?P<chk>[./a-z0-9]{22})?
- $"""), re.X|re.I)
+ $""", re.X|re.I)
@classmethod
def from_string(cls, hash):
@@ -562,14 +561,14 @@ class crypt16(uh.TruncateMixin, uh.HasSalt, uh.GenericHandler):
return cls(salt=salt, checksum=chk)
def to_string(self):
- hash = u("%s%s") % (self.salt, self.checksum)
- return uascii_to_str(hash)
+ hash = u"%s%s" % (self.salt, self.checksum)
+ return hash
#===================================================================
# backend
#===================================================================
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
# check for truncation (during .hash() calls only)
@@ -579,8 +578,8 @@ class crypt16(uh.TruncateMixin, uh.HasSalt, uh.GenericHandler):
# parse salt value
try:
salt_value = h64.decode_int12(self.salt.encode("ascii"))
- except ValueError: # pragma: no cover - caught by class
- raise suppress_cause(ValueError("invalid chars in salt"))
+ except ValueError: # pragma: no cover - caught by class
+ raise ValueError("invalid chars in salt") from None
# convert first 8 byts of secret string into an integer,
key1 = _crypt_secret_to_key(secret)
diff --git a/passlib/handlers/digests.py b/passlib/handlers/digests.py
index 982155c..d9bf283 100644
--- a/passlib/handlers/digests.py
+++ b/passlib/handlers/digests.py
@@ -9,7 +9,6 @@ import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.utils import to_native_str, to_bytes, render_bytes, consteq
-from passlib.utils.compat import unicode, str_to_uascii
import passlib.utils.handlers as uh
from passlib.crypto.digest import lookup_hash
# local
@@ -45,9 +44,9 @@ class HexDigestHash(uh.StaticHandler):
return hash.lower()
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
- return str_to_uascii(self._hash_func(secret).hexdigest())
+ return self._hash_func(secret).hexdigest()
#===================================================================
# eoc
@@ -118,7 +117,7 @@ class htdigest(uh.MinimalHandler):
if not encoding:
encoding = cls.default_encoding
uh.validate_secret(secret)
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode(encoding)
user = to_bytes(user, encoding, "user")
realm = to_bytes(realm, encoding, "realm")
diff --git a/passlib/handlers/django.py b/passlib/handlers/django.py
index 6dd499a..aabd998 100644
--- a/passlib/handlers/django.py
+++ b/passlib/handlers/django.py
@@ -13,7 +13,6 @@ from passlib.handlers.bcrypt import _wrapped_bcrypt
from passlib.hash import argon2, bcrypt, pbkdf2_sha1, pbkdf2_sha256
from passlib.utils import to_unicode, rng, getrandstr
from passlib.utils.binary import BASE64_CHARS
-from passlib.utils.compat import str_to_uascii, uascii_to_str, unicode, u
from passlib.crypto.digest import pbkdf2_hmac
import passlib.utils.handlers as uh
# local
@@ -115,13 +114,13 @@ class django_salted_sha1(DjangoSaltedHash):
"""
name = "django_salted_sha1"
django_name = "sha1"
- ident = u("sha1$")
+ ident = u"sha1$"
checksum_size = 40
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
- return str_to_uascii(sha1(self.salt.encode("ascii") + secret).hexdigest())
+ return sha1(self.salt.encode("ascii") + secret).hexdigest()
class django_salted_md5(DjangoSaltedHash):
"""This class implements Django's Salted MD5 hash, and follows the :ref:`password-hash-api`.
@@ -153,20 +152,20 @@ class django_salted_md5(DjangoSaltedHash):
"""
name = "django_salted_md5"
django_name = "md5"
- ident = u("md5$")
+ ident = u"md5$"
checksum_size = 32
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
- return str_to_uascii(md5(self.salt.encode("ascii") + secret).hexdigest())
+ return md5(self.salt.encode("ascii") + secret).hexdigest()
#=============================================================================
# BCrypt
#=============================================================================
django_bcrypt = uh.PrefixWrapper("django_bcrypt", bcrypt,
- prefix=u('bcrypt$'), ident=u("bcrypt$"),
+ prefix=u'bcrypt$', ident=u"bcrypt$",
# NOTE: this docstring is duplicated in the docs, since sphinx
# seems to be having trouble reading it via autodata::
doc="""This class implements Django 1.4's BCrypt wrapper, and follows the :ref:`password-hash-api`.
@@ -209,7 +208,7 @@ class django_bcrypt_sha256(_wrapped_bcrypt):
# XXX: we can't use .ident attr due to bcrypt code using it.
# working around that via django_prefix
- django_prefix = u('bcrypt_sha256$')
+ django_prefix = u'bcrypt_sha256$'
@classmethod
def identify(cls, hash):
@@ -226,17 +225,17 @@ class django_bcrypt_sha256(_wrapped_bcrypt):
bhash = hash[len(cls.django_prefix):]
if not bhash.startswith("$2"):
raise uh.exc.MalformedHashError(cls)
- return super(django_bcrypt_sha256, cls).from_string(bhash)
+ return super().from_string(bhash)
def to_string(self):
- bhash = super(django_bcrypt_sha256, self).to_string()
- return uascii_to_str(self.django_prefix) + bhash
+ bhash = super().to_string()
+ return self.django_prefix + bhash
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
secret = hexlify(self._digest(secret).digest())
- return super(django_bcrypt_sha256, self)._calc_checksum(secret)
+ return super()._calc_checksum(secret)
#=============================================================================
# PBKDF2 variants
@@ -280,7 +279,7 @@ class django_pbkdf2_sha256(DjangoVariableHash):
"""
name = "django_pbkdf2_sha256"
django_name = "pbkdf2_sha256"
- ident = u('pbkdf2_sha256$')
+ ident = u'pbkdf2_sha256$'
min_salt_size = 1
max_rounds = 0xffffffff # setting at 32-bit limit for now
checksum_chars = uh.PADDED_BASE64_CHARS
@@ -331,7 +330,7 @@ class django_pbkdf2_sha1(django_pbkdf2_sha256):
"""
name = "django_pbkdf2_sha1"
django_name = "pbkdf2_sha1"
- ident = u('pbkdf2_sha1$')
+ ident = u'pbkdf2_sha1$'
checksum_size = 28 # 20 bytes -> base64
default_rounds = pbkdf2_sha1.default_rounds # NOTE: django 1.6 uses 12000
_digest = "sha1"
@@ -346,8 +345,8 @@ class django_pbkdf2_sha1(django_pbkdf2_sha256):
django_argon2 = uh.PrefixWrapper(
name="django_argon2",
wrapped=argon2.using(type="I"),
- prefix=u('argon2'),
- ident=u('argon2$argon2i$'),
+ prefix=u'argon2',
+ ident=u'argon2$argon2i$',
# NOTE: this docstring is duplicated in the docs, since sphinx
# seems to be having trouble reading it via autodata::
doc="""This class implements Django 1.10's Argon2 wrapper, and follows the :ref:`password-hash-api`.
@@ -402,7 +401,7 @@ class django_des_crypt(uh.TruncateMixin, uh.HasSalt, uh.GenericHandler):
name = "django_des_crypt"
django_name = "crypt"
setting_kwds = ("salt", "salt_size", "truncate_error")
- ident = u("crypt$")
+ ident = u"crypt$"
checksum_chars = salt_chars = uh.HASH64_CHARS
checksum_size = 11
min_salt_size = default_salt_size = 2
@@ -487,7 +486,7 @@ class django_disabled(uh.ifc.DisabledHash, uh.StaticHandler):
.. versionchanged:: 1.7 started appending an alphanumeric string.
"""
name = "django_disabled"
- _hash_prefix = u("!")
+ _hash_prefix = u"!"
suffix_length = 40
# XXX: move this to StaticHandler, or wherever _hash_prefix is being used?
diff --git a/passlib/handlers/fshp.py b/passlib/handlers/fshp.py
index db13e74..f8f31a9 100644
--- a/passlib/handlers/fshp.py
+++ b/passlib/handlers/fshp.py
@@ -12,8 +12,7 @@ import logging; log = logging.getLogger(__name__)
# pkg
from passlib.utils import to_unicode
import passlib.utils.handlers as uh
-from passlib.utils.compat import bascii_to_str, iteritems, u,\
- unicode
+from passlib.utils.compat import bascii_to_str
from passlib.crypto.digest import pbkdf1
# local
__all__ = [
@@ -67,7 +66,7 @@ class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
name = "fshp"
setting_kwds = ("salt", "salt_size", "rounds", "variant")
checksum_chars = uh.PADDED_BASE64_CHARS
- ident = u("{FSHP")
+ ident = u"{FSHP"
# checksum_size is property() that depends on variant
#--HasRawSalt--
@@ -92,8 +91,8 @@ class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
3: ("sha512", 64),
}
_variant_aliases = dict(
- [(unicode(k),k) for k in _variant_info] +
- [(v[0],k) for k,v in iteritems(_variant_info)]
+ [(str(k),k) for k in _variant_info] +
+ [(v[0],k) for k,v in _variant_info.items()]
)
#===================================================================
@@ -101,7 +100,7 @@ class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
#===================================================================
@classmethod
def using(cls, variant=None, **kwds):
- subcls = super(fshp, cls).using(**kwds)
+ subcls = super().using(**kwds)
if variant is not None:
subcls.default_variant = cls._norm_variant(variant)
return subcls
@@ -125,13 +124,13 @@ class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
else:
raise TypeError("no variant specified")
self.variant = variant
- super(fshp, self).__init__(**kwds)
+ super().__init__(**kwds)
@classmethod
def _norm_variant(cls, variant):
if isinstance(variant, bytes):
variant = variant.decode("ascii")
- if isinstance(variant, unicode):
+ if isinstance(variant, str):
try:
variant = cls._variant_aliases[variant]
except KeyError:
@@ -154,14 +153,14 @@ class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
# formatting
#===================================================================
- _hash_regex = re.compile(u(r"""
+ _hash_regex = re.compile(r"""
^
\{FSHP
(\d+)\| # variant
(\d+)\| # salt size
(\d+)\} # rounds
([a-zA-Z0-9+/]+={0,3}) # digest
- $"""), re.X)
+ $""", re.X)
@classmethod
def from_string(cls, hash):
@@ -192,7 +191,7 @@ class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
#===================================================================
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
# NOTE: for some reason, FSHP uses pbkdf1 with password & salt reversed.
# this has only a minimal impact on security,
diff --git a/passlib/handlers/ldap_digests.py b/passlib/handlers/ldap_digests.py
index 30254f0..f4d0c7e 100644
--- a/passlib/handlers/ldap_digests.py
+++ b/passlib/handlers/ldap_digests.py
@@ -12,7 +12,6 @@ import re
# pkg
from passlib.handlers.misc import plaintext
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
@@ -53,7 +52,7 @@ class _Base64DigestHelper(uh.StaticHandler):
return cls.ident
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
chk = self._hash_func(secret).digest()
return b64encode(chk).decode("ascii")
@@ -92,10 +91,10 @@ class _SaltedBase64DigestHelper(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHand
def to_string(self):
data = self.checksum + self.salt
hash = self.ident + b64encode(data).decode("ascii")
- return uascii_to_str(hash)
+ return hash
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
return self._hash_func(secret + self.salt).digest()
@@ -108,9 +107,9 @@ class ldap_md5(_Base64DigestHelper):
The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords.
"""
name = "ldap_md5"
- ident = u("{MD5}")
+ ident = u"{MD5}"
_hash_func = md5
- _hash_regex = re.compile(u(r"^\{MD5\}(?P<chk>[+/a-zA-Z0-9]{22}==)$"))
+ _hash_regex = re.compile(r"^\{MD5\}(?P<chk>[+/a-zA-Z0-9]{22}==)$")
class ldap_sha1(_Base64DigestHelper):
"""This class stores passwords using LDAP's plain SHA1 format, and follows the :ref:`password-hash-api`.
@@ -118,9 +117,9 @@ class ldap_sha1(_Base64DigestHelper):
The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords.
"""
name = "ldap_sha1"
- ident = u("{SHA}")
+ ident = u"{SHA}"
_hash_func = sha1
- _hash_regex = re.compile(u(r"^\{SHA\}(?P<chk>[+/a-zA-Z0-9]{27}=)$"))
+ _hash_regex = re.compile(r"^\{SHA\}(?P<chk>[+/a-zA-Z0-9]{27}=)$")
class ldap_salted_md5(_SaltedBase64DigestHelper):
"""This class stores passwords using LDAP's salted MD5 format, and follows the :ref:`password-hash-api`.
@@ -156,10 +155,10 @@ class ldap_salted_md5(_SaltedBase64DigestHelper):
This format now supports variable length salts, instead of a fix 4 bytes.
"""
name = "ldap_salted_md5"
- ident = u("{SMD5}")
+ ident = u"{SMD5}"
checksum_size = 16
_hash_func = md5
- _hash_regex = re.compile(u(r"^\{SMD5\}(?P<tmp>[+/a-zA-Z0-9]{27,}={0,2})$"))
+ _hash_regex = re.compile(r"^\{SMD5\}(?P<tmp>[+/a-zA-Z0-9]{27,}={0,2})$")
class ldap_salted_sha1(_SaltedBase64DigestHelper):
"""
@@ -197,11 +196,11 @@ class ldap_salted_sha1(_SaltedBase64DigestHelper):
This format now supports variable length salts, instead of a fix 4 bytes.
"""
name = "ldap_salted_sha1"
- ident = u("{SSHA}")
+ ident = u"{SSHA}"
checksum_size = 20
_hash_func = sha1
# NOTE: 32 = ceil((20 + 4) * 4/3)
- _hash_regex = re.compile(u(r"^\{SSHA\}(?P<tmp>[+/a-zA-Z0-9]{32,}={0,2})$"))
+ _hash_regex = re.compile(r"^\{SSHA\}(?P<tmp>[+/a-zA-Z0-9]{32,}={0,2})$")
@@ -237,12 +236,12 @@ class ldap_salted_sha256(_SaltedBase64DigestHelper):
.. versionadded:: 1.7.3
"""
name = "ldap_salted_sha256"
- ident = u("{SSHA256}")
+ ident = u"{SSHA256}"
checksum_size = 32
default_salt_size = 8
_hash_func = sha256
# NOTE: 48 = ceil((32 + 4) * 4/3)
- _hash_regex = re.compile(u(r"^\{SSHA256\}(?P<tmp>[+/a-zA-Z0-9]{48,}={0,2})$"))
+ _hash_regex = re.compile(r"^\{SSHA256\}(?P<tmp>[+/a-zA-Z0-9]{48,}={0,2})$")
class ldap_salted_sha512(_SaltedBase64DigestHelper):
@@ -277,12 +276,12 @@ class ldap_salted_sha512(_SaltedBase64DigestHelper):
.. versionadded:: 1.7.3
"""
name = "ldap_salted_sha512"
- ident = u("{SSHA512}")
+ ident = u"{SSHA512}"
checksum_size = 64
default_salt_size = 8
_hash_func = sha512
# NOTE: 91 = ceil((64 + 4) * 4/3)
- _hash_regex = re.compile(u(r"^\{SSHA512\}(?P<tmp>[+/a-zA-Z0-9]{91,}={0,2})$"))
+ _hash_regex = re.compile(r"^\{SSHA512\}(?P<tmp>[+/a-zA-Z0-9]{91,}={0,2})$")
class ldap_plaintext(plaintext):
@@ -299,7 +298,7 @@ class ldap_plaintext(plaintext):
:param encoding:
This controls the character encoding to use (defaults to ``utf-8``).
- This encoding will be used to encode :class:`!unicode` passwords
+ This encoding will be used to encode :class:`!str` passwords
under Python 2, and decode :class:`!bytes` hashes under Python 3.
.. versionchanged:: 1.6
@@ -309,7 +308,7 @@ class ldap_plaintext(plaintext):
# is override identify()
name = "ldap_plaintext"
- _2307_pat = re.compile(u(r"^\{\w+\}.*$"))
+ _2307_pat = re.compile(r"^\{\w+\}.*$")
@uh.deprecated_method(deprecated="1.7", removed="2.0")
@classmethod
@@ -337,7 +336,7 @@ def _init_ldap_crypt_handlers():
g = globals()
for wname in unix_crypt_schemes:
name = 'ldap_' + wname
- g[name] = uh.PrefixWrapper(name, wname, prefix=u("{CRYPT}"), lazy=True)
+ g[name] = uh.PrefixWrapper(name, wname, prefix=u"{CRYPT}", lazy=True)
del g
_init_ldap_crypt_handlers()
diff --git a/passlib/handlers/md5_crypt.py b/passlib/handlers/md5_crypt.py
index e3a2dfa..419862c 100644
--- a/passlib/handlers/md5_crypt.py
+++ b/passlib/handlers/md5_crypt.py
@@ -9,7 +9,6 @@ import logging; log = logging.getLogger(__name__)
# pkg
from passlib.utils import safe_crypt, test_crypt, repeat_string
from passlib.utils.binary import h64
-from passlib.utils.compat import unicode, u
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -67,15 +66,15 @@ def _raw_md5_crypt(pwd, salt, use_apr=False):
# validate secret
# XXX: not sure what official unicode policy is, using this as default
- if isinstance(pwd, unicode):
+ if isinstance(pwd, str):
pwd = pwd.encode("utf-8")
- assert isinstance(pwd, bytes), "pwd not unicode or bytes"
+ assert isinstance(pwd, bytes), "pwd not str or bytes"
if _BNULL in pwd:
raise uh.exc.NullPasswordError(md5_crypt)
pwd_len = len(pwd)
# validate salt - should have been taken care of by caller
- assert isinstance(salt, unicode), "salt not unicode"
+ assert isinstance(salt, str), "salt not str"
salt = salt.encode("ascii")
assert len(salt) < 9, "salt too large"
# NOTE: spec says salts larger than 8 bytes should be truncated,
@@ -255,7 +254,7 @@ class md5_crypt(uh.HasManyBackends, _MD5_Common):
# class attrs
#===================================================================
name = "md5_crypt"
- ident = u("$1$")
+ ident = u"$1$"
#===================================================================
# methods
@@ -329,7 +328,7 @@ class apr_md5_crypt(_MD5_Common):
# class attrs
#===================================================================
name = "apr_md5_crypt"
- ident = u("$apr1$")
+ ident = u"$apr1$"
#===================================================================
# methods
diff --git a/passlib/handlers/misc.py b/passlib/handlers/misc.py
index 44abc34..8c4de54 100644
--- a/passlib/handlers/misc.py
+++ b/passlib/handlers/misc.py
@@ -10,73 +10,19 @@ from warnings import warn
# site
# pkg
from passlib.utils import to_native_str, str_consteq
-from passlib.utils.compat import unicode, u, unicode_or_bytes_types
+from passlib.utils.compat import unicode_or_bytes
import passlib.utils.handlers as uh
# local
__all__ = [
"unix_disabled",
- "unix_fallback",
"plaintext",
]
#=============================================================================
# handler
#=============================================================================
-class unix_fallback(uh.ifc.DisabledHash, uh.StaticHandler):
- """This class provides the fallback behavior for unix shadow files, and follows the :ref:`password-hash-api`.
- This class does not implement a hash, but instead provides fallback
- behavior as found in /etc/shadow on most unix variants.
- If used, should be the last scheme in the context.
-
- * this class will positively identify all hash strings.
- * for security, passwords will always hash to ``!``.
- * it rejects all passwords if the hash is NOT an empty string (``!`` or ``*`` are frequently used).
- * by default it rejects all passwords if the hash is an empty string,
- but if ``enable_wildcard=True`` is passed to verify(),
- all passwords will be allowed through if the hash is an empty string.
-
- .. deprecated:: 1.6
- This has been deprecated due to its "wildcard" feature,
- and will be removed in Passlib 1.8. Use :class:`unix_disabled` instead.
- """
- name = "unix_fallback"
- context_kwds = ("enable_wildcard",)
-
- @classmethod
- def identify(cls, hash):
- if isinstance(hash, unicode_or_bytes_types):
- return True
- else:
- raise uh.exc.ExpectedStringError(hash, "hash")
-
- def __init__(self, enable_wildcard=False, **kwds):
- warn("'unix_fallback' is deprecated, "
- "and will be removed in Passlib 1.8; "
- "please use 'unix_disabled' instead.",
- DeprecationWarning)
- super(unix_fallback, self).__init__(**kwds)
- self.enable_wildcard = enable_wildcard
-
- def _calc_checksum(self, secret):
- if self.checksum:
- # NOTE: hash will generally be "!", but we want to preserve
- # it in case it's something else, like "*".
- return self.checksum
- else:
- return u("!")
-
- @classmethod
- def verify(cls, secret, hash, enable_wildcard=False):
- uh.validate_secret(secret)
- if not isinstance(hash, unicode_or_bytes_types):
- raise uh.exc.ExpectedStringError(hash, "hash")
- elif hash:
- return False
- else:
- return enable_wildcard
-
-_MARKER_CHARS = u("*!")
+_MARKER_CHARS = u"*!"
_MARKER_BYTES = b"*!"
class unix_disabled(uh.ifc.DisabledHash, uh.MinimalHandler):
@@ -99,8 +45,8 @@ class unix_disabled(uh.ifc.DisabledHash, uh.MinimalHandler):
(:attr:`!unix_disabled.default_marker` will contain the default value)
.. versionadded:: 1.6
- This class was added as a replacement for the now-deprecated
- :class:`unix_fallback` class, which had some undesirable features.
+ This class was added as a replacement for the now-removed
+ :class:`!unix_fallback` class.
"""
name = "unix_disabled"
setting_kwds = ("marker",)
@@ -110,16 +56,16 @@ class unix_disabled(uh.ifc.DisabledHash, uh.MinimalHandler):
# TODO: rename attr to 'marker'...
if 'bsd' in sys.platform: # pragma: no cover -- runtime detection
- default_marker = u("*")
+ default_marker = u"*"
else:
# use the linux default for other systems
# (glibc also supports adding old hash after the marker
# so it can be restored later).
- default_marker = u("!")
+ default_marker = u"!"
@classmethod
def using(cls, marker=None, **kwds):
- subcls = super(unix_disabled, cls).using(**kwds)
+ subcls = super().using(**kwds)
if marker is not None:
if not cls.identify(marker):
raise ValueError("invalid marker: %r" % marker)
@@ -142,7 +88,7 @@ class unix_disabled(uh.ifc.DisabledHash, uh.MinimalHandler):
# * linux may use "!" + hash to disable but preserve original hash
# * linux counts empty string as "any password";
# this code recognizes it, but treats it the same as "!"
- if isinstance(hash, unicode):
+ if isinstance(hash, str):
start = _MARKER_CHARS
elif isinstance(hash, bytes):
start = _MARKER_BYTES
@@ -215,7 +161,7 @@ class plaintext(uh.MinimalHandler):
:param encoding:
This controls the character encoding to use (defaults to ``utf-8``).
- This encoding will be used to encode :class:`!unicode` passwords
+ This encoding will be used to encode :class:`!str` passwords
under Python 2, and decode :class:`!bytes` hashes under Python 3.
.. versionchanged:: 1.6
@@ -230,7 +176,7 @@ class plaintext(uh.MinimalHandler):
@classmethod
def identify(cls, hash):
- if isinstance(hash, unicode_or_bytes_types):
+ if isinstance(hash, unicode_or_bytes):
return True
else:
raise uh.exc.ExpectedStringError(hash, "hash")
diff --git a/passlib/handlers/mssql.py b/passlib/handlers/mssql.py
index b060b36..fcd1fe5 100644
--- a/passlib/handlers/mssql.py
+++ b/passlib/handlers/mssql.py
@@ -43,7 +43,7 @@ from warnings import warn
# site
# pkg
from passlib.utils import consteq
-from passlib.utils.compat import bascii_to_str, unicode, u
+from passlib.utils.compat import bascii_to_str
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -55,17 +55,17 @@ __all__ = [
# mssql 2000
#=============================================================================
def _raw_mssql(secret, salt):
- assert isinstance(secret, unicode)
+ assert isinstance(secret, str)
assert isinstance(salt, bytes)
return sha1(secret.encode("utf-16-le") + salt).digest()
BIDENT = b"0x0100"
##BIDENT2 = b("\x01\x00")
-UIDENT = u("0x0100")
+UIDENT = u"0x0100"
def _ident_mssql(hash, csize, bsize):
"""common identify for mssql 2000/2005"""
- if isinstance(hash, unicode):
+ if isinstance(hash, str):
if len(hash) == csize and hash.startswith(UIDENT):
return True
elif isinstance(hash, bytes):
@@ -79,7 +79,7 @@ def _ident_mssql(hash, csize, bsize):
def _parse_mssql(hash, csize, bsize, handler):
"""common parser for mssql 2000/2005; returns 4 byte salt + checksum"""
- if isinstance(hash, unicode):
+ if isinstance(hash, str):
if len(hash) == csize and hash.startswith(UIDENT):
try:
return unhexlify(hash[6:].encode("utf-8"))
diff --git a/passlib/handlers/mysql.py b/passlib/handlers/mysql.py
index 4a71253..0958c46 100644
--- a/passlib/handlers/mysql.py
+++ b/passlib/handlers/mysql.py
@@ -30,8 +30,6 @@ from warnings import warn
# site
# pkg
from passlib.utils import to_native_str
-from passlib.utils.compat import bascii_to_str, unicode, u, \
- byte_elem_value, str_to_uascii
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -65,7 +63,7 @@ class mysql323(uh.StaticHandler):
def _calc_checksum(self, secret):
# FIXME: no idea if mysql has a policy about handling unicode passwords
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
MASK_32 = 0xffffffff
@@ -78,11 +76,11 @@ class mysql323(uh.StaticHandler):
for c in secret:
if c in WHITE:
continue
- tmp = byte_elem_value(c)
+ tmp = c
nr1 ^= ((((nr1 & 63)+add)*tmp) + (nr1 << 8)) & MASK_32
nr2 = (nr2+((nr2 << 8) ^ nr1)) & MASK_32
add = (add+tmp) & MASK_32
- return u("%08x%08x") % (nr1 & MASK_31, nr2 & MASK_31)
+ return u"%08x%08x" % (nr1 & MASK_31, nr2 & MASK_31)
#===================================================================
# eoc
@@ -102,7 +100,7 @@ class mysql41(uh.StaticHandler):
# class attrs
#===================================================================
name = "mysql41"
- _hash_prefix = u("*")
+ _hash_prefix = u"*"
checksum_chars = uh.HEX_CHARS
checksum_size = 40
@@ -115,9 +113,9 @@ class mysql41(uh.StaticHandler):
def _calc_checksum(self, secret):
# FIXME: no idea if mysql has a policy about handling unicode passwords
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
- return str_to_uascii(sha1(sha1(secret).digest()).hexdigest()).upper()
+ return sha1(sha1(secret).digest()).hexdigest().upper()
#===================================================================
# eoc
diff --git a/passlib/handlers/oracle.py b/passlib/handlers/oracle.py
index a094f37..92f1767 100644
--- a/passlib/handlers/oracle.py
+++ b/passlib/handlers/oracle.py
@@ -10,8 +10,6 @@ import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.utils import to_unicode, xor_bytes
-from passlib.utils.compat import irange, u, \
- uascii_to_str, unicode, str_to_uascii
from passlib.crypto.des import des_encrypt_block
import passlib.utils.handlers as uh
# local
@@ -41,7 +39,7 @@ def des_cbc_encrypt(key, value, iv=b'\x00' * 8, pad=b'\x00'):
"""
value += pad * (-len(value) % 8) # null pad to multiple of 8
hash = iv # start things off
- for offset in irange(0,len(value),8):
+ for offset in range(0, len(value), 8):
chunk = xor_bytes(hash, value[offset:offset+8])
hash = des_encrypt_block(key, chunk)
return hash
@@ -141,7 +139,7 @@ class oracle11(uh.HasSalt, uh.GenericHandler):
#===================================================================
# methods
#===================================================================
- _hash_regex = re.compile(u("^S:(?P<chk>[0-9a-f]{40})(?P<salt>[0-9a-f]{20})$"), re.I)
+ _hash_regex = re.compile(u"^S:(?P<chk>[0-9a-f]{40})(?P<salt>[0-9a-f]{20})$", re.I)
@classmethod
def from_string(cls, hash):
@@ -154,14 +152,14 @@ class oracle11(uh.HasSalt, uh.GenericHandler):
def to_string(self):
chk = self.checksum
- hash = u("S:%s%s") % (chk.upper(), self.salt.upper())
- return uascii_to_str(hash)
+ hash = u"S:%s%s" % (chk.upper(), self.salt.upper())
+ return hash
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
chk = sha1(secret + unhexlify(self.salt.encode("ascii"))).hexdigest()
- return str_to_uascii(chk).upper()
+ return chk.upper()
#===================================================================
# eoc
diff --git a/passlib/handlers/pbkdf2.py b/passlib/handlers/pbkdf2.py
index 274278d..407c2b3 100644
--- a/passlib/handlers/pbkdf2.py
+++ b/passlib/handlers/pbkdf2.py
@@ -10,7 +10,7 @@ import logging; log = logging.getLogger(__name__)
# pkg
from passlib.utils import to_unicode
from passlib.utils.binary import ab64_decode, ab64_encode
-from passlib.utils.compat import str_to_bascii, u, uascii_to_str, unicode
+from passlib.utils.compat import str_to_bascii
from passlib.crypto.digest import pbkdf2_hmac
import passlib.utils.handlers as uh
# local
@@ -81,7 +81,7 @@ def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=
"""create new Pbkdf2DigestHandler subclass for a specific hash"""
name = 'pbkdf2_' + hash_name
if ident is None:
- ident = u("$pbkdf2-%s$") % (hash_name,)
+ ident = u"$pbkdf2-%s$" % (hash_name,)
base = Pbkdf2DigestHandler
return type(name, (base,), dict(
__module__=module, # so ABCMeta won't clobber it.
@@ -128,7 +128,7 @@ def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=
#------------------------------------------------------------------------
# derived handlers
#------------------------------------------------------------------------
-pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, 131000, ident=u("$pbkdf2$"))
+pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, 131000, ident=u"$pbkdf2$")
pbkdf2_sha256 = create_pbkdf2_hash("sha256", 32, 29000)
pbkdf2_sha512 = create_pbkdf2_hash("sha512", 64, 25000)
@@ -183,7 +183,7 @@ class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Generic
#--GenericHandler--
name = "cta_pbkdf2_sha1"
setting_kwds = ("salt", "salt_size", "rounds")
- ident = u("$p5k2$")
+ ident = u"$p5k2$"
checksum_size = 20
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
@@ -279,8 +279,8 @@ class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
#--GenericHandler--
name = "dlitz_pbkdf2_sha1"
setting_kwds = ("salt", "salt_size", "rounds")
- ident = u("$p5k2$")
- _stub_checksum = u("0" * 48 + "=")
+ ident = u"$p5k2$"
+ _stub_checksum = u"0" * 48 + "="
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
# sanity check. underlying algorithm (and reference implementation)
@@ -370,7 +370,7 @@ class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler)
#--GenericHandler--
name = "atlassian_pbkdf2_sha1"
setting_kwds =("salt",)
- ident = u("{PKCS5S2}")
+ ident = u"{PKCS5S2}"
checksum_size = 32
#--HasRawSalt--
@@ -389,7 +389,7 @@ class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler)
def to_string(self):
data = self.salt + self.checksum
hash = self.ident + b64encode(data).decode("ascii")
- return uascii_to_str(hash)
+ return hash
def _calc_checksum(self, secret):
# TODO: find out what crowd's policy is re: unicode
@@ -436,7 +436,7 @@ class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gene
name = "grub_pbkdf2_sha512"
setting_kwds = ("salt", "salt_size", "rounds")
- ident = u("grub.pbkdf2.sha512.")
+ ident = u"grub.pbkdf2.sha512."
checksum_size = 64
# NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
@@ -453,7 +453,7 @@ class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gene
@classmethod
def from_string(cls, hash):
- rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u("."),
+ rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u".",
handler=cls)
salt = unhexlify(salt.encode("ascii"))
if chk:
@@ -463,7 +463,7 @@ class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.Gene
def to_string(self):
salt = hexlify(self.salt).decode("ascii").upper()
chk = hexlify(self.checksum).decode("ascii").upper()
- return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u("."))
+ return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u".")
def _calc_checksum(self, secret):
# TODO: find out what grub's policy is re: unicode
diff --git a/passlib/handlers/phpass.py b/passlib/handlers/phpass.py
index 6736f0f..102f9de 100644
--- a/passlib/handlers/phpass.py
+++ b/passlib/handlers/phpass.py
@@ -14,7 +14,6 @@ import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.utils.binary import h64
-from passlib.utils.compat import u, uascii_to_str, unicode
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -79,9 +78,9 @@ class phpass(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.GenericHandler):
rounds_cost = "log2"
#--HasManyIdents--
- default_ident = u("$P$")
- ident_values = (u("$P$"), u("$H$"))
- ident_aliases = {u("P"):u("$P$"), u("H"):u("$H$")}
+ default_ident = u"$P$"
+ ident_values = (u"$P$", u"$H$")
+ ident_aliases = {u"P":u"$P$", u"H":u"$H$"}
#===================================================================
# formatting
@@ -105,18 +104,18 @@ class phpass(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.GenericHandler):
)
def to_string(self):
- hash = u("%s%s%s%s") % (self.ident,
+ hash = u"%s%s%s%s" % (self.ident,
h64.encode_int6(self.rounds).decode("ascii"),
self.salt,
- self.checksum or u(''))
- return uascii_to_str(hash)
+ self.checksum or u'')
+ return hash
#===================================================================
# backend
#===================================================================
def _calc_checksum(self, secret):
# FIXME: can't find definitive policy on how phpass handles non-ascii.
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
real_rounds = 1<<self.rounds
result = md5(self.salt.encode("ascii") + secret).digest()
diff --git a/passlib/handlers/postgres.py b/passlib/handlers/postgres.py
index 17156fa..abe7663 100644
--- a/passlib/handlers/postgres.py
+++ b/passlib/handlers/postgres.py
@@ -8,7 +8,6 @@ import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.utils import to_bytes
-from passlib.utils.compat import str_to_uascii, unicode, u
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -33,7 +32,7 @@ class postgres_md5(uh.HasUserContext, uh.StaticHandler):
# algorithm information
#===================================================================
name = "postgres_md5"
- _hash_prefix = u("md5")
+ _hash_prefix = u"md5"
checksum_chars = uh.HEX_CHARS
checksum_size = 32
@@ -41,10 +40,10 @@ class postgres_md5(uh.HasUserContext, uh.StaticHandler):
# primary interface
#===================================================================
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
user = to_bytes(self.user, "utf-8", param="user")
- return str_to_uascii(md5(secret + user).hexdigest())
+ return md5(secret + user).hexdigest()
#===================================================================
# eoc
diff --git a/passlib/handlers/roundup.py b/passlib/handlers/roundup.py
index c7f99c1..bb00cf6 100644
--- a/passlib/handlers/roundup.py
+++ b/passlib/handlers/roundup.py
@@ -7,7 +7,6 @@ import logging; log = logging.getLogger(__name__)
# site
# pkg
import passlib.utils.handlers as uh
-from passlib.utils.compat import u
# local
__all__ = [
"roundup_plaintext",
@@ -18,11 +17,11 @@ __all__ = [
#
#=============================================================================
roundup_plaintext = uh.PrefixWrapper("roundup_plaintext", "plaintext",
- prefix=u("{plaintext}"), lazy=True)
+ prefix=u"{plaintext}", lazy=True)
# NOTE: these are here because they're currently only known to be used by roundup
-ldap_hex_md5 = uh.PrefixWrapper("ldap_hex_md5", "hex_md5", u("{MD5}"), lazy=True)
-ldap_hex_sha1 = uh.PrefixWrapper("ldap_hex_sha1", "hex_sha1", u("{SHA}"), lazy=True)
+ldap_hex_md5 = uh.PrefixWrapper("ldap_hex_md5", "hex_md5", u"{MD5}", lazy=True)
+ldap_hex_sha1 = uh.PrefixWrapper("ldap_hex_sha1", "hex_sha1", u"{SHA}", lazy=True)
#=============================================================================
# eof
diff --git a/passlib/handlers/scram.py b/passlib/handlers/scram.py
index 87bfabd..256de03 100644
--- a/passlib/handlers/scram.py
+++ b/passlib/handlers/scram.py
@@ -8,7 +8,7 @@ import logging; log = logging.getLogger(__name__)
# pkg
from passlib.utils import consteq, saslprep, to_native_str, splitcomma
from passlib.utils.binary import ab64_decode, ab64_encode
-from passlib.utils.compat import bascii_to_str, iteritems, u, native_string_types
+from passlib.utils.compat import bascii_to_str
from passlib.crypto.digest import pbkdf2_hmac, norm_hash_name
import passlib.utils.handlers as uh
# local
@@ -87,7 +87,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
#--GenericHandler--
name = "scram"
setting_kwds = ("salt", "salt_size", "rounds", "algs")
- ident = u("$scram$")
+ ident = u"$scram$"
#--HasSalt--
default_salt_size = 12
@@ -193,7 +193,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
SaltedPassword := Hi(Normalize(password), salt, i)
- :type password: unicode or utf-8 bytes
+ :type password: str or utf-8 bytes
:arg password: password to run through digest
:type salt: bytes
@@ -286,7 +286,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
default_algs = algs
# create subclass
- subcls = super(scram, cls).using(**kwds)
+ subcls = super().using(**kwds)
# fill in algs
if default_algs is not None:
@@ -297,7 +297,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
# init
#===================================================================
def __init__(self, algs=None, **kwds):
- super(scram, self).__init__(**kwds)
+ super().__init__(**kwds)
# init algs
digest_map = self.checksum
@@ -318,7 +318,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
def _norm_checksum(self, checksum, relaxed=False):
if not isinstance(checksum, dict):
raise uh.exc.ExpectedTypeError(checksum, "dict", "checksum")
- for alg, digest in iteritems(checksum):
+ for alg, digest in checksum.items():
if alg != norm_hash_name(alg, 'iana'):
raise ValueError("malformed algorithm name in scram hash: %r" %
(alg,))
@@ -336,7 +336,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
@classmethod
def _norm_algs(cls, algs):
"""normalize algs parameter"""
- if isinstance(algs, native_string_types):
+ if isinstance(algs, str):
algs = splitcomma(algs)
algs = sorted(norm_hash_name(alg, 'iana') for alg in algs)
if any(len(alg)>9 for alg in algs):
@@ -357,7 +357,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
return True
# hand off to base implementation
- return super(scram, self)._calc_needs_update(**kwds)
+ return super()._calc_needs_update(**kwds)
#===================================================================
# digest methods
@@ -390,7 +390,7 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
# check entire hash for consistency.
if full:
correct = failed = False
- for alg, digest in iteritems(chkmap):
+ for alg, digest in chkmap.items():
other = self._calc_checksum(secret, alg)
# NOTE: could do this length check in norm_algs(),
# but don't need to be that strict, and want to be able
@@ -429,15 +429,14 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
##def _test_reference_scram():
## "quick hack testing scram reference vectors"
## # NOTE: "n,," is GS2 header - see https://tools.ietf.org/html/rfc5801
-## from passlib.utils.compat import print_
##
## engine = _scram_engine(
## alg="sha-1",
## salt='QSXCR+Q6sek8bf92'.decode("base64"),
## rounds=4096,
-## password=u("pencil"),
+## password=u"pencil",
## )
-## print_(engine.digest.encode("base64").rstrip())
+## print(engine.digest.encode("base64").rstrip())
##
## msg = engine.format_auth_msg(
## username="user",
diff --git a/passlib/handlers/scrypt.py b/passlib/handlers/scrypt.py
index 1686fda..2d3d36b 100644
--- a/passlib/handlers/scrypt.py
+++ b/passlib/handlers/scrypt.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement, absolute_import
# core
import logging; log = logging.getLogger(__name__)
# site
@@ -10,7 +9,7 @@ import logging; log = logging.getLogger(__name__)
from passlib.crypto import scrypt as _scrypt
from passlib.utils import h64, to_bytes
from passlib.utils.binary import h64, b64s_decode, b64s_encode
-from passlib.utils.compat import u, bascii_to_str, suppress_cause
+from passlib.utils.compat import bascii_to_str
from passlib.utils.decor import classproperty
import passlib.utils.handlers as uh
# local
@@ -22,10 +21,10 @@ __all__ = [
# scrypt format identifiers
#=============================================================================
-IDENT_SCRYPT = u("$scrypt$") # identifier used by passlib
-IDENT_7 = u("$7$") # used by official scrypt spec
+IDENT_SCRYPT = u"$scrypt$" # identifier used by passlib
+IDENT_7 = u"$7$" # used by official scrypt spec
-_UDOLLAR = u("$")
+_UDOLLAR = u"$"
#=============================================================================
# handler
@@ -154,9 +153,9 @@ class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum
@classmethod
def using(cls, block_size=None, **kwds):
- subcls = super(scrypt, cls).using(**kwds)
+ subcls = super().using(**kwds)
if block_size is not None:
- if isinstance(block_size, uh.native_string_types):
+ if isinstance(block_size, str):
block_size = int(block_size)
subcls.block_size = subcls._norm_block_size(block_size, relaxed=kwds.get("relaxed"))
@@ -164,7 +163,7 @@ class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum
try:
_scrypt.validate(1 << cls.default_rounds, cls.block_size, cls.parallelism)
except ValueError as err:
- raise suppress_cause(ValueError("scrypt: invalid settings combination: " + str(err)))
+ raise ValueError("scrypt: invalid settings combination: " + str(err)) from None
return subcls
@@ -288,7 +287,7 @@ class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum
try:
salt.decode("ascii")
except UnicodeDecodeError:
- raise suppress_cause(NotImplementedError("scrypt $7$ hashes dont support non-ascii salts"))
+ raise NotImplementedError("scrypt $7$ hashes dont support non-ascii salts") from None
return bascii_to_str(b"".join([
b"$7$",
h64.encode_int6(self.rounds),
@@ -303,7 +302,7 @@ class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum
# init
#===================================================================
def __init__(self, block_size=None, **kwds):
- super(scrypt, self).__init__(**kwds)
+ super().__init__(**kwds)
# init block size
if block_size is None:
@@ -320,7 +319,7 @@ class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum
return uh.norm_integer(cls, block_size, min=1, param="block_size", relaxed=relaxed)
def _generate_salt(self):
- salt = super(scrypt, self)._generate_salt()
+ salt = super()._generate_salt()
if self.ident == IDENT_7:
# this format doesn't support non-ascii salts.
# as workaround, we take raw bytes, encoded to base64
@@ -372,7 +371,7 @@ class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum
# XXX: for now, marking all hashes which don't have matching block_size setting
if self.block_size != type(self).block_size:
return True
- return super(scrypt, self)._calc_needs_update(**kwds)
+ return super()._calc_needs_update(**kwds)
#===================================================================
# eoc
diff --git a/passlib/handlers/sha1_crypt.py b/passlib/handlers/sha1_crypt.py
index 8f9aa71..287f044 100644
--- a/passlib/handlers/sha1_crypt.py
+++ b/passlib/handlers/sha1_crypt.py
@@ -11,7 +11,6 @@ import logging; log = logging.getLogger(__name__)
# pkg
from passlib.utils import safe_crypt, test_crypt
from passlib.utils.binary import h64
-from passlib.utils.compat import u, unicode, irange
from passlib.crypto.digest import compile_hmac
import passlib.utils.handlers as uh
# local
@@ -62,7 +61,7 @@ class sha1_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
#--GenericHandler--
name = "sha1_crypt"
setting_kwds = ("salt", "salt_size", "rounds")
- ident = u("$sha1$")
+ ident = u"$sha1$"
checksum_size = 28
checksum_chars = uh.HASH64_CHARS
@@ -126,16 +125,16 @@ class sha1_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
return True
def _calc_checksum_builtin(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
if _BNULL in secret:
raise uh.exc.NullPasswordError(self)
rounds = self.rounds
# NOTE: this seed value is NOT the same as the config string
- result = (u("%s$sha1$%s") % (self.salt, rounds)).encode("ascii")
+ result = (u"%s$sha1$%s" % (self.salt, rounds)).encode("ascii")
# NOTE: this algorithm is essentially PBKDF1, modified to use HMAC.
keyed_hmac = compile_hmac("sha1", secret)
- for _ in irange(rounds):
+ for _ in range(rounds):
result = keyed_hmac(result)
return h64.encode_transposed_bytes(result, self._chk_offsets).decode("ascii")
diff --git a/passlib/handlers/sha2_crypt.py b/passlib/handlers/sha2_crypt.py
index e6060c5..d539472 100644
--- a/passlib/handlers/sha2_crypt.py
+++ b/passlib/handlers/sha2_crypt.py
@@ -10,8 +10,6 @@ import logging; log = logging.getLogger(__name__)
from passlib.utils import safe_crypt, test_crypt, \
repeat_string, to_unicode
from passlib.utils.binary import h64
-from passlib.utils.compat import byte_elem_value, u, \
- uascii_to_str, unicode
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -86,7 +84,7 @@ def _raw_sha2_crypt(pwd, salt, rounds, use_512=False):
# for details).
# validate secret
- if isinstance(pwd, unicode):
+ if isinstance(pwd, str):
# XXX: not sure what official unicode policy is, using this as default
pwd = pwd.encode("utf-8")
assert isinstance(pwd, bytes)
@@ -101,7 +99,7 @@ def _raw_sha2_crypt(pwd, salt, rounds, use_512=False):
# by the handler class.
# validate salt
- assert isinstance(salt, unicode), "salt not unicode"
+ assert isinstance(salt, str), "salt not str"
salt = salt.encode("ascii")
salt_len = len(salt)
assert salt_len < 17, "salt too large"
@@ -163,7 +161,7 @@ def _raw_sha2_crypt(pwd, salt, rounds, use_512=False):
#===================================================================
# digest S - used instead of salt itself when calculating digest C
#===================================================================
- ds = hash_const(salt * (16 + byte_elem_value(da[0]))).digest()[:salt_len]
+ ds = hash_const(salt * (16 + da[0])).digest()[:salt_len]
assert len(ds) == salt_len, "salt_len somehow > hash_len!"
#===================================================================
@@ -246,9 +244,9 @@ def _raw_sha2_crypt(pwd, salt, rounds, use_512=False):
#=============================================================================
# handlers
#=============================================================================
-_UROUNDS = u("rounds=")
-_UDOLLAR = u("$")
-_UZERO = u("0")
+_UROUNDS = u"rounds="
+_UDOLLAR = u"$"
+_UZERO = u"0"
class _SHA2_Common(uh.HasManyBackends, uh.HasRounds, uh.HasSalt,
uh.GenericHandler):
@@ -278,7 +276,7 @@ class _SHA2_Common(uh.HasManyBackends, uh.HasRounds, uh.HasSalt,
implicit_rounds = False
def __init__(self, implicit_rounds=None, **kwds):
- super(_SHA2_Common, self).__init__(**kwds)
+ super().__init__(**kwds)
# if user calls hash() w/ 5000 rounds, default to compact form.
if implicit_rounds is None:
implicit_rounds = (self.use_defaults and self.rounds == 5000)
@@ -339,12 +337,12 @@ class _SHA2_Common(uh.HasManyBackends, uh.HasRounds, uh.HasSalt,
def to_string(self):
if self.rounds == 5000 and self.implicit_rounds:
- hash = u("%s%s$%s") % (self.ident, self.salt,
- self.checksum or u(''))
+ hash = u"%s%s$%s" % (self.ident, self.salt,
+ self.checksum or u'')
else:
- hash = u("%srounds=%d$%s$%s") % (self.ident, self.rounds,
- self.salt, self.checksum or u(''))
- return uascii_to_str(hash)
+ hash = u"%srounds=%d$%s$%s" % (self.ident, self.rounds,
+ self.salt, self.checksum or u'')
+ return hash
#===================================================================
# backends
@@ -444,7 +442,7 @@ class sha256_crypt(_SHA2_Common):
# class attrs
#===================================================================
name = "sha256_crypt"
- ident = u("$5$")
+ ident = u"$5$"
checksum_size = 43
# NOTE: using 25/75 weighting of builtin & os_crypt backends
default_rounds = 535000
@@ -511,7 +509,7 @@ class sha512_crypt(_SHA2_Common):
# class attrs
#===================================================================
name = "sha512_crypt"
- ident = u("$6$")
+ ident = u"$6$"
checksum_size = 86
_cdb_use_512 = True
# NOTE: using 25/75 weighting of builtin & os_crypt backends
diff --git a/passlib/handlers/sun_md5_crypt.py b/passlib/handlers/sun_md5_crypt.py
index 0eeb4e7..d80d7ff 100644
--- a/passlib/handlers/sun_md5_crypt.py
+++ b/passlib/handlers/sun_md5_crypt.py
@@ -19,8 +19,7 @@ from warnings import warn
# pkg
from passlib.utils import to_unicode
from passlib.utils.binary import h64
-from passlib.utils.compat import byte_elem_value, irange, u, \
- uascii_to_str, unicode, str_to_bascii
+from passlib.utils.compat import str_to_bascii
import passlib.utils.handlers as uh
# local
__all__ = [
@@ -73,7 +72,7 @@ MAGIC_HAMLET = (
)
# NOTE: these sequences are pre-calculated iteration ranges used by X & Y loops w/in rounds function below
-xr = irange(7)
+xr = range(7)
_XY_ROUNDS = [
tuple((i,i,i+3) for i in xr), # xrounds 0
tuple((i,i+1,i+4) for i in xr), # xrounds 1
@@ -124,7 +123,7 @@ def raw_sun_md5_crypt(secret, rounds, salt):
round = 0
while round < real_rounds:
# convert last result byte string to list of byte-ints for easy access
- rval = [ byte_elem_value(c) for c in result ].__getitem__
+ rval = [c for c in result].__getitem__
# build up X bit by bit
x = 0
@@ -151,7 +150,7 @@ def raw_sun_md5_crypt(secret, rounds, salt):
h = md5(result)
if coin:
h.update(MAGIC_HAMLET)
- h.update(unicode(round).encode("ascii"))
+ h.update(str(round).encode("ascii"))
result = h.digest()
round += 1
@@ -237,7 +236,7 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
# XXX: ^ not sure what it does if past this bound... does 32 int roll over?
rounds_cost = "linear"
- ident_values = (u("$md5$"), u("$md5,"))
+ ident_values = (u"$md5$", u"$md5,")
#===================================================================
# instance attrs
@@ -249,7 +248,7 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
#===================================================================
def __init__(self, bare_salt=False, **kwds):
self.bare_salt = bare_salt
- super(sun_md5_crypt, self).__init__(**kwds)
+ super().__init__(**kwds)
#===================================================================
# internal helpers
@@ -268,11 +267,11 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
# if so, parse and validate it.
# by end, set 'rounds' to int value, and 'tail' containing salt+chk
#
- if hash.startswith(u("$md5$")):
+ if hash.startswith(u"$md5$"):
rounds = 0
salt_idx = 5
- elif hash.startswith(u("$md5,rounds=")):
- idx = hash.find(u("$"), 12)
+ elif hash.startswith(u"$md5,rounds="):
+ idx = hash.find(u"$", 12)
if idx == -1:
raise uh.exc.MalformedHashError(cls, "unexpected end of rounds")
rstr = hash[12:idx]
@@ -280,7 +279,7 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
rounds = int(rstr)
except ValueError:
raise uh.exc.MalformedHashError(cls, "bad rounds")
- if rstr != unicode(rounds):
+ if rstr != str(rounds):
raise uh.exc.ZeroPaddedRoundsError(cls)
if rounds == 0:
# NOTE: not sure if this is forbidden by spec or not;
@@ -296,20 +295,20 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
# to deal cleanly with some backward-compatible workarounds
# implemented by original implementation.
#
- chk_idx = hash.rfind(u("$"), salt_idx)
+ chk_idx = hash.rfind(u"$", salt_idx)
if chk_idx == -1:
# ''-config for $-hash
salt = hash[salt_idx:]
chk = None
bare_salt = True
elif chk_idx == len(hash)-1:
- if chk_idx > salt_idx and hash[-2] == u("$"):
+ if chk_idx > salt_idx and hash[-2] == u"$":
raise uh.exc.MalformedHashError(cls, "too many '$' separators")
# $-config for $$-hash
salt = hash[salt_idx:-1]
chk = None
bare_salt = False
- elif chk_idx > 0 and hash[chk_idx-1] == u("$"):
+ elif chk_idx > 0 and hash[chk_idx-1] == u"$":
# $$-hash
salt = hash[salt_idx:chk_idx-1]
chk = hash[chk_idx+1:]
@@ -328,16 +327,16 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
)
def to_string(self, _withchk=True):
- ss = u('') if self.bare_salt else u('$')
+ ss = u'' if self.bare_salt else u'$'
rounds = self.rounds
if rounds > 0:
- hash = u("$md5,rounds=%d$%s%s") % (rounds, self.salt, ss)
+ hash = u"$md5,rounds=%d$%s%s" % (rounds, self.salt, ss)
else:
- hash = u("$md5$%s%s") % (self.salt, ss)
+ hash = u"$md5$%s%s" % (self.salt, ss)
if _withchk:
chk = self.checksum
- hash = u("%s$%s") % (hash, chk)
- return uascii_to_str(hash)
+ hash = u"%s$%s" % (hash, chk)
+ return hash
#===================================================================
# primary interface
@@ -349,7 +348,7 @@ class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
def _calc_checksum(self, secret):
# NOTE: no reference for how sun_md5_crypt handles unicode
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
config = str_to_bascii(self.to_string(_withchk=False))
return raw_sun_md5_crypt(secret, self.rounds, config).decode("ascii")
diff --git a/passlib/handlers/windows.py b/passlib/handlers/windows.py
index e17beba..5ac890c 100644
--- a/passlib/handlers/windows.py
+++ b/passlib/handlers/windows.py
@@ -9,7 +9,6 @@ from warnings import warn
# site
# pkg
from passlib.utils import to_unicode, right_pad_string
-from passlib.utils.compat import unicode
from passlib.crypto.digest import lookup_hash
md4 = lookup_hash("md4").const
import passlib.utils.handlers as uh
@@ -100,7 +99,7 @@ class lmhash(uh.TruncateMixin, uh.HasEncodingContext, uh.StaticHandler):
def raw(cls, secret, encoding=None):
"""encode password using LANMAN hash algorithm.
- :type secret: unicode or utf-8 encoded bytes
+ :type secret: str or utf-8 encoded bytes
:arg secret: secret to hash
:type encoding: str
:arg encoding:
@@ -117,7 +116,7 @@ class lmhash(uh.TruncateMixin, uh.HasEncodingContext, uh.StaticHandler):
# http://www.freerainbowtables.com/phpBB3/viewtopic.php?t=387&p=12163
from passlib.crypto.des import des_encrypt_block
MAGIC = cls._magic
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
# perform uppercasing while we're still unicode,
# to give a better shot at getting non-ascii chars right.
# (though some codepages do NOT upper-case the same as unicode).
@@ -129,7 +128,7 @@ class lmhash(uh.TruncateMixin, uh.HasEncodingContext, uh.StaticHandler):
# but *that* might not always be right.
secret = secret.upper()
else:
- raise TypeError("secret must be unicode or bytes")
+ raise TypeError("secret must be str or bytes")
secret = right_pad_string(secret, 14)
return des_encrypt_block(secret[0:7], MAGIC) + \
des_encrypt_block(secret[7:14], MAGIC)
@@ -180,14 +179,6 @@ class nthash(uh.StaticHandler):
# XXX: found refs that say only first 128 chars are used.
return md4(secret.encode("utf-16-le")).digest()
- @classmethod
- def raw_nthash(cls, secret, hex=False):
- warn("nthash.raw_nthash() is deprecated, and will be removed "
- "in Passlib 1.8, please use nthash.raw() instead",
- DeprecationWarning)
- ret = nthash.raw(secret)
- return hexlify(ret).decode("ascii") if hex else ret
-
#===================================================================
# eoc
#===================================================================
@@ -270,7 +261,7 @@ class msdcc(uh.HasUserContext, uh.StaticHandler):
def raw(cls, secret, user):
"""encode password using mscash v1 algorithm
- :arg secret: secret as unicode or utf-8 encoded bytes
+ :arg secret: secret as str or utf-8 encoded bytes
:arg user: username to use as salt
:returns: returns string of raw bytes
@@ -315,7 +306,7 @@ class msdcc2(uh.HasUserContext, uh.StaticHandler):
def raw(cls, secret, user):
"""encode password using msdcc v2 algorithm
- :type secret: unicode or utf-8 bytes
+ :type secret: str or utf-8 bytes
:arg secret: secret
:type user: str
diff --git a/passlib/hash.py b/passlib/hash.py
index 2cc0628..898e315 100644
--- a/passlib/hash.py
+++ b/passlib/hash.py
@@ -45,7 +45,7 @@ if False:
from passlib.handlers.fshp import fshp
from passlib.handlers.ldap_digests import ldap_bcrypt, ldap_bsdi_crypt, ldap_des_crypt, ldap_md5, ldap_md5_crypt, ldap_plaintext, ldap_salted_md5, ldap_salted_sha1, ldap_salted_sha256, ldap_salted_sha512, ldap_sha1, ldap_sha1_crypt, ldap_sha256_crypt, ldap_sha512_crypt
from passlib.handlers.md5_crypt import apr_md5_crypt, md5_crypt
- from passlib.handlers.misc import plaintext, unix_disabled, unix_fallback
+ from passlib.handlers.misc import plaintext, unix_disabled
from passlib.handlers.mssql import mssql2000, mssql2005
from passlib.handlers.mysql import mysql323, mysql41
from passlib.handlers.oracle import oracle10, oracle11
diff --git a/passlib/ifc.py b/passlib/ifc.py
index 559d256..c183d6b 100644
--- a/passlib/ifc.py
+++ b/passlib/ifc.py
@@ -14,28 +14,15 @@ __all__ = [
]
#=============================================================================
-# 2/3 compatibility helpers
-#=============================================================================
-def recreate_with_metaclass(meta):
- """class decorator that re-creates class using metaclass"""
- def builder(cls):
- if meta is type(cls):
- return cls
- return meta(cls.__name__, cls.__bases__, cls.__dict__.copy())
- return builder
-
-#=============================================================================
# PasswordHash interface
#=============================================================================
-from abc import ABCMeta, abstractmethod, abstractproperty
-
-# TODO: make this actually use abstractproperty(),
-# now that we dropped py25, 'abc' is always available.
+from abc import ABC, abstractmethod
-# XXX: rename to PasswordHasher?
+# XXX: mark some attributes with abstractproperty()?
+# or would type hinting be enough?
-@recreate_with_metaclass(ABCMeta)
-class PasswordHash(object):
+# XXX: rename this to PasswordHasher?
+class PasswordHash(ABC):
"""This class describes an abstract interface which all password hashes
in Passlib adhere to. Under Python 2.6 and up, this is an actual
Abstract Base Class built using the :mod:`!abc` module.
diff --git a/passlib/pwd.py b/passlib/pwd.py
index 27ed228..6c7f1a4 100644
--- a/passlib/pwd.py
+++ b/passlib/pwd.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import absolute_import, division, print_function, unicode_literals
# core
import codecs
from collections import defaultdict
@@ -18,7 +17,6 @@ import os
# site
# pkg
from passlib import exc
-from passlib.utils.compat import PY2, irange, itervalues, int_types
from passlib.utils import rng, getrandstr, to_unicode
from passlib.utils.decor import memoized_property
# local
@@ -87,7 +85,7 @@ def _self_info_rate(source):
values = counts.values()
size = sum(values)
else:
- values = itervalues(counts)
+ values = counts.values()
if not size:
return 0
# NOTE: the following performs ``- sum(value / size * logf(value / size, 2) for value in values)``,
@@ -267,7 +265,7 @@ class SequenceGenerator(object):
# hand off to parent
if kwds and _superclasses(self, SequenceGenerator) == (object,):
raise TypeError("Unexpected keyword(s): %s" % ", ".join(kwds.keys()))
- super(SequenceGenerator, self).__init__(**kwds)
+ super().__init__(**kwds)
#=============================================================================
# informational helpers
@@ -304,8 +302,8 @@ class SequenceGenerator(object):
"""
if returns is None:
return next(self)
- elif isinstance(returns, int_types):
- return [next(self) for _ in irange(returns)]
+ elif isinstance(returns, int):
+ return [next(self) for _ in range(returns)]
elif returns is iter:
return self
else:
@@ -314,10 +312,6 @@ class SequenceGenerator(object):
def __iter__(self):
return self
- if PY2:
- def next(self):
- return self.__next__()
-
#=============================================================================
# eoc
#=============================================================================
@@ -396,7 +390,7 @@ class WordGenerator(SequenceGenerator):
self.chars = chars
# hand off to parent
- super(WordGenerator, self).__init__(**kwds)
+ super().__init__(**kwds)
# log.debug("WordGenerator(): entropy/char=%r", self.entropy_per_symbol)
#=============================================================================
@@ -488,7 +482,7 @@ def genword(entropy=None, length=None, returns=None, **kwds):
* ``"hex"`` -- Lower case hexadecimal. Providers 4 bits of entropy per character.
:returns:
- :class:`!unicode` string containing randomly generated password;
+ :class:`!str` string containing randomly generated password;
or list of 1+ passwords if :samp:`returns={int}` is specified.
"""
gen = WordGenerator(length=length, entropy=entropy, **kwds)
@@ -549,7 +543,7 @@ class WordsetDict(MutableMapping):
def __init__(self, *args, **kwds):
self.paths = {}
self._loaded = {}
- super(WordsetDict, self).__init__(*args, **kwds)
+ super().__init__(*args, **kwds)
def __getitem__(self, key):
try:
@@ -661,7 +655,7 @@ class PhraseGenerator(SequenceGenerator):
self.sep = sep
# hand off to parent
- super(PhraseGenerator, self).__init__(**kwds)
+ super().__init__(**kwds)
##log.debug("PhraseGenerator(): entropy/word=%r entropy/char=%r min_chars=%r",
## self.entropy_per_symbol, self.entropy_per_char, self.min_chars)
@@ -678,7 +672,7 @@ class PhraseGenerator(SequenceGenerator):
#=============================================================================
def __next__(self):
- words = (self.rng.choice(self.words) for _ in irange(self.length))
+ words = (self.rng.choice(self.words) for _ in range(self.length))
return self.sep.join(words)
#=============================================================================
@@ -780,7 +774,7 @@ def genphrase(entropy=None, length=None, returns=None, **kwds):
Defaults to ``" "`` (a space), but can be an empty string, a hyphen, etc.
:returns:
- :class:`!unicode` string containing randomly generated passphrase;
+ :class:`!str` containing randomly generated passphrase;
or list of 1+ passphrases if :samp:`returns={int}` is specified.
"""
gen = PhraseGenerator(entropy=entropy, length=length, **kwds)
diff --git a/passlib/registry.py b/passlib/registry.py
index 9964b25..3d2976b 100644
--- a/passlib/registry.py
+++ b/passlib/registry.py
@@ -14,7 +14,6 @@ from passlib.utils import (
is_crypt_handler, has_crypt as os_crypt_present,
unix_crypt_schemes as os_crypt_schemes,
)
-from passlib.utils.compat import unicode_or_str
from passlib.utils.decor import memoize_single_value
# local
__all__ = [
@@ -162,7 +161,6 @@ _locations = dict(
sha512_crypt = "passlib.handlers.sha2_crypt",
sun_md5_crypt = "passlib.handlers.sun_md5_crypt",
unix_disabled = "passlib.handlers.misc",
- unix_fallback = "passlib.handlers.misc",
)
# master regexp for detecting valid handler names
@@ -324,7 +322,7 @@ def get_crypt_handler(name, default=_UNSET):
pass
# normalize name (and if changed, check dict again)
- assert isinstance(name, unicode_or_str), "name must be string instance"
+ assert isinstance(name, str), "name must be string instance"
alt = name.replace("-","_").lower()
if alt != name:
warn("handler names should be lower-case, and use underscores instead "
@@ -435,10 +433,10 @@ def _resolve(hasher, param="value"):
"""
if is_crypt_handler(hasher):
return hasher
- elif isinstance(hasher, unicode_or_str):
+ elif isinstance(hasher, str):
return get_crypt_handler(hasher)
else:
- raise exc.ExpectedTypeError(hasher, unicode_or_str, param)
+ raise exc.ExpectedTypeError(hasher, str, param)
#: backend aliases
diff --git a/passlib/tests/backports.py b/passlib/tests/backports.py
deleted file mode 100644
index 5058cec..0000000
--- a/passlib/tests/backports.py
+++ /dev/null
@@ -1,67 +0,0 @@
-"""backports of needed unittest2 features"""
-#=============================================================================
-# imports
-#=============================================================================
-from __future__ import with_statement
-# core
-import logging; log = logging.getLogger(__name__)
-import re
-import sys
-##from warnings import warn
-# site
-# pkg
-from passlib.utils.compat import PY26
-# local
-__all__ = [
- "TestCase",
- "unittest",
- # TODO: deprecate these exports in favor of "unittest.XXX"
- "skip", "skipIf", "skipUnless",
-]
-
-#=============================================================================
-# import latest unittest module available
-#=============================================================================
-try:
- import unittest2 as unittest
-except ImportError:
- if PY26:
- raise ImportError("Passlib's tests require 'unittest2' under Python 2.6 (as of Passlib 1.7)")
- # python 2.7 and python 3.2 both have unittest2 features (at least, the ones we use)
- import unittest
-
-#=============================================================================
-# unittest aliases
-#=============================================================================
-skip = unittest.skip
-skipIf = unittest.skipIf
-skipUnless = unittest.skipUnless
-SkipTest = unittest.SkipTest
-
-#=============================================================================
-# custom test harness
-#=============================================================================
-class TestCase(unittest.TestCase):
- """backports a number of unittest2 features in TestCase"""
-
- #===================================================================
- # backport some unittest2 names
- #===================================================================
-
- #---------------------------------------------------------------
- # backport assertRegex() alias from 3.2 to 2.7
- # was present in 2.7 under an alternate name
- #---------------------------------------------------------------
- if not hasattr(unittest.TestCase, "assertRegex"):
- assertRegex = unittest.TestCase.assertRegexpMatches
-
- if not hasattr(unittest.TestCase, "assertRaisesRegex"):
- assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
-
- #===================================================================
- # eoc
- #===================================================================
-
-#=============================================================================
-# eof
-#=============================================================================
diff --git a/passlib/tests/test_apache.py b/passlib/tests/test_apache.py
index 198b425..fcdbbc6 100644
--- a/passlib/tests/test_apache.py
+++ b/passlib/tests/test_apache.py
@@ -2,19 +2,16 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
from logging import getLogger
+import unittest
import os
import subprocess
# site
# pkg
from passlib import apache, registry
from passlib.exc import MissingBackendError
-from passlib.utils.compat import irange
-from passlib.tests.backports import unittest
from passlib.tests.utils import TestCase, get_file, set_file, ensure_mtime_changed
-from passlib.utils.compat import u
from passlib.utils import to_bytes
from passlib.utils.handlers import to_unicode_for_identify
# module
@@ -122,8 +119,8 @@ class HtpasswdFileTest(TestCase):
b'user6:$5$rounds=110000$cCRp/xUUGVgwR4aP$'
b'p0.QKFS5qLNRqw1/47lXYiAcgIjJK.WjCO8nrEKuUK.\n')
- def test_00_constructor_autoload(self):
- """test constructor autoload"""
+ def test_00_constructor_new(self):
+ """constructor -- 'new' keyword"""
# check with existing file
path = self.mktemp()
set_file(path, self.sample_01)
@@ -143,13 +140,6 @@ class HtpasswdFileTest(TestCase):
self.assertEqual(ht.path, path)
self.assertFalse(ht.mtime)
- # check autoload=False (deprecated alias for new=True)
- with self.assertWarningList("``autoload=False`` is deprecated"):
- ht = apache.HtpasswdFile(path, autoload=False)
- self.assertEqual(ht.to_string(), b"")
- self.assertEqual(ht.path, path)
- self.assertFalse(ht.mtime)
-
# check missing file
os.remove(path)
self.assertRaises(IOError, apache.HtpasswdFile, path)
@@ -196,21 +186,9 @@ class HtpasswdFileTest(TestCase):
self.assertFalse(ht.set_password("user5", "pass5"))
self.assertEqual(ht.to_string(), self.sample_03)
- # test legacy default kwd
- with self.assertWarningList("``default`` is deprecated"):
- ht = apache.HtpasswdFile.from_string(self.sample_01, default="plaintext")
- self.assertTrue(ht.set_password("user2", "pass2x"))
- self.assertFalse(ht.set_password("user5", "pass5"))
- self.assertEqual(ht.to_string(), self.sample_03)
-
# invalid user
self.assertRaises(ValueError, ht.set_password, "user:", "pass")
- # test that legacy update() still works
- with self.assertWarningList("update\(\) is deprecated"):
- ht.update("user2", "test")
- self.assertTrue(ht.check_password("user2", "test"))
-
def test_02_set_password_autosave(self):
path = self.mktemp()
sample = b'user1:pass1\n'
@@ -263,7 +241,7 @@ class HtpasswdFileTest(TestCase):
# users 1..6 of sample_01 run through all the main hash formats,
# to make sure they're recognized.
- for i in irange(1, 7):
+ for i in range(1, 7):
i = str(i)
try:
self.assertTrue(ht.check_password("user"+i, "pass"+i))
@@ -276,11 +254,6 @@ class HtpasswdFileTest(TestCase):
self.assertRaises(ValueError, ht.check_password, "user:", "pass")
- # test that legacy verify() still works
- with self.assertWarningList(["verify\(\) is deprecated"]*2):
- self.assertTrue(ht.verify("user1", "pass1"))
- self.assertFalse(ht.verify("user1", "pass2"))
-
def test_05_load(self):
"""test load()"""
# setup empty file
@@ -348,17 +321,16 @@ class HtpasswdFileTest(TestCase):
# check sample utf-8
ht = apache.HtpasswdFile.from_string(self.sample_04_utf8, encoding="utf-8",
return_unicode=True)
- self.assertEqual(ht.users(), [ u("user\u00e6") ])
+ self.assertEqual(ht.users(), [ u"user\u00e6" ])
- # test deprecated encoding=None
- with self.assertWarningList("``encoding=None`` is deprecated"):
- ht = apache.HtpasswdFile.from_string(self.sample_04_utf8, encoding=None)
- self.assertEqual(ht.users(), [ b'user\xc3\xa6' ])
+ # encoding=None should throw error
+ self.assertRaises(TypeError, apache.HtpasswdFile.from_string,
+ self.sample_04_utf8, encoding=None)
# check sample latin-1
ht = apache.HtpasswdFile.from_string(self.sample_04_latin1,
encoding="latin-1", return_unicode=True)
- self.assertEqual(ht.users(), [ u("user\u00e6") ])
+ self.assertEqual(ht.users(), [ u"user\u00e6" ])
def test_08_get_hash(self):
"""test get_hash()"""
@@ -367,9 +339,6 @@ class HtpasswdFileTest(TestCase):
self.assertEqual(ht.get_hash("user4"), b"pass4")
self.assertEqual(ht.get_hash("user5"), None)
- with self.assertWarningList("find\(\) is deprecated"):
- self.assertEqual(ht.find("user4"), b"pass4")
-
def test_09_to_string(self):
"""test to_string"""
@@ -602,11 +571,6 @@ class HtdigestFileTest(TestCase):
self.assertRaises(ValueError, ht.set_password, "user", "realm:", "pass")
self.assertRaises(ValueError, ht.set_password, "user", "r"*256, "pass")
- # test that legacy update() still works
- with self.assertWarningList("update\(\) is deprecated"):
- ht.update("user2", "realm2", "test")
- self.assertTrue(ht.check_password("user2", "test"))
-
# TODO: test set_password autosave
def test_03_users(self):
@@ -625,7 +589,7 @@ class HtdigestFileTest(TestCase):
self.assertRaises(TypeError, ht.check_password, 1, 'realm', 'pass5')
self.assertRaises(TypeError, ht.check_password, 'user', 1, 'pass5')
self.assertIs(ht.check_password("user5", "realm","pass5"), None)
- for i in irange(1,5):
+ for i in range(1, 5):
i = str(i)
self.assertTrue(ht.check_password("user"+i, "realm", "pass"+i))
self.assertIs(ht.check_password("user"+i, "realm", "pass5"), False)
@@ -636,11 +600,6 @@ class HtdigestFileTest(TestCase):
self.assertTrue(ht.check_password("user1", "pass1"))
self.assertIs(ht.check_password("user5", "pass5"), None)
- # test that legacy verify() still works
- with self.assertWarningList(["verify\(\) is deprecated"]*2):
- self.assertTrue(ht.verify("user1", "realm", "pass1"))
- self.assertFalse(ht.verify("user1", "realm", "pass2"))
-
# invalid user
self.assertRaises(ValueError, ht.check_password, "user:", "realm", "pass")
@@ -678,13 +637,6 @@ class HtdigestFileTest(TestCase):
hc.load(path)
self.assertEqual(hc.to_string(), self.sample_01)
- # change file, test deprecated force=False kwd
- ensure_mtime_changed(path)
- set_file(path, "")
- with self.assertWarningList(r"load\(force=False\) is deprecated"):
- ha.load(force=False)
- self.assertEqual(ha.to_string(), b"")
-
def test_06_save(self):
"""test save()"""
# load from file
@@ -725,9 +677,6 @@ class HtdigestFileTest(TestCase):
self.assertEqual(ht.get_hash("user4", "realm"), "ab7b5d5f28ccc7666315f508c7358519")
self.assertEqual(ht.get_hash("user5", "realm"), None)
- with self.assertWarningList("find\(\) is deprecated"):
- self.assertEqual(ht.find("user4", "realm"), "ab7b5d5f28ccc7666315f508c7358519")
-
def test_09_encodings(self):
"""test encoding parameter"""
# test bad encodings cause failure in constructor
@@ -735,13 +684,13 @@ class HtdigestFileTest(TestCase):
# check sample utf-8
ht = apache.HtdigestFile.from_string(self.sample_04_utf8, encoding="utf-8", return_unicode=True)
- self.assertEqual(ht.realms(), [ u("realm\u00e6") ])
- self.assertEqual(ht.users(u("realm\u00e6")), [ u("user\u00e6") ])
+ self.assertEqual(ht.realms(), [ u"realm\u00e6" ])
+ self.assertEqual(ht.users(u"realm\u00e6"), [ u"user\u00e6" ])
# check sample latin-1
ht = apache.HtdigestFile.from_string(self.sample_04_latin1, encoding="latin-1", return_unicode=True)
- self.assertEqual(ht.realms(), [ u("realm\u00e6") ])
- self.assertEqual(ht.users(u("realm\u00e6")), [ u("user\u00e6") ])
+ self.assertEqual(ht.realms(), [ u"realm\u00e6" ])
+ self.assertEqual(ht.users(u"realm\u00e6"), [ u"user\u00e6" ])
def test_10_to_string(self):
"""test to_string()"""
diff --git a/passlib/tests/test_apps.py b/passlib/tests/test_apps.py
index 167437f..3a2b0d3 100644
--- a/passlib/tests/test_apps.py
+++ b/passlib/tests/test_apps.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
import logging; log = logging.getLogger(__name__)
# site
diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py
index 09b52c0..d7f90c6 100644
--- a/passlib/tests/test_context.py
+++ b/passlib/tests/test_context.py
@@ -3,12 +3,7 @@
# imports
#=============================================================================
# core
-from __future__ import with_statement
-from passlib.utils.compat import PY3
-if PY3:
- from configparser import NoSectionError
-else:
- from ConfigParser import NoSectionError
+from configparser import NoSectionError
import datetime
from functools import partial
import logging; log = logging.getLogger(__name__)
@@ -20,7 +15,6 @@ from passlib import hash
from passlib.context import CryptContext, LazyCryptContext
from passlib.exc import PasslibConfigWarning, PasslibHashWarning
from passlib.utils import tick, to_unicode
-from passlib.utils.compat import irange, u, unicode, str_to_uascii, PY2, PY26
import passlib.utils.handlers as uh
from passlib.tests.utils import (TestCase, set_file, TICK_RESOLUTION,
quicksleep, time_call, handler_derived_from)
@@ -75,7 +69,7 @@ class CryptContextTest(TestCase):
sample_1_resolved_dict = merge_dicts(sample_1_dict,
schemes = sample_1_handlers)
- sample_1_unnormalized = u("""\
+ sample_1_unnormalized = u"""\
[passlib]
schemes = des_crypt, md5_crypt, bsdi_crypt, sha512_crypt
default = md5_crypt
@@ -85,9 +79,9 @@ bsdi_crypt__default_rounds = 25001
bsdi_crypt__max_rounds = 30001
sha512_crypt__max_rounds = 50000
sha512_crypt__min_rounds = 40000
-""")
+"""
- sample_1_unicode = u("""\
+ sample_1_unicode = u"""\
[passlib]
schemes = des_crypt, md5_crypt, bsdi_crypt, sha512_crypt
default = md5_crypt
@@ -97,7 +91,7 @@ bsdi_crypt__max_rounds = 30001
sha512_crypt__max_rounds = 50000
sha512_crypt__min_rounds = 40000
-""")
+"""
#---------------------------------------------------------------
# sample 1 external files
@@ -107,12 +101,12 @@ sha512_crypt__min_rounds = 40000
sample_1_path = os.path.join(here, "sample1.cfg")
# sample 1 with '\r\n' linesep
- sample_1b_unicode = sample_1_unicode.replace(u("\n"), u("\r\n"))
+ sample_1b_unicode = sample_1_unicode.replace(u"\n", u"\r\n")
sample_1b_path = os.path.join(here, "sample1b.cfg")
# sample 1 using UTF-16 and alt section
- sample_1c_bytes = sample_1_unicode.replace(u("[passlib]"),
- u("[mypolicy]")).encode("utf-16")
+ sample_1c_bytes = sample_1_unicode.replace(u"[passlib]",
+ u"[mypolicy]").encode("utf-16")
sample_1c_path = os.path.join(here, "sample1c.cfg")
# enable to regenerate sample files
@@ -176,7 +170,7 @@ sha512_crypt__min_rounds = 45000
# setup
#===================================================================
def setUp(self):
- super(CryptContextTest, self).setUp()
+ super().setUp()
warnings.filterwarnings("ignore", "The 'all' scheme is deprecated.*")
warnings.filterwarnings("ignore", ".*'scheme' keyword is deprecated as of Passlib 1.7.*")
@@ -207,7 +201,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(ctx.to_dict(), self.sample_3_dict)
# test unicode scheme names (issue 54)
- ctx = CryptContext(schemes=[u("sha256_crypt")])
+ ctx = CryptContext(schemes=[u"sha256_crypt"])
self.assertEqual(ctx.schemes(), ("sha256_crypt",))
def test_02_from_string(self):
@@ -748,11 +742,6 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(ctx.handler(category="admin", unconfigured=True), hash.md5_crypt)
self.assertHandlerDerivedFrom(ctx.handler(category="staff"), hash.sha256_crypt)
- # test unicode category strings are accepted under py2
- if PY2:
- self.assertEqual(ctx.handler(category=u("staff"), unconfigured=True), hash.sha256_crypt)
- self.assertEqual(ctx.handler(category=u("admin"), unconfigured=True), hash.md5_crypt)
-
def test_33_options(self):
"""test internal _get_record_options() method"""
@@ -847,11 +836,7 @@ sha512_crypt__min_rounds = 45000
dump = ctx.to_string()
# check ctx->string returns canonical format.
- # NOTE: ConfigParser for PY26 doesn't use OrderedDict,
- # making to_string()'s ordering unpredictable...
- # so we skip this test under PY26.
- if not PY26:
- self.assertEqual(dump, self.sample_1_unicode)
+ self.assertEqual(dump, self.sample_1_unicode)
# check ctx->string->ctx->dict returns original
ctx2 = CryptContext.from_string(dump)
@@ -933,14 +918,6 @@ sha512_crypt__min_rounds = 45000
# border cases
#--------------------------------------------------------------
- # test unicode category strings are accepted under py2
- # this tests basic _get_record() used by hash/genhash/verify.
- # we have to omit scheme=xxx so codepath is tested fully
- if PY2:
- c2 = cc.copy(default="phpass")
- self.assertTrue(c2.genconfig(category=u("admin")).startswith("$P$5"))
- self.assertTrue(c2.genconfig(category=u("staff")).startswith("$H$5"))
-
# throws error without schemes
self.assertRaises(KeyError, CryptContext().genconfig)
self.assertRaises(KeyError, CryptContext().genconfig, scheme='md5_crypt')
@@ -1175,9 +1152,9 @@ sha512_crypt__min_rounds = 45000
def _calc_checksum(self, secret):
from hashlib import md5
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
- return str_to_uascii(md5(secret).hexdigest())
+ return md5(secret).hexdigest()
# calling needs_update should query callback
ctx = CryptContext([dummy])
@@ -1568,7 +1545,7 @@ sha512_crypt__min_rounds = 45000
handler = context.handler(scheme)
salt = handler.default_salt_chars[0:1] * handler.max_salt_size
seen = set()
- for i in irange(300):
+ for i in range(300):
h = context.genconfig(scheme, salt=salt)
r = handler.from_string(h).rounds
seen.add(r)
@@ -1576,20 +1553,8 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(max(seen), upper, "vary_rounds had wrong upper limit:")
#===================================================================
- # harden_verify / min_verify_time
+ # dummy_verify()
#===================================================================
- def test_harden_verify_parsing(self):
- """harden_verify -- parsing"""
- warnings.filterwarnings("ignore", ".*harden_verify.*",
- category=DeprecationWarning)
-
- # valid values
- ctx = CryptContext(schemes=["sha256_crypt"])
- self.assertEqual(ctx.harden_verify, None)
- self.assertEqual(ctx.using(harden_verify="").harden_verify, None)
- self.assertEqual(ctx.using(harden_verify="true").harden_verify, None)
- self.assertEqual(ctx.using(harden_verify="false").harden_verify, None)
-
def test_dummy_verify(self):
"""
dummy_verify() method
@@ -1728,13 +1693,13 @@ class DelayHash(uh.StaticHandler):
checksum_chars = uh.LOWER_HEX_CHARS
checksum_size = 40
delay = 0
- _hash_prefix = u("$x$")
+ _hash_prefix = u"$x$"
def _calc_checksum(self, secret):
time.sleep(self.delay)
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
- return str_to_uascii(hashlib.sha1(b"prefix" + secret).hexdigest())
+ return hashlib.sha1(b"prefix" + secret).hexdigest()
#=============================================================================
# LazyCryptContext
diff --git a/passlib/tests/test_context_deprecated.py b/passlib/tests/test_context_deprecated.py
deleted file mode 100644
index 0f76624..0000000
--- a/passlib/tests/test_context_deprecated.py
+++ /dev/null
@@ -1,743 +0,0 @@
-"""tests for passlib.context
-
-this file is a clone of the 1.5 test_context.py,
-containing the tests using the legacy CryptPolicy api.
-it's being preserved here to ensure the old api doesn't break
-(until Passlib 1.8, when this and the legacy api will be removed).
-"""
-#=============================================================================
-# imports
-#=============================================================================
-from __future__ import with_statement
-# core
-from logging import getLogger
-import os
-import warnings
-# site
-try:
- from pkg_resources import resource_filename
-except ImportError:
- resource_filename = None
-# pkg
-from passlib import hash
-from passlib.context import CryptContext, CryptPolicy, LazyCryptContext
-from passlib.utils import to_bytes, to_unicode
-import passlib.utils.handlers as uh
-from passlib.tests.utils import TestCase, set_file
-from passlib.registry import (register_crypt_handler_path,
- _has_crypt_handler as has_crypt_handler,
- _unload_handler_name as unload_handler_name,
- )
-# module
-log = getLogger(__name__)
-
-#=============================================================================
-#
-#=============================================================================
-class CryptPolicyTest(TestCase):
- """test CryptPolicy object"""
-
- # TODO: need to test user categories w/in all this
-
- descriptionPrefix = "CryptPolicy"
-
- #===================================================================
- # sample crypt policies used for testing
- #===================================================================
-
- #---------------------------------------------------------------
- # sample 1 - average config file
- #---------------------------------------------------------------
- # NOTE: copy of this is stored in file passlib/tests/sample_config_1s.cfg
- sample_config_1s = """\
-[passlib]
-schemes = des_crypt, md5_crypt, bsdi_crypt, sha512_crypt
-default = md5_crypt
-all.vary_rounds = 10%%
-bsdi_crypt.max_rounds = 30000
-bsdi_crypt.default_rounds = 25000
-sha512_crypt.max_rounds = 50000
-sha512_crypt.min_rounds = 40000
-"""
- sample_config_1s_path = os.path.abspath(os.path.join(
- os.path.dirname(__file__), "sample_config_1s.cfg"))
- if not os.path.exists(sample_config_1s_path) and resource_filename:
- # in case we're zipped up in an egg.
- sample_config_1s_path = resource_filename("passlib.tests",
- "sample_config_1s.cfg")
-
- # make sure sample_config_1s uses \n linesep - tests rely on this
- assert sample_config_1s.startswith("[passlib]\nschemes")
-
- sample_config_1pd = dict(
- schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"],
- default = "md5_crypt",
- # NOTE: not maintaining backwards compat for rendering to "10%"
- all__vary_rounds = 0.1,
- bsdi_crypt__max_rounds = 30000,
- bsdi_crypt__default_rounds = 25000,
- sha512_crypt__max_rounds = 50000,
- sha512_crypt__min_rounds = 40000,
- )
-
- sample_config_1pid = {
- "schemes": "des_crypt, md5_crypt, bsdi_crypt, sha512_crypt",
- "default": "md5_crypt",
- # NOTE: not maintaining backwards compat for rendering to "10%"
- "all.vary_rounds": 0.1,
- "bsdi_crypt.max_rounds": 30000,
- "bsdi_crypt.default_rounds": 25000,
- "sha512_crypt.max_rounds": 50000,
- "sha512_crypt.min_rounds": 40000,
- }
-
- sample_config_1prd = dict(
- schemes = [ hash.des_crypt, hash.md5_crypt, hash.bsdi_crypt, hash.sha512_crypt],
- default = "md5_crypt", # NOTE: passlib <= 1.5 was handler obj.
- # NOTE: not maintaining backwards compat for rendering to "10%"
- all__vary_rounds = 0.1,
- bsdi_crypt__max_rounds = 30000,
- bsdi_crypt__default_rounds = 25000,
- sha512_crypt__max_rounds = 50000,
- sha512_crypt__min_rounds = 40000,
- )
-
- #---------------------------------------------------------------
- # sample 2 - partial policy & result of overlay on sample 1
- #---------------------------------------------------------------
- sample_config_2s = """\
-[passlib]
-bsdi_crypt.min_rounds = 29000
-bsdi_crypt.max_rounds = 35000
-bsdi_crypt.default_rounds = 31000
-sha512_crypt.min_rounds = 45000
-"""
-
- sample_config_2pd = dict(
- # using this to test full replacement of existing options
- bsdi_crypt__min_rounds = 29000,
- bsdi_crypt__max_rounds = 35000,
- bsdi_crypt__default_rounds = 31000,
- # using this to test partial replacement of existing options
- sha512_crypt__min_rounds=45000,
- )
-
- sample_config_12pd = dict(
- schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"],
- default = "md5_crypt",
- # NOTE: not maintaining backwards compat for rendering to "10%"
- all__vary_rounds = 0.1,
- bsdi_crypt__min_rounds = 29000,
- bsdi_crypt__max_rounds = 35000,
- bsdi_crypt__default_rounds = 31000,
- sha512_crypt__max_rounds = 50000,
- sha512_crypt__min_rounds=45000,
- )
-
- #---------------------------------------------------------------
- # sample 3 - just changing default
- #---------------------------------------------------------------
- sample_config_3pd = dict(
- default="sha512_crypt",
- )
-
- sample_config_123pd = dict(
- schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"],
- default = "sha512_crypt",
- # NOTE: not maintaining backwards compat for rendering to "10%"
- all__vary_rounds = 0.1,
- bsdi_crypt__min_rounds = 29000,
- bsdi_crypt__max_rounds = 35000,
- bsdi_crypt__default_rounds = 31000,
- sha512_crypt__max_rounds = 50000,
- sha512_crypt__min_rounds=45000,
- )
-
- #---------------------------------------------------------------
- # sample 4 - category specific
- #---------------------------------------------------------------
- sample_config_4s = """
-[passlib]
-schemes = sha512_crypt
-all.vary_rounds = 10%%
-default.sha512_crypt.max_rounds = 20000
-admin.all.vary_rounds = 5%%
-admin.sha512_crypt.max_rounds = 40000
-"""
-
- sample_config_4pd = dict(
- schemes = [ "sha512_crypt" ],
- # NOTE: not maintaining backwards compat for rendering to "10%"
- all__vary_rounds = 0.1,
- sha512_crypt__max_rounds = 20000,
- # NOTE: not maintaining backwards compat for rendering to "5%"
- admin__all__vary_rounds = 0.05,
- admin__sha512_crypt__max_rounds = 40000,
- )
-
- #---------------------------------------------------------------
- # sample 5 - to_string & deprecation testing
- #---------------------------------------------------------------
- sample_config_5s = sample_config_1s + """\
-deprecated = des_crypt
-admin__context__deprecated = des_crypt, bsdi_crypt
-"""
-
- sample_config_5pd = sample_config_1pd.copy()
- sample_config_5pd.update(
- deprecated = [ "des_crypt" ],
- admin__context__deprecated = [ "des_crypt", "bsdi_crypt" ],
- )
-
- sample_config_5pid = sample_config_1pid.copy()
- sample_config_5pid.update({
- "deprecated": "des_crypt",
- "admin.context.deprecated": "des_crypt, bsdi_crypt",
- })
-
- sample_config_5prd = sample_config_1prd.copy()
- sample_config_5prd.update({
- # XXX: should deprecated return the actual handlers in this case?
- # would have to modify how policy stores info, for one.
- "deprecated": ["des_crypt"],
- "admin__context__deprecated": ["des_crypt", "bsdi_crypt"],
- })
-
- #===================================================================
- # constructors
- #===================================================================
- def setUp(self):
- TestCase.setUp(self)
- warnings.filterwarnings("ignore",
- r"The CryptPolicy class has been deprecated")
- warnings.filterwarnings("ignore",
- r"the method.*hash_needs_update.*is deprecated")
- warnings.filterwarnings("ignore", "The 'all' scheme is deprecated.*")
- warnings.filterwarnings("ignore", "bsdi_crypt rounds should be odd")
-
- def test_00_constructor(self):
- """test CryptPolicy() constructor"""
- policy = CryptPolicy(**self.sample_config_1pd)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- policy = CryptPolicy(self.sample_config_1pd)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- self.assertRaises(TypeError, CryptPolicy, {}, {})
- self.assertRaises(TypeError, CryptPolicy, {}, dummy=1)
-
- # check key with too many separators is rejected
- self.assertRaises(TypeError, CryptPolicy,
- schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"],
- bad__key__bsdi_crypt__max_rounds = 30000,
- )
-
- # check nameless handler rejected
- class nameless(uh.StaticHandler):
- name = None
- self.assertRaises(ValueError, CryptPolicy, schemes=[nameless])
-
- # check scheme must be name or crypt handler
- self.assertRaises(TypeError, CryptPolicy, schemes=[uh.StaticHandler])
-
- # check name conflicts are rejected
- class dummy_1(uh.StaticHandler):
- name = 'dummy_1'
- self.assertRaises(KeyError, CryptPolicy, schemes=[dummy_1, dummy_1])
-
- # with unknown deprecated value
- self.assertRaises(KeyError, CryptPolicy,
- schemes=['des_crypt'],
- deprecated=['md5_crypt'])
-
- # with unknown default value
- self.assertRaises(KeyError, CryptPolicy,
- schemes=['des_crypt'],
- default='md5_crypt')
-
- def test_01_from_path_simple(self):
- """test CryptPolicy.from_path() constructor"""
- # NOTE: this is separate so it can also run under GAE
-
- # test preset stored in existing file
- path = self.sample_config_1s_path
- policy = CryptPolicy.from_path(path)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # test if path missing
- self.assertRaises(EnvironmentError, CryptPolicy.from_path, path + 'xxx')
-
- def test_01_from_path(self):
- """test CryptPolicy.from_path() constructor with encodings"""
- path = self.mktemp()
-
- # test "\n" linesep
- set_file(path, self.sample_config_1s)
- policy = CryptPolicy.from_path(path)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # test "\r\n" linesep
- set_file(path, self.sample_config_1s.replace("\n","\r\n"))
- policy = CryptPolicy.from_path(path)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # test with custom encoding
- uc2 = to_bytes(self.sample_config_1s, "utf-16", source_encoding="utf-8")
- set_file(path, uc2)
- policy = CryptPolicy.from_path(path, encoding="utf-16")
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- def test_02_from_string(self):
- """test CryptPolicy.from_string() constructor"""
- # test "\n" linesep
- policy = CryptPolicy.from_string(self.sample_config_1s)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # test "\r\n" linesep
- policy = CryptPolicy.from_string(
- self.sample_config_1s.replace("\n","\r\n"))
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # test with unicode
- data = to_unicode(self.sample_config_1s)
- policy = CryptPolicy.from_string(data)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # test with non-ascii-compatible encoding
- uc2 = to_bytes(self.sample_config_1s, "utf-16", source_encoding="utf-8")
- policy = CryptPolicy.from_string(uc2, encoding="utf-16")
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # test category specific options
- policy = CryptPolicy.from_string(self.sample_config_4s)
- self.assertEqual(policy.to_dict(), self.sample_config_4pd)
-
- def test_03_from_source(self):
- """test CryptPolicy.from_source() constructor"""
- # pass it a path
- policy = CryptPolicy.from_source(self.sample_config_1s_path)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # pass it a string
- policy = CryptPolicy.from_source(self.sample_config_1s)
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # pass it a dict (NOTE: make a copy to detect in-place modifications)
- policy = CryptPolicy.from_source(self.sample_config_1pd.copy())
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # pass it existing policy
- p2 = CryptPolicy.from_source(policy)
- self.assertIs(policy, p2)
-
- # pass it something wrong
- self.assertRaises(TypeError, CryptPolicy.from_source, 1)
- self.assertRaises(TypeError, CryptPolicy.from_source, [])
-
- def test_04_from_sources(self):
- """test CryptPolicy.from_sources() constructor"""
-
- # pass it empty list
- self.assertRaises(ValueError, CryptPolicy.from_sources, [])
-
- # pass it one-element list
- policy = CryptPolicy.from_sources([self.sample_config_1s])
- self.assertEqual(policy.to_dict(), self.sample_config_1pd)
-
- # pass multiple sources
- policy = CryptPolicy.from_sources(
- [
- self.sample_config_1s_path,
- self.sample_config_2s,
- self.sample_config_3pd,
- ])
- self.assertEqual(policy.to_dict(), self.sample_config_123pd)
-
- def test_05_replace(self):
- """test CryptPolicy.replace() constructor"""
-
- p1 = CryptPolicy(**self.sample_config_1pd)
-
- # check overlaying sample 2
- p2 = p1.replace(**self.sample_config_2pd)
- self.assertEqual(p2.to_dict(), self.sample_config_12pd)
-
- # check repeating overlay makes no change
- p2b = p2.replace(**self.sample_config_2pd)
- self.assertEqual(p2b.to_dict(), self.sample_config_12pd)
-
- # check overlaying sample 3
- p3 = p2.replace(self.sample_config_3pd)
- self.assertEqual(p3.to_dict(), self.sample_config_123pd)
-
- def test_06_forbidden(self):
- """test CryptPolicy() forbidden kwds"""
-
- # salt not allowed to be set
- self.assertRaises(KeyError, CryptPolicy,
- schemes=["des_crypt"],
- des_crypt__salt="xx",
- )
- self.assertRaises(KeyError, CryptPolicy,
- schemes=["des_crypt"],
- all__salt="xx",
- )
-
- # schemes not allowed for category
- self.assertRaises(KeyError, CryptPolicy,
- schemes=["des_crypt"],
- user__context__schemes=["md5_crypt"],
- )
-
- #===================================================================
- # reading
- #===================================================================
- def test_10_has_schemes(self):
- """test has_schemes() method"""
-
- p1 = CryptPolicy(**self.sample_config_1pd)
- self.assertTrue(p1.has_schemes())
-
- p3 = CryptPolicy(**self.sample_config_3pd)
- self.assertTrue(not p3.has_schemes())
-
- def test_11_iter_handlers(self):
- """test iter_handlers() method"""
-
- p1 = CryptPolicy(**self.sample_config_1pd)
- s = self.sample_config_1prd['schemes']
- self.assertEqual(list(p1.iter_handlers()), s)
-
- p3 = CryptPolicy(**self.sample_config_3pd)
- self.assertEqual(list(p3.iter_handlers()), [])
-
- def test_12_get_handler(self):
- """test get_handler() method"""
-
- p1 = CryptPolicy(**self.sample_config_1pd)
-
- # check by name
- self.assertIs(p1.get_handler("bsdi_crypt"), hash.bsdi_crypt)
-
- # check by missing name
- self.assertIs(p1.get_handler("sha256_crypt"), None)
- self.assertRaises(KeyError, p1.get_handler, "sha256_crypt", required=True)
-
- # check default
- self.assertIs(p1.get_handler(), hash.md5_crypt)
-
- def test_13_get_options(self):
- """test get_options() method"""
-
- p12 = CryptPolicy(**self.sample_config_12pd)
-
- self.assertEqual(p12.get_options("bsdi_crypt"),dict(
- # NOTE: not maintaining backwards compat for rendering to "10%"
- vary_rounds = 0.1,
- min_rounds = 29000,
- max_rounds = 35000,
- default_rounds = 31000,
- ))
-
- self.assertEqual(p12.get_options("sha512_crypt"),dict(
- # NOTE: not maintaining backwards compat for rendering to "10%"
- vary_rounds = 0.1,
- min_rounds = 45000,
- max_rounds = 50000,
- ))
-
- p4 = CryptPolicy.from_string(self.sample_config_4s)
- self.assertEqual(p4.get_options("sha512_crypt"), dict(
- # NOTE: not maintaining backwards compat for rendering to "10%"
- vary_rounds=0.1,
- max_rounds=20000,
- ))
-
- self.assertEqual(p4.get_options("sha512_crypt", "user"), dict(
- # NOTE: not maintaining backwards compat for rendering to "10%"
- vary_rounds=0.1,
- max_rounds=20000,
- ))
-
- self.assertEqual(p4.get_options("sha512_crypt", "admin"), dict(
- # NOTE: not maintaining backwards compat for rendering to "5%"
- vary_rounds=0.05,
- max_rounds=40000,
- ))
-
- def test_14_handler_is_deprecated(self):
- """test handler_is_deprecated() method"""
- pa = CryptPolicy(**self.sample_config_1pd)
- pb = CryptPolicy(**self.sample_config_5pd)
-
- self.assertFalse(pa.handler_is_deprecated("des_crypt"))
- self.assertFalse(pa.handler_is_deprecated(hash.bsdi_crypt))
- self.assertFalse(pa.handler_is_deprecated("sha512_crypt"))
-
- self.assertTrue(pb.handler_is_deprecated("des_crypt"))
- self.assertFalse(pb.handler_is_deprecated(hash.bsdi_crypt))
- self.assertFalse(pb.handler_is_deprecated("sha512_crypt"))
-
- # check categories as well
- self.assertTrue(pb.handler_is_deprecated("des_crypt", "user"))
- self.assertFalse(pb.handler_is_deprecated("bsdi_crypt", "user"))
- self.assertTrue(pb.handler_is_deprecated("des_crypt", "admin"))
- self.assertTrue(pb.handler_is_deprecated("bsdi_crypt", "admin"))
-
- # check deprecation is overridden per category
- pc = CryptPolicy(
- schemes=["md5_crypt", "des_crypt"],
- deprecated=["md5_crypt"],
- user__context__deprecated=["des_crypt"],
- )
- self.assertTrue(pc.handler_is_deprecated("md5_crypt"))
- self.assertFalse(pc.handler_is_deprecated("des_crypt"))
- self.assertFalse(pc.handler_is_deprecated("md5_crypt", "user"))
- self.assertTrue(pc.handler_is_deprecated("des_crypt", "user"))
-
- def test_15_min_verify_time(self):
- """test get_min_verify_time() method"""
- # silence deprecation warnings for min verify time
- warnings.filterwarnings("ignore", category=DeprecationWarning)
-
- pa = CryptPolicy()
- self.assertEqual(pa.get_min_verify_time(), 0)
- self.assertEqual(pa.get_min_verify_time('admin'), 0)
-
- pb = pa.replace(min_verify_time=.1)
- self.assertEqual(pb.get_min_verify_time(), 0)
- self.assertEqual(pb.get_min_verify_time('admin'), 0)
-
- #===================================================================
- # serialization
- #===================================================================
- def test_20_iter_config(self):
- """test iter_config() method"""
- p5 = CryptPolicy(**self.sample_config_5pd)
- self.assertEqual(dict(p5.iter_config()), self.sample_config_5pd)
- self.assertEqual(dict(p5.iter_config(resolve=True)), self.sample_config_5prd)
- self.assertEqual(dict(p5.iter_config(ini=True)), self.sample_config_5pid)
-
- def test_21_to_dict(self):
- """test to_dict() method"""
- p5 = CryptPolicy(**self.sample_config_5pd)
- self.assertEqual(p5.to_dict(), self.sample_config_5pd)
- self.assertEqual(p5.to_dict(resolve=True), self.sample_config_5prd)
-
- def test_22_to_string(self):
- """test to_string() method"""
- pa = CryptPolicy(**self.sample_config_5pd)
- s = pa.to_string() # NOTE: can't compare string directly, ordering etc may not match
- pb = CryptPolicy.from_string(s)
- self.assertEqual(pb.to_dict(), self.sample_config_5pd)
-
- s = pa.to_string(encoding="latin-1")
- self.assertIsInstance(s, bytes)
-
- #===================================================================
- #
- #===================================================================
-
-#=============================================================================
-# CryptContext
-#=============================================================================
-class CryptContextTest(TestCase):
- """test CryptContext class"""
- descriptionPrefix = "CryptContext"
-
- def setUp(self):
- TestCase.setUp(self)
- warnings.filterwarnings("ignore",
- r"CryptContext\(\)\.replace\(\) has been deprecated.*")
- warnings.filterwarnings("ignore",
- r"The CryptContext ``policy`` keyword has been deprecated.*")
- warnings.filterwarnings("ignore", ".*(CryptPolicy|context\.policy).*(has|have) been deprecated.*")
- warnings.filterwarnings("ignore",
- r"the method.*hash_needs_update.*is deprecated")
-
- #===================================================================
- # constructor
- #===================================================================
- def test_00_constructor(self):
- """test constructor"""
- # create crypt context using handlers
- cc = CryptContext([hash.md5_crypt, hash.bsdi_crypt, hash.des_crypt])
- c,b,a = cc.policy.iter_handlers()
- self.assertIs(a, hash.des_crypt)
- self.assertIs(b, hash.bsdi_crypt)
- self.assertIs(c, hash.md5_crypt)
-
- # create context using names
- cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"])
- c,b,a = cc.policy.iter_handlers()
- self.assertIs(a, hash.des_crypt)
- self.assertIs(b, hash.bsdi_crypt)
- self.assertIs(c, hash.md5_crypt)
-
- # policy kwd
- policy = cc.policy
- cc = CryptContext(policy=policy)
- self.assertEqual(cc.to_dict(), policy.to_dict())
-
- cc = CryptContext(policy=policy, default="bsdi_crypt")
- self.assertNotEqual(cc.to_dict(), policy.to_dict())
- self.assertEqual(cc.to_dict(), dict(schemes=["md5_crypt","bsdi_crypt","des_crypt"],
- default="bsdi_crypt"))
-
- self.assertRaises(TypeError, setattr, cc, 'policy', None)
- self.assertRaises(TypeError, CryptContext, policy='x')
-
- def test_01_replace(self):
- """test replace()"""
-
- cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"])
- self.assertIs(cc.policy.get_handler(), hash.md5_crypt)
-
- cc2 = cc.replace()
- self.assertIsNot(cc2, cc)
- # NOTE: was not able to maintain backward compatibility with this...
- ##self.assertIs(cc2.policy, cc.policy)
-
- cc3 = cc.replace(default="bsdi_crypt")
- self.assertIsNot(cc3, cc)
- # NOTE: was not able to maintain backward compatibility with this...
- ##self.assertIs(cc3.policy, cc.policy)
- self.assertIs(cc3.policy.get_handler(), hash.bsdi_crypt)
-
- def test_02_no_handlers(self):
- """test no handlers"""
-
- # check constructor...
- cc = CryptContext()
- self.assertRaises(KeyError, cc.identify, 'hash', required=True)
- self.assertRaises(KeyError, cc.hash, 'secret')
- self.assertRaises(KeyError, cc.verify, 'secret', 'hash')
-
- # check updating policy after the fact...
- cc = CryptContext(['md5_crypt'])
- p = CryptPolicy(schemes=[])
- cc.policy = p
-
- self.assertRaises(KeyError, cc.identify, 'hash', required=True)
- self.assertRaises(KeyError, cc.hash, 'secret')
- self.assertRaises(KeyError, cc.verify, 'secret', 'hash')
-
- #===================================================================
- # policy adaptation
- #===================================================================
- sample_policy_1 = dict(
- schemes = [ "des_crypt", "md5_crypt", "phpass", "bsdi_crypt",
- "sha256_crypt"],
- deprecated = [ "des_crypt", ],
- default = "sha256_crypt",
- bsdi_crypt__max_rounds = 30,
- bsdi_crypt__default_rounds = 25,
- bsdi_crypt__vary_rounds = 0,
- sha256_crypt__max_rounds = 3000,
- sha256_crypt__min_rounds = 2000,
- sha256_crypt__default_rounds = 3000,
- phpass__ident = "H",
- phpass__default_rounds = 7,
- )
-
- def test_12_hash_needs_update(self):
- """test hash_needs_update() method"""
- cc = CryptContext(**self.sample_policy_1)
-
- # check deprecated scheme
- self.assertTrue(cc.hash_needs_update('9XXD4trGYeGJA'))
- self.assertFalse(cc.hash_needs_update('$1$J8HC2RCr$HcmM.7NxB2weSvlw2FgzU0'))
-
- # check min rounds
- self.assertTrue(cc.hash_needs_update('$5$rounds=1999$jD81UCoo.zI.UETs$Y7qSTQ6mTiU9qZB4fRr43wRgQq4V.5AAf7F97Pzxey/'))
- self.assertFalse(cc.hash_needs_update('$5$rounds=2000$228SSRje04cnNCaQ$YGV4RYu.5sNiBvorQDlO0WWQjyJVGKBcJXz3OtyQ2u8'))
-
- # check max rounds
- self.assertFalse(cc.hash_needs_update('$5$rounds=3000$fS9iazEwTKi7QPW4$VasgBC8FqlOvD7x2HhABaMXCTh9jwHclPA9j5YQdns.'))
- self.assertTrue(cc.hash_needs_update('$5$rounds=3001$QlFHHifXvpFX4PLs$/0ekt7lSs/lOikSerQ0M/1porEHxYq7W/2hdFpxA3fA'))
-
- #===================================================================
- # border cases
- #===================================================================
- def test_30_nonstring_hash(self):
- """test non-string hash values cause error"""
- warnings.filterwarnings("ignore", ".*needs_update.*'scheme' keyword is deprecated.*")
-
- #
- # test hash=None or some other non-string causes TypeError
- # and that explicit-scheme code path behaves the same.
- #
- cc = CryptContext(["des_crypt"])
- for hash, kwds in [
- (None, {}),
- # NOTE: 'scheme' kwd is deprecated...
- (None, {"scheme": "des_crypt"}),
- (1, {}),
- ((), {}),
- ]:
-
- self.assertRaises(TypeError, cc.hash_needs_update, hash, **kwds)
-
- cc2 = CryptContext(["mysql323"])
- self.assertRaises(TypeError, cc2.hash_needs_update, None)
-
- #===================================================================
- # eoc
- #===================================================================
-
-#=============================================================================
-# LazyCryptContext
-#=============================================================================
-class dummy_2(uh.StaticHandler):
- name = "dummy_2"
-
-class LazyCryptContextTest(TestCase):
- descriptionPrefix = "LazyCryptContext"
-
- def setUp(self):
- TestCase.setUp(self)
-
- # make sure this isn't registered before OR after
- unload_handler_name("dummy_2")
- self.addCleanup(unload_handler_name, "dummy_2")
-
- # silence some warnings
- warnings.filterwarnings("ignore",
- r"CryptContext\(\)\.replace\(\) has been deprecated")
- warnings.filterwarnings("ignore", ".*(CryptPolicy|context\.policy).*(has|have) been deprecated.*")
-
- def test_kwd_constructor(self):
- """test plain kwds"""
- self.assertFalse(has_crypt_handler("dummy_2"))
- register_crypt_handler_path("dummy_2", "passlib.tests.test_context")
-
- cc = LazyCryptContext(iter(["dummy_2", "des_crypt"]), deprecated=["des_crypt"])
-
- self.assertFalse(has_crypt_handler("dummy_2", True))
-
- self.assertTrue(cc.policy.handler_is_deprecated("des_crypt"))
- self.assertEqual(cc.policy.schemes(), ["dummy_2", "des_crypt"])
-
- self.assertTrue(has_crypt_handler("dummy_2", True))
-
- def test_callable_constructor(self):
- """test create_policy() hook, returning CryptPolicy"""
- self.assertFalse(has_crypt_handler("dummy_2"))
- register_crypt_handler_path("dummy_2", "passlib.tests.test_context")
-
- def create_policy(flag=False):
- self.assertTrue(flag)
- return CryptPolicy(schemes=iter(["dummy_2", "des_crypt"]), deprecated=["des_crypt"])
-
- cc = LazyCryptContext(create_policy=create_policy, flag=True)
-
- self.assertFalse(has_crypt_handler("dummy_2", True))
-
- self.assertTrue(cc.policy.handler_is_deprecated("des_crypt"))
- self.assertEqual(cc.policy.schemes(), ["dummy_2", "des_crypt"])
-
- self.assertTrue(has_crypt_handler("dummy_2", True))
-
-#=============================================================================
-# eof
-#=============================================================================
diff --git a/passlib/tests/test_crypto_builtin_md4.py b/passlib/tests/test_crypto_builtin_md4.py
index 0aca1eb..e74b924 100644
--- a/passlib/tests/test_crypto_builtin_md4.py
+++ b/passlib/tests/test_crypto_builtin_md4.py
@@ -2,16 +2,16 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement, division
# core
from binascii import hexlify
import hashlib
+from unittest import skipUnless
# site
# pkg
# module
-from passlib.utils.compat import bascii_to_str, PY3, u
+from passlib.utils.compat import bascii_to_str
from passlib.crypto.digest import lookup_hash
-from passlib.tests.utils import TestCase, skipUnless
+from passlib.tests.utils import TestCase
# local
__all__ = [
"_Common_MD4_Test",
@@ -62,16 +62,10 @@ class _Common_MD4_Test(TestCase):
h.update(b'bcdefghijklmnopqrstuvwxyz')
self.assertEqual(h.hexdigest(), "d79e1c308aa5bbcdeea8ed63df412da9")
- if PY3:
- # reject unicode, hash should return digest of b''
- h = md4()
- self.assertRaises(TypeError, h.update, u('a'))
- self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0")
- else:
- # coerce unicode to ascii, hash should return digest of b'a'
- h = md4()
- h.update(u('a'))
- self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24")
+ # reject unicode, hash should return digest of b''
+ h = md4()
+ self.assertRaises(TypeError, h.update, u'a')
+ self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0")
def test_md4_hexdigest(self):
"""hexdigest() method"""
@@ -124,7 +118,7 @@ class MD4_SSL_Test(_Common_MD4_Test):
# this is more to test our test is correct :)
def setUp(self):
- super(MD4_SSL_Test, self).setUp()
+ super().setUp()
# make sure we're using right constructor.
self.assertEqual(self.get_md4_const().__module__, "hashlib")
@@ -134,7 +128,7 @@ class MD4_Builtin_Test(_Common_MD4_Test):
descriptionPrefix = "passlib.crypto._md4.md4()"
def setUp(self):
- super(MD4_Builtin_Test, self).setUp()
+ super().setUp()
if has_native_md4():
diff --git a/passlib/tests/test_crypto_des.py b/passlib/tests/test_crypto_des.py
index ab31845..20f3681 100644
--- a/passlib/tests/test_crypto_des.py
+++ b/passlib/tests/test_crypto_des.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement, division
# core
from functools import partial
# site
diff --git a/passlib/tests/test_crypto_digest.py b/passlib/tests/test_crypto_digest.py
index 461d209..66f781c 100644
--- a/passlib/tests/test_crypto_digest.py
+++ b/passlib/tests/test_crypto_digest.py
@@ -2,17 +2,17 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement, division
# core
from binascii import hexlify
import hashlib
+from unittest import skipUnless
import warnings
# site
# pkg
# module
from passlib.exc import UnknownHashError
-from passlib.utils.compat import PY3, u, JYTHON
-from passlib.tests.utils import TestCase, TEST_MODE, skipUnless, hb
+from passlib.utils.compat import JYTHON
+from passlib.tests.utils import TestCase, TEST_MODE, hb
#=============================================================================
# test assorted crypto helpers
@@ -56,7 +56,7 @@ class HashInfoTest(TestCase):
warnings.filterwarnings("ignore", '.*unsupported hash')
# test string types
- self.assertEqual(norm_hash_name(u("MD4")), "md4")
+ self.assertEqual(norm_hash_name(u"MD4"), "md4")
self.assertEqual(norm_hash_name(b"MD4"), "md4")
self.assertRaises(TypeError, norm_hash_name, None)
@@ -497,12 +497,7 @@ class Pbkdf2Test(TestCase):
self.assertEqual("hashlib-ssl" in PBKDF2_BACKENDS, has_hashlib_ssl)
# check for appropriate builtin
- from passlib.utils.compat import PY3
- if PY3:
- self.assertIn("builtin-from-bytes", PBKDF2_BACKENDS)
- else:
- # XXX: only true as long as this is preferred over hexlify
- self.assertIn("builtin-unpack", PBKDF2_BACKENDS)
+ self.assertIn("builtin-from-bytes", PBKDF2_BACKENDS)
def test_border(self):
"""test border cases"""
diff --git a/passlib/tests/test_crypto_scrypt.py b/passlib/tests/test_crypto_scrypt.py
index 73ff1fa..5009db0 100644
--- a/passlib/tests/test_crypto_scrypt.py
+++ b/passlib/tests/test_crypto_scrypt.py
@@ -7,15 +7,16 @@ from binascii import hexlify
import hashlib
import logging; log = logging.getLogger(__name__)
import struct
+from unittest import skipUnless
import warnings
warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*")
# site
# pkg
from passlib import exc
from passlib.utils import getrandbytes
-from passlib.utils.compat import PYPY, u, bascii_to_str
+from passlib.utils.compat import PYPY, bascii_to_str
from passlib.utils.decor import classproperty
-from passlib.tests.utils import TestCase, skipUnless, TEST_MODE, hb
+from passlib.tests.utils import TestCase, TEST_MODE, hb
# subject
from passlib.crypto import scrypt as scrypt_mod
# local
@@ -312,7 +313,7 @@ class _CommonScryptTest(TestCase):
def setUp(self):
assert self.backend
scrypt_mod._set_backend(self.backend)
- super(_CommonScryptTest, self).setUp()
+ super().setUp()
#=============================================================================
# reference vectors
@@ -450,7 +451,7 @@ class _CommonScryptTest(TestCase):
return hexstr(scrypt_mod.scrypt(secret, "salt", 2, 2, 2, 16))
# unicode
- TEXT = u("abc\u00defg")
+ TEXT = u"abc\u00defg"
self.assertEqual(run_scrypt(TEXT), '05717106997bfe0da42cf4779a2f8bd8')
# utf8 bytes
@@ -475,7 +476,7 @@ class _CommonScryptTest(TestCase):
return hexstr(scrypt_mod.scrypt("secret", salt, 2, 2, 2, 16))
# unicode
- TEXT = u("abc\u00defg")
+ TEXT = u"abc\u00defg"
self.assertEqual(run_scrypt(TEXT), 'a748ec0f4613929e9e5f03d1ab741d88')
# utf8 bytes
@@ -597,7 +598,7 @@ class BuiltinScryptTest(_CommonScryptTest):
backend = "builtin"
def setUp(self):
- super(BuiltinScryptTest, self).setUp()
+ super().setUp()
warnings.filterwarnings("ignore", "(?i)using builtin scrypt backend",
category=exc.PasslibSecurityWarning)
diff --git a/passlib/tests/test_ext_django.py b/passlib/tests/test_ext_django.py
index 2a0b418..4a2d4c9 100644
--- a/passlib/tests/test_ext_django.py
+++ b/passlib/tests/test_ext_django.py
@@ -3,7 +3,6 @@
# imports
#=============================================================================
# core
-from __future__ import absolute_import, division, print_function
import logging; log = logging.getLogger(__name__)
import sys
import re
@@ -15,7 +14,7 @@ from passlib.context import CryptContext
from passlib.ext.django.utils import (
DJANGO_VERSION, MIN_DJANGO_VERSION, DjangoTranslator, quirks,
)
-from passlib.utils.compat import iteritems, get_method_function, u
+from passlib.utils.compat import get_method_function
from passlib.utils.decor import memoized_property
# tests
from passlib.tests.utils import TestCase, TEST_MODE, handler_derived_from
@@ -77,7 +76,7 @@ UNSET = object()
def update_settings(**kwds):
"""helper to update django settings from kwds"""
- for k,v in iteritems(kwds):
+ for k,v in kwds.items():
if v is UNSET:
if hasattr(settings, k):
delattr(settings, k)
@@ -347,7 +346,7 @@ class _ExtensionTest(TestCase, _ExtensionSupport):
#=============================================================================
def setUp(self):
- super(_ExtensionTest, self).setUp()
+ super().setUp()
self.require_TEST_MODE("default")
@@ -844,7 +843,7 @@ class ExtensionBehaviorTest(DjangoBehaviorTest):
)
def setUp(self):
- super(ExtensionBehaviorTest, self).setUp()
+ super().setUp()
# always load extension before each test
self.load_extension(PASSLIB_CONFIG=self.config)
@@ -875,9 +874,9 @@ class DjangoExtensionTest(_ExtensionTest):
self.load_extension(PASSLIB_CONFIG="disabled", check=False)
self.assert_unpatched()
- # check legacy config=None
- with self.assertWarningList("PASSLIB_CONFIG=None is deprecated"):
- self.load_extension(PASSLIB_CONFIG=None, check=False)
+ # check onfig=None is rejected
+ self.assertRaises(TypeError, self.load_extension, PASSLIB_CONFIG=None,
+ check=False)
self.assert_unpatched()
# try stock django 1.0 context
@@ -960,9 +959,9 @@ class DjangoExtensionTest(_ExtensionTest):
"v2RWkZQzctPdejyRqmmTDQpZN6wTh7.RUy9zF2LftT6")
self.assertEqual(hasher.safe_summary(encoded),
{'algorithm': 'sha256_crypt',
- 'salt': u('abcdab**********'),
+ 'salt': u'abcdab**********',
'rounds': 1234,
- 'hash': u('v2RWkZ*************************************'),
+ 'hash': u'v2RWkZ*************************************',
})
# made up name should throw error
@@ -974,9 +973,9 @@ class DjangoExtensionTest(_ExtensionTest):
#===================================================================
def test_11_config_disabled(self):
"""test PASSLIB_CONFIG='disabled'"""
- # test config=None (deprecated)
- with self.assertWarningList("PASSLIB_CONFIG=None is deprecated"):
- self.load_extension(PASSLIB_CONFIG=None, check=False)
+ # test config=None is rejected
+ self.assertRaises(TypeError, self.load_extension, PASSLIB_CONFIG=None,
+ check=False)
self.assert_unpatched()
# test disabled config
diff --git a/passlib/tests/test_ext_django_source.py b/passlib/tests/test_ext_django_source.py
index 4b42e59..a6da6b6 100644
--- a/passlib/tests/test_ext_django_source.py
+++ b/passlib/tests/test_ext_django_source.py
@@ -4,12 +4,10 @@ test passlib.ext.django against django source tests
#=============================================================================
# imports
#=============================================================================
-from __future__ import absolute_import, division, print_function
# core
import logging; log = logging.getLogger(__name__)
# site
# pkg
-from passlib.utils.compat import suppress_cause
from passlib.ext.django.utils import DJANGO_VERSION, DjangoTranslator, _PasslibHasherWrapper
# tests
from passlib.tests.utils import TestCase, TEST_MODE
@@ -60,10 +58,8 @@ elif has_min_django:
try:
from auth_tests import test_hashers as test_hashers_mod
except ImportError as err:
- raise suppress_cause(
- EnvironmentError("error trying to import django tests "
- "from source path (%r): %r" %
- (source_path, err)))
+ raise EnvironmentError("error trying to import django tests "
+ "from source path (%r): %r" % (source_path, err)) from None
finally:
sys.path.remove(tests_path)
@@ -89,7 +85,6 @@ if test_hashers_mod:
from django.core.signals import setting_changed
from django.dispatch import receiver
from django.utils.module_loading import import_string
- from passlib.utils.compat import get_unbound_method_function
class HashersTest(test_hashers_mod.TestUtilsHashPass, _ExtensionSupport):
"""
@@ -102,7 +97,7 @@ if test_hashers_mod:
#==================================================================
# port patchAttr() helper method from passlib.tests.utils.TestCase
- patchAttr = get_unbound_method_function(TestCase.patchAttr)
+ patchAttr = TestCase.patchAttr
#==================================================================
# custom setup
@@ -212,7 +207,7 @@ if test_hashers_mod:
def tearDown(self):
# NOTE: could rely on addCleanup() instead, but need py26 compat
self.unload_extension()
- super(HashersTest, self).tearDown()
+ super().tearDown()
#==================================================================
# skip a few methods that can't be replicated properly
diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py
index cad5ef9..e3a3e30 100644
--- a/passlib/tests/test_handlers.py
+++ b/passlib/tests/test_handlers.py
@@ -2,18 +2,15 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
-import logging; log = logging.getLogger(__name__)
-import os
+import logging
+log = logging.getLogger(__name__)
import sys
import warnings
# site
# pkg
from passlib import exc, hash
-from passlib.utils import repeat_string
-from passlib.utils.compat import irange, PY3, u, get_method_function
-from passlib.tests.utils import TestCase, HandlerCase, skipUnless, \
+from passlib.tests.utils import TestCase, HandlerCase, \
TEST_MODE, UserHandlerMixin, EncodingHandlerMixin
# module
@@ -22,9 +19,9 @@ from passlib.tests.utils import TestCase, HandlerCase, skipUnless, \
#=============================================================================
# some common unicode passwords which used as test cases
-UPASS_WAV = u('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2')
-UPASS_USD = u("\u20AC\u00A5$")
-UPASS_TABLE = u("t\u00e1\u0411\u2113\u0259")
+UPASS_WAV = u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2'
+UPASS_USD = u"\u20AC\u00A5$"
+UPASS_TABLE = u"t\u00e1\u0411\u2113\u0259"
PASS_TABLE_UTF8 = b't\xc3\xa1\xd0\x91\xe2\x84\x93\xc9\x99' # utf-8
@@ -150,7 +147,7 @@ class bigcrypt_test(HandlerCase):
# check that _norm_checksum() also validates checksum size.
# (current code uses regex in parser)
self.assertRaises(ValueError, hash.bigcrypt, use_defaults=True,
- checksum=u('yh4XPJGsOZ'))
+ checksum=u'yh4XPJGsOZ')
#=============================================================================
# bsdi crypt
@@ -208,7 +205,7 @@ class _bsdi_crypt_test(HandlerCase):
def test_77_fuzz_input(self, **kwds):
# we want to generate even rounds to verify it's correct, but want to ignore warnings
warnings.filterwarnings("ignore", "bsdi_crypt rounds should be odd.*")
- super(_bsdi_crypt_test, self).test_77_fuzz_input(**kwds)
+ super().test_77_fuzz_input(**kwds)
def test_needs_update_w_even_rounds(self):
"""needs_update() should flag even rounds"""
@@ -289,7 +286,7 @@ class _des_crypt_test(HandlerCase):
('AlOtBsOl', 'cEpWz5IUCShqM'),
# ensures utf-8 used for unicode
- (u('hell\u00D6'), 'saykDgk3BPZ9E'),
+ (u'hell\u00D6', 'saykDgk3BPZ9E'),
]
known_unidentified_hashes = [
# bad char in otherwise correctly formatted hash
@@ -379,11 +376,11 @@ class fshp_test(HandlerCase):
handler(variant=1, **kwds)
# accepts bytes or unicode
- handler(variant=u('1'), **kwds)
+ handler(variant=u'1', **kwds)
handler(variant=b'1', **kwds)
# aliases
- handler(variant=u('sha256'), **kwds)
+ handler(variant=u'sha256', **kwds)
handler(variant=b'sha256', **kwds)
# rejects None
@@ -585,7 +582,7 @@ class ldap_salted_sha256_test(HandlerCase):
("password", '{SSHA256}x1tymSTVjozxQ2PtT46ysrzhZxbcskK0o2f8hEFx7fAQQmhtDSEkJA=='),
("test", '{SSHA256}xfqc9aOR6z15YaEk3/Ufd7UL9+JozB/1EPmCDTizL0GkdA7BuNda6w=='),
("toomanysecrets", '{SSHA256}RrTKrg6HFXcjJ+eDAq4UtbODxOr9RLeG+I69FoJvutcbY0zpfU+p1Q=='),
- (u('letm\xe8\xefn'), '{SSHA256}km7UjUTBZN8a+gf1ND2/qn15N7LsO/jmGYJXvyTfJKAbI0RoLWWslQ=='),
+ (u'letm\xe8\xefn', '{SSHA256}km7UjUTBZN8a+gf1ND2/qn15N7LsO/jmGYJXvyTfJKAbI0RoLWWslQ=='),
# alternate salt sizes (4, 15, 16)
# generated locally
@@ -612,7 +609,7 @@ class ldap_salted_sha512_test(HandlerCase):
# generated by testing ldap server web interface (see issue 124 comments)
# salt size = 8
("toomanysecrets", '{SSHA512}wExp4xjiCHS0zidJDC4UJq9EEeIebAQPJ1PWSwfhxWjfutI9XiiKuHm2AE41cEFfK+8HyI8bh+ztbczUGsvVFIgICWWPt7qu'),
- (u('letm\xe8\xefn'), '{SSHA512}mpNUSmZc3TNx+RnPwkIAVMf7ocEKLPrIoQNsg4Eu8dHvyCeb2xzHp5A6n4tF7ntknSvfvRZaJII4ImvNJlYsgiwAm0FMqR+3'),
+ (u'letm\xe8\xefn', '{SSHA512}mpNUSmZc3TNx+RnPwkIAVMf7ocEKLPrIoQNsg4Eu8dHvyCeb2xzHp5A6n4tF7ntknSvfvRZaJII4ImvNJlYsgiwAm0FMqR+3'),
# generated locally
# salt size = 8
@@ -642,8 +639,8 @@ class ldap_plaintext_test(HandlerCase):
handler = hash.ldap_plaintext
known_correct_hashes = [
("password", 'password'),
- (UPASS_TABLE, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
- (PASS_TABLE_UTF8, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
+ (UPASS_TABLE, UPASS_TABLE),
+ (PASS_TABLE_UTF8, UPASS_TABLE),
]
known_unidentified_hashes = [
"{FOO}bar",
@@ -706,7 +703,7 @@ class _ldap_sha1_crypt_test(HandlerCase):
def populate_settings(self, kwds):
kwds.setdefault("rounds", 10)
- super(_ldap_sha1_crypt_test, self).populate_settings(kwds)
+ super().populate_settings(kwds)
def test_77_fuzz_input(self, **ignored):
raise self.skipTest("unneeded")
@@ -738,14 +735,14 @@ class lmhash_test(EncodingHandlerMixin, HandlerCase):
('Yokohama', '5ecd9236d21095ce7584248b8d2c9f9e'),
# ensures cp437 used for unicode
- (u('ENCYCLOP\xC6DIA'), 'fed6416bffc9750d48462b9d7aaac065'),
- (u('encyclop\xE6dia'), 'fed6416bffc9750d48462b9d7aaac065'),
+ (u'ENCYCLOP\xC6DIA', 'fed6416bffc9750d48462b9d7aaac065'),
+ (u'encyclop\xE6dia', 'fed6416bffc9750d48462b9d7aaac065'),
# test various encoding values
- ((u("\xC6"), None), '25d8ab4a0659c97aaad3b435b51404ee'),
- ((u("\xC6"), "cp437"), '25d8ab4a0659c97aaad3b435b51404ee'),
- ((u("\xC6"), "latin-1"), '184eecbbe9991b44aad3b435b51404ee'),
- ((u("\xC6"), "utf-8"), '00dd240fcfab20b8aad3b435b51404ee'),
+ ((u"\xC6", None), '25d8ab4a0659c97aaad3b435b51404ee'),
+ ((u"\xC6", "cp437"), '25d8ab4a0659c97aaad3b435b51404ee'),
+ ((u"\xC6", "latin-1"), '184eecbbe9991b44aad3b435b51404ee'),
+ ((u"\xC6", "utf-8"), '00dd240fcfab20b8aad3b435b51404ee'),
]
known_unidentified_hashes = [
@@ -795,7 +792,7 @@ class _md5_crypt_test(HandlerCase):
('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', '$1$jQS7o98J$V6iTcr71CGgwW2laf17pi1'),
('test', '$1$SuMrG47N$ymvzYjr7QcEQjaK5m1PGx1'),
(b'test', '$1$SuMrG47N$ymvzYjr7QcEQjaK5m1PGx1'),
- (u('s'), '$1$ssssssss$YgmLTApYTv12qgTwBoj8i/'),
+ (u's', '$1$ssssssss$YgmLTApYTv12qgTwBoj8i/'),
# ensures utf-8 used for unicode
(UPASS_TABLE, '$1$d6/Ky1lU$/xpf8m7ftmWLF.TjHCqel0'),
@@ -866,9 +863,9 @@ class msdcc_test(UserHandlerMixin, HandlerCase):
(("", "root"), "176a4c2bd45ac73687676c2f09045353"),
(("test1", "TEST1"), "64cd29e36a8431a2b111378564a10631"),
(("okolada", "nineteen_characters"), "290efa10307e36a79b3eebf2a6b29455"),
- ((u("\u00FC"), u("\u00FC")), "48f84e6f73d6d5305f6558a33fa2c9bb"),
- ((u("\u00FC\u00FC"), u("\u00FC\u00FC")), "593246a8335cf0261799bda2a2a9c623"),
- ((u("\u20AC\u20AC"), "user"), "9121790702dda0fa5d353014c334c2ce"),
+ ((u"\u00FC", u"\u00FC"), "48f84e6f73d6d5305f6558a33fa2c9bb"),
+ ((u"\u00FC\u00FC", u"\u00FC\u00FC"), "593246a8335cf0261799bda2a2a9c623"),
+ ((u"\u20AC\u20AC", "user"), "9121790702dda0fa5d353014c334c2ce"),
#
# custom
@@ -904,9 +901,9 @@ class msdcc2_test(UserHandlerMixin, HandlerCase):
(("test2", "TEST2"), "c6758e5be7fc943d00b97972a8a97620"),
(("test3", "test3"), "360e51304a2d383ea33467ab0b639cc4"),
(("test4", "test4"), "6f79ee93518306f071c47185998566ae"),
- ((u("\u00FC"), "joe"), "bdb80f2c4656a8b8591bd27d39064a54"),
- ((u("\u20AC\u20AC"), "joe"), "1e1e20f482ff748038e47d801d0d1bda"),
- ((u("\u00FC\u00FC"), "admin"), "0839e4a07c00f18a8c65cf5b985b9e73"),
+ ((u"\u00FC", "joe"), "bdb80f2c4656a8b8591bd27d39064a54"),
+ ((u"\u20AC\u20AC", "joe"), "1e1e20f482ff748038e47d801d0d1bda"),
+ ((u"\u00FC\u00FC", "admin"), "0839e4a07c00f18a8c65cf5b985b9e73"),
#
# custom
@@ -999,7 +996,7 @@ class mssql2000_test(HandlerCase):
known_malformed_hashes = [
# non-hex char -----\/
b'0x01005B200543327G2E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3',
- u('0x01005B200543327G2E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3'),
+ u'0x01005B200543327G2E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3',
]
class mssql2005_test(HandlerCase):
@@ -1160,8 +1157,8 @@ class nthash_test(HandlerCase):
#
# http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx
#
- ("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")),
- ("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")),
+ ("OLDPASSWORD", u"6677b2c394311355b54f25eec5bfacf5"),
+ ("NEWPASSWORD", u"256781a62031289d3c2c98c14f1efc8c"),
#
# from JTR 1.7.9
@@ -1217,7 +1214,7 @@ class oracle10_test(UserHandlerMixin, HandlerCase):
# http://www.petefinnigan.com/default/default_password_list.htm
#
(('tiger', 'scott'), 'F894844C34402B67'),
- ((u('ttTiGGeR'), u('ScO')), '7AA1A84E31ED7771'),
+ ((u'ttTiGGeR', u'ScO'), '7AA1A84E31ED7771'),
(("d_syspw", "SYSTEM"), '1B9F1F9A5CB9EB31'),
(("strat_passwd", "strat_user"), 'AEBEDBB4EFB5225B'),
@@ -1322,8 +1319,8 @@ class plaintext_test(HandlerCase):
('password', 'password'),
# ensure unicode uses utf-8
- (UPASS_TABLE, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
- (PASS_TABLE_UTF8, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
+ (UPASS_TABLE, UPASS_TABLE),
+ (PASS_TABLE_UTF8, UPASS_TABLE),
]
#=============================================================================
@@ -1456,7 +1453,7 @@ class _sha256_crypt_test(HandlerCase):
('test', '$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1'),
('Compl3X AlphaNu3meric', '$5$rounds=10350$o.pwkySLCzwTdmQX$nCMVsnF3TXWcBPOympBUUSQi6LGGloZoOsVJMGJ09UB'),
('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', '$5$rounds=11944$9dhlu07dQMRWvTId$LyUI5VWkGFwASlzntk1RLurxX54LUhgAcJZIt0pYGT7'),
- (u('with unic\u00D6de'), '$5$rounds=1000$IbG0EuGQXw5EkMdP$LQ5AfPf13KufFsKtmazqnzSGZ4pxtUNw3woQ.ELRDF4'),
+ (u'with unic\u00D6de', '$5$rounds=1000$IbG0EuGQXw5EkMdP$LQ5AfPf13KufFsKtmazqnzSGZ4pxtUNw3woQ.ELRDF4'),
]
if TEST_MODE("full"):
@@ -1754,7 +1751,7 @@ class unix_disabled_test(HandlerCase):
def test_76_hash_border(self):
# so empty strings pass
self.accepts_all_hashes = True
- super(unix_disabled_test, self).test_76_hash_border()
+ super().test_76_hash_border()
def test_90_special(self):
"""test marker option & special behavior"""
@@ -1779,41 +1776,6 @@ class unix_disabled_test(HandlerCase):
self.assertRaises(ValueError, handler.hash, 'stub', marker='abc')
self.assertRaises(ValueError, handler.using, marker='abc')
-class unix_fallback_test(HandlerCase):
- handler = hash.unix_fallback
- accepts_all_hashes = True
-
- known_correct_hashes = [
- # *everything* should hash to "!", and nothing should verify
- ("password", "!"),
- (UPASS_TABLE, "!"),
- ]
-
- # silence annoying deprecation warning
- def setUp(self):
- super(unix_fallback_test, self).setUp()
- warnings.filterwarnings("ignore", "'unix_fallback' is deprecated")
-
- def test_90_wildcard(self):
- """test enable_wildcard flag"""
- h = self.handler
- self.assertTrue(h.verify('password','', enable_wildcard=True))
- self.assertFalse(h.verify('password',''))
- for c in "!*x":
- self.assertFalse(h.verify('password',c, enable_wildcard=True))
- self.assertFalse(h.verify('password',c))
-
- def test_91_preserves_existing(self):
- """test preserves existing disabled hash"""
- handler = self.handler
-
- # use marker if no hash
- self.assertEqual(handler.genhash("stub", ""), "!")
- self.assertEqual(handler.hash("stub"), "!")
-
- # use hash if provided and valid
- self.assertEqual(handler.genhash("stub", "!asd"), "!asd")
-
#=============================================================================
# eof
#=============================================================================
diff --git a/passlib/tests/test_handlers_argon2.py b/passlib/tests/test_handlers_argon2.py
index e771769..3f800ba 100644
--- a/passlib/tests/test_handlers_argon2.py
+++ b/passlib/tests/test_handlers_argon2.py
@@ -10,7 +10,6 @@ import warnings
# site
# pkg
from passlib import hash
-from passlib.utils.compat import unicode
from passlib.tests.utils import HandlerCase, TEST_MODE
from passlib.tests.test_handlers import UPASS_TABLE, PASS_TABLE_UTF8
# module
@@ -200,7 +199,7 @@ class _base_argon2_test(HandlerCase):
]
def setUpWarnings(self):
- super(_base_argon2_test, self).setUpWarnings()
+ super().setUpWarnings()
warnings.filterwarnings("ignore", ".*Using argon2pure backend.*")
def do_stub_encrypt(self, handler=None, **settings):
@@ -213,7 +212,7 @@ class _base_argon2_test(HandlerCase):
assert self.checksum
return self.to_string()
else:
- return super(_base_argon2_test, self).do_stub_encrypt(handler, **settings)
+ return super().do_stub_encrypt(handler, **settings)
def test_03_legacy_hash_workflow(self):
# override base method
@@ -303,7 +302,7 @@ class _base_argon2_test(HandlerCase):
# check supported type_values
for value in cls.type_values:
- self.assertIsInstance(value, unicode)
+ self.assertIsInstance(value, str)
self.assertTrue("i" in cls.type_values)
self.assertTrue("d" in cls.type_values)
diff --git a/passlib/tests/test_handlers_bcrypt.py b/passlib/tests/test_handlers_bcrypt.py
index 64fc8bf..abe44ec 100644
--- a/passlib/tests/test_handlers_bcrypt.py
+++ b/passlib/tests/test_handlers_bcrypt.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
import logging; log = logging.getLogger(__name__)
import os
@@ -12,7 +11,6 @@ import warnings
from passlib import hash
from passlib.handlers.bcrypt import IDENT_2, IDENT_2X
from passlib.utils import repeat_string, to_bytes, is_safe_crypt_input
-from passlib.utils.compat import irange, PY3
from passlib.tests.utils import HandlerCase, TEST_MODE
from passlib.tests.test_handlers import UPASS_TABLE
# module
@@ -179,7 +177,7 @@ class _bcrypt_test(HandlerCase):
self.addCleanup(os.environ.__delitem__, key)
os.environ[key] = "true"
- super(_bcrypt_test, self).setUp()
+ super().setUp()
# silence this warning, will come up a bunch during testing of old 2a hashes.
warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*")
@@ -188,7 +186,7 @@ class _bcrypt_test(HandlerCase):
# builtin is still just way too slow.
if self.backend == "builtin":
kwds.setdefault("rounds", 4)
- super(_bcrypt_test, self).populate_settings(kwds)
+ super().populate_settings(kwds)
#===================================================================
# fuzz testing
@@ -202,8 +200,6 @@ class _bcrypt_test(HandlerCase):
fuzz_verifiers = HandlerCase.fuzz_verifiers + (
"fuzz_verifier_bcrypt",
- "fuzz_verifier_pybcrypt",
- "fuzz_verifier_bcryptor",
)
def fuzz_verifier_bcrypt(self):
@@ -238,61 +234,6 @@ class _bcrypt_test(HandlerCase):
raise ValueError("bcrypt rejected hash: %r (secret=%r)" % (hash, secret))
return check_bcrypt
- def fuzz_verifier_pybcrypt(self):
- # test against py-bcrypt, if available
- from passlib.handlers.bcrypt import (
- IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y,
- _PyBcryptBackend,
- )
- from passlib.utils import to_native_str
-
- loaded = _PyBcryptBackend._load_backend_mixin("pybcrypt", False)
- if not loaded:
- return
-
- from passlib.handlers.bcrypt import _pybcrypt as bcrypt_mod
-
- lock = _PyBcryptBackend._calc_lock # reuse threadlock workaround for pybcrypt 0.2
-
- def check_pybcrypt(secret, hash):
- """pybcrypt"""
- secret = to_native_str(secret, self.FuzzHashGenerator.password_encoding)
- if len(secret) > 200: # vulnerable to wraparound bug
- secret = secret[:200]
- if hash.startswith((IDENT_2B, IDENT_2Y)):
- hash = IDENT_2A + hash[4:]
- try:
- if lock:
- with lock:
- return bcrypt_mod.hashpw(secret, hash) == hash
- else:
- return bcrypt_mod.hashpw(secret, hash) == hash
- except ValueError:
- raise ValueError("py-bcrypt rejected hash: %r" % (hash,))
- return check_pybcrypt
-
- def fuzz_verifier_bcryptor(self):
- # test against bcryptor if available
- from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2Y, IDENT_2B
- from passlib.utils import to_native_str
- try:
- from bcryptor.engine import Engine
- except ImportError:
- return
- def check_bcryptor(secret, hash):
- """bcryptor"""
- secret = to_native_str(secret, self.FuzzHashGenerator.password_encoding)
- if hash.startswith((IDENT_2B, IDENT_2Y)):
- hash = IDENT_2A + hash[4:]
- elif hash.startswith(IDENT_2):
- # bcryptor doesn't support $2$ hashes; but we can fake it
- # using the $2a$ algorithm, by repeating the password until
- # it's 72 chars in length.
- hash = IDENT_2A + hash[3:]
- if secret:
- secret = repeat_string(secret, 72)
- return Engine(False).hash_key(secret, hash) == hash
- return check_bcryptor
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
@@ -366,9 +307,9 @@ class _bcrypt_test(HandlerCase):
"unexpectedly malformed hash: %r" % (hash,)
self.assertTrue(hash[28] in '.Oeu',
"unused bits incorrectly set in hash: %r" % (hash,))
- for i in irange(6):
+ for i in range(6):
check_padding(bcrypt.genconfig())
- for i in irange(3):
+ for i in range(3):
check_padding(bcrypt.using(rounds=bcrypt.min_rounds).hash("bob"))
#
@@ -425,8 +366,7 @@ class _bcrypt_test(HandlerCase):
# create test cases for specific backends
bcrypt_bcrypt_test = _bcrypt_test.create_backend_case("bcrypt")
-bcrypt_pybcrypt_test = _bcrypt_test.create_backend_case("pybcrypt")
-bcrypt_bcryptor_test = _bcrypt_test.create_backend_case("bcryptor")
+
class bcrypt_os_crypt_test(_bcrypt_test.create_backend_case("os_crypt")):
@@ -583,14 +523,14 @@ class _bcrypt_sha256_test(HandlerCase):
else:
self.addCleanup(os.environ.__delitem__, key)
os.environ[key] = "enabled"
- super(_bcrypt_sha256_test, self).setUp()
+ super().setUp()
warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*")
def populate_settings(self, kwds):
# builtin is still just way too slow.
if self.backend == "builtin":
kwds.setdefault("rounds", 4)
- super(_bcrypt_sha256_test, self).populate_settings(kwds)
+ super().populate_settings(kwds)
#===================================================================
# override ident tests for now
@@ -670,8 +610,7 @@ class _bcrypt_sha256_test(HandlerCase):
# create test cases for specific backends
bcrypt_sha256_bcrypt_test = _bcrypt_sha256_test.create_backend_case("bcrypt")
-bcrypt_sha256_pybcrypt_test = _bcrypt_sha256_test.create_backend_case("pybcrypt")
-bcrypt_sha256_bcryptor_test = _bcrypt_sha256_test.create_backend_case("bcryptor")
+
class bcrypt_sha256_os_crypt_test(_bcrypt_sha256_test.create_backend_case("os_crypt")):
diff --git a/passlib/tests/test_handlers_cisco.py b/passlib/tests/test_handlers_cisco.py
index ea6594b..bc23c52 100644
--- a/passlib/tests/test_handlers_cisco.py
+++ b/passlib/tests/test_handlers_cisco.py
@@ -4,14 +4,12 @@ passlib.tests.test_handlers_cisco - tests for Cisco-specific algorithms
#=============================================================================
# imports
#=============================================================================
-from __future__ import absolute_import, division, print_function
# core
import logging
log = logging.getLogger(__name__)
# site
# pkg
from passlib import hash, exc
-from passlib.utils.compat import u
from .utils import UserHandlerMixin, HandlerCase, repeat_string
from .test_handlers import UPASS_TABLE
# module
@@ -151,8 +149,8 @@ class _PixAsaSharedTest(UserHandlerMixin, HandlerCase):
# observed behaviors include:
# * ssh cli stripping non-ascii chars entirely
# * ASDM web iface double-encoding utf-8 strings
- ((u("t\xe1ble").encode("utf-8"), 'user'), 'Og8fB4NyF0m5Ed9c'),
- ((u("t\xe1ble").encode("utf-8").decode("latin-1").encode("utf-8"),
+ ((u"t\xe1ble".encode("utf-8"), 'user'), 'Og8fB4NyF0m5Ed9c'),
+ ((u"t\xe1ble".encode("utf-8").decode("latin-1").encode("utf-8"),
'user'), 'cMvFC2XVBmK/68yB'), # confirmed ASA 9.6 when typed into ASDM
]
diff --git a/passlib/tests/test_handlers_django.py b/passlib/tests/test_handlers_django.py
index f7c9a0d..9d03a98 100644
--- a/passlib/tests/test_handlers_django.py
+++ b/passlib/tests/test_handlers_django.py
@@ -2,17 +2,16 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
import logging; log = logging.getLogger(__name__)
import re
+from unittest import skipUnless, SkipTest
import warnings
# site
# pkg
from passlib import hash
from passlib.utils import repeat_string
-from passlib.utils.compat import u
-from passlib.tests.utils import TestCase, HandlerCase, skipUnless, SkipTest
+from passlib.tests.utils import TestCase, HandlerCase
from passlib.tests.test_handlers import UPASS_USD, UPASS_TABLE
from passlib.tests.test_ext_django import DJANGO_VERSION, MIN_DJANGO_VERSION, \
check_django_hasher_has_backend
@@ -23,7 +22,7 @@ from passlib.tests.test_ext_django import DJANGO_VERSION, MIN_DJANGO_VERSION, \
#=============================================================================
# standard string django uses
-UPASS_LETMEIN = u('l\xe8tmein')
+UPASS_LETMEIN = u'l\xe8tmein'
def vstr(version):
return ".".join(str(e) for e in version)
@@ -146,7 +145,7 @@ class django_des_crypt_test(HandlerCase, _DjangoHelper):
# ensures utf-8 used for unicode
(UPASS_USD, 'crypt$c2e86$c2hN1Bxd6ZiWs'),
(UPASS_TABLE, 'crypt$0.aQs$0.wB.TT0Czvlo'),
- (u("hell\u00D6"), "crypt$sa$saykDgk3BPZ9E"),
+ (u"hell\u00D6", "crypt$sa$saykDgk3BPZ9E"),
# prevent regression of issue 22
("foo", 'crypt$MNVY.9ajgdvDQ$MNVY.9ajgdvDQ'),
@@ -298,7 +297,7 @@ class django_bcrypt_test(HandlerCase, _DjangoHelper):
def populate_settings(self, kwds):
# speed up test w/ lower rounds
kwds.setdefault("rounds", 4)
- super(django_bcrypt_test, self).populate_settings(kwds)
+ super().populate_settings(kwds)
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
@@ -348,7 +347,7 @@ class django_bcrypt_sha256_test(HandlerCase, _DjangoHelper):
def populate_settings(self, kwds):
# speed up test w/ lower rounds
kwds.setdefault("rounds", 4)
- super(django_bcrypt_sha256_test, self).populate_settings(kwds)
+ super().populate_settings(kwds)
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
@@ -382,7 +381,7 @@ class django_argon2_test(HandlerCase, _DjangoHelper):
]
def setUpWarnings(self):
- super(django_argon2_test, self).setUpWarnings()
+ super().setUpWarnings()
warnings.filterwarnings("ignore", ".*Using argon2pure backend.*")
def do_stub_encrypt(self, handler=None, **settings):
diff --git a/passlib/tests/test_handlers_pbkdf2.py b/passlib/tests/test_handlers_pbkdf2.py
index 4d2f048..658f2ce 100644
--- a/passlib/tests/test_handlers_pbkdf2.py
+++ b/passlib/tests/test_handlers_pbkdf2.py
@@ -9,7 +9,6 @@ import warnings
# site
# pkg
from passlib import hash
-from passlib.utils.compat import u
from passlib.tests.utils import TestCase, HandlerCase
from passlib.tests.test_handlers import UPASS_WAV
# module
@@ -123,7 +122,7 @@ class cta_pbkdf2_sha1_test(HandlerCase):
#
# test vectors from original implementation
#
- (u("hashy the \N{SNOWMAN}"), '$p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0='),
+ (u"hashy the \N{SNOWMAN}", '$p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0='),
#
# custom
@@ -201,11 +200,11 @@ class scram_test(HandlerCase):
# test unicode passwords & saslprep (all the passwords below
# should normalize to the same value: 'IX \xE0')
- (u('IX \xE0'), '$scram$6400$0BojBCBE6P2/N4bQ$'
+ (u'IX \xE0', '$scram$6400$0BojBCBE6P2/N4bQ$'
'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'),
- (u('\u2168\u3000a\u0300'), '$scram$6400$0BojBCBE6P2/N4bQ$'
+ (u'\u2168\u3000a\u0300', '$scram$6400$0BojBCBE6P2/N4bQ$'
'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'),
- (u('\u00ADIX \xE0'), '$scram$6400$0BojBCBE6P2/N4bQ$'
+ (u'\u00ADIX \xE0', '$scram$6400$0BojBCBE6P2/N4bQ$'
'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'),
]
@@ -245,7 +244,7 @@ class scram_test(HandlerCase):
]
def setUp(self):
- super(scram_test, self).setUp()
+ super().setUp()
# some platforms lack stringprep (e.g. Jython, IronPython)
self.require_stringprep()
@@ -287,7 +286,7 @@ class scram_test(HandlerCase):
"""test internal parsing of 'checksum' keyword"""
# check non-bytes checksum values are rejected
self.assertRaises(TypeError, self.handler, use_defaults=True,
- checksum={'sha-1': u('X')*20})
+ checksum={'sha-1': u'X'*20})
# check sha-1 is required
self.assertRaises(ValueError, self.handler, use_defaults=True,
@@ -340,9 +339,9 @@ class scram_test(HandlerCase):
# check various encodings of password work.
s1 = b'\x01\x02\x03'
d1 = b'\xb2\xfb\xab\x82[tNuPnI\x8aZZ\x19\x87\xcen\xe9\xd3'
- self.assertEqual(hash(u("\u2168"), s1, 1000, 'sha-1'), d1)
+ self.assertEqual(hash(u"\u2168", s1, 1000, 'sha-1'), d1)
self.assertEqual(hash(b"\xe2\x85\xa8", s1, 1000, 'SHA-1'), d1)
- self.assertEqual(hash(u("IX"), s1, 1000, 'sha1'), d1)
+ self.assertEqual(hash(u"IX", s1, 1000, 'sha1'), d1)
self.assertEqual(hash(b"IX", s1, 1000, 'SHA1'), d1)
# check algs
@@ -354,7 +353,7 @@ class scram_test(HandlerCase):
self.assertRaises(ValueError, hash, "IX", s1, 0, 'sha-1')
# unicode salts accepted as of passlib 1.7 (previous caused TypeError)
- self.assertEqual(hash(u("IX"), s1.decode("latin-1"), 1000, 'sha1'), d1)
+ self.assertEqual(hash(u"IX", s1.decode("latin-1"), 1000, 'sha1'), d1)
def test_94_saslprep(self):
"""test hash/verify use saslprep"""
@@ -363,18 +362,18 @@ class scram_test(HandlerCase):
# to verify full normalization behavior.
# hash unnormalized
- h = self.do_encrypt(u("I\u00ADX"))
- self.assertTrue(self.do_verify(u("IX"), h))
- self.assertTrue(self.do_verify(u("\u2168"), h))
+ h = self.do_encrypt(u"I\u00ADX")
+ self.assertTrue(self.do_verify(u"IX", h))
+ self.assertTrue(self.do_verify(u"\u2168", h))
# hash normalized
- h = self.do_encrypt(u("\xF3"))
- self.assertTrue(self.do_verify(u("o\u0301"), h))
- self.assertTrue(self.do_verify(u("\u200Do\u0301"), h))
+ h = self.do_encrypt(u"\xF3")
+ self.assertTrue(self.do_verify(u"o\u0301", h))
+ self.assertTrue(self.do_verify(u"\u200Do\u0301", h))
# throws error if forbidden char provided
- self.assertRaises(ValueError, self.do_encrypt, u("\uFDD0"))
- self.assertRaises(ValueError, self.do_verify, u("\uFDD0"), h)
+ self.assertRaises(ValueError, self.do_encrypt, u"\uFDD0")
+ self.assertRaises(ValueError, self.do_verify, u"\uFDD0", h)
def test_94_using_w_default_algs(self, param="default_algs"):
"""using() -- 'default_algs' parameter"""
diff --git a/passlib/tests/test_handlers_scrypt.py b/passlib/tests/test_handlers_scrypt.py
index 5ab6d9f..2ce6463 100644
--- a/passlib/tests/test_handlers_scrypt.py
+++ b/passlib/tests/test_handlers_scrypt.py
@@ -86,14 +86,14 @@ class _scrypt_test(HandlerCase):
]
def setUpWarnings(self):
- super(_scrypt_test, self).setUpWarnings()
+ super().setUpWarnings()
warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*")
def populate_settings(self, kwds):
# builtin is still just way too slow.
if self.backend == "builtin":
kwds.setdefault("rounds", 6)
- super(_scrypt_test, self).populate_settings(kwds)
+ super().populate_settings(kwds)
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
diff --git a/passlib/tests/test_hosts.py b/passlib/tests/test_hosts.py
index cbf93ab..b8e6e5e 100644
--- a/passlib/tests/test_hosts.py
+++ b/passlib/tests/test_hosts.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
import logging; log = logging.getLogger(__name__)
# site
diff --git a/passlib/tests/test_pwd.py b/passlib/tests/test_pwd.py
index 2c983cd..cc7bad3 100644
--- a/passlib/tests/test_pwd.py
+++ b/passlib/tests/test_pwd.py
@@ -66,7 +66,7 @@ class WordGeneratorTest(TestCase):
descriptionPrefix = "passlib.pwd.genword()"
def setUp(self):
- super(WordGeneratorTest, self).setUp()
+ super().setUp()
# patch some RNG references so they're reproducible.
from passlib.pwd import SequenceGenerator
diff --git a/passlib/tests/test_registry.py b/passlib/tests/test_registry.py
index 8cec48d..09d8dfd 100644
--- a/passlib/tests/test_registry.py
+++ b/passlib/tests/test_registry.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
from logging import getLogger
import warnings
@@ -40,7 +39,7 @@ class RegistryTest(TestCase):
descriptionPrefix = "passlib.registry"
def setUp(self):
- super(RegistryTest, self).setUp()
+ super().setUp()
# backup registry state & restore it after test.
locations = dict(registry._locations)
diff --git a/passlib/tests/test_totp.py b/passlib/tests/test_totp.py
index 604d2e9..e9c6dd9 100644
--- a/passlib/tests/test_totp.py
+++ b/passlib/tests/test_totp.py
@@ -3,6 +3,7 @@
# imports
#=============================================================================
# core
+from binascii import Error as DecodeError
import datetime
from functools import partial
import logging; log = logging.getLogger(__name__)
@@ -11,7 +12,6 @@ import time as _time
# site
# pkg
from passlib import exc
-from passlib.utils.compat import unicode, u
from passlib.tests.utils import TestCase, time_call
# subject
from passlib import totp as totp_module
@@ -25,16 +25,6 @@ __all__ = [
# helpers
#=============================================================================
-# XXX: python 3 changed what error base64.b16decode() throws, from TypeError to base64.Error().
-# it wasn't until 3.3 that base32decode() also got changed.
-# really should normalize this in the code to a single BinaryDecodeError,
-# predicting this cross-version is getting unmanagable.
-Base32DecodeError = Base16DecodeError = TypeError
-if sys.version_info >= (3,0):
- from binascii import Error as Base16DecodeError
-if sys.version_info >= (3,3):
- from binascii import Error as Base32DecodeError
-
PASS1 = "abcdef"
PASS2 = b"\x00\xFF"
KEY1 = '4AOGGDBBQSYHNTUZ'
@@ -174,7 +164,7 @@ class AppWalletTest(TestCase):
self.assertEqual(wallet._secrets, ref)
# accept unicode
- wallet = AppWallet({u("1"): b"aaa", u("02"): b"bbb", u("C"): b"ccc"})
+ wallet = AppWallet({u"1": b"aaa", u"02": b"bbb", u"C": b"ccc"})
self.assertEqual(wallet._secrets, ref)
# normalize int tags
@@ -192,7 +182,7 @@ class AppWalletTest(TestCase):
self.assertRaises(ValueError, AppWallet, {"ab*$": "aaa"})
# coerce value to bytes
- wallet = AppWallet({"1": u("aaa"), "02": "bbb", "C": b"ccc"})
+ wallet = AppWallet({"1": u"aaa", "02": "bbb", "C": b"ccc"})
self.assertEqual(wallet._secrets, ref)
# forbid invalid value types
@@ -406,7 +396,7 @@ class TotpTest(TestCase):
# setup
#=============================================================================
def setUp(self):
- super(TotpTest, self).setUp()
+ super().setUp()
# clear norm_hash_name() cache so 'unknown hash' warnings get emitted each time
from passlib.crypto.digest import lookup_hash
@@ -629,13 +619,13 @@ class TotpTest(TestCase):
self.assertEqual(TOTP(' 4aog gdbb qsyh ntuz ').key, KEY1_RAW)
# .. w/ invalid char
- self.assertRaises(Base32DecodeError, TOTP, 'ao!ggdbbqsyhntuz')
+ self.assertRaises(DecodeError, TOTP, 'ao!ggdbbqsyhntuz')
# handle hex encoding
self.assertEqual(TOTP('e01c630c2184b076ce99', 'hex').key, KEY1_RAW)
# .. w/ invalid char
- self.assertRaises(Base16DecodeError, TOTP, 'X01c630c2184b076ce99', 'hex')
+ self.assertRaises(DecodeError, TOTP, 'X01c630c2184b076ce99', 'hex')
# handle raw bytes
self.assertEqual(TOTP(KEY1_RAW, "raw").key, KEY1_RAW)
@@ -749,7 +739,7 @@ class TotpTest(TestCase):
otp = self.randotp(digits=7)
# unicode & bytes
- self.assertEqual(otp.normalize_token(u('1234567')), '1234567')
+ self.assertEqual(otp.normalize_token(u'1234567'), '1234567')
self.assertEqual(otp.normalize_token(b'1234567'), '1234567')
# int
@@ -880,7 +870,7 @@ class TotpTest(TestCase):
time = self.randtime()
result = otp.generate(time)
token = result.token
- self.assertIsInstance(token, unicode)
+ self.assertIsInstance(token, str)
start_time = result.counter * 30
# should generate same token for next 29s
@@ -1204,8 +1194,8 @@ class TotpTest(TestCase):
from_source = TOTP.from_source
# uri (unicode)
- otp = from_source(u("otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&"
- "issuer=Example"))
+ otp = from_source(u"otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&"
+ "issuer=Example")
self.assertEqual(otp.key, KEY4_RAW)
# uri (bytes)
@@ -1218,7 +1208,7 @@ class TotpTest(TestCase):
self.assertEqual(otp.key, KEY4_RAW)
# json (unicode)
- otp = from_source(u('{"v": 1, "type": "totp", "key": "JBSWY3DPEHPK3PXP"}'))
+ otp = from_source(u'{"v": 1, "type": "totp", "key": "JBSWY3DPEHPK3PXP"}')
self.assertEqual(otp.key, KEY4_RAW)
# json (bytes)
@@ -1239,7 +1229,7 @@ class TotpTest(TestCase):
self.assertIs(otp2, otp1)
# random string
- self.assertRaises(ValueError, from_source, u("foo"))
+ self.assertRaises(ValueError, from_source, u"foo")
self.assertRaises(ValueError, from_source, b"foo")
#=============================================================================
@@ -1278,8 +1268,8 @@ class TotpTest(TestCase):
self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?digits=6")
# undecodable secret
- self.assertRaises(Base32DecodeError, from_uri, "otpauth://totp/Example:alice@google.com?"
- "secret=JBSWY3DPEHP@3PXP")
+ self.assertRaises(DecodeError, from_uri, "otpauth://totp/Example:alice@google.com?"
+ "secret=JBSWY3DPEHP@3PXP")
#--------------------------------------------------------------------------------
# label param
@@ -1468,8 +1458,7 @@ class TotpTest(TestCase):
self.assertRaises(ValueError, from_dict, dict(v=1, type="totp"))
# undecodable secret
- self.assertRaises(Base32DecodeError, from_dict,
- dict(v=1, type="totp", key="JBSWY3DPEHP@3PXP"))
+ self.assertRaises(DecodeError, from_dict, dict(v=1, type="totp", key="JBSWY3DPEHP@3PXP"))
#--------------------------------------------------------------------------------
# label & issuer params
diff --git a/passlib/tests/test_utils.py b/passlib/tests/test_utils.py
index 59ba160..2bc5c48 100644
--- a/passlib/tests/test_utils.py
+++ b/passlib/tests/test_utils.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
from functools import partial
import warnings
@@ -10,7 +9,7 @@ import warnings
# pkg
# module
from passlib.utils import is_ascii_safe, to_bytes
-from passlib.utils.compat import irange, PY2, PY3, u, unicode, join_bytes, PYPY
+from passlib.utils.compat import join_bytes, PYPY
from passlib.tests.utils import TestCase, hb, run_with_fixed_seeds
#=============================================================================
@@ -30,21 +29,24 @@ class MiscTest(TestCase):
# test synthentic dir()
dir(compat)
- self.assertTrue('UnicodeIO' in dir(compat))
- self.assertTrue('irange' in dir(compat))
+ # FIXME: find another lazy-loaded attr to check, all current ones removed after py2 comapt gone.
+ # self.assertTrue('UnicodeIO' in dir(compat))
def test_classproperty(self):
from passlib.utils.decor import classproperty
+ def xprop_func(cls):
+ return cls.xvar
+
class test(object):
xvar = 1
- @classproperty
- def xprop(cls):
- return cls.xvar
+
+ xprop = classproperty(xprop_func)
self.assertEqual(test.xprop, 1)
+
prop = test.__dict__['xprop']
- self.assertIs(prop.im_func, prop.__func__)
+ self.assertIs(prop.__func__, xprop_func)
def test_deprecated_function(self):
from passlib.utils.decor import deprecated_function
@@ -81,10 +83,6 @@ class MiscTest(TestCase):
self.assertEqual(d.value, 0)
self.assertEqual(d.counter, 1)
- prop = dummy.value
- if not PY3:
- self.assertIs(prop.im_func, prop.__func__)
-
def test_getrandbytes(self):
"""getrandbytes()"""
from passlib.utils import getrandbytes
@@ -124,11 +122,11 @@ class MiscTest(TestCase):
# and hope that they're sufficient to test the range of behavior.
# letters
- x = wrapper(u('abc'), 32)
- y = wrapper(u('abc'), 32)
- self.assertIsInstance(x, unicode)
+ x = wrapper(u'abc', 32)
+ y = wrapper(u'abc', 32)
+ self.assertIsInstance(x, str)
self.assertNotEqual(x,y)
- self.assertEqual(sorted(set(x)), [u('a'),u('b'),u('c')])
+ self.assertEqual(sorted(set(x)), [u'a',u'b',u'c'])
# bytes
x = wrapper(b'abc', 32)
@@ -136,7 +134,7 @@ class MiscTest(TestCase):
self.assertIsInstance(x, bytes)
self.assertNotEqual(x,y)
# NOTE: decoding this due to py3 bytes
- self.assertEqual(sorted(set(x.decode("ascii"))), [u('a'),u('b'),u('c')])
+ self.assertEqual(sorted(set(x.decode("ascii"))), [u'a',u'b',u'c'])
def test_generate_password(self):
"""generate_password()"""
@@ -200,18 +198,18 @@ class MiscTest(TestCase):
# helpers to generate hashes & config strings to work with
def get_hash(secret):
- assert isinstance(secret, unicode)
+ assert isinstance(secret, str)
hash = hasher.hash(secret)
if isinstance(hash, bytes): # py2
hash = hash.decode("utf-8")
- assert isinstance(hash, unicode)
+ assert isinstance(hash, str)
return hash
# test ascii password & return type
- s1 = u("test")
+ s1 = u"test"
h1 = get_hash(s1)
result = safe_crypt(s1, h1)
- self.assertIsInstance(result, unicode)
+ self.assertIsInstance(result, str)
self.assertEqual(result, h1)
self.assertEqual(safe_crypt(to_bytes(s1), to_bytes(h1)), h1)
@@ -220,7 +218,7 @@ class MiscTest(TestCase):
self.assertEqual(safe_crypt(s1, h1x), h1)
# test utf-8 / unicode password
- s2 = u('test\u1234')
+ s2 = u'test\u1234'
h2 = get_hash(s2)
self.assertEqual(safe_crypt(s2, h2), h2)
self.assertEqual(safe_crypt(to_bytes(s2), to_bytes(h2)), h2)
@@ -259,30 +257,29 @@ class MiscTest(TestCase):
from passlib.utils import consteq, str_consteq
# ensure error raises for wrong types
- self.assertRaises(TypeError, consteq, u(''), b'')
- self.assertRaises(TypeError, consteq, u(''), 1)
- self.assertRaises(TypeError, consteq, u(''), None)
+ self.assertRaises(TypeError, consteq, u'', b'')
+ self.assertRaises(TypeError, consteq, u'', 1)
+ self.assertRaises(TypeError, consteq, u'', None)
- self.assertRaises(TypeError, consteq, b'', u(''))
+ self.assertRaises(TypeError, consteq, b'', u'')
self.assertRaises(TypeError, consteq, b'', 1)
self.assertRaises(TypeError, consteq, b'', None)
- self.assertRaises(TypeError, consteq, None, u(''))
+ self.assertRaises(TypeError, consteq, None, u'')
self.assertRaises(TypeError, consteq, None, b'')
- self.assertRaises(TypeError, consteq, 1, u(''))
+ self.assertRaises(TypeError, consteq, 1, u'')
self.assertRaises(TypeError, consteq, 1, b'')
def consteq_supports_string(value):
- # under PY2, it supports all unicode strings (when present at all),
- # under PY3, compare_digest() only supports ascii unicode strings.
- # confirmed for: cpython 2.7.9, cpython 3.4, pypy, pypy3, pyston
- return (consteq is str_consteq or PY2 or is_ascii_safe(value))
+ # compare_digest() only supports ascii unicode strings.
+ # confirmed for: cpython 3.4, pypy3, pyston
+ return (consteq is str_consteq or is_ascii_safe(value))
# check equal inputs compare correctly
for value in [
- u("a"),
- u("abc"),
- u("\xff\xa2\x12\x00")*10,
+ u"a",
+ u"abc",
+ u"\xff\xa2\x12\x00"*10,
]:
if consteq_supports_string(value):
self.assertTrue(consteq(value, value), "value %r:" % (value,))
@@ -296,18 +293,18 @@ class MiscTest(TestCase):
# check non-equal inputs compare correctly
for l,r in [
# check same-size comparisons with differing contents fail.
- (u("a"), u("c")),
- (u("abcabc"), u("zbaabc")),
- (u("abcabc"), u("abzabc")),
- (u("abcabc"), u("abcabz")),
- ((u("\xff\xa2\x12\x00")*10)[:-1] + u("\x01"),
- u("\xff\xa2\x12\x00")*10),
+ (u"a", u"c"),
+ (u"abcabc", u"zbaabc"),
+ (u"abcabc", u"abzabc"),
+ (u"abcabc", u"abcabz"),
+ ((u"\xff\xa2\x12\x00"*10)[:-1] + u"\x01",
+ u"\xff\xa2\x12\x00"*10),
# check different-size comparisons fail.
- (u(""), u("a")),
- (u("abc"), u("abcdef")),
- (u("abc"), u("defabc")),
- (u("qwertyuiopasdfghjklzxcvbnm"), u("abc")),
+ (u"", u"a"),
+ (u"abc", u"abcdef"),
+ (u"abc", u"defabc"),
+ (u"qwertyuiopasdfghjklzxcvbnm", u"abc"),
]:
if consteq_supports_string(l) and consteq_supports_string(r):
self.assertFalse(consteq(l, r), "values %r %r:" % (l,r))
@@ -328,12 +325,12 @@ class MiscTest(TestCase):
# NOTE: below code was used to generate stats for analysis
##from math import log as logb
##import timeit
- ##multipliers = [ 1<<s for s in irange(9)]
+ ##multipliers = [ 1<<s for s in range(9)]
##correct = u"abcdefgh"*(1<<4)
##incorrect = u"abcdxfgh"
##print
##first = True
- ##for run in irange(1):
+ ##for run in range(1):
## times = []
## chars = []
## for m in multipliers:
@@ -365,73 +362,73 @@ class MiscTest(TestCase):
self.assertRaises(TypeError, sp, b'')
# empty strings
- self.assertEqual(sp(u('')), u(''))
- self.assertEqual(sp(u('\u00AD')), u(''))
+ self.assertEqual(sp(u''), u'')
+ self.assertEqual(sp(u'\u00AD'), u'')
# verify B.1 chars are stripped,
- self.assertEqual(sp(u("$\u00AD$\u200D$")), u("$$$"))
+ self.assertEqual(sp(u"$\u00AD$\u200D$"), u"$$$")
# verify C.1.2 chars are replaced with space
- self.assertEqual(sp(u("$ $\u00A0$\u3000$")), u("$ $ $ $"))
+ self.assertEqual(sp(u"$ $\u00A0$\u3000$"), u"$ $ $ $")
# verify normalization to KC
- self.assertEqual(sp(u("a\u0300")), u("\u00E0"))
- self.assertEqual(sp(u("\u00E0")), u("\u00E0"))
+ self.assertEqual(sp(u"a\u0300"), u"\u00E0")
+ self.assertEqual(sp(u"\u00E0"), u"\u00E0")
# verify various forbidden characters
# control chars
- self.assertRaises(ValueError, sp, u("\u0000"))
- self.assertRaises(ValueError, sp, u("\u007F"))
- self.assertRaises(ValueError, sp, u("\u180E"))
- self.assertRaises(ValueError, sp, u("\uFFF9"))
+ self.assertRaises(ValueError, sp, u"\u0000")
+ self.assertRaises(ValueError, sp, u"\u007F")
+ self.assertRaises(ValueError, sp, u"\u180E")
+ self.assertRaises(ValueError, sp, u"\uFFF9")
# private use
- self.assertRaises(ValueError, sp, u("\uE000"))
+ self.assertRaises(ValueError, sp, u"\uE000")
# non-characters
- self.assertRaises(ValueError, sp, u("\uFDD0"))
+ self.assertRaises(ValueError, sp, u"\uFDD0")
# surrogates
- self.assertRaises(ValueError, sp, u("\uD800"))
+ self.assertRaises(ValueError, sp, u"\uD800")
# non-plaintext chars
- self.assertRaises(ValueError, sp, u("\uFFFD"))
+ self.assertRaises(ValueError, sp, u"\uFFFD")
# non-canon
- self.assertRaises(ValueError, sp, u("\u2FF0"))
+ self.assertRaises(ValueError, sp, u"\u2FF0")
# change display properties
- self.assertRaises(ValueError, sp, u("\u200E"))
- self.assertRaises(ValueError, sp, u("\u206F"))
+ self.assertRaises(ValueError, sp, u"\u200E")
+ self.assertRaises(ValueError, sp, u"\u206F")
# unassigned code points (as of unicode 3.2)
- self.assertRaises(ValueError, sp, u("\u0900"))
- self.assertRaises(ValueError, sp, u("\uFFF8"))
+ self.assertRaises(ValueError, sp, u"\u0900")
+ self.assertRaises(ValueError, sp, u"\uFFF8")
# tagging characters
- self.assertRaises(ValueError, sp, u("\U000e0001"))
+ self.assertRaises(ValueError, sp, u"\U000e0001")
# verify bidi behavior
# if starts with R/AL -- must end with R/AL
- self.assertRaises(ValueError, sp, u("\u0627\u0031"))
- self.assertEqual(sp(u("\u0627")), u("\u0627"))
- self.assertEqual(sp(u("\u0627\u0628")), u("\u0627\u0628"))
- self.assertEqual(sp(u("\u0627\u0031\u0628")), u("\u0627\u0031\u0628"))
+ self.assertRaises(ValueError, sp, u"\u0627\u0031")
+ self.assertEqual(sp(u"\u0627"), u"\u0627")
+ self.assertEqual(sp(u"\u0627\u0628"), u"\u0627\u0628")
+ self.assertEqual(sp(u"\u0627\u0031\u0628"), u"\u0627\u0031\u0628")
# if starts with R/AL -- cannot contain L
- self.assertRaises(ValueError, sp, u("\u0627\u0041\u0628"))
+ self.assertRaises(ValueError, sp, u"\u0627\u0041\u0628")
# if doesn't start with R/AL -- can contain R/AL, but L & EN allowed
- self.assertRaises(ValueError, sp, u("x\u0627z"))
- self.assertEqual(sp(u("x\u0041z")), u("x\u0041z"))
+ self.assertRaises(ValueError, sp, u"x\u0627z")
+ self.assertEqual(sp(u"x\u0041z"), u"x\u0041z")
#------------------------------------------------------
# examples pulled from external sources, to be thorough
#------------------------------------------------------
# rfc 4031 section 3 examples
- self.assertEqual(sp(u("I\u00ADX")), u("IX")) # strip SHY
- self.assertEqual(sp(u("user")), u("user")) # unchanged
- self.assertEqual(sp(u("USER")), u("USER")) # case preserved
- self.assertEqual(sp(u("\u00AA")), u("a")) # normalize to KC form
- self.assertEqual(sp(u("\u2168")), u("IX")) # normalize to KC form
- self.assertRaises(ValueError, sp, u("\u0007")) # forbid control chars
- self.assertRaises(ValueError, sp, u("\u0627\u0031")) # invalid bidi
+ self.assertEqual(sp(u"I\u00ADX"), u"IX") # strip SHY
+ self.assertEqual(sp(u"user"), u"user") # unchanged
+ self.assertEqual(sp(u"USER"), u"USER") # case preserved
+ self.assertEqual(sp(u"\u00AA"), u"a") # normalize to KC form
+ self.assertEqual(sp(u"\u2168"), u"IX") # normalize to KC form
+ self.assertRaises(ValueError, sp, u"\u0007") # forbid control chars
+ self.assertRaises(ValueError, sp, u"\u0627\u0031") # invalid bidi
# rfc 3454 section 6 examples
# starts with RAL char, must end with RAL char
- self.assertRaises(ValueError, sp, u("\u0627\u0031"))
- self.assertEqual(sp(u("\u0627\u0031\u0628")), u("\u0627\u0031\u0628"))
+ self.assertRaises(ValueError, sp, u"\u0627\u0031")
+ self.assertEqual(sp(u"\u0627\u0031\u0628"), u"\u0627\u0031\u0628")
def test_splitcomma(self):
from passlib.utils import splitcomma
@@ -569,31 +566,24 @@ class CodecTest(TestCase):
def test_bytes(self):
"""test b() helper, bytes and native str type"""
- if PY3:
- import builtins
- self.assertIs(bytes, builtins.bytes)
- else:
- import __builtin__ as builtins
- self.assertIs(bytes, builtins.str)
+ import builtins
+ self.assertIs(bytes, builtins.bytes)
self.assertIsInstance(b'', bytes)
self.assertIsInstance(b'\x00\xff', bytes)
- if PY3:
- self.assertEqual(b'\x00\xff'.decode("latin-1"), "\x00\xff")
- else:
- self.assertEqual(b'\x00\xff', "\x00\xff")
+ self.assertEqual(b'\x00\xff'.decode("latin-1"), "\x00\xff")
def test_to_bytes(self):
"""test to_bytes()"""
from passlib.utils import to_bytes
# check unicode inputs
- self.assertEqual(to_bytes(u('abc')), b'abc')
- self.assertEqual(to_bytes(u('\x00\xff')), b'\x00\xc3\xbf')
+ self.assertEqual(to_bytes(u'abc'), b'abc')
+ self.assertEqual(to_bytes(u'\x00\xff'), b'\x00\xc3\xbf')
# check unicode w/ encodings
- self.assertEqual(to_bytes(u('\x00\xff'), 'latin-1'), b'\x00\xff')
- self.assertRaises(ValueError, to_bytes, u('\x00\xff'), 'ascii')
+ self.assertEqual(to_bytes(u'\x00\xff', 'latin-1'), b'\x00\xff')
+ self.assertRaises(ValueError, to_bytes, u'\x00\xff', 'ascii')
# check bytes inputs
self.assertEqual(to_bytes(b'abc'), b'abc')
@@ -617,17 +607,17 @@ class CodecTest(TestCase):
from passlib.utils import to_unicode
# check unicode inputs
- self.assertEqual(to_unicode(u('abc')), u('abc'))
- self.assertEqual(to_unicode(u('\x00\xff')), u('\x00\xff'))
+ self.assertEqual(to_unicode(u'abc'), u'abc')
+ self.assertEqual(to_unicode(u'\x00\xff'), u'\x00\xff')
# check unicode input ignores encoding
- self.assertEqual(to_unicode(u('\x00\xff'), "ascii"), u('\x00\xff'))
+ self.assertEqual(to_unicode(u'\x00\xff', "ascii"), u'\x00\xff')
# check bytes input
- self.assertEqual(to_unicode(b'abc'), u('abc'))
- self.assertEqual(to_unicode(b'\x00\xc3\xbf'), u('\x00\xff'))
+ self.assertEqual(to_unicode(b'abc'), u'abc')
+ self.assertEqual(to_unicode(b'\x00\xc3\xbf'), u'\x00\xff')
self.assertEqual(to_unicode(b'\x00\xff', 'latin-1'),
- u('\x00\xff'))
+ u'\x00\xff')
self.assertRaises(ValueError, to_unicode, b'\x00\xff')
# check other
@@ -639,28 +629,21 @@ class CodecTest(TestCase):
from passlib.utils import to_native_str
# test plain ascii
- self.assertEqual(to_native_str(u('abc'), 'ascii'), 'abc')
+ self.assertEqual(to_native_str(u'abc', 'ascii'), 'abc')
self.assertEqual(to_native_str(b'abc', 'ascii'), 'abc')
# test invalid ascii
- if PY3:
- self.assertEqual(to_native_str(u('\xE0'), 'ascii'), '\xE0')
- self.assertRaises(UnicodeDecodeError, to_native_str, b'\xC3\xA0',
- 'ascii')
- else:
- self.assertRaises(UnicodeEncodeError, to_native_str, u('\xE0'),
- 'ascii')
- self.assertEqual(to_native_str(b'\xC3\xA0', 'ascii'), '\xC3\xA0')
+ self.assertEqual(to_native_str(u'\xE0', 'ascii'), '\xE0')
+ self.assertRaises(UnicodeDecodeError, to_native_str, b'\xC3\xA0',
+ 'ascii')
# test latin-1
- self.assertEqual(to_native_str(u('\xE0'), 'latin-1'), '\xE0')
+ self.assertEqual(to_native_str(u'\xE0', 'latin-1'), '\xE0')
self.assertEqual(to_native_str(b'\xE0', 'latin-1'), '\xE0')
# test utf-8
- self.assertEqual(to_native_str(u('\xE0'), 'utf-8'),
- '\xE0' if PY3 else '\xC3\xA0')
- self.assertEqual(to_native_str(b'\xC3\xA0', 'utf-8'),
- '\xE0' if PY3 else '\xC3\xA0')
+ self.assertEqual(to_native_str(u'\xE0', 'utf-8'), '\xE0')
+ self.assertEqual(to_native_str(b'\xC3\xA0', 'utf-8'), '\xE0')
# other types rejected
self.assertRaises(TypeError, to_native_str, None, 'ascii')
@@ -669,9 +652,9 @@ class CodecTest(TestCase):
"""test is_ascii_safe()"""
from passlib.utils import is_ascii_safe
self.assertTrue(is_ascii_safe(b"\x00abc\x7f"))
- self.assertTrue(is_ascii_safe(u("\x00abc\x7f")))
+ self.assertTrue(is_ascii_safe(u"\x00abc\x7f"))
self.assertFalse(is_ascii_safe(b"\x00abc\x80"))
- self.assertFalse(is_ascii_safe(u("\x00abc\x80")))
+ self.assertFalse(is_ascii_safe(u"\x00abc\x80"))
def test_is_same_codec(self):
"""test is_same_codec()"""
@@ -714,15 +697,15 @@ class Base64EngineTest(TestCase):
# accept bytes or unicode
self.assertEqual(ab64_decode(b"abc"), hb("69b7"))
- self.assertEqual(ab64_decode(u("abc")), hb("69b7"))
+ self.assertEqual(ab64_decode(u"abc"), hb("69b7"))
# reject non-ascii unicode
- self.assertRaises(ValueError, ab64_decode, u("ab\xff"))
+ self.assertRaises(ValueError, ab64_decode, u"ab\xff")
# underlying a2b_ascii treats non-base64 chars as "Incorrect padding"
self.assertRaises(TypeError, ab64_decode, b"ab\xff")
self.assertRaises(TypeError, ab64_decode, b"ab!")
- self.assertRaises(TypeError, ab64_decode, u("ab!"))
+ self.assertRaises(TypeError, ab64_decode, u"ab!")
# insert correct padding, handle dirty padding bits
self.assertEqual(ab64_decode(b"abcd"), hb("69b71d")) # 0 mod 4
@@ -744,8 +727,7 @@ class Base64EngineTest(TestCase):
self.assertEqual(ab64_encode(hb("69b7")), b"abc")
# reject unicode
- self.assertRaises(TypeError if PY3 else UnicodeEncodeError,
- ab64_encode, hb("69b7").decode("latin-1"))
+ self.assertRaises(TypeError, ab64_encode, hb("69b7").decode("latin-1"))
# insert correct padding before decoding
self.assertEqual(ab64_encode(hb("69b71d")), b"abcd") # 0 mod 4
@@ -761,15 +743,15 @@ class Base64EngineTest(TestCase):
# accept bytes or unicode
self.assertEqual(b64s_decode(b"abc"), hb("69b7"))
- self.assertEqual(b64s_decode(u("abc")), hb("69b7"))
+ self.assertEqual(b64s_decode(u"abc"), hb("69b7"))
# reject non-ascii unicode
- self.assertRaises(ValueError, b64s_decode, u("ab\xff"))
+ self.assertRaises(ValueError, b64s_decode, u"ab\xff")
# underlying a2b_ascii treats non-base64 chars as "Incorrect padding"
self.assertRaises(TypeError, b64s_decode, b"ab\xff")
self.assertRaises(TypeError, b64s_decode, b"ab!")
- self.assertRaises(TypeError, b64s_decode, u("ab!"))
+ self.assertRaises(TypeError, b64s_decode, u"ab!")
# insert correct padding, handle dirty padding bits
self.assertEqual(b64s_decode(b"abcd"), hb("69b71d")) # 0 mod 4
@@ -786,8 +768,7 @@ class Base64EngineTest(TestCase):
self.assertEqual(b64s_encode(hb("69b7")), b"abc")
# reject unicode
- self.assertRaises(TypeError if PY3 else UnicodeEncodeError,
- b64s_encode, hb("69b7").decode("latin-1"))
+ self.assertRaises(TypeError, b64s_encode, hb("69b7").decode("latin-1"))
# insert correct padding before decoding
self.assertEqual(b64s_encode(hb("69b71d")), b"abcd") # 0 mod 4
@@ -835,7 +816,7 @@ class _Base64Test(TestCase):
"""test encode_bytes() with bad input"""
engine = self.engine
encode = engine.encode_bytes
- self.assertRaises(TypeError, encode, u('\x00'))
+ self.assertRaises(TypeError, encode, u'\x00')
self.assertRaises(TypeError, encode, None)
#===================================================================
@@ -851,7 +832,7 @@ class _Base64Test(TestCase):
def test_decode_bytes_padding(self):
"""test decode_bytes() ignores padding bits"""
- bchr = (lambda v: bytes([v])) if PY3 else chr
+ bchr = lambda v: bytes([v])
engine = self.engine
m = self.m
decode = engine.decode_bytes
@@ -901,7 +882,7 @@ class _Base64Test(TestCase):
from passlib.utils import getrandbytes, getrandstr
rng = self.getRandom()
saw_zero = False
- for i in irange(500):
+ for i in range(500):
#
# test raw -> encode() -> decode() -> raw
#
@@ -999,7 +980,7 @@ class _Base64Test(TestCase):
out = engine.decode_bytes(tmp)
self.assertEqual(out, result)
- self.assertRaises(TypeError, engine.encode_transposed_bytes, u("a"), [])
+ self.assertRaises(TypeError, engine.encode_transposed_bytes, u"a", [])
def test_decode_transposed_bytes(self):
"""test decode_transposed_bytes()"""
@@ -1050,7 +1031,7 @@ class _Base64Test(TestCase):
# do random testing.
from passlib.utils import getrandstr
- for i in irange(100):
+ for i in range(100):
# generate random value, encode, and then decode
value = rng.randint(0, upper-1)
encoded = encode(value)
diff --git a/passlib/tests/test_utils_handlers.py b/passlib/tests/test_utils_handlers.py
index 19cd4ca..f665aac 100644
--- a/passlib/tests/test_utils_handlers.py
+++ b/passlib/tests/test_utils_handlers.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
import re
import hashlib
@@ -12,11 +11,8 @@ import warnings
# pkg
from passlib.hash import ldap_md5, sha256_crypt
from passlib.exc import MissingBackendError, PasslibHashWarning
-from passlib.utils.compat import str_to_uascii, \
- uascii_to_str, unicode
import passlib.utils.handlers as uh
from passlib.tests.utils import HandlerCase, TestCase
-from passlib.utils.compat import u
# module
log = getLogger(__name__)
@@ -50,27 +46,27 @@ class SkeletonTest(TestCase):
class d1(uh.StaticHandler):
name = "d1"
context_kwds = ("flag",)
- _hash_prefix = u("_")
- checksum_chars = u("ab")
+ _hash_prefix = u"_"
+ checksum_chars = u"ab"
checksum_size = 1
def __init__(self, flag=False, **kwds):
- super(d1, self).__init__(**kwds)
+ super().__init__(**kwds)
self.flag = flag
def _calc_checksum(self, secret):
- return u('b') if self.flag else u('a')
+ return u'b' if self.flag else u'a'
# check default identify method
- self.assertTrue(d1.identify(u('_a')))
+ self.assertTrue(d1.identify(u'_a'))
self.assertTrue(d1.identify(b'_a'))
- self.assertTrue(d1.identify(u('_b')))
+ self.assertTrue(d1.identify(u'_b'))
- self.assertFalse(d1.identify(u('_c')))
+ self.assertFalse(d1.identify(u'_c'))
self.assertFalse(d1.identify(b'_c'))
- self.assertFalse(d1.identify(u('a')))
- self.assertFalse(d1.identify(u('b')))
- self.assertFalse(d1.identify(u('c')))
+ self.assertFalse(d1.identify(u'a'))
+ self.assertFalse(d1.identify(u'b'))
+ self.assertFalse(d1.identify(u'c'))
self.assertRaises(TypeError, d1.identify, None)
self.assertRaises(TypeError, d1.identify, 1)
@@ -79,65 +75,17 @@ class SkeletonTest(TestCase):
# check default verify method
self.assertTrue(d1.verify('s', b'_a'))
- self.assertTrue(d1.verify('s',u('_a')))
+ self.assertTrue(d1.verify('s',u'_a'))
self.assertFalse(d1.verify('s', b'_b'))
- self.assertFalse(d1.verify('s',u('_b')))
+ self.assertFalse(d1.verify('s',u'_b'))
self.assertTrue(d1.verify('s', b'_b', flag=True))
self.assertRaises(ValueError, d1.verify, 's', b'_c')
- self.assertRaises(ValueError, d1.verify, 's', u('_c'))
+ self.assertRaises(ValueError, d1.verify, 's', u'_c')
# check default hash method
self.assertEqual(d1.hash('s'), '_a')
self.assertEqual(d1.hash('s', flag=True), '_b')
- def test_01_calc_checksum_hack(self):
- """test StaticHandler legacy attr"""
- # release 1.5 StaticHandler required genhash(),
- # not _calc_checksum, be implemented. we have backward compat wrapper,
- # this tests that it works.
-
- class d1(uh.StaticHandler):
- name = "d1"
-
- @classmethod
- def identify(cls, hash):
- if not hash or len(hash) != 40:
- return False
- try:
- int(hash, 16)
- except ValueError:
- return False
- return True
-
- @classmethod
- def genhash(cls, secret, hash):
- if secret is None:
- raise TypeError("no secret provided")
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- # NOTE: have to support hash=None since this is test of legacy 1.5 api
- if hash is not None and not cls.identify(hash):
- raise ValueError("invalid hash")
- return hashlib.sha1(b"xyz" + secret).hexdigest()
-
- @classmethod
- def verify(cls, secret, hash):
- if hash is None:
- raise ValueError("no hash specified")
- return cls.genhash(secret, hash) == hash.lower()
-
- # hash should issue api warnings, but everything else should be fine.
- with self.assertWarningList("d1.*should be updated.*_calc_checksum"):
- hash = d1.hash("test")
- self.assertEqual(hash, '7c622762588a0e5cc786ad0a143156f9fd38eea3')
-
- self.assertTrue(d1.verify("test", hash))
- self.assertFalse(d1.verify("xtest", hash))
-
- # not defining genhash either, however, should cause NotImplementedError
- del d1.genhash
- self.assertRaises(NotImplementedError, d1.hash, 'test')
-
#===================================================================
# GenericHandler & mixins
#===================================================================
@@ -148,7 +96,7 @@ class SkeletonTest(TestCase):
def from_string(cls, hash):
if isinstance(hash, bytes):
hash = hash.decode("ascii")
- if hash == u('a'):
+ if hash == u'a':
return cls(checksum=hash)
else:
raise ValueError
@@ -161,7 +109,7 @@ class SkeletonTest(TestCase):
self.assertFalse(d1.identify('b'))
# check regexp
- d1._hash_regex = re.compile(u('@.'))
+ d1._hash_regex = re.compile(u'@.')
self.assertRaises(TypeError, d1.identify, None)
self.assertRaises(TypeError, d1.identify, 1)
self.assertTrue(d1.identify('@a'))
@@ -169,7 +117,7 @@ class SkeletonTest(TestCase):
del d1._hash_regex
# check ident-based
- d1.ident = u('!')
+ d1.ident = u'!'
self.assertRaises(TypeError, d1.identify, None)
self.assertRaises(TypeError, d1.identify, 1)
self.assertTrue(d1.identify('!a'))
@@ -182,35 +130,35 @@ class SkeletonTest(TestCase):
class d1(uh.GenericHandler):
name = 'd1'
checksum_size = 4
- checksum_chars = u('xz')
+ checksum_chars = u'xz'
def norm_checksum(checksum=None, **k):
return d1(checksum=checksum, **k).checksum
# too small
- self.assertRaises(ValueError, norm_checksum, u('xxx'))
+ self.assertRaises(ValueError, norm_checksum, u'xxx')
# right size
- self.assertEqual(norm_checksum(u('xxxx')), u('xxxx'))
- self.assertEqual(norm_checksum(u('xzxz')), u('xzxz'))
+ self.assertEqual(norm_checksum(u'xxxx'), u'xxxx')
+ self.assertEqual(norm_checksum(u'xzxz'), u'xzxz')
# too large
- self.assertRaises(ValueError, norm_checksum, u('xxxxx'))
+ self.assertRaises(ValueError, norm_checksum, u'xxxxx')
# wrong chars
- self.assertRaises(ValueError, norm_checksum, u('xxyx'))
+ self.assertRaises(ValueError, norm_checksum, u'xxyx')
# wrong type
self.assertRaises(TypeError, norm_checksum, b'xxyx')
# relaxed
# NOTE: this could be turned back on if we test _norm_checksum() directly...
- #with self.assertWarningList("checksum should be unicode"):
- # self.assertEqual(norm_checksum(b'xxzx', relaxed=True), u('xxzx'))
+ #with self.assertWarningList("checksum should be str"):
+ # self.assertEqual(norm_checksum(b'xxzx', relaxed=True), u'xxzx')
#self.assertRaises(TypeError, norm_checksum, 1, relaxed=True)
# test _stub_checksum behavior
- self.assertEqual(d1()._stub_checksum, u('xxxx'))
+ self.assertEqual(d1()._stub_checksum, u'xxxx')
def test_12_norm_checksum_raw(self):
"""test GenericHandler + HasRawChecksum mixin"""
@@ -224,11 +172,11 @@ class SkeletonTest(TestCase):
# test bytes
self.assertEqual(norm_checksum(b'1234'), b'1234')
- # test unicode
- self.assertRaises(TypeError, norm_checksum, u('xxyx'))
+ # test str
+ self.assertRaises(TypeError, norm_checksum, u'xxyx')
# NOTE: this could be turned back on if we test _norm_checksum() directly...
- # self.assertRaises(TypeError, norm_checksum, u('xxyx'), relaxed=True)
+ # self.assertRaises(TypeError, norm_checksum, u'xxyx', relaxed=True)
# test _stub_checksum behavior
self.assertEqual(d1()._stub_checksum, b'\x00'*4)
@@ -487,9 +435,9 @@ class SkeletonTest(TestCase):
class d1(uh.HasManyIdents, uh.GenericHandler):
name = 'd1'
setting_kwds = ('ident',)
- default_ident = u("!A")
- ident_values = (u("!A"), u("!B"))
- ident_aliases = { u("A"): u("!A")}
+ default_ident = u"!A"
+ ident_values = (u"!A", u"!B")
+ ident_aliases = { u"A": u"!A"}
def norm_ident(**k):
return d1(**k).ident
@@ -497,25 +445,25 @@ class SkeletonTest(TestCase):
# check ident=None
self.assertRaises(TypeError, norm_ident)
self.assertRaises(TypeError, norm_ident, ident=None)
- self.assertEqual(norm_ident(use_defaults=True), u('!A'))
+ self.assertEqual(norm_ident(use_defaults=True), u'!A')
# check valid idents
- self.assertEqual(norm_ident(ident=u('!A')), u('!A'))
- self.assertEqual(norm_ident(ident=u('!B')), u('!B'))
- self.assertRaises(ValueError, norm_ident, ident=u('!C'))
+ self.assertEqual(norm_ident(ident=u'!A'), u'!A')
+ self.assertEqual(norm_ident(ident=u'!B'), u'!B')
+ self.assertRaises(ValueError, norm_ident, ident=u'!C')
# check aliases
- self.assertEqual(norm_ident(ident=u('A')), u('!A'))
+ self.assertEqual(norm_ident(ident=u'A'), u'!A')
# check invalid idents
- self.assertRaises(ValueError, norm_ident, ident=u('B'))
+ self.assertRaises(ValueError, norm_ident, ident=u'B')
# check identify is honoring ident system
- self.assertTrue(d1.identify(u("!Axxx")))
- self.assertTrue(d1.identify(u("!Bxxx")))
- self.assertFalse(d1.identify(u("!Cxxx")))
- self.assertFalse(d1.identify(u("A")))
- self.assertFalse(d1.identify(u("")))
+ self.assertTrue(d1.identify(u"!Axxx"))
+ self.assertTrue(d1.identify(u"!Bxxx"))
+ self.assertFalse(d1.identify(u"!Cxxx"))
+ self.assertFalse(d1.identify(u"A"))
+ self.assertFalse(d1.identify(u""))
self.assertRaises(TypeError, d1.identify, None)
self.assertRaises(TypeError, d1.identify, 1)
@@ -538,12 +486,12 @@ class SkeletonTest(TestCase):
# simple hash w/ salt
result = hash.des_crypt.parsehash("OgAwTx2l6NADI")
- self.assertEqual(result, {'checksum': u('AwTx2l6NADI'), 'salt': u('Og')})
+ self.assertEqual(result, {'checksum': u'AwTx2l6NADI', 'salt': u'Og'})
# parse rounds and extra implicit_rounds flag
h = '$5$LKO/Ute40T3FNF95$U0prpBQd4PloSGU0pnpM4z9wKn4vZ1.jsrzQfPqxph9'
- s = u('LKO/Ute40T3FNF95')
- c = u('U0prpBQd4PloSGU0pnpM4z9wKn4vZ1.jsrzQfPqxph9')
+ s = u'LKO/Ute40T3FNF95'
+ c = u'U0prpBQd4PloSGU0pnpM4z9wKn4vZ1.jsrzQfPqxph9'
result = hash.sha256_crypt.parsehash(h)
self.assertEqual(result, dict(salt=s, rounds=5000,
implicit_rounds=True, checksum=c))
@@ -555,14 +503,14 @@ class SkeletonTest(TestCase):
# sanitize
result = hash.sha256_crypt.parsehash(h, sanitize=True)
self.assertEqual(result, dict(rounds=5000, implicit_rounds=True,
- salt=u('LK**************'),
- checksum=u('U0pr***************************************')))
+ salt=u'LK**************',
+ checksum=u'U0pr***************************************'))
# parse w/o implicit rounds flag
result = hash.sha256_crypt.parsehash('$5$rounds=10428$uy/jIAhCetNCTtb0$YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3')
self.assertEqual(result, dict(
- checksum=u('YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3'),
- salt=u('uy/jIAhCetNCTtb0'),
+ checksum=u'YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3',
+ salt=u'uy/jIAhCetNCTtb0',
rounds=10428,
))
@@ -578,9 +526,9 @@ class SkeletonTest(TestCase):
# sanitizing of raw checksums & salts
result = hash.pbkdf2_sha1.parsehash(h1, sanitize=True)
self.assertEqual(result, dict(
- checksum=u('O26************************'),
+ checksum=u'O26************************',
rounds=60000,
- salt=u('Do********************'),
+ salt=u'Do********************',
))
def test_92_bitsize(self):
@@ -722,7 +670,7 @@ class PrefixWrapperTest(TestCase):
def test_12_ident(self):
# test ident is proxied
h = uh.PrefixWrapper("h2", "ldap_md5", "{XXX}")
- self.assertEqual(h.ident, u("{XXX}{MD5}"))
+ self.assertEqual(h.ident, u"{XXX}{MD5}")
self.assertIs(h.ident_values, None)
# test lack of ident means no proxy
@@ -737,7 +685,7 @@ class PrefixWrapperTest(TestCase):
# test custom ident overrides default
h = uh.PrefixWrapper("h3", "ldap_md5", "{XXX}", ident="{X")
- self.assertEqual(h.ident, u("{X"))
+ self.assertEqual(h.ident, u"{X")
self.assertIs(h.ident_values, None)
# test custom ident must match
@@ -750,11 +698,11 @@ class PrefixWrapperTest(TestCase):
# test ident_values is proxied
h = uh.PrefixWrapper("h4", "phpass", "{XXX}")
self.assertIs(h.ident, None)
- self.assertEqual(h.ident_values, (u("{XXX}$P$"), u("{XXX}$H$")))
+ self.assertEqual(h.ident_values, (u"{XXX}$P$", u"{XXX}$H$"))
# test ident=True means use prefix even if hash has no ident.
h = uh.PrefixWrapper("h5", "des_crypt", "{XXX}", ident=True)
- self.assertEqual(h.ident, u("{XXX}"))
+ self.assertEqual(h.ident, u"{XXX}")
self.assertIs(h.ident_values, None)
# ... but requires prefix
@@ -763,7 +711,7 @@ class PrefixWrapperTest(TestCase):
# orig_prefix + HasManyIdent - warning
with self.assertWarningList("orig_prefix.*may not work correctly"):
h = uh.PrefixWrapper("h7", "phpass", orig_prefix="$", prefix="?")
- self.assertEqual(h.ident_values, None) # TODO: should output (u("?P$"), u("?H$")))
+ self.assertEqual(h.ident_values, None) # TODO: should output (u"?P$", u"?H$"))
self.assertEqual(h.ident, None)
def test_13_repr(self):
@@ -796,10 +744,11 @@ class UnsaltedHash(uh.StaticHandler):
checksum_size = 40
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
data = b"boblious" + secret
- return str_to_uascii(hashlib.sha1(data).hexdigest())
+ return hashlib.sha1(data).hexdigest()
+
class SaltedHash(uh.HasSalt, uh.GenericHandler):
"""test algorithm with a salt"""
@@ -811,7 +760,7 @@ class SaltedHash(uh.HasSalt, uh.GenericHandler):
checksum_size = 40
salt_chars = checksum_chars = uh.LOWER_HEX_CHARS
- _hash_regex = re.compile(u("^@salt[0-9a-f]{42,44}$"))
+ _hash_regex = re.compile(u"^@salt[0-9a-f]{42,44}$")
@classmethod
def from_string(cls, hash):
@@ -822,14 +771,15 @@ class SaltedHash(uh.HasSalt, uh.GenericHandler):
return cls(salt=hash[5:-40], checksum=hash[-40:])
def to_string(self):
- hash = u("@salt%s%s") % (self.salt, self.checksum)
- return uascii_to_str(hash)
+ hash = u"@salt%s%s" % (self.salt, self.checksum)
+ return hash
def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
+ if isinstance(secret, str):
secret = secret.encode("utf-8")
data = self.salt.encode("ascii") + secret + self.salt.encode("ascii")
- return str_to_uascii(hashlib.sha1(data).hexdigest())
+ return hashlib.sha1(data).hexdigest()
+
#=============================================================================
# test sample algorithms - really a self-test of HandlerCase
@@ -838,7 +788,7 @@ class SaltedHash(uh.HasSalt, uh.GenericHandler):
# TODO: provide data samples for algorithms
# (positive knowns, negative knowns, invalid identify)
-UPASS_TEMP = u('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2')
+UPASS_TEMP = u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2'
class UnsaltedHashTest(HandlerCase):
handler = UnsaltedHash
diff --git a/passlib/tests/test_utils_md4.py b/passlib/tests/test_utils_md4.py
index 5d824a1..e3e04d4 100644
--- a/passlib/tests/test_utils_md4.py
+++ b/passlib/tests/test_utils_md4.py
@@ -29,7 +29,7 @@ class Legacy_MD4_Test(_Common_MD4_Test):
descriptionPrefix = "passlib.utils.md4.md4()"
def setUp(self):
- super(Legacy_MD4_Test, self).setUp()
+ super().setUp()
warnings.filterwarnings("ignore", ".*passlib.utils.md4.*deprecated", DeprecationWarning)
def get_md4_const(self):
diff --git a/passlib/tests/test_utils_pbkdf2.py b/passlib/tests/test_utils_pbkdf2.py
index 443eb53..0e10248 100644
--- a/passlib/tests/test_utils_pbkdf2.py
+++ b/passlib/tests/test_utils_pbkdf2.py
@@ -12,72 +12,16 @@ passlib.tests -- tests for passlib.utils.pbkdf2
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
import hashlib
import warnings
# site
# pkg
# module
-from passlib.utils.compat import u, JYTHON
+from passlib.utils.compat import JYTHON
from passlib.tests.utils import TestCase, hb
#=============================================================================
-# test assorted crypto helpers
-#=============================================================================
-class UtilsTest(TestCase):
- """test various utils functions"""
- descriptionPrefix = "passlib.utils.pbkdf2"
-
- ndn_formats = ["hashlib", "iana"]
- ndn_values = [
- # (iana name, hashlib name, ... other unnormalized names)
- ("md5", "md5", "SCRAM-MD5-PLUS", "MD-5"),
- ("sha1", "sha-1", "SCRAM-SHA-1", "SHA1"),
- ("sha256", "sha-256", "SHA_256", "sha2-256"),
- ("ripemd160", "ripemd-160", "SCRAM-RIPEMD-160", "RIPEmd160",
- # NOTE: there was an older "RIPEMD" & "RIPEMD-128", but python treates "RIPEMD"
- # as alias for "RIPEMD-160"
- "ripemd", "SCRAM-RIPEMD"),
- ("test128", "test-128", "TEST128"),
- ("test2", "test2", "TEST-2"),
- ("test3_128", "test3-128", "TEST-3-128"),
- ]
-
- def setUp(self):
- super(UtilsTest, self).setUp()
- warnings.filterwarnings("ignore", ".*passlib.utils.pbkdf2.*deprecated", DeprecationWarning)
-
- def test_norm_hash_name(self):
- """norm_hash_name()"""
- from itertools import chain
- from passlib.utils.pbkdf2 import norm_hash_name
- from passlib.crypto.digest import _known_hash_names
-
- # test formats
- for format in self.ndn_formats:
- norm_hash_name("md4", format)
- self.assertRaises(ValueError, norm_hash_name, "md4", None)
- self.assertRaises(ValueError, norm_hash_name, "md4", "fake")
-
- # test types
- self.assertEqual(norm_hash_name(u("MD4")), "md4")
- self.assertEqual(norm_hash_name(b"MD4"), "md4")
- self.assertRaises(TypeError, norm_hash_name, None)
-
- # test selected results
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", '.*unknown hash')
- for row in chain(_known_hash_names, self.ndn_values):
- for idx, format in enumerate(self.ndn_formats):
- correct = row[idx]
- for value in row:
- result = norm_hash_name(value, format)
- self.assertEqual(result, correct,
- "name=%r, format=%r:" % (value,
- format))
-
-#=============================================================================
# test PBKDF1 support
#=============================================================================
class Pbkdf1_Test(TestCase):
@@ -108,7 +52,7 @@ class Pbkdf1_Test(TestCase):
)
def setUp(self):
- super(Pbkdf1_Test, self).setUp()
+ super().setUp()
warnings.filterwarnings("ignore", ".*passlib.utils.pbkdf2.*deprecated", DeprecationWarning)
def test_known(self):
@@ -263,7 +207,7 @@ class Pbkdf2_Test(TestCase):
]
def setUp(self):
- super(Pbkdf2_Test, self).setUp()
+ super().setUp()
warnings.filterwarnings("ignore", ".*passlib.utils.pbkdf2.*deprecated", DeprecationWarning)
def test_known(self):
diff --git a/passlib/tests/test_win32.py b/passlib/tests/test_win32.py
deleted file mode 100644
index e818b62..0000000
--- a/passlib/tests/test_win32.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""tests for passlib.win32 -- (c) Assurance Technologies 2003-2009"""
-#=============================================================================
-# imports
-#=============================================================================
-# core
-import warnings
-# site
-# pkg
-from passlib.tests.utils import TestCase
-# module
-from passlib.utils.compat import u
-
-#=============================================================================
-#
-#=============================================================================
-class UtilTest(TestCase):
- """test util funcs in passlib.win32"""
-
- ##test hashes from http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx
- ## among other places
-
- def setUp(self):
- super(UtilTest, self).setUp()
- warnings.filterwarnings("ignore",
- "the 'passlib.win32' module is deprecated")
-
- def test_lmhash(self):
- from passlib.win32 import raw_lmhash
- for secret, hash in [
- ("OLDPASSWORD", u("c9b81d939d6fd80cd408e6b105741864")),
- ("NEWPASSWORD", u('09eeab5aa415d6e4d408e6b105741864')),
- ("welcome", u("c23413a8a1e7665faad3b435b51404ee")),
- ]:
- result = raw_lmhash(secret, hex=True)
- self.assertEqual(result, hash)
-
- def test_nthash(self):
- warnings.filterwarnings("ignore",
- r"nthash\.raw_nthash\(\) is deprecated")
- from passlib.win32 import raw_nthash
- for secret, hash in [
- ("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")),
- ("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")),
- ]:
- result = raw_nthash(secret, hex=True)
- self.assertEqual(result, hash)
-
-#=============================================================================
-# eof
-#=============================================================================
diff --git a/passlib/tests/tox_support.py b/passlib/tests/tox_support.py
index 43170bc..d0dcd4e 100644
--- a/passlib/tests/tox_support.py
+++ b/passlib/tests/tox_support.py
@@ -14,7 +14,6 @@ import re
import logging; log = logging.getLogger(__name__)
# site
# pkg
-from passlib.utils.compat import print_
# local
__all__ = [
]
@@ -40,7 +39,7 @@ def do_hash_tests(*args):
from passlib.tests import test_handlers
names = [TH_PATH + ":" + name + suffix for name in dir(test_handlers)
if not name.startswith("_") and any(re.match(arg,name) for arg in args)]
- print_("\n".join(names))
+ print("\n".join(names))
return not names
def do_preset_tests(name):
@@ -48,7 +47,7 @@ def do_preset_tests(name):
if name == "django" or name == "django-hashes":
do_hash_tests("django_.*_test", "hex_md5_test")
if name == "django":
- print_("passlib.tests.test_ext_django")
+ print("passlib.tests.test_ext_django")
else:
raise ValueError("unknown name: %r" % name)
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index 79a9f9f..9ade9de 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
from binascii import unhexlify
import contextlib
@@ -16,8 +15,9 @@ import sys
import tempfile
import threading
import time
+import unittest
from passlib.exc import PasslibHashWarning, PasslibConfigWarning
-from passlib.utils.compat import PY3, JYTHON
+from passlib.utils.compat import JYTHON
import warnings
from warnings import warn
# site
@@ -25,11 +25,9 @@ from warnings import warn
from passlib import exc
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, \
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, nullcontext
from passlib.utils.decor import classproperty
import passlib.utils.handlers as uh
# local
@@ -219,7 +217,7 @@ def patch_calc_min_rounds(handler):
#=============================================================================
def set_file(path, content):
"""set file to specified bytes"""
- if isinstance(content, unicode):
+ if isinstance(content, str):
content = content.encode("utf-8")
with open(path, "wb") as fh:
fh.write(content)
@@ -231,15 +229,10 @@ def get_file(path):
def tonn(source):
"""convert native string to non-native string"""
- if not isinstance(source, str):
- return source
- elif PY3:
+ if isinstance(source, str):
return source.encode("utf-8")
else:
- try:
- return source.decode("utf-8")
- except UnicodeDecodeError:
- return source.decode("latin-1")
+ return source
def hb(source):
"""
@@ -288,7 +281,7 @@ def run_with_fixed_seeds(count=128, master_seed=0x243F6A8885A308D3):
@wraps(func)
def wrapper(*args, **kwds):
rng = random.Random(master_seed)
- for _ in irange(count):
+ for _ in range(count):
kwds['seed'] = rng.getrandbits(32)
func(*args, **kwds)
return wrapper
@@ -298,7 +291,7 @@ def run_with_fixed_seeds(count=128, master_seed=0x243F6A8885A308D3):
# custom test harness
#=============================================================================
-class TestCase(_TestCase):
+class TestCase(unittest.TestCase):
"""passlib-specific test case class
this class adds a number of features to the standard TestCase...
@@ -321,7 +314,7 @@ class TestCase(_TestCase):
def shortDescription(self):
"""wrap shortDescription() method to prepend descriptionPrefix"""
- desc = super(TestCase, self).shortDescription()
+ desc = super().shortDescription()
prefix = self.descriptionPrefix
if prefix:
desc = "%s: %s" % (prefix, desc or str(self))
@@ -333,7 +326,7 @@ class TestCase(_TestCase):
#---------------------------------------------------------------
@classproperty
def __unittest_skip__(cls):
- # NOTE: this attr is technically a unittest2 internal detail.
+ # NOTE: this attr is technically a unittest internal detail.
name = cls.__name__
return name.startswith("_") or \
getattr(cls, "_%s__unittest_skip" % name, False)
@@ -354,7 +347,7 @@ class TestCase(_TestCase):
resetWarningState = True
def setUp(self):
- super(TestCase, self).setUp()
+ super().setUp()
self.setUpWarnings()
# have uh.debug_only_repr() return real values for duration of test
self.patchAttr(exc, "ENABLE_DEBUG_ONLY_REPR", True)
@@ -375,7 +368,6 @@ class TestCase(_TestCase):
# should be kept until then, so we test the legacy paths.
warnings.filterwarnings("ignore", r"the method .*\.(encrypt|genconfig|genhash)\(\) is deprecated")
warnings.filterwarnings("ignore", r"the 'vary_rounds' option is deprecated")
- warnings.filterwarnings("ignore", r"Support for `(py-bcrypt|bcryptor)` is deprecated")
#---------------------------------------------------------------
# tweak message formatting so longMessage mode is only enabled
@@ -397,7 +389,7 @@ class TestCase(_TestCase):
msg = kwds.pop("__msg__", None)
if _callable is None:
# FIXME: this ignores 'msg'
- return super(TestCase, self).assertRaises(_exc_type, None,
+ return super().assertRaises(_exc_type, None,
*args, **kwds)
try:
result = _callable(*args, **kwds)
@@ -411,7 +403,7 @@ class TestCase(_TestCase):
# forbid a bunch of deprecated aliases so I stop using them
#---------------------------------------------------------------
def assertEquals(self, *a, **k):
- raise AssertionError("this alias is deprecated by unittest2")
+ raise AssertionError("this alias is deprecated by stdlib unittest")
assertNotEquals = assertRegexMatches = assertEquals
#===================================================================
@@ -470,14 +462,13 @@ class TestCase(_TestCase):
def __init__(self, case, **kwds):
self.case = case
self.kwds = kwds
- self.__super = super(TestCase._AssertWarningList, self)
- self.__super.__init__(record=True)
+ super().__init__(record=True)
def __enter__(self):
- self.log = self.__super.__enter__()
+ self.log = super().__enter__()
def __exit__(self, *exc_info):
- self.__super.__exit__(*exc_info)
+ super().__exit__(*exc_info)
if exc_info[0] is None:
self.case.assertWarningList(self.log, **self.kwds)
@@ -631,20 +622,14 @@ class TestCase(_TestCase):
# subtests
#===================================================================
- has_real_subtest = hasattr(_TestCase, "subTest")
-
@contextlib.contextmanager
def subTest(self, *args, **kwds):
"""
- wrapper/backport for .subTest() which also traps SkipTest errors.
+ wrapper for .subTest() which traps SkipTest errors.
(see source for details)
"""
- # this function works around two things:
- # * TestCase.subTest() wasn't added until Py34; so for older python versions,
- # we either need unittest2 installed, or provide stub of our own.
- # this method provides a stub if needed (based on .has_real_subtest check)
- #
- # * as 2020-10-08, .subTest() doesn't play nicely w/ .skipTest();
+ # this function works around issue that as 2020-10-08,
+ # .subTest() doesn't play nicely w/ .skipTest();
# and also makes it hard to debug which subtest had a failure.
# (see https://bugs.python.org/issue25894 and https://bugs.python.org/issue35327)
# this method traps skipTest exceptions, and adds some logging to help debug
@@ -663,13 +648,8 @@ class TestCase(_TestCase):
test_log = self.getLogger()
title = _render_title(*args, **kwds)
- # use real subtest manager if available
- if self.has_real_subtest:
- ctx = super(TestCase, self).subTest(*args, **kwds)
- else:
- ctx = nullcontext()
-
# run the subtest
+ ctx = super().subTest(*args, **kwds)
with ctx:
test_log.info("running subtest: %s", title)
try:
@@ -677,7 +657,9 @@ class TestCase(_TestCase):
except SkipTest:
# silence "SkipTest" exceptions, want to keep running next subtest.
test_log.info("subtest skipped: %s", title)
- pass
+ # XXX: should revisit whether latest py3 version of UTs handle this ok,
+ # meaning it's safe to re-raise this.
+ return
except Exception as err:
# log unhandled exception occurred
# (assuming traceback will be reported up higher, so not bothering here)
@@ -734,8 +716,7 @@ class TestCase(_TestCase):
return logger named after current test.
"""
cls = type(self)
- # NOTE: conditional on qualname for PY2 compat
- path = cls.__module__ + "." + getattr(cls, "__qualname__", cls.__name__)
+ path = cls.__module__ + "." + cls.__qualname__
name = self._testMethodName
if name:
path = path + "." + name
@@ -778,8 +759,7 @@ class HandlerCase(TestCase):
.. note::
- This is subclass of :class:`unittest.TestCase`
- (or :class:`unittest2.TestCase` if available).
+ This is subclass of :class:`unittest.TestCase`.
"""
#===================================================================
# class attrs - should be filled in by subclass
@@ -831,8 +811,8 @@ class HandlerCase(TestCase):
# passwords used to test basic hash behavior - generally
# don't need to be overidden.
stock_passwords = [
- u("test"),
- u("\u20AC\u00A5$"),
+ u"test",
+ u"\u20AC\u00A5$",
b'\xe2\x82\xac\xc2\xa5$'
]
@@ -1065,7 +1045,7 @@ class HandlerCase(TestCase):
if test_requires_backend and self._skip_backend_reason:
raise self.skipTest(self._skip_backend_reason)
- super(HandlerCase, self).setUp()
+ super().setUp()
# if needed, select specific backend for duration of test
# NOTE: skipping this if create_backend_case() signalled we're skipping backend
@@ -1384,7 +1364,7 @@ class HandlerCase(TestCase):
def sampler(func):
value1 = func()
- for _ in irange(samples):
+ for _ in range(samples):
value2 = func()
if value1 != value2:
return
@@ -1500,7 +1480,7 @@ class HandlerCase(TestCase):
self.do_stub_encrypt(salt=salt)
# check some invalid salt chars, make sure they're rejected
- source = u('\x00\xff')
+ source = u'\x00\xff'
if raw:
source = source.encode("latin-1")
chunk = max(mn, 1)
@@ -1516,7 +1496,7 @@ class HandlerCase(TestCase):
if getattr(self.handler, "_salt_is_bytes", False):
return bytes
else:
- return unicode
+ return str
def test_15_salt_type(self):
"""test non-string salt values"""
@@ -1530,12 +1510,11 @@ class HandlerCase(TestCase):
self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=fake())
# unicode should be accepted only if salt_type is unicode.
- if salt_type is not unicode:
- self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=u('x') * salt_size)
+ if salt_type is not str:
+ self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=u'x' * salt_size)
- # bytes should be accepted only if salt_type is bytes,
- # OR if salt type is unicode and running PY2 - to allow native strings.
- if not (salt_type is bytes or (PY2 and salt_type is unicode)):
+ # bytes should be accepted only if salt_type is bytes
+ if salt_type is not bytes:
self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=b'x' * salt_size)
def test_using_salt_size(self):
@@ -1936,7 +1915,7 @@ class HandlerCase(TestCase):
handler, subcls, small, medium, large, adj = self._create_using_rounds_helper()
def get_effective_range(cls):
- seen = set(get_effective_rounds(cls) for _ in irange(1000))
+ seen = set(get_effective_rounds(cls) for _ in range(1000))
return min(seen), max(seen)
def assert_rounds_range(vary_rounds, lower, upper):
@@ -2011,24 +1990,24 @@ class HandlerCase(TestCase):
# check ident_values list
for value in cls.ident_values:
- self.assertIsInstance(value, unicode,
- "cls.ident_values must be unicode:")
+ self.assertIsInstance(value, str,
+ "cls.ident_values must be str:")
self.assertTrue(len(cls.ident_values)>1,
"cls.ident_values must have 2+ elements:")
# check default_ident value
- self.assertIsInstance(cls.default_ident, unicode,
- "cls.default_ident must be unicode:")
+ self.assertIsInstance(cls.default_ident, str,
+ "cls.default_ident must be str:")
self.assertTrue(cls.default_ident in cls.ident_values,
"cls.default_ident must specify member of cls.ident_values")
# check optional aliases list
if cls.ident_aliases:
- for alias, ident in iteritems(cls.ident_aliases):
- self.assertIsInstance(alias, unicode,
- "cls.ident_aliases keys must be unicode:") # XXX: allow ints?
- self.assertIsInstance(ident, unicode,
- "cls.ident_aliases values must be unicode:")
+ for alias, ident in cls.ident_aliases.items():
+ self.assertIsInstance(alias, str,
+ "cls.ident_aliases keys must be str:") # XXX: allow ints?
+ self.assertIsInstance(ident, str,
+ "cls.ident_aliases values must be str:")
self.assertTrue(ident in cls.ident_values,
"cls.ident_aliases must map to cls.ident_values members: %r" % (ident,))
@@ -2347,7 +2326,7 @@ class HandlerCase(TestCase):
chars = self.forbidden_characters
if not chars:
raise self.skipTest("none listed")
- base = u('stub')
+ base = u'stub'
if isinstance(chars, bytes):
from passlib.utils.compat import iter_byte_chars
chars = iter_byte_chars(chars)
@@ -2366,7 +2345,7 @@ class HandlerCase(TestCase):
"""
check if we're expecting potential verify failure due to crypt.crypt() encoding limitation
"""
- if PY3 and self.backend == "os_crypt" and isinstance(secret, bytes):
+ if self.backend == "os_crypt" and isinstance(secret, bytes):
try:
secret.decode("utf-8")
except UnicodeDecodeError:
@@ -2578,7 +2557,7 @@ class HandlerCase(TestCase):
#
# test hash='' is rejected for all but the plaintext hashes
#
- for hash in [u(''), b'']:
+ for hash in [u'', b'']:
if self.accepts_all_hashes:
# then it accepts empty string as well.
self.assertTrue(self.do_identify(hash))
@@ -2609,7 +2588,7 @@ class HandlerCase(TestCase):
def require_parsehash(self):
if not hasattr(self.handler, "parsehash"):
- raise SkipTest("parsehash() not implemented")
+ raise self.skipTest("parsehash() not implemented")
def test_70_parsehash(self):
"""
@@ -2638,11 +2617,6 @@ class HandlerCase(TestCase):
# but all else should be the same
result3 = handler.parsehash(hash, sanitize=True)
correct3 = result.copy()
- if PY2:
- # silence warning about bytes & unicode not comparing
- # (sanitize may convert bytes into base64 text)
- warnings.filterwarnings("ignore", ".*unequal comparison failed to convert.*",
- category=UnicodeWarning)
for key in ("salt", "checksum"):
if key in result3:
self.assertNotEqual(result3[key], correct3[key])
@@ -2656,7 +2630,7 @@ class HandlerCase(TestCase):
"""
if value is None:
return
- self.assertIsInstance(value, unicode)
+ self.assertIsInstance(value, str)
# assumes mask_value() defaults will never show more than <show> chars (4);
# and show nothing if size less than 1/<pct> (8).
ref = value if len(value) < 8 else value[4:]
@@ -2797,7 +2771,7 @@ class HandlerCase(TestCase):
def wrapper():
try:
self.test_77_fuzz_input(threaded=True)
- except SkipTest:
+ except unittest.SkipTest:
pass
except:
with failed_lock:
@@ -2811,7 +2785,7 @@ class HandlerCase(TestCase):
thread.setDaemon(True)
thread.start()
return thread
- threads = [launch(n) for n in irange(thread_count)]
+ threads = [launch(n) for n in range(thread_count)]
# wait until all threads exit
timeout = self.max_fuzz_time * thread_count * 4
@@ -2874,7 +2848,7 @@ class HandlerCase(TestCase):
used by fuzz testing.
verifiers should be callable with signature
- ``func(password: unicode, hash: ascii str) -> ok: bool``.
+ ``func(password: str, hash: ascii str) -> ok: bool``.
"""
handler = self.handler
verifiers = []
@@ -2929,7 +2903,7 @@ class HandlerCase(TestCase):
#==========================================================
# alphabet for randomly generated passwords
- password_alphabet = u('qwertyASDF1234<>.@*#! \u00E1\u0259\u0411\u2113')
+ password_alphabet = u'qwertyASDF1234<>.@*#! \u00E1\u0259\u0411\u2113'
# encoding when testing bytes
password_encoding = "utf-8"
@@ -3039,7 +3013,7 @@ class HandlerCase(TestCase):
# occasionally try an empty password
rng = self.rng
if rng.random() < .0001:
- return u('')
+ return u''
# check if truncate size needs to be considered
handler = self.handler
@@ -3058,7 +3032,7 @@ class HandlerCase(TestCase):
result = getrandstr(rng, self.password_alphabet, size)
# trim ones that encode past truncate point.
- if truncate_size and isinstance(result, unicode):
+ if truncate_size and isinstance(result, str):
while len(result.encode("utf-8")) > truncate_size:
result = result[:-1]
@@ -3210,7 +3184,7 @@ class OsCryptMixin(HandlerCase):
if not self.handler.has_backend("os_crypt"):
# XXX: currently, any tests that use this are skipped entirely! (see issue 120)
self._patch_safe_crypt()
- super(OsCryptMixin, self).setUp()
+ super().setUp()
@classmethod
def _get_safe_crypt_handler_backend(cls):
@@ -3267,7 +3241,7 @@ class OsCryptMixin(HandlerCase):
when it's known os_crypt will be faked by _patch_safe_crypt()
"""
assert backend == "os_crypt"
- reason = super(OsCryptMixin, cls)._get_skip_backend_reason(backend)
+ reason = super()._get_skip_backend_reason(backend)
from passlib.utils import has_crypt
if reason == cls._BACKEND_NOT_AVAILABLE and has_crypt:
@@ -3515,7 +3489,7 @@ class UserHandlerMixin(HandlerCase):
context_map = HandlerCase.FuzzHashGenerator.context_map.copy()
context_map.update(user="random_user")
- user_alphabet = u("asdQWE123")
+ user_alphabet = u"asdQWE123"
def random_user(self):
rng = self.rng
@@ -3543,14 +3517,14 @@ class EncodingHandlerMixin(HandlerCase):
# restrict stock passwords & fuzz alphabet to latin-1,
# so different encodings can be tested safely.
stock_passwords = [
- u("test"),
+ u"test",
b"test",
- u("\u00AC\u00BA"),
+ u"\u00AC\u00BA",
]
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
- password_alphabet = u('qwerty1234<>.@*#! \u00AC')
+ password_alphabet = u'qwerty1234<>.@*#! \u00AC'
def populate_context(self, secret, kwds):
"""insert encoding into kwds"""
@@ -3569,13 +3543,13 @@ class reset_warnings(warnings.catch_warnings):
"""catch_warnings() wrapper which clears warning registry & filters"""
def __init__(self, reset_filter="always", reset_registry=".*", **kwds):
- super(reset_warnings, self).__init__(**kwds)
+ super().__init__(**kwds)
self._reset_filter = reset_filter
self._reset_registry = re.compile(reset_registry) if reset_registry else None
def __enter__(self):
# let parent class archive filter state
- ret = super(reset_warnings, self).__enter__()
+ ret = super().__enter__()
# reset the filter to list everything
if self._reset_filter:
@@ -3614,7 +3588,7 @@ class reset_warnings(warnings.catch_warnings):
setattr(mod, "__warningregistry__", orig)
else:
reg.update(orig)
- super(reset_warnings, self).__exit__(*exc_info)
+ super().__exit__(*exc_info)
#=============================================================================
# eof
diff --git a/passlib/totp.py b/passlib/totp.py
index 9ad5000..a133354 100644
--- a/passlib/totp.py
+++ b/passlib/totp.py
@@ -2,8 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import absolute_import, division, print_function
-from passlib.utils.compat import PY3
# core
import base64
import calendar
@@ -14,11 +12,7 @@ import struct
import sys
import time as _time
import re
-if PY3:
- from urllib.parse import urlparse, parse_qsl, quote, unquote
-else:
- from urllib import quote, unquote
- from urlparse import urlparse, parse_qsl
+from urllib.parse import urlparse, parse_qsl, quote, unquote
from warnings import warn
# site
try:
@@ -37,8 +31,7 @@ from passlib.exc import TokenError, MalformedTokenError, InvalidTokenError, Used
from passlib.utils import (to_unicode, to_bytes, consteq,
getrandbytes, rng, SequenceMixin, xor_bytes, getrandstr)
from passlib.utils.binary import BASE64_CHARS, b32encode, b32decode
-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.compat import bascii_to_str, num_types
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
@@ -60,20 +53,6 @@ __all__ = [
]
#=============================================================================
-# HACK: python < 2.7.4's urlparse() won't parse query strings unless the url scheme
-# is one of the schemes in the urlparse.uses_query list. 2.7 abandoned
-# this, and parses query if present, regardless of the scheme.
-# as a workaround for older versions, we add "otpauth" to the known list.
-# this was fixed by https://bugs.python.org/issue9374, in 2.7.4 release.
-#=============================================================================
-if sys.version_info < (2,7,4):
- from urlparse import uses_query
- if "otpauth" not in uses_query:
- uses_query.append("otpauth")
- log.debug("registered 'otpauth' scheme with urlparse.uses_query")
- del uses_query
-
-#=============================================================================
# internal helpers
#=============================================================================
@@ -82,7 +61,7 @@ if sys.version_info < (2,7,4):
#-----------------------------------------------------------------------------
#: regex used to clean whitespace from tokens & keys
-_clean_re = re.compile(u(r"\s|[-=]"), re.U)
+_clean_re = re.compile(r"\s|[-=]", re.U)
_chunk_sizes = [4,6,5]
@@ -112,7 +91,7 @@ def group_string(value, sep="-"):
"""
klen = len(value)
size = _get_group_size(klen)
- return sep.join(value[o:o+size] for o in irange(0, klen, size))
+ return sep.join(value[o:o+size] for o in range(0, klen, size))
#-----------------------------------------------------------------------------
# encoding helpers
@@ -269,7 +248,7 @@ class AppWallet(object):
# init cost
#
if encrypt_cost is not None:
- if isinstance(encrypt_cost, native_string_types):
+ if isinstance(encrypt_cost, str):
encrypt_cost = int(encrypt_cost)
assert encrypt_cost >= 0
self.encrypt_cost = encrypt_cost
@@ -311,7 +290,7 @@ class AppWallet(object):
# to make this easy to pass in configuration from a separate file,
# 'secrets' can be string using two formats -- json & "tag:value\n"
check_type = True
- if isinstance(source, native_string_types):
+ if isinstance(source, str):
if source.lstrip().startswith(("[", "{")):
# json list / dict
source = json.loads(source)
@@ -344,12 +323,12 @@ class AppWallet(object):
for tag, value in source)
def _parse_secret_pair(self, tag, value):
- if isinstance(tag, native_string_types):
+ if isinstance(tag, str):
pass
elif isinstance(tag, int):
tag = str(tag)
else:
- raise TypeError("tag must be unicode/string: %r" % (tag,))
+ raise TypeError("tag must be string: %r" % (tag,))
if not _tag_re.match(tag):
raise ValueError("tag contains invalid characters: %r" % (tag,))
if not isinstance(value, bytes):
@@ -378,7 +357,7 @@ class AppWallet(object):
try:
return secrets[tag]
except KeyError:
- raise suppress_cause(KeyError("unknown secret tag: %r" % (tag,)))
+ raise KeyError("unknown secret tag: %r" % (tag,)) from None
#========================================================================
# encrypted key helpers -- used internally by TOTP
@@ -772,7 +751,7 @@ class TOTP(object):
new=False, digits=None, alg=None, size=None, period=None,
label=None, issuer=None, changed=False,
**kwds):
- super(TOTP, self).__init__(**kwds)
+ super().__init__(**kwds)
if changed:
self.changed = changed
@@ -819,7 +798,7 @@ class TOTP(object):
# validate digits
if digits is None:
digits = self.digits
- if not isinstance(digits, int_types):
+ if not isinstance(digits, int):
raise TypeError("digits must be an integer, not a %r" % type(digits))
if digits < 6 or digits > 10:
raise ValueError("digits must in range(6,11)")
@@ -849,7 +828,7 @@ class TOTP(object):
"""
check that serial value (e.g. 'counter') is non-negative integer
"""
- if not isinstance(value, int_types):
+ if not isinstance(value, int):
raise exc.ExpectedTypeError(value, "int", param)
if value < minval:
raise ValueError("%s must be >= %d" % (param, minval))
@@ -995,7 +974,7 @@ class TOTP(object):
:returns:
unix epoch timestamp as :class:`int`.
"""
- if isinstance(time, int_types):
+ if isinstance(time, int):
return time
elif isinstance(time, float):
return int(time)
@@ -1034,20 +1013,20 @@ class TOTP(object):
or use the class default.
:arg token:
- token as ascii bytes, unicode, or an integer.
+ token as ascii bytes, str, or an integer.
:raises ValueError:
if token has wrong number of digits, or contains non-numeric characters.
:returns:
- token as :class:`!unicode` string, containing only digits 0-9.
+ token as :class:`!str`, containing only digits 0-9.
"""
digits = self_or_cls.digits
- if isinstance(token, int_types):
- token = u("%0*d") % (digits, token)
+ if isinstance(token, int):
+ token = u"%0*d" % (digits, token)
else:
token = to_unicode(token, param="token")
- token = _clean_re.sub(u(""), token)
+ token = _clean_re.sub(u"", token)
if not token.isdigit():
raise MalformedTokenError("Token must contain only the digits 0-9")
if len(token) != digits:
@@ -1110,7 +1089,7 @@ class TOTP(object):
:returns: token as unicode string
"""
# generate digest
- assert isinstance(counter, int_types), "counter must be integer"
+ assert isinstance(counter, int), "counter must be integer"
assert counter >= 0, "counter must be non-negative"
keyed_hmac = self._keyed_hmac
if keyed_hmac is None:
@@ -1120,8 +1099,9 @@ class TOTP(object):
assert len(digest) == digest_size, "digest_size: sanity check failed"
# derive 31-bit token value
+ # assert isinstance(digest, bytes)
assert digest_size >= 20, "digest_size: sanity check 2 failed" # otherwise 0xF+4 will run off end of hash.
- offset = byte_elem_value(digest[-1]) & 0xF
+ offset = digest[-1] & 0xF
value = _unpack_uint32(digest[offset:offset+4])[0] & 0x7fffffff
# render to decimal string, return last <digits> chars
@@ -1130,7 +1110,7 @@ class TOTP(object):
# if 31-bit mask removed (which breaks spec), would only get values 0-4.
digits = self.digits
assert 0 < digits < 11, "digits: sanity check failed"
- return (u("%0*d") % (digits, value))[-digits:]
+ return (u"%0*d" % (digits, value))[-digits:]
#=============================================================================
# token verification
@@ -1293,8 +1273,8 @@ class TOTP(object):
# XXX: if (end - start) is very large (e.g. for resync purposes),
# could start with expected value, and work outward from there,
# alternately checking before & after it until match is found.
- # XXX: can't use irange(start, end) here since py2x/win32
- # throws error on values >= (1<<31), which 'end' can be.
+ # TODO: replace counter loop with "for counter in range(start, end)";
+ # think this was holding from PY2+win32 issue with values > 32 bit (e.g. 'end').
counter = start
while counter < end:
if consteq(token, generate(counter)):
@@ -1550,11 +1530,11 @@ class TOTP(object):
# NOTE: not using urllib.urlencode() because it encodes ' ' as '+';
# but spec says to use '%20', and not sure how fragile
# the various totp clients' parsers are.
- param_str = u("&").join(u("%s=%s") % (key, quote(value, '')) for key, value in params)
+ param_str = u"&".join(u"%s=%s" % (key, quote(value, '')) for key, value in params)
assert param_str, "param_str should never be empty"
# render uri
- return u("otpauth://totp/%s?%s") % (label, param_str)
+ return u"otpauth://totp/%s?%s" % (label, param_str)
def _to_uri_params(self):
"""return list of (key, param) entries for URI"""
@@ -1578,7 +1558,7 @@ class TOTP(object):
(as generated by :meth:`to_json`).
:arg json:
- Serialized output from :meth:`to_json`, as unicode or ascii bytes.
+ Serialized output from :meth:`to_json`, as str or ascii bytes.
:raises ValueError:
If the key has been encrypted, but the application secret isn't available;
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'
diff --git a/passlib/utils/binary.py b/passlib/utils/binary.py
index 521b64a..f60ddf2 100644
--- a/passlib/utils/binary.py
+++ b/passlib/utils/binary.py
@@ -5,7 +5,6 @@ passlib.utils.binary - binary data encoding/decoding/manipulation
# imports
#=============================================================================
# core
-from __future__ import absolute_import, division, print_function
from base64 import (
b64encode,
b64decode,
@@ -19,10 +18,9 @@ log = logging.getLogger(__name__)
# pkg
from passlib import exc
from passlib.utils.compat import (
- PY3, bascii_to_str,
- irange, imap, iter_byte_chars, join_byte_values, join_byte_elems,
- nextgetter, suppress_cause,
- u, unicode, unicode_or_bytes_types,
+ bascii_to_str,
+ iter_byte_chars,
+ unicode_or_bytes,
)
from passlib.utils.decor import memoized_property
# from passlib.utils import BASE64_CHARS, HASH64_CHARS
@@ -64,28 +62,28 @@ __all__ = [
#-------------------------------------------------------------
#: standard base64 charmap
-BASE64_CHARS = u("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
+BASE64_CHARS = u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
#: alt base64 charmap -- "." instead of "+"
-AB64_CHARS = u("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./")
+AB64_CHARS = u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./"
#: charmap used by HASH64 encoding.
-HASH64_CHARS = u("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
+HASH64_CHARS = u"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
#: charmap used by BCrypt
-BCRYPT_CHARS = u("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+BCRYPT_CHARS = u"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
#: std base64 chars + padding char
-PADDED_BASE64_CHARS = BASE64_CHARS + u("=")
+PADDED_BASE64_CHARS = BASE64_CHARS + u"="
#: all hex chars
-HEX_CHARS = u("0123456789abcdefABCDEF")
+HEX_CHARS = u"0123456789abcdefABCDEF"
#: upper case hex chars
-UPPER_HEX_CHARS = u("0123456789ABCDEF")
+UPPER_HEX_CHARS = u"0123456789ABCDEF"
#: lower case hex chars
-LOWER_HEX_CHARS = u("0123456789abcdef")
+LOWER_HEX_CHARS = u"0123456789abcdef"
#-------------------------------------------------------------
# byte strings
@@ -93,7 +91,7 @@ LOWER_HEX_CHARS = u("0123456789abcdef")
#: special byte string containing all possible byte values
#: NOTE: for efficiency, this is treated as singleton by some of the code
-ALL_BYTE_VALUES = join_byte_values(irange(256))
+ALL_BYTE_VALUES = bytes(range(256))
#: some string constants we reuse
B_EMPTY = b''
@@ -128,10 +126,10 @@ def compile_byte_translation(mapping, source=None):
assert isinstance(source, bytes) and len(source) == 255
target = list(iter_byte_chars(source))
for k, v in mapping.items():
- if isinstance(k, unicode_or_bytes_types):
+ if isinstance(k, unicode_or_bytes):
k = ord(k)
assert isinstance(k, int) and 0 <= k < 256
- if isinstance(v, unicode):
+ if isinstance(v, str):
v = v.encode("ascii")
assert isinstance(v, bytes) and len(v) == 1
target[k] = v
@@ -152,12 +150,12 @@ def b64s_decode(data):
decode from shortened base64 format which omits padding & whitespace.
uses default ``+/`` altchars.
"""
- if isinstance(data, unicode):
+ if isinstance(data, str):
# needs bytes for replace() call, but want to accept ascii-unicode ala a2b_base64()
try:
data = data.encode("ascii")
except UnicodeEncodeError:
- raise suppress_cause(ValueError("string argument should contain only ASCII characters"))
+ raise ValueError("string argument should contain only ASCII characters") from None
off = len(data) & 3
if off == 0:
pass
@@ -170,7 +168,7 @@ def b64s_decode(data):
try:
return a2b_base64(data)
except _BinAsciiError as err:
- raise suppress_cause(TypeError(err))
+ raise TypeError(err) from None
#=============================================================================
# adapted-base64 encoding
@@ -198,12 +196,12 @@ def ab64_decode(data):
it is primarily used by Passlib's custom pbkdf2 hashes.
"""
- if isinstance(data, unicode):
+ if isinstance(data, str):
# needs bytes for replace() call, but want to accept ascii-unicode ala a2b_base64()
try:
data = data.encode("ascii")
except UnicodeEncodeError:
- raise suppress_cause(ValueError("string argument should contain only ASCII characters"))
+ raise ValueError("string argument should contain only ASCII characters") from None
return b64s_decode(data.replace(b".", b"+"))
#=============================================================================
@@ -233,7 +231,7 @@ def b32decode(source):
padding optional, ignored if present.
"""
# encode & correct for typos
- if isinstance(source, unicode):
+ if isinstance(source, str):
source = source.encode("ascii")
source = source.translate(_b32_translate)
@@ -336,7 +334,7 @@ class Base64Engine(object):
#===================================================================
def __init__(self, charmap, big=False):
# validate charmap, generate encode64/decode64 helper functions.
- if isinstance(charmap, unicode):
+ if isinstance(charmap, str):
charmap = charmap.encode("latin-1")
elif not isinstance(charmap, bytes):
raise exc.ExpectedStringError(charmap, "charmap")
@@ -385,12 +383,9 @@ class Base64Engine(object):
if not isinstance(source, bytes):
raise TypeError("source must be bytes, not %s" % (type(source),))
chunks, tail = divmod(len(source), 3)
- if PY3:
- next_value = nextgetter(iter(source))
- else:
- next_value = nextgetter(ord(elem) for elem in source)
+ next_value = iter(source).__next__
gen = self._encode_bytes(next_value, chunks, tail)
- out = join_byte_elems(imap(self._encode64, gen))
+ out = bytes(map(self._encode64, gen))
##if tail:
## padding = self.padding
## if padding:
@@ -495,9 +490,9 @@ class Base64Engine(object):
if tail == 1:
# only 6 bits left, can't encode a whole byte!
raise ValueError("input string length cannot be == 1 mod 4")
- next_value = nextgetter(imap(self._decode64, source))
+ next_value = map(self._decode64, source).__next__
try:
- return join_byte_values(self._decode_bytes(next_value, chunks, tail))
+ return bytes(self._decode_bytes(next_value, chunks, tail))
except KeyError as err:
raise ValueError("invalid character: %r" % (err.args[0],))
@@ -626,7 +621,7 @@ class Base64Engine(object):
# we have dirty bits - repair the string by decoding last char,
# clearing the padding bits via <mask>, and encoding new char.
- if isinstance(source, unicode):
+ if isinstance(source, str):
cm = self.charmap
last = cm[cm.index(last) & mask]
assert last in padset, "failed to generate valid padding char"
@@ -635,8 +630,7 @@ class Base64Engine(object):
# all chars used by encoding are 7-bit ascii.
last = self._encode64(self._decode64(last) & mask)
assert last in padset, "failed to generate valid padding char"
- if PY3:
- last = bytes([last])
+ last = bytes([last])
return True, source[:-1] + last
def repair_unused(self, source):
@@ -661,19 +655,19 @@ class Base64Engine(object):
"""encode byte string, first transposing source using offset list"""
if not isinstance(source, bytes):
raise TypeError("source must be bytes, not %s" % (type(source),))
- tmp = join_byte_elems(source[off] for off in offsets)
+ tmp = bytes(source[off] for off in offsets)
return self.encode_bytes(tmp)
def decode_transposed_bytes(self, source, offsets):
"""decode byte string, then reverse transposition described by offset list"""
# NOTE: if transposition does not use all bytes of source,
- # the original can't be recovered... and join_byte_elems() will throw
+ # the original can't be recovered... and bytes() will throw
# an error because 1+ values in <buf> will be None.
tmp = self.decode_bytes(source)
buf = [None] * len(offsets)
for off, char in zip(offsets, tmp):
buf[off] = char
- return join_byte_elems(buf)
+ return bytes(buf)
#===================================================================
# integer decoding helpers - mainly used by des_crypt family
@@ -724,9 +718,8 @@ class Base64Engine(object):
raise TypeError("source must be bytes, not %s" % (type(source),))
if len(source) != 1:
raise ValueError("source must be exactly 1 byte")
- if PY3:
- # convert to 8bit int before doing lookup
- source = source[0]
+ # convert to 8bit int before doing lookup
+ source = source[0]
try:
return self._decode64(source)
except KeyError:
@@ -792,13 +785,13 @@ class Base64Engine(object):
pad = -bits % 6
bits += pad
if self.big:
- itr = irange(bits-6, -6, -6)
+ itr = range(bits-6, -6, -6)
# shift to add lsb padding.
value <<= pad
else:
- itr = irange(0, bits, 6)
+ itr = range(0, bits, 6)
# padding is msb, so no change needed.
- return join_byte_elems(imap(self._encode64,
+ return bytes(map(self._encode64,
((value>>off) & 0x3f for off in itr)))
#---------------------------------------------------------------
@@ -809,10 +802,7 @@ class Base64Engine(object):
"""encodes 6-bit integer -> single hash64 character"""
if value < 0 or value > 63:
raise ValueError("value out of range")
- if PY3:
- return self.bytemap[value:value+1]
- else:
- return self._encode64(value)
+ return self.bytemap[value:value+1]
def encode_int12(self, value):
"""encodes 12-bit integer -> 2 char string"""
@@ -821,7 +811,7 @@ class Base64Engine(object):
raw = [value & 0x3f, (value>>6) & 0x3f]
if self.big:
raw = reversed(raw)
- return join_byte_elems(imap(self._encode64, raw))
+ return bytes(map(self._encode64, raw))
def encode_int24(self, value):
"""encodes 24-bit integer -> 4 char string"""
@@ -831,7 +821,7 @@ class Base64Engine(object):
(value>>12) & 0x3f, (value>>18) & 0x3f]
if self.big:
raw = reversed(raw)
- return join_byte_elems(imap(self._encode64, raw))
+ return bytes(map(self._encode64, raw))
def encode_int30(self, value):
"""decode 5 char string -> 30 bit integer"""
@@ -862,7 +852,7 @@ class LazyBase64Engine(Base64Engine):
def _lazy_init(self):
args, kwds = self._lazy_opts
- super(LazyBase64Engine, self).__init__(*args, **kwds)
+ super().__init__(*args, **kwds)
del self._lazy_opts
self.__class__ = Base64Engine
diff --git a/passlib/utils/compat/__init__.py b/passlib/utils/compat/__init__.py
index f6ead24..06bd266 100644
--- a/passlib/utils/compat/__init__.py
+++ b/passlib/utils/compat/__init__.py
@@ -7,14 +7,10 @@
# python version
#------------------------------------------------------------------------
import sys
-PY2 = sys.version_info < (3,0)
-PY3 = sys.version_info >= (3,0)
# make sure it's not an unsupported version, even if we somehow got this far
-if sys.version_info < (2,6) or (3,0) <= sys.version_info < (3,2):
- raise RuntimeError("Passlib requires Python 2.6, 2.7, or >= 3.2 (as of passlib 1.7)")
-
-PY26 = sys.version_info < (2,7)
+if sys.version_info < (3, 5):
+ raise RuntimeError("Passlib requires Python >= 3.5 (as of passlib 1.8)")
#------------------------------------------------------------------------
# python implementation
@@ -26,18 +22,10 @@ PYPY = hasattr(sys, "pypy_version_info")
if PYPY and sys.pypy_version_info < (2,0):
raise RuntimeError("passlib requires pypy >= 2.0 (as of passlib 1.7)")
-# e.g. '2.7.7\n[Pyston 0.5.1]'
-# NOTE: deprecated support 2019-11
-PYSTON = "Pyston" in sys.version
-
#=============================================================================
# common imports
#=============================================================================
import logging; log = logging.getLogger(__name__)
-if PY3:
- import builtins
-else:
- import __builtin__ as builtins
def add_doc(obj, doc):
"""add docstring to an object"""
@@ -47,38 +35,15 @@ def add_doc(obj, doc):
# the default exported vars
#=============================================================================
__all__ = [
- # python versions
- 'PY2', 'PY3', 'PY26',
-
- # io
- 'BytesIO', 'StringIO', 'NativeStringIO', 'SafeConfigParser',
- 'print_',
-
# type detection
## 'is_mapping',
- 'int_types',
'num_types',
- 'unicode_or_bytes_types',
- 'native_string_types',
+ 'unicode_or_bytes',
# unicode/bytes types & helpers
- 'u',
- 'unicode',
- 'uascii_to_str', 'bascii_to_str',
- 'str_to_uascii', 'str_to_bascii',
+ 'bascii_to_str',
+ 'str_to_bascii',
'join_unicode', 'join_bytes',
- 'join_byte_values', 'join_byte_elems',
- 'byte_elem_value',
- 'iter_byte_values',
-
- # iteration helpers
- 'irange', #'lrange',
- 'imap', 'lmap',
- 'iteritems', 'itervalues',
- 'next',
-
- # collections
- 'OrderedDict',
# context helpers
'nullcontext',
@@ -94,179 +59,47 @@ _lazy_attrs = dict()
#=============================================================================
# unicode & bytes types
#=============================================================================
-if PY3:
- unicode = str
-
- # TODO: once we drop python 3.2 support, can use u'' again!
- def u(s):
- assert isinstance(s, str)
- return s
-
- unicode_or_bytes_types = (str, bytes)
- native_string_types = (unicode,)
-else:
- unicode = builtins.unicode
-
- def u(s):
- assert isinstance(s, str)
- return s.decode("unicode_escape")
-
- unicode_or_bytes_types = (basestring,)
- native_string_types = (basestring,)
-
-# shorter preferred aliases
-unicode_or_bytes = unicode_or_bytes_types
-unicode_or_str = native_string_types
-
-# unicode -- unicode type, regardless of python version
-# bytes -- bytes type, regardless of python version
-# unicode_or_bytes_types -- types that text can occur in, whether encoded or not
-# native_string_types -- types that native python strings (dict keys etc) can occur in.
+#: alias for isinstance() tests to detect any string type
+unicode_or_bytes = (str, bytes)
#=============================================================================
# unicode & bytes helpers
#=============================================================================
# function to join list of unicode strings
-join_unicode = u('').join
+join_unicode = u''.join
# function to join list of byte strings
join_bytes = b''.join
-if PY3:
- def uascii_to_str(s):
- assert isinstance(s, unicode)
- return s
+if True: # legacy PY3 indent
def bascii_to_str(s):
assert isinstance(s, bytes)
return s.decode("ascii")
- def str_to_uascii(s):
- assert isinstance(s, str)
- return s
-
def str_to_bascii(s):
assert isinstance(s, str)
return s.encode("ascii")
- join_byte_values = join_byte_elems = bytes
-
- def byte_elem_value(elem):
- assert isinstance(elem, int)
- return elem
-
- def iter_byte_values(s):
- assert isinstance(s, bytes)
- return s
-
def iter_byte_chars(s):
assert isinstance(s, bytes)
# FIXME: there has to be a better way to do this
return (bytes([c]) for c in s)
-else:
- def uascii_to_str(s):
- assert isinstance(s, unicode)
- return s.encode("ascii")
-
- def bascii_to_str(s):
- assert isinstance(s, bytes)
- return s
-
- def str_to_uascii(s):
- assert isinstance(s, str)
- return s.decode("ascii")
-
- def str_to_bascii(s):
- assert isinstance(s, str)
- return s
-
- def join_byte_values(values):
- return join_bytes(chr(v) for v in values)
-
- join_byte_elems = join_bytes
-
- byte_elem_value = ord
-
- def iter_byte_values(s):
- assert isinstance(s, bytes)
- return (ord(c) for c in s)
-
- def iter_byte_chars(s):
- assert isinstance(s, bytes)
- return s
-
-add_doc(uascii_to_str, "helper to convert ascii unicode -> native str")
+# TODO: move docstrings to funcs...
add_doc(bascii_to_str, "helper to convert ascii bytes -> native str")
-add_doc(str_to_uascii, "helper to convert ascii native str -> unicode")
add_doc(str_to_bascii, "helper to convert ascii native str -> bytes")
-# join_byte_values -- function to convert list of ordinal integers to byte string.
-
-# join_byte_elems -- function to convert list of byte elements to byte string;
-# i.e. what's returned by ``b('a')[0]``...
-# this is b('a') under PY2, but 97 under PY3.
-
# byte_elem_value -- function to convert byte element to integer -- a noop under PY3
-add_doc(iter_byte_values, "iterate over byte string as sequence of ints 0-255")
add_doc(iter_byte_chars, "iterate over byte string as sequence of 1-byte strings")
#=============================================================================
# numeric
#=============================================================================
-if PY3:
- int_types = (int,)
- num_types = (int, float)
-else:
- int_types = (int, long)
- num_types = (int, long, float)
-
-#=============================================================================
-# iteration helpers
-#
-# irange - range iterable / view (xrange under py2, range under py3)
-# lrange - range list (range under py2, list(range()) under py3)
-#
-# imap - map to iterator
-# lmap - map to list
-#=============================================================================
-if PY3:
- irange = range
- ##def lrange(*a,**k):
- ## return list(range(*a,**k))
-
- def lmap(*a, **k):
- return list(map(*a,**k))
- imap = map
-
- def iteritems(d):
- return d.items()
- def itervalues(d):
- return d.values()
-
- def nextgetter(obj):
- return obj.__next__
-
- izip = zip
-
-else:
- irange = xrange
- ##lrange = range
- lmap = map
- from itertools import imap, izip
-
- def iteritems(d):
- return d.iteritems()
- def itervalues(d):
- return d.itervalues()
-
- def nextgetter(obj):
- return obj.next
-
-add_doc(nextgetter, "return function that yields successive values from iterable")
+num_types = (int, float)
#=============================================================================
# typing
@@ -278,103 +111,10 @@ add_doc(nextgetter, "return function that yields successive values from iterable
#=============================================================================
# introspection
#=============================================================================
-if PY3:
- method_function_attr = "__func__"
-else:
- method_function_attr = "im_func"
def get_method_function(func):
"""given (potential) method, return underlying function"""
- return getattr(func, method_function_attr, func)
-
-def get_unbound_method_function(func):
- """given unbound method, return underlying function"""
- return func if PY3 else func.__func__
-
-def error_from(exc, # *,
- cause=None):
- """
- backward compat hack to suppress exception cause in python3.3+
-
- one python < 3.3 support is dropped, can replace all uses with "raise exc from None"
- """
- exc.__cause__ = cause
- exc.__suppress_context__ = True
- return exc
-
-# legacy alias
-suppress_cause = error_from
-
-#=============================================================================
-# input/output
-#=============================================================================
-if PY3:
- _lazy_attrs = dict(
- BytesIO="io.BytesIO",
- UnicodeIO="io.StringIO",
- NativeStringIO="io.StringIO",
- SafeConfigParser="configparser.ConfigParser",
- )
-
- print_ = getattr(builtins, "print")
-
-else:
- _lazy_attrs = dict(
- BytesIO="cStringIO.StringIO",
- UnicodeIO="StringIO.StringIO",
- NativeStringIO="cStringIO.StringIO",
- SafeConfigParser="ConfigParser.SafeConfigParser",
- )
-
- def print_(*args, **kwds):
- """The new-style print function."""
- # extract kwd args
- fp = kwds.pop("file", sys.stdout)
- sep = kwds.pop("sep", None)
- end = kwds.pop("end", None)
- if kwds:
- raise TypeError("invalid keyword arguments")
-
- # short-circuit if no target
- if fp is None:
- return
-
- # use unicode or bytes ?
- want_unicode = isinstance(sep, unicode) or isinstance(end, unicode) or \
- any(isinstance(arg, unicode) for arg in args)
-
- # pick default end sequence
- if end is None:
- end = u("\n") if want_unicode else "\n"
- elif not isinstance(end, unicode_or_bytes_types):
- raise TypeError("end must be None or a string")
-
- # pick default separator
- if sep is None:
- sep = u(" ") if want_unicode else " "
- elif not isinstance(sep, unicode_or_bytes_types):
- raise TypeError("sep must be None or a string")
-
- # write to buffer
- first = True
- write = fp.write
- for arg in args:
- if first:
- first = False
- else:
- write(sep)
- if not isinstance(arg, basestring):
- arg = str(arg)
- write(arg)
- write(end)
-
-#=============================================================================
-# collections
-#=============================================================================
-if PY26:
- _lazy_attrs['OrderedDict'] = 'passlib.utils.compat._ordered_dict.OrderedDict'
-else:
- _lazy_attrs['OrderedDict'] = 'collections.OrderedDict'
+ return getattr(func, "__func__", func)
#=============================================================================
# context managers
diff --git a/passlib/utils/compat/_ordered_dict.py b/passlib/utils/compat/_ordered_dict.py
deleted file mode 100644
index cfd766d..0000000
--- a/passlib/utils/compat/_ordered_dict.py
+++ /dev/null
@@ -1,242 +0,0 @@
-"""passlib.utils.compat._ordered_dict -- backport of collections.OrderedDict for py26
-
-taken from stdlib-suggested recipe at http://code.activestate.com/recipes/576693/
-
-this should be imported from passlib.utils.compat.OrderedDict, not here.
-"""
-
-try:
- from thread import get_ident as _get_ident
-except ImportError:
- from dummy_thread import get_ident as _get_ident
-
-class OrderedDict(dict):
- """Dictionary that remembers insertion order"""
- # An inherited dict maps keys to values.
- # The inherited dict provides __getitem__, __len__, __contains__, and get.
- # The remaining methods are order-aware.
- # Big-O running times for all methods are the same as for regular dictionaries.
-
- # The internal self.__map dictionary maps keys to links in a doubly linked list.
- # The circular doubly linked list starts and ends with a sentinel element.
- # The sentinel element never gets deleted (this simplifies the algorithm).
- # Each link is stored as a list of length three: [PREV, NEXT, KEY].
-
- def __init__(self, *args, **kwds):
- '''Initialize an ordered dictionary. Signature is the same as for
- regular dictionaries, but keyword arguments are not recommended
- because their insertion order is arbitrary.
-
- '''
- if len(args) > 1:
- raise TypeError('expected at most 1 arguments, got %d' % len(args))
- try:
- self.__root
- except AttributeError:
- self.__root = root = [] # sentinel node
- root[:] = [root, root, None]
- self.__map = {}
- self.__update(*args, **kwds)
-
- def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
- 'od.__setitem__(i, y) <==> od[i]=y'
- # Setting a new item creates a new link which goes at the end of the linked
- # list, and the inherited dictionary is updated with the new key/value pair.
- if key not in self:
- root = self.__root
- last = root[0]
- last[1] = root[0] = self.__map[key] = [last, root, key]
- dict_setitem(self, key, value)
-
- def __delitem__(self, key, dict_delitem=dict.__delitem__):
- 'od.__delitem__(y) <==> del od[y]'
- # Deleting an existing item uses self.__map to find the link which is
- # then removed by updating the links in the predecessor and successor nodes.
- dict_delitem(self, key)
- link_prev, link_next, key = self.__map.pop(key)
- link_prev[1] = link_next
- link_next[0] = link_prev
-
- def __iter__(self):
- 'od.__iter__() <==> iter(od)'
- root = self.__root
- curr = root[1]
- while curr is not root:
- yield curr[2]
- curr = curr[1]
-
- def __reversed__(self):
- 'od.__reversed__() <==> reversed(od)'
- root = self.__root
- curr = root[0]
- while curr is not root:
- yield curr[2]
- curr = curr[0]
-
- def clear(self):
- 'od.clear() -> None. Remove all items from od.'
- try:
- for node in self.__map.itervalues():
- del node[:]
- root = self.__root
- root[:] = [root, root, None]
- self.__map.clear()
- except AttributeError:
- pass
- dict.clear(self)
-
- def popitem(self, last=True):
- '''od.popitem() -> (k, v), return and remove a (key, value) pair.
- Pairs are returned in LIFO order if last is true or FIFO order if false.
-
- '''
- if not self:
- raise KeyError('dictionary is empty')
- root = self.__root
- if last:
- link = root[0]
- link_prev = link[0]
- link_prev[1] = root
- root[0] = link_prev
- else:
- link = root[1]
- link_next = link[1]
- root[1] = link_next
- link_next[0] = root
- key = link[2]
- del self.__map[key]
- value = dict.pop(self, key)
- return key, value
-
- # -- the following methods do not depend on the internal structure --
-
- def keys(self):
- 'od.keys() -> list of keys in od'
- return list(self)
-
- def values(self):
- 'od.values() -> list of values in od'
- return [self[key] for key in self]
-
- def items(self):
- 'od.items() -> list of (key, value) pairs in od'
- return [(key, self[key]) for key in self]
-
- def iterkeys(self):
- 'od.iterkeys() -> an iterator over the keys in od'
- return iter(self)
-
- def itervalues(self):
- 'od.itervalues -> an iterator over the values in od'
- for k in self:
- yield self[k]
-
- def iteritems(self):
- 'od.iteritems -> an iterator over the (key, value) items in od'
- for k in self:
- yield (k, self[k])
-
- def update(*args, **kwds):
- '''od.update(E, **F) -> None. Update od from dict/iterable E and F.
-
- If E is a dict instance, does: for k in E: od[k] = E[k]
- If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
- Or if E is an iterable of items, does: for k, v in E: od[k] = v
- In either case, this is followed by: for k, v in F.items(): od[k] = v
-
- '''
- if len(args) > 2:
- raise TypeError('update() takes at most 2 positional '
- 'arguments (%d given)' % (len(args),))
- elif not args:
- raise TypeError('update() takes at least 1 argument (0 given)')
- self = args[0]
- # Make progressively weaker assumptions about "other"
- other = ()
- if len(args) == 2:
- other = args[1]
- if isinstance(other, dict):
- for key in other:
- self[key] = other[key]
- elif hasattr(other, 'keys'):
- for key in other.keys():
- self[key] = other[key]
- else:
- for key, value in other:
- self[key] = value
- for key, value in kwds.items():
- self[key] = value
-
- __update = update # let subclasses override update without breaking __init__
-
- __marker = object()
-
- def pop(self, key, default=__marker):
- '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
- If key is not found, d is returned if given, otherwise KeyError is raised.
-
- '''
- if key in self:
- result = self[key]
- del self[key]
- return result
- if default is self.__marker:
- raise KeyError(key)
- return default
-
- def setdefault(self, key, default=None):
- 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
- if key in self:
- return self[key]
- self[key] = default
- return default
-
- def __repr__(self, _repr_running={}):
- 'od.__repr__() <==> repr(od)'
- call_key = id(self), _get_ident()
- if call_key in _repr_running:
- return '...'
- _repr_running[call_key] = 1
- try:
- if not self:
- return '%s()' % (self.__class__.__name__,)
- return '%s(%r)' % (self.__class__.__name__, self.items())
- finally:
- del _repr_running[call_key]
-
- def __reduce__(self):
- 'Return state information for pickling'
- items = [[k, self[k]] for k in self]
- inst_dict = vars(self).copy()
- for k in vars(OrderedDict()):
- inst_dict.pop(k, None)
- if inst_dict:
- return (self.__class__, (items,), inst_dict)
- return self.__class__, (items,)
-
- def copy(self):
- 'od.copy() -> a shallow copy of od'
- return self.__class__(self)
-
- @classmethod
- def fromkeys(cls, iterable, value=None):
- '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
- and values equal to v (which defaults to None).
-
- '''
- d = cls()
- for key in iterable:
- d[key] = value
- return d
-
- def __eq__(self, other):
- '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
- while comparison to a regular mapping is order-insensitive.
-
- '''
- if isinstance(other, OrderedDict):
- return len(self)==len(other) and self.items() == other.items()
- return dict.__eq__(self, other)
-
- def __ne__(self, other):
- return not self == other
diff --git a/passlib/utils/decor.py b/passlib/utils/decor.py
index 9041d5d..e1fd601 100644
--- a/passlib/utils/decor.py
+++ b/passlib/utils/decor.py
@@ -5,7 +5,6 @@ passlib.utils.decor -- helper decorators & properties
# imports
#=============================================================================
# core
-from __future__ import absolute_import, division, print_function
import logging
log = logging.getLogger(__name__)
from functools import wraps, update_wrapper
@@ -13,7 +12,6 @@ import types
from warnings import warn
# site
# pkg
-from passlib.utils.compat import PY3
# local
__all__ = [
"classproperty",
@@ -33,15 +31,12 @@ 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
+ # XXX: rename to .fget to match property?
+ self.__func__ = func
def __get__(self, obj, cls):
- return self.im_func(cls)
+ return self.__func__(cls)
- @property
- def __func__(self):
- """py3 compatible alias"""
- return self.im_func
class hybrid_method(object):
"""
@@ -50,16 +45,14 @@ class hybrid_method(object):
"""
def __init__(self, func):
+ # XXX: rename to .fget to match property?
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)
+ return types.MethodType(self.func, obj)
#=============================================================================
# memoization
@@ -104,13 +97,6 @@ class memoized_property(object):
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).
@@ -174,8 +160,7 @@ def deprecated_function(msg=None, deprecated=None, removed=None, updoc=True,
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__
+ func = func.__func__
opts = dict(
mod=func_module or func.__module__,
name=func.__name__,
diff --git a/passlib/utils/des.py b/passlib/utils/des.py
index 034bfc4..1c407b9 100644
--- a/passlib/utils/des.py
+++ b/passlib/utils/des.py
@@ -17,30 +17,15 @@ warn("the 'passlib.utils.des' module has been relocated to 'passlib.crypto.des'
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",
+expand_des_key = deprecated_function(deprecated="1.7", removed="2.0",
replacement="passlib.crypto.des.expand_des_key")(expand_des_key)
-des_encrypt_block = deprecated_function(deprecated="1.7", removed="1.8",
+des_encrypt_block = deprecated_function(deprecated="1.7", removed="2.0",
replacement="passlib.crypto.des.des_encrypt_block")(des_encrypt_block)
-des_encrypt_int_block = deprecated_function(deprecated="1.7", removed="1.8",
+des_encrypt_int_block = deprecated_function(deprecated="1.7", removed="2.0",
replacement="passlib.crypto.des.des_encrypt_int_block")(des_encrypt_int_block)
#=============================================================================
-# deprecated functions -- not carried over to passlib.crypto.des
-#=============================================================================
-import struct
-_unpack_uint64 = struct.Struct(">Q").unpack
-
-@deprecated_function(deprecated="1.6", removed="1.8",
- replacement="passlib.crypto.des.des_encrypt_int_block()")
-def mdes_encrypt_int_block(key, input, salt=0, rounds=1): # pragma: no cover -- deprecated & unused
- if isinstance(key, bytes):
- if len(key) == 7:
- key = expand_des_key(key)
- key = _unpack_uint64(key)[0]
- return des_encrypt_int_block(key, input, salt, rounds)
-
-#=============================================================================
# eof
#=============================================================================
diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py
index f8681fa..cea096c 100644
--- a/passlib/utils/handlers.py
+++ b/passlib/utils/handlers.py
@@ -2,7 +2,6 @@
#=============================================================================
# imports
#=============================================================================
-from __future__ import with_statement
# core
import inspect
import logging; log = logging.getLogger(__name__)
@@ -27,9 +26,7 @@ from passlib.utils.binary import (
HEX_CHARS, UPPER_HEX_CHARS, LOWER_HEX_CHARS,
ALL_BYTE_VALUES,
)
-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.compat import join_unicode, unicode_or_bytes
from passlib.utils.decor import classproperty, deprecated_method
# local
__all__ = [
@@ -113,19 +110,19 @@ def extract_settings_kwds(handler, kwds):
#=============================================================================
# parsing helpers
#=============================================================================
-_UDOLLAR = u("$")
-_UZERO = u("0")
+_UDOLLAR = u"$"
+_UZERO = u"0"
def validate_secret(secret):
"""ensure secret has correct type & size"""
- if not isinstance(secret, unicode_or_bytes_types):
+ if not isinstance(secret, unicode_or_bytes):
raise exc.ExpectedStringError(secret, "secret")
if len(secret) > MAX_PASSWORD_SIZE:
raise exc.PasswordSizeError(MAX_PASSWORD_SIZE)
def to_unicode_for_identify(hash):
"""convert hash to unicode for identify method"""
- if isinstance(hash, unicode):
+ if isinstance(hash, str):
return hash
elif isinstance(hash, bytes):
# try as utf-8, but if it fails, use foolproof latin-1,
@@ -144,8 +141,8 @@ def parse_mc2(hash, prefix, sep=_UDOLLAR, handler=None):
this expects a hash of the format :samp:`{prefix}{salt}[${checksum}]`,
such as md5_crypt, and parses it into salt / checksum portions.
- :arg hash: the hash to parse (bytes or unicode)
- :arg prefix: the identifying prefix (unicode)
+ :arg hash: the hash to parse (bytes or str)
+ :arg prefix: the identifying prefix (str)
:param sep: field separator (unicode, defaults to ``$``).
:param handler: handler class to pass to error constructors.
@@ -154,12 +151,12 @@ def parse_mc2(hash, prefix, sep=_UDOLLAR, handler=None):
"""
# detect prefix
hash = to_unicode(hash, "ascii", "hash")
- assert isinstance(prefix, unicode)
+ assert isinstance(prefix, str)
if not hash.startswith(prefix):
raise exc.InvalidHashError(handler)
# parse 2-part hash or 1-part config string
- assert isinstance(sep, unicode)
+ assert isinstance(sep, str)
parts = hash[len(prefix):].split(sep)
if len(parts) == 2:
salt, chk = parts
@@ -193,12 +190,12 @@ def parse_mc3(hash, prefix, sep=_UDOLLAR, rounds_base=10,
"""
# detect prefix
hash = to_unicode(hash, "ascii", "hash")
- assert isinstance(prefix, unicode)
+ assert isinstance(prefix, str)
if not hash.startswith(prefix):
raise exc.InvalidHashError(handler)
# parse 3-part hash or 2-part config string
- assert isinstance(sep, unicode)
+ assert isinstance(sep, str)
parts = hash[len(prefix):].split(sep)
if len(parts) == 3:
rounds, salt, chk = parts
@@ -266,7 +263,7 @@ def parse_int(source, base=10, default=None, param="value", handler=None):
#=============================================================================
# formatting helpers
#=============================================================================
-def render_mc2(ident, salt, checksum, sep=u("$")):
+def render_mc2(ident, salt, checksum, sep=u"$"):
"""format hash using 2-part modular crypt format; inverse of parse_mc2()
returns native string with format :samp:`{ident}{salt}[${checksum}]`,
@@ -284,9 +281,9 @@ def render_mc2(ident, salt, checksum, sep=u("$")):
parts = [ident, salt, sep, checksum]
else:
parts = [ident, salt]
- return uascii_to_str(join_unicode(parts))
+ return join_unicode(parts)
-def render_mc3(ident, rounds, salt, checksum, sep=u("$"), rounds_base=10):
+def render_mc3(ident, rounds, salt, checksum, sep=u"$", rounds_base=10):
"""format hash using 3-part modular crypt format; inverse of parse_mc3()
returns native string with format :samp:`{ident}[{rounds}$]{salt}[${checksum}]`,
@@ -303,17 +300,17 @@ def render_mc3(ident, rounds, salt, checksum, sep=u("$"), rounds_base=10):
config or hash (native str)
"""
if rounds is None:
- rounds = u('')
+ rounds = u''
elif rounds_base == 16:
- rounds = u("%x") % rounds
+ rounds = u"%x" % rounds
else:
assert rounds_base == 10
- rounds = unicode(rounds)
+ rounds = str(rounds)
if checksum:
parts = [ident, rounds, sep, salt, sep, checksum]
else:
parts = [ident, rounds, sep, salt]
- return uascii_to_str(join_unicode(parts))
+ return join_unicode(parts)
def mask_value(value, show=4, pct=0.125, char=u"*"):
@@ -336,12 +333,12 @@ def mask_value(value, show=4, pct=0.125, char=u"*"):
"""
if value is None:
return None
- if not isinstance(value, unicode):
+ if not isinstance(value, str):
if isinstance(value, bytes):
from passlib.utils.binary import ab64_encode
value = ab64_encode(value).decode("ascii")
else:
- value = unicode(value)
+ value = str(value)
size = len(value)
show = min(show, int(size * pct))
return value[:show] + char * (size - show)
@@ -372,7 +369,7 @@ def norm_integer(handler, value, min=1, max=None, # *
:returns: validated value
"""
# check type
- if not isinstance(value, int_types):
+ if not isinstance(value, int):
raise exc.ExpectedTypeError(value, "integer", param)
# check minimum
@@ -452,7 +449,7 @@ class TruncateMixin(MinimalHandler):
@classmethod
def using(cls, truncate_error=None, **kwds):
- subcls = super(TruncateMixin, cls).using(**kwds)
+ subcls = super().using(**kwds)
if truncate_error is not None:
truncate_error = as_bool(truncate_error, param="truncate_error")
if truncate_error is not None:
@@ -618,7 +615,7 @@ class GenericHandler(MinimalHandler):
#===================================================================
def __init__(self, checksum=None, use_defaults=False, **kwds):
self.use_defaults = use_defaults
- super(GenericHandler, self).__init__(**kwds)
+ super().__init__(**kwds)
if checksum is not None:
# XXX: do we need to set .relaxed for checksum coercion?
self.checksum = self._norm_checksum(checksum)
@@ -641,12 +638,12 @@ class GenericHandler(MinimalHandler):
if not isinstance(checksum, bytes):
raise exc.ExpectedTypeError(checksum, "bytes", "checksum")
- elif not isinstance(checksum, unicode):
+ elif not isinstance(checksum, str):
if isinstance(checksum, bytes) and relaxed:
warn("checksum should be unicode, not bytes", PasslibHashWarning)
checksum = checksum.decode("ascii")
else:
- raise exc.ExpectedTypeError(checksum, "unicode", "checksum")
+ raise exc.ExpectedTypeError(checksum, "str", "checksum")
# check size
cc = self.checksum_size
@@ -713,8 +710,7 @@ class GenericHandler(MinimalHandler):
:returns:
hash string with salt & digest included.
- should return native string type (ascii-bytes under python 2,
- unicode under python 3)
+ should return native str.
"""
raise NotImplementedError("%s must implement from_string()" % (self.__class__,))
@@ -752,7 +748,7 @@ class GenericHandler(MinimalHandler):
string, taking config from object state
calc checksum implementations may assume secret is always
- either unicode or bytes, checks are performed by verify/etc.
+ either str or bytes, checks are performed by verify/etc.
"""
raise NotImplementedError("%s must implement _calc_checksum()" %
(self.__class__,))
@@ -906,7 +902,7 @@ class GenericHandler(MinimalHandler):
def bitsize(cls, **kwds):
"""[experimental method] return info about bitsizes of hash"""
try:
- info = super(GenericHandler, cls).bitsize(**kwds)
+ info = super().bitsize(**kwds)
except AttributeError:
info = {}
cc = ALL_BYTE_VALUES if cls._checksum_is_bytes else cls.checksum_chars
@@ -940,7 +936,7 @@ class StaticHandler(GenericHandler):
setting_kwds = ()
# optional constant prefix subclasses can specify
- _hash_prefix = u("")
+ _hash_prefix = u""
@classmethod
def from_string(cls, hash, **context):
@@ -966,46 +962,7 @@ class StaticHandler(GenericHandler):
return hash
def to_string(self):
- return uascii_to_str(self._hash_prefix + self.checksum)
-
- # per-subclass: stores dynamically created subclass used by _calc_checksum() stub
- __cc_compat_hack = None
-
- def _calc_checksum(self, secret):
- """given secret; calcuate and return encoded checksum portion of hash
- string, taking config from object state
- """
- # NOTE: prior to 1.6, StaticHandler required classes implement genhash
- # instead of this method. so if we reach here, we try calling genhash.
- # if that succeeds, we issue deprecation warning. if it fails,
- # we'll just recurse back to here, but in a different instance.
- # so before we call genhash, we create a subclass which handles
- # throwing the NotImplementedError.
- cls = self.__class__
- assert cls.__module__ != __name__
- wrapper_cls = cls.__cc_compat_hack
- if wrapper_cls is None:
- def inner(self, secret):
- raise NotImplementedError("%s must implement _calc_checksum()" %
- (cls,))
- wrapper_cls = cls.__cc_compat_hack = type(cls.__name__ + "_wrapper",
- (cls,), dict(_calc_checksum=inner, __module__=cls.__module__))
- context = dict((k,getattr(self,k)) for k in self.context_kwds)
- # NOTE: passing 'config=None' here even though not currently allowed by ifc,
- # since it *is* allowed under the old 1.5 ifc we're checking for here.
- try:
- hash = wrapper_cls.genhash(secret, None, **context)
- except TypeError as err:
- if str(err) == "config must be string":
- raise NotImplementedError("%s must implement _calc_checksum()" %
- (cls,))
- else:
- raise
- warn("%r should be updated to implement StaticHandler._calc_checksum() "
- "instead of StaticHandler.genhash(), support for the latter "
- "style will be removed in Passlib 1.8" % cls,
- DeprecationWarning)
- return str_to_uascii(hash)
+ return self._hash_prefix + self.checksum
#=============================================================================
# GenericHandler mixin classes
@@ -1016,7 +973,7 @@ class HasEncodingContext(GenericHandler):
default_encoding = "utf-8"
def __init__(self, encoding=None, **kwds):
- super(HasEncodingContext, self).__init__(**kwds)
+ super().__init__(**kwds)
self.encoding = encoding or self.default_encoding
class HasUserContext(GenericHandler):
@@ -1024,7 +981,7 @@ class HasUserContext(GenericHandler):
context_kwds = ("user",)
def __init__(self, user=None, **kwds):
- super(HasUserContext, self).__init__(**kwds)
+ super().__init__(**kwds)
self.user = user
# XXX: would like to validate user input here, but calls to from_string()
@@ -1033,16 +990,16 @@ class HasUserContext(GenericHandler):
# wrap funcs to accept 'user' as positional arg for ease of use.
@classmethod
def hash(cls, secret, user=None, **context):
- return super(HasUserContext, cls).hash(secret, user=user, **context)
+ return super().hash(secret, user=user, **context)
@classmethod
def verify(cls, secret, hash, user=None, **context):
- return super(HasUserContext, cls).verify(secret, hash, user=user, **context)
+ return super().verify(secret, hash, user=user, **context)
@deprecated_method(deprecated="1.7", removed="2.0")
@classmethod
def genhash(cls, secret, config, user=None, **context):
- return super(HasUserContext, cls).genhash(secret, config, user=user, **context)
+ return super().genhash(secret, config, user=user, **context)
# XXX: how to guess the entropy of a username?
# most of these hashes are for a system (e.g. Oracle)
@@ -1051,7 +1008,7 @@ class HasUserContext(GenericHandler):
# need to find good reference about this.
##@classmethod
##def bitsize(cls, **kwds):
- ## info = super(HasUserContext, cls).bitsize(**kwds)
+ ## info = super().bitsize(**kwds)
## info['user'] = xxx
## return info
@@ -1097,7 +1054,7 @@ class HasManyIdents(GenericHandler):
#===================================================================
# class attrs
#===================================================================
- default_ident = None # should be unicode
+ default_ident = None # should be str
ident_values = None # should be list of unicode strings
ident_aliases = None # should be dict of unicode -> unicode
# NOTE: any aliases provided to norm_ident() as bytes
@@ -1134,7 +1091,7 @@ class HasManyIdents(GenericHandler):
default_ident = ident
# create subclass
- subcls = super(HasManyIdents, cls).using(**kwds)
+ subcls = super().using(**kwds)
# add custom default ident
# (NOTE: creates instance to run value through _norm_ident())
@@ -1146,7 +1103,7 @@ class HasManyIdents(GenericHandler):
# init
#===================================================================
def __init__(self, ident=None, **kwds):
- super(HasManyIdents, self).__init__(**kwds)
+ super().__init__(**kwds)
# init ident
if ident is not None:
@@ -1333,12 +1290,12 @@ class HasSalt(GenericHandler):
default_salt_size = salt_size
# generate new subclass
- subcls = super(HasSalt, cls).using(**kwds)
+ subcls = super().using(**kwds)
# replace default_rounds
relaxed = kwds.get("relaxed")
if default_salt_size is not None:
- if isinstance(default_salt_size, native_string_types):
+ if isinstance(default_salt_size, str):
default_salt_size = int(default_salt_size)
subcls.default_salt_size = subcls._clip_to_valid_salt_size(default_salt_size,
param="salt_size",
@@ -1406,7 +1363,7 @@ class HasSalt(GenericHandler):
# init
#===================================================================
def __init__(self, salt=None, **kwds):
- super(HasSalt, self).__init__(**kwds)
+ super().__init__(**kwds)
if salt is not None:
salt = self._parse_salt(salt)
elif self.use_defaults:
@@ -1446,12 +1403,12 @@ class HasSalt(GenericHandler):
if not isinstance(salt, bytes):
raise exc.ExpectedTypeError(salt, "bytes", "salt")
else:
- if not isinstance(salt, unicode):
+ if not isinstance(salt, str):
# NOTE: allowing bytes under py2 so salt can be native str.
- if isinstance(salt, bytes) and (PY2 or relaxed):
+ if relaxed and isinstance(salt, bytes):
salt = salt.decode("ascii")
else:
- raise exc.ExpectedTypeError(salt, "unicode", "salt")
+ raise exc.ExpectedTypeError(salt, "str", "salt")
# check charset
sc = cls.salt_chars
@@ -1495,7 +1452,7 @@ class HasSalt(GenericHandler):
@classmethod
def bitsize(cls, salt_size=None, **kwds):
"""[experimental method] return info about bitsizes of hash"""
- info = super(HasSalt, cls).bitsize(**kwds)
+ info = super().bitsize(**kwds)
if salt_size is None:
salt_size = cls.default_salt_size
# FIXME: this may overestimate size due to padding bits
@@ -1650,7 +1607,7 @@ class HasRounds(GenericHandler):
default_rounds = rounds
# generate new subclass
- subcls = super(HasRounds, cls).using(**kwds)
+ subcls = super().using(**kwds)
# replace min_desired_rounds
relaxed = kwds.get("relaxed")
@@ -1659,7 +1616,7 @@ class HasRounds(GenericHandler):
min_desired_rounds = cls.min_desired_rounds
else:
explicit_min_rounds = True
- if isinstance(min_desired_rounds, native_string_types):
+ if isinstance(min_desired_rounds, str):
min_desired_rounds = int(min_desired_rounds)
subcls.min_desired_rounds = subcls._norm_rounds(min_desired_rounds,
param="min_desired_rounds",
@@ -1669,7 +1626,7 @@ class HasRounds(GenericHandler):
if max_desired_rounds is None:
max_desired_rounds = cls.max_desired_rounds
else:
- if isinstance(max_desired_rounds, native_string_types):
+ if isinstance(max_desired_rounds, str):
max_desired_rounds = int(max_desired_rounds)
if min_desired_rounds and max_desired_rounds < min_desired_rounds:
msg = "%s: max_desired_rounds (%r) below min_desired_rounds (%r)" % \
@@ -1685,7 +1642,7 @@ class HasRounds(GenericHandler):
# replace default_rounds
if default_rounds is not None:
- if isinstance(default_rounds, native_string_types):
+ if isinstance(default_rounds, str):
default_rounds = int(default_rounds)
if min_desired_rounds and default_rounds < min_desired_rounds:
raise ValueError("%s: default_rounds (%r) below min_desired_rounds (%r)" %
@@ -1703,7 +1660,7 @@ class HasRounds(GenericHandler):
# replace / set vary_rounds
if vary_rounds is not None:
- if isinstance(vary_rounds, native_string_types):
+ if isinstance(vary_rounds, str):
if vary_rounds.endswith("%"):
vary_rounds = float(vary_rounds[:-1]) * 0.01
elif "." in vary_rounds:
@@ -1782,7 +1739,7 @@ class HasRounds(GenericHandler):
vary_rounds = int(default_rounds * vary_rounds)
# calculate bounds based on default_rounds +/- vary_rounds
- assert vary_rounds >= 0 and isinstance(vary_rounds, int_types)
+ assert vary_rounds >= 0 and isinstance(vary_rounds, int)
lower = linear_to_native(default_rounds - vary_rounds, False)
upper = linear_to_native(default_rounds + vary_rounds, True)
return cls._clip_to_desired_rounds(lower), cls._clip_to_desired_rounds(upper)
@@ -1791,7 +1748,7 @@ class HasRounds(GenericHandler):
# init
#===================================================================
def __init__(self, rounds=None, **kwds):
- super(HasRounds, self).__init__(**kwds)
+ super().__init__(**kwds)
if rounds is not None:
rounds = self._parse_rounds(rounds)
elif self.use_defaults:
@@ -1873,7 +1830,7 @@ class HasRounds(GenericHandler):
max_desired_rounds = self.max_desired_rounds
if max_desired_rounds and self.rounds > max_desired_rounds:
return True
- return super(HasRounds, self)._calc_needs_update(**kwds)
+ return super()._calc_needs_update(**kwds)
#===================================================================
# experimental methods
@@ -1881,7 +1838,7 @@ class HasRounds(GenericHandler):
@classmethod
def bitsize(cls, rounds=None, vary_rounds=.1, **kwds):
"""[experimental method] return info about bitsizes of hash"""
- info = super(HasRounds, cls).bitsize(**kwds)
+ info = super().bitsize(**kwds)
# NOTE: this essentially estimates how many bits of "salt"
# can be added by varying the rounds value just a little bit.
if cls.rounds_cost != "log2":
@@ -1930,9 +1887,9 @@ class ParallelismMixin(GenericHandler):
@classmethod
def using(cls, parallelism=None, **kwds):
- subcls = super(ParallelismMixin, cls).using(**kwds)
+ subcls = super().using(**kwds)
if parallelism is not None:
- if isinstance(parallelism, native_string_types):
+ if isinstance(parallelism, str):
parallelism = int(parallelism)
subcls.parallelism = subcls._norm_parallelism(parallelism, relaxed=kwds.get("relaxed"))
return subcls
@@ -1941,7 +1898,7 @@ class ParallelismMixin(GenericHandler):
# init
#===================================================================
def __init__(self, parallelism=None, **kwds):
- super(ParallelismMixin, self).__init__(**kwds)
+ super().__init__(**kwds)
# init parallelism
if parallelism is None:
@@ -1965,7 +1922,7 @@ class ParallelismMixin(GenericHandler):
# XXX: for now, marking all hashes which don't have matching parallelism setting
if self.parallelism != type(self).parallelism:
return True
- return super(ParallelismMixin, self)._calc_needs_update(**kwds)
+ return super()._calc_needs_update(**kwds)
#===================================================================
# eoc
@@ -2308,7 +2265,7 @@ class SubclassBackendMixin(BackendMixin):
@classmethod
def _set_backend(cls, name, dryrun):
# invoke backend loader (will throw error if fails)
- super(SubclassBackendMixin, cls)._set_backend(name, dryrun)
+ super()._set_backend(name, dryrun)
# sanity check call args (should trust .set_backend, but will really
# foul things up if this isn't the owner)
@@ -2486,7 +2443,7 @@ class PrefixWrapper(object):
#: list of attributes which should be cloned by .using()
_using_clone_attrs = ()
- def __init__(self, name, wrapped, prefix=u(''), orig_prefix=u(''), lazy=False,
+ def __init__(self, name, wrapped, prefix=u'', orig_prefix=u'', lazy=False,
doc=None, ident=None):
self.name = name
if isinstance(prefix, bytes):
@@ -2652,7 +2609,7 @@ class PrefixWrapper(object):
if not hash.startswith(orig_prefix):
raise exc.InvalidHashError(self.wrapped)
wrapped = self.prefix + hash[len(orig_prefix):]
- return uascii_to_str(wrapped)
+ return wrapped
#: set by _using(), helper for test harness' handler_derived_from()
_derived_from = None
diff --git a/passlib/utils/pbkdf2.py b/passlib/utils/pbkdf2.py
index 273143b..d9bd083 100644
--- a/passlib/utils/pbkdf2.py
+++ b/passlib/utils/pbkdf2.py
@@ -6,20 +6,14 @@ maybe rename to "kdf" since it's getting more key derivation functions added.
#=============================================================================
# imports
#=============================================================================
-from __future__ import division
# core
import logging; log = logging.getLogger(__name__)
# site
# pkg
from passlib.exc import ExpectedTypeError
-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
+from passlib.crypto.digest import lookup_hash, pbkdf1 as _pbkdf1, pbkdf2_hmac, compile_hmac
# local
__all__ = [
- # hash utils
- "norm_hash_name",
-
# prf utils
"get_prf",
@@ -38,13 +32,6 @@ warn("the module 'passlib.utils.pbkdf2' is deprecated as of Passlib 1.7, "
DeprecationWarning)
#=============================================================================
-# hash helpers
-#=============================================================================
-
-norm_hash_name = deprecated_function(deprecated="1.7", removed="1.8", func_module=__name__,
- replacement="passlib.crypto.digest.norm_hash_name")(norm_hash_name)
-
-#=============================================================================
# prf lookup
#=============================================================================
@@ -97,7 +84,7 @@ def get_prf(name):
global _prf_cache
if name in _prf_cache:
return _prf_cache[name]
- if isinstance(name, native_string_types):
+ if isinstance(name, str):
if not name.startswith(_HMAC_PREFIXES):
raise ValueError("unknown prf algorithm: %r" % (name,))
digest = lookup_hash(name[5:]).name
@@ -183,7 +170,7 @@ def pbkdf2(secret, salt, rounds, keylen=None, prf="hmac-sha1"):
This has been deprecated in favor of :func:`passlib.crypto.digest.pbkdf2_hmac`,
and will be removed in Passlib 2.0. *Note the call signature has changed.*
"""
- if callable(prf) or (isinstance(prf, native_string_types) and not prf.startswith(_HMAC_PREFIXES)):
+ if callable(prf) or (isinstance(prf, str) and not prf.startswith(_HMAC_PREFIXES)):
raise NotImplementedError("non-HMAC prfs are not supported as of Passlib 1.7")
digest = prf[5:]
return pbkdf2_hmac(digest, secret, salt, rounds, keylen)
diff --git a/passlib/win32.py b/passlib/win32.py
deleted file mode 100644
index 223dd6c..0000000
--- a/passlib/win32.py
+++ /dev/null
@@ -1,68 +0,0 @@
-"""passlib.win32 - MS Windows support - DEPRECATED, WILL BE REMOVED IN 1.8
-
-the LMHASH and NTHASH algorithms are used in various windows related contexts,
-but generally not in a manner compatible with how passlib is structured.
-
-in particular, they have no identifying marks, both being
-32 bytes of binary data. thus, they can't be easily identified
-in a context with other hashes, so a CryptHandler hasn't been defined for them.
-
-this module provided two functions to aid in any use-cases which exist.
-
-.. warning::
-
- these functions should not be used for new code unless an existing
- system requires them, they are both known broken,
- and are beyond insecure on their own.
-
-.. autofunction:: raw_lmhash
-.. autofunction:: raw_nthash
-
-See also :mod:`passlib.hash.nthash`.
-"""
-
-from warnings import warn
-warn("the 'passlib.win32' module is deprecated, and will be removed in "
- "passlib 1.8; please use the 'passlib.hash.nthash' and "
- "'passlib.hash.lmhash' classes instead.",
- DeprecationWarning)
-
-#=============================================================================
-# imports
-#=============================================================================
-# core
-from binascii import hexlify
-# site
-# pkg
-from passlib.utils.compat import unicode
-from passlib.crypto.des import des_encrypt_block
-from passlib.hash import nthash
-# local
-__all__ = [
- "nthash",
- "raw_lmhash",
- "raw_nthash",
-]
-#=============================================================================
-# helpers
-#=============================================================================
-LM_MAGIC = b"KGS!@#$%"
-
-raw_nthash = nthash.raw_nthash
-
-def raw_lmhash(secret, encoding="ascii", hex=False):
- """encode password using des-based LMHASH algorithm; returns string of raw bytes, or unicode hex"""
- # NOTE: various references say LMHASH uses the OEM codepage of the host
- # for its encoding. until a clear reference is found,
- # as well as a path for getting the encoding,
- # letting this default to "ascii" to prevent incorrect hashes
- # from being made w/o user explicitly choosing an encoding.
- if isinstance(secret, unicode):
- secret = secret.encode(encoding)
- ns = secret.upper()[:14] + b"\x00" * (14-len(secret))
- out = des_encrypt_block(ns[:7], LM_MAGIC) + des_encrypt_block(ns[7:], LM_MAGIC)
- return hexlify(out).decode("ascii") if hex else out
-
-#=============================================================================
-# eoc
-#=============================================================================
diff --git a/setup.py b/setup.py
index 5310d4f..75c6b8c 100644
--- a/setup.py
+++ b/setup.py
@@ -64,6 +64,9 @@ opts = dict(
],
},
+ # NOTE: 'python_requires' should be kept in sync w/ passlib.utils.compat's version check.
+ python_requires='>=3.5',
+
#==================================================================
# details
#==================================================================
@@ -71,7 +74,7 @@ opts = dict(
"comprehensive password hashing framework supporting over 30 schemes",
long_description="""\
-Passlib is a password hashing library for Python 2 & 3, which provides
+Passlib is a password hashing library for Python 3, which provides
cross-platform implementations of over 30 password hashing algorithms, as well
as a framework for managing existing password hashes. It's designed to be useful
for a wide range of tasks, from verifying a hash found in /etc/shadow, to
@@ -103,12 +106,7 @@ Intended Audience :: Developers
License :: OSI Approved :: BSD License
Natural Language :: English
Operating System :: OS Independent
-Programming Language :: Python :: 2
-Programming Language :: Python :: 2.6
-Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
-Programming Language :: Python :: 3.3
-Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
diff --git a/tox.ini b/tox.ini
index 43ef667..e148ea6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -47,7 +47,7 @@ envlist =
# TODO: would like to 'default-pyston' but doesnt quite work
# TODO: also add default-jython27
# NOTE: removed 2.6 & 3.3 as of 2019-11, tox+pip no longer work for these versions.
- default-py{27,34,35,36,37,38,39,py,py3},
+ default-py{35,36,37,38,39,py3},
# pbkdf2 backend testing
# NOTE: 'hashlib' takes priority under py34+
@@ -55,36 +55,30 @@ envlist =
# 'unpack' used for py2
## pdbkf2-fastpbkdf2-py{2,3}, # tested by default config
pbkdf2-hashlib-py{3,py3},
- pbkdf2-unpack-py{2,py},
pbkdf2-frombytes-py{3,py3},
# bcrypt backend testing (bcrypt cffi tested by default test)
- # NOTE: 'other' checks bcryptor & py-bcrypt
## bcrypthash-bcrypt-py{2,3,py,py3}, # tested by default config
- bcrypthash-other-py{2,3}
- bcrypthash-{builtin,disabled}-py{2,3,py,py3}
+ bcrypthash-{builtin,disabled}-py{3,py3}
# scrypt backend testing (builtin backend tested by default test)
# XXX: 'scrypt' not compatible w/ pypy, or would include this under default.
# could still do that for all but pypy, and do special test for builtin.
- scrypthash-scrypt-py{2,3},
+ scrypthash-scrypt-py3,
## scrypthash-stdlib-py{3}, # will only work for py36+ && openssl 1.1+
## scrypthash-builtin-py{2,3,py,py3}, # tested by default config
# argon2 backend testing (argon2_cffi tested by default test)
## argon2hash-argon2cffi-py{2,3,py,py3} # tested by default config
- argon2hash-argon2pure-py{2,3,py,py3},
+ argon2hash-argon2pure-py{3,py3},
# django tests
# NOTE: django distributes it'a tests as part of source, not the package, so for full
# integration tests to run, caller must provide a copy of the latest django source,
# and set the env var PASSLIB_TESTS_DJANGO_SOURCE_PATH to point to it.
- # NOTE: django 2.0 dropped python 2 support, so not including that in matrix.
# django support roadmap -- https://www.djangoproject.com/download/
# django python versions -- https://docs.djangoproject.com/en/3.1/faq/install/#what-python-version-can-i-use-with-django
- django-dj{1x,18}-wdeps-py{2,3},
- django-dj{Latest,31,30,22,21,20}-wdeps-py3,
- django-dj{1x}-nodeps-py2,
+ django-dj{Latest,31,30,22,21,20,1x,18}-wdeps-py3,
django-dj{Latest}-nodeps-py3,
# other tests
@@ -95,24 +89,15 @@ envlist =
#===========================================================================
[testenv]
basepython =
- py2: python2
- py26: python2.6
- py27: python2.7
-
py3: python3
- py33: python3.3
- py34: python3.4
py35: python3.5
py36: python3.6
py37: python3.7
py38: python3.8
py39: python3.9
- pypy: pypy
pypy3: pypy3
- jython27: jython2.7
-
passenv =
PASSLIB_TEST_MODE
PASSLIB_TESTS_DJANGO_SOURCE_PATH
@@ -126,7 +111,7 @@ setenv =
bcrypthash-disabled: PASSLIB_TEST_MODE = quick
# option that depends on rednose (see below)
- !py33-!py34: HIDE_SKIPS = --hide-skips
+ HIDE_SKIPS = --hide-skips
# nose option fragments
with_coverage: TEST_COVER_OPTS = --with-xunit --with-coverage --cover-xml --cover-package passlib
@@ -151,10 +136,9 @@ commands =
deps =
# common
nose
- !py33-!py34: rednose
+ rednose
coverage
randomize
- unittest2
# totp helper tests
# NOTE: cryptography requires python-dev, libffi-dev, libssl-dev
@@ -172,8 +156,6 @@ deps =
# NOTE: bcrypt10 env disabled, just used to check legacy issues
## bcrypthash-bcrypt10: bcrypt<1.1
default,bcrypthash-bcrypt: bcrypt
- bcrypthash-other-py{2,26,27}: bcryptor
- bcrypthash-other: py-bcrypt
# scrypt backend tests
# XXX: would test 'scrypt' under default, but not compatible w/ pypy,
@@ -200,6 +182,7 @@ deps =
# django-nodeps -- would like to use this as negative dependancy for 'bcrypt' instead
# needed by django's internal tests
+ # XXX: does django still need this as of py35?
django: mock
#===========================================================================