diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2020-10-06 14:13:33 -0400 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2020-10-06 14:13:33 -0400 |
commit | 65f4a25efd41639552df52dba40bfc377ae00871 (patch) | |
tree | 363bf55e05d7484d8b407e58fa2526719138b201 | |
parent | cc1c32da8866bdcc65bee916cd202f660b0cd88e (diff) | |
download | passlib-65f4a25efd41639552df52dba40bfc377ae00871.tar.gz |
cleanup old python compat -- removed PY2 & PY3 conditionals
-rw-r--r-- | admin/bench_pbkdf2.py | 4 | ||||
-rw-r--r-- | passlib/apache.py | 15 | ||||
-rw-r--r-- | passlib/context.py | 49 | ||||
-rw-r--r-- | passlib/crypto/_md4.py | 8 | ||||
-rw-r--r-- | passlib/crypto/digest.py | 4 | ||||
-rw-r--r-- | passlib/handlers/argon2.py | 7 | ||||
-rw-r--r-- | passlib/handlers/bcrypt.py | 4 | ||||
-rw-r--r-- | passlib/pwd.py | 6 | ||||
-rw-r--r-- | passlib/tests/test_context.py | 21 | ||||
-rw-r--r-- | passlib/tests/test_crypto_builtin_md4.py | 16 | ||||
-rw-r--r-- | passlib/tests/test_crypto_digest.py | 9 | ||||
-rw-r--r-- | passlib/tests/test_handlers.py | 10 | ||||
-rw-r--r-- | passlib/tests/test_handlers_bcrypt.py | 2 | ||||
-rw-r--r-- | passlib/tests/test_utils.py | 51 | ||||
-rw-r--r-- | passlib/tests/utils.py | 28 | ||||
-rw-r--r-- | passlib/totp.py | 7 | ||||
-rw-r--r-- | passlib/utils/__init__.py | 112 | ||||
-rw-r--r-- | passlib/utils/binary.py | 20 | ||||
-rw-r--r-- | passlib/utils/compat/__init__.py | 114 | ||||
-rw-r--r-- | passlib/utils/decor.py | 13 | ||||
-rw-r--r-- | passlib/utils/handlers.py | 4 |
21 files changed, 127 insertions, 377 deletions
diff --git a/admin/bench_pbkdf2.py b/admin/bench_pbkdf2.py index 4492f82..e04f46d 100644 --- a/admin/bench_pbkdf2.py +++ b/admin/bench_pbkdf2.py @@ -23,7 +23,6 @@ except ImportError: assert reload, "expected builtin reload()" # py2x # site # pkg -from passlib.utils.compat import PY3 # local #============================================================================= @@ -124,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/passlib/apache.py b/passlib/apache.py index 02c1c45..1507cf5 100644 --- a/passlib/apache.py +++ b/passlib/apache.py @@ -15,7 +15,7 @@ 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, unicode, BytesIO # local __all__ = [ 'HtpasswdFile', @@ -50,8 +50,8 @@ class _CommonFile(object): encoding = None # whether users() and other public methods should return unicode or bytes? - # (defaults to False under PY2, True under PY3) - return_unicode = None + # (defaults to True) + return_unicode = True # if bound to local file, these will be set. _path = None # local file path @@ -109,7 +109,7 @@ class _CommonFile(object): # XXX: add a new() classmethod, ala TOTP.new()? def __init__(self, path=None, new=False, autosave=False, - encoding="utf-8", return_unicode=PY3, + encoding="utf-8", return_unicode=True, ): # set encoding if not encoding: @@ -752,7 +752,7 @@ 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) @@ -1058,8 +1058,7 @@ class HtdigestFile(_CommonFile): 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): @@ -1081,7 +1080,7 @@ 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) diff --git a/passlib/context.py b/passlib/context.py index abbd105..330a306 100644 --- a/passlib/context.py +++ b/passlib/context.py @@ -18,7 +18,7 @@ from passlib.utils import (handlers as uh, to_bytes, ) from passlib.utils.binary import BASE64_CHARS from passlib.utils.compat import (iteritems, num_types, irange, - PY2, PY3, unicode, SafeConfigParser, + unicode, SafeConfigParser, NativeStringIO, BytesIO, unicode_or_bytes_types, native_string_types, ) @@ -545,9 +545,6 @@ class _CryptConfig(object): # 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")) raise ExpectedTypeError(category, "str or None", "category") if scheme is not None and not isinstance(scheme, native_string_types): raise ExpectedTypeError(scheme, "str or None", "scheme") @@ -868,10 +865,7 @@ class CryptContext(object): # 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) @@ -882,15 +876,10 @@ class CryptContext(object): @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. + # NOTE: this expects a unicode stream, + # and resulting dict will 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) + p.read_file(stream, filename) # XXX: could change load() to accept list of items, # and skip intermediate dict creation return dict(p.items(section)) @@ -906,22 +895,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. @@ -989,11 +965,7 @@ class CryptContext(object): #----------------------------------------------------------- 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 = to_unicode(source, encoding, param="source") source = self._parse_ini_stream(NativeStringIO(source), section, "<string passed to CryptContext.load()>") elif isinstance(source, CryptContext): @@ -1400,10 +1372,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): diff --git a/passlib/crypto/_md4.py b/passlib/crypto/_md4.py index bdc211f..7d1a79c 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, irange # local __all__ = ["md4"] @@ -181,11 +181,7 @@ class md4(object): 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/digest.py b/passlib/crypto/digest.py index b0c6e26..d70f8d1 100644 --- a/passlib/crypto/digest.py +++ b/passlib/crypto/digest.py @@ -32,7 +32,7 @@ except ImportError: 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.compat import irange, int_types, unicode_or_bytes_types, error_from from passlib.utils.decor import memoized_property # local __all__ = [ @@ -870,7 +870,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): diff --git a/passlib/handlers/argon2.py b/passlib/handlers/argon2.py index d35d5dd..9bb4005 100644 --- a/passlib/handlers/argon2.py +++ b/passlib/handlers/argon2.py @@ -29,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 unicode, bascii_to_str, uascii_to_str, PY2 +from passlib.utils.compat import unicode, bascii_to_str, uascii_to_str import passlib.utils.handlers as uh # local __all__ = [ @@ -500,10 +500,7 @@ class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin, 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") + raise uh.exc.ExpectedTypeError(value, "str", "type") # check if type is valid if value in ALL_TYPES_SET: diff --git a/passlib/handlers/bcrypt.py b/passlib/handlers/bcrypt.py index 7283f78..d42b72b 100644 --- a/passlib/handlers/bcrypt.py +++ b/passlib/handlers/bcrypt.py @@ -30,7 +30,7 @@ from passlib.utils import safe_crypt, repeat_string, to_bytes, parse_version, \ 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 uascii_to_str, unicode, str_to_uascii, PY3, error_from +from passlib.utils.compat import uascii_to_str, unicode, str_to_uascii, error_from import passlib.utils.handlers as uh # local @@ -809,7 +809,7 @@ 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: diff --git a/passlib/pwd.py b/passlib/pwd.py index be22e91..599773d 100644 --- a/passlib/pwd.py +++ b/passlib/pwd.py @@ -17,7 +17,7 @@ import os # site # pkg from passlib import exc -from passlib.utils.compat import PY2, irange, itervalues, int_types +from passlib.utils.compat import irange, itervalues, int_types from passlib.utils import rng, getrandstr, to_unicode from passlib.utils.decor import memoized_property # local @@ -313,10 +313,6 @@ class SequenceGenerator(object): def __iter__(self): return self - if PY2: - def next(self): - return self.__next__() - #============================================================================= # eoc #============================================================================= diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py index 7e5996f..3459adc 100644 --- a/passlib/tests/test_context.py +++ b/passlib/tests/test_context.py @@ -3,11 +3,7 @@ # imports #============================================================================= # core -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__) @@ -19,7 +15,7 @@ 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, unicode, str_to_uascii, PY2 +from passlib.utils.compat import irange, unicode, str_to_uascii import passlib.utils.handlers as uh from passlib.tests.utils import (TestCase, set_file, TICK_RESOLUTION, quicksleep, time_call, handler_derived_from) @@ -747,11 +743,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""" @@ -928,14 +919,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') diff --git a/passlib/tests/test_crypto_builtin_md4.py b/passlib/tests/test_crypto_builtin_md4.py index 2ee76b5..0610cb9 100644 --- a/passlib/tests/test_crypto_builtin_md4.py +++ b/passlib/tests/test_crypto_builtin_md4.py @@ -8,7 +8,7 @@ import hashlib # site # pkg # module -from passlib.utils.compat import bascii_to_str, PY3 +from passlib.utils.compat import bascii_to_str from passlib.crypto.digest import lookup_hash from passlib.tests.utils import TestCase, skipUnless # local @@ -61,16 +61,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""" diff --git a/passlib/tests/test_crypto_digest.py b/passlib/tests/test_crypto_digest.py index 4b33323..5f0dd40 100644 --- a/passlib/tests/test_crypto_digest.py +++ b/passlib/tests/test_crypto_digest.py @@ -10,7 +10,7 @@ import warnings # pkg # module from passlib.exc import UnknownHashError -from passlib.utils.compat import PY3, JYTHON +from passlib.utils.compat import JYTHON from passlib.tests.utils import TestCase, TEST_MODE, skipUnless, hb #============================================================================= @@ -496,12 +496,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_handlers.py b/passlib/tests/test_handlers.py index cffa425..38836fa 100644 --- a/passlib/tests/test_handlers.py +++ b/passlib/tests/test_handlers.py @@ -11,7 +11,7 @@ import warnings # pkg from passlib import exc, hash from passlib.utils import repeat_string -from passlib.utils.compat import irange, PY3, get_method_function +from passlib.utils.compat import irange, get_method_function from passlib.tests.utils import TestCase, HandlerCase, skipUnless, \ TEST_MODE, UserHandlerMixin, EncodingHandlerMixin # module @@ -641,8 +641,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", @@ -1321,8 +1321,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), ] #============================================================================= diff --git a/passlib/tests/test_handlers_bcrypt.py b/passlib/tests/test_handlers_bcrypt.py index 156604d..55d5203 100644 --- a/passlib/tests/test_handlers_bcrypt.py +++ b/passlib/tests/test_handlers_bcrypt.py @@ -11,7 +11,7 @@ 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.utils.compat import irange from passlib.tests.utils import HandlerCase, TEST_MODE from passlib.tests.test_handlers import UPASS_TABLE # module diff --git a/passlib/tests/test_utils.py b/passlib/tests/test_utils.py index 83630b0..6790794 100644 --- a/passlib/tests/test_utils.py +++ b/passlib/tests/test_utils.py @@ -9,7 +9,7 @@ import warnings # pkg # module from passlib.utils import is_ascii_safe, to_bytes -from passlib.utils.compat import irange, PY2, PY3, unicode, join_bytes, PYPY +from passlib.utils.compat import irange, unicode, join_bytes, PYPY from passlib.tests.utils import TestCase, hb, run_with_fixed_seeds #============================================================================= @@ -80,10 +80,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 @@ -272,10 +268,9 @@ class MiscTest(TestCase): 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 [ @@ -568,19 +563,12 @@ 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()""" @@ -642,24 +630,17 @@ class CodecTest(TestCase): 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(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') @@ -743,8 +724,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 @@ -785,8 +765,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 @@ -850,7 +829,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 diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py index 9fb97f7..e55ce3e 100644 --- a/passlib/tests/utils.py +++ b/passlib/tests/utils.py @@ -17,7 +17,7 @@ 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 @@ -28,7 +28,7 @@ import passlib.registry as registry 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, unicode, PY2 +from passlib.utils.compat import iteritems, irange, unicode from passlib.utils.decor import classproperty import passlib.utils.handlers as uh # local @@ -230,15 +230,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): """ @@ -672,8 +667,7 @@ class TestCase(unittest.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 @@ -1470,9 +1464,8 @@ class HandlerCase(TestCase): if salt_type is not unicode: 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): @@ -2303,7 +2296,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: @@ -2575,11 +2568,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]) diff --git a/passlib/totp.py b/passlib/totp.py index 0462088..b636c51 100644 --- a/passlib/totp.py +++ b/passlib/totp.py @@ -2,7 +2,6 @@ #============================================================================= # imports #============================================================================= -from passlib.utils.compat import PY3 # core import base64 import calendar @@ -13,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: diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py index 0c950ba..e25d861 100644 --- a/passlib/utils/__init__.py +++ b/passlib/utils/__init__.py @@ -62,7 +62,7 @@ from passlib.utils.decor import ( ) from passlib.exc import ExpectedStringError, ExpectedTypeError from passlib.utils.compat import (add_doc, join_bytes, join_byte_values, - join_byte_elems, irange, imap, PY3, + join_byte_elems, irange, imap, join_unicode, unicode, byte_elem_value, nextgetter, unicode_or_str, unicode_or_bytes_types, get_method_function, suppress_cause, PYPY) @@ -113,7 +113,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? @@ -166,30 +166,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 :| +# 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]) +_VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD +_VAR_ANY_SET = {_VAR_KEYWORD, inspect.Parameter.VAR_POSITIONAL} - 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 - -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,11 +326,11 @@ def consteq(left, right): if isinstance(left, unicode): if not isinstance(right, unicode): raise TypeError("inputs must be both unicode or both bytes") - is_py3_bytes = False + 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 + is_bytes = True else: raise TypeError("inputs must be both unicode or both bytes") @@ -359,7 +352,7 @@ def consteq(left, right): # 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: @@ -554,19 +547,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") @@ -760,22 +745,15 @@ def to_unicode(source, encoding="utf-8", param="value"): 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, unicode): + return source + else: + raise ExpectedStringError(source, param) + add_doc(to_native_str, """Take in unicode or bytes, return native string. @@ -837,7 +815,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 @@ -882,7 +860,7 @@ else: # chars in this string... _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, @@ -942,27 +920,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. diff --git a/passlib/utils/binary.py b/passlib/utils/binary.py index 101cbe8..bdef73b 100644 --- a/passlib/utils/binary.py +++ b/passlib/utils/binary.py @@ -18,7 +18,7 @@ log = logging.getLogger(__name__) # pkg from passlib import exc from passlib.utils.compat import ( - PY3, bascii_to_str, + bascii_to_str, irange, imap, iter_byte_chars, join_byte_values, join_byte_elems, nextgetter, suppress_cause, unicode, unicode_or_bytes_types, @@ -384,10 +384,7 @@ 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 = nextgetter(iter(source)) gen = self._encode_bytes(next_value, chunks, tail) out = join_byte_elems(imap(self._encode64, gen)) ##if tail: @@ -634,8 +631,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): @@ -723,9 +719,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: @@ -808,10 +803,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""" diff --git a/passlib/utils/compat/__init__.py b/passlib/utils/compat/__init__.py index 9e58d58..2ad834e 100644 --- a/passlib/utils/compat/__init__.py +++ b/passlib/utils/compat/__init__.py @@ -7,8 +7,6 @@ # 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 < (3, 5): @@ -32,10 +30,7 @@ PYSTON = "Pyston" in sys.version # common imports #============================================================================= import logging; log = logging.getLogger(__name__) -if PY3: - import builtins -else: - import __builtin__ as builtins +import builtins def add_doc(obj, doc): """add docstring to an object""" @@ -45,9 +40,6 @@ def add_doc(obj, doc): # the default exported vars #============================================================================= __all__ = [ - # python versions - 'PY2', 'PY3', - # io 'BytesIO', 'StringIO', 'NativeStringIO', 'SafeConfigParser', @@ -91,7 +83,8 @@ _lazy_attrs = dict() #============================================================================= # unicode & bytes types #============================================================================= -if PY3: + +if True: # legacy PY3 indent unicode = str # NOTE: don't need to use this for general u'xxx' case, @@ -103,15 +96,6 @@ if PY3: unicode_or_bytes_types = (str, bytes) native_string_types = (unicode,) -else: - unicode = builtins.unicode - - def u(s): - assert isinstance(s, str) - return s.decode("ascii") - - unicode_or_bytes_types = (basestring,) - native_string_types = (basestring,) # shorter preferred aliases # TODO: align compat module w/ crowbar.compat naming @@ -132,7 +116,7 @@ join_unicode = u''.join # function to join list of byte strings join_bytes = b''.join -if PY3: +if True: # legacy PY3 indent def uascii_to_str(s): assert isinstance(s, unicode) return s @@ -164,38 +148,7 @@ if PY3: # 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 - +# TODO: move docstrings to funcs... add_doc(uascii_to_str, "helper to convert ascii unicode -> native str") add_doc(bascii_to_str, "helper to convert ascii bytes -> native str") add_doc(str_to_uascii, "helper to convert ascii native str -> unicode") @@ -205,7 +158,7 @@ add_doc(str_to_bascii, "helper to convert ascii native str -> bytes") # 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. +# this is 97 under PY3. # byte_elem_value -- function to convert byte element to integer -- a noop under PY3 @@ -215,12 +168,9 @@ 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) + +int_types = (int,) +num_types = (int, float) #============================================================================= # iteration helpers @@ -231,7 +181,7 @@ else: # imap - map to iterator # lmap - map to list #============================================================================= -if PY3: +if True: # legacy PY3 indent irange = range ##def lrange(*a,**k): ## return list(range(*a,**k)) @@ -250,21 +200,6 @@ if PY3: 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") #============================================================================= @@ -277,10 +212,7 @@ add_doc(nextgetter, "return function that yields successive values from iterable #============================================================================= # introspection #============================================================================= -if PY3: - method_function_attr = "__func__" -else: - method_function_attr = "im_func" +method_function_attr = "__func__" def get_method_function(func): """given (potential) method, return underlying function""" @@ -288,7 +220,7 @@ def get_method_function(func): def get_unbound_method_function(func): """given unbound method, return underlying function""" - return func if PY3 else func.__func__ + return func def error_from(exc, # *, cause=None): @@ -307,21 +239,13 @@ suppress_cause = error_from #============================================================================= # input/output #============================================================================= -if PY3: - _lazy_attrs = dict( - BytesIO="io.BytesIO", - UnicodeIO="io.StringIO", - NativeStringIO="io.StringIO", - SafeConfigParser="configparser.ConfigParser", - ) - -else: - _lazy_attrs = dict( - BytesIO="cStringIO.StringIO", - UnicodeIO="StringIO.StringIO", - NativeStringIO="cStringIO.StringIO", - SafeConfigParser="ConfigParser.SafeConfigParser", - ) + +_lazy_attrs = dict( + BytesIO="io.BytesIO", + UnicodeIO="io.StringIO", + NativeStringIO="io.StringIO", + SafeConfigParser="configparser.ConfigParser", +) #============================================================================= # collections diff --git a/passlib/utils/decor.py b/passlib/utils/decor.py index 392e569..bd4c290 100644 --- a/passlib/utils/decor.py +++ b/passlib/utils/decor.py @@ -12,7 +12,6 @@ import types from warnings import warn # site # pkg -from passlib.utils.compat import PY3 # local __all__ = [ "classproperty", @@ -55,10 +54,7 @@ class hybrid_method(object): 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 @@ -103,13 +99,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). diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py index 6115d11..8034cc3 100644 --- a/passlib/utils/handlers.py +++ b/passlib/utils/handlers.py @@ -28,7 +28,7 @@ from passlib.utils.binary import ( ) from passlib.utils.compat import join_byte_values, irange, native_string_types, \ uascii_to_str, join_unicode, unicode, str_to_uascii, \ - join_unicode, unicode_or_bytes_types, PY2, int_types + join_unicode, unicode_or_bytes_types, int_types from passlib.utils.decor import classproperty, deprecated_method # local __all__ = [ @@ -1408,7 +1408,7 @@ class HasSalt(GenericHandler): else: if not isinstance(salt, unicode): # 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") |