From af0d5345bf7a9664c5becb4d375eed5ba84e4883 Mon Sep 17 00:00:00 2001 From: thatmattlove Date: Fri, 17 Sep 2021 18:12:37 -0700 Subject: [PATCH] Add `dict` and `object.__init__` comparison utilities with tests --- hyperglass/util/__init__.py | 28 +++++++++++++ hyperglass/util/tests/test_utilities.py | 55 +++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 hyperglass/util/tests/test_utilities.py diff --git a/hyperglass/util/__init__.py b/hyperglass/util/__init__.py index 12ed801..bf00ade 100644 --- a/hyperglass/util/__init__.py +++ b/hyperglass/util/__init__.py @@ -334,3 +334,31 @@ def at_least(minimum: int, value: int,) -> int: if value < minimum: return minimum return value + + +def compare_dicts(dict_a: t.Dict[t.Any, t.Any], dict_b: t.Dict[t.Any, t.Any]) -> bool: + """Determine if two dictationaries are (mostly) equal.""" + if isinstance(dict_a, t.Dict) and isinstance(dict_b, t.Dict): + dict_a_keys, dict_a_values = set(dict_a.keys()), set(dict_a.values()) + dict_b_keys, dict_b_values = set(dict_b.keys()), set(dict_b.values()) + return all((dict_a_keys == dict_b_keys, dict_a_values == dict_b_values)) + return False + + +def compare_init(obj_a: object, obj_b: object) -> bool: + """Compare the `__init__` annoations of two objects.""" + + def _check_obj(obj: object): + """Ensure `__annotations__` exists on the `__init__` method.""" + if hasattr(obj, "__init__") and isinstance(getattr(obj, "__init__", None), t.Callable): + if hasattr(obj.__init__, "__annotations__") and isinstance( + getattr(obj.__init__, "__annotations__", None), t.Dict + ): + return True + return False + + if all((_check_obj(obj_a), _check_obj(obj_b))): + obj_a.__init__.__annotations__.pop("self", None) + obj_b.__init__.__annotations__.pop("self", None) + return compare_dicts(obj_a.__init__.__annotations__, obj_b.__init__.__annotations__) + return False diff --git a/hyperglass/util/tests/test_utilities.py b/hyperglass/util/tests/test_utilities.py new file mode 100644 index 0000000..7f236d1 --- /dev/null +++ b/hyperglass/util/tests/test_utilities.py @@ -0,0 +1,55 @@ +"""Test generic utilities.""" + +# Local +from .. import compare_init, compare_dicts + + +def test_compare_dicts(): + + d1 = {"one": 1, "two": 2} + d2 = {"one": 1, "two": 2} + d3 = {"one": 1, "three": 3} + d4 = {"one": 1, "two": 3} + d5 = {} + d6 = {} + checks = ( + (d1, d2, True), + (d1, d3, False), + (d1, d4, False), + (d1, d1, True), + (d5, d6, True), + (d1, [], False), + ) + for a, b, expected in checks: + assert compare_dicts(a, b) is expected + + +def test_compare_init(): + class Compare1: + def __init__(self, item: str) -> None: + pass + + class Compare2: + def __init__(self: "Compare2", item: str) -> None: + pass + + class Compare3: + def __init__(self: "Compare3", item: str, other_item: int) -> None: + pass + + class Compare4: + def __init__(self: "Compare4", item: bool) -> None: + pass + + class Compare5: + pass + + checks = ( + (Compare1, Compare2, True), + (Compare1, Compare3, False), + (Compare1, Compare4, False), + (Compare1, Compare5, False), + (Compare1, Compare1, True), + ) + for a, b, expected in checks: + assert compare_init(a, b) is expected