codekingpro/portable-devtools
114k
1# Copyright 2009-2024 Joshua Bronson. All rights reserved.2#3# This Source Code Form is subject to the terms of the Mozilla Public4# License, v. 2.0. If a copy of the MPL was not distributed with this5# file, You can obtain one at http://mozilla.org/MPL/2.0/.6 7 8# * Code review nav *9# (see comments in __init__.py)10# ============================================================================11# ← Prev: _frozen.py Current: _bidict.py Next: _orderedbase.py →12# ============================================================================13 14 15"""Provide :class:`MutableBidict` and :class:`bidict`."""16 17from __future__ import annotations18 19import typing as t20 21from ._abc import MutableBidirectionalMapping22from ._base import BidictBase23from ._dup import ON_DUP_DROP_OLD24from ._dup import ON_DUP_RAISE25from ._dup import OnDup26from ._typing import DT27from ._typing import KT28from ._typing import MISSING29from ._typing import ODT30from ._typing import VT31from ._typing import MapOrItems32 33 34class MutableBidict(BidictBase[KT, VT], MutableBidirectionalMapping[KT, VT]):35 """Base class for mutable bidirectional mappings."""36 37 if t.TYPE_CHECKING:38 39 @property40 def inverse(self) -> MutableBidict[VT, KT]: ...41 42 @property43 def inv(self) -> MutableBidict[VT, KT]: ...44 45 def _pop(self, key: KT) -> VT:46 val = self._fwdm.pop(key)47 del self._invm[val]48 return val49 50 def __delitem__(self, key: KT) -> None:51 """*x.__delitem__(y) ⟺ del x[y]*"""52 self._pop(key)53 54 def __setitem__(self, key: KT, val: VT) -> None:55 """Set the value for *key* to *val*.56 57 If *key* is already associated with *val*, this is a no-op.58 59 If *key* is already associated with a different value,60 the old value will be replaced with *val*,61 as with dict's :meth:`__setitem__`.62 63 If *val* is already associated with a different key,64 an exception is raised65 to protect against accidental removal of the key66 that's currently associated with *val*.67 68 Use :meth:`put` instead if you want to specify different behavior in69 the case that the provided key or value duplicates an existing one.70 Or use :meth:`forceput` to unconditionally associate *key* with *val*,71 replacing any existing items as necessary to preserve uniqueness.72 73 :raises bidict.ValueDuplicationError: if *val* duplicates that of an74 existing item.75 76 :raises bidict.KeyAndValueDuplicationError: if *key* duplicates the key of an77 existing item and *val* duplicates the value of a different78 existing item.79 """80 self.put(key, val, on_dup=self.on_dup)81 82 def put(self, key: KT, val: VT, on_dup: OnDup = ON_DUP_RAISE) -> None:83 """Associate *key* with *val*, honoring the :class:`OnDup` given in *on_dup*.84 85 For example, if *on_dup* is :attr:`~bidict.ON_DUP_RAISE`,86 then *key* will be associated with *val* if and only if87 *key* is not already associated with an existing value and88 *val* is not already associated with an existing key,89 otherwise an exception will be raised.90 91 If *key* is already associated with *val*, this is a no-op.92 93 :raises bidict.KeyDuplicationError: if attempting to insert an item94 whose key only duplicates an existing item's, and *on_dup.key* is95 :attr:`~bidict.RAISE`.96 97 :raises bidict.ValueDuplicationError: if attempting to insert an item98 whose value only duplicates an existing item's, and *on_dup.val* is99 :attr:`~bidict.RAISE`.100 101 :raises bidict.KeyAndValueDuplicationError: if attempting to insert an102 item whose key duplicates one existing item's, and whose value103 duplicates another existing item's, and *on_dup.val* is104 :attr:`~bidict.RAISE`.105 """106 self._update(((key, val),), on_dup=on_dup)107 108 def forceput(self, key: KT, val: VT) -> None:109 """Associate *key* with *val* unconditionally.110 111 Replace any existing mappings containing key *key* or value *val*112 as necessary to preserve uniqueness.113 """114 self.put(key, val, on_dup=ON_DUP_DROP_OLD)115 116 def clear(self) -> None:117 """Remove all items."""118 self._fwdm.clear()119 self._invm.clear()120 121 @t.overload122 def pop(self, key: KT, /) -> VT: ...123 @t.overload124 def pop(self, key: KT, default: DT = ..., /) -> VT | DT: ...125 126 def pop(self, key: KT, default: ODT[DT] = MISSING, /) -> VT | DT:127 """*x.pop(k[, d]) → v*128 129 Remove specified key and return the corresponding value.130 131 :raises KeyError: if *key* is not found and no *default* is provided.132 """133 try:134 return self._pop(key)135 except KeyError:136 if default is MISSING:137 raise138 return default139 140 def popitem(self) -> tuple[KT, VT]:141 """*x.popitem() → (k, v)*142 143 Remove and return some item as a (key, value) pair.144 145 :raises KeyError: if *x* is empty.146 """147 key, val = self._fwdm.popitem()148 del self._invm[val]149 return key, val150 151 def update(self, arg: MapOrItems[KT, VT] = (), /, **kw: VT) -> None:152 """Like calling :meth:`putall` with *self.on_dup* passed for *on_dup*."""153 self._update(arg, kw=kw)154 155 def forceupdate(self, arg: MapOrItems[KT, VT] = (), /, **kw: VT) -> None:156 """Like a bulk :meth:`forceput`."""157 self._update(arg, kw=kw, on_dup=ON_DUP_DROP_OLD)158 159 def putall(self, items: MapOrItems[KT, VT], on_dup: OnDup = ON_DUP_RAISE) -> None:160 """Like a bulk :meth:`put`.161 162 If one of the given items causes an exception to be raised,163 none of the items is inserted.164 """165 self._update(items, on_dup=on_dup)166 167 # other's type is Mapping rather than Maplike since bidict() |= SupportsKeysAndGetItem({})168 # raises a TypeError, just like dict() |= SupportsKeysAndGetItem({}) does.169 def __ior__(self, other: t.Mapping[KT, VT]) -> MutableBidict[KT, VT]:170 """Return self|=other."""171 self.update(other)172 return self173 174 175class bidict(MutableBidict[KT, VT]):176 """The main bidirectional mapping type.177 178 See :ref:`intro:Introduction` and :ref:`basic-usage:Basic Usage`179 to get started (also available at https://bidict.rtfd.io).180 """181 182 if t.TYPE_CHECKING:183 184 @property185 def inverse(self) -> bidict[VT, KT]: ...186 187 @property188 def inv(self) -> bidict[VT, KT]: ...189 190 191# * Code review nav *192# ============================================================================193# ← Prev: _frozen.py Current: _bidict.py Next: _orderedbase.py →194# ============================================================================195 