source: trunk/src/allmydata/util/dictutil.py

Last change on this file was da43acf, checked in by Jean-Paul Calderone <exarkun@…>, at 2023-07-20T18:27:50Z

more accurate docstring for dictutil.filter

  • Property mode set to 100644
File size: 4.0 KB
Line 
1"""
2Tools to mess with dicts.
3"""
4
5from __future__ import annotations
6from typing import Callable, TypeVar
7
8K = TypeVar("K")
9V = TypeVar("V")
10
11def filter(pred: Callable[[V], bool], orig: dict[K, V]) -> dict[K, V]:
12    """
13    Filter out key/value pairs whose value fails to match a predicate.
14    """
15    return {
16        k: v
17        for (k, v)
18        in orig.items()
19        if pred(v)
20    }
21
22class DictOfSets(dict):
23    def add(self, key, value):
24        if key in self:
25            self[key].add(value)
26        else:
27            self[key] = set([value])
28
29    def update(self, otherdictofsets):
30        for key, values in list(otherdictofsets.items()):
31            if key in self:
32                self[key].update(values)
33            else:
34                self[key] = set(values)
35
36    def discard(self, key, value):
37        if not key in self:
38            return
39        self[key].discard(value)
40        if not self[key]:
41            del self[key]
42
43class AuxValueDict(dict):
44    """I behave like a regular dict, but each key is associated with two
45    values: the main value, and an auxilliary one. Setting the main value
46    (with the usual d[key]=value) clears the auxvalue. You can set both main
47    and auxvalue at the same time, and can retrieve the values separately.
48
49    The main use case is a dictionary that represents unpacked child values
50    for a directory node, where a common pattern is to modify one or more
51    children and then pass the dict back to a packing function. The original
52    packed representation can be cached in the auxvalue, and the packing
53    function can use it directly on all unmodified children. On large
54    directories with a complex packing function, this can save considerable
55    time."""
56
57    def __init__(self, *args, **kwargs):
58        super(AuxValueDict, self).__init__(*args, **kwargs)
59        self.auxilliary = {}
60
61    def __setitem__(self, key, value):
62        super(AuxValueDict, self).__setitem__(key, value)
63        self.auxilliary[key] = None # clear the auxvalue
64
65    def __delitem__(self, key):
66        super(AuxValueDict, self).__delitem__(key)
67        self.auxilliary.pop(key)
68
69    def get_aux(self, key, default=None):
70        """Retrieve the auxilliary value. There is no way to distinguish
71        between an auxvalue of 'None' and a key that does not have an
72        auxvalue, and get_aux() will not raise KeyError when called with a
73        missing key."""
74        return self.auxilliary.get(key, default)
75
76    def set_with_aux(self, key, value, auxilliary):
77        """Set both the main value and the auxilliary value. There is no way
78        to distinguish between an auxvalue of 'None' and a key that does not
79        have an auxvalue."""
80        super(AuxValueDict, self).__setitem__(key, value)
81        self.auxilliary[key] = auxilliary
82
83
84class _TypedKeyDict(dict):
85    """Dictionary that enforces key type.
86
87    Doesn't override everything, but probably good enough to catch most
88    problems.
89
90    Subclass and override KEY_TYPE.
91    """
92
93    KEY_TYPE = object
94
95    def __init__(self, *args, **kwargs):
96        dict.__init__(self, *args, **kwargs)
97        for key in self:
98            if not isinstance(key, self.KEY_TYPE):
99                raise TypeError("{} must be of type {}".format(
100                    repr(key), self.KEY_TYPE))
101
102
103def _make_enforcing_override(K, method_name):
104    def f(self, key, *args, **kwargs):
105        if not isinstance(key, self.KEY_TYPE):
106            raise TypeError("{} must be of type {}".format(
107                repr(key), self.KEY_TYPE))
108        return getattr(dict, method_name)(self, key, *args, **kwargs)
109    f.__name__ = method_name
110    setattr(K, method_name, f)
111
112for _method_name in ["__setitem__", "__getitem__", "setdefault", "get",
113                     "__delitem__"]:
114    _make_enforcing_override(_TypedKeyDict, _method_name)
115del _method_name
116
117
118class BytesKeyDict(_TypedKeyDict):
119    """Keys should be bytes."""
120
121    KEY_TYPE = bytes
122
123
124class UnicodeKeyDict(_TypedKeyDict):
125    """Keys should be unicode strings."""
126
127    KEY_TYPE = str
Note: See TracBrowser for help on using the repository browser.