Skip to content

write_table() format auto-detection is always broken: extension keeps its leading dot #175

Description

@jinskeep-morpc

Summary

morpc.write_table() raises RuntimeError whenever format is not passed explicitly. It derives the format from the file extension as ".csv" — with the leading dot — but PANDAS_EXPORT_ARGS_OVERRIDE is keyed "csv", so the lookup never matches.

The auto-detection branch has therefore never worked for any format. Confirmed on main at v0.5.18.

Reproduction

import morpc, pandas as pd, tempfile, os

df = pd.DataFrame({"a": [1, 2]})
path = os.path.join(tempfile.mkdtemp(), "t.csv")

morpc.write_table(df, path, index=False)              # RuntimeError
morpc.write_table(df, path, format="csv", index=False) # works
morpc.write_table | INFO | Format is unspecified. Will attempt to determine format based on file extension.
morpc.write_table | ERROR | This function does not currently support format .csv.
  Add export arguments for this format in morpc.PANDAS_EXPORT_ARGS_OVERRIDE or use the
  native pandas export functions.
RuntimeError

Note the error message itself shows the mismatch: format .csv against a dict keyed csv.

Cause

morpc/morpc.py line 3041:

format = os.path.splitext(path)[1]     # -> ".csv", not "csv"

against the dict at line 11:

PANDAS_EXPORT_ARGS_OVERRIDE = {
    "csv": {"lineterminator": "\r\n"},
    "xlsx": None
}

The lookup at line 3046 raises KeyError, which is converted to RuntimeError. Even if the lookup were made tolerant, the dispatch at lines 3055 and 3057 compares format == "csv" / == "xlsx" and would fall through to the same error, so the dot has to be stripped at the source.

Fix

-        format = os.path.splitext(path)[1]
+        format = os.path.splitext(path)[1].lstrip(".")

That fixes both the lookup and the dispatch.

Alternatively, use the EXTENSION_MAP idiom already established in morpc/frictionless/frictionless.py (lines 610-612), which is keyed by extension with the dot and maps to the bare format name. create_resource() gets this right for the same file — it logs Format not specified. Using format derived from data file extension: csv — so the two functions in the same package currently disagree about what "format" means.

Why this has gone unnoticed

Every MORPC standardize repository I checked passes format="csv" explicitly, so nothing in production hits the auto-detection path:

write_table(features, OUTPUT_PATH, format="csv", index=False)
write_table(facilities, FACILITIES_PATH, format="csv", index=False)
write_table(summaryOut, SUMMARY_PATH, format="csv", index=False)

The exception is morpc-repotemplate-standardize, whose summary-table cell calls morpc.write_table(summaryOut, SUMMARY_PATH, index=False) with no format. So every new repository scaffolded from the template fails at that cell until someone adds format="csv". repo_setup.md line 161 also tells authors to "Write CSVs with morpc.write_table()" without mentioning the argument.

Worth fixing the template as well as the function, or the next scaffolded repo hits it again.

Impact

Found while converting morpc-ohiodbhfacilities-standardize to GeoPackage releases; that repository was scaffolded from the template and inherited the argument-less call. It now passes format="csv" explicitly with a comment pointing at this behaviour.

Related: #174, also in code that resolves a format or driver and then fails to use it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions