diff --git a/dol/tests/test_trans.py b/dol/tests/test_trans.py index 6248d7c0..6494f1ca 100644 --- a/dol/tests/test_trans.py +++ b/dol/tests/test_trans.py @@ -1,6 +1,38 @@ """Test trans.py functionality.""" -from dol.trans import filt_iter, redirect_getattr_to_getitem +import dol.util +from dol.trans import ( + filt_iter, + filter_prefixes, + filter_regex, + filter_suffixes, + redirect_getattr_to_getitem, +) + + +def test_filter_regex_is_os_independent(): + """Regex filters must compile as REGEXES, not as path templates. + + Regression for a Windows-only bug: ``filter_regex`` used the path-oriented + ``safe_compile``, which ``re.escape``s its input on Windows. That turned a + pattern like ``(\\.json)$`` into a literal matcher, so ``filter_suffixes('.json')`` + rejected every ``*.json`` key on Windows and ``dol.Jsons`` raised + ``KeyError: 'Key not in store: .json'`` on write. + """ + real_system = dol.util.platform.system + try: + # Simulate every platform, including Windows (the broken one). + for system in ("Linux", "Darwin", "Windows"): + dol.util.platform.system = lambda system=system: system + assert bool(filter_regex(r"(\.json)$")("doc-001.json")) is True + assert bool(filter_regex(r"(\.json)$")("doc-001.txt")) is False + assert bool(filter_suffixes(".json")("doc-001.json")) is True + assert bool(filter_suffixes([".txt", ".doc"])("report.doc")) is True + assert bool(filter_suffixes(".json")("doc-001.txt")) is False + assert bool(filter_prefixes("test")("test_image.jpg")) is True + assert bool(filter_prefixes("test")("report.doc")) is False + finally: + dol.util.platform.system = real_system def test_filt_iter(): diff --git a/dol/trans.py b/dol/trans.py index 3390d7ba..beb883f8 100644 --- a/dol/trans.py +++ b/dol/trans.py @@ -1481,9 +1481,21 @@ def filter_regex(regex, *, return_search_func=False): >>> is_txt("report.doc") False + The argument is a *regular expression*, so it is compiled with ``re.compile`` + -- NOT ``safe_compile`` (which is for file-path templates and ``re.escape``s its + input on Windows). Using ``safe_compile`` here silently broke every regex filter + on Windows: e.g. ``filter_suffixes('.json')`` (used by ``Jsons``) had its + ``(\.json)$`` pattern escaped into a literal string matcher, so no ``*.json`` key + matched and the store raised ``KeyError: 'Key not in store: .json'``. + + >>> is_json = filter_regex(r"(\.json)$") # works identically on every OS + >>> is_json("doc-001.json") + True + >>> is_json("doc-001.txt") + False """ if isinstance(regex, str): - regex = safe_compile(regex) + regex = re.compile(regex) if return_search_func: return regex.search else: