Coverage for src/configuraptor/beautify.py: 100%
26 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-09 20:07 +0100
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-09 20:07 +0100
1"""
2Add @beautify behavior to enhance configuraptor/TypedConfig classes automagically!
3"""
5import functools
6import typing
8from .dump import asdict, asjson
10T = typing.TypeVar("T")
13def is_default(obj: typing.Any, prop: str) -> bool:
14 """
15 Check if the property of an object is set to its default value.
17 Args:
18 obj (typing.Any): The object to check.
19 prop (str): The property to check.
21 Returns:
22 bool: True if the property is set to its default value, False otherwise.
23 """
24 return getattr(obj, prop) is getattr(object, prop)
27def patch(cls: typing.Type[T], patch_repr: bool, patch_str: bool) -> None:
28 """
29 Patch the __str__ and __repr__ methods of a class if they are set to their default values.
31 Args:
32 cls (typing.Type[typing.Any]): The class to patch.
33 patch_repr: patch __repr__? (if no custom one set yet)
34 patch_str: patch __str__? (if no custom one set yet)
35 """
37 def _repr(self: T) -> str:
38 """
39 Custom __repr__ by configuraptor @beautify.
40 """
41 clsname = type(self).__name__
42 data = asdict(self, with_top_level_key=False, exclude_internals=2)
43 return f"<{clsname} {data}>"
45 def _str(self: T) -> str:
46 """
47 Custom __str__ by configuraptor @beautify.
48 """
49 return asjson(self, with_top_level_key=False, exclude_internals=2)
51 # if magic method is already set, don't overwrite it!
52 if patch_str and is_default(cls, "__str__"):
53 cls.__str__ = _str # type: ignore
55 if patch_repr and is_default(cls, "__repr__"):
56 cls.__repr__ = _repr # type: ignore
59@typing.overload
60def beautify(
61 maybe_cls: typing.Type[T],
62 repr: bool = True, # noqa A002
63 str: bool = True, # noqa A002
64) -> typing.Type[T]:
65 """
66 Overload function for the beautify decorator when used without parentheses.
67 """
70@typing.overload
71def beautify(
72 maybe_cls: None = None,
73 repr: bool = True, # noqa A002
74 str: bool = True, # noqa A002
75) -> typing.Callable[[typing.Type[T]], typing.Type[T]]:
76 """
77 Overload function for the beautify decorator when used with parentheses.
78 """
81def beautify(
82 maybe_cls: typing.Type[T] | None = None,
83 repr: bool = True, # noqa A002
84 str: bool = True, # noqa A002
85) -> typing.Type[T] | typing.Callable[[typing.Type[T]], typing.Type[T]]:
86 """
87 The beautify decorator. Enhances a class by patching its __str__ and __repr__ methods.
89 Args:
90 maybe_cls (typing.Type[T] | None, optional): The class to beautify. None when used with parentheses.
91 repr: patch __repr__? (if no custom one set yet)
92 str: patch __str__? (if no custom one set yet)
94 Returns:
95 The beautified class or the beautify decorator.
96 """
97 if maybe_cls:
98 patch(maybe_cls, patch_repr=repr, patch_str=str)
99 return maybe_cls
100 else:
101 return functools.partial(beautify, repr=repr, str=str)