add regex match counting (-RPc / -CPc)
Port the regex counting feature from the mako_changes-20230429 branch (2ff4d60): the -RPc/--revision-pattern-count and -CPc/--comment-pattern-count flags cause revision and comment patterns to output the number of matches instead of the matched text, with 0 (never null) for revisions with no matches or deleted content. The flags apply to all revision or comment patterns respectively, and count columns are typed int64 in the output schema. Counting the matches of common or large patterns previously required returning the full matched text, which made for very large outputs. Also take the code review suggestion from that branch (933ca75): use assignment expressions so matching no longer makes redundant calls to search() before finditer()/findall() or calls match.group() twice. Includes unit tests for the count semantics and schema types, and an end-to-end test that verifies counts against the number of matches recomputed from each revision's text and comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit0b6e0fbc70)
This commit is contained in:
@@ -427,6 +427,55 @@ def test_regex_deleted_revisions():
|
||||
assert deleted["npov_npov"].isna().all()
|
||||
assert deleted["talk_talk"].isna().all()
|
||||
|
||||
def test_regex_count_only():
|
||||
# count mode returns match counts instead of the matched text, with 0
|
||||
# (never None) for revisions with no matches or deleted content
|
||||
pair = RegexPair(r"\bcat\b", "cats", count_only=True)
|
||||
assert pair.matchmake("cat cat dog cat") == {"cats": 3}
|
||||
assert pair.matchmake("dog") == {"cats": 0}
|
||||
assert pair.matchmake(None) == {"cats": 0}
|
||||
assert pair.get_pyarrow_fields()[0].type == pa.int64()
|
||||
|
||||
pair = RegexPair(r"(?P<a>\bcat\b)|(?P<b>\bdog\b)", "pets", count_only=True)
|
||||
assert pair.matchmake("cat dog cat") == {"pets_a": 2, "pets_b": 1}
|
||||
assert pair.matchmake("bird") == {"pets_a": 0, "pets_b": 0}
|
||||
assert pair.matchmake(None) == {"pets_a": 0, "pets_b": 0}
|
||||
assert all(f.type == pa.int64() for f in pair.get_pyarrow_fields())
|
||||
|
||||
# without count_only the original behavior is unchanged
|
||||
pair = RegexPair(r"\bcat\b", "cats")
|
||||
assert pair.matchmake("cat cat dog cat") == {"cats": "cat, cat, cat"}
|
||||
assert pair.get_pyarrow_fields()[0].type == pa.string()
|
||||
|
||||
def test_regex_count_e2e():
|
||||
# -RPc/-CPc produce integer columns whose values equal the number of
|
||||
# matches in the revision text and comment respectively
|
||||
tester = WikiqTester(wiki=REGEXTEST, case_name="regex_count")
|
||||
|
||||
try:
|
||||
tester.call_wikiq(
|
||||
"--text",
|
||||
"-RP 'TestCase' -RPl testcases -RPc",
|
||||
"-CP '\\b[Cc]hevalier\\b' -CPl chev -CPc",
|
||||
)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
pytest.fail(exc.stderr.decode("utf8"))
|
||||
|
||||
import re
|
||||
test = pd.read_table(tester.output)
|
||||
|
||||
assert test["testcases"].dtype == "int64"
|
||||
assert test["chev"].dtype == "int64"
|
||||
|
||||
for _, row in test.iterrows():
|
||||
text = row["text"] if isinstance(row["text"], str) else None
|
||||
expected = len(re.findall("TestCase", text)) if text is not None else 0
|
||||
assert row["testcases"] == expected, f"revid {row['revid']}: {row['testcases']} != {expected}"
|
||||
|
||||
comment = row["edit_summary"] if isinstance(row["edit_summary"], str) else None
|
||||
expected = len(re.findall(r"\b[Cc]hevalier\b", comment)) if comment is not None else 0
|
||||
assert row["chev"] == expected, f"revid {row['revid']}: {row['chev']} != {expected}"
|
||||
|
||||
def test_redirect_detection():
|
||||
from wikiq.tables import RedirectDetector
|
||||
|
||||
|
||||
Reference in New Issue
Block a user