Skip to content

fix(contracts): route to_numeric calls through the shared crash guard - #417

Merged
kevincostner17 merged 1 commit into
mainfrom
fix/contracts-safe-to-numeric
Sep 15, 2026
Merged

kevincostner17 merged 1 commit into
mainfrom
fix/contracts-safe-to-numeric

Conversation

@kevincostner17

Copy link
Copy Markdown
Contributor

Summary

This finishes the to_numeric crash-guard migration started in #407 and #409.

On pandas < 3, pd.to_numeric reads the exponent digits of a numeric string into a C int without checking for overflow, and does so before it rejects trailing text (pandas-dev/pandas#62617). A cell such as "81e3104049863b72" can therefore segfault the process, whatever errors= is set to. #409 routed every direct call in src/ through freshdata._numeric.safe_to_numeric, except the four in enterprise/contracts.py, which were deferred.

  • _contract_values: it applies min_value/max_value to columns of any non-datetime dtype. A text column holding such a token could reach the parser through enforce_contract and compare_to_baseline(contract=...). It now uses safe_to_numeric.
  • _profile_column, _ks_statistic and _psi_numeric: these only ever see int or float columns, but they also use the guard, so the file needs no exemption.
  • Results: for input with no unsafe cell, safe_to_numeric passes the call to pandas unchanged, so results are identical.

Tests

  • Static guard: _DEFERRED in tests/test_numeric.py is now empty, so the static guard covers enterprise/contracts.py.
  • test_contract_apis_survive_crash_tokens: runs build_baseline, enforce_contract and compare_to_baseline on object and string columns holding crash tokens. It runs in a child interpreter behind a to_numeric tripwire, and checks that the numeric cells are still range-checked.
  • test_contract_calls_match_raw_to_numeric: checks that the baseline and both reports match raw pd.to_numeric on ordinary frames, and that all four call sites are exercised.
  • Shared preamble: the existing child-process tripwire moved into a shared _CHILD_TRIPWIRE preamble.

Verification

  • Before the fix, on both Python 3.12 / pandas 2.3.3 and Python 3.9 / pandas 1.5.3:
    • the new child test exits 97 (unguarded to_numeric reached '81e3104049863b72')
    • the static guard reports {'enterprise/contracts.py': 4}
  • ruff check .: clean. mypy src/freshdata: no issues.
  • pytest -m "not online and not large": 6257 passed on Python 3.12 / pandas 2.3.3, and 6228 passed on Python 3.9 / pandas 1.5.3.

On pandas < 3, pd.to_numeric reads the exponent digits of a numeric
string into a C int with no overflow check, before it rejects trailing
text (pandas-dev/pandas#62617). A cell such as "81e3104049863b72" can
segfault the process whatever errors= says.

enterprise/contracts.py still had four direct calls. The one in
_contract_values runs on any non-datetime column with min_value or
max_value, so a text column holding such a token reached the parser from
enforce_contract and compare_to_baseline(contract=...). The other three
(_profile_column, _ks_statistic, _psi_numeric) only see int/float-family
columns, but go through the guard too so the file needs no exemption.
safe_to_numeric hands input with no unsafe cell to pandas untouched, so
results are unchanged.

This completes the migration from #409: _DEFERRED in tests/test_numeric.py
is now empty and the static guard covers contracts.py. New tests run
enforce_contract, build_baseline and compare_to_baseline on crash tokens
in a child interpreter behind a to_numeric tripwire, and check parity
with raw pd.to_numeric on ordinary frames.
@coderabbitai

coderabbitai Bot commented Sep 15, 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: 12a017fa-c415-4ea4-ac7e-a4140a846df1


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 3b4bde4 into main Sep 15, 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.

1 participant