Last active
April 6, 2020 09:36
-
-
Save antonagestam/3a83c68fb2be41e099716190af8b964e to your computer and use it in GitHub Desktop.
A subclass of dict that filters keys and values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import ( | |
Any, | |
Callable, | |
Dict, | |
Generic, | |
Hashable, | |
Iterable, | |
Mapping, | |
Optional, | |
Tuple, | |
TypeVar, | |
Union, | |
overload, | |
) | |
__all__ = ("FilterDict",) | |
K = TypeVar("K", bound=Optional[Hashable]) | |
V = TypeVar("V") | |
InitIterable = Iterable[Tuple[K, V]] | |
# Better name? | |
InitSequence = Union[Mapping[K, V], InitIterable[K, V]] | |
class FilterDict(Dict[K, V], Generic[K, V]): | |
""" | |
TODO: docstring :PppPppP | |
""" | |
@overload | |
def _filter_seq_and_kwargs( | |
self, seq: None, kwargs: Mapping[str, V] | |
) -> Tuple[None, Dict[str, V]]: | |
... | |
@overload | |
def _filter_seq_and_kwargs( | |
self, seq: InitSequence[K, V], kwargs: Mapping[str, V] | |
) -> Tuple[Dict[K, V], Dict[str, V]]: | |
... | |
def _filter_seq_and_kwargs(self, seq, kwargs): | |
# Filter kwargs | |
kwargs = {k: v for k, v in kwargs if not self.filter(k, v)} | |
# Filter sequence | |
if seq is None: | |
mapping = None | |
else: | |
items = seq.items() if isinstance(seq, Mapping) else seq | |
mapping = {k: v for k, v in items if not self.filter(k, v)} | |
return mapping, kwargs | |
def __init__( | |
self, | |
filter_: Callable[[K, V], bool], | |
/, | |
seq: InitSequence[K, V] = (), | |
**kwargs: V, | |
) -> None: | |
self.filter = filter_ | |
seq, kwargs = self._filter_seq_and_kwargs(seq, kwargs) | |
super().__init__(seq, **kwargs) | |
def __setitem__(self, key: K, value: V) -> None: | |
if self.filter(key, value): | |
try: | |
del self[key] | |
except KeyError: | |
pass | |
else: | |
super().__setitem__(key, value) | |
@overload | |
def update(self, __m: Mapping[K, V], **kwargs: V) -> None: | |
... | |
@overload | |
def update(self, __m: InitIterable[K, V], **kwargs: V) -> None: | |
... | |
@overload | |
def update(self, **kwargs: V) -> None: | |
... | |
def update(self, __m=None, **kwargs) -> None: | |
seq, kwargs = self._filter_seq_and_kwargs(__m, kwargs) | |
if seq is None: | |
super().update(**kwargs) | |
else: | |
super().update(seq, **kwargs) | |
def setdefault(self, k: K, default: V = ...) -> V: | |
raise NotImplementedError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from unittest import TestCase | |
from utils.filterdict import FilterDict | |
class TestFilterDict(TestCase): | |
def test_filters_value_on_instantiation(self): | |
d = FilterDict(lambda _, v: v is None, {"a": None}) | |
self.assertEqual(d, {}) | |
def test_filters_value_on_set_item(self): | |
d = FilterDict(lambda _, v: v is None) | |
d["a"], d["b"] = "a", None | |
self.assertEqual(d, {"a": "a"}) | |
def test_filters_value_on_update(self): | |
d = FilterDict(lambda _, v: v is None) | |
d.update({"a": None}) | |
self.assertEqual(d, {}) | |
def test_filters_key_on_instantiation(self): | |
d = FilterDict(lambda k, _: k is None, {None: "a"}) | |
self.assertEqual({}, d) | |
def test_filters_key_on_set_item(self): | |
d = FilterDict(lambda k, _: k is None) | |
d[None] = "a" | |
self.assertEqual(d, {}) | |
def test_filters_key_on_update(self): | |
d = FilterDict(lambda k, _: k is None) | |
d.update({None: "a"}) | |
self.assertEqual(d, {}) | |
def test_set_item_deletes_on_filter_match(self): | |
d: dict = FilterDict(lambda _, v: v is None, {"a": "a"}) | |
d["a"] = None | |
self.assertEqual(d, {}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
No filter is applied when calling
.update()
:Something like this is missing:
(It would probably be nicer to modify
args
and/orkwargs
before callingsuper
though)