Skip to content

Commit f9fa11b

Browse files
committed
Define Configuration as a dataclass
We need to hack a bit in __setattr__() to preserve the current behaviour but make dataclass fields initialization working. We also add a unit test to check the behaviour when trying to change dataclass fields, making sure they do not end up in 'entries'.
1 parent a06e5cc commit f9fa11b

File tree

2 files changed

+25
-22
lines changed

2 files changed

+25
-22
lines changed

pgtoolkit/conf.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
from collections.abc import Iterable, Iterator
5353
from dataclasses import dataclass, field
5454
from datetime import timedelta
55-
from typing import IO, Any, NoReturn, Union
55+
from typing import IO, Any, ClassVar, NoReturn, Union
5656
from warnings import warn
5757

5858
from ._helpers import JSONDateEncoder, open_or_return
@@ -401,6 +401,7 @@ def add(
401401
super().__setitem__(name, entry)
402402

403403

404+
@dataclass
404405
class Configuration:
405406
r"""Holds a parsed configuration.
406407
@@ -457,29 +458,20 @@ class Configuration:
457458
458459
""" # noqa
459460

460-
lines: list[str]
461-
entries: dict[str, Entry]
462-
path: str | None
463-
464-
_parameter_re = re.compile(
465-
r"^(?P<name>[a-z_.]+)(?: +(?!=)| *= *)(?P<value>.*?)"
466-
"[\\s\t]*"
467-
r"(?P<comment>#.*)?$"
468-
)
469-
470461
# Internally, lines property contains an updated list of all comments and
471462
# entries serialized. When adding a setting or updating an existing one,
472463
# the serialized line is updated accordingly. This allows to keep comments
473464
# and serialize only what's needed. Other lines are just written as-is.
474465

475-
def __init__(self, path: str | None = None) -> None:
476-
self.__dict__.update(
477-
dict(
478-
lines=[],
479-
entries=OrderedDict(),
480-
path=path,
481-
)
482-
)
466+
path: str | None = None
467+
lines: list[str] = field(default_factory=list, init=False)
468+
entries: dict[str, Entry] = field(default_factory=OrderedDict, init=False)
469+
470+
_parameter_re: ClassVar = re.compile(
471+
r"^(?P<name>[a-z_.]+)(?: +(?!=)| *= *)(?P<value>.*?)"
472+
"[\\s\t]*"
473+
r"(?P<comment>#.*)?$"
474+
)
483475

484476
def parse(self, fo: Iterable[str]) -> Iterator[tuple[pathlib.Path, IncludeType]]:
485477
for raw_line in fo:
@@ -552,13 +544,13 @@ def __iadd__(self, other: Any) -> Configuration:
552544

553545
def __getattr__(self, name: str) -> Value:
554546
try:
555-
return self[name]
547+
return self.entries[name].value
556548
except KeyError:
557549
raise AttributeError(name)
558550

559551
def __setattr__(self, name: str, value: Value) -> None:
560-
if name in self.__dict__:
561-
self.__dict__[name] = value
552+
if name in self.__dataclass_fields__:
553+
super().__setattr__(name, value)
562554
else:
563555
self[name] = value
564556

tests/test_conf.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,17 @@ def test_parser():
159159
parse(["bad_line"])
160160

161161

162+
def test_configuration_fields():
163+
"""Configuration fields (the ones from dataclass definition) can be changed."""
164+
from pgtoolkit.conf import Configuration
165+
166+
cfg = Configuration(path="my/postgresql.conf")
167+
assert cfg.path == "my/postgresql.conf"
168+
cfg.path = "changed/to/postgres.conf"
169+
assert cfg.path == "changed/to/postgres.conf"
170+
assert "path" not in cfg and "path" not in cfg.entries
171+
172+
162173
def test_configuration_multiple_entries():
163174
from pgtoolkit.conf import Configuration
164175

0 commit comments

Comments
 (0)