From 3f2f9235a351b8eab9cd7e2ecd2144ee15ae5617 Mon Sep 17 00:00:00 2001 From: Vasek Sraier Date: Fri, 22 Oct 2021 14:09:25 +0200 Subject: [PATCH] simple custom implementation of dataclasses in order to prevent dependence on the backport library --- .../compat/dataclasses.py | 47 ++++++++++++++++++- manager/tests/utils/test_dataclasses.py | 15 ++++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 manager/tests/utils/test_dataclasses.py diff --git a/manager/knot_resolver_manager/compat/dataclasses.py b/manager/knot_resolver_manager/compat/dataclasses.py index 57f77fd2f..7f5628c4c 100644 --- a/manager/knot_resolver_manager/compat/dataclasses.py +++ b/manager/knot_resolver_manager/compat/dataclasses.py @@ -7,6 +7,49 @@ the option to do it transparently, without changing anything else. """ -from dataclasses import dataclass, field, is_dataclass +from typing import Any, Dict, Set, Type -__all__ = ["dataclass", "is_dataclass", "field"] +_CUSTOM_DATACLASS_MARKER = "_CUSTOM_DATACLASS_MARKER" + + +def dataclass(cls: Any): + anot: Dict[str, Type[Any]] = cls.__dict__.get("__annotations__", {}) + + def ninit(slf: Any, *args: Any, **kwargs: Any) -> None: + nonlocal anot + + ianot = iter(anot.keys()) + used: Set[str] = set() + + # set normal arguments + for arg in args: + name = next(ianot) + setattr(slf, name, arg) + used.add(name) + + # set keyd arguments + for key, val in kwargs.items(): + assert key in anot, f"Key '{key}' not defined with a type annotation" + setattr(slf, key, val) + used.add(key) + + # set default values + for key in anot: + if key in used: + continue + assert hasattr( + cls, key + ), f"Field '{key}' does not have default value and was not defined in the constructor" + dfl = getattr(cls, key) + setattr(slf, key, dfl) + + setattr(cls, "__init__", ninit) + setattr(cls, _CUSTOM_DATACLASS_MARKER, ...) + return cls + + +def is_dataclass(cls: Any) -> bool: + return hasattr(cls, _CUSTOM_DATACLASS_MARKER) + + +__all__ = ["dataclass", "is_dataclass"] diff --git a/manager/tests/utils/test_dataclasses.py b/manager/tests/utils/test_dataclasses.py new file mode 100644 index 000000000..c402c0923 --- /dev/null +++ b/manager/tests/utils/test_dataclasses.py @@ -0,0 +1,15 @@ +from knot_resolver_manager.compat.dataclasses import dataclass, is_dataclass + + +def test_dataclass(): + @dataclass + class A: + b: int = 5 + + val = A(6) + assert val.b == 6 + + val = A(b=7) + assert val.b == 7 + + assert is_dataclass(A) -- 2.47.3