From a05f9bef5a2a8ef8e8104a2e889504bc5e29ecd4 Mon Sep 17 00:00:00 2001 From: nicoo Date: Tue, 27 Apr 2021 19:14:16 +0200 Subject: [PATCH 1/2] config: Make `Config` objects immutable This cleans up a fair bit of the logic in here, and safeguards us against potential bugs. It is however a technically-breaking change. --- emanate/config.py | 49 ++++++++++++++++------------------------------- setup.cfg | 1 + 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/emanate/config.py b/emanate/config.py index aedd62a..cc9ecc8 100644 --- a/emanate/config.py +++ b/emanate/config.py @@ -10,12 +10,15 @@ from pathlib import Path from collections.abc import Iterable +from frozendict import frozendict + PATHS = frozenset(('destination', 'source',)) PATH_SETS = frozenset(('ignore',)) PATH_KEYS = PATHS.union(PATH_SETS) -class Config(dict): + +class Config(frozendict): """Simple wrapper around dict, allowing accessing values as attributes.""" def __getattr__(self, name): @@ -27,10 +30,6 @@ def __getattr__(self, name): return self[name] - def copy(self): - """Return a new Config, with the same contents as self.""" - return Config(self) - @classmethod def defaults(cls, src): """Return Emanate's default configuration. @@ -65,25 +64,15 @@ def resolve(self, rel_to): """ assert isinstance(rel_to, Path) assert rel_to.is_absolute() - result = self.copy() - for key in PATHS: - if key not in result: - continue - - assert isinstance(result[key], (str, Path)) - result[key] = rel_to / Path(result[key]).expanduser() - - for key in PATH_SETS: - if key not in result: - continue - - assert isinstance(result[key], Iterable) - assert all((isinstance(p, (Path, str)) for p in result[key])) - result[key] = frozenset((rel_to / Path(p).expanduser() for p in result[key])) - - return result + def _resolve(path): + return rel_to / Path(path).expanduser() + return self.copy(**{ + key: _resolve(v) if key in PATHS else frozenset(map(_resolve, v)) + for (key, v) in self.items() + if key in PATH_KEYS + }) def merge(*configs, strict_resolve=True): # pylint: disable=no-method-argument """Merge several Config objects. @@ -99,17 +88,11 @@ def _merge_one(config, other): if strict_resolve and not other.resolved: raise ValueError("Merging a non-resolved configuration") - config = config.copy() - for key, value in other.items(): - if value is None: - continue - - if key == 'ignore': - config[key] = config.get(key, frozenset()).union(value) - else: - config[key] = value - - return config + return config.copy(**other, **{ + key: config[key].union(other[key]) + for key in PATH_SETS + if key in config and key in other + }) return functools.reduce(_merge_one, filter(None, configs), Config()) diff --git a/setup.cfg b/setup.cfg index 7f6ca41..f54306b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,7 @@ python_requires = >=3.6 install_requires = dataclasses~=0.8.0; python_version < '3.7' importlib_metadata~=3.0; python_version < '3.8' + frozendict~=1.2 [options.extras_require] From 25371d307f4a1d6400ce857269772b66ac572c57 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Sun, 9 Apr 2023 05:46:01 -0400 Subject: [PATCH 2/2] Bump frozendict version --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 8b86766..5a54fbd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,7 +24,7 @@ packages = find: python_requires = >=3.8 install_requires = - frozendict~=1.2 + frozendict~=2.3 [options.extras_require]