diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2019-11-22 17:04:54 -0500 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2019-11-22 17:04:54 -0500 |
| commit | 482dba920f91c1e7f3906930a56126aec42caa7c (patch) | |
| tree | 806c3d7b4dc65d1229ab376b6b0488438fd69d4b | |
| parent | fc81bf7969129891c36496b5e2392c290b0f9b3c (diff) | |
| parent | c7c4ca4ece10ac0e1090bfa6a3bc41baabcf5ce7 (diff) | |
| download | passlib-482dba920f91c1e7f3906930a56126aec42caa7c.tar.gz | |
Merge from stable
| -rw-r--r-- | LICENSE | 2 | ||||
| -rw-r--r-- | docs/conf.py | 12 | ||||
| -rw-r--r-- | docs/history/1.7.rst | 28 | ||||
| -rw-r--r-- | docs/install.rst | 25 | ||||
| -rw-r--r-- | docs/lib/passlib.hash.bcrypt.rst | 12 | ||||
| -rw-r--r-- | docs/lib/passlib.pwd.rst | 4 | ||||
| -rw-r--r-- | docs/requirements.txt | 5 | ||||
| -rw-r--r-- | passlib/handlers/argon2.py | 5 | ||||
| -rw-r--r-- | passlib/handlers/bcrypt.py | 11 | ||||
| -rw-r--r-- | passlib/tests/test_context.py | 2 | ||||
| -rw-r--r-- | passlib/tests/test_ext_django.py | 5 | ||||
| -rw-r--r-- | passlib/tests/test_handlers_django.py | 21 | ||||
| -rw-r--r-- | passlib/tests/utils.py | 16 | ||||
| -rw-r--r-- | passlib/utils/__init__.py | 4 | ||||
| -rw-r--r-- | passlib/utils/compat/__init__.py | 1 | ||||
| -rw-r--r-- | passlib/utils/handlers.py | 49 | ||||
| -rw-r--r-- | setup.py | 15 | ||||
| -rw-r--r-- | tox.ini | 22 |
18 files changed, 176 insertions, 63 deletions
@@ -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]) @@ -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 @@ -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 |
