Skip to content

Commit d8f88c7

Browse files
authored
refactor: use SQLite URIs for in-memory databases (#2034)
This lets us ATTACH DATABASE between in-memory databases.
1 parent 575740b commit d8f88c7

File tree

2 files changed

+13
-7
lines changed

2 files changed

+13
-7
lines changed

coverage/sqldata.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import sys
1919
import textwrap
2020
import threading
21+
import uuid
2122
import zlib
2223
from collections.abc import Collection, Mapping, Sequence
2324
from typing import Any, Callable, cast
@@ -263,7 +264,7 @@ def _debug_dataio(self, msg: str, filename: str) -> None:
263264
def _choose_filename(self) -> None:
264265
"""Set self._filename based on inited attributes."""
265266
if self._no_disk:
266-
self._filename = ":memory:"
267+
self._filename = f"file:coverage-{uuid.uuid4()}?mode=memory&cache=shared"
267268
else:
268269
self._filename = self._basename
269270
suffix = filename_suffix(self._suffix)
@@ -289,7 +290,7 @@ def close(self, force: bool = False) -> None:
289290
def _open_db(self) -> None:
290291
"""Open an existing db file, and read its metadata."""
291292
self._debug_dataio("Opening data file", self._filename)
292-
self._dbs[threading.get_ident()] = SqliteDb(self._filename, self._debug)
293+
self._dbs[threading.get_ident()] = SqliteDb(self._filename, self._debug, self._no_disk)
293294
self._read_db()
294295

295296
def _read_db(self) -> None:
@@ -402,7 +403,7 @@ def loads(self, data: bytes) -> None:
402403
f"Unrecognized serialization: {data[:40]!r} (head of {len(data)} bytes)",
403404
)
404405
script = zlib.decompress(data[1:]).decode("utf-8")
405-
self._dbs[threading.get_ident()] = db = SqliteDb(self._filename, self._debug)
406+
self._dbs[threading.get_ident()] = db = SqliteDb(self._filename, self._debug, self._no_disk)
406407
with db:
407408
db.executescript(script)
408409
self._read_db()

coverage/sqlitedb.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ class SqliteDb:
2828
etc(a, b)
2929
3030
"""
31-
def __init__(self, filename: str, debug: TDebugCtl) -> None:
31+
def __init__(self, filename: str, debug: TDebugCtl, no_disk: bool = False) -> None:
3232
self.debug = debug
3333
self.filename = filename
34+
self.no_disk = no_disk
3435
self.nest = 0
3536
self.con: sqlite3.Connection | None = None
3637

@@ -49,7 +50,11 @@ def _connect(self) -> None:
4950
if self.debug.should("sql"):
5051
self.debug.write(f"Connecting to {self.filename!r}")
5152
try:
52-
self.con = sqlite3.connect(self.filename, check_same_thread=False)
53+
# Use uri=True when connecting to memory URIs
54+
if self.filename.startswith("file:"):
55+
self.con = sqlite3.connect(self.filename, check_same_thread=False, uri=True)
56+
else:
57+
self.con = sqlite3.connect(self.filename, check_same_thread=False)
5358
except sqlite3.Error as exc:
5459
raise DataError(f"Couldn't use data file {self.filename!r}: {exc}") from exc
5560

@@ -78,7 +83,7 @@ def _connect(self) -> None:
7883
def close(self, force: bool = False) -> None:
7984
"""If needed, close the connection."""
8085
if self.con is not None:
81-
if force or self.filename != ":memory:":
86+
if force or not self.no_disk:
8287
if self.debug.should("sql"):
8388
self.debug.write(f"Closing {self.con!r} on {self.filename!r}")
8489
self.con.close()
@@ -120,7 +125,7 @@ def _execute(self, sql: str, parameters: Iterable[Any]) -> sqlite3.Cursor:
120125
return self.con.execute(sql, parameters) # type: ignore[arg-type]
121126
except sqlite3.Error as exc:
122127
msg = str(exc)
123-
if self.filename != ":memory:":
128+
if not self.no_disk:
124129
try:
125130
# `execute` is the first thing we do with the database, so try
126131
# hard to provide useful hints if something goes wrong now.

0 commit comments

Comments
 (0)