Skip to content

fix(textclean): an unknown field type must not lose its protection silently - #474

Merged
kevincostner17 merged 1 commit into
mainfrom
fix/field-type-lookup
Sep 16, 2026
Merged

kevincostner17 merged 1 commit into
mainfrom
fix/field-type-lookup

Conversation

@kevincostner17

Copy link
Copy Markdown
Contributor

Summary

textclean.config_for_field matched the declared semantic_type exactly — case-sensitive and untrimmed — and returned the caller's base config on no match. The failure mode ran backwards for a safety-oriented library: a recognised structural type was protected from lossy transformation, while an unrecognised one received it in full. Declaring more about a column bought less protection.

With TextCleanConfig(case="lower", remove_punctuation=True) and the value Sekr3t-P@ss!!!:

declared semantic_type recognised result
identifier, ticker, email yes Sekr3t-P@ss!!! preserved
free_text yes sekr3tpss — correct, it is free text
password, api_key, secret no sekr3tpss
Ticker, TICKER, "ticker " no sekr3tpss
identifer (typo), e-mail no sekr3tpss

So a near-miss in the type name quietly downgraded a protected column, and a credential-bearing column named by a type outside the vocabulary was mangled.

The fix

  • Normalised lookup (casefold + strip), so Ticker, TICKER and "ticker " resolve to ticker.
  • A still-unrecognised type warns when a lossy option is active — the courtesy fieldcheck already extends for an unknown semantic_type. The message names the type, the specific options that will run, and the known types.

Deliberately limited

  • Every lossy option (case, remove_punctuation, strip_html, strip_urls, max_char_repeat, max_length) is opt-in, so a default fd.clean never reached this path and is unchanged.
  • The default config has no lossy option, so no warning fires on the common path — verified by a test, since the suite treats freshdata warnings as errors.
  • semantic_type=None is not a misspelling and stays silent.
  • Behaviour for an unrecognised type is otherwise unchanged. Whether it should instead default to the structural (lossless) config is a real behaviour change and is left for a decision rather than taken here.

Worth noting textclean's 28 known types are a superset of both fieldcheck._KNOWN_SEMANTIC_TYPES (23) and SEMANTIC_TYPES (17), so this was lookup strictness rather than a third taxonomy.

Verification

  • tests/test_field_type_lookup.py: 21 cases covering the case/whitespace variants, the must-still-be-lossy free_text control, the warning content, and the silent default path.
  • Full suite py3.12: 6643 passed, 22 skipped, 0 failed, coverage 93.90%.
  • ruff check . clean.

…lently

config_for_field matched the declared semantic_type exactly -- case-sensitive
and untrimmed -- and returned the caller's base config on no match. The failure
mode ran backwards for a safety-oriented library: a recognised structural type
was protected from lossy transformation, while an unrecognised one received it
in full. Declaring more about a column bought less protection.

With TextCleanConfig(case="lower", remove_punctuation=True):

    identifier / ticker / email   'Sekr3t-P@ss!!!'  preserved
    password / api_key / secret   'sekr3tpss'
    Ticker / TICKER / 'ticker '   'sekr3tpss'
    identifer (typo) / e-mail     'sekr3tpss'

So a near-miss in the type name quietly downgraded a protected column, and a
credential-bearing column named by a type outside the vocabulary was mangled.

The lookup is now normalised (casefold + strip), so Ticker, TICKER and
"ticker " resolve to ticker. A type that is still unrecognised warns when a
lossy option is active, which is the courtesy fieldcheck already extends for an
unknown semantic_type; the message names the column's type, the specific
options that will run, and the known types.

Deliberately limited:
- Every lossy option is opt-in, so a default fd.clean never reached this path
  and is unchanged.
- The default config has no lossy option, so no warning fires on the common
  path -- verified by a test, since the suite treats freshdata warnings as
  errors.
- semantic_type=None is not a misspelling and stays silent.
- Behaviour for an unrecognised type is otherwise unchanged. Whether it should
  instead default to the structural (lossless) config is a real behaviour
  change and is left for a decision rather than taken here.

Worth noting textclean's 28 known types are a superset of both
fieldcheck._KNOWN_SEMANTIC_TYPES (23) and SEMANTIC_TYPES (17), so this was
lookup strictness rather than a third taxonomy.

21 new tests; full suite 6643 passed / 0 failed, coverage 93.90%.
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: e6642ad1-9357-4646-8b75-444e5c413253


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

FreshData benchmark report — performance

  • freshdata: ?
  • python: ?
  • platform: ?
fixture n_rows n_cols p50 s p95 s peak MB repair % false-repair % preserve % trust monotonic export %

Authored-code reduction (Metric 6)

@kevincostner17
kevincostner17 merged commit c4ffef4 into main Sep 16, 2026
22 checks passed
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.

2 participants