omlish 0.0.0.dev264__py3-none-any.whl → 0.0.0.dev266__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. omlish/__about__.py +5 -5
  2. omlish/collections/__init__.py +5 -0
  3. omlish/collections/abc.py +24 -0
  4. omlish/collections/cache/descriptor.py +3 -0
  5. omlish/collections/cache/impl.py +3 -0
  6. omlish/collections/cache/types.py +3 -0
  7. omlish/collections/coerce.py +2 -1
  8. omlish/collections/frozen.py +3 -0
  9. omlish/collections/hasheq.py +3 -0
  10. omlish/collections/identity.py +3 -0
  11. omlish/collections/kv/__init__.py +51 -0
  12. omlish/collections/kv/base.py +55 -0
  13. omlish/collections/kv/capabilities.py +46 -0
  14. omlish/collections/kv/filtered.py +101 -0
  15. omlish/collections/kv/mappings.py +76 -0
  16. omlish/collections/kv/transformed.py +123 -0
  17. omlish/collections/kv/wrappers.py +99 -0
  18. omlish/collections/mappings.py +3 -0
  19. omlish/collections/ordered.py +3 -0
  20. omlish/collections/persistent/persistent.py +3 -0
  21. omlish/collections/persistent/treap.py +5 -1
  22. omlish/collections/persistent/treapmap.py +3 -0
  23. omlish/collections/sorted/sorted.py +3 -0
  24. omlish/collections/unmodifiable.py +3 -0
  25. omlish/inject/bindings.py +3 -0
  26. omlish/inject/eagers.py +3 -0
  27. omlish/inject/elements.py +4 -1
  28. omlish/inject/impl/elements.py +3 -0
  29. omlish/inject/impl/injector.py +3 -0
  30. omlish/inject/impl/multis.py +3 -0
  31. omlish/inject/impl/proxy.py +3 -0
  32. omlish/inject/impl/scopes.py +3 -0
  33. omlish/inject/injector.py +3 -0
  34. omlish/inject/inspect.py +3 -0
  35. omlish/inject/keys.py +3 -0
  36. omlish/inject/listeners.py +3 -0
  37. omlish/inject/multis.py +2 -0
  38. omlish/inject/origins.py +3 -0
  39. omlish/inject/overrides.py +3 -0
  40. omlish/inject/privates.py +3 -0
  41. omlish/inject/providers.py +3 -0
  42. omlish/inject/types.py +3 -0
  43. omlish/inject/utils.py +3 -0
  44. omlish/lang/__init__.py +2 -0
  45. omlish/lang/functions.py +14 -1
  46. omlish/lang/objects.py +18 -0
  47. omlish/term/codes.py +25 -17
  48. omlish/term/progressbar.py +29 -8
  49. {omlish-0.0.0.dev264.dist-info → omlish-0.0.0.dev266.dist-info}/METADATA +7 -7
  50. {omlish-0.0.0.dev264.dist-info → omlish-0.0.0.dev266.dist-info}/RECORD +54 -47
  51. {omlish-0.0.0.dev264.dist-info → omlish-0.0.0.dev266.dist-info}/WHEEL +0 -0
  52. {omlish-0.0.0.dev264.dist-info → omlish-0.0.0.dev266.dist-info}/entry_points.txt +0 -0
  53. {omlish-0.0.0.dev264.dist-info → omlish-0.0.0.dev266.dist-info}/licenses/LICENSE +0 -0
  54. {omlish-0.0.0.dev264.dist-info → omlish-0.0.0.dev266.dist-info}/top_level.txt +0 -0
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev264'
2
- __revision__ = '4a101c8e5b339dfa81732f13f8df74dec23b204f'
1
+ __version__ = '0.0.0.dev266'
2
+ __revision__ = '965387f5aa0862e8b1e30cbd03e24adb0a3e87bc'
3
3
 
4
4
 
5
5
  #
@@ -99,15 +99,15 @@ class Project(ProjectBase):
99
99
  'aiosqlite ~= 0.21',
100
100
  'asyncpg ~= 0.30',
101
101
 
102
- 'apsw ~= 3.47',
102
+ 'apsw ~= 3.49',
103
103
 
104
- 'sqlean.py ~= 3.45',
104
+ 'sqlean.py ~= 3.47',
105
105
 
106
106
  'duckdb ~= 1.2',
107
107
  ],
108
108
 
109
109
  'testing': [
110
- 'pytest ~= 8.0',
110
+ 'pytest ~= 8.3',
111
111
  ],
112
112
  }
113
113
 
@@ -57,6 +57,11 @@ from .identity import ( # noqa
57
57
  IdentityWeakSet,
58
58
  )
59
59
 
60
+ if _ta.TYPE_CHECKING:
61
+ from . import kv
62
+ else:
63
+ kv = _lang.proxy_import('.kv', __package__)
64
+
60
65
  from .mappings import ( # noqa
61
66
  MissingDict,
62
67
  TypeMap,
omlish/collections/abc.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # ruff: noqa: ANN204
2
2
 
3
+
3
4
  class Hashable:
4
5
  def __hash__(self): ...
5
6
 
@@ -156,3 +157,26 @@ class MutableMapping(Mapping):
156
157
 
157
158
 
158
159
  # endregion
160
+
161
+
162
+ # region Views
163
+
164
+
165
+ class MappingView(Sized):
166
+ @property
167
+ def _mapping(self): ... # noqa
168
+
169
+
170
+ class KeysView(MappingView, Set):
171
+ pass
172
+
173
+
174
+ class ValuesView(MappingView, Collection):
175
+ pass
176
+
177
+
178
+ class ItemsView(MappingView, Set):
179
+ pass
180
+
181
+
182
+ # endregion
@@ -15,6 +15,9 @@ C = ta.TypeVar('C', bound=ta.Callable)
15
15
  CC: ta.TypeAlias = ta.Callable[[C], C]
16
16
 
17
17
 
18
+ ##
19
+
20
+
18
21
  class Scope(enum.Enum):
19
22
  INSTANCE = 'INSTANCE'
20
23
  CLASS = 'CLASS'
@@ -30,6 +30,9 @@ V2 = ta.TypeVar('V2')
30
30
  log = logging.getLogger(__name__)
31
31
 
32
32
 
33
+ ##
34
+
35
+
33
36
  class SKIP(lang.Marker):
34
37
  pass
35
38
 
@@ -6,6 +6,9 @@ K = ta.TypeVar('K')
6
6
  V = ta.TypeVar('V')
7
7
 
8
8
 
9
+ ##
10
+
11
+
9
12
  class OverweightError(Exception):
10
13
  pass
11
14
 
@@ -12,7 +12,8 @@ K2 = ta.TypeVar('K2')
12
12
  V = ta.TypeVar('V')
13
13
  V2 = ta.TypeVar('V2')
14
14
 
15
- _map = map
15
+
16
+ ##
16
17
 
17
18
 
18
19
  def _unpack_fn(fn):
@@ -11,6 +11,9 @@ K = ta.TypeVar('K')
11
11
  V = ta.TypeVar('V')
12
12
 
13
13
 
14
+ ##
15
+
16
+
14
17
  class Frozen(ta.Hashable, abc.ABC):
15
18
  pass
16
19
 
@@ -16,6 +16,9 @@ K2 = ta.TypeVar('K2')
16
16
  V2 = ta.TypeVar('V2')
17
17
 
18
18
 
19
+ ##
20
+
21
+
19
22
  class HashEq(lang.Abstract, ta.Generic[K]):
20
23
  @abc.abstractmethod
21
24
  def hash(self, k: K) -> int:
@@ -11,6 +11,9 @@ K = ta.TypeVar('K')
11
11
  V = ta.TypeVar('V')
12
12
 
13
13
 
14
+ ##
15
+
16
+
14
17
  class IdentityWrapper(ta.Generic[T]):
15
18
  def __init__(self, value: T) -> None:
16
19
  super().__init__()
@@ -0,0 +1,51 @@
1
+ from .base import ( # noqa
2
+ Kv,
3
+ MutableKv,
4
+ )
5
+
6
+ from .capabilities import ( # noqa
7
+ Closeable,
8
+ close,
9
+ closing,
10
+
11
+ Flushable,
12
+ flush,
13
+ )
14
+
15
+ from .filtered import ( # noqa
16
+ KeyFilteredKv,
17
+ KeyFilteredMutableKv,
18
+
19
+ ValueFilteredKeyError,
20
+ ValueFilteredKv,
21
+ ValueFilteredMutableKv,
22
+ )
23
+
24
+ from .mappings import ( # noqa
25
+ MappingKv,
26
+ MappingMutableKv,
27
+
28
+ KvMapping,
29
+ KvMutableMapping,
30
+ )
31
+
32
+ from .transformed import ( # noqa
33
+ KeyTransformedKv,
34
+ KeyTransformedMutableKey,
35
+
36
+ ValueTransformedKv,
37
+ ValueTransformedMutableKv,
38
+ )
39
+
40
+ from .wrappers import ( # noqa
41
+ WrapperKv,
42
+
43
+ underlying,
44
+ underlying_of,
45
+
46
+ SimpleWrapperKv,
47
+ SimpleWrapperMutableKv,
48
+
49
+ UnmodifiableError,
50
+ UnmodifiableKv,
51
+ )
@@ -0,0 +1,55 @@
1
+ """
2
+ TODO:
3
+ - further decomp?
4
+ - Sized + Iterable?
5
+ - COM-style QueryInterface? :|
6
+ - OpKv
7
+ - table ala guava - (row key, column key) keys, sparse storage
8
+ - router
9
+ - value thunker idiom - for key-iterable-only storage, transform Kv[K, V], to Kv[K, ta.Callable[[], V]]
10
+ - zict classes
11
+ - AsyncBuffer
12
+ - Buffer
13
+ - Cache
14
+ - File
15
+ - Func
16
+ - LMDB
17
+ - LRU
18
+ - Sieve
19
+ - Zip
20
+ """
21
+ import abc
22
+ import typing as ta
23
+
24
+ from ... import lang
25
+
26
+
27
+ K = ta.TypeVar('K')
28
+ V = ta.TypeVar('V')
29
+
30
+
31
+ ##
32
+
33
+
34
+ class Kv(lang.Abstract, ta.Generic[K, V]):
35
+ @abc.abstractmethod
36
+ def __getitem__(self, k: K, /) -> V:
37
+ raise NotImplementedError
38
+
39
+ @abc.abstractmethod
40
+ def __len__(self) -> int:
41
+ raise NotImplementedError
42
+
43
+ @abc.abstractmethod
44
+ def items(self) -> ta.Iterator[tuple[K, V]]:
45
+ raise NotImplementedError
46
+
47
+
48
+ class MutableKv(Kv[K, V], lang.Abstract): # noqa
49
+ @abc.abstractmethod
50
+ def __setitem__(self, k: K, v: V, /) -> None:
51
+ raise NotImplementedError
52
+
53
+ @abc.abstractmethod
54
+ def __delitem__(self, k: K, /) -> None:
55
+ raise NotImplementedError
@@ -0,0 +1,46 @@
1
+ import abc
2
+ import contextlib
3
+ import typing as ta
4
+
5
+ from ... import lang
6
+ from .base import Kv
7
+ from .wrappers import underlying_of
8
+
9
+
10
+ KvT = ta.TypeVar('KvT', bound=Kv)
11
+
12
+
13
+ ##
14
+
15
+
16
+ class Closeable(lang.Abstract):
17
+ @abc.abstractmethod
18
+ def close(self) -> None:
19
+ pass
20
+
21
+
22
+ def close(root: Kv) -> None:
23
+ for c in underlying_of(root, Closeable): # type: ignore[type-abstract]
24
+ c.close()
25
+
26
+
27
+ @contextlib.contextmanager
28
+ def closing(kv: KvT) -> ta.Iterator[KvT]:
29
+ try:
30
+ yield kv
31
+ finally:
32
+ close(kv)
33
+
34
+
35
+ ##
36
+
37
+
38
+ class Flushable(lang.Abstract):
39
+ @abc.abstractmethod
40
+ def flush(self) -> None:
41
+ pass
42
+
43
+
44
+ def flush(root: Kv) -> None:
45
+ for c in underlying_of(root, Flushable): # type: ignore[type-abstract]
46
+ c.flush()
@@ -0,0 +1,101 @@
1
+ import typing as ta
2
+
3
+ from ... import lang
4
+ from .base import Kv
5
+ from .base import MutableKv
6
+ from .wrappers import SimpleWrapperKv
7
+ from .wrappers import SimpleWrapperMutableKv
8
+
9
+
10
+ K = ta.TypeVar('K')
11
+ V = ta.TypeVar('V')
12
+
13
+
14
+ ##
15
+
16
+
17
+ class KeyFilteredKv(SimpleWrapperKv[K, V]):
18
+ def __init__(
19
+ self,
20
+ u: Kv[K, V],
21
+ fn: ta.Callable[[K], bool],
22
+ ) -> None:
23
+ super().__init__(u)
24
+
25
+ self._fn = fn
26
+
27
+ def __getitem__(self, k: K, /) -> V:
28
+ if not self._fn(k):
29
+ raise KeyError(k)
30
+ return self._u[k]
31
+
32
+ def __len__(self) -> int:
33
+ return lang.ilen(self.items())
34
+
35
+ def items(self) -> ta.Iterator[tuple[K, V]]:
36
+ fn = self._fn
37
+ return filter(lambda t: fn(t[0]), self._u.items())
38
+
39
+
40
+ class KeyFilteredMutableKv(KeyFilteredKv[K, V], SimpleWrapperMutableKv[K, V]):
41
+ def __init__(
42
+ self,
43
+ u: MutableKv[K, V],
44
+ fn: ta.Callable[[K], bool],
45
+ ) -> None:
46
+ super().__init__(u, fn)
47
+
48
+ def __setitem__(self, k: K, v: V, /) -> None:
49
+ if not self._fn(k):
50
+ raise KeyError(k)
51
+ self._u[k] = v
52
+
53
+ def __delitem__(self, k: K, /) -> None:
54
+ if not self._fn(k):
55
+ raise KeyError(k)
56
+ del self._u[k]
57
+
58
+
59
+ ##
60
+
61
+
62
+ class ValueFilteredKeyError(KeyError):
63
+ pass
64
+
65
+
66
+ class ValueFilteredKv(SimpleWrapperKv[K, V]):
67
+ def __init__(
68
+ self,
69
+ u: Kv[K, V],
70
+ fn: ta.Callable[[V], bool],
71
+ ) -> None:
72
+ super().__init__(u)
73
+
74
+ self._fn = fn
75
+
76
+ def __getitem__(self, k: K, /) -> V:
77
+ v = self._u[k]
78
+ if not self._fn(v):
79
+ raise ValueFilteredKeyError(k)
80
+ return v
81
+
82
+ def __len__(self) -> int:
83
+ return lang.ilen(self.items())
84
+
85
+ def items(self) -> ta.Iterator[tuple[K, V]]:
86
+ fn = self._fn
87
+ return ((k, v) for k, v in self._u.items() if fn(v))
88
+
89
+
90
+ class ValueFilteredMutableKv(ValueFilteredKv[K, V], SimpleWrapperMutableKv[K, V]):
91
+ def __init__(
92
+ self,
93
+ u: MutableKv[K, V],
94
+ fn: ta.Callable[[V], bool],
95
+ ) -> None:
96
+ super().__init__(u, fn)
97
+
98
+ def __setitem__(self, k: K, v: V, /) -> None:
99
+ if not self._fn(v):
100
+ raise ValueFilteredKeyError(k)
101
+ self._u[k] = v
@@ -0,0 +1,76 @@
1
+ import typing as ta
2
+
3
+ from .base import Kv
4
+ from .base import MutableKv
5
+
6
+
7
+ K = ta.TypeVar('K')
8
+ V = ta.TypeVar('V')
9
+
10
+
11
+ ##
12
+
13
+
14
+ class MappingKv(Kv[K, V]):
15
+ def __init__(self, m: ta.Mapping[K, V]) -> None:
16
+ super().__init__()
17
+
18
+ self._m = m
19
+
20
+ def __getitem__(self, k: K, /) -> V:
21
+ return self._m[k]
22
+
23
+ def __len__(self) -> int:
24
+ return len(self._m)
25
+
26
+ def items(self) -> ta.Iterator[tuple[K, V]]:
27
+ return iter(self._m.items())
28
+
29
+
30
+ class MappingMutableKv(MappingKv[K, V], MutableKv[K, V]):
31
+ def __init__(self, m: ta.MutableMapping[K, V]) -> None:
32
+ super().__init__(m)
33
+
34
+ _m: ta.MutableMapping[K, V]
35
+
36
+ def __setitem__(self, k: K, v: V, /) -> None:
37
+ self._m[k] = v
38
+
39
+ def __delitem__(self, k: K, /) -> None:
40
+ del self._m[k]
41
+
42
+
43
+ ##
44
+
45
+
46
+ class KvMapping(ta.Mapping[K, V]):
47
+ def __init__(self, kv: Kv[K, V]) -> None:
48
+ super().__init__()
49
+
50
+ self._kv = kv
51
+
52
+ def __getitem__(self, key: K, /) -> V:
53
+ return self._kv[key]
54
+
55
+ def __len__(self) -> int:
56
+ return len(self._kv)
57
+
58
+ # FIXME: ItemsView
59
+ def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore[override]
60
+ return iter(self._kv.items())
61
+
62
+ def __iter__(self) -> ta.Iterator[K]:
63
+ return (k for k, v in self.items())
64
+
65
+
66
+ class KvMutableMapping(KvMapping[K, V], ta.MutableMapping[K, V]):
67
+ def __init__(self, kv: MutableKv[K, V]) -> None:
68
+ super().__init__(kv)
69
+
70
+ _kv: MutableKv[K, V]
71
+
72
+ def __setitem__(self, key: K, value: V, /) -> None:
73
+ self._kv[key] = value
74
+
75
+ def __delitem__(self, key: K, /) -> None:
76
+ del self._kv[key]
@@ -0,0 +1,123 @@
1
+ import typing as ta
2
+
3
+ from ... import check
4
+ from .base import Kv
5
+ from .base import MutableKv
6
+ from .wrappers import WrapperKv
7
+
8
+
9
+ K = ta.TypeVar('K')
10
+ V = ta.TypeVar('V')
11
+
12
+ KF = ta.TypeVar('KF')
13
+ KT = ta.TypeVar('KT')
14
+ VF = ta.TypeVar('VF')
15
+ VT = ta.TypeVar('VT')
16
+
17
+
18
+ ##
19
+
20
+
21
+ class KeyTransformedKv(WrapperKv[KT, V], ta.Generic[KT, KF, V]):
22
+ def __init__(
23
+ self,
24
+ u: Kv[KF, V],
25
+ *,
26
+ t_to_f: ta.Callable[[KT], KF] | None = None,
27
+ f_to_t: ta.Callable[[KF], KT] | None = None,
28
+ ) -> None:
29
+ super().__init__()
30
+
31
+ self._u = u
32
+ self._t_to_f = t_to_f
33
+ self._f_to_t = f_to_t
34
+
35
+ def underlying(self) -> ta.Sequence[Kv]:
36
+ return [self._u]
37
+
38
+ def __getitem__(self, k: KT, /) -> V:
39
+ fn = check.not_none(self._t_to_f)
40
+ return self._u[fn(k)]
41
+
42
+ def __len__(self) -> int:
43
+ return len(self._u)
44
+
45
+ def items(self) -> ta.Iterator[tuple[KT, V]]:
46
+ fn = check.not_none(self._f_to_t)
47
+ return ((fn(k), v) for k, v in self._u.items())
48
+
49
+
50
+ class KeyTransformedMutableKey(KeyTransformedKv[KT, KF, V], MutableKv[KT, V], ta.Generic[KT, KF, V]):
51
+ def __init__(
52
+ self,
53
+ u: MutableKv[KF, V],
54
+ *,
55
+ t_to_f: ta.Callable[[KT], KF] | None = None,
56
+ f_to_t: ta.Callable[[KF], KT] | None = None,
57
+ ) -> None:
58
+ super().__init__(
59
+ u,
60
+ t_to_f=t_to_f,
61
+ f_to_t=f_to_t,
62
+ )
63
+
64
+ _u: MutableKv[KF, V]
65
+
66
+ def __setitem__(self, k: KT, v: V, /) -> None:
67
+ fn = check.not_none(self._t_to_f)
68
+ self._u[fn(k)] = v
69
+
70
+ def __delitem__(self, k: KT, /) -> None:
71
+ fn = check.not_none(self._t_to_f)
72
+ del self._u[fn(k)]
73
+
74
+
75
+ ##
76
+
77
+
78
+ class ValueTransformedKv(WrapperKv[K, VT], ta.Generic[K, VT, VF]):
79
+ def __init__(
80
+ self,
81
+ u: Kv[K, VF],
82
+ f_to_t: ta.Callable[[VF], VT] | None = None,
83
+ ) -> None:
84
+ super().__init__()
85
+
86
+ self._u = u
87
+ self._f_to_t = f_to_t
88
+
89
+ def underlying(self) -> ta.Sequence[Kv]:
90
+ return [self._u]
91
+
92
+ def __getitem__(self, k: K, /) -> VT:
93
+ fn = check.not_none(self._f_to_t)
94
+ return fn(self._u[k])
95
+
96
+ def __len__(self) -> int:
97
+ return len(self._u)
98
+
99
+ def items(self) -> ta.Iterator[tuple[K, VT]]:
100
+ fn = check.not_none(self._f_to_t)
101
+ return ((k, fn(v)) for k, v in self._u.items())
102
+
103
+
104
+ class ValueTransformedMutableKv(ValueTransformedKv[K, VT, VF], MutableKv[K, VT], ta.Generic[K, VT, VF]):
105
+ def __init__(
106
+ self,
107
+ u: MutableKv[K, VF],
108
+ *,
109
+ f_to_t: ta.Callable[[VF], VT] | None = None,
110
+ t_to_f: ta.Callable[[VT], VF] | None = None,
111
+ ) -> None:
112
+ super().__init__(u, f_to_t)
113
+
114
+ self._t_to_f = t_to_f
115
+
116
+ _u: MutableKv[K, VF]
117
+
118
+ def __setitem__(self, k: K, v: VT, /) -> None:
119
+ fn = check.not_none(self._t_to_f)
120
+ self._u[k] = fn(v)
121
+
122
+ def __delitem__(self, k: K, /) -> None:
123
+ del self._u[k]
@@ -0,0 +1,99 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from ... import lang
5
+ from .base import Kv
6
+ from .base import MutableKv
7
+
8
+
9
+ K = ta.TypeVar('K')
10
+ V = ta.TypeVar('V')
11
+
12
+ T = ta.TypeVar('T')
13
+
14
+
15
+ ##
16
+
17
+
18
+ class WrapperKv(Kv[K, V], lang.Abstract):
19
+ @abc.abstractmethod
20
+ def underlying(self) -> ta.Iterable[Kv]:
21
+ raise NotImplementedError
22
+
23
+
24
+ ##
25
+
26
+
27
+ def underlying(
28
+ root: Kv,
29
+ *,
30
+ leaves_only: bool = False,
31
+ filter: ta.Callable[[Kv], bool] | None = None, # noqa
32
+ ) -> ta.Iterator[Kv]:
33
+ def rec(c):
34
+ if isinstance(c, WrapperKv):
35
+ if not leaves_only:
36
+ yield c
37
+ for n in c.underlying():
38
+ yield from rec(n)
39
+ else:
40
+ yield c
41
+
42
+ for u in rec(root):
43
+ if filter is not None and not filter(u):
44
+ continue
45
+ yield u
46
+
47
+
48
+ def underlying_of(root: Kv, cls: type[T]) -> ta.Iterator[T]:
49
+ return underlying(root, filter=lambda c: isinstance(c, cls)) # type: ignore[return-value]
50
+
51
+
52
+ ##
53
+
54
+
55
+ class SimpleWrapperKv(WrapperKv[K, V]):
56
+ def __init__(self, u: Kv[K, V]) -> None:
57
+ super().__init__()
58
+
59
+ self._u = u
60
+
61
+ def underlying(self) -> ta.Sequence[Kv]:
62
+ return [self._u]
63
+
64
+ def __getitem__(self, k: K, /) -> V:
65
+ return self._u[k]
66
+
67
+ def __len__(self) -> int:
68
+ return len(self._u)
69
+
70
+ def items(self) -> ta.Iterator[tuple[K, V]]:
71
+ return self._u.items()
72
+
73
+
74
+ class SimpleWrapperMutableKv(SimpleWrapperKv[K, V], MutableKv[K, V]):
75
+ def __init__(self, u: MutableKv[K, V]) -> None:
76
+ super().__init__(u)
77
+
78
+ _u: MutableKv[K, V]
79
+
80
+ def __setitem__(self, k: K, v: V, /) -> None:
81
+ self._u[k] = v
82
+
83
+ def __delitem__(self, k: K, /) -> None:
84
+ del self._u[k]
85
+
86
+
87
+ ##
88
+
89
+
90
+ class UnmodifiableError(Exception):
91
+ pass
92
+
93
+
94
+ class UnmodifiableKv(SimpleWrapperKv[K, V], MutableKv[K, V]):
95
+ def __setitem__(self, k: K, v: V, /) -> None:
96
+ raise UnmodifiableError
97
+
98
+ def __delitem__(self, k: K, /) -> None:
99
+ raise UnmodifiableError