Coverage for src/gentrie/trie/removal.py: 96%
38 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-08-17 11:24 -0700
« prev ^ index » next coverage.py v7.6.10, created at 2025-08-17 11:24 -0700
1# -*- coding: utf-8 -*-
2"""Entry removal operations for the trie."""
3from typing import Optional
5from ..exceptions import ErrorTag, TrieKeyError, TrieTypeError
6from ..protocols import GeneralizedKey, TrieKeyToken
7from ..types import TrieId
8from ..validation import is_generalizedkey
10from .trie_mixins import TrieMixinsInterface
12# Disabled because pyright does not understand mixins
13# use of private attributes from the mixing class as declared
14# in the TrieMixinsInterface protocol.
15# pyright: reportPrivateUsage=false
18class TrieRemovalMixin:
19 """Mixin providing entry removal operations.
21 Note: This mixin accesses private attributes of the mixing class.
22 This is intentional and necessary for the mixin pattern
23 """
25 def remove(self: TrieMixinsInterface, key: TrieId | GeneralizedKey) -> None:
26 """Remove the specified key from the trie.
28 Removes the key from the trie. If the key is not found, it raises a KeyError.
29 The key can be specified either as a :class:`TrieId` or as a :class:`GeneralizedKey`.
31 Args:
32 key (TrieId | GeneralizedKey): identifier for key to remove.
34 Raises:
35 TrieTypeError: if the key arg is not a :class:`TrieId` or a valid :class:`GeneralizedKey`.
36 TrieKeyError: if the key arg does not match the id or trie key of any entries in the trie.
37 """
38 ident: Optional[TrieId] = None
39 if isinstance(key, TrieId):
40 ident = key
41 # If runtime validation is disabled, we just ASSUME the key is a GeneralizedKey
42 # if we got to here.
43 elif (not self.runtime_validation) or is_generalizedkey(key):
44 try:
45 ident = self[key].ident
46 except KeyError:
47 ident = None
48 except TypeError as exc:
49 raise RuntimeError("[GTR003] failed lookup of key because of unexpected exception") from exc
50 else:
51 raise TrieTypeError(
52 msg="key arg must be of type TrieId or a valid GeneralizedKey",
53 tag=ErrorTag.REMOVAL_INVALID_KEY_TYPE
54 )
56 if ident is None or ident not in self._trie_index:
57 raise TrieKeyError(
58 msg="key not found",
59 tag=ErrorTag.REMOVAL_KEY_NOT_FOUND)
61 # Get the node and delete its id from the trie index and entries
62 # and remove the node from the trie.
63 node = self._trie_index[ident]
64 del self._trie_index[ident]
65 del self._trie_entries[ident]
67 # Remove the id from the node
68 node.ident = None
70 # If the node still has other trie ids or children, we're done: return
71 if node.children:
72 return
74 # No trie ids or children are left for this node, so prune
75 # nodes up the trie tree as needed.
76 token: Optional[TrieKeyToken] = node.token
77 parent = node.parent
78 while parent is not None:
79 del parent.children[token]
80 # explicitly break possible cyclic references
81 node.parent = node.token = None
83 # If the parent node has a trie id or children, we're done: return
84 if parent.ident or parent.children:
85 return
86 # Keep purging nodes up the tree
87 token = parent.token
88 node = parent
89 parent = node.parent
90 return