• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pymorphy2-fork / DAWG-Python / 13367421021

17 Feb 2025 09:50AM UTC coverage: 86.722% (-0.6%) from 87.367%
13367421021

Pull #41

github

web-flow
Merge d05dde194 into 44e13ac5b
Pull Request #41: Add annotations to units.py, fix some other typing problems

168 of 218 branches covered (77.06%)

Branch coverage included in aggregate %.

64 of 71 new or added lines in 3 files covered. (90.14%)

2 existing lines in 1 file now uncovered.

668 of 746 relevant lines covered (89.54%)

5.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.93
/dawg_python/wrapper.py
1
from __future__ import annotations
6✔
2

3
import array
6✔
4
import struct
6✔
5
from typing import TYPE_CHECKING
6✔
6

7
from . import units
6✔
8

9
if TYPE_CHECKING:
6!
NEW
10
    from io import BytesIO
×
11

NEW
12
    from typing_extensions import Self
×
13

14

15
class Dictionary:
6✔
16
    """
17
    Dictionary class for retrieval and binary I/O.
18
    """
19

20
    def __init__(self) -> None:
6✔
21
        self._units = array.array("I")
6✔
22

23
    ROOT = 0
6✔
24
    "Root index"
4✔
25

26
    def has_value(self, index: int) -> bool:
6✔
27
        """Checks if a given index is related to the end of a key."""
28
        return units.has_leaf(self._units[index])
6✔
29

30
    def value(self, index: int) -> int:
6✔
31
        """Gets a value from a given index."""
32
        offset = units.offset(self._units[index])
6✔
33
        value_index = (index ^ offset) & units.PRECISION_MASK
6✔
34
        return units.value(self._units[value_index])
6✔
35

36
    def read(self, fp: BytesIO) -> None:
6✔
37
        """Reads a dictionary from an input stream."""
38
        base_size = struct.unpack("=I", fp.read(4))[0]
6✔
39
        self._units.fromfile(fp, base_size)
6✔
40

41
    def contains(self, key: bytes) -> bool:
6✔
42
        """Exact matching."""
43
        index = self.follow_bytes(key, self.ROOT)
6✔
44
        if index is None:
6✔
45
            return False
6✔
46
        return self.has_value(index)
6✔
47

48
    def find(self, key: bytes) -> int:
6✔
49
        """Exact matching (returns value)"""
50
        index = self.follow_bytes(key, self.ROOT)
6✔
51
        if index is None:
6!
52
            return -1
×
53
        if not self.has_value(index):
6✔
54
            return -1
6✔
55
        return self.value(index)
6✔
56

57
    def follow_char(self, label: int, index: int) -> int | None:
6✔
58
        """Follows a transition"""
59
        offset = units.offset(self._units[index])
6✔
60
        next_index = (index ^ offset ^ label) & units.PRECISION_MASK
6✔
61

62
        if units.label(self._units[next_index]) != label:
6✔
63
            return None
6✔
64

65
        return next_index
6✔
66

67
    def follow_bytes(self, s: bytes, index: int) -> int | None:
6✔
68
        """Follows transitions."""
69
        for ch in s:
6✔
70
            index = self.follow_char(ch, index)
6✔
71
            if index is None:
6✔
72
                return None
6✔
73

74
        return index
6✔
75

76
    @classmethod
6✔
77
    def load(cls, path) -> Self:
6✔
78
        dawg = cls()
6✔
79
        with open(path, "rb") as f:
6✔
80
            dawg.read(f)
6✔
81
        return dawg
6✔
82

83

84
class Guide:
6✔
85
    ROOT = 0
6✔
86

87
    def __init__(self) -> None:
6✔
88
        self._units = array.array("B")
6✔
89

90
    def child(self, index: int) -> int:
6✔
91
        return self._units[index * 2]
6✔
92

93
    def sibling(self, index: int) -> int:
6✔
94
        return self._units[index * 2 + 1]
6✔
95

96
    def read(self, fp: BytesIO) -> None:
6✔
97
        base_size = struct.unpack("=I", fp.read(4))[0]
6✔
98
        self._units.fromfile(fp, base_size * 2)
6✔
99

100
    def size(self) -> int:
6✔
101
        return len(self._units)
6✔
102

103

104
class Completer:
6✔
105
    _dic: Dictionary | None
6✔
106
    _guide: Guide | None
6✔
107

108
    def __init__(self, dic: Dictionary | None = None, guide: Guide | None = None) -> None:
6✔
109
        self._dic = dic
6✔
110
        self._guide = guide
6✔
111

112
    def value(self) -> int:
6✔
113
        return self._dic.value(self._last_index)
6✔
114

115
    def start(self, index: int, prefix: bytes = b"") -> None:
6✔
116
        self.key = bytearray(prefix)
6✔
117

118
        if self._guide.size():
6✔
119
            self._index_stack = [index]
6✔
120
            self._last_index = self._dic.ROOT
6✔
121
        else:
122
            self._index_stack = []
6✔
123

124
    def next(self) -> bool:
6✔
125
        "Gets the next key"
126

127
        if not self._index_stack:
6✔
128
            return False
6✔
129

130
        index = self._index_stack[-1]
6✔
131

132
        if self._last_index != self._dic.ROOT:
6✔
133
            child_label = self._guide.child(index)  # UCharType
6✔
134

135
            if child_label:
6✔
136
                # Follows a transition to the first child.
137
                index = self._follow(child_label, index)
6✔
138
                if index is None:
6!
139
                    return False
×
140
            else:
141
                while True:
4✔
142
                    sibling_label = self._guide.sibling(index)
6✔
143
                    # Moves to the previous node.
144
                    if len(self.key) > 0:
6✔
145
                        self.key.pop()
6✔
146

147
                    self._index_stack.pop()
6✔
148
                    if not self._index_stack:
6✔
149
                        return False
6✔
150

151
                    index = self._index_stack[-1]
6✔
152
                    if sibling_label:
6✔
153
                        # Follows a transition to the next sibling.
154
                        index = self._follow(sibling_label, index)
6✔
155
                        if index is None:
6!
156
                            return False
×
157
                        break
4✔
158

159
        return self._find_terminal(index)
6✔
160

161
    def _follow(self, label: int, index: int) -> int | None:
6✔
162
        next_index = self._dic.follow_char(label, index)
6✔
163
        if next_index is None:
6!
164
            return None
×
165

166
        self.key.append(label)
6✔
167
        self._index_stack.append(next_index)
6✔
168
        return next_index
6✔
169

170
    def _find_terminal(self, index: int) -> bool:
6✔
171
        while not self._dic.has_value(index):
6✔
172
            label = self._guide.child(index)
6✔
173

174
            index = self._dic.follow_char(label, index)
6✔
175
            if index is None:
6!
176
                return False
×
177

178
            self.key.append(label)
6✔
179
            self._index_stack.append(index)
6✔
180

181
        self._last_index = index
6✔
182
        return True
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc