fix(pfs): patch pfs.pipe.pl via proxy module — test poisoned global polars.DataFrame, corrupting duckdb's replacement-scan type cache (refs #598)

This commit is contained in:
kert
2026-08-12 13:12:41 -04:00
parent 223c6e1e95
commit 2c39737060

View File

@@ -489,7 +489,22 @@ class TestLoadZipCarrierEdges:
p.write_text(line) p.write_text(line)
f = _f("ZIP5_2024.txt", 2024, ".txt", path=str(p)) f = _f("ZIP5_2024.txt", 2024, ".txt", path=str(p))
# Patch pl.DataFrame to drop year_quarter before the if-check # Patch pl.DataFrame to drop year_quarter before the if-check.
#
# IMPORTANT: patch the module *binding* pfs.pipe.pl, not the
# attribute pfs.pipe.pl.DataFrame. `pl` inside pfs.pipe is the
# real `polars` module object, so `patch("pfs.pipe.pl.DataFrame")`
# would replace `polars.DataFrame` globally (process-wide) for
# the duration of the `with` block. If duckdb resolves its
# Python replacement-scan type for polars DataFrames for the
# first time while that global is a MagicMock, it caches the
# MagicMock as "the polars DataFrame type" for the rest of the
# process, and every later `SELECT * FROM df` scan anywhere in
# the suite fails with
# "TypeError: isinstance() arg 2 must be a type, a tuple of
# types, or a union". Using a standalone proxy module for the
# `pl` name (with the real polars attributes copied in) keeps
# the real `polars.DataFrame` class untouched.
_orig_df = pl.DataFrame _orig_df = pl.DataFrame
def _df_no_yq(rows): def _df_no_yq(rows):
@@ -498,7 +513,11 @@ class TestLoadZipCarrierEdges:
df = df.drop("year_quarter") df = df.drop("year_quarter")
return df return df
with patch("pfs.pipe.pl.DataFrame", side_effect=_df_no_yq): fake_pl = types.ModuleType("fake_polars_proxy")
fake_pl.__dict__.update(pl.__dict__)
fake_pl.DataFrame = _df_no_yq
with patch("pfs.pipe.pl", fake_pl):
result = _load_zip_carrier_files([f], con) result = _load_zip_carrier_files([f], con)
assert result["rows"] >= 1 assert result["rows"] >= 1