Skip to content

Stop write_gsf mutating the caller's DataFrame - #104

Merged
lispandfound merged 1 commit into
mainfrom
fix/89-write-gsf-no-mutate
Sep 9, 2026
Merged

Stop write_gsf mutating the caller's DataFrame#104
lispandfound merged 1 commit into
mainfrom
fix/89-write-gsf-no-mutate

Conversation

@lispandfound

Copy link
Copy Markdown
Contributor

Fixes #89

Problem

write_gsf filled its optional columns by assigning straight into the caller's DataFrame:

# before
if "init_time" not in gsf_df:
    gsf_df["init_time"] = -1
if "slip" not in gsf_df:
    gsf_df["slip"] = -1
if "loc_rake" not in gsf_df:
    raise ValueError("The DataFrame must have a 'loc_rake' column.")

A frame handed to write_gsf came back carrying two columns of -1 sentinels it never had. Since write_gsf is a serialisation function, nothing in its signature or docstring suggests the input is consumed.

Because the assignments sat above the loc_rake check, the mutation also happened on the failure path — write_gsf could raise, write no file, and still leave the caller's frame modified.

Impact

df = pd.DataFrame({... "loc_rake": [0.0], "seg_no": [0]})
write_gsf(df, path)
list(df.columns)
# before: [..., seg_no, init_time, slip]   values -1
# after:  [..., seg_no]

Sentinel -1 values silently persisting in a caller's slip/init_time columns are the kind of thing that reads as real data downstream.

Changes

  • source_modelling/gsf.py — fill defaults on a shallow copy (copy(deep=False); only new columns are added, the existing column data is never written through, so a deep copy would be wasted work on large fault meshes). Move the loc_rake validation above the defaulting so the failure path leaves nothing behind.
  • tests/test_gsf.py — add test_write_gsf_does_not_mutate_input, covering both the success and the raising path.

No change to the file written — the same columns are serialised with the same defaults.

Verification

  • New test fails without the fix, passes with it.
  • pytest tests/test_gsf.py — 13 passed.
  • ruff check, ruff format, numpydoc lint clean.

🤖 Generated with Claude Code

write_gsf filled the optional init_time and slip columns by assigning
directly into the DataFrame it was given, so a frame passed in came back
carrying two columns of -1 sentinels it never had. Because the
assignments sat above the loc_rake validation, the mutation happened even
when write_gsf went on to raise and write nothing.

Fills the defaults on a shallow copy instead, and moves the loc_rake
validation above the defaulting so the failure path leaves no debris.

Fixes #89

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lispandfound
lispandfound merged commit 653e6bc into main Sep 9, 2026
7 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.

gsf.py: write_gsf mutates the caller's DataFrame, even on the failure path

1 participant