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

pymorphy2-fork / DAWG-Python / 13370339198

17 Feb 2025 12:33PM CUT coverage: 89.367% (+2.0%) from 87.367%
13370339198

Pull #41

github

web-flow
Merge a881cb3b0 into 44e13ac5b
Pull Request #41: Add type annotations

155 of 188 branches covered (82.45%)

Branch coverage included in aggregate %.

82 of 90 new or added lines in 3 files covered. (91.11%)

2 existing lines in 1 file now uncovered.

635 of 696 relevant lines covered (91.24%)

5.47 hits per line

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

91.36
/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
×
NEW
11
    from pathlib import Path
×
12

NEW
13
    from typing_extensions import Self
×
14

15

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

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

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

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

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

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

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

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

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

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

66
        return next_index
6✔
67

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

75
        return index
6✔
76

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

84

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

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

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

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

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

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

104

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

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

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

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

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

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

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

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

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

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

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

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

160
        return self._find_terminal(index)
6✔
161

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

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

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

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

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

182
        self._last_index = index
6✔
183
        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