diff --git a/CHANGELOG.md b/CHANGELOG.md index bc27b0d..10f684e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [2.6.5] - 2026-06 + +### Changed +- The combined-unmask-regex fallback (v2.6.4) now logs a `WARNING` when the + alternation regex fails to compile, instead of silently switching to the + slow per-mask path + ## [2.6.4] - 2026-06 ### Performance diff --git a/data_masking.py b/data_masking.py index 707f585..293b897 100644 --- a/data_masking.py +++ b/data_masking.py @@ -30,7 +30,7 @@ # Re-exports from masking package for backward compatibility # ============================================================================ -__version__ = "2.6.4" +__version__ = "2.6.5" from masking.constants import ( __version__, __author__, __contact__, __phone__, __license__, __year__, diff --git a/masking/constants.py b/masking/constants.py index 1017496..f30d4d8 100644 --- a/masking/constants.py +++ b/masking/constants.py @@ -26,7 +26,7 @@ # ============================================================================ # МЕТАДАНІ # ============================================================================ -__version__ = "2.6.4" +__version__ = "2.6.5" __author__ = "Vladyslav V. Prodan" __contact__ = "github.com/click0" __phone__ = "+38(099)6053340" diff --git a/tests/test_unmask.py b/tests/test_unmask.py index 4a6b84a..b6706e8 100644 --- a/tests/test_unmask.py +++ b/tests/test_unmask.py @@ -532,6 +532,29 @@ def test_empty_mapping_noop(self): assert restored == "текст без масок" assert stats["restored_count"] == 0 + def test_regex_compile_failure_logs_warning_and_falls_back(self, monkeypatch, caplog): + # Якщо об'єднаний regex не компілюється — попередження в лог + # і коректний результат через повільний шлях + import re as _re + import unmasking.engine as eng + + real_compile = _re.compile + + def boom(pattern, *a, **k): + if "коваленко" in pattern.lower(): + raise _re.error("forced failure") + return real_compile(pattern, *a, **k) + + monkeypatch.setattr(eng.re, "compile", boom) + masking_map = self._map({ + "surname": {"петренко": {"masked_as": "коваленко", "instances": [1]}} + }) + with caplog.at_level("WARNING"): + restored, stats = eng.unmask_other_data("коваленко тут", masking_map) + assert restored == "петренко тут" + assert stats["restored_count"] == 1 + assert any("falling back" in r.message for r in caplog.records) + if __name__ == "__main__": pytest.main([__file__, "-v"]) diff --git a/unmasking/cli.py b/unmasking/cli.py index b583d7b..ddad09c 100644 --- a/unmasking/cli.py +++ b/unmasking/cli.py @@ -58,7 +58,7 @@ # ============================================================================ # МЕТАДАНІ # ============================================================================ -__version__ = "2.6.4" +__version__ = "2.6.5" def main(): diff --git a/unmasking/engine.py b/unmasking/engine.py index 7867f3b..3740423 100644 --- a/unmasking/engine.py +++ b/unmasking/engine.py @@ -7,9 +7,12 @@ Extracted from unmask_data.py during the package refactoring (v2.5.0). """ +import logging import re from typing import Any, Dict, List, Tuple +_logger = logging.getLogger(__name__) + from rank_data import ( RANK_DECLENSIONS, RANK_FEMININE_MAP, @@ -193,8 +196,14 @@ def unmask_other_data(masked_text: str, masking_map: Dict) -> Tuple[str, Dict]: r'(? скільки входжень уже зустріли