summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2020-10-06 14:13:33 -0400
committerEli Collins <elic@assurancetechnologies.com>2020-10-06 14:13:33 -0400
commit65f4a25efd41639552df52dba40bfc377ae00871 (patch)
tree363bf55e05d7484d8b407e58fa2526719138b201
parentcc1c32da8866bdcc65bee916cd202f660b0cd88e (diff)
downloadpasslib-65f4a25efd41639552df52dba40bfc377ae00871.tar.gz
cleanup old python compat -- removed PY2 & PY3 conditionals
-rw-r--r--admin/bench_pbkdf2.py4
-rw-r--r--passlib/apache.py15
-rw-r--r--passlib/context.py49
-rw-r--r--passlib/crypto/_md4.py8
-rw-r--r--passlib/crypto/digest.py4
-rw-r--r--passlib/handlers/argon2.py7
-rw-r--r--passlib/handlers/bcrypt.py4
-rw-r--r--passlib/pwd.py6
-rw-r--r--passlib/tests/test_context.py21
-rw-r--r--passlib/tests/test_crypto_builtin_md4.py16
-rw-r--r--passlib/tests/test_crypto_digest.py9
-rw-r--r--passlib/tests/test_handlers.py10
-rw-r--r--passlib/tests/test_handlers_bcrypt.py2
-rw-r--r--passlib/tests/test_utils.py51
-rw-r--r--passlib/tests/utils.py28
-rw-r--r--passlib/totp.py7
-rw-r--r--passlib/utils/__init__.py112
-rw-r--r--passlib/utils/binary.py20
-rw-r--r--passlib/utils/compat/__init__.py114
-rw-r--r--passlib/utils/decor.py13
-rw-r--r--passlib/utils/handlers.py4
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")