Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion data_masking.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__,
Expand Down
2 changes: 1 addition & 1 deletion masking/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# ============================================================================
# МЕТАДАНІ
# ============================================================================
__version__ = "2.6.4"
__version__ = "2.6.5"
__author__ = "Vladyslav V. Prodan"
__contact__ = "github.com/click0"
__phone__ = "+38(099)6053340"
Expand Down
23 changes: 23 additions & 0 deletions tests/test_unmask.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
2 changes: 1 addition & 1 deletion unmasking/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
# ============================================================================
# МЕТАДАНІ
# ============================================================================
__version__ = "2.6.4"
__version__ = "2.6.5"


def main():
Expand Down
13 changes: 11 additions & 2 deletions unmasking/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -193,8 +196,14 @@ def unmask_other_data(masked_text: str, masking_map: Dict) -> Tuple[str, Dict]:
r'(?<!\w)(' + '|'.join(re.escape(m) for m in masks_sorted) + r')(?!\w)',
re.IGNORECASE,
)
except re.error:
# Запасний шлях (екзотичні символи в масках)
except re.error as e:
# Запасний шлях (екзотичні символи в масках). Не тихо: логуємо,
# бо повільний шлях значно дорожчий на великих документах.
_logger.warning(
"Combined unmask regex failed to compile (%s); falling back to "
"per-mask scan over %d masks — this is much slower on large files",
e, len(instance_map),
)
return _unmask_other_data_slow(restored_text, instance_map)

seen = {} # lower(маска) -> скільки входжень уже зустріли
Expand Down
Loading