diff --git a/ChangeLog b/ChangeLog index 55a6eb783..6d4ed25a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,10 @@ What's New in astroid 2.12.12? ============================== Release date: TBA +* Add the ``length`` parameter to ``hash.digest`` & ``hash.hexdigest`` in the ``hashlib`` brain. + + Refs PyCQA/pylint#4039 + diff --git a/astroid/brain/brain_hashlib.py b/astroid/brain/brain_hashlib.py index b628361d8..e321af6dc 100644 --- a/astroid/brain/brain_hashlib.py +++ b/astroid/brain/brain_hashlib.py @@ -10,48 +10,86 @@ def _hashlib_transform(): maybe_usedforsecurity = ", usedforsecurity=True" if PY39_PLUS else "" - signature = f"value=''{maybe_usedforsecurity}" + init_signature = f"value=''{maybe_usedforsecurity}" + digest_signature = "self" + shake_digest_signature = "self, length" + template = """ - class %(name)s(object): - def __init__(self, %(signature)s): pass - def digest(self): - return %(digest)s - def copy(self): - return self - def update(self, value): pass - def hexdigest(self): - return '' - @property - def name(self): - return %(name)r - @property - def block_size(self): - return 1 - @property - def digest_size(self): - return 1 + class %(name)s: + def __init__(self, %(init_signature)s): pass + def digest(%(digest_signature)s): + return %(digest)s + def copy(self): + return self + def update(self, value): pass + def hexdigest(%(digest_signature)s): + return '' + @property + def name(self): + return %(name)r + @property + def block_size(self): + return 1 + @property + def digest_size(self): + return 1 """ + algorithms_with_signature = dict.fromkeys( - ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"], signature + [ + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + "sha3_224", + "sha3_256", + "sha3_384", + "sha3_512", + ], + (init_signature, digest_signature), + ) + + blake2b_signature = ( + "data=b'', *, digest_size=64, key=b'', salt=b'', " + "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, " + f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" + ) + + blake2s_signature = ( + "data=b'', *, digest_size=32, key=b'', salt=b'', " + "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, " + f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" ) - blake2b_signature = f"data=b'', *, digest_size=64, key=b'', salt=b'', \ - person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \ - node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" - blake2s_signature = f"data=b'', *, digest_size=32, key=b'', salt=b'', \ - person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \ - node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" - new_algorithms = dict.fromkeys( - ["sha3_224", "sha3_256", "sha3_384", "sha3_512", "shake_128", "shake_256"], - signature, + + shake_algorithms = dict.fromkeys( + ["shake_128", "shake_256"], + (init_signature, shake_digest_signature), ) - algorithms_with_signature.update(new_algorithms) + algorithms_with_signature.update(shake_algorithms) + algorithms_with_signature.update( - {"blake2b": blake2b_signature, "blake2s": blake2s_signature} + { + "blake2b": (blake2b_signature, digest_signature), + "blake2s": (blake2s_signature, digest_signature), + } ) + classes = "".join( - template % {"name": hashfunc, "digest": 'b""', "signature": signature} - for hashfunc, signature in algorithms_with_signature.items() + template + % { + "name": hashfunc, + "digest": 'b""', + "init_signature": init_signature, + "digest_signature": digest_signature, + } + for hashfunc, ( + init_signature, + digest_signature, + ) in algorithms_with_signature.items() ) + return parse(classes) diff --git a/tests/unittest_brain.py b/tests/unittest_brain.py index 2f88fc542..114751c8d 100644 --- a/tests/unittest_brain.py +++ b/tests/unittest_brain.py @@ -83,24 +83,44 @@ def _assert_hashlib_class(self, class_obj: ClassDef) -> None: len(class_obj["__init__"].args.defaults), 2 if PY39_PLUS else 1 ) self.assertEqual(len(class_obj["update"].args.args), 2) - self.assertEqual(len(class_obj["digest"].args.args), 1) - self.assertEqual(len(class_obj["hexdigest"].args.args), 1) def test_hashlib(self) -> None: """Tests that brain extensions for hashlib work.""" hashlib_module = MANAGER.ast_from_module_name("hashlib") - for class_name in ("md5", "sha1"): + for class_name in ( + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + "sha3_224", + "sha3_256", + "sha3_384", + "sha3_512", + ): class_obj = hashlib_module[class_name] self._assert_hashlib_class(class_obj) + self.assertEqual(len(class_obj["digest"].args.args), 1) + self.assertEqual(len(class_obj["hexdigest"].args.args), 1) - def test_hashlib_py36(self) -> None: + def test_shake(self) -> None: + """Tests that the brain extensions for the hashlib shake algorithms work.""" hashlib_module = MANAGER.ast_from_module_name("hashlib") - for class_name in ("sha3_224", "sha3_512", "shake_128"): + for class_name in ("shake_128", "shake_256"): class_obj = hashlib_module[class_name] self._assert_hashlib_class(class_obj) + self.assertEqual(len(class_obj["digest"].args.args), 2) + self.assertEqual(len(class_obj["hexdigest"].args.args), 2) + + def test_blake2(self) -> None: + """Tests that the brain extensions for the hashlib blake2 hash functions work.""" + hashlib_module = MANAGER.ast_from_module_name("hashlib") for class_name in ("blake2b", "blake2s"): class_obj = hashlib_module[class_name] self.assertEqual(len(class_obj["__init__"].args.args), 2) + self.assertEqual(len(class_obj["digest"].args.args), 1) + self.assertEqual(len(class_obj["hexdigest"].args.args), 1) class CollectionsDequeTests(unittest.TestCase):