robotcode-robot 2.6.0__tar.gz → 2.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/PKG-INFO +2 -2
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/pyproject.toml +1 -1
- robotcode_robot-2.6.1/src/robotcode/robot/__version__.py +1 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/data_cache.py +174 -55
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/document_cache_helper.py +17 -0
- robotcode_robot-2.6.0/src/robotcode/robot/__version__.py +0 -1
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/.gitignore +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/README.md +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/__init__.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/config/__init__.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/config/loader.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/config/model.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/config/utils.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/__init__.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/analyzer_result.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/diagnostic_rules.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/diagnostics_modifier.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/entities.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/errors.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/import_resolver.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/imports_manager.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/keyword_finder.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/library_doc.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/model_helper.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/namespace.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/namespace_analyzer.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/project_index.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/scope_tree.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/__init__.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/analyzer.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/enums.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/model.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/nodes.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/run_keyword.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/serialization.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/semantic_analyzer/variable_tokenizer.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/variable_scope.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/workspace_config.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/py.typed +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/__init__.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/ast.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/markdownformatter.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/match.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/robot_patching.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/robot_path.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/stubs.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/variables.py +0 -0
- {robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/visitor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotcode-robot
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.1
|
|
4
4
|
Summary: Support classes for RobotCode for handling Robot Framework projects.
|
|
5
5
|
Project-URL: Homepage, https://robotcode.io
|
|
6
6
|
Project-URL: Donate, https://opencollective.com/robotcode
|
|
@@ -26,7 +26,7 @@ Classifier: Topic :: Utilities
|
|
|
26
26
|
Classifier: Typing :: Typed
|
|
27
27
|
Requires-Python: >=3.10
|
|
28
28
|
Requires-Dist: platformdirs>=4.3
|
|
29
|
-
Requires-Dist: robotcode-core==2.6.
|
|
29
|
+
Requires-Dist: robotcode-core==2.6.1
|
|
30
30
|
Requires-Dist: robotframework>=5.0.0
|
|
31
31
|
Requires-Dist: tomli>=1.1.0; python_version < '3.11'
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.6.1"
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/data_cache.py
RENAMED
|
@@ -2,11 +2,14 @@ import os
|
|
|
2
2
|
import pickle
|
|
3
3
|
import sqlite3
|
|
4
4
|
import sys
|
|
5
|
+
import threading
|
|
5
6
|
from contextlib import contextmanager
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from enum import Enum
|
|
8
9
|
from pathlib import Path
|
|
9
|
-
from typing import Any, Generic, Iterator, List, Optional, Tuple, Type, TypeVar, Union
|
|
10
|
+
from typing import Any, Callable, Generic, Iterator, List, Optional, Tuple, Type, TypeVar, Union
|
|
11
|
+
|
|
12
|
+
from robotcode.core.utils.logging import LoggingDescriptor
|
|
10
13
|
|
|
11
14
|
from ..utils import get_robot_version_str
|
|
12
15
|
|
|
@@ -19,6 +22,7 @@ else:
|
|
|
19
22
|
|
|
20
23
|
_M = TypeVar("_M")
|
|
21
24
|
_D = TypeVar("_D")
|
|
25
|
+
_R = TypeVar("_R")
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
class CacheSection(Enum):
|
|
@@ -38,14 +42,14 @@ class CacheEntry(Generic[_M, _D]):
|
|
|
38
42
|
|
|
39
43
|
def __init__(
|
|
40
44
|
self,
|
|
41
|
-
|
|
45
|
+
cache: "SqliteDataCache",
|
|
42
46
|
section: "CacheSection",
|
|
43
47
|
entry_name: str,
|
|
44
48
|
meta_blob: Optional[bytes],
|
|
45
49
|
meta_type: Union[Type[_M], Tuple[Type[_M], ...]],
|
|
46
50
|
data_type: Union[Type[_D], Tuple[Type[_D], ...]],
|
|
47
51
|
) -> None:
|
|
48
|
-
self.
|
|
52
|
+
self._cache = cache
|
|
49
53
|
self._section = section
|
|
50
54
|
self._entry_name = entry_name
|
|
51
55
|
self._meta_blob = meta_blob
|
|
@@ -70,10 +74,7 @@ class CacheEntry(Generic[_M, _D]):
|
|
|
70
74
|
@property
|
|
71
75
|
def data(self) -> _D:
|
|
72
76
|
if not self._data_loaded:
|
|
73
|
-
row = self.
|
|
74
|
-
f"SELECT data FROM {self._section.value} WHERE entry_name = ?",
|
|
75
|
-
(self._entry_name,),
|
|
76
|
-
).fetchone()
|
|
77
|
+
row = self._cache._fetch_data(self._section, self._entry_name)
|
|
77
78
|
if row is None:
|
|
78
79
|
raise RuntimeError(f"Cache entry '{self._entry_name}' disappeared from DB")
|
|
79
80
|
result = pickle.loads(row[0])
|
|
@@ -247,16 +248,50 @@ def build_cache_dir(base_path: Path) -> Path:
|
|
|
247
248
|
)
|
|
248
249
|
|
|
249
250
|
|
|
251
|
+
# Primary SQLite result codes for an unusable database file.
|
|
252
|
+
_SQLITE_CORRUPT = 11
|
|
253
|
+
_SQLITE_NOTADB = 26
|
|
254
|
+
|
|
255
|
+
# Substring markers used to recognize corruption when no error code is available
|
|
256
|
+
# (Python < 3.11) or when the error is raised by the sqlite3 module itself rather
|
|
257
|
+
# than the engine (e.g. "Could not decode to UTF-8" on a corrupt TEXT column, which
|
|
258
|
+
# carries no error code on any version). SQLite error strings are fixed and not
|
|
259
|
+
# localized, so substring matching is safe.
|
|
260
|
+
_CORRUPTION_MESSAGE_MARKERS = ("malformed", "not a database", "disk image", "corrupt", "decode")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _is_corruption(exc: sqlite3.DatabaseError) -> bool:
|
|
264
|
+
"""Whether the error means the database file itself is corrupt/unusable.
|
|
265
|
+
|
|
266
|
+
A locked database, a closed connection (``ProgrammingError``) or a constraint
|
|
267
|
+
violation are *not* corruption and must not trigger a destructive rebuild.
|
|
268
|
+
"""
|
|
269
|
+
code = getattr(exc, "sqlite_errorcode", None) # available since Python 3.11
|
|
270
|
+
if code is not None:
|
|
271
|
+
return (code & 0xFF) in (_SQLITE_CORRUPT, _SQLITE_NOTADB)
|
|
272
|
+
message = str(exc).lower()
|
|
273
|
+
return any(marker in message for marker in _CORRUPTION_MESSAGE_MARKERS)
|
|
274
|
+
|
|
275
|
+
|
|
250
276
|
class SqliteDataCache:
|
|
251
277
|
"""Cache backend using a single SQLite database with per-section tables.
|
|
252
278
|
|
|
253
279
|
Each CacheSection gets its own table with entry_name as PK, plus meta and data
|
|
254
280
|
BLOB columns. An app_version is stored in a metadata table; on version mismatch
|
|
255
281
|
all tables are dropped and recreated.
|
|
282
|
+
|
|
283
|
+
All access to the single shared connection is serialized through a lock, and a
|
|
284
|
+
corrupt database (``sqlite3.DatabaseError``, e.g. "database disk image is
|
|
285
|
+
malformed") is detected and rebuilt from scratch instead of propagating to
|
|
286
|
+
callers.
|
|
256
287
|
"""
|
|
257
288
|
|
|
289
|
+
_logger = LoggingDescriptor()
|
|
290
|
+
|
|
258
291
|
def __init__(self, cache_dir: Path, app_version: str = "") -> None:
|
|
259
292
|
self.cache_dir = cache_dir
|
|
293
|
+
self._app_version = app_version
|
|
294
|
+
self._lock = threading.Lock()
|
|
260
295
|
|
|
261
296
|
if not cache_dir.exists():
|
|
262
297
|
cache_dir.mkdir(parents=True)
|
|
@@ -267,25 +302,84 @@ class SqliteDataCache:
|
|
|
267
302
|
|
|
268
303
|
self._lock_fd = _acquire_shared_lock(cache_dir)
|
|
269
304
|
|
|
270
|
-
|
|
271
|
-
|
|
305
|
+
try:
|
|
306
|
+
self._open()
|
|
307
|
+
except sqlite3.DatabaseError as e:
|
|
308
|
+
if not _is_corruption(e):
|
|
309
|
+
raise
|
|
310
|
+
self._rebuild()
|
|
311
|
+
|
|
312
|
+
def _open(self, *, in_memory: bool = False) -> None:
|
|
313
|
+
"""Open the connection, configure it, and ensure the schema exists."""
|
|
314
|
+
self._conn = sqlite3.connect(":memory:" if in_memory else str(self.db_path), check_same_thread=False)
|
|
272
315
|
self._conn.execute("PRAGMA journal_mode=WAL")
|
|
273
316
|
self._conn.execute("PRAGMA synchronous=NORMAL")
|
|
274
317
|
self._conn.execute("PRAGMA cache_size=-8000")
|
|
275
|
-
self._conn.execute("PRAGMA
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
318
|
+
self._conn.execute("PRAGMA busy_timeout=5000")
|
|
319
|
+
# Memory-mapped reads race with a concurrent writer extending the file on
|
|
320
|
+
# macOS/APFS and can persist a torn page ("database disk image is
|
|
321
|
+
# malformed"); keep mmap only where the unified page cache makes it safe.
|
|
322
|
+
self._conn.execute(f"PRAGMA mmap_size={0 if sys.platform == 'darwin' else 67108864}")
|
|
323
|
+
self._ensure_schema()
|
|
324
|
+
|
|
325
|
+
def _purge_db_files(self) -> None:
|
|
326
|
+
# Best-effort: on Windows another process (a second editor window) may hold
|
|
327
|
+
# cache.db open, so unlink can raise PermissionError. Failing to delete must
|
|
328
|
+
# not abort recovery - _rebuild falls back to an in-memory cache.
|
|
329
|
+
for suffix in ("", "-wal", "-shm"):
|
|
330
|
+
try:
|
|
331
|
+
(self.cache_dir / f"cache.db{suffix}").unlink(missing_ok=True)
|
|
332
|
+
except OSError:
|
|
333
|
+
pass
|
|
334
|
+
|
|
335
|
+
def _rebuild(self) -> None:
|
|
336
|
+
"""Discard a corrupt database and reopen an empty one.
|
|
337
|
+
|
|
338
|
+
If the corrupt file cannot be removed or reopened (e.g. another process holds
|
|
339
|
+
it open), fall back to a transient in-memory database so the cache stays usable
|
|
340
|
+
for this session instead of taking down the language server.
|
|
341
|
+
"""
|
|
342
|
+
self._logger.warning(lambda: f"Cache database {self.db_path} is corrupt, rebuilding it from scratch.")
|
|
343
|
+
conn = getattr(self, "_conn", None)
|
|
344
|
+
if conn is not None:
|
|
345
|
+
try:
|
|
346
|
+
conn.close()
|
|
347
|
+
except sqlite3.Error:
|
|
348
|
+
pass
|
|
349
|
+
self._purge_db_files()
|
|
350
|
+
try:
|
|
351
|
+
self._open()
|
|
352
|
+
except sqlite3.DatabaseError as e:
|
|
353
|
+
if not _is_corruption(e):
|
|
354
|
+
raise
|
|
355
|
+
self._logger.warning(
|
|
356
|
+
lambda: f"Could not rebuild cache database {self.db_path}; using a temporary in-memory cache."
|
|
357
|
+
)
|
|
358
|
+
self._open(in_memory=True)
|
|
359
|
+
|
|
360
|
+
def _run(self, operation: Callable[[], _R]) -> _R:
|
|
361
|
+
"""Run a DB operation under the connection lock, rebuilding the cache once if it is corrupt."""
|
|
362
|
+
with self._lock:
|
|
363
|
+
try:
|
|
364
|
+
return operation()
|
|
365
|
+
except sqlite3.DatabaseError as e:
|
|
366
|
+
if not _is_corruption(e):
|
|
367
|
+
raise
|
|
368
|
+
self._rebuild()
|
|
369
|
+
return operation()
|
|
370
|
+
|
|
371
|
+
def _ensure_schema(self) -> None:
|
|
280
372
|
self._conn.execute("CREATE TABLE IF NOT EXISTS _meta ( key TEXT PRIMARY KEY, value TEXT NOT NULL)")
|
|
281
373
|
|
|
282
374
|
row = self._conn.execute("SELECT value FROM _meta WHERE key = 'app_version'").fetchone()
|
|
283
375
|
stored_version = row[0] if row else None
|
|
284
376
|
|
|
285
|
-
if stored_version !=
|
|
377
|
+
if stored_version != self._app_version:
|
|
286
378
|
for table in _TABLE_NAMES:
|
|
287
379
|
self._conn.execute(f"DROP TABLE IF EXISTS {table}")
|
|
288
|
-
self._conn.execute(
|
|
380
|
+
self._conn.execute(
|
|
381
|
+
"INSERT OR REPLACE INTO _meta (key, value) VALUES ('app_version', ?)", (self._app_version,)
|
|
382
|
+
)
|
|
289
383
|
|
|
290
384
|
for table in _TABLE_NAMES:
|
|
291
385
|
self._conn.execute(
|
|
@@ -305,15 +399,25 @@ class SqliteDataCache:
|
|
|
305
399
|
meta_type: Union[Type[_M], Tuple[Type[_M], ...]],
|
|
306
400
|
data_type: Union[Type[_D], Tuple[Type[_D], ...]],
|
|
307
401
|
) -> Optional[CacheEntry[_M, _D]]:
|
|
308
|
-
row = self.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
402
|
+
row = self._run(
|
|
403
|
+
lambda: self._conn.execute(
|
|
404
|
+
f"SELECT meta FROM {section.value} WHERE entry_name = ?",
|
|
405
|
+
(entry_name,),
|
|
406
|
+
).fetchone()
|
|
407
|
+
)
|
|
312
408
|
|
|
313
409
|
if row is None:
|
|
314
410
|
return None
|
|
315
411
|
|
|
316
|
-
return CacheEntry(self
|
|
412
|
+
return CacheEntry(self, section, entry_name, row[0], meta_type, data_type)
|
|
413
|
+
|
|
414
|
+
def _fetch_data(self, section: CacheSection, entry_name: str) -> Optional[Any]:
|
|
415
|
+
return self._run(
|
|
416
|
+
lambda: self._conn.execute(
|
|
417
|
+
f"SELECT data FROM {section.value} WHERE entry_name = ?",
|
|
418
|
+
(entry_name,),
|
|
419
|
+
).fetchone()
|
|
420
|
+
)
|
|
317
421
|
|
|
318
422
|
def save_entry(
|
|
319
423
|
self,
|
|
@@ -324,19 +428,24 @@ class SqliteDataCache:
|
|
|
324
428
|
) -> None:
|
|
325
429
|
meta_blob = pickle.dumps(meta, protocol=pickle.HIGHEST_PROTOCOL) if meta is not None else None
|
|
326
430
|
data_blob = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
431
|
+
|
|
432
|
+
def op() -> None:
|
|
433
|
+
self._conn.execute(
|
|
434
|
+
f"INSERT INTO {section.value} (entry_name, meta, data)"
|
|
435
|
+
f" VALUES (?, ?, ?)"
|
|
436
|
+
f" ON CONFLICT(entry_name) DO UPDATE SET"
|
|
437
|
+
f" meta = excluded.meta, data = excluded.data, modified_at = CURRENT_TIMESTAMP",
|
|
438
|
+
(entry_name, meta_blob, data_blob),
|
|
439
|
+
)
|
|
440
|
+
self._conn.commit()
|
|
441
|
+
|
|
442
|
+
self._run(op)
|
|
335
443
|
|
|
336
444
|
def close(self) -> None:
|
|
337
|
-
self.
|
|
338
|
-
|
|
339
|
-
|
|
445
|
+
with self._lock:
|
|
446
|
+
self._conn.close()
|
|
447
|
+
fd, self._lock_fd = self._lock_fd, None
|
|
448
|
+
_release_lock(fd)
|
|
340
449
|
|
|
341
450
|
@property
|
|
342
451
|
def db_path(self) -> Path:
|
|
@@ -344,17 +453,19 @@ class SqliteDataCache:
|
|
|
344
453
|
|
|
345
454
|
@property
|
|
346
455
|
def app_version(self) -> Optional[str]:
|
|
347
|
-
row = self._conn.execute("SELECT value FROM _meta WHERE key = 'app_version'").fetchone()
|
|
456
|
+
row = self._run(lambda: self._conn.execute("SELECT value FROM _meta WHERE key = 'app_version'").fetchone())
|
|
348
457
|
return row[0] if row else None
|
|
349
458
|
|
|
350
459
|
def get_section_stats(self, section: CacheSection) -> "SectionStats":
|
|
351
|
-
row = self.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
460
|
+
row = self._run(
|
|
461
|
+
lambda: self._conn.execute(
|
|
462
|
+
f"SELECT COUNT(*),"
|
|
463
|
+
f" COALESCE(SUM(LENGTH(meta) + LENGTH(data)), 0),"
|
|
464
|
+
f" MIN(created_at),"
|
|
465
|
+
f" MAX(modified_at)"
|
|
466
|
+
f" FROM {section.value}",
|
|
467
|
+
).fetchone()
|
|
468
|
+
)
|
|
358
469
|
assert row is not None
|
|
359
470
|
return SectionStats(
|
|
360
471
|
section=section,
|
|
@@ -365,12 +476,14 @@ class SqliteDataCache:
|
|
|
365
476
|
)
|
|
366
477
|
|
|
367
478
|
def list_entries(self, section: CacheSection) -> List["EntryInfo"]:
|
|
368
|
-
rows = self.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
479
|
+
rows = self._run(
|
|
480
|
+
lambda: self._conn.execute(
|
|
481
|
+
f"SELECT entry_name, created_at, modified_at,"
|
|
482
|
+
f" LENGTH(meta), LENGTH(data)"
|
|
483
|
+
f" FROM {section.value}"
|
|
484
|
+
f" ORDER BY entry_name",
|
|
485
|
+
).fetchall()
|
|
486
|
+
)
|
|
374
487
|
return [
|
|
375
488
|
EntryInfo(
|
|
376
489
|
entry_name=r[0],
|
|
@@ -383,17 +496,23 @@ class SqliteDataCache:
|
|
|
383
496
|
]
|
|
384
497
|
|
|
385
498
|
def clear_section(self, section: CacheSection) -> int:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
499
|
+
def op() -> int:
|
|
500
|
+
cursor = self._conn.execute(f"DELETE FROM {section.value}")
|
|
501
|
+
self._conn.commit()
|
|
502
|
+
return cursor.rowcount
|
|
503
|
+
|
|
504
|
+
return self._run(op)
|
|
389
505
|
|
|
390
506
|
def clear_all(self) -> int:
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
507
|
+
def op() -> int:
|
|
508
|
+
total = 0
|
|
509
|
+
for table in _TABLE_NAMES:
|
|
510
|
+
cursor = self._conn.execute(f"DELETE FROM {table}")
|
|
511
|
+
total += cursor.rowcount
|
|
512
|
+
self._conn.commit()
|
|
513
|
+
return total
|
|
514
|
+
|
|
515
|
+
return self._run(op)
|
|
397
516
|
|
|
398
517
|
|
|
399
518
|
@dataclass
|
|
@@ -303,6 +303,23 @@ class DocumentsCacheHelper:
|
|
|
303
303
|
|
|
304
304
|
return self.get_general_model(document)
|
|
305
305
|
|
|
306
|
+
def get_uncached_model(self, document: TextDocument) -> ast.AST:
|
|
307
|
+
"""Build a fresh model that is never cached on the document.
|
|
308
|
+
|
|
309
|
+
The cached model returned by `get_model` is shared between all features.
|
|
310
|
+
Consumers that mutate the model in place (e.g. the Robocop formatter) must
|
|
311
|
+
use this instead, otherwise the mutations corrupt the cached model and break
|
|
312
|
+
subsequent operations.
|
|
313
|
+
"""
|
|
314
|
+
document_type = self.get_document_type(document)
|
|
315
|
+
|
|
316
|
+
if document_type == DocumentType.INIT:
|
|
317
|
+
return self.__get_init_model(document)
|
|
318
|
+
if document_type == DocumentType.RESOURCE:
|
|
319
|
+
return self.__get_resource_model(document)
|
|
320
|
+
|
|
321
|
+
return self.__get_general_model(document)
|
|
322
|
+
|
|
306
323
|
def __get_model(
|
|
307
324
|
self,
|
|
308
325
|
document: TextDocument,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.6.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/analyzer_result.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/diagnostic_rules.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/import_resolver.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/imports_manager.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/keyword_finder.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/library_doc.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/model_helper.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/namespace.py
RENAMED
|
File without changes
|
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/project_index.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/scope_tree.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/variable_scope.py
RENAMED
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/diagnostics/workspace_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{robotcode_robot-2.6.0 → robotcode_robot-2.6.1}/src/robotcode/robot/utils/markdownformatter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|