coverage 7.11.3__cp314-cp314-musllinux_1_2_x86_64.whl
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.
Potentially problematic release.
This version of coverage might be problematic. Click here for more details.
- coverage/__init__.py +40 -0
- coverage/__main__.py +12 -0
- coverage/annotate.py +114 -0
- coverage/bytecode.py +196 -0
- coverage/cmdline.py +1184 -0
- coverage/collector.py +486 -0
- coverage/config.py +731 -0
- coverage/context.py +74 -0
- coverage/control.py +1481 -0
- coverage/core.py +139 -0
- coverage/data.py +227 -0
- coverage/debug.py +669 -0
- coverage/disposition.py +59 -0
- coverage/env.py +135 -0
- coverage/exceptions.py +85 -0
- coverage/execfile.py +329 -0
- coverage/files.py +553 -0
- coverage/html.py +856 -0
- coverage/htmlfiles/coverage_html.js +733 -0
- coverage/htmlfiles/favicon_32.png +0 -0
- coverage/htmlfiles/index.html +164 -0
- coverage/htmlfiles/keybd_closed.png +0 -0
- coverage/htmlfiles/pyfile.html +149 -0
- coverage/htmlfiles/style.css +377 -0
- coverage/htmlfiles/style.scss +824 -0
- coverage/inorout.py +614 -0
- coverage/jsonreport.py +188 -0
- coverage/lcovreport.py +219 -0
- coverage/misc.py +373 -0
- coverage/multiproc.py +120 -0
- coverage/numbits.py +146 -0
- coverage/parser.py +1213 -0
- coverage/patch.py +166 -0
- coverage/phystokens.py +197 -0
- coverage/plugin.py +617 -0
- coverage/plugin_support.py +299 -0
- coverage/py.typed +1 -0
- coverage/python.py +269 -0
- coverage/pytracer.py +369 -0
- coverage/regions.py +127 -0
- coverage/report.py +298 -0
- coverage/report_core.py +117 -0
- coverage/results.py +471 -0
- coverage/sqldata.py +1153 -0
- coverage/sqlitedb.py +239 -0
- coverage/sysmon.py +482 -0
- coverage/templite.py +306 -0
- coverage/tomlconfig.py +210 -0
- coverage/tracer.cpython-314-x86_64-linux-musl.so +0 -0
- coverage/tracer.pyi +43 -0
- coverage/types.py +206 -0
- coverage/version.py +35 -0
- coverage/xmlreport.py +264 -0
- coverage-7.11.3.dist-info/METADATA +221 -0
- coverage-7.11.3.dist-info/RECORD +59 -0
- coverage-7.11.3.dist-info/WHEEL +5 -0
- coverage-7.11.3.dist-info/entry_points.txt +4 -0
- coverage-7.11.3.dist-info/licenses/LICENSE.txt +177 -0
- coverage-7.11.3.dist-info/top_level.txt +1 -0
coverage/sqlitedb.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
|
2
|
+
# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
|
|
3
|
+
|
|
4
|
+
"""SQLite abstraction for coverage.py"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import contextlib
|
|
9
|
+
import re
|
|
10
|
+
import sqlite3
|
|
11
|
+
from collections.abc import Iterable, Iterator
|
|
12
|
+
from typing import Any, cast
|
|
13
|
+
|
|
14
|
+
from coverage.debug import auto_repr, clipped_repr, exc_one_line
|
|
15
|
+
from coverage.exceptions import DataError
|
|
16
|
+
from coverage.types import TDebugCtl
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SqliteDb:
|
|
20
|
+
"""A simple abstraction over a SQLite database.
|
|
21
|
+
|
|
22
|
+
Use as a context manager, then you can use it like a
|
|
23
|
+
:class:`python:sqlite3.Connection` object::
|
|
24
|
+
|
|
25
|
+
with SqliteDb(filename, debug_control) as db:
|
|
26
|
+
with db.execute("select a, b from some_table") as cur:
|
|
27
|
+
for a, b in cur:
|
|
28
|
+
etc(a, b)
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, filename: str, debug: TDebugCtl, no_disk: bool = False) -> None:
|
|
33
|
+
self.debug = debug
|
|
34
|
+
self.filename = filename
|
|
35
|
+
self.no_disk = no_disk
|
|
36
|
+
self.nest = 0
|
|
37
|
+
self.con: sqlite3.Connection | None = None
|
|
38
|
+
|
|
39
|
+
__repr__ = auto_repr
|
|
40
|
+
|
|
41
|
+
def _connect(self) -> None:
|
|
42
|
+
"""Connect to the db and do universal initialization."""
|
|
43
|
+
if self.con is not None:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# It can happen that Python switches threads while the tracer writes
|
|
47
|
+
# data. The second thread will also try to write to the data,
|
|
48
|
+
# effectively causing a nested context. However, given the idempotent
|
|
49
|
+
# nature of the tracer operations, sharing a connection among threads
|
|
50
|
+
# is not a problem.
|
|
51
|
+
if self.debug.should("sql"):
|
|
52
|
+
self.debug.write(f"Connecting to {self.filename!r}")
|
|
53
|
+
try:
|
|
54
|
+
# Use uri=True when connecting to memory URIs
|
|
55
|
+
if self.filename.startswith("file:"):
|
|
56
|
+
self.con = sqlite3.connect(self.filename, check_same_thread=False, uri=True)
|
|
57
|
+
else:
|
|
58
|
+
self.con = sqlite3.connect(self.filename, check_same_thread=False)
|
|
59
|
+
except sqlite3.Error as exc:
|
|
60
|
+
raise DataError(f"Couldn't use data file {self.filename!r}: {exc}") from exc
|
|
61
|
+
|
|
62
|
+
if self.debug.should("sql"):
|
|
63
|
+
self.debug.write(f"Connected to {self.filename!r} as {self.con!r}")
|
|
64
|
+
|
|
65
|
+
self.con.create_function("REGEXP", 2, lambda txt, pat: re.search(txt, pat) is not None)
|
|
66
|
+
|
|
67
|
+
# Turning off journal_mode can speed up writing. It can't always be
|
|
68
|
+
# disabled, so we have to be prepared for *-journal files elsewhere.
|
|
69
|
+
# In Python 3.12+, we can change the config to allow journal_mode=off.
|
|
70
|
+
if hasattr(sqlite3, "SQLITE_DBCONFIG_DEFENSIVE"):
|
|
71
|
+
# Turn off defensive mode, so that journal_mode=off can succeed.
|
|
72
|
+
self.con.setconfig( # type: ignore[attr-defined, unused-ignore]
|
|
73
|
+
sqlite3.SQLITE_DBCONFIG_DEFENSIVE,
|
|
74
|
+
False,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# This pragma makes writing faster. It disables rollbacks, but we never need them.
|
|
78
|
+
self.execute_void("pragma journal_mode=off")
|
|
79
|
+
|
|
80
|
+
# This pragma makes writing faster. It can fail in unusual situations
|
|
81
|
+
# (https://github.com/coveragepy/coveragepy/issues/1646), so use fail_ok=True
|
|
82
|
+
# to keep things going.
|
|
83
|
+
self.execute_void("pragma synchronous=off", fail_ok=True)
|
|
84
|
+
|
|
85
|
+
def close(self, force: bool = False) -> None:
|
|
86
|
+
"""If needed, close the connection."""
|
|
87
|
+
if self.con is not None:
|
|
88
|
+
if force or not self.no_disk:
|
|
89
|
+
if self.debug.should("sql"):
|
|
90
|
+
self.debug.write(f"Closing {self.con!r} on {self.filename!r}")
|
|
91
|
+
self.con.close()
|
|
92
|
+
self.con = None
|
|
93
|
+
|
|
94
|
+
def __enter__(self) -> SqliteDb:
|
|
95
|
+
if self.nest == 0:
|
|
96
|
+
self._connect()
|
|
97
|
+
assert self.con is not None
|
|
98
|
+
self.con.__enter__()
|
|
99
|
+
self.nest += 1
|
|
100
|
+
return self
|
|
101
|
+
|
|
102
|
+
def __exit__(self, exc_type, exc_value, traceback) -> None: # type: ignore[no-untyped-def]
|
|
103
|
+
self.nest -= 1
|
|
104
|
+
if self.nest == 0:
|
|
105
|
+
try:
|
|
106
|
+
assert self.con is not None
|
|
107
|
+
self.con.__exit__(exc_type, exc_value, traceback)
|
|
108
|
+
self.close()
|
|
109
|
+
except Exception as exc:
|
|
110
|
+
if self.debug.should("sql"):
|
|
111
|
+
self.debug.write(f"EXCEPTION from __exit__: {exc_one_line(exc)}")
|
|
112
|
+
raise DataError(f"Couldn't end data file {self.filename!r}: {exc}") from exc
|
|
113
|
+
|
|
114
|
+
def _execute(self, sql: str, parameters: Iterable[Any]) -> sqlite3.Cursor:
|
|
115
|
+
"""Same as :meth:`python:sqlite3.Connection.execute`."""
|
|
116
|
+
if self.debug.should("sql"):
|
|
117
|
+
tail = f" with {parameters!r}" if parameters else ""
|
|
118
|
+
self.debug.write(f"Executing {sql!r}{tail}")
|
|
119
|
+
try:
|
|
120
|
+
assert self.con is not None
|
|
121
|
+
try:
|
|
122
|
+
return self.con.execute(sql, parameters) # type: ignore[arg-type]
|
|
123
|
+
except Exception:
|
|
124
|
+
# In some cases, an error might happen that isn't really an
|
|
125
|
+
# error. Try again immediately.
|
|
126
|
+
# https://github.com/coveragepy/coveragepy/issues/1010
|
|
127
|
+
return self.con.execute(sql, parameters) # type: ignore[arg-type]
|
|
128
|
+
except sqlite3.Error as exc:
|
|
129
|
+
msg = str(exc)
|
|
130
|
+
if not self.no_disk:
|
|
131
|
+
try:
|
|
132
|
+
# `execute` is the first thing we do with the database, so try
|
|
133
|
+
# hard to provide useful hints if something goes wrong now.
|
|
134
|
+
with open(self.filename, "rb") as bad_file:
|
|
135
|
+
cov4_sig = b"!coverage.py: This is a private format"
|
|
136
|
+
if bad_file.read(len(cov4_sig)) == cov4_sig:
|
|
137
|
+
msg = (
|
|
138
|
+
"Looks like a coverage 4.x data file. "
|
|
139
|
+
+ "Are you mixing versions of coverage?"
|
|
140
|
+
)
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
if self.debug.should("sql"):
|
|
144
|
+
self.debug.write(f"EXCEPTION from execute: {exc_one_line(exc)}")
|
|
145
|
+
raise DataError(f"Couldn't use data file {self.filename!r}: {msg}") from exc
|
|
146
|
+
|
|
147
|
+
@contextlib.contextmanager
|
|
148
|
+
def execute(
|
|
149
|
+
self,
|
|
150
|
+
sql: str,
|
|
151
|
+
parameters: Iterable[Any] = (),
|
|
152
|
+
) -> Iterator[sqlite3.Cursor]:
|
|
153
|
+
"""Context managed :meth:`python:sqlite3.Connection.execute`.
|
|
154
|
+
|
|
155
|
+
Use with a ``with`` statement to auto-close the returned cursor.
|
|
156
|
+
"""
|
|
157
|
+
cur = self._execute(sql, parameters)
|
|
158
|
+
try:
|
|
159
|
+
yield cur
|
|
160
|
+
finally:
|
|
161
|
+
cur.close()
|
|
162
|
+
|
|
163
|
+
def execute_void(self, sql: str, parameters: Iterable[Any] = (), fail_ok: bool = False) -> None:
|
|
164
|
+
"""Same as :meth:`python:sqlite3.Connection.execute` when you don't need the cursor.
|
|
165
|
+
|
|
166
|
+
If `fail_ok` is True, then SQLite errors are ignored.
|
|
167
|
+
"""
|
|
168
|
+
try:
|
|
169
|
+
# PyPy needs the .close() calls here, or sqlite gets twisted up:
|
|
170
|
+
# https://bitbucket.org/pypy/pypy/issues/2872/default-isolation-mode-is-different-on
|
|
171
|
+
self._execute(sql, parameters).close()
|
|
172
|
+
except DataError:
|
|
173
|
+
if not fail_ok:
|
|
174
|
+
raise
|
|
175
|
+
|
|
176
|
+
def execute_for_rowid(self, sql: str, parameters: Iterable[Any] = ()) -> int:
|
|
177
|
+
"""Like execute, but returns the lastrowid."""
|
|
178
|
+
with self.execute(sql, parameters) as cur:
|
|
179
|
+
assert cur.lastrowid is not None
|
|
180
|
+
rowid: int = cur.lastrowid
|
|
181
|
+
if self.debug.should("sqldata"):
|
|
182
|
+
self.debug.write(f"Row id result: {rowid!r}")
|
|
183
|
+
return rowid
|
|
184
|
+
|
|
185
|
+
def execute_one(self, sql: str, parameters: Iterable[Any] = ()) -> tuple[Any, ...] | None:
|
|
186
|
+
"""Execute a statement and return the one row that results.
|
|
187
|
+
|
|
188
|
+
This is like execute(sql, parameters).fetchone(), except it is
|
|
189
|
+
correct in reading the entire result set. This will raise an
|
|
190
|
+
exception if more than one row results.
|
|
191
|
+
|
|
192
|
+
Returns a row, or None if there were no rows.
|
|
193
|
+
"""
|
|
194
|
+
with self.execute(sql, parameters) as cur:
|
|
195
|
+
rows = list(cur)
|
|
196
|
+
if len(rows) == 0:
|
|
197
|
+
return None
|
|
198
|
+
elif len(rows) == 1:
|
|
199
|
+
return cast(tuple[Any, ...], rows[0])
|
|
200
|
+
else:
|
|
201
|
+
raise AssertionError(f"SQL {sql!r} shouldn't return {len(rows)} rows")
|
|
202
|
+
|
|
203
|
+
def _executemany(self, sql: str, data: list[Any]) -> sqlite3.Cursor:
|
|
204
|
+
"""Same as :meth:`python:sqlite3.Connection.executemany`."""
|
|
205
|
+
if self.debug.should("sql"):
|
|
206
|
+
final = ":" if self.debug.should("sqldata") else ""
|
|
207
|
+
self.debug.write(f"Executing many {sql!r} with {len(data)} rows{final}")
|
|
208
|
+
if self.debug.should("sqldata"):
|
|
209
|
+
for i, row in enumerate(data):
|
|
210
|
+
self.debug.write(f"{i:4d}: {row!r}")
|
|
211
|
+
assert self.con is not None
|
|
212
|
+
try:
|
|
213
|
+
return self.con.executemany(sql, data)
|
|
214
|
+
except Exception:
|
|
215
|
+
# In some cases, an error might happen that isn't really an
|
|
216
|
+
# error. Try again immediately.
|
|
217
|
+
# https://github.com/coveragepy/coveragepy/issues/1010
|
|
218
|
+
return self.con.executemany(sql, data)
|
|
219
|
+
|
|
220
|
+
def executemany_void(self, sql: str, data: list[Any]) -> None:
|
|
221
|
+
"""Same as :meth:`python:sqlite3.Connection.executemany` when you don't need the cursor."""
|
|
222
|
+
self._executemany(sql, data).close()
|
|
223
|
+
|
|
224
|
+
def executescript(self, script: str) -> None:
|
|
225
|
+
"""Same as :meth:`python:sqlite3.Connection.executescript`."""
|
|
226
|
+
if self.debug.should("sql"):
|
|
227
|
+
self.debug.write(
|
|
228
|
+
"Executing script with {} chars: {}".format(
|
|
229
|
+
len(script),
|
|
230
|
+
clipped_repr(script, 100),
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
assert self.con is not None
|
|
234
|
+
self.con.executescript(script).close()
|
|
235
|
+
|
|
236
|
+
def dump(self) -> str:
|
|
237
|
+
"""Return a multi-line string, the SQL dump of the database."""
|
|
238
|
+
assert self.con is not None
|
|
239
|
+
return "\n".join(self.con.iterdump())
|