Coverage for src/gentrie/nodes.py: 95%

32 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-08-17 11:24 -0700

1# -*- coding: utf-8 -*- 

2"""Internal node implementation for the gentrie package.""" 

3 

4from copy import deepcopy 

5from textwrap import indent 

6from typing import Any, Optional, TYPE_CHECKING 

7 

8from .protocols import TrieKeyToken 

9from .types import TrieId 

10 

11if TYPE_CHECKING: 

12 from .trie import GeneralizedTrie 

13 

14 

15class Node: # pylint: disable=too-few-public-methods 

16 """A node in the trie. 

17 

18 A node is a container for a key in the trie. It has a unique identifier 

19 and a reference to the key. 

20 

21 Attributes: 

22 ident (TrieId): Unique identifier for the key. 

23 token (TrieKeyToken): Token for the key. 

24 parent (Optional[GeneralizedTrie | Node): Reference to the parent node. 

25 children (dict[TrieKeyToken, Node]): Dictionary of child nodes. 

26 """ 

27 

28 def __init__(self, token: TrieKeyToken, parent: "GeneralizedTrie | Node", value: Optional[Any] = None) -> None: 

29 self.ident: Optional[TrieId] = None 

30 self.token: TrieKeyToken = token 

31 self.value: Optional[Any] = value 

32 self.parent: Optional["GeneralizedTrie | Node"] = parent 

33 self.children: dict[TrieKeyToken, "Node"] = {} 

34 

35 def __str__(self) -> str: 

36 """Generates a stringified version of the trie for visual examination. 

37 

38 The output IS NOT executable code but more in the nature of debug and testing support.""" 

39 output: list[str] = ["{"] 

40 if self.parent is None: 40 ↛ 41line 40 didn't jump to line 41 because the condition on line 40 was never true

41 output.append(" parent = None") 

42 else: 

43 from .trie import GeneralizedTrie # pylint: disable=import-outside-toplevel 

44 if isinstance(self.parent, GeneralizedTrie): 

45 output.append(" parent = root node") 

46 else: 

47 output.append(f" parent = {repr(self.parent.token)}") 

48 output.append(f" node token = {repr(self.token)}") 

49 if self.ident: 

50 output.append(f" trie id = {self.ident}") 

51 if self.children: 

52 output.append(" children = {") 

53 for child_key, child_value in self.children.items(): 

54 output.append(f" {repr(child_key)} = " + indent(str(child_value), " ").lstrip()) 

55 output.append(" }") 

56 output.append("}") 

57 return "\n".join(output) 

58 

59 def _as_dict(self) -> dict[str, Any]: 

60 """Converts the node to a dictionary representation. 

61 

62 This is useful for tests and debugging purposes and is not intended 

63 for general purpose serialization of the trie. It's output is not 

64 suitable for use with :func:`json.dumps()` or similar functions and 

65 is subject to change without notice. This is NOT a public API - it is 

66 intended for internal use by tests only. 

67 

68 Returns: 

69 :class:`dict[str, Any]`: Dictionary representation of the node. 

70 The dictionary contains the following keys: 

71 - "ident": The unique identifier of the node. 

72 - "token": The token of the node. 

73 - "value": The value associated with the node. 

74 - "parent": The token of the parent node, or None if there is no parent. 

75 - "children": A dictionary of child nodes, where the keys are the tokens 

76 of the child nodes and the values are dictionaries representing the child nodes. 

77 """ 

78 # pylint: disable=protected-access 

79 # Using deepcopy to ensure that the dictionary is a copy of the data in the trie, 

80 # not a dictionary of live references to it 

81 return deepcopy( 

82 { 

83 "ident": self.ident, 

84 "token": self.token, 

85 "value": self.value, 

86 "parent": self.parent.token if self.parent else None, 

87 "children": {str(k): v._as_dict() for k, v in self.children.items()}, 

88 } 

89 )