chore: improve dicts module

This commit is contained in:
sebthom
2025-05-13 20:42:24 +02:00
parent 1f9895850f
commit 9a3c0190ba
2 changed files with 38 additions and 14 deletions

View File

@@ -2,6 +2,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-ArtifactOfProjectHomePage: https://github.com/Second-Hand-Friends/kleinanzeigen-bot/
import copy, json, os # isort: skip
from collections import defaultdict
from collections.abc import Callable
from gettext import gettext as _
from importlib.resources import read_text as get_resource_as_string
@@ -12,6 +13,7 @@ from typing import Any, Final
from ruamel.yaml import YAML
from . import files, loggers # pylint: disable=cyclic-import
from .misc import K, V
LOG:Final[loggers.Logger] = loggers.get_logger(__name__)
@@ -23,30 +25,50 @@ def apply_defaults(
override:Callable[[Any, Any], bool] = lambda _k, _v: False
) -> dict[Any, Any]:
"""
>>> apply_defaults({}, {"foo": "bar"})
{'foo': 'bar'}
>>> apply_defaults({"foo": "foo"}, {"foo": "bar"})
{'foo': 'foo'}
>>> apply_defaults({"foo": ""}, {"foo": "bar"})
{'foo': ''}
>>> apply_defaults({}, {"foo": "bar"}, ignore = lambda k, _: k == "foo")
>>> apply_defaults({}, {'a': 'b'})
{'a': 'b'}
>>> apply_defaults({'a': 'b'}, {'a': 'c'})
{'a': 'b'}
>>> apply_defaults({'a': ''}, {'a': 'b'})
{'a': ''}
>>> apply_defaults({}, {'a': 'b'}, ignore = lambda k, _: k == 'a')
{}
>>> apply_defaults({"foo": ""}, {"foo": "bar"}, override = lambda _, v: v == "")
{'foo': 'bar'}
>>> apply_defaults({"foo": None}, {"foo": "bar"}, override = lambda _, v: v == "")
{'foo': None}
>>> apply_defaults({'a': ''}, {'a': 'b'}, override = lambda _, v: v == '')
{'a': 'b'}
>>> apply_defaults({'a': None}, {'a': 'b'}, override = lambda _, v: v == '')
{'a': None}
>>> apply_defaults({'a': {'x': 1}}, {'a': {'x': 0, 'y': 2}})
{'a': {'x': 1, 'y': 2}}
>>> apply_defaults({'a': {'b': False}}, {'a': { 'b': True}})
{'a': {'b': False}}
"""
for key, default_value in defaults.items():
if key in target:
if isinstance(target[key], dict) and isinstance(default_value, dict):
apply_defaults(target[key], default_value, ignore = ignore)
elif override(key, target[key]):
apply_defaults(
target = target[key],
defaults = default_value,
ignore = ignore,
override = override
)
elif override(key, target[key]): # force overwrite if override says so
target[key] = copy.deepcopy(default_value)
elif not ignore(key, default_value):
elif not ignore(key, default_value): # only set if not explicitly ignored
target[key] = copy.deepcopy(default_value)
return target
def defaultdict_to_dict(d: defaultdict[K, V]) -> dict[K, V]:
"""Recursively convert defaultdict to dict."""
result: dict[K, V] = {}
for key, value in d.items():
if isinstance(value, defaultdict):
result[key] = defaultdict_to_dict(value) # type: ignore[assignment]
else:
result[key] = value
return result
def load_dict(filepath:str, content_label:str = "") -> dict[str, Any]:
"""
:raises FileNotFoundError

View File

@@ -11,6 +11,8 @@ from . import i18n
# https://mypy.readthedocs.io/en/stable/generics.html#generic-functions
T = TypeVar("T")
K = TypeVar("K")
V = TypeVar("V")
def ensure(