Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_orderedbase.py239 linesDownload Raw Back to bidict
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: _bidict.py      Current: _orderedbase.py   Next: _orderedbidict.py →12# ============================================================================13 14 15"""Provide :class:`OrderedBidictBase`."""16 17from __future__ import annotations18 19import typing as t20from weakref import ref as weakref21 22from ._base import BidictBase23from ._base import Unwrites24from ._bidict import bidict25from ._iter import iteritems26from ._typing import KT27from ._typing import MISSING28from ._typing import OKT29from ._typing import OVT30from ._typing import VT31from ._typing import MapOrItems32 33 34AT = t.TypeVar('AT')  # attr type35 36 37class WeakAttr(t.Generic[AT]):38    """Descriptor to automatically manage (de)referencing the given slot as a weakref.39 40    See https://docs.python.org/3/howto/descriptor.html#managed-attributes41    for an intro to using descriptors like this for managed attributes.42    """43 44    def __init__(self, *, slot: str) -> None:45        self.slot = slot46 47    def __set__(self, instance: t.Any, value: AT) -> None:48        setattr(instance, self.slot, weakref(value))49 50    def __get__(self, instance: t.Any, __owner: t.Any = None) -> AT:51        return t.cast(AT, getattr(instance, self.slot)())52 53 54class Node:55    """A node in a circular doubly-linked list56    used to encode the order of items in an ordered bidict.57 58    A weak reference to the previous node is stored59    to avoid creating strong reference cycles.60    Referencing/dereferencing the weakref is handled automatically by :class:`WeakAttr`.61    """62 63    prv: WeakAttr[Node] = WeakAttr(slot='_prv_weak')64    __slots__ = ('__weakref__', '_prv_weak', 'nxt')65 66    nxt: Node | WeakAttr[Node]  # Allow subclasses to use a WeakAttr for nxt too (see SentinelNode)67 68    def __init__(self, prv: Node, nxt: Node) -> None:69        self.prv = prv70        self.nxt = nxt71 72    def unlink(self) -> None:73        """Remove self from in between prv and nxt.74        Self's references to prv and nxt are retained so it can be relinked (see below).75        """76        self.prv.nxt = self.nxt77        self.nxt.prv = self.prv78 79    def relink(self) -> None:80        """Restore self between prv and nxt after unlinking (see above)."""81        self.prv.nxt = self.nxt.prv = self82 83 84class SentinelNode(Node):85    """Special node in a circular doubly-linked list86    that links the first node with the last node.87    When its next and previous references point back to itself88    it represents an empty list.89    """90 91    nxt: WeakAttr[Node] = WeakAttr(slot='_nxt_weak')92    __slots__ = ('_nxt_weak',)93 94    def __init__(self) -> None:95        super().__init__(self, self)96 97    def iternodes(self, *, reverse: bool = False) -> t.Iterator[Node]:98        """Iterator yielding nodes in the requested order."""99        attr = 'prv' if reverse else 'nxt'100        node = getattr(self, attr)101        while node is not self:102            yield node103            node = getattr(node, attr)104 105    def new_last_node(self) -> Node:106        """Create and return a new terminal node."""107        old_last = self.prv108        new_last = Node(old_last, self)109        old_last.nxt = self.prv = new_last110        return new_last111 112 113class OrderedBidictBase(BidictBase[KT, VT]):114    """Base class implementing an ordered :class:`BidirectionalMapping`."""115 116    _node_by_korv: bidict[t.Any, Node]117    _bykey: bool118 119    def __init__(self, arg: MapOrItems[KT, VT] = (), /, **kw: VT) -> None:120        """Make a new ordered bidirectional mapping.121        The signature behaves like that of :class:`dict`.122        Items passed in are added in the order they are passed,123        respecting the :attr:`~bidict.BidictBase.on_dup`124        class attribute in the process.125 126        The order in which items are inserted is remembered,127        similar to :class:`collections.OrderedDict`.128        """129        self._sntl = SentinelNode()130        self._node_by_korv = bidict()131        self._bykey = True132        super().__init__(arg, **kw)133 134    if t.TYPE_CHECKING:135 136        @property137        def inverse(self) -> OrderedBidictBase[VT, KT]: ...138 139        @property140        def inv(self) -> OrderedBidictBase[VT, KT]: ...141 142    def _make_inverse(self) -> OrderedBidictBase[VT, KT]:143        inv = t.cast(OrderedBidictBase[VT, KT], super()._make_inverse())144        inv._sntl = self._sntl145        inv._node_by_korv = self._node_by_korv146        inv._bykey = not self._bykey147        return inv148 149    def _assoc_node(self, node: Node, key: KT, val: VT) -> None:150        korv = key if self._bykey else val151        self._node_by_korv.forceput(korv, node)152 153    def _dissoc_node(self, node: Node) -> None:154        del self._node_by_korv.inverse[node]155        node.unlink()156 157    def _init_from(self, other: MapOrItems[KT, VT]) -> None:158        """See :meth:`BidictBase._init_from`."""159        super()._init_from(other)160        bykey = self._bykey161        korv_by_node = self._node_by_korv.inverse162        korv_by_node.clear()163        korv_by_node_set = korv_by_node.__setitem__164        self._sntl.nxt = self._sntl.prv = self._sntl165        new_node = self._sntl.new_last_node166        for k, v in iteritems(other):167            korv_by_node_set(new_node(), k if bykey else v)168 169    def _write(self, newkey: KT, newval: VT, oldkey: OKT[KT], oldval: OVT[VT], unwrites: Unwrites | None) -> None:170        """See :meth:`bidict.BidictBase._spec_write`."""171        super()._write(newkey, newval, oldkey, oldval, unwrites)172        assoc, dissoc = self._assoc_node, self._dissoc_node173        node_by_korv, bykey = self._node_by_korv, self._bykey174        if oldval is MISSING and oldkey is MISSING:  # no key or value duplication175            # {0: 1, 2: 3} | {4: 5} => {0: 1, 2: 3, 4: 5}176            newnode = self._sntl.new_last_node()177            assoc(newnode, newkey, newval)178            if unwrites is not None:179                unwrites.append((dissoc, newnode))180        elif oldval is not MISSING and oldkey is not MISSING:  # key and value duplication across two different items181            # {0: 1, 2: 3} | {0: 3} => {0: 3}182            #    n1, n2             =>   n1   (collapse n1 and n2 into n1)183            # oldkey: 2, oldval: 1, oldnode: n2, newkey: 0, newval: 3, newnode: n1184            if bykey:185                oldnode = node_by_korv[oldkey]186                newnode = node_by_korv[newkey]187            else:188                oldnode = node_by_korv[newval]189                newnode = node_by_korv[oldval]190            dissoc(oldnode)191            assoc(newnode, newkey, newval)192            if unwrites is not None:193                unwrites.extend((194                    (assoc, newnode, newkey, oldval),195                    (assoc, oldnode, oldkey, newval),196                    (oldnode.relink,),197                ))198        elif oldval is not MISSING:  # just key duplication199            # {0: 1, 2: 3} | {2: 4} => {0: 1, 2: 4}200            # oldkey: MISSING, oldval: 3, newkey: 2, newval: 4201            node = node_by_korv[newkey if bykey else oldval]202            assoc(node, newkey, newval)203            if unwrites is not None:204                unwrites.append((assoc, node, newkey, oldval))205        else:206            assert oldkey is not MISSING  # just value duplication207            # {0: 1, 2: 3} | {4: 3} => {0: 1, 4: 3}208            # oldkey: 2, oldval: MISSING, newkey: 4, newval: 3209            node = node_by_korv[oldkey if bykey else newval]210            assoc(node, newkey, newval)211            if unwrites is not None:212                unwrites.append((assoc, node, oldkey, newval))213 214    def __iter__(self) -> t.Iterator[KT]:215        """Iterator over the contained keys in insertion order."""216        return self._iter(reverse=False)217 218    def __reversed__(self) -> t.Iterator[KT]:219        """Iterator over the contained keys in reverse insertion order."""220        return self._iter(reverse=True)221 222    def _iter(self, *, reverse: bool = False) -> t.Iterator[KT]:223        nodes = self._sntl.iternodes(reverse=reverse)224        korv_by_node = self._node_by_korv.inverse225        if self._bykey:226            for node in nodes:227                yield korv_by_node[node]228        else:229            key_by_val = self._invm230            for node in nodes:231                val = korv_by_node[node]232                yield key_by_val[val]233 234 235#                             * Code review nav *236# ============================================================================237# ← Prev: _bidict.py      Current: _orderedbase.py   Next: _orderedbidict.py →238# ============================================================================239 
codekingpro/portable-devtools · Team Ai