Skip to content

Commit deedd7b

Browse files
committed
perf(robot): optimize dataclass identity for ProjectIndex caching
Prepare VariableDefinition, KeywordDoc, and LibraryDoc for efficient use as dictionary keys in the upcoming ProjectIndex disk cache and cross-process worker communication.
1 parent 9215942 commit deedd7b

File tree

2 files changed

+387
-237
lines changed

2 files changed

+387
-237
lines changed

packages/robot/src/robotcode/robot/diagnostics/entities.py

Lines changed: 74 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import hashlib
12
from dataclasses import dataclass, field
23
from enum import Enum
34
from typing import (
@@ -36,6 +37,7 @@ def range(self) -> Range:
3637
def __hash__(self) -> int:
3738
return hash(
3839
(
40+
type(self),
3941
self.line_no,
4042
self.col_offset,
4143
self.end_line_no,
@@ -45,7 +47,7 @@ def __hash__(self) -> int:
4547
)
4648

4749

48-
@dataclass(slots=True)
50+
@dataclass(slots=True, eq=False)
4951
class Import(SourceEntity):
5052
name: Optional[str]
5153
name_token: Optional[Token]
@@ -115,7 +117,7 @@ class VariableDefinitionType(Enum):
115117
VARIABLE_NOT_FOUND = "variable not found"
116118

117119

118-
@dataclass(slots=True)
120+
@dataclass(slots=True, eq=False)
119121
class VariableDefinition(SourceEntity):
120122
name: str
121123
name_token: Optional[Token] # TODO: this is not needed anymore, but kept for compatibility
@@ -129,6 +131,47 @@ class VariableDefinition(SourceEntity):
129131
value_is_native: bool = field(default=False, compare=False)
130132
value_type: Optional[str] = field(default=None, compare=False)
131133

134+
_hash_value: int = field(default=0, init=False, compare=False, hash=False, repr=False)
135+
_stable_id: str = field(default="", init=False, compare=False, hash=False, repr=False)
136+
137+
def __post_init__(self) -> None:
138+
self._hash_value = hash(
139+
(
140+
type(self),
141+
self.type.value,
142+
self.name,
143+
self.source,
144+
self.line_no,
145+
self.col_offset,
146+
self.end_line_no,
147+
self.end_col_offset,
148+
)
149+
)
150+
151+
def _compute_stable_id(self) -> str:
152+
h = hashlib.sha256()
153+
h.update(
154+
"\0".join(
155+
(
156+
type(self).__qualname__,
157+
self.type.value,
158+
self.name,
159+
self.source or "",
160+
str(self.line_no),
161+
str(self.col_offset),
162+
str(self.end_line_no),
163+
str(self.end_col_offset),
164+
)
165+
).encode()
166+
)
167+
return h.hexdigest()
168+
169+
@property
170+
def stable_id(self) -> str:
171+
if not self._stable_id:
172+
self._stable_id = self._compute_stable_id()
173+
return self._stable_id
174+
132175
@property
133176
def matcher(self) -> VariableMatcher:
134177
return search_variable(self.name)
@@ -139,8 +182,22 @@ def convertable_name(self) -> str:
139182
value_type = f": {self.value_type}" if self.value_type else ""
140183
return f"{m.identifier}{{{m.base.strip()}{value_type}}}"
141184

185+
def __eq__(self, other: object) -> bool:
186+
if not isinstance(other, VariableDefinition):
187+
return NotImplemented
188+
return (
189+
type(self) is type(other)
190+
and self.type is other.type
191+
and self.name == other.name
192+
and self.source == other.source
193+
and self.line_no == other.line_no
194+
and self.col_offset == other.col_offset
195+
and self.end_line_no == other.end_line_no
196+
and self.end_col_offset == other.end_col_offset
197+
)
198+
142199
def __hash__(self) -> int:
143-
return hash((self.type, self.name, self.source, self.range))
200+
return self._hash_value
144201

145202
@property
146203
def name_range(self) -> Range:
@@ -157,74 +214,50 @@ def range(self) -> Range:
157214
)
158215

159216

160-
@dataclass(slots=True)
217+
@dataclass(slots=True, eq=False)
161218
class TestVariableDefinition(VariableDefinition):
162219
type: VariableDefinitionType = VariableDefinitionType.TEST_VARIABLE
163220

164-
def __hash__(self) -> int:
165-
return hash((self.type, self.name, self.source, self.range))
166-
167221

168-
@dataclass(slots=True)
222+
@dataclass(slots=True, eq=False)
169223
class LocalVariableDefinition(VariableDefinition):
170224
type: VariableDefinitionType = VariableDefinitionType.LOCAL_VARIABLE
171225

172-
def __hash__(self) -> int:
173-
return hash((self.type, self.name, self.source, self.range))
174-
175226

176-
@dataclass(slots=True)
227+
@dataclass(slots=True, eq=False)
177228
class GlobalVariableDefinition(VariableDefinition):
178229
type: VariableDefinitionType = VariableDefinitionType.GLOBAL_VARIABLE
179230

180-
def __hash__(self) -> int:
181-
return hash((self.type, self.name, self.source, self.range))
182-
183231

184-
@dataclass(slots=True)
232+
@dataclass(slots=True, eq=False)
185233
class BuiltInVariableDefinition(GlobalVariableDefinition):
186234
type: VariableDefinitionType = VariableDefinitionType.BUILTIN_VARIABLE
187235
resolvable: bool = True
188236

189-
def __hash__(self) -> int:
190-
return hash((self.type, self.name, self.source, self.range))
191237

192-
193-
@dataclass(slots=True)
238+
@dataclass(slots=True, eq=False)
194239
class CommandLineVariableDefinition(GlobalVariableDefinition):
195240
type: VariableDefinitionType = VariableDefinitionType.COMMAND_LINE_VARIABLE
196241
resolvable: bool = True
197242

198-
def __hash__(self) -> int:
199-
return hash((self.type, self.name, self.source, self.range))
200-
201243

202-
@dataclass(slots=True)
244+
@dataclass(slots=True, eq=False)
203245
class ArgumentDefinition(LocalVariableDefinition):
204246
type: VariableDefinitionType = VariableDefinitionType.ARGUMENT
205247
keyword_doc: Optional["KeywordDoc"] = field(default=None, compare=False, metadata={"nosave": True})
206248

207-
def __hash__(self) -> int:
208-
return hash((self.type, self.name, self.source, self.range))
209-
210249

211-
@dataclass(slots=True)
250+
@dataclass(slots=True, eq=False)
212251
class EmbeddedArgumentDefinition(ArgumentDefinition):
213252
pattern: Optional[str] = field(default=None, compare=False)
214253

215-
def __hash__(self) -> int:
216-
return hash((self.type, self.name, self.source, self.range))
217-
218254

219-
@dataclass(slots=True)
255+
@dataclass(slots=True, eq=False)
220256
class LibraryArgumentDefinition(ArgumentDefinition):
221257
type: VariableDefinitionType = VariableDefinitionType.LIBRARY_ARGUMENT
222258

223-
def __hash__(self) -> int:
224-
return hash((self.type, self.name, self.source, self.range))
225-
226259

227-
@dataclass(slots=True, frozen=True, eq=False, repr=False)
260+
@dataclass(slots=True, eq=False, repr=False)
228261
class NativeValue:
229262
value: Any
230263

@@ -235,34 +268,25 @@ def __str__(self) -> str:
235268
return str(self.value)
236269

237270

238-
@dataclass(slots=True)
271+
@dataclass(slots=True, eq=False)
239272
class ImportedVariableDefinition(VariableDefinition):
240273
type: VariableDefinitionType = VariableDefinitionType.IMPORTED_VARIABLE
241274
value: Optional[NativeValue] = field(default=None, compare=False)
242275

243-
def __hash__(self) -> int:
244-
return hash((self.type, self.name, self.source, self.range))
245-
246276

247-
@dataclass(slots=True)
277+
@dataclass(slots=True, eq=False)
248278
class EnvironmentVariableDefinition(VariableDefinition):
249279
type: VariableDefinitionType = VariableDefinitionType.ENVIRONMENT_VARIABLE
250280
resolvable: bool = True
251281

252282
default_value: Any = field(default=None, compare=False)
253283

254-
def __hash__(self) -> int:
255-
return hash((self.type, self.name, self.source, self.range))
256-
257284

258-
@dataclass(slots=True)
285+
@dataclass(slots=True, eq=False)
259286
class VariableNotFoundDefinition(VariableDefinition):
260287
type: VariableDefinitionType = VariableDefinitionType.VARIABLE_NOT_FOUND
261288
resolvable: bool = False
262289

263-
def __hash__(self) -> int:
264-
return hash((self.type, self.name, self.source, self.range))
265-
266290

267291
@dataclass(slots=True)
268292
class LibraryEntry:
@@ -339,6 +363,7 @@ class TestCaseDefinition(SourceEntity):
339363
def __hash__(self) -> int:
340364
return hash(
341365
(
366+
type(self),
342367
self.line_no,
343368
self.col_offset,
344369
self.end_line_no,
@@ -356,6 +381,7 @@ class TagDefinition(SourceEntity):
356381
def __hash__(self) -> int:
357382
return hash(
358383
(
384+
type(self),
359385
self.line_no,
360386
self.col_offset,
361387
self.end_line_no,

0 commit comments

Comments
 (0)