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.
- carquet-0.7.2/CMakeLists.txt +49 -0
- carquet-0.7.2/LICENSE +21 -0
- carquet-0.7.2/PKG-INFO +194 -0
- carquet-0.7.2/README.md +155 -0
- carquet-0.7.2/native/CMakeLists.txt +1194 -0
- carquet-0.7.2/native/LICENSE +21 -0
- carquet-0.7.2/native/cmake/carquet.pc.in +13 -0
- carquet-0.7.2/native/cmake/carquetConfig.cmake.in +16 -0
- carquet-0.7.2/native/include/carquet/carquet.h +3958 -0
- carquet-0.7.2/native/include/carquet/error.h +306 -0
- carquet-0.7.2/native/include/carquet/types.h +292 -0
- carquet-0.7.2/native/src/cli/cli.h +95 -0
- carquet-0.7.2/native/src/cli/codegen.c +163 -0
- carquet-0.7.2/native/src/cli/codegen_read.c +603 -0
- carquet-0.7.2/native/src/cli/codegen_write.c +13 -0
- carquet-0.7.2/native/src/cli/commands.c +2236 -0
- carquet-0.7.2/native/src/cli/main.c +479 -0
- carquet-0.7.2/native/src/compression/brotli.c +104 -0
- carquet-0.7.2/native/src/compression/custom.c +60 -0
- carquet-0.7.2/native/src/compression/custom.h +28 -0
- carquet-0.7.2/native/src/compression/gzip.c +231 -0
- carquet-0.7.2/native/src/compression/lz4.c +213 -0
- carquet-0.7.2/native/src/compression/snappy.c +952 -0
- carquet-0.7.2/native/src/compression/snappy.h +82 -0
- carquet-0.7.2/native/src/compression/zstd.c +254 -0
- carquet-0.7.2/native/src/core/allocator.c +95 -0
- carquet-0.7.2/native/src/core/allocator.h +31 -0
- carquet-0.7.2/native/src/core/arena.c +301 -0
- carquet-0.7.2/native/src/core/arena.h +171 -0
- carquet-0.7.2/native/src/core/bitpack.c +577 -0
- carquet-0.7.2/native/src/core/bitpack.h +353 -0
- carquet-0.7.2/native/src/core/buffer.c +426 -0
- carquet-0.7.2/native/src/core/buffer.h +302 -0
- carquet-0.7.2/native/src/core/compat.h +57 -0
- carquet-0.7.2/native/src/core/decimal_cmp.h +56 -0
- carquet-0.7.2/native/src/core/endian.c +13 -0
- carquet-0.7.2/native/src/core/endian.h +388 -0
- carquet-0.7.2/native/src/core/error.c +370 -0
- carquet-0.7.2/native/src/core/float16.h +46 -0
- carquet-0.7.2/native/src/core/geo_wkb.c +178 -0
- carquet-0.7.2/native/src/core/geo_wkb.h +36 -0
- carquet-0.7.2/native/src/encoding/byte_stream_split.c +311 -0
- carquet-0.7.2/native/src/encoding/delta.c +693 -0
- carquet-0.7.2/native/src/encoding/delta_length.c +250 -0
- carquet-0.7.2/native/src/encoding/delta_strings.c +496 -0
- carquet-0.7.2/native/src/encoding/dictionary.c +1059 -0
- carquet-0.7.2/native/src/encoding/dictionary.h +72 -0
- carquet-0.7.2/native/src/encoding/plain.c +503 -0
- carquet-0.7.2/native/src/encoding/plain.h +251 -0
- carquet-0.7.2/native/src/encoding/rle.c +799 -0
- carquet-0.7.2/native/src/encoding/rle.h +303 -0
- carquet-0.7.2/native/src/metadata/bloom_filter.c +373 -0
- carquet-0.7.2/native/src/metadata/page_index.c +1111 -0
- carquet-0.7.2/native/src/metadata/schema.c +835 -0
- carquet-0.7.2/native/src/reader/arrow_c_export.c +796 -0
- carquet-0.7.2/native/src/reader/arrow_c_read.c +776 -0
- carquet-0.7.2/native/src/reader/arrow_schema_read.c +295 -0
- carquet-0.7.2/native/src/reader/arrow_schema_read.h +50 -0
- carquet-0.7.2/native/src/reader/batch_reader.c +3821 -0
- carquet-0.7.2/native/src/reader/column_reader.c +203 -0
- carquet-0.7.2/native/src/reader/file_reader.c +1604 -0
- carquet-0.7.2/native/src/reader/mmap_reader.c +364 -0
- carquet-0.7.2/native/src/reader/page_filter.c +958 -0
- carquet-0.7.2/native/src/reader/page_filter.h +83 -0
- carquet-0.7.2/native/src/reader/page_reader.c +3157 -0
- carquet-0.7.2/native/src/reader/reader_internal.h +348 -0
- carquet-0.7.2/native/src/reader/row_group_reader.c +16 -0
- carquet-0.7.2/native/src/reader/statistics.c +437 -0
- carquet-0.7.2/native/src/reader/worker_pool.c +258 -0
- carquet-0.7.2/native/src/reader/worker_pool.h +119 -0
- carquet-0.7.2/native/src/simd/arm/neon_ops.c +1591 -0
- carquet-0.7.2/native/src/simd/arm/sve_ops.c +914 -0
- carquet-0.7.2/native/src/simd/detect.c +276 -0
- carquet-0.7.2/native/src/simd/dispatch.c +1463 -0
- carquet-0.7.2/native/src/simd/simd_unaligned.h +50 -0
- carquet-0.7.2/native/src/simd/x86/avx2_ops.c +1466 -0
- carquet-0.7.2/native/src/simd/x86/avx512_ops.c +1245 -0
- carquet-0.7.2/native/src/simd/x86/avx_ops.c +375 -0
- carquet-0.7.2/native/src/simd/x86/sse_ops.c +1498 -0
- carquet-0.7.2/native/src/thrift/parquet_types.c +1982 -0
- carquet-0.7.2/native/src/thrift/parquet_types.h +561 -0
- carquet-0.7.2/native/src/thrift/thrift_decode.c +514 -0
- carquet-0.7.2/native/src/thrift/thrift_decode.h +265 -0
- carquet-0.7.2/native/src/thrift/thrift_encode.c +202 -0
- carquet-0.7.2/native/src/thrift/thrift_encode.h +232 -0
- carquet-0.7.2/native/src/util/crc32.c +32 -0
- carquet-0.7.2/native/src/util/xxhash.c +118 -0
- carquet-0.7.2/native/src/writer/arrow_c_import.c +1072 -0
- carquet-0.7.2/native/src/writer/arrow_schema.c +637 -0
- carquet-0.7.2/native/src/writer/arrow_schema.h +45 -0
- carquet-0.7.2/native/src/writer/column_writer.c +2027 -0
- carquet-0.7.2/native/src/writer/file_writer.c +3850 -0
- carquet-0.7.2/native/src/writer/page_tasks.h +112 -0
- carquet-0.7.2/native/src/writer/page_writer.c +2381 -0
- carquet-0.7.2/native/src/writer/row_group_writer.c +1251 -0
- carquet-0.7.2/pyproject.toml +130 -0
- carquet-0.7.2/src/carquet/__init__.py +395 -0
- carquet-0.7.2/src/carquet/_arrow.py +70 -0
- carquet-0.7.2/src/carquet/_ffi.py +283 -0
- carquet-0.7.2/src/carquet/errors.py +101 -0
- carquet-0.7.2/src/carquet/py.typed +0 -0
- 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>`).
|
carquet-0.7.2/README.md
ADDED
|
@@ -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>`).
|