Coverage.py database schema

New in version 5.0.

Coverage.py stores data in a SQLite database, by default called .coverage. For most needs, the CoverageData API will be sufficient, and should be preferred to accessing the database directly. Only advanced uses will need to use the database.

The schema can change without changing the major version of coverage.py, so be careful when accessing the database directly. The coverage_schema table has the schema number of the database. The schema described here corresponds to:

SCHEMA_VERSION = 7

You can use SQLite tools such as the sqlite3 module in the Python standard library to access the data. Some data is stored in a packed format that will need custom functions to access. See register_sqlite_functions().

Database schema

This is the database schema.

TODO: explain more. Readers: what needs explaining?

CREATE TABLE coverage_schema (
    -- One row, to record the version of the schema in this db.
    version integer
);

CREATE TABLE meta (
    -- Key-value pairs, to record metadata about the data
    key text,
    value text,
    unique (key)
    -- Keys:
    --  'has_arcs' boolean      -- Is this data recording branches?
    --  'sys_argv' text         -- The coverage command line that recorded the data.
    --  'version' text          -- The version of coverage.py that made the file.
    --  'when' text             -- Datetime when the file was created.
);

CREATE TABLE file (
    -- A row per file measured.
    id integer primary key,
    path text,
    unique (path)
);

CREATE TABLE context (
    -- A row per context measured.
    id integer primary key,
    context text,
    unique (context)
);

CREATE TABLE line_bits (
    -- If recording lines, a row per context per file executed.
    -- All of the line numbers for that file/context are in one numbits.
    file_id integer,            -- foreign key to `file`.
    context_id integer,         -- foreign key to `context`.
    numbits blob,               -- see the numbits functions in coverage.numbits
    foreign key (file_id) references file (id),
    foreign key (context_id) references context (id),
    unique (file_id, context_id)
);

CREATE TABLE arc (
    -- If recording branches, a row per context per from/to line transition executed.
    file_id integer,            -- foreign key to `file`.
    context_id integer,         -- foreign key to `context`.
    fromno integer,             -- line number jumped from.
    tono integer,               -- line number jumped to.
    foreign key (file_id) references file (id),
    foreign key (context_id) references context (id),
    unique (file_id, context_id, fromno, tono)
);

CREATE TABLE tracer (
    -- A row per file indicating the tracer used for that file.
    file_id integer primary key,
    tracer text,
    foreign key (file_id) references file (id)
);

Numbits

Functions to manipulate packed binary representations of number sets.

To save space, coverage stores sets of line numbers in SQLite using a packed binary representation called a numbits. A numbits is a set of positive integers.

A numbits is stored as a blob in the database. The exact meaning of the bytes in the blobs should be considered an implementation detail that might change in the future. Use these functions to work with those binary blobs of data.

coverage.numbits.num_in_numbits(num, numbits)

Does the integer num appear in numbits?

Arguments:

num (integer)

numbits (binary blob)

Returns:

A boolean, true if num is a member of numbits.

coverage.numbits.numbits_any_intersection(numbits1, numbits2)

Is there any number that appears in both numbits?

Determine whether two number sets have a non-empty intersection. This is faster than computing the intersection.

Arguments:

numbits1, numbits2: packed number sets.

Returns:

A boolean, true if there is any number in both of the number sets.

coverage.numbits.numbits_intersection(numbits1, numbits2)

Compute the intersection of two numbits.

Arguments:

numbits1, numbits2: packed number sets.

Returns:

A new numbits, the intersection of the two number sets.

coverage.numbits.numbits_to_nums(numbits)

Convert a numbits into a list of numbers.

Arguments:

numbits (a binary blob): the packed number set.

Returns:

A list of integers.

coverage.numbits.numbits_union(numbits1, numbits2)

Compute the union of two numbits.

Arguments:

numbits1, numbits2: packed number sets.

Returns:

A new numbits, the union of the two number sets.

coverage.numbits.nums_to_numbits(nums)

Convert nums into a numbits.

Arguments:

nums (a reusable iterable of integers): the line numbers to store.

Returns:

A binary blob.

coverage.numbits.register_sqlite_functions(connection)

Define numbits functions in a SQLite connection.

This defines these functions for use in SQLite statements:

connection is a sqlite3.Connection object. After creating the connection, pass it to this function to register the numbits functions. Then you can use numbits functions in your queries:

import sqlite3
from coverage.numbits import register_sqlite_functions

conn = sqlite3.connect('example.db')
register_sqlite_functions(conn)
c = conn.cursor()
# Kind of a nonsense query: find all the files and contexts that
# executed line 47 in any file:
c.execute(
    "select file_id, context_id from line_bits where num_in_numbits(?, numbits)",
    (47,)
)