Skip to content

Commit 484e363

Browse files
committed
Context patterns are regexes, not globs
1 parent 079b6d8 commit 484e363

File tree

4 files changed

+36
-9
lines changed

4 files changed

+36
-9
lines changed

CHANGES.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ development at the same time, such as 4.5.x and 5.0.
2020
Unreleased
2121
----------
2222

23-
Nothing yet.
23+
- The :class:`.CoverageData` API has changed how queries are limited to
24+
specific contexts. Now you use :meth:`.CoverageData.set_query_context` to
25+
set a single exact-match string, or :meth:`.CoverageData.set_query_contexts`
26+
to set a list of regular expressions to match contexts. This changes the
27+
command-line ``--contexts`` option to use regular expressions instead of
28+
filename-style wildcards.
2429

2530

2631
.. _changes_50a7:

coverage/cmdline.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ class Opts(object):
107107
)
108108
contexts = optparse.make_option(
109109
'', '--contexts', action='store',
110-
metavar="PAT1,PAT2,...",
110+
metavar="REGEX1,REGEX2,...",
111111
help=(
112112
"Only display data from lines covered in the given contexts. "
113-
"Accepts shell-style wildcards, which must be quoted."
113+
"Accepts Python regexes, which must be quoted."
114114
),
115115
)
116116
output_xml = optparse.make_option(

coverage/sqldata.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
import glob
1515
import itertools
1616
import os
17+
import re
1718
import sqlite3
1819
import sys
1920
import zlib
2021

2122
from coverage.backward import get_thread_id, iitems, to_bytes, to_string
2223
from coverage.debug import NoDebugging, SimpleReprMixin, clipped_repr
2324
from coverage.files import PathAliases
24-
from coverage.misc import CoverageException, file_be_gone, filename_suffix, isolate_module
25-
from coverage.misc import contract
26-
from coverage.numbits import nums_to_numbits, numbits_to_nums, numbits_union
25+
from coverage.misc import CoverageException, contract, file_be_gone, filename_suffix, isolate_module
26+
from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits
2727
from coverage.version import __version__
2828

2929
os = isolate_module(os)
@@ -714,17 +714,33 @@ def file_tracer(self, filename):
714714
return "" # File was measured, but no tracer associated.
715715

716716
def set_query_context(self, context):
717+
"""Set the context for subsequent querying.
718+
719+
The next `lines`, `arcs`, or `contexts_by_lineno` calls will be limited
720+
to only one context. `context` is a string which must match a context
721+
exactly. If it does not, no exception is raised, but queries will
722+
return no data.
723+
724+
"""
717725
self._start_using()
718726
with self._connect() as con:
719727
cur = con.execute("select id from context where context = ?", (context,))
720728
self._query_context_ids = [row[0] for row in cur.fetchall()]
721729

722730
def set_query_contexts(self, contexts):
723-
"""Set query contexts for future `lines`, `arcs` etc. calls."""
731+
"""Set the contexts for subsequent querying.
732+
733+
The next `lines`, `arcs`, or `contexts_by_lineno` calls will be limited
734+
to the specified contexts. `contexts` is a list of Python regular
735+
expressions. Contexts will be matched using :func:`re.search <python:re.search>`.
736+
Data will be included in query results if they are part of any of the
737+
contexts matched.
738+
739+
"""
724740
self._start_using()
725741
if contexts:
726742
with self._connect() as con:
727-
context_clause = ' or '.join(['context glob ?'] * len(contexts))
743+
context_clause = ' or '.join(['context regexp ?'] * len(contexts))
728744
cur = con.execute("select id from context where " + context_clause, contexts)
729745
self._query_context_ids = [row[0] for row in cur.fetchall()]
730746
else:
@@ -842,6 +858,7 @@ def connect(self):
842858
if self.debug:
843859
self.debug.write("Connecting to {!r}".format(self.filename))
844860
self.con = sqlite3.connect(filename, check_same_thread=False)
861+
self.con.create_function('REGEXP', 2, _regexp)
845862

846863
# This pragma makes writing faster. It disables rollbacks, but we never need them.
847864
# PyPy needs the .close() calls here, or sqlite gets twisted up:
@@ -893,3 +910,8 @@ def executescript(self, script):
893910
def dump(self):
894911
"""Return a multi-line string, the dump of the database."""
895912
return "\n".join(self.con.iterdump())
913+
914+
915+
def _regexp(text, pattern):
916+
"""A regexp function for SQLite."""
917+
return re.search(text, pattern) is not None

tests/test_html.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,7 @@ def test_filtered_dynamic_contexts(self):
10991099
cov = coverage.Coverage(source=["."])
11001100
cov.set_option("run:dynamic_context", "test_function")
11011101
cov.set_option("html:show_contexts", True)
1102-
cov.set_option("report:contexts", ["*test_one*"])
1102+
cov.set_option("report:contexts", ["test_one"])
11031103
mod = self.start_import_stop(cov, "two_tests")
11041104
d = html_data_from_cov(cov, mod)
11051105

0 commit comments

Comments
 (0)