feat(classify): add regex_fields whole-field AND rule type - #155
TimeToBuildBob wants to merge 3 commits into
Conversation
Extend Rule to support a 'regex_fields' variant where every named field
must match its own pattern in its entirety (re.fullmatch, logical AND).
Existing 'regex' rules are fully backward-compatible.
Motivation: users need separate categories when two apps (e.g. mstsc.exe
vs winbox.exe) show identical titles. Title-only regexes cannot tell them
apart; only an app-AND-title rule can.
Contract:
- type: 'regex_fields', fields: {<field>: <pattern>, ...}
- Optional ignore_case: bool (default False)
- All named fields must exist as strings in event.data
- Each pattern is tested with re.fullmatch (whole-field anchoring)
- 'regex' and 'select_keys' members raise ValueError on this variant,
guarding against the rollout hazard where old Python reads a stray
'regex' member and produces false positives
- Empty fields dict raises ValueError
11 new tests added to tests/test_transforms.py; 37 existing tests pass.
Git-Session-Id: 3f81
\n in a regex pattern is a newline metacharacter (matches actual newline), so the previous assertion was wrong. Rewrote the test to document the actual engine behavior: partial patterns don't match full-field values, \n metacharacter matches embedded newlines, bare dot does not. Git-Session-Id: 3f81
|
| self.priority = _parse_optional_priority(rules) | ||
| flags = (re.IGNORECASE if self.ignore_case else 0) | re.UNICODE | ||
|
|
||
| if self._rule_type == "regex_fields": |
There was a problem hiding this comment.
Unknown Types Silently Fall Back
Unsupported type values enter the legacy regex branch instead of being rejected. A misspelled regex_fields rule can silently never match or, if it includes a stale regex member, use legacy OR/search semantics and misclassify events. Validate that type is either regex or regex_fields.
| raise ValueError( | ||
| f"regex_fields: pattern for field '{field}' must be a non-empty string" | ||
| ) | ||
| self._field_patterns[field] = re.compile(pattern, flags) |
There was a problem hiding this comment.
Malformed Patterns Escape Error Handling
Compiling an invalid field pattern raises re.error, but the category and tag query wrappers only translate ValueError into QueryFunctionException. An invalid user-supplied regex_fields pattern therefore escapes the normal configuration-error path as an unexpected exception. Wrap compilation in a ValueError or explicitly translate re.error.
Git-Session-Id: 5673c0d5-27b3-5309-a5c9-0229d1db2bd2
|
@greptileai review |
|
CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click. This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted. |
🤖 AI code reviewAdds a new Safe to merge — no P0/P1 findingsConfidence 5/5 ✅ No findings. The diff looks correct to me on this pass. Files changed (2) — the diff as I read it
Reviewed Maintainer commands
|
Summary
Adds a new
regex_fieldsrule variant for category/tag classification that lets users constrain multiple fields simultaneously (logical AND) with whole-field matching semantics.This addresses the use-case from ActivityWatch/aw-webui#939: when two apps (e.g.
mstsc.exefor RDP,winbox.exefor Winbox) share the same window title, the existingregexrule cannot distinguish them because it OR-tests fields. Aregex_fieldsrule with bothappandtitlepatterns resolves this exactly.New rule format
{ "type": "regex_fields", "fields": { "app": "mstsc\\.exe", "title": "office\\.example\\.com" }, "ignore_case": false }All named fields must be present in the event data, be strings, and fully satisfy their pattern (
re.fullmatch), so it must match the entire field value.Backward compatibility
regexrules with OR semantics are completely unchanged."type": "regex_fields").typefield and reads the staleregexmember. This variant explicitly raisesValueErrorifregexorselect_keysare present alongsidetype: regex_fields.ignore_caseappliesre.IGNORECASEto all field patterns.What changed
aw_transform/classify.py:Rule.__init__now dispatches ontypefield; new_field_patternsattribute;Rule.matchusesfullmatchfor theregex_fieldspath.tests/test_transforms.pycovering AND semantics, whole-field anchoring, ignore_case, missing/non-string fields, empty-fields error, invalid field names/patterns, rollout hazard guard, and embedded newlines.Testing
48 tests pass (
pytest tests/test_transforms.py).Companion PR for aw-server-rust (Rust server): ActivityWatch/aw-server-rust#670
Closes ActivityWatch/aw-webui#939 (partial — webui editor wiring is a follow-up once capability is advertised)