diff options
Diffstat (limited to 'passlib/tests/utils.py')
-rw-r--r-- | passlib/tests/utils.py | 154 |
1 files changed, 64 insertions, 90 deletions
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py index 79a9f9f..9ade9de 100644 --- a/passlib/tests/utils.py +++ b/passlib/tests/utils.py @@ -2,7 +2,6 @@ #============================================================================= # imports #============================================================================= -from __future__ import with_statement # core from binascii import unhexlify import contextlib @@ -16,8 +15,9 @@ import sys import tempfile import threading import time +import unittest from passlib.exc import PasslibHashWarning, PasslibConfigWarning -from passlib.utils.compat import PY3, JYTHON +from passlib.utils.compat import JYTHON import warnings from warnings import warn # site @@ -25,11 +25,9 @@ from warnings import warn from passlib import exc from passlib.exc import MissingBackendError import passlib.registry as registry -from passlib.tests.backports import TestCase as _TestCase, skip, skipIf, skipUnless, SkipTest from passlib.utils import has_rounds_info, has_salt_info, rounds_cost_values, \ rng as sys_rng, getrandstr, is_ascii_safe, to_native_str, \ repeat_string, tick, batch -from passlib.utils.compat import iteritems, irange, u, unicode, PY2, nullcontext from passlib.utils.decor import classproperty import passlib.utils.handlers as uh # local @@ -219,7 +217,7 @@ def patch_calc_min_rounds(handler): #============================================================================= def set_file(path, content): """set file to specified bytes""" - if isinstance(content, unicode): + if isinstance(content, str): content = content.encode("utf-8") with open(path, "wb") as fh: fh.write(content) @@ -231,15 +229,10 @@ def get_file(path): def tonn(source): """convert native string to non-native string""" - if not isinstance(source, str): - return source - elif PY3: + if isinstance(source, str): return source.encode("utf-8") else: - try: - return source.decode("utf-8") - except UnicodeDecodeError: - return source.decode("latin-1") + return source def hb(source): """ @@ -288,7 +281,7 @@ def run_with_fixed_seeds(count=128, master_seed=0x243F6A8885A308D3): @wraps(func) def wrapper(*args, **kwds): rng = random.Random(master_seed) - for _ in irange(count): + for _ in range(count): kwds['seed'] = rng.getrandbits(32) func(*args, **kwds) return wrapper @@ -298,7 +291,7 @@ def run_with_fixed_seeds(count=128, master_seed=0x243F6A8885A308D3): # custom test harness #============================================================================= -class TestCase(_TestCase): +class TestCase(unittest.TestCase): """passlib-specific test case class this class adds a number of features to the standard TestCase... @@ -321,7 +314,7 @@ class TestCase(_TestCase): def shortDescription(self): """wrap shortDescription() method to prepend descriptionPrefix""" - desc = super(TestCase, self).shortDescription() + desc = super().shortDescription() prefix = self.descriptionPrefix if prefix: desc = "%s: %s" % (prefix, desc or str(self)) @@ -333,7 +326,7 @@ class TestCase(_TestCase): #--------------------------------------------------------------- @classproperty def __unittest_skip__(cls): - # NOTE: this attr is technically a unittest2 internal detail. + # NOTE: this attr is technically a unittest internal detail. name = cls.__name__ return name.startswith("_") or \ getattr(cls, "_%s__unittest_skip" % name, False) @@ -354,7 +347,7 @@ class TestCase(_TestCase): resetWarningState = True def setUp(self): - super(TestCase, self).setUp() + super().setUp() self.setUpWarnings() # have uh.debug_only_repr() return real values for duration of test self.patchAttr(exc, "ENABLE_DEBUG_ONLY_REPR", True) @@ -375,7 +368,6 @@ class TestCase(_TestCase): # should be kept until then, so we test the legacy paths. warnings.filterwarnings("ignore", r"the method .*\.(encrypt|genconfig|genhash)\(\) is deprecated") warnings.filterwarnings("ignore", r"the 'vary_rounds' option is deprecated") - warnings.filterwarnings("ignore", r"Support for `(py-bcrypt|bcryptor)` is deprecated") #--------------------------------------------------------------- # tweak message formatting so longMessage mode is only enabled @@ -397,7 +389,7 @@ class TestCase(_TestCase): msg = kwds.pop("__msg__", None) if _callable is None: # FIXME: this ignores 'msg' - return super(TestCase, self).assertRaises(_exc_type, None, + return super().assertRaises(_exc_type, None, *args, **kwds) try: result = _callable(*args, **kwds) @@ -411,7 +403,7 @@ class TestCase(_TestCase): # forbid a bunch of deprecated aliases so I stop using them #--------------------------------------------------------------- def assertEquals(self, *a, **k): - raise AssertionError("this alias is deprecated by unittest2") + raise AssertionError("this alias is deprecated by stdlib unittest") assertNotEquals = assertRegexMatches = assertEquals #=================================================================== @@ -470,14 +462,13 @@ class TestCase(_TestCase): def __init__(self, case, **kwds): self.case = case self.kwds = kwds - self.__super = super(TestCase._AssertWarningList, self) - self.__super.__init__(record=True) + super().__init__(record=True) def __enter__(self): - self.log = self.__super.__enter__() + self.log = super().__enter__() def __exit__(self, *exc_info): - self.__super.__exit__(*exc_info) + super().__exit__(*exc_info) if exc_info[0] is None: self.case.assertWarningList(self.log, **self.kwds) @@ -631,20 +622,14 @@ class TestCase(_TestCase): # subtests #=================================================================== - has_real_subtest = hasattr(_TestCase, "subTest") - @contextlib.contextmanager def subTest(self, *args, **kwds): """ - wrapper/backport for .subTest() which also traps SkipTest errors. + wrapper for .subTest() which traps SkipTest errors. (see source for details) """ - # this function works around two things: - # * TestCase.subTest() wasn't added until Py34; so for older python versions, - # we either need unittest2 installed, or provide stub of our own. - # this method provides a stub if needed (based on .has_real_subtest check) - # - # * as 2020-10-08, .subTest() doesn't play nicely w/ .skipTest(); + # this function works around issue that as 2020-10-08, + # .subTest() doesn't play nicely w/ .skipTest(); # and also makes it hard to debug which subtest had a failure. # (see https://bugs.python.org/issue25894 and https://bugs.python.org/issue35327) # this method traps skipTest exceptions, and adds some logging to help debug @@ -663,13 +648,8 @@ class TestCase(_TestCase): test_log = self.getLogger() title = _render_title(*args, **kwds) - # use real subtest manager if available - if self.has_real_subtest: - ctx = super(TestCase, self).subTest(*args, **kwds) - else: - ctx = nullcontext() - # run the subtest + ctx = super().subTest(*args, **kwds) with ctx: test_log.info("running subtest: %s", title) try: @@ -677,7 +657,9 @@ class TestCase(_TestCase): except SkipTest: # silence "SkipTest" exceptions, want to keep running next subtest. test_log.info("subtest skipped: %s", title) - pass + # XXX: should revisit whether latest py3 version of UTs handle this ok, + # meaning it's safe to re-raise this. + return except Exception as err: # log unhandled exception occurred # (assuming traceback will be reported up higher, so not bothering here) @@ -734,8 +716,7 @@ class TestCase(_TestCase): return logger named after current test. """ cls = type(self) - # NOTE: conditional on qualname for PY2 compat - path = cls.__module__ + "." + getattr(cls, "__qualname__", cls.__name__) + path = cls.__module__ + "." + cls.__qualname__ name = self._testMethodName if name: path = path + "." + name @@ -778,8 +759,7 @@ class HandlerCase(TestCase): .. note:: - This is subclass of :class:`unittest.TestCase` - (or :class:`unittest2.TestCase` if available). + This is subclass of :class:`unittest.TestCase`. """ #=================================================================== # class attrs - should be filled in by subclass @@ -831,8 +811,8 @@ class HandlerCase(TestCase): # passwords used to test basic hash behavior - generally # don't need to be overidden. stock_passwords = [ - u("test"), - u("\u20AC\u00A5$"), + u"test", + u"\u20AC\u00A5$", b'\xe2\x82\xac\xc2\xa5$' ] @@ -1065,7 +1045,7 @@ class HandlerCase(TestCase): if test_requires_backend and self._skip_backend_reason: raise self.skipTest(self._skip_backend_reason) - super(HandlerCase, self).setUp() + super().setUp() # if needed, select specific backend for duration of test # NOTE: skipping this if create_backend_case() signalled we're skipping backend @@ -1384,7 +1364,7 @@ class HandlerCase(TestCase): def sampler(func): value1 = func() - for _ in irange(samples): + for _ in range(samples): value2 = func() if value1 != value2: return @@ -1500,7 +1480,7 @@ class HandlerCase(TestCase): self.do_stub_encrypt(salt=salt) # check some invalid salt chars, make sure they're rejected - source = u('\x00\xff') + source = u'\x00\xff' if raw: source = source.encode("latin-1") chunk = max(mn, 1) @@ -1516,7 +1496,7 @@ class HandlerCase(TestCase): if getattr(self.handler, "_salt_is_bytes", False): return bytes else: - return unicode + return str def test_15_salt_type(self): """test non-string salt values""" @@ -1530,12 +1510,11 @@ class HandlerCase(TestCase): self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=fake()) # unicode should be accepted only if salt_type is unicode. - if salt_type is not unicode: - self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=u('x') * salt_size) + if salt_type is not str: + self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=u'x' * salt_size) - # bytes should be accepted only if salt_type is bytes, - # OR if salt type is unicode and running PY2 - to allow native strings. - if not (salt_type is bytes or (PY2 and salt_type is unicode)): + # bytes should be accepted only if salt_type is bytes + if salt_type is not bytes: self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=b'x' * salt_size) def test_using_salt_size(self): @@ -1936,7 +1915,7 @@ class HandlerCase(TestCase): handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() def get_effective_range(cls): - seen = set(get_effective_rounds(cls) for _ in irange(1000)) + seen = set(get_effective_rounds(cls) for _ in range(1000)) return min(seen), max(seen) def assert_rounds_range(vary_rounds, lower, upper): @@ -2011,24 +1990,24 @@ class HandlerCase(TestCase): # check ident_values list for value in cls.ident_values: - self.assertIsInstance(value, unicode, - "cls.ident_values must be unicode:") + self.assertIsInstance(value, str, + "cls.ident_values must be str:") self.assertTrue(len(cls.ident_values)>1, "cls.ident_values must have 2+ elements:") # check default_ident value - self.assertIsInstance(cls.default_ident, unicode, - "cls.default_ident must be unicode:") + self.assertIsInstance(cls.default_ident, str, + "cls.default_ident must be str:") self.assertTrue(cls.default_ident in cls.ident_values, "cls.default_ident must specify member of cls.ident_values") # check optional aliases list if cls.ident_aliases: - for alias, ident in iteritems(cls.ident_aliases): - self.assertIsInstance(alias, unicode, - "cls.ident_aliases keys must be unicode:") # XXX: allow ints? - self.assertIsInstance(ident, unicode, - "cls.ident_aliases values must be unicode:") + for alias, ident in cls.ident_aliases.items(): + self.assertIsInstance(alias, str, + "cls.ident_aliases keys must be str:") # XXX: allow ints? + self.assertIsInstance(ident, str, + "cls.ident_aliases values must be str:") self.assertTrue(ident in cls.ident_values, "cls.ident_aliases must map to cls.ident_values members: %r" % (ident,)) @@ -2347,7 +2326,7 @@ class HandlerCase(TestCase): chars = self.forbidden_characters if not chars: raise self.skipTest("none listed") - base = u('stub') + base = u'stub' if isinstance(chars, bytes): from passlib.utils.compat import iter_byte_chars chars = iter_byte_chars(chars) @@ -2366,7 +2345,7 @@ class HandlerCase(TestCase): """ check if we're expecting potential verify failure due to crypt.crypt() encoding limitation """ - if PY3 and self.backend == "os_crypt" and isinstance(secret, bytes): + if self.backend == "os_crypt" and isinstance(secret, bytes): try: secret.decode("utf-8") except UnicodeDecodeError: @@ -2578,7 +2557,7 @@ class HandlerCase(TestCase): # # test hash='' is rejected for all but the plaintext hashes # - for hash in [u(''), b'']: + for hash in [u'', b'']: if self.accepts_all_hashes: # then it accepts empty string as well. self.assertTrue(self.do_identify(hash)) @@ -2609,7 +2588,7 @@ class HandlerCase(TestCase): def require_parsehash(self): if not hasattr(self.handler, "parsehash"): - raise SkipTest("parsehash() not implemented") + raise self.skipTest("parsehash() not implemented") def test_70_parsehash(self): """ @@ -2638,11 +2617,6 @@ class HandlerCase(TestCase): # but all else should be the same result3 = handler.parsehash(hash, sanitize=True) correct3 = result.copy() - if PY2: - # silence warning about bytes & unicode not comparing - # (sanitize may convert bytes into base64 text) - warnings.filterwarnings("ignore", ".*unequal comparison failed to convert.*", - category=UnicodeWarning) for key in ("salt", "checksum"): if key in result3: self.assertNotEqual(result3[key], correct3[key]) @@ -2656,7 +2630,7 @@ class HandlerCase(TestCase): """ if value is None: return - self.assertIsInstance(value, unicode) + self.assertIsInstance(value, str) # assumes mask_value() defaults will never show more than <show> chars (4); # and show nothing if size less than 1/<pct> (8). ref = value if len(value) < 8 else value[4:] @@ -2797,7 +2771,7 @@ class HandlerCase(TestCase): def wrapper(): try: self.test_77_fuzz_input(threaded=True) - except SkipTest: + except unittest.SkipTest: pass except: with failed_lock: @@ -2811,7 +2785,7 @@ class HandlerCase(TestCase): thread.setDaemon(True) thread.start() return thread - threads = [launch(n) for n in irange(thread_count)] + threads = [launch(n) for n in range(thread_count)] # wait until all threads exit timeout = self.max_fuzz_time * thread_count * 4 @@ -2874,7 +2848,7 @@ class HandlerCase(TestCase): used by fuzz testing. verifiers should be callable with signature - ``func(password: unicode, hash: ascii str) -> ok: bool``. + ``func(password: str, hash: ascii str) -> ok: bool``. """ handler = self.handler verifiers = [] @@ -2929,7 +2903,7 @@ class HandlerCase(TestCase): #========================================================== # alphabet for randomly generated passwords - password_alphabet = u('qwertyASDF1234<>.@*#! \u00E1\u0259\u0411\u2113') + password_alphabet = u'qwertyASDF1234<>.@*#! \u00E1\u0259\u0411\u2113' # encoding when testing bytes password_encoding = "utf-8" @@ -3039,7 +3013,7 @@ class HandlerCase(TestCase): # occasionally try an empty password rng = self.rng if rng.random() < .0001: - return u('') + return u'' # check if truncate size needs to be considered handler = self.handler @@ -3058,7 +3032,7 @@ class HandlerCase(TestCase): result = getrandstr(rng, self.password_alphabet, size) # trim ones that encode past truncate point. - if truncate_size and isinstance(result, unicode): + if truncate_size and isinstance(result, str): while len(result.encode("utf-8")) > truncate_size: result = result[:-1] @@ -3210,7 +3184,7 @@ class OsCryptMixin(HandlerCase): if not self.handler.has_backend("os_crypt"): # XXX: currently, any tests that use this are skipped entirely! (see issue 120) self._patch_safe_crypt() - super(OsCryptMixin, self).setUp() + super().setUp() @classmethod def _get_safe_crypt_handler_backend(cls): @@ -3267,7 +3241,7 @@ class OsCryptMixin(HandlerCase): when it's known os_crypt will be faked by _patch_safe_crypt() """ assert backend == "os_crypt" - reason = super(OsCryptMixin, cls)._get_skip_backend_reason(backend) + reason = super()._get_skip_backend_reason(backend) from passlib.utils import has_crypt if reason == cls._BACKEND_NOT_AVAILABLE and has_crypt: @@ -3515,7 +3489,7 @@ class UserHandlerMixin(HandlerCase): context_map = HandlerCase.FuzzHashGenerator.context_map.copy() context_map.update(user="random_user") - user_alphabet = u("asdQWE123") + user_alphabet = u"asdQWE123" def random_user(self): rng = self.rng @@ -3543,14 +3517,14 @@ class EncodingHandlerMixin(HandlerCase): # restrict stock passwords & fuzz alphabet to latin-1, # so different encodings can be tested safely. stock_passwords = [ - u("test"), + u"test", b"test", - u("\u00AC\u00BA"), + u"\u00AC\u00BA", ] class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - password_alphabet = u('qwerty1234<>.@*#! \u00AC') + password_alphabet = u'qwerty1234<>.@*#! \u00AC' def populate_context(self, secret, kwds): """insert encoding into kwds""" @@ -3569,13 +3543,13 @@ class reset_warnings(warnings.catch_warnings): """catch_warnings() wrapper which clears warning registry & filters""" def __init__(self, reset_filter="always", reset_registry=".*", **kwds): - super(reset_warnings, self).__init__(**kwds) + super().__init__(**kwds) self._reset_filter = reset_filter self._reset_registry = re.compile(reset_registry) if reset_registry else None def __enter__(self): # let parent class archive filter state - ret = super(reset_warnings, self).__enter__() + ret = super().__enter__() # reset the filter to list everything if self._reset_filter: @@ -3614,7 +3588,7 @@ class reset_warnings(warnings.catch_warnings): setattr(mod, "__warningregistry__", orig) else: reg.update(orig) - super(reset_warnings, self).__exit__(*exc_info) + super().__exit__(*exc_info) #============================================================================= # eof |