carquet 0.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. carquet-0.7.2/CMakeLists.txt +49 -0
  2. carquet-0.7.2/LICENSE +21 -0
  3. carquet-0.7.2/PKG-INFO +194 -0
  4. carquet-0.7.2/README.md +155 -0
  5. carquet-0.7.2/native/CMakeLists.txt +1194 -0
  6. carquet-0.7.2/native/LICENSE +21 -0
  7. carquet-0.7.2/native/cmake/carquet.pc.in +13 -0
  8. carquet-0.7.2/native/cmake/carquetConfig.cmake.in +16 -0
  9. carquet-0.7.2/native/include/carquet/carquet.h +3958 -0
  10. carquet-0.7.2/native/include/carquet/error.h +306 -0
  11. carquet-0.7.2/native/include/carquet/types.h +292 -0
  12. carquet-0.7.2/native/src/cli/cli.h +95 -0
  13. carquet-0.7.2/native/src/cli/codegen.c +163 -0
  14. carquet-0.7.2/native/src/cli/codegen_read.c +603 -0
  15. carquet-0.7.2/native/src/cli/codegen_write.c +13 -0
  16. carquet-0.7.2/native/src/cli/commands.c +2236 -0
  17. carquet-0.7.2/native/src/cli/main.c +479 -0
  18. carquet-0.7.2/native/src/compression/brotli.c +104 -0
  19. carquet-0.7.2/native/src/compression/custom.c +60 -0
  20. carquet-0.7.2/native/src/compression/custom.h +28 -0
  21. carquet-0.7.2/native/src/compression/gzip.c +231 -0
  22. carquet-0.7.2/native/src/compression/lz4.c +213 -0
  23. carquet-0.7.2/native/src/compression/snappy.c +952 -0
  24. carquet-0.7.2/native/src/compression/snappy.h +82 -0
  25. carquet-0.7.2/native/src/compression/zstd.c +254 -0
  26. carquet-0.7.2/native/src/core/allocator.c +95 -0
  27. carquet-0.7.2/native/src/core/allocator.h +31 -0
  28. carquet-0.7.2/native/src/core/arena.c +301 -0
  29. carquet-0.7.2/native/src/core/arena.h +171 -0
  30. carquet-0.7.2/native/src/core/bitpack.c +577 -0
  31. carquet-0.7.2/native/src/core/bitpack.h +353 -0
  32. carquet-0.7.2/native/src/core/buffer.c +426 -0
  33. carquet-0.7.2/native/src/core/buffer.h +302 -0
  34. carquet-0.7.2/native/src/core/compat.h +57 -0
  35. carquet-0.7.2/native/src/core/decimal_cmp.h +56 -0
  36. carquet-0.7.2/native/src/core/endian.c +13 -0
  37. carquet-0.7.2/native/src/core/endian.h +388 -0
  38. carquet-0.7.2/native/src/core/error.c +370 -0
  39. carquet-0.7.2/native/src/core/float16.h +46 -0
  40. carquet-0.7.2/native/src/core/geo_wkb.c +178 -0
  41. carquet-0.7.2/native/src/core/geo_wkb.h +36 -0
  42. carquet-0.7.2/native/src/encoding/byte_stream_split.c +311 -0
  43. carquet-0.7.2/native/src/encoding/delta.c +693 -0
  44. carquet-0.7.2/native/src/encoding/delta_length.c +250 -0
  45. carquet-0.7.2/native/src/encoding/delta_strings.c +496 -0
  46. carquet-0.7.2/native/src/encoding/dictionary.c +1059 -0
  47. carquet-0.7.2/native/src/encoding/dictionary.h +72 -0
  48. carquet-0.7.2/native/src/encoding/plain.c +503 -0
  49. carquet-0.7.2/native/src/encoding/plain.h +251 -0
  50. carquet-0.7.2/native/src/encoding/rle.c +799 -0
  51. carquet-0.7.2/native/src/encoding/rle.h +303 -0
  52. carquet-0.7.2/native/src/metadata/bloom_filter.c +373 -0
  53. carquet-0.7.2/native/src/metadata/page_index.c +1111 -0
  54. carquet-0.7.2/native/src/metadata/schema.c +835 -0
  55. carquet-0.7.2/native/src/reader/arrow_c_export.c +796 -0
  56. carquet-0.7.2/native/src/reader/arrow_c_read.c +776 -0
  57. carquet-0.7.2/native/src/reader/arrow_schema_read.c +295 -0
  58. carquet-0.7.2/native/src/reader/arrow_schema_read.h +50 -0
  59. carquet-0.7.2/native/src/reader/batch_reader.c +3821 -0
  60. carquet-0.7.2/native/src/reader/column_reader.c +203 -0
  61. carquet-0.7.2/native/src/reader/file_reader.c +1604 -0
  62. carquet-0.7.2/native/src/reader/mmap_reader.c +364 -0
  63. carquet-0.7.2/native/src/reader/page_filter.c +958 -0
  64. carquet-0.7.2/native/src/reader/page_filter.h +83 -0
  65. carquet-0.7.2/native/src/reader/page_reader.c +3157 -0
  66. carquet-0.7.2/native/src/reader/reader_internal.h +348 -0
  67. carquet-0.7.2/native/src/reader/row_group_reader.c +16 -0
  68. carquet-0.7.2/native/src/reader/statistics.c +437 -0
  69. carquet-0.7.2/native/src/reader/worker_pool.c +258 -0
  70. carquet-0.7.2/native/src/reader/worker_pool.h +119 -0
  71. carquet-0.7.2/native/src/simd/arm/neon_ops.c +1591 -0
  72. carquet-0.7.2/native/src/simd/arm/sve_ops.c +914 -0
  73. carquet-0.7.2/native/src/simd/detect.c +276 -0
  74. carquet-0.7.2/native/src/simd/dispatch.c +1463 -0
  75. carquet-0.7.2/native/src/simd/simd_unaligned.h +50 -0
  76. carquet-0.7.2/native/src/simd/x86/avx2_ops.c +1466 -0
  77. carquet-0.7.2/native/src/simd/x86/avx512_ops.c +1245 -0
  78. carquet-0.7.2/native/src/simd/x86/avx_ops.c +375 -0
  79. carquet-0.7.2/native/src/simd/x86/sse_ops.c +1498 -0
  80. carquet-0.7.2/native/src/thrift/parquet_types.c +1982 -0
  81. carquet-0.7.2/native/src/thrift/parquet_types.h +561 -0
  82. carquet-0.7.2/native/src/thrift/thrift_decode.c +514 -0
  83. carquet-0.7.2/native/src/thrift/thrift_decode.h +265 -0
  84. carquet-0.7.2/native/src/thrift/thrift_encode.c +202 -0
  85. carquet-0.7.2/native/src/thrift/thrift_encode.h +232 -0
  86. carquet-0.7.2/native/src/util/crc32.c +32 -0
  87. carquet-0.7.2/native/src/util/xxhash.c +118 -0
  88. carquet-0.7.2/native/src/writer/arrow_c_import.c +1072 -0
  89. carquet-0.7.2/native/src/writer/arrow_schema.c +637 -0
  90. carquet-0.7.2/native/src/writer/arrow_schema.h +45 -0
  91. carquet-0.7.2/native/src/writer/column_writer.c +2027 -0
  92. carquet-0.7.2/native/src/writer/file_writer.c +3850 -0
  93. carquet-0.7.2/native/src/writer/page_tasks.h +112 -0
  94. carquet-0.7.2/native/src/writer/page_writer.c +2381 -0
  95. carquet-0.7.2/native/src/writer/row_group_writer.c +1251 -0
  96. carquet-0.7.2/pyproject.toml +130 -0
  97. carquet-0.7.2/src/carquet/__init__.py +395 -0
  98. carquet-0.7.2/src/carquet/_arrow.py +70 -0
  99. carquet-0.7.2/src/carquet/_ffi.py +283 -0
  100. carquet-0.7.2/src/carquet/errors.py +101 -0
  101. carquet-0.7.2/src/carquet/py.typed +0 -0
  102. carquet-0.7.2/tests/test_carquet.py +252 -0
@@ -0,0 +1,49 @@
1
+ # Thin CMake entry point for the Python wheel (scikit-build-core).
2
+ #
3
+ # It builds the C library from the repository root (or from `native/` inside an
4
+ # sdist, where the C tree is copied by `sdist.force-include` in pyproject.toml)
5
+ # as one self-contained shared library, and installs *only* that library into
6
+ # the `carquet` Python package directory. Headers, the CMake package config,
7
+ # the pkg-config file and the CLI are deliberately left out of the wheel.
8
+ cmake_minimum_required(VERSION 3.16)
9
+ project(carquet_python LANGUAGES C)
10
+
11
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/native/CMakeLists.txt")
12
+ # Unpacked sdist: the C sources were copied under python/native/.
13
+ set(CARQUET_C_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/native")
14
+ elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../include/carquet/carquet.h")
15
+ # Git checkout: python/ sits next to the C tree.
16
+ get_filename_component(CARQUET_C_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
17
+ else()
18
+ message(FATAL_ERROR "carquet C sources not found (expected ../include/carquet/carquet.h or native/)")
19
+ endif()
20
+ message(STATUS "carquet Python wheel: building C library from ${CARQUET_C_SOURCE_DIR}")
21
+
22
+ # Wheel build configuration. These are plain cache overrides of the root
23
+ # project's options; pyproject.toml passes the same values via cmake.define so
24
+ # a manual `cmake -S python` behaves identically.
25
+ set(CARQUET_BUILD_SHARED ON CACHE BOOL "wheel: single shared library" FORCE)
26
+ set(CARQUET_BUILD_CLI OFF CACHE BOOL "wheel: no CLI" FORCE)
27
+ set(CARQUET_BUILD_DEV OFF CACHE BOOL "wheel: no tests/examples/benchmarks" FORCE)
28
+ set(CARQUET_BUILD_TESTS OFF CACHE BOOL "" FORCE)
29
+ set(CARQUET_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
30
+ set(CARQUET_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
31
+ set(CARQUET_BUILD_INTEROP OFF CACHE BOOL "" FORCE)
32
+ set(CARQUET_BUNDLE_DEPS ON CACHE BOOL "wheel: static zstd/zlib/lz4/brotli inside libcarquet" FORCE)
33
+
34
+ # The root CMakeLists.txt only emits install() rules when it is the top-level
35
+ # project, so adding it as a subdirectory gives us the `carquet` target with no
36
+ # install rules of its own; the fetched codec subprojects' install rules are
37
+ # skipped too because only the component below is installed
38
+ # (tool.scikit-build.install.components in pyproject.toml).
39
+ add_subdirectory("${CARQUET_C_SOURCE_DIR}" carquet-c)
40
+
41
+ # Land the runtime library directly in the package directory
42
+ # (wheel.install-dir = "carquet" makes CMAKE_INSTALL_PREFIX that directory):
43
+ # carquet/libcarquet.so | carquet/libcarquet.dylib | carquet/carquet.dll
44
+ install(TARGETS carquet
45
+ COMPONENT carquet_python
46
+ LIBRARY DESTINATION "."
47
+ RUNTIME DESTINATION "."
48
+ ARCHIVE DESTINATION "." EXCLUDE_FROM_ALL # MSVC import library: not needed by ctypes
49
+ )
carquet-0.7.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Johan HG Natter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
carquet-0.7.2/PKG-INFO ADDED
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.1
2
+ Name: carquet
3
+ Version: 0.7.2
4
+ Summary: Fast Apache Parquet reader/writer in pure C, exposed to Python through the Arrow C Data Interface
5
+ Keywords: parquet,arrow,columnar,pyarrow,zstd,snappy
6
+ Author: Johan H. G. Natter
7
+ License: MIT
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: C
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Database
24
+ Classifier: Topic :: Scientific/Engineering
25
+ Classifier: Topic :: Software Development :: Libraries
26
+ Classifier: Typing :: Typed
27
+ Project-URL: Homepage, https://github.com/Vitruves/carquet
28
+ Project-URL: Documentation, https://github.com/Vitruves/carquet/blob/main/python/README.md
29
+ Project-URL: Repository, https://github.com/Vitruves/carquet
30
+ Project-URL: Changelog, https://github.com/Vitruves/carquet/blob/main/CHANGELOG.md
31
+ Project-URL: Issues, https://github.com/Vitruves/carquet/issues
32
+ Requires-Python: >=3.9
33
+ Provides-Extra: arrow
34
+ Requires-Dist: pyarrow>=14; extra == "arrow"
35
+ Provides-Extra: test
36
+ Requires-Dist: pytest; extra == "test"
37
+ Requires-Dist: pyarrow>=14; extra == "test"
38
+ Description-Content-Type: text/markdown
39
+
40
+ # carquet for Python
41
+
42
+ Python bindings for [carquet](https://github.com/Vitruves/carquet), a pure C11 Apache Parquet reader and writer. The wheel bundles the C library (with zstd, zlib, LZ4, Snappy and Brotli compiled in) and talks to it through `ctypes`; tables cross the boundary via the [Arrow C Data Interface](https://arrow.apache.org/docs/format/CDataInterface.html), so a `pyarrow.Table` is produced or consumed without any Python-side decoding or copying of the Parquet logic.
43
+
44
+ ```bash
45
+ pip install carquet # library only: info(), validate(), lib_version()
46
+ pip install "carquet[arrow]" # + pyarrow, needed for read_table / write_table / schema / open_reader
47
+ ```
48
+
49
+ Requires Python 3.9+. PyArrow (>= 14) is an optional runtime dependency: it is imported lazily by the table-level functions only.
50
+
51
+ ## Quick start
52
+
53
+ ```python
54
+ import pyarrow as pa
55
+ import carquet
56
+
57
+ table = pa.table({"id": pa.array(range(5), pa.int64()), "name": ["a", "b", None, "d", "e"]})
58
+
59
+ carquet.write_table(table, "out.parquet", compression="zstd")
60
+ back = carquet.read_table("out.parquet") # pyarrow.Table
61
+ assert back.equals(table)
62
+
63
+ fi = carquet.info("out.parquet") # footer only, no pyarrow needed
64
+ print(fi.num_rows, fi.num_row_groups, fi.num_columns, fi.file_size, fi.created_by)
65
+
66
+ carquet.validate("out.parquet") # raises carquet.CarquetError on a damaged file
67
+ print(carquet.__version__, carquet.lib_version())
68
+ ```
69
+
70
+ ## API
71
+
72
+ ### `carquet.read_table(path, *, columns=None, use_mmap=True) -> pyarrow.Table`
73
+
74
+ Reads every row group with `carquet_reader_read_arrow()`; each row group becomes one chunk of the returned table, nested `struct` / `list` / `map` columns included. `columns` selects (and orders) top-level fields by name after the read. `use_mmap=False` reads through buffered I/O instead of a memory map.
75
+
76
+ ### `carquet.write_table(table, path, *, compression="zstd", compression_level=None, row_group_size=None, write_page_index=False, write_statistics=True, data_page_version=1, store_schema=True, write_crc=True) -> None`
77
+
78
+ Writes a `pyarrow.Table` (or a single `RecordBatch`). The Arrow schema is converted with `carquet_arrow_import_schema()` and every row group is handed over as a struct `ArrowArray` to `carquet_writer_write_arrow()`.
79
+
80
+ | Argument | Meaning |
81
+ |---|---|
82
+ | `compression` | `"none"`/`"uncompressed"`, `"snappy"`, `"gzip"`, `"lz4"` (LZ4_RAW, what PyArrow/DuckDB/Spark write), `"lz4_hadoop"` (legacy framed LZ4), `"zstd"` (default), `"brotli"` |
83
+ | `compression_level` | codec level (zstd 1-22, gzip 1-9, brotli 0-11); `None` uses the codec default |
84
+ | `row_group_size` | maximum rows per row group; default 1 048 576 like `pyarrow.parquet.write_table` |
85
+ | `write_page_index` | emit the column index / offset index (page-level pruning for readers that support it) |
86
+ | `write_statistics` | min/max statistics per column chunk |
87
+ | `data_page_version` | `1` (DATA_PAGE) or `2` (DATA_PAGE_V2) |
88
+ | `store_schema` | embed the Arrow schema as the `ARROW:schema` footer entry (flat schemas) so PyArrow restores exact Arrow types |
89
+ | `write_crc` | page CRC32 checksums |
90
+
91
+ Brotli is compiled into the published wheels. A build of the library without it makes `write_table(..., compression="brotli")` raise `CarquetError` with `status == StatusCode.UNSUPPORTED_CODEC`.
92
+
93
+ ### `carquet.schema(path) -> pyarrow.Schema`
94
+
95
+ The file schema via `carquet_arrow_export_schema()`, without reading data.
96
+
97
+ ### `carquet.info(path) -> FileInfo`
98
+
99
+ Dataclass with `path`, `num_rows`, `num_row_groups`, `num_columns` (leaf columns), `file_size`, `version` (Parquet format version in the footer) and `created_by`. Only the footer is read (`carquet_get_file_info`). Works without PyArrow.
100
+
101
+ ### `carquet.validate(path) -> None`
102
+
103
+ Structural validation (`carquet_validate_file`: magic bytes, footer, metadata). Raises `CarquetError` on failure. Works without PyArrow.
104
+
105
+ ### `carquet.open_reader(path, *, use_mmap=True, verify_checksums=True, num_threads=0) -> Reader`
106
+
107
+ Keeps the file open for row-group-level access:
108
+
109
+ ```python
110
+ with carquet.open_reader("out.parquet") as r:
111
+ print(r.num_rows, r.num_row_groups, r.num_columns, r.schema)
112
+ first = r.read_row_group(0) # pyarrow.RecordBatch
113
+ for batch in r.iter_batches(): # all row groups, in order
114
+ ...
115
+ whole = r.read_all(columns=["id"]) # pyarrow.Table
116
+ ```
117
+
118
+ ### Errors
119
+
120
+ Every failing C call raises `carquet.CarquetError`. It carries `code` (the numeric `carquet_status_t`), `status` (a `carquet.StatusCode` `IntEnum`, e.g. `StatusCode.FILE_NOT_FOUND`), `message` (the library's detailed message, never empty) and, when the library sets them, `offset`, `column_index` and `row_group_index`.
121
+
122
+ ```python
123
+ try:
124
+ carquet.info("missing.parquet")
125
+ except carquet.CarquetError as e:
126
+ print(e.status.name, e.code, e.message)
127
+ ```
128
+
129
+ ### Versions
130
+
131
+ `carquet.__version__` is the package version; `carquet.lib_version()` is `carquet_version()` from the loaded shared library. Both come from `CARQUET_VERSION_STRING` in `include/carquet/carquet.h` at build time.
132
+
133
+ ## Interoperability
134
+
135
+ Files written by `carquet.write_table` are standard Parquet (format version 2 footer, PLAIN / RLE_DICTIONARY encodings, optional DATA_PAGE_V2 and page index), readable by every mainstream implementation:
136
+
137
+ ```python
138
+ import pyarrow.parquet as pq
139
+ pq.read_table("out.parquet")
140
+
141
+ import duckdb
142
+ duckdb.sql("SELECT count(*) FROM 'out.parquet'")
143
+
144
+ import polars as pl
145
+ pl.read_parquet("out.parquet")
146
+ ```
147
+
148
+ The reverse direction works the same: `carquet.read_table` opens files produced by PyArrow, DuckDB, Spark, polars and others (the test suite round-trips through PyArrow in both directions).
149
+
150
+ ## How the library is bundled
151
+
152
+ The wheel contains the pure-Python package and one shared library placed next to `__init__.py`: `carquet/libcarquet.so` (Linux), `carquet/libcarquet.dylib` (macOS) or `carquet/carquet.dll` (Windows). It is built from the repository's own `CMakeLists.txt` with `CARQUET_BUILD_SHARED=ON`, `CARQUET_BUILD_CLI=OFF`, `CARQUET_BUILD_DEV=OFF` and `CARQUET_BUNDLE_DEPS=ON`, so zstd, zlib, LZ4 and Brotli are fetched at build time and linked statically into that single file (Snappy and LZ4_RAW are implemented inside carquet). The result depends only on the C runtime and system libraries. Linux wheels keep OpenMP (parallel column reads, page-parallel encoding); `auditwheel` grafts `libgomp` into the wheel. The macOS and Windows wheels are built without OpenMP so they carry no `libomp`/`vcomp` dependency.
153
+
154
+ The loader (`carquet/_ffi.py`) looks for the library next to the package, then in `lib/`, `lib64/` and `bin/` under it (layouts a plain `cmake --install` produces), then on the system path. Set the `CARQUET_LIBRARY` environment variable to an explicit file to override.
155
+
156
+ ## Building the wheel locally
157
+
158
+ From the repository root, with CMake >= 3.16, a C11 compiler and git (for the codec sources) available:
159
+
160
+ ```bash
161
+ pip wheel ./python -w dist/ # or: python -m build python/ --outdir dist/
162
+ pip install dist/carquet-*.whl
163
+ pytest python/tests # needs pytest + pyarrow
164
+ ```
165
+
166
+ `pip install -e ./python` is not supported (the shared library must be installed into the package); use a regular install. If your shell exports `CC`, `CFLAGS`, `LDFLAGS` or `SDKROOT` for another toolchain, clear them for the build (`env -u CC -u CFLAGS -u CPPFLAGS -u LDFLAGS -u SDKROOT pip wheel ./python -w dist/`).
167
+
168
+ The build is driven by `python/CMakeLists.txt`, a thin wrapper that adds the repository root as a subdirectory and installs only the runtime library (CMake component `carquet_python`) into the package directory. `pyproject.toml` (scikit-build-core) does the rest.
169
+
170
+ ### Source distribution
171
+
172
+ The sdist root is `python/`, so the C tree one level up cannot be referenced as `..` once unpacked. `tool.scikit-build.sdist.force-include` therefore copies `CMakeLists.txt`, `LICENSE`, `include/`, `src/` and `cmake/` into `native/` inside the sdist, `python/CMakeLists.txt` prefers `native/` when it exists, and a `[[tool.scikit-build.overrides]]` entry (`if.from-sdist = true`) points the version regex at `native/include/carquet/carquet.h`. `python -m build --sdist python/` produces it; `pip install carquet-<version>.tar.gz` compiles it like the git checkout.
173
+
174
+ ### Release wheels
175
+
176
+ `.github/workflows/wheels.yml` builds wheels with [cibuildwheel](https://cibuildwheel.pypa.io/) for CPython 3.9 - 3.13 on Linux (x86_64, aarch64; manylinux_2_28), macOS (arm64, x86_64) and Windows (AMD64), runs this test suite against each wheel, builds the sdist and publishes everything to PyPI via trusted publishing when a `v*` tag is pushed.
177
+
178
+ ## Supported platforms
179
+
180
+ | Platform | Wheels | Notes |
181
+ |---|---|---|
182
+ | Linux x86_64 / aarch64 (glibc 2.28+) | yes | SSE/AVX2/AVX-512 or NEON dispatched at runtime; OpenMP on |
183
+ | macOS 11+ arm64, macOS 10.13+ x86_64 | yes | no OpenMP |
184
+ | Windows x64 | yes | MSVC build, no OpenMP; Windows loader path is covered by CI only |
185
+ | musl, 32-bit, PyPy | source build only | no PyArrow wheels exist there to test against |
186
+
187
+ ## Limitations
188
+
189
+ - No Parquet modular encryption (not implemented in the C library).
190
+ - Nested data (struct / list / map) goes through the Arrow bridge (`carquet_reader_read_arrow` / `carquet_writer_write_arrow`), which assembles each row group as a fresh copy; the zero-copy batch export in the C API is not used by the Python package.
191
+ - `columns=` is applied after the row group is read (no column projection in the Arrow read path yet).
192
+ - Row groups are materialised one at a time; there is no streaming/iterator API below row-group granularity.
193
+ - Unions and run-end-encoded Arrow arrays are rejected on write (no Parquet equivalent); dictionary-encoded Arrow columns are written as their values.
194
+ - `created_by` is always the library's own string (`Carquet <version>`).
@@ -0,0 +1,155 @@
1
+ # carquet for Python
2
+
3
+ Python bindings for [carquet](https://github.com/Vitruves/carquet), a pure C11 Apache Parquet reader and writer. The wheel bundles the C library (with zstd, zlib, LZ4, Snappy and Brotli compiled in) and talks to it through `ctypes`; tables cross the boundary via the [Arrow C Data Interface](https://arrow.apache.org/docs/format/CDataInterface.html), so a `pyarrow.Table` is produced or consumed without any Python-side decoding or copying of the Parquet logic.
4
+
5
+ ```bash
6
+ pip install carquet # library only: info(), validate(), lib_version()
7
+ pip install "carquet[arrow]" # + pyarrow, needed for read_table / write_table / schema / open_reader
8
+ ```
9
+
10
+ Requires Python 3.9+. PyArrow (>= 14) is an optional runtime dependency: it is imported lazily by the table-level functions only.
11
+
12
+ ## Quick start
13
+
14
+ ```python
15
+ import pyarrow as pa
16
+ import carquet
17
+
18
+ table = pa.table({"id": pa.array(range(5), pa.int64()), "name": ["a", "b", None, "d", "e"]})
19
+
20
+ carquet.write_table(table, "out.parquet", compression="zstd")
21
+ back = carquet.read_table("out.parquet") # pyarrow.Table
22
+ assert back.equals(table)
23
+
24
+ fi = carquet.info("out.parquet") # footer only, no pyarrow needed
25
+ print(fi.num_rows, fi.num_row_groups, fi.num_columns, fi.file_size, fi.created_by)
26
+
27
+ carquet.validate("out.parquet") # raises carquet.CarquetError on a damaged file
28
+ print(carquet.__version__, carquet.lib_version())
29
+ ```
30
+
31
+ ## API
32
+
33
+ ### `carquet.read_table(path, *, columns=None, use_mmap=True) -> pyarrow.Table`
34
+
35
+ Reads every row group with `carquet_reader_read_arrow()`; each row group becomes one chunk of the returned table, nested `struct` / `list` / `map` columns included. `columns` selects (and orders) top-level fields by name after the read. `use_mmap=False` reads through buffered I/O instead of a memory map.
36
+
37
+ ### `carquet.write_table(table, path, *, compression="zstd", compression_level=None, row_group_size=None, write_page_index=False, write_statistics=True, data_page_version=1, store_schema=True, write_crc=True) -> None`
38
+
39
+ Writes a `pyarrow.Table` (or a single `RecordBatch`). The Arrow schema is converted with `carquet_arrow_import_schema()` and every row group is handed over as a struct `ArrowArray` to `carquet_writer_write_arrow()`.
40
+
41
+ | Argument | Meaning |
42
+ |---|---|
43
+ | `compression` | `"none"`/`"uncompressed"`, `"snappy"`, `"gzip"`, `"lz4"` (LZ4_RAW, what PyArrow/DuckDB/Spark write), `"lz4_hadoop"` (legacy framed LZ4), `"zstd"` (default), `"brotli"` |
44
+ | `compression_level` | codec level (zstd 1-22, gzip 1-9, brotli 0-11); `None` uses the codec default |
45
+ | `row_group_size` | maximum rows per row group; default 1 048 576 like `pyarrow.parquet.write_table` |
46
+ | `write_page_index` | emit the column index / offset index (page-level pruning for readers that support it) |
47
+ | `write_statistics` | min/max statistics per column chunk |
48
+ | `data_page_version` | `1` (DATA_PAGE) or `2` (DATA_PAGE_V2) |
49
+ | `store_schema` | embed the Arrow schema as the `ARROW:schema` footer entry (flat schemas) so PyArrow restores exact Arrow types |
50
+ | `write_crc` | page CRC32 checksums |
51
+
52
+ Brotli is compiled into the published wheels. A build of the library without it makes `write_table(..., compression="brotli")` raise `CarquetError` with `status == StatusCode.UNSUPPORTED_CODEC`.
53
+
54
+ ### `carquet.schema(path) -> pyarrow.Schema`
55
+
56
+ The file schema via `carquet_arrow_export_schema()`, without reading data.
57
+
58
+ ### `carquet.info(path) -> FileInfo`
59
+
60
+ Dataclass with `path`, `num_rows`, `num_row_groups`, `num_columns` (leaf columns), `file_size`, `version` (Parquet format version in the footer) and `created_by`. Only the footer is read (`carquet_get_file_info`). Works without PyArrow.
61
+
62
+ ### `carquet.validate(path) -> None`
63
+
64
+ Structural validation (`carquet_validate_file`: magic bytes, footer, metadata). Raises `CarquetError` on failure. Works without PyArrow.
65
+
66
+ ### `carquet.open_reader(path, *, use_mmap=True, verify_checksums=True, num_threads=0) -> Reader`
67
+
68
+ Keeps the file open for row-group-level access:
69
+
70
+ ```python
71
+ with carquet.open_reader("out.parquet") as r:
72
+ print(r.num_rows, r.num_row_groups, r.num_columns, r.schema)
73
+ first = r.read_row_group(0) # pyarrow.RecordBatch
74
+ for batch in r.iter_batches(): # all row groups, in order
75
+ ...
76
+ whole = r.read_all(columns=["id"]) # pyarrow.Table
77
+ ```
78
+
79
+ ### Errors
80
+
81
+ Every failing C call raises `carquet.CarquetError`. It carries `code` (the numeric `carquet_status_t`), `status` (a `carquet.StatusCode` `IntEnum`, e.g. `StatusCode.FILE_NOT_FOUND`), `message` (the library's detailed message, never empty) and, when the library sets them, `offset`, `column_index` and `row_group_index`.
82
+
83
+ ```python
84
+ try:
85
+ carquet.info("missing.parquet")
86
+ except carquet.CarquetError as e:
87
+ print(e.status.name, e.code, e.message)
88
+ ```
89
+
90
+ ### Versions
91
+
92
+ `carquet.__version__` is the package version; `carquet.lib_version()` is `carquet_version()` from the loaded shared library. Both come from `CARQUET_VERSION_STRING` in `include/carquet/carquet.h` at build time.
93
+
94
+ ## Interoperability
95
+
96
+ Files written by `carquet.write_table` are standard Parquet (format version 2 footer, PLAIN / RLE_DICTIONARY encodings, optional DATA_PAGE_V2 and page index), readable by every mainstream implementation:
97
+
98
+ ```python
99
+ import pyarrow.parquet as pq
100
+ pq.read_table("out.parquet")
101
+
102
+ import duckdb
103
+ duckdb.sql("SELECT count(*) FROM 'out.parquet'")
104
+
105
+ import polars as pl
106
+ pl.read_parquet("out.parquet")
107
+ ```
108
+
109
+ The reverse direction works the same: `carquet.read_table` opens files produced by PyArrow, DuckDB, Spark, polars and others (the test suite round-trips through PyArrow in both directions).
110
+
111
+ ## How the library is bundled
112
+
113
+ The wheel contains the pure-Python package and one shared library placed next to `__init__.py`: `carquet/libcarquet.so` (Linux), `carquet/libcarquet.dylib` (macOS) or `carquet/carquet.dll` (Windows). It is built from the repository's own `CMakeLists.txt` with `CARQUET_BUILD_SHARED=ON`, `CARQUET_BUILD_CLI=OFF`, `CARQUET_BUILD_DEV=OFF` and `CARQUET_BUNDLE_DEPS=ON`, so zstd, zlib, LZ4 and Brotli are fetched at build time and linked statically into that single file (Snappy and LZ4_RAW are implemented inside carquet). The result depends only on the C runtime and system libraries. Linux wheels keep OpenMP (parallel column reads, page-parallel encoding); `auditwheel` grafts `libgomp` into the wheel. The macOS and Windows wheels are built without OpenMP so they carry no `libomp`/`vcomp` dependency.
114
+
115
+ The loader (`carquet/_ffi.py`) looks for the library next to the package, then in `lib/`, `lib64/` and `bin/` under it (layouts a plain `cmake --install` produces), then on the system path. Set the `CARQUET_LIBRARY` environment variable to an explicit file to override.
116
+
117
+ ## Building the wheel locally
118
+
119
+ From the repository root, with CMake >= 3.16, a C11 compiler and git (for the codec sources) available:
120
+
121
+ ```bash
122
+ pip wheel ./python -w dist/ # or: python -m build python/ --outdir dist/
123
+ pip install dist/carquet-*.whl
124
+ pytest python/tests # needs pytest + pyarrow
125
+ ```
126
+
127
+ `pip install -e ./python` is not supported (the shared library must be installed into the package); use a regular install. If your shell exports `CC`, `CFLAGS`, `LDFLAGS` or `SDKROOT` for another toolchain, clear them for the build (`env -u CC -u CFLAGS -u CPPFLAGS -u LDFLAGS -u SDKROOT pip wheel ./python -w dist/`).
128
+
129
+ The build is driven by `python/CMakeLists.txt`, a thin wrapper that adds the repository root as a subdirectory and installs only the runtime library (CMake component `carquet_python`) into the package directory. `pyproject.toml` (scikit-build-core) does the rest.
130
+
131
+ ### Source distribution
132
+
133
+ The sdist root is `python/`, so the C tree one level up cannot be referenced as `..` once unpacked. `tool.scikit-build.sdist.force-include` therefore copies `CMakeLists.txt`, `LICENSE`, `include/`, `src/` and `cmake/` into `native/` inside the sdist, `python/CMakeLists.txt` prefers `native/` when it exists, and a `[[tool.scikit-build.overrides]]` entry (`if.from-sdist = true`) points the version regex at `native/include/carquet/carquet.h`. `python -m build --sdist python/` produces it; `pip install carquet-<version>.tar.gz` compiles it like the git checkout.
134
+
135
+ ### Release wheels
136
+
137
+ `.github/workflows/wheels.yml` builds wheels with [cibuildwheel](https://cibuildwheel.pypa.io/) for CPython 3.9 - 3.13 on Linux (x86_64, aarch64; manylinux_2_28), macOS (arm64, x86_64) and Windows (AMD64), runs this test suite against each wheel, builds the sdist and publishes everything to PyPI via trusted publishing when a `v*` tag is pushed.
138
+
139
+ ## Supported platforms
140
+
141
+ | Platform | Wheels | Notes |
142
+ |---|---|---|
143
+ | Linux x86_64 / aarch64 (glibc 2.28+) | yes | SSE/AVX2/AVX-512 or NEON dispatched at runtime; OpenMP on |
144
+ | macOS 11+ arm64, macOS 10.13+ x86_64 | yes | no OpenMP |
145
+ | Windows x64 | yes | MSVC build, no OpenMP; Windows loader path is covered by CI only |
146
+ | musl, 32-bit, PyPy | source build only | no PyArrow wheels exist there to test against |
147
+
148
+ ## Limitations
149
+
150
+ - No Parquet modular encryption (not implemented in the C library).
151
+ - Nested data (struct / list / map) goes through the Arrow bridge (`carquet_reader_read_arrow` / `carquet_writer_write_arrow`), which assembles each row group as a fresh copy; the zero-copy batch export in the C API is not used by the Python package.
152
+ - `columns=` is applied after the row group is read (no column projection in the Arrow read path yet).
153
+ - Row groups are materialised one at a time; there is no streaming/iterator API below row-group granularity.
154
+ - Unions and run-end-encoded Arrow arrays are rejected on write (no Parquet equivalent); dictionary-encoded Arrow columns are written as their values.
155
+ - `created_by` is always the library's own string (`Carquet <version>`).