Skip to main content

pipelie

Finds the bugs that leave your pipeline reporting green while the numbers are wrong.

pip install pipelie
import pipelie
print(pipelie.audit(df, target="built", key=["id"]))

Or in CI, where it belongs:

df = pipelie.guard(df, target="built", key=["id"])   # raises on anything critical

Why this exists and not dbt tests

Schema tests, null counts and row counts answer "is the data there?"

They do not answer "does the data mean what it is about to be used to mean?"

Every check in this package corresponds to a bug that shipped to production while the tests passed. Not hypothetical failure modes — things that actually happened, were written up, and cost real work:

the bug what every existing test said
An anomaly ranking sorted by Unix timestamp schema valid, no nulls, values distinct
A cosine similarity that returned 0.9998583 for all 42 rows numeric, in range, populated
One date column holding four different date formats, 84% of values silently coerced to NaT looked like ordinary missing data
Queue ID used as a primary key when it was not unique in 3 of 4 sources column present, correct dtype
"Solar" and "Photovoltaic" counted as different technologies valid categories, no nulls
Whether a field was missing predicted the outcome being measured 20% null, within tolerance

Each of those produced a plausible-looking number that was wrong. That is the failure mode this package is for.

What it checks

check catches
clock_in_disguise A number that is really a timestamp, a row index, or a counter. Epoch-range detection plus rank correlation against every date column.
degenerate All-null columns, constants, and near-constants — a "similarity" that cannot separate anything.
parse_carnage One column holding more than one date format, with the share of each. This is the bug that destroys most of a column and looks like missingness.
informative_missingness Whether a value is missing predicts your target. If so, dropping those rows is selecting on the answer, and every rate you compute afterwards is biased.
vocabulary_collisions The same category written two ways — case, punctuation, or abbreviation — splitting one rate into two.
placeholders -999, TODO, 1970-01-01 and friends, still present in the data long after the promise to replace them.
duplicate_rows Re-ingested rows that manufacture a "structural break", and declared keys that are not unique.

informative_missingness is Bonferroni-corrected across the columns it tests, so a wide table does not manufacture a finding.

What it looks like

examples/a_wrong_number.py builds a customer table with five ordinary bugs in it, none of which any schema test can see. Run it and the first thing printed is the reassuring part: no nulls, correct dtypes, plausible ranges. Everything passes. That is the moment the number gets shipped.

Then:

CRITICAL clock_in_disguise [churn_risk]
    every value falls inside the Unix epoch range for seconds.
CRITICAL informative_missingness [support_note]
    whether this is missing predicts 'churned'.
    evidence: churned|missing=0.000  churned|present=1.000  z=-65.6
CRITICAL duplicate_rows
    300 of 4,300 rows (7.0%) are exact duplicates.
CRITICAL parse_carnage [signup_date]
    holds 2 different date formats in one column.
WARNING  placeholders [credit_limit]
    12.1% of values are exactly -999, a conventional 'no data' marker.

What each one costs:

  • The "highest-risk customers, call these first" list is the five most recent signups. The risk score was never computed.
  • Drop the rows with a missing support note -- a standard cleaning step -- and the churn rate goes from 50% to 100%.
  • The reporting job ran twice on Tuesday, so every total is 7% too high.
  • Average credit limit was reported as 4,275. Every real limit is 5,000.
  • Half the signup dates would silently become missing.

A real example

Four US grid operators publish their interconnection queues. Merged into one frame and audited with no configuration:

CRITICAL parse_carnage [Queue Date]
    holds 4 different date formats in one column. Parsed together, one format
    wins and the rest become NaT -- which is indistinguishable from data that
    was simply absent.
    evidence: formats=4  examples={'2025-10-08T00:37:52+00:00': '40%',
              '2003-11-18 08:00:00': '24%', '2008-01-30': '19%',
              '1/14/2025': '18%'}  values=9,640

CRITICAL duplicate_rows [Queue ID]
    declared key is not unique: 1,351 duplicate row(s) across ['Queue ID'].

Both are real. The first cost 7,400 of 9,640 dates on an earlier pandas, and the loss was invisible because it looked like ordinary missing data. The second would have silently corrupted every change record built on that key.

Finding them by hand took hours. pipelie finds them in under a second, and it found two more date columns with the same defect that had not been checked at all.

Design

Silence is the default. A checker that fires on clean data gets muted, and a muted checker is worse than none. Thresholds favour silence over noise, anything uncertain is a warning rather than a critical, and the test suite contains explicit "must stay quiet" cases on clean frames.

Every finding carries its evidence. Not a prose summary — the counts, shares and correlations behind the claim, so you can check it rather than trust it.

Nothing is mutated. audit() reads. What to do about a finding is your call.

What it does not do

  • It does not check orchestration. A DAG that reports success when the write threw, a timeout that cannot stop the work, or a test that passes by never running the code are all real failures, and none of them are visible in the resulting table.
  • It does not know your domain. It cannot tell you a capacity figure is in the wrong unit, only that it is oddly constant.
  • Silence is not proof of correctness. It means these particular lies are absent.

Background

The failure modes come from Thirteen Ways a Pipeline Lies, a write-up of thirteen documented bugs in one system, each of which reported green.

Apache-2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pipelie-0.1.0.tar.gz (20.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pipelie-0.1.0-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file pipelie-0.1.0.tar.gz.

File metadata

  • Download URL: pipelie-0.1.0.tar.gz
  • Upload date:
  • Size: 20.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for pipelie-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3cd3ae356b2bf7c2888375a1c953c5b0e9e7ab375dba08854a5c039a356c8028
MD5 9b154b1abb157630e54cbed4ed800908
BLAKE2b-256 114bf2497a7c99f7c6409008658a53b9ccf4c13b25412a137ae0c03f8370ab0d

See more details on using hashes here.

File details

Details for the file pipelie-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pipelie-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for pipelie-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1b548302598486fccb5cd683a72e0491e0be3221fb0015fef94c086bab7df64a
MD5 ee7a3996a7f77c74915a6a37f7fca360
BLAKE2b-256 7cb9f1ea644e5ffbfaf8270686834f8c2dccbb361f28182506bed850981a6bd7

See more details on using hashes here.

Release history Release notifications | RSS feed

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page