Datagrunt 4.3.0: First-Class Parquet Reading and Writing
Datagrunt has been able to write Parquet for a long time — CSVWriter.write_parquet() and, more recently, ExcelWriter.write_parquet() have shipped for releases. What it could never do was start from one. If your data already lived in a Parquet file, you were on your own.
Datagrunt 4.3.0 closes that gap with a full, symmetric pair: ParquetReader and ParquetWriter, designed to feel exactly like the CSV, Excel, and PDF classes you already know.
Reading a Parquet file
from datagrunt import ParquetReader
reader = ParquetReader("data.parquet")
reader.to_dataframe() # as a Polars DataFrame
reader.to_arrow_table() # ... as a PyArrow Table
reader.to_dicts() # ... as a list of row dictsA path that is not a Parquet file raises a ValueError at construction, and a missing path raises FileNotFoundError — mistakes surface immediately, not deep inside a parse. Empty or blank files return empty results rather than raising.
Reading is powered by a single canonical engine — Polars (pl.read_parquet) — so there is no engine argument to choose. Parquet is natively columnar and Arrow-backed, so no new dependency was needed: polars, pyarrow, and duckdb already ship with Datagrunt.
One table, no sheets
Where the defining feature of Excel is its multiple worksheets, Parquet is the opposite: a flat, single table. So ParquetReader drops the sheet dimension entirely — there is no .sheets property and no sheet= argument. The API is the clean, flat surface you know from the CSV classes.
You can still run SQL against the file — it is registered into an on-demand DuckDB connection under db_table:
reader.query_data(f"SELECT * FROM {reader.db_table} LIMIT 5")The full Polars read surface, passed straight through
Because reading is Polars-only, you get the entire read_parquet option set for free via **read_options — set once on the constructor (applied to every read) or per call (per-call wins):
# Read only the columns you need
reader.to_dataframe(columns=["name", "city"])
# Cap the read
reader.to_dataframe(n_rows=100)The key source is reserved — the file path is controlled exclusively through the constructor — and passing it raises a ValueError.
Same mechanism as the Excel reader. If you read the 4.2.0 Excel post, this is the identical passthrough: read options are keyword arguments forwarded verbatim to Polars. The only reason these examples look different from the Excel ones is that the underlying Polars functions differ.
pl.read_parquetexposes its options as plain top-level keyword arguments (columns,n_rows, …), so you pass them directly — there is no dict to wrap them in.pl.read_excelinstead groups its calamine options inside a parameter namedread_options, which is why the Excel examples showread_options={...}. That’s a difference in the Polars signatures, not in Datagrunt.
Writing it back out
ParquetWriter reads a Parquet file and exports it to any of Datagrunt’s formats. And because writing is Polars-only too, **write_options pass straight through to the underlying writer — so you can re-encode a Parquet file with a different compression codec in one line:
from datagrunt import ParquetWriter
writer = ParquetWriter("data.parquet")
writer.write_csv("out.csv")
writer.write_json("out.json")
writer.write_json_newline_delimited("out.jsonl")
writer.write_excel("out.xlsx")
# Re-encode Parquet → Parquet; compression= passes through to Polars
writer.write_parquet("recompressed.parquet", compression="zstd")Empty or blank source files produce a single empty (0-byte) output file rather than raising.
Consistent with the rest of Datagrunt
- Same
normalize_columns. Set it on the constructor (or per call) to get normalized, collision-safe column names everywhere — using the exact same implementation as the CSV and Excel APIs. - Automatic resource lifecycle. You never have to call
.close(). The DuckDB connection that backsquery_datais opened on demand and released when the reader goes out of scope;close()and the context-manager protocol are available for early release. - Preserve, not transform. Values are written verbatim. As with CSV and Excel, a value beginning with
=,+,-, or@is treated as a formula when opened in a spreadsheet application (CSV/formula injection, CWE-1236) — Datagrunt documents this and leaves sanitization to the application layer rather than silently mutating your data.
Upgrade today
Datagrunt 4.3.0 is available on PyPI:
uv pip install --upgrade datagruntfrom datagrunt import ParquetReader, ParquetWriter