.agents/skills/update-deps/references/migration-gotchas.md
Load this when a dependency bump breaks tests or pyright/ruff with a library API change.
Each entry is a pattern that was actually hit and fixed in this codebase. Apply the minimal
fix and keep it consistent with sibling code.
DataFrame.swapaxes removed → np.array_split(df, n) returns ndarraysnp.array_split internally calls np.swapaxes, which used to delegate to
DataFrame.swapaxes and return DataFrames with column names intact. In pandas 3.0
swapaxes was removed (deprecated in 2.1), so np.array_split(df, n) now yields plain
numpy arrays. Rebuilding with pd.DataFrame(fold) produces integer RangeIndex columns,
so later df["some_column"] raises KeyError.
Symptom: KeyError: '<column>' with a traceback ending in
pandas/core/indexes/range.py ... get_loc.
Fix — split positional indices instead of the frame, then select rows with iloc:
# Broken under pandas 3.0
return [pd.DataFrame(fold) for fold in np.array_split(reports, n)]
# Fixed — preserves columns, dtypes, and even fold sizes
return [reports.iloc[indices] for indices in np.array_split(np.arange(len(reports)), n)]
copy= keyword removed from merge/concat/join/set_axis etc.pandas 3.0 makes Copy-on-Write the default and drops the copy= parameter. Calls like
df.merge(other, copy=False) or pd.concat([...], copy=False) raise TypeError.
Fix: delete the copy= argument — CoW already avoids the unnecessary copy.
inplace under Copy-on-WriteWith CoW, mutating a slice (df[mask]["col"] = x) no longer writes back and may warn/error.
Assign through .loc: df.loc[mask, "col"] = x. Reassign results of inplace=True-style
operations rather than relying on in-place mutation of a view.
np.float_, np.int0, np.bool8, np.object0, etc.) — use the builtin
or the explicit sized dtype (np.float64, np.bool_).np.array_split on a DataFrame no longer preserves the frame (see the pandas entry above).RUF069 (float-equality-comparison): x == 0.0 / != 0.0 is flagged. Prefer a
non-equality guard when semantically valid (x <= 0.0 for a non-positive divide guard) or
math.isclose(...) for tolerance checks. Avoid a blanket # noqa when a real fix exists.
ASYNC119 (yield in context manager in async generator): do not yield while holding a
with/async with in an async generator. Materialize inside the block, then yield after
it closes — matching the sibling providers:
with Path.open(path, "r", encoding=enc) as f:
rows = list(csv.DictReader(f))
for row in rows:
yield transform(row)
dev group pins pandas-stubs~=3.0. When bumping
pandas, bump the matching stubs so pyright reflects the new API.pyright may surface new optional/overload errors from updated
stubs; fix at the call site rather than suppressing, unless the stub is demonstrably wrong.uv run poe check and
uv run poe test_unit after each fix to confirm.