commit: f40af28d677663b4cd8cfe053ad88259077a1710
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sun Jan 15 06:59:31 2023 +0000
Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Thu Feb 2 19:59:11 2023 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=f40af28d
refactor(config): adding typing to config hints along with immutability.
ConfigHint's should be treated as immutable; any manipulation of their data
should be handled either via clone or via pre-processing. This system is
complex enough we don't want mutability, and the allowance of that was an
oversight from the good ole days of py2.4.
Force immutability sincei t's the actual design contract and also to flush
out anyone breaking said contract (no pkgcore code breaks that, to be clear).
Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>
src/pkgcore/config/hint.py | 40 ++++++++++++++++++++++++++--------------
tests/config/test_basics.py | 5 +++--
2 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/src/pkgcore/config/hint.py b/src/pkgcore/config/hint.py
index 73419865e..4a052fe52 100644
--- a/src/pkgcore/config/hint.py
+++ b/src/pkgcore/config/hint.py
@@ -3,10 +3,21 @@
__all__ = ("ConfigHint", "configurable")
+import typing
+
class ConfigHint:
"""Hint for introspection supplying overrides."""
+ types: dict[str, str]
+ positional: tuple[str, ...]
+ required: tuple[str, ...]
+ typename: typing.Optional[str]
+ allow_unknowns: bool
+ authorative: bool
+ requires_config: bool
+ raw_class: bool
+
# be aware this is used in clone
__slots__ = (
"types",
@@ -22,19 +33,19 @@ class ConfigHint:
def __init__(
self,
- types=None,
- positional=None,
- required=None,
- doc=None,
- typename=None,
- allow_unknowns=False,
- authorative=False,
- requires_config=False,
- raw_class=False,
- ):
+ types: typing.Optional[dict[str, str]] = None,
+ positional: typing.Optional[typing.Sequence[str]] = None,
+ required: typing.Optional[typing.Sequence[str]] = None,
+ doc: typing.Optional[str] = None,
+ typename: typing.Optional[str] = None,
+ allow_unknowns: bool = False,
+ authorative: bool = False,
+ requires_config: bool = False,
+ raw_class: bool = False,
+ ) -> None:
self.types = types or {}
- self.positional = positional or []
- self.required = required or []
+ self.positional = tuple(positional or [])
+ self.required = tuple(required or [])
self.typename = typename
self.allow_unknowns = allow_unknowns
self.doc = doc
@@ -42,12 +53,13 @@ class ConfigHint:
self.requires_config = requires_config
self.raw_class = raw_class
- def clone(self, **kwds):
+ def clone(self, **kwds: typing.Any) -> "ConfigHint":
+ """return a copy of this ConfigHint with the given kwds overrides"""
new_kwds = {}
for attr in self.__slots__:
new_kwds[attr] = kwds.pop(attr, getattr(self, attr))
if kwds:
- raise TypeError("unknown type overrides: %r" % kwds)
+ raise TypeError(f"unknown type overrides: {kwds!r}")
return self.__class__(**new_kwds)
diff --git a/tests/config/test_basics.py b/tests/config/test_basics.py
index 374fc1352..929c907c2 100644
--- a/tests/config/test_basics.py
+++ b/tests/config/test_basics.py
@@ -196,6 +196,7 @@ class TestConfigHint:
def test_clone(self):
c = ConfigHint(
types={"foo": "list", "one": "str"},
+ # use lists to ensure it forces immutability.
positional=["one"],
required=["one"],
typename="barn",
@@ -205,8 +206,8 @@ class TestConfigHint:
types={"foo": "list", "one": "str", "two": "str"},
required=["one", "two"]
)
assert c2.types == {"foo": "list", "one": "str", "two": "str"}
- assert c2.positional == c.positional
- assert c2.required == ["one", "two"]
+ assert c2.positional == tuple(c.positional)
+ assert c2.required == ("one", "two")
assert c2.typename == c.typename
assert c2.allow_unknowns == c.allow_unknowns
assert c2.doc == c.doc