Skip to content

compare: keep column names with '..' as flat keys for dictdiffer#51

Open
HrachShah wants to merge 2 commits into
simonw:mainfrom
HrachShah:fix/double-dot-column-name-40
Open

compare: keep column names with '..' as flat keys for dictdiffer#51
HrachShah wants to merge 2 commits into
simonw:mainfrom
HrachShah:fix/double-dot-column-name-40

Conversation

@HrachShah

Copy link
Copy Markdown

Fixes #40

dictdiffer's default dot_notation=True interprets .. in a key as a path with a parent step. When a column is named name..date_range, dictdiffer parses it as a nested path rather than a literal key, and emits add/remove 2-tuples for column renames instead of change 3-tuples. The existing per-row unpack in compare() then raises ValueError: not enough values to unpack (expected 2, got 1).

This PR passes dot_notation=False to the diff() call so every top-level column name is treated as a flat string. The ignore_columns lookup is also affected: with dot_notation=True a column named ..foo is never matched by the ignore list because dictdiffer sees it as a path, not a literal key. The per-row unpack in compare() is unchanged; with dot_notation=False the path is always a list, so the existing field[0] if isinstance(field, list) else field flattening still turns it back into the right column name.

Reproducer (from the issue):

printf '%s\n' '[{"_id":"X","name.date_range":null,"name.official_title.date_range":"[1979 TO 1992]"}]' > a.json
printf '%s\n' '[{"_id":"X","name..date_range":null,"name.official_title.date_range":"[1979 TO 1993]"}]' > b.json
csv-diff --format=json --key=_id a.json b.json

Pre-fix: ValueError: not enough values to unpack (expected 2, got 1).
Post-fix: returns the expected diff with name..date_range listed under columns_added and name.date_range under columns_removed.

Includes two new tests in tests/test_csv_diff.py:

  • test_compare_handles_double_dot_in_column_name — the column-rename case
  • test_compare_handles_double_dot_in_column_value_change — value change in a .. column, which already worked (since the result is still a change 3-tuple) but was unwrapped with a fragile field-string code path; now unwrapped via the list path the new option forces.

All 28 tests pass.

Zo Bot added 2 commits July 4, 2026 03:41
The csv module's underlying C parser caps each field at 131072 bytes by
default. load_csv had no protection against that, so a row with a long
string (e.g. a nucleotide sequence, a large JSON blob, an embedded log
line) raised an uncaught _csv.Error instead of being returned to the
caller.

Set csv.field_size_limit(sys.maxsize) at the top of load_csv so the
parser is allowed to handle the longest field the caller can possibly
give it. sys.maxsize is the platform's PY_SSIZE_T_MAX and matches the
recommendation in the Python csv docs for "set the limit as high as
possible" without overflowing the parser.

Closes simonw#41.
dictdiffer's default dot_notation=True interprets '..' in a key as a
path with a parent step, so a column named 'name..date_range' gets
parsed as if it were nested under a 'name' key. When a row sits in
both previous and current but the column has been renamed from
'name.date_range' to 'name..date_range', the resulting diff contains
'add' and 'remove' 2-tuples for the column swap instead of 'change'
3-tuples, and the existing per-row unpack in compare() raises
'ValueError: not enough values to unpack (expected 2, got 1)'.

compare() now passes dot_notation=False so dictdiffer treats every
top-level column name as a flat string. The 'ignore_columns' lookup
is also affected - with dot_notation=True a column named '..foo' is
never matched by the ignore list because dictdiffer sees it as a
path, not a literal key. The per-row unpack is unchanged; with
dot_notation=False the path is always a list, so the existing
'field[0] if isinstance(field, list) else field' flattening still
turns it back into the right column name.

Closes simonw#40.
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.

Error when column name contains ".." (two dots)

1 participant