summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2019-11-22 17:04:54 -0500
committerEli Collins <elic@assurancetechnologies.com>2019-11-22 17:04:54 -0500
commit482dba920f91c1e7f3906930a56126aec42caa7c (patch)
tree806c3d7b4dc65d1229ab376b6b0488438fd69d4b
parentfc81bf7969129891c36496b5e2392c290b0f9b3c (diff)
parentc7c4ca4ece10ac0e1090bfa6a3bc41baabcf5ce7 (diff)
downloadpasslib-482dba920f91c1e7f3906930a56126aec42caa7c.tar.gz
Merge from stable
-rw-r--r--LICENSE2
-rw-r--r--docs/conf.py12
-rw-r--r--docs/history/1.7.rst28
-rw-r--r--docs/install.rst25
-rw-r--r--docs/lib/passlib.hash.bcrypt.rst12
-rw-r--r--docs/lib/passlib.pwd.rst4
-rw-r--r--docs/requirements.txt5
-rw-r--r--passlib/handlers/argon2.py5
-rw-r--r--passlib/handlers/bcrypt.py11
-rw-r--r--passlib/tests/test_context.py2
-rw-r--r--passlib/tests/test_ext_django.py5
-rw-r--r--passlib/tests/test_handlers_django.py21
-rw-r--r--passlib/tests/utils.py16
-rw-r--r--passlib/utils/__init__.py4
-rw-r--r--passlib/utils/compat/__init__.py1
-rw-r--r--passlib/utils/handlers.py49
-rw-r--r--setup.py15
-rw-r--r--tox.ini22
18 files changed, 176 insertions, 63 deletions
diff --git a/LICENSE b/LICENSE
index ba416e5..00170b5 100644
--- a/LICENSE
+++ b/LICENSE
@@ -17,7 +17,7 @@ Passlib is (c) `Assurance Technologies <http://www.assurancetechnologies.com>`_,
and is released under the `BSD license <http://www.opensource.org/licenses/bsd-license.php>`_::
Passlib
- Copyright (c) 2008-2017 Assurance Technologies, LLC.
+ Copyright (c) 2008-2019 Assurance Technologies, LLC.
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/docs/conf.py b/docs/conf.py
index ab34062..7b61d34 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,7 +51,7 @@ import cloud_sptheme as csp
#=============================================================================
# If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.4'
+needs_sphinx = '1.6'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
@@ -82,6 +82,9 @@ extensions = [
'cloud_sptheme.ext.autoattribute_search_bases',
'cloud_sptheme.ext.docfield_markup',
'cloud_sptheme.ext.escaped_samp_literals',
+
+ # silence "footnote not referenced" warning -- should maybe redo these in docs :)
+ 'cloud_sptheme.ext.allow_unreferenced_footnotes',
]
# Add any paths that contain templates here, relative to this directory.
@@ -159,6 +162,11 @@ modindex_common_prefix = ["passlib."]
# appended to all pages
rst_epilog = "\n.. |updated| replace:: %s\n" % updated
+# Intersphinx
+intersphinx_mapping = {
+ 'python': ('https://docs.python.org/3', None),
+}
+
#=============================================================================
# Options for all output
#=============================================================================
@@ -205,7 +213,7 @@ if csp.is_cloud_theme(html_theme):
)
# Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = [csp.get_theme_dir()]
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
diff --git a/docs/history/1.7.rst b/docs/history/1.7.rst
index c45c7f0..5a5cf12 100644
--- a/docs/history/1.7.rst
+++ b/docs/history/1.7.rst
@@ -2,8 +2,8 @@
Passlib 1.7
===========
-**1.7.2** (NOT YET RELEASED)
-============================
+**1.7.2** (2019-11-22)
+======================
This release rolls up assorted bug & compatibility fixes since 1.7.1.
@@ -44,6 +44,26 @@ Bugfixes
* **unittests**: ``crypt()`` unittests now account for linux systems running libxcrypt
(such as recent Fedora releases)
+Deprecations
+------------
+
+.. rst-class:: float-center
+
+.. warning::
+
+ 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.6 & 3.3; and Passlib 2.0 will drop
+ support for Python 2.x entirely.**
+
+* Support for Python 2.6 & 3.3 is deprecated; and will be dropped in Passlib 1.8.
+
+* .. py:currentmodule:: passlib.hash
+
+ :class:`bcrypt`: ``py-bcrypt`` and ``bcryptor`` backends are deprecated, and support
+ will be removed in Passlib 1.8. Please switch to the ``bcrypt`` backend.
+
Other Changes
-------------
@@ -54,7 +74,9 @@ Other Changes
:class:`argon2`: Now throws helpful error if "argon2" package is actually an incompatible
or supported version of argon2_cffi (:issue:`99`).
-* **documentation**: Various updates & corrections.
+* **documentation**: Various updates & corrections.
+ building the documentation now requires Sphinx 1.6 or newer.
+
**1.7.1** (2017-1-30)
=====================
diff --git a/docs/install.rst b/docs/install.rst
index 00cab65..43d25fc 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -9,12 +9,19 @@ Supported Platforms
Passlib requires Python 2 (>= 2.6) or Python 3 (>= 3.3).
It is known to work with the following Python implementations:
+.. rst-class:: float-right
+
+.. warning::
+
+ * Support for Python 2.6 and 3.3 will be dropped in Passlib 1.8
+
+ * Support for Python 2.x will be dropped in Passlib 2.0
+
* CPython 2 -- v2.6 or newer.
* CPython 3 -- v3.3 or newer.
* PyPy -- v2.0 or newer.
* PyPy3 -- v5.3 or newer.
* Jython -- v2.7 or newer.
-* Pyston -- v0.5.1 or newer.
Passlib should work with all operating systems and environments,
as it contains builtin fallbacks for almost all OS-dependant features.
@@ -33,6 +40,13 @@ Optional Libraries
`py-bcrypt <https://pypi.python.org/pypi/py-bcrypt>`_, or
`bcryptor <https://bitbucket.org/ares/bcryptor/overview>`_
+ .. rst-class:: float-right
+
+ .. 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,
@@ -138,9 +152,8 @@ online at `<https://passlib.readthedocs.io>`_.
If you wish to generate your own copy of the documentation,
you will need to:
-1. Install `Sphinx <http://sphinx.pocoo.org/>`_ (1.4 or newer)
-2. Install the `Cloud Sphinx Theme <http://packages.python.org/cloud_sptheme>`_ (1.9.2 or newer).
-3. Download the Passlib source
-4. From the Passlib source directory, run :samp:`python setup.py build_sphinx`.
-5. Once Sphinx completes its run, point a web browser to the file at :samp:`{SOURCE}/build/sphinx/html/index.html`
+1. Download the Passlib source, extract it, and ``cd`` into the source directory.
+2. Install all the dependencies required via ``pip install -e .[build_docs]``.
+3. Run :samp:`python setup.py build_sphinx`.
+4. Once Sphinx completes its run, point a web browser to the file at :samp:`{SOURCE}/build/sphinx/html/index.html`
to access the Passlib documentation in html format.
diff --git a/docs/lib/passlib.hash.bcrypt.rst b/docs/lib/passlib.hash.bcrypt.rst
index e116983..a8c4625 100644
--- a/docs/lib/passlib.hash.bcrypt.rst
+++ b/docs/lib/passlib.hash.bcrypt.rst
@@ -48,11 +48,19 @@ Interface
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.
-3. `bcryptor <https://bitbucket.org/ares/bcryptor/overview>`_, 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.pwd.rst b/docs/lib/passlib.pwd.rst
index bb36844..8b41bd8 100644
--- a/docs/lib/passlib.pwd.rst
+++ b/docs/lib/passlib.pwd.rst
@@ -47,6 +47,6 @@ Passlib does not currently offer any password strength estimation routines.
However, the (javascript-based) `zxcvbn <https://github.com/dropbox/zxcvbn>`_
project is a *very* good choice.
-Though there are a few different python ports of ZXCVBN library, as of 2019-3-4,
-`zxcvbn <https://pypi.python.org/pypi/zxcvbn>` is the most up-to-date,
+Though there are a few different python ports of ZXCVBN library, as of 2019-11-13,
+`zxcvbn (@ pypi) <https://pypi.python.org/pypi/zxcvbn>`_ is the most up-to-date,
and is endorsed by the upstream zxcvbn developers.
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 2ce7120..5afb30c 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,2 @@
-# NOTE: switched to custom branch until https://github.com/dreamhost/sphinxcontrib-fulltoc/issues/10 is fixed
-## sphinxcontrib-fulltoc
-git+https://github.com/eli-collins/sphinxcontrib-fulltoc.git
-
+sphinxcontrib-fulltoc
hg+https://bitbucket.org/ecollins/cloud_sptheme
diff --git a/passlib/handlers/argon2.py b/passlib/handlers/argon2.py
index d4c33c7..7373fbc 100644
--- a/passlib/handlers/argon2.py
+++ b/passlib/handlers/argon2.py
@@ -28,7 +28,7 @@ _argon2pure = None # dynamically imported by _load_backend_argon2pure()
# pkg
from passlib import exc
from passlib.crypto.digest import MAX_UINT32
-from passlib.utils import classproperty, to_bytes
+from passlib.utils import classproperty, to_bytes, render_bytes
from passlib.utils.binary import b64s_encode, b64s_decode
from passlib.utils.compat import unicode, bascii_to_str, uascii_to_str, PY2
import passlib.utils.handlers as uh
@@ -753,7 +753,8 @@ class _CffiBackend(_Argon2Common):
raise cls._adapt_backend_error(err)
#: helper for verify() method below -- maps prefixes to type constants
- _byte_ident_map = {b"$argon2%s$" % type.encode("ascii"): type for type in ALL_TYPES}
+ _byte_ident_map = dict((render_bytes(b"$argon2%s$", type.encode("ascii")), type)
+ for type in ALL_TYPES)
@classmethod
def verify(cls, secret, hash):
diff --git a/passlib/handlers/bcrypt.py b/passlib/handlers/bcrypt.py
index 2fdcad1..a60b074 100644
--- a/passlib/handlers/bcrypt.py
+++ b/passlib/handlers/bcrypt.py
@@ -616,6 +616,12 @@ class _BcryptorBackend(_BcryptCommon):
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):
@@ -653,6 +659,11 @@ class _PyBcryptBackend(_BcryptCommon):
# 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__
diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py
index 68603ba..2421ef4 100644
--- a/passlib/tests/test_context.py
+++ b/passlib/tests/test_context.py
@@ -299,7 +299,7 @@ sha512_crypt__min_rounds = 45000
def test_09_repr(self):
"""test repr()"""
cc1 = CryptContext(**self.sample_1_dict)
- # NOTE: "0x-1234" format used by Pyston 0.5.1
+ # NOTE: "0x-1234" format used by Pyston 0.5.1 (support deprecated 2019-11)
self.assertRegex(repr(cc1), "^<CryptContext at 0x-?[0-9a-f]+>$")
#===================================================================
diff --git a/passlib/tests/test_ext_django.py b/passlib/tests/test_ext_django.py
index b884330..e0fa51a 100644
--- a/passlib/tests/test_ext_django.py
+++ b/passlib/tests/test_ext_django.py
@@ -118,7 +118,10 @@ def create_mock_setter():
# build config dict that matches stock django
# XXX: move these to passlib.apps?
-if DJANGO_VERSION >= (1, 10):
+if DJANGO_VERSION >= (1, 11):
+ stock_config = _apps.django110_context.to_dict()
+ stock_rounds = 36000
+elif DJANGO_VERSION >= (1, 10):
stock_config = _apps.django110_context.to_dict()
stock_rounds = 30000
elif DJANGO_VERSION >= (1, 9):
diff --git a/passlib/tests/test_handlers_django.py b/passlib/tests/test_handlers_django.py
index bbb4496..8159a24 100644
--- a/passlib/tests/test_handlers_django.py
+++ b/passlib/tests/test_handlers_django.py
@@ -32,6 +32,10 @@ class _DjangoHelper(TestCase):
min_django_version = MIN_DJANGO_VERSION
#: max django version where hash alg is present
+ #: TODO: for a bunch of the tests below, this is just max version where
+ #: settings.PASSWORD_HASHERS includes it by default -- could add helper to patch
+ #: desired django hasher back in for duration of test.
+ #: XXX: change this to "disabled_in_django_version" instead?
max_django_version = None
def _require_django_support(self):
@@ -56,10 +60,7 @@ class _DjangoHelper(TestCase):
"""django/check_password"""
if self.handler.name == "django_bcrypt" and hash.startswith("bcrypt$$2y$"):
hash = hash.replace("$$2y$", "$$2a$")
- if self.django_has_encoding_glitch and isinstance(secret, bytes):
- # e.g. unsalted_md5 on 1.5 and higher try to combine
- # salt + password before encoding to bytes, leading to ascii error.
- # this works around that issue.
+ if isinstance(secret, bytes):
secret = secret.decode("utf-8")
return check_password(secret, hash)
return verify_django
@@ -79,8 +80,6 @@ class _DjangoHelper(TestCase):
"mangled secret=%r hash=%r incorrect verified" %
(secret, hash))
- django_has_encoding_glitch = False
-
def test_91_django_generation(self):
"""test against output of Django's make_password()"""
self._require_django_support()
@@ -95,9 +94,7 @@ class _DjangoHelper(TestCase):
secret, other = generator.random_password_pair()
if not secret: # django rejects empty passwords.
continue
- if self.django_has_encoding_glitch and isinstance(secret, bytes):
- # e.g. unsalted_md5 tried to combine salt + password before encoding to bytes,
- # leading to ascii error. this works around that issue.
+ if isinstance(secret, bytes):
secret = secret.decode("utf-8")
hash = make_password(secret, hasher=name)
self.assertTrue(self.do_identify(hash))
@@ -167,8 +164,6 @@ class django_salted_md5_test(HandlerCase, _DjangoHelper):
handler = hash.django_salted_md5
max_django_version = (1,9)
- django_has_encoding_glitch = True
-
known_correct_hashes = [
# test extra large salt
("password", 'md5$123abcdef$c8272612932975ee80e8a35995708e80'),
@@ -209,8 +204,6 @@ class django_salted_sha1_test(HandlerCase, _DjangoHelper):
handler = hash.django_salted_sha1
max_django_version = (1,9)
- django_has_encoding_glitch = True
-
known_correct_hashes = [
# test extra large salt
("password",'sha1$123abcdef$e4a1877b0e35c47329e7ed7e58014276168a37ba'),
@@ -270,6 +263,8 @@ class django_pbkdf2_sha1_test(HandlerCase, _DjangoHelper):
class django_bcrypt_test(HandlerCase, _DjangoHelper):
"""test django_bcrypt"""
handler = hash.django_bcrypt
+ # XXX: not sure when this wasn't in default list anymore. somewhere in [2.0 - 2.2]
+ max_django_version = (2, 0)
fuzz_salts_need_bcrypt_repair = True
known_correct_hashes = [
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index 78c765d..1ba7449 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -2529,10 +2529,24 @@ class HandlerCase(TestCase):
for key in ("salt", "checksum"):
if key in result3:
self.assertNotEqual(result3[key], correct3[key])
- self.assertTrue(result3[key].endswith("**"), "%r is masked" % result3[key])
+ self.assert_is_masked(result3[key])
correct3[key] = result3[key]
self.assertEqual(result3, correct3)
+ def assert_is_masked(self, value):
+ """
+ check value properly masked by :func:`passlib.utils.mask_value`
+ """
+ if value is None:
+ return
+ self.assertIsInstance(value, unicode)
+ # 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:]
+ if set(ref) == set(["*"]):
+ return True
+ raise self.fail("value not masked: %r" % value)
+
def test_71_parsehash_results(self):
"""
parsehash() -- known outputs
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index 2fc0e95..3f85389 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -538,6 +538,10 @@ def render_bytes(source, *args):
Calling ``render_bytes(source, *args)`` should function roughly the same as
``source % args`` under Python 2.
+
+ .. todo::
+ python >= 3.5 added back limited support for bytes %,
+ can revisit when 3.3/3.4 is dropped.
"""
if isinstance(source, bytes):
source = source.decode("latin-1")
diff --git a/passlib/utils/compat/__init__.py b/passlib/utils/compat/__init__.py
index 21415c4..2cc1e5b 100644
--- a/passlib/utils/compat/__init__.py
+++ b/passlib/utils/compat/__init__.py
@@ -27,6 +27,7 @@ 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
#=============================================================================
diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py
index 50440de..e53a99f 100644
--- a/passlib/utils/handlers.py
+++ b/passlib/utils/handlers.py
@@ -315,6 +315,38 @@ def render_mc3(ident, rounds, salt, checksum, sep=u"$", rounds_base=10):
parts = [ident, rounds, sep, salt]
return uascii_to_str(join_unicode(parts))
+
+def mask_value(value, show=4, pct=0.125, char=u"*"):
+ """
+ helper to mask contents of sensitive field.
+
+ :param value:
+ raw value (str, bytes, etc)
+
+ :param show:
+ max # of characters to remain visible
+
+ :param pct:
+ don't show more than this % of input.
+
+ :param char:
+ character to use for masking
+
+ :rtype: str | None
+ """
+ if value is None:
+ return None
+ if not isinstance(value, unicode):
+ if isinstance(value, bytes):
+ from passlib.utils.binary import ab64_encode
+ value = ab64_encode(value).decode("ascii")
+ else:
+ value = unicode(value)
+ size = len(value)
+ show = min(show, int(size * pct))
+ return value[:show] + char * (size - show)
+
+
#=============================================================================
# parameter helpers
#=============================================================================
@@ -831,21 +863,6 @@ class GenericHandler(MinimalHandler):
"""
return tuple(key for key in cls.setting_kwds if key not in cls._unparsed_settings)
- # XXX: make this a global function?
- @staticmethod
- def _sanitize(value, char=u"*"):
- """default method to obscure sensitive fields"""
- if value is None:
- return None
- if isinstance(value, bytes):
- from passlib.utils.binary import ab64_encode
- value = ab64_encode(value).decode("ascii")
- elif not isinstance(value, unicode):
- value = unicode(value)
- size = len(value)
- clip = min(4, size//8)
- return value[:clip] + char * (size-clip)
-
@classmethod
def parsehash(cls, hash, checksum=True, sanitize=False):
"""[experimental method] parse hash into dictionary of settings.
@@ -880,7 +897,7 @@ class GenericHandler(MinimalHandler):
kwds['checksum'] = self.checksum
if sanitize:
if sanitize is True:
- sanitize = cls._sanitize
+ sanitize = mask_value
for key in cls._unsafe_settings:
if key in kwds:
kwds[key] = sanitize(kwds[key])
diff --git a/setup.py b/setup.py
index 1d6d421..f715240 100644
--- a/setup.py
+++ b/setup.py
@@ -46,9 +46,22 @@ opts = dict(
# NOTE: 'download_url' set below
extras_require={
+ # extras w/ recommended library for argon2 backend
"argon2": "argon2_cffi>=18.2.0",
+
+ # extras w/ recommended library for bcrypt backend (if not present on host)
"bcrypt": "bcrypt>=3.1.0",
+
+ # extras to make (full) use of "passlib.totp" module
"totp": "cryptography",
+
+ # extras required to build passlib docs
+ # TODO: automate way to sync this w/ "./docs/requirements.txt"
+ "build_docs": [
+ "sphinx>=1.6",
+ "sphinxcontrib-fulltoc>=1.2.0",
+ "cloud_sptheme>=1.10.0",
+ ],
},
#==================================================================
@@ -98,6 +111,8 @@ 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
+Programming Language :: Python :: 3.8
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: Jython
Programming Language :: Python :: Implementation :: PyPy
diff --git a/tox.ini b/tox.ini
index aaef4aa..8a99d6b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -46,7 +46,8 @@ envlist =
# default tests
# TODO: would like to 'default-pyston' but doesnt quite work
# TODO: also add default-jython27
- default-py{26,27,33,34,35,36,py,py3},
+ # 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,py,py3},
# pbkdf2 backend testing
# NOTE: 'hashlib' takes priority under py34+
@@ -54,8 +55,8 @@ envlist =
# 'unpack' used for py2
## pdbkf2-fastpbkdf2-py{2,3}, # tested by default config
pbkdf2-hashlib-py{3,py3},
- pbkdf2-unpack-py{26,27,py},
- pbkdf2-frombytes-py{33,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
@@ -67,6 +68,7 @@ envlist =
# 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-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)
@@ -77,9 +79,8 @@ envlist =
# NOTE: django >= 1.7 distributes 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.
- django18-wdeps-py{2,3},
- django-wdeps-py{2,3},
- django-nodeps-py{2,3},
+ django{18,1x}-wdeps-py{2,3},
+ django-{wdeps,nodeps}-py{2,3},
# other tests
gae-py27
@@ -99,6 +100,8 @@ basepython =
py34: python3.4
py35: python3.5
py36: python3.6
+ py37: python3.7
+ py38: python3.8
pypy: pypy
pypy3: pypy3
@@ -135,7 +138,7 @@ commands =
argon2hash: nosetests {posargs:{env:TEST_OPTS} passlib.tests.test_handlers_argon2}
# django tests
- django{,18}: nosetests {posargs:{env:TEST_OPTS} passlib.tests.test_ext_django passlib.tests.test_handlers_django}
+ django{,1x,18}: nosetests {posargs:{env:TEST_OPTS} passlib.tests.test_ext_django passlib.tests.test_handlers_django}
deps =
# common
@@ -178,12 +181,13 @@ deps =
# django extension tests
django18: django>=1.8,<1.9
+ django1x: django<2
django: django
- django{,18}-deps: bcrypt
+ django{,1x,18}-wdeps: bcrypt
# django{,18}-nodeps -- would like to use this as negative dependancy for 'bcrypt' instead
# needed by django's internal tests
- django{,18}: mock
+ django{,1x,18}: mock
#===========================================================================
# Google App Engine integration