Skip to content

refactor: extract shared tokenization helper to service/_tokens.py - #25

Merged
Burton-David merged 1 commit into
mainfrom
refactor/shared-tokens
May 31, 2026
Merged

refactor: extract shared tokenization helper to service/_tokens.py#25
Burton-David merged 1 commit into
mainfrom
refactor/shared-tokens

Conversation

@Burton-David

Copy link
Copy Markdown
Owner

What

The NFKD-fold + lowercase + split-on-non-alphanumeric routine was copied across three modules, each with its own _NON_ALNUM_RE and several near-identical inline copies of the fold:

  • service/search.py_title_key, cross-source title dedup (title + first-author surname)
  • service/discovery.py_title_tokens / has_significant_tokens / _surname, find_paper Jaccard matching
  • reranker/fake.py_tokens, deterministic test scoring

A divergence in any one would silently break dedup or matching, since they all have to fold identically.

Change

New service/_tokens.py exposing:

  • normalize_unicode(text) -> str — the NFKD encode/decode fold
  • tokenize(text, *, stopwords=frozenset()) -> list[str] — fold, lowercase, split, drop empties + stopwords (each call site passes its own stopword set; search and discovery deliberately use different ones)
  • NON_ALNUM_RE — compiled once at module load

All three call sites now import from it; their local _NON_ALNUM_RE, re, and unicodedata imports are gone. No behavior change.

Closes #7.

Test plan

  • ruff check src tests — clean (no unused imports, import order verified)
  • mypy src — clean
  • pytest -q --strict-markers — 524 passed, 14 skipped
  • New tests/unit/test_tokens.py (11 cases: diacritic folding, undecomposable-non-ASCII drop, number/hyphen splitting, stopword filtering, order preservation, empty/punctuation-only input)
  • Existing test_search_service.py / test_discovery_service.py / test_reranker.py pass unchanged — the regression guard for "no behavior change"

The NFKD-fold + lowercase + split-on-non-alphanumeric routine was copied
across three modules — cross-source title dedup (service/search.py),
find_paper Jaccard matching (service/discovery.py), and the deterministic
test reranker (reranker/fake.py) — each with its own _NON_ALNUM_RE and four
near-identical inline copies of the fold. A divergence in any one would
silently break dedup or matching.

Centralize on service/_tokens.py with normalize_unicode(), tokenize()
(stopwords supplied per call site, since search and discovery use different
sets), and the shared NON_ALNUM_RE. No behavior change — the existing
search/discovery/reranker tests are the regression guard. Adds
tests/unit/test_tokens.py covering the helper directly.
@Burton-David
Burton-David merged commit 44ced41 into main May 31, 2026
1 check passed
@Burton-David
Burton-David deleted the refactor/shared-tokens branch May 31, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extract shared tokenization helper to service/_tokens.py

1 participant