pybcsv 1.5.7__tar.gz → 1.5.10.dev0__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 (120) hide show
  1. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/PKG-INFO +39 -1
  2. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/README.md +38 -0
  3. pybcsv-1.5.10.dev0/VERSION.txt +1 -0
  4. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/__init__.py +72 -15
  5. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/__init__.pyi +7 -5
  6. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/_bcsv.pyi +44 -124
  7. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/_version.py +3 -3
  8. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/bindings.cpp +524 -463
  9. pybcsv-1.5.10.dev0/pybcsv/parquet_utils.py +1248 -0
  10. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pyproject.toml +4 -0
  11. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_cycle4_python_hardening.py +51 -29
  12. pybcsv-1.5.10.dev0/tests/test_parquet_conversion.py +518 -0
  13. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_performance_edge_cases.py +173 -122
  14. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_arrow.py +68 -56
  15. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_core.py +76 -20
  16. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_pandas.py +138 -96
  17. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_performance.py +34 -23
  18. pybcsv-1.5.10.dev0/tests/test_streaming_bindings.py +499 -0
  19. pybcsv-1.5.7/VERSION.txt +0 -1
  20. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/BUILD_NOTES.md +0 -0
  21. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/CMakeLists.txt +0 -0
  22. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/LICENSE +0 -0
  23. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/MANIFEST.in +0 -0
  24. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/benchmarks/README.md +0 -0
  25. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/benchmarks/run_pybcsv_benchmarks.py +0 -0
  26. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/examples/README.md +0 -0
  27. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/examples/advanced_usage.py +0 -0
  28. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/examples/basic_usage.py +0 -0
  29. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/examples/pandas_integration.py +0 -0
  30. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/examples/performance_benchmark.py +0 -0
  31. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/bcsv.h +0 -0
  32. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/bcsv.hpp +0 -0
  33. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/bcsv_c_api.h +0 -0
  34. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/bitset.h +0 -0
  35. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/bitset.hpp +0 -0
  36. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/byte_buffer.h +0 -0
  37. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/checksum.hpp +0 -0
  38. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_file/file_codec_concept.h +0 -0
  39. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_file/file_codec_dispatch.h +0 -0
  40. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_file/file_codec_packet001.h +0 -0
  41. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_file/file_codec_packet_lz4_001.h +0 -0
  42. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_file/file_codec_packet_lz4_batch001.h +0 -0
  43. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_file/file_codec_stream001.h +0 -0
  44. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_file/file_codec_stream_lz4_001.h +0 -0
  45. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_row/row_codec_delta002.h +0 -0
  46. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_row/row_codec_delta002.hpp +0 -0
  47. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_row/row_codec_dispatch.h +0 -0
  48. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_row/row_codec_flat001.h +0 -0
  49. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_row/row_codec_flat001.hpp +0 -0
  50. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_row/row_codec_zoh001.h +0 -0
  51. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/codec_row/row_codec_zoh001.hpp +0 -0
  52. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/column_name_index.h +0 -0
  53. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/column_name_index.hpp +0 -0
  54. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/csv_reader.h +0 -0
  55. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/csv_reader.hpp +0 -0
  56. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/csv_writer.h +0 -0
  57. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/csv_writer.hpp +0 -0
  58. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/definitions.h +0 -0
  59. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/file_footer.h +0 -0
  60. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/file_header.h +0 -0
  61. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/file_header.hpp +0 -0
  62. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/layout.h +0 -0
  63. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/layout.hpp +0 -0
  64. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/layout_guard.h +0 -0
  65. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/lz4_block.hpp +0 -0
  66. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/lz4_stream.hpp +0 -0
  67. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/packet_header.h +0 -0
  68. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/reader.h +0 -0
  69. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/reader.hpp +0 -0
  70. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/reader_concept.h +0 -0
  71. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/row.h +0 -0
  72. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/row.hpp +0 -0
  73. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/row_visitors.h +0 -0
  74. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler.h +0 -0
  75. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler.hpp +0 -0
  76. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_ast.h +0 -0
  77. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_compiler.h +0 -0
  78. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_parser.h +0 -0
  79. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_tokenizer.h +0 -0
  80. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_types.h +0 -0
  81. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_vm.h +0 -0
  82. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_vm.hpp +0 -0
  83. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/sampler/sampler_window.h +0 -0
  84. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/std_charconv_compat.h +0 -0
  85. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/vle.hpp +0 -0
  86. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/writer.h +0 -0
  87. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/writer.hpp +0 -0
  88. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/bcsv/writer_concept.h +0 -0
  89. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4.c +0 -0
  90. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4.h +0 -0
  91. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4file.c +0 -0
  92. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4file.h +0 -0
  93. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4frame.c +0 -0
  94. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4frame.h +0 -0
  95. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4frame_static.h +0 -0
  96. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4hc.c +0 -0
  97. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/lz4hc.h +0 -0
  98. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/xxhash.c +0 -0
  99. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/lz4-1.10.0/xxhash.h +0 -0
  100. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/xxHash-0.8.3/xxhash.c +0 -0
  101. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/include/xxHash-0.8.3/xxhash.h +0 -0
  102. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/__version__.py +0 -0
  103. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/pandas_utils.py +0 -0
  104. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/polars_utils.py +0 -0
  105. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/pybcsv/py.typed +0 -0
  106. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/sync_headers.py +0 -0
  107. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/README.md +0 -0
  108. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/conftest.py +0 -0
  109. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_basic_functionality.py +0 -0
  110. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_count_rows.py +0 -0
  111. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_csv_writer_reader.py +0 -0
  112. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_data_types.py +0 -0
  113. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_interoperability.py +0 -0
  114. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_minimal_string.py +0 -0
  115. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_codecs.py +0 -0
  116. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_columnar.py +0 -0
  117. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_direct_access.py +0 -0
  118. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_errors.py +0 -0
  119. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_polars.py +0 -0
  120. {pybcsv-1.5.7 → pybcsv-1.5.10.dev0}/tests/test_pybcsv_sampler.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybcsv
3
- Version: 1.5.7
3
+ Version: 1.5.10.dev0
4
4
  Summary: High-performance Python bindings for the BCSV (Binary CSV) library with pandas integration
5
5
  Keywords: csv,binary,data-science,compression,performance,pandas,numpy,big-data,data-processing
6
6
  Author-Email: Tobias Weber <weber.tobias.md@gmail.com>
@@ -156,6 +156,44 @@ with pybcsv.ReaderDirectAccess() as da:
156
156
  print(da.read(100)) # alternative syntax
157
157
  ```
158
158
 
159
+ ## Parquet Conversion Tools (CLI)
160
+
161
+ Installing `pybcsv` provides two streaming command-line converters (require the
162
+ `arrow` extra: `pip install pybcsv[arrow]`). They stream in bounded batches, so
163
+ they handle files larger than memory.
164
+
165
+ ```bash
166
+ # Parquet → BCSV
167
+ parquet2bcsv input.parquet -o output.bcsv
168
+ # --row-codec {delta,zoh,flat} row codec (default: delta)
169
+ # --file-codec {packet_lz4_batch,...} file codec (default: packet_lz4_batch)
170
+ # --chunk-size N rows per streamed batch (default: 512000)
171
+ # -f/--force overwrite an existing output
172
+
173
+ # BCSV → Parquet
174
+ bcsv2parquet input.bcsv -o output.parquet
175
+ # --columns "a,b,c" select/reorder columns (order is honored)
176
+ # --slice 10:100 Python-style row slice
177
+ # --unflatten (default) reconstruct nested structs from dotted/bracketed names
178
+ # --no-unflatten keep flat columns (names like 'a.b', 'vals[0]')
179
+ # --parquet-compression {none,snappy,gzip,zstd,lz4}
180
+ ```
181
+
182
+ **Schema mapping.** Parquet structs and fixed-size lists are *flattened* to BCSV
183
+ columns using dotted (`location.lat`) and bracketed (`vals[0]`) names;
184
+ `bcsv2parquet --unflatten` reverses this. Notes and limitations:
185
+
186
+ - **No nulls:** BCSV has no null representation — a null in any converted column
187
+ is rejected with the offending row number. Filter nulls before converting.
188
+ - **Type widening:** `float16`/`bfloat16` widen to `float32`; `large_string`
189
+ maps to `string`.
190
+ - **Unsupported types** (variable-length lists, maps, timestamps, decimals,
191
+ dictionaries) are rejected with a clear error.
192
+ - **Column names ending in `_`** are rejected (the unflatten escape protocol
193
+ reserves trailing underscores).
194
+ - **Colliding names:** if a literal dotted column (`a.b`) and a struct path both
195
+ map to the same nested path, `--unflatten` fails loudly; use `--no-unflatten`.
196
+
159
197
  ## Available Types
160
198
 
161
199
  | Constant | Description |
@@ -110,6 +110,44 @@ with pybcsv.ReaderDirectAccess() as da:
110
110
  print(da.read(100)) # alternative syntax
111
111
  ```
112
112
 
113
+ ## Parquet Conversion Tools (CLI)
114
+
115
+ Installing `pybcsv` provides two streaming command-line converters (require the
116
+ `arrow` extra: `pip install pybcsv[arrow]`). They stream in bounded batches, so
117
+ they handle files larger than memory.
118
+
119
+ ```bash
120
+ # Parquet → BCSV
121
+ parquet2bcsv input.parquet -o output.bcsv
122
+ # --row-codec {delta,zoh,flat} row codec (default: delta)
123
+ # --file-codec {packet_lz4_batch,...} file codec (default: packet_lz4_batch)
124
+ # --chunk-size N rows per streamed batch (default: 512000)
125
+ # -f/--force overwrite an existing output
126
+
127
+ # BCSV → Parquet
128
+ bcsv2parquet input.bcsv -o output.parquet
129
+ # --columns "a,b,c" select/reorder columns (order is honored)
130
+ # --slice 10:100 Python-style row slice
131
+ # --unflatten (default) reconstruct nested structs from dotted/bracketed names
132
+ # --no-unflatten keep flat columns (names like 'a.b', 'vals[0]')
133
+ # --parquet-compression {none,snappy,gzip,zstd,lz4}
134
+ ```
135
+
136
+ **Schema mapping.** Parquet structs and fixed-size lists are *flattened* to BCSV
137
+ columns using dotted (`location.lat`) and bracketed (`vals[0]`) names;
138
+ `bcsv2parquet --unflatten` reverses this. Notes and limitations:
139
+
140
+ - **No nulls:** BCSV has no null representation — a null in any converted column
141
+ is rejected with the offending row number. Filter nulls before converting.
142
+ - **Type widening:** `float16`/`bfloat16` widen to `float32`; `large_string`
143
+ maps to `string`.
144
+ - **Unsupported types** (variable-length lists, maps, timestamps, decimals,
145
+ dictionaries) are rejected with a clear error.
146
+ - **Column names ending in `_`** are rejected (the unflatten escape protocol
147
+ reserves trailing underscores).
148
+ - **Colliding names:** if a literal dotted column (`a.b`) and a struct path both
149
+ map to the same nested path, `--unflatten` fails loudly; use `--no-unflatten`.
150
+
113
151
  ## Available Types
114
152
 
115
153
  | Constant | Description |
@@ -0,0 +1 @@
1
+ 1.5.9
@@ -1,8 +1,8 @@
1
1
  # Copyright (c) 2025-2026 Tobias Weber <weber.tobias.md@gmail.com>
2
- #
2
+ #
3
3
  # This file is part of the BCSV library.
4
- #
5
- # Licensed under the MIT License. See LICENSE file in the project root
4
+ #
5
+ # Licensed under the MIT License. See LICENSE file in the project root
6
6
  # for full license information.
7
7
 
8
8
  """pybcsv - Python bindings for the BCSV library."""
@@ -12,10 +12,12 @@ from .__version__ import __version__
12
12
  # Import the compiled extension module — fail immediately if not available
13
13
  try:
14
14
  from ._bcsv import *
15
+
15
16
  _BINDINGS_AVAILABLE = True
16
17
  except ImportError:
17
18
  try:
18
- from _bcsv import * # fallback for legacy in-tree builds
19
+ from _bcsv import * # fallback for legacy in-tree builds
20
+
19
21
  _BINDINGS_AVAILABLE = True
20
22
  except ImportError as _exc:
21
23
  raise ImportError(
@@ -27,35 +29,86 @@ except ImportError:
27
29
  # Try to import pandas utilities if pandas is available
28
30
  try:
29
31
  from .pandas_utils import write_dataframe, read_dataframe, to_csv, from_csv
32
+
30
33
  _PANDAS_UTILS_AVAILABLE = True
31
34
  except ImportError:
32
35
  _PANDAS_UTILS_AVAILABLE = False
33
-
36
+
34
37
  # Create stub functions that raise ImportError
35
38
  def write_dataframe(*args, **kwargs):
36
- raise ImportError("pandas is not available. Please install pandas to use DataFrame functions.")
37
-
39
+ raise ImportError(
40
+ "pandas is not available. Please install pandas to use DataFrame functions."
41
+ )
42
+
38
43
  def read_dataframe(*args, **kwargs):
39
- raise ImportError("pandas is not available. Please install pandas to use DataFrame functions.")
40
-
44
+ raise ImportError(
45
+ "pandas is not available. Please install pandas to use DataFrame functions."
46
+ )
47
+
41
48
  def to_csv(*args, **kwargs):
42
- raise ImportError("pandas is not available. Please install pandas to use CSV conversion functions.")
43
-
49
+ raise ImportError(
50
+ "pandas is not available. Please install pandas to use CSV conversion functions."
51
+ )
52
+
44
53
  def from_csv(*args, **kwargs):
45
- raise ImportError("pandas is not available. Please install pandas to use CSV conversion functions.")
54
+ raise ImportError(
55
+ "pandas is not available. Please install pandas to use CSV conversion functions."
56
+ )
57
+
46
58
 
47
59
  # Try to import Polars utilities if polars + pyarrow are available
48
60
  try:
49
61
  from .polars_utils import read_polars, write_polars
62
+
50
63
  _POLARS_UTILS_AVAILABLE = True
51
64
  except ImportError:
52
65
  _POLARS_UTILS_AVAILABLE = False
53
66
 
54
67
  def read_polars(*args, **kwargs):
55
- raise ImportError("polars and pyarrow are required. Install with: pip install pybcsv[polars,arrow]")
68
+ raise ImportError(
69
+ "polars and pyarrow are required. Install with: pip install pybcsv[polars,arrow]"
70
+ )
56
71
 
57
72
  def write_polars(*args, **kwargs):
58
- raise ImportError("polars and pyarrow are required. Install with: pip install pybcsv[polars,arrow]")
73
+ raise ImportError(
74
+ "polars and pyarrow are required. Install with: pip install pybcsv[polars,arrow]"
75
+ )
76
+
77
+
78
+ # Try to import Parquet utilities if pyarrow is available
79
+ try:
80
+ from .parquet_utils import parquet_to_bcsv, bcsv_to_parquet
81
+
82
+ _PARQUET_AVAILABLE = True
83
+ except ImportError:
84
+ _PARQUET_AVAILABLE = False
85
+
86
+ def parquet_to_bcsv(*args, **kwargs):
87
+ raise ImportError(
88
+ "pyarrow is required for Parquet conversion. "
89
+ "Install with: pip install pybcsv[arrow]"
90
+ )
91
+
92
+ def bcsv_to_parquet(*args, **kwargs):
93
+ raise ImportError(
94
+ "pyarrow is required for Parquet conversion. "
95
+ "Install with: pip install pybcsv[arrow]"
96
+ )
97
+
98
+
99
+ def iter_arrow_batches(reader, *, batch_size=512000, columns=None, start_row=0):
100
+ """Yield pa.RecordBatch objects from an already-open ReaderDirectAccess.
101
+
102
+ Memory-bounded: exactly one batch is held at a time. `reader` must be open.
103
+ """
104
+ pos = start_row
105
+ while True:
106
+ batch = reader.read_arrow_batch(pos, batch_size, columns)
107
+ if batch is None:
108
+ break
109
+ yield batch
110
+ pos += batch.num_rows
111
+
59
112
 
60
113
  __all__ = [
61
114
  "__version__",
@@ -80,6 +133,10 @@ __all__ = [
80
133
  # Arrow interop
81
134
  "read_to_arrow",
82
135
  "write_from_arrow",
136
+ "iter_arrow_batches",
137
+ # Parquet interop
138
+ "parquet_to_bcsv",
139
+ "bcsv_to_parquet",
83
140
  # Polars interop
84
141
  "read_polars",
85
142
  "write_polars",
@@ -89,4 +146,4 @@ __all__ = [
89
146
  "read_dataframe",
90
147
  "to_csv",
91
148
  "from_csv",
92
- ]
149
+ ]
@@ -48,15 +48,12 @@ def write_dataframe(
48
48
  type_hints: Optional[Dict[str, ColumnType]] = None,
49
49
  strict: bool = False,
50
50
  ) -> None: ...
51
-
52
51
  def read_dataframe(
53
52
  filename: str,
54
53
  columns: Optional[list] = None,
55
54
  optimize_dtypes: bool = True,
56
55
  ) -> object: ...
57
-
58
56
  def to_csv(bcsv_filename: str, csv_filename: str, **csv_kwargs: object) -> None: ...
59
-
60
57
  def from_csv(
61
58
  csv_filename: str,
62
59
  bcsv_filename: str,
@@ -64,16 +61,21 @@ def from_csv(
64
61
  type_hints: Optional[Dict[str, ColumnType]] = None,
65
62
  **csv_kwargs: object,
66
63
  ) -> None: ...
67
-
68
64
  def read_polars(
69
65
  filename: str,
70
66
  columns: object = None,
71
67
  chunk_size: int = 0,
72
68
  ) -> object: ...
73
-
74
69
  def write_polars(
75
70
  df: object,
76
71
  filename: str,
77
72
  row_codec: str = "delta",
78
73
  compression_level: int = 1,
79
74
  ) -> None: ...
75
+ def iter_arrow_batches(
76
+ reader: ReaderDirectAccess,
77
+ *,
78
+ batch_size: int = 512000,
79
+ columns: Optional[list] = None,
80
+ start_row: int = 0,
81
+ ) -> object: ... # yields pa.RecordBatch
@@ -4,7 +4,6 @@ from collections.abc import Sequence
4
4
  import enum
5
5
  from typing import overload
6
6
 
7
-
8
7
  class ColumnType(enum.Enum):
9
8
  BOOL = 0
10
9
 
@@ -56,54 +55,35 @@ STRING: ColumnType = ColumnType.STRING
56
55
 
57
56
  class ColumnDefinition:
58
57
  def __init__(self, name: str, type: ColumnType) -> None: ...
59
-
60
58
  @property
61
59
  def name(self) -> str: ...
62
-
63
60
  @name.setter
64
61
  def name(self, arg: str, /) -> None: ...
65
-
66
62
  @property
67
63
  def type(self) -> ColumnType: ...
68
-
69
64
  @type.setter
70
65
  def type(self, arg: ColumnType, /) -> None: ...
71
-
72
66
  def __repr__(self) -> str: ...
73
67
 
74
68
  class Layout:
75
69
  @overload
76
70
  def __init__(self) -> None: ...
77
-
78
71
  @overload
79
72
  def __init__(self, arg: Sequence[ColumnDefinition], /) -> None: ...
80
-
81
73
  @overload
82
74
  def add_column(self, column: ColumnDefinition) -> None: ...
83
-
84
75
  @overload
85
76
  def add_column(self, name: str, type: ColumnType) -> None: ...
86
-
87
77
  def column_count(self) -> int: ...
88
-
89
78
  def column_name(self, index: int) -> str: ...
90
-
91
79
  def column_type(self, index: int) -> ColumnType: ...
92
-
93
80
  def has_column(self, name: str) -> bool: ...
94
-
95
81
  def column_index(self, name: str) -> int: ...
96
-
97
82
  def get_column_names(self) -> list[str]: ...
98
-
99
83
  def get_column_types(self) -> list[ColumnType]: ...
100
-
101
84
  def get_column(self, index: int) -> ColumnDefinition: ...
102
-
103
85
  def __len__(self) -> int: ...
104
-
105
86
  def __getitem__(self, arg: int, /) -> ColumnDefinition: ...
106
-
107
87
  def __repr__(self) -> str: ...
108
88
 
109
89
  class FileFlags(enum.IntEnum):
@@ -120,9 +100,7 @@ class FileFlags(enum.IntEnum):
120
100
  DELTA_ENCODING = 16
121
101
 
122
102
  def __or__(self, arg: FileFlags, /) -> int: ...
123
-
124
103
  def __and__(self, arg: FileFlags, /) -> int: ...
125
-
126
104
  def __invert__(self) -> int: ...
127
105
 
128
106
  NONE: FileFlags = FileFlags.NONE
@@ -138,58 +116,43 @@ BATCH_COMPRESS: FileFlags = FileFlags.BATCH_COMPRESS
138
116
  DELTA_ENCODING: FileFlags = FileFlags.DELTA_ENCODING
139
117
 
140
118
  class Writer:
141
- def __init__(self, layout: Layout, row_codec: str = 'delta') -> None: ...
142
-
143
- def open(self, filename: str, overwrite: bool = False, compression_level: int = 1, block_size_kb: int = 8192, flags: FileFlags = FileFlags.BATCH_COMPRESS) -> bool: ...
144
-
119
+ def __init__(self, layout: Layout, row_codec: str = "delta") -> None: ...
120
+ def open(
121
+ self,
122
+ filename: str,
123
+ overwrite: bool = False,
124
+ compression_level: int = 1,
125
+ block_size_kb: int = 8192,
126
+ flags: FileFlags = FileFlags.BATCH_COMPRESS,
127
+ ) -> bool: ...
145
128
  def write_row(self, arg: list, /) -> None: ...
146
-
147
129
  def write_rows(self, arg: list, /) -> None:
148
130
  """Write multiple rows efficiently with batching"""
149
131
 
150
132
  def close(self) -> None: ...
151
-
152
133
  def flush(self) -> None: ...
153
-
154
134
  def is_open(self) -> bool: ...
155
-
156
135
  def row_count(self) -> int: ...
157
-
158
136
  def row_codec(self) -> str: ...
159
-
160
137
  def compression_level(self) -> int: ...
161
-
162
138
  def layout(self) -> Layout: ...
163
-
139
+ def write_batch(self, batch: object) -> None: ... # batch: pa.RecordBatch
164
140
  def __enter__(self) -> Writer: ...
165
-
166
141
  def __exit__(self, *args) -> None: ...
167
-
168
142
  def __repr__(self) -> str: ...
169
143
 
170
144
  class Reader:
171
145
  def __init__(self) -> None: ...
172
-
173
146
  def open(self, filename: str) -> bool: ...
174
-
175
147
  def layout(self) -> Layout: ...
176
-
177
148
  def read_next(self) -> bool: ...
178
-
179
149
  def close(self) -> None: ...
180
-
181
150
  def is_open(self) -> bool: ...
182
-
183
151
  def file_flags(self) -> int: ...
184
-
185
152
  def compression_level(self) -> int: ...
186
-
187
153
  def row_pos(self) -> int: ...
188
-
189
154
  def version_string(self) -> str: ...
190
-
191
155
  def creation_time(self) -> int: ...
192
-
193
156
  def count_rows(self) -> int:
194
157
  """Count the total number of rows in the file"""
195
158
 
@@ -205,115 +168,79 @@ class Reader:
205
168
  """
206
169
 
207
170
  def read_row(self) -> object: ...
208
-
209
171
  def read_all(self) -> list: ...
210
-
211
172
  def __enter__(self) -> Reader: ...
212
-
213
173
  def __exit__(self, *args) -> None: ...
214
-
215
174
  def __iter__(self) -> Reader: ...
216
-
217
175
  def __next__(self) -> object: ...
218
-
219
176
  def __repr__(self) -> str: ...
220
177
 
221
178
  def type_to_string(arg: ColumnType, /) -> str:
222
179
  """Convert ColumnType to string"""
223
180
 
224
181
  class CsvWriter:
225
- def __init__(self, layout: Layout, delimiter: str = ',', decimal_sep: str = '.') -> None: ...
226
-
227
- def open(self, filename: str, overwrite: bool = False, include_header: bool = True) -> bool: ...
228
-
182
+ def __init__(
183
+ self, layout: Layout, delimiter: str = ",", decimal_sep: str = "."
184
+ ) -> None: ...
185
+ def open(
186
+ self, filename: str, overwrite: bool = False, include_header: bool = True
187
+ ) -> bool: ...
229
188
  def close(self) -> None: ...
230
-
231
189
  def is_open(self) -> bool: ...
232
-
233
190
  def row_count(self) -> int: ...
234
-
235
191
  def layout(self) -> Layout: ...
236
-
237
192
  def delimiter(self) -> str: ...
238
-
239
193
  def decimal_separator(self) -> str: ...
240
-
241
194
  def __enter__(self) -> CsvWriter: ...
242
-
243
195
  def __exit__(self, *args) -> None: ...
244
-
245
196
  def write_row(self, arg: list, /) -> None: ...
246
-
247
197
  def write_rows(self, arg: list, /) -> None:
248
198
  """Write multiple rows efficiently"""
249
199
 
250
200
  class CsvReader:
251
- def __init__(self, layout: Layout, delimiter: str = ',', decimal_sep: str = '.') -> None: ...
252
-
201
+ def __init__(
202
+ self, layout: Layout, delimiter: str = ",", decimal_sep: str = "."
203
+ ) -> None: ...
253
204
  def open(self, filename: str, has_header: bool = True) -> bool: ...
254
-
255
205
  def read_next(self) -> bool: ...
256
-
257
206
  def close(self) -> None: ...
258
-
259
207
  def is_open(self) -> bool: ...
260
-
261
208
  def row_pos(self) -> int: ...
262
-
263
209
  def file_line(self) -> int: ...
264
-
265
210
  def layout(self) -> Layout: ...
266
-
267
211
  def delimiter(self) -> str: ...
268
-
269
212
  def decimal_separator(self) -> str: ...
270
-
271
213
  def error_msg(self) -> str: ...
272
-
273
214
  def read_row(self) -> object: ...
274
-
275
215
  def read_all(self) -> list: ...
276
-
277
216
  def __enter__(self) -> CsvReader: ...
278
-
279
217
  def __exit__(self, *args) -> None: ...
280
-
281
218
  def __iter__(self) -> CsvReader: ...
282
-
283
219
  def __next__(self) -> object: ...
284
220
 
285
221
  class ReaderDirectAccess:
286
222
  def __init__(self) -> None: ...
287
-
288
223
  def open(self, filename: str, rebuild_footer: bool = False) -> bool: ...
289
-
290
224
  def read(self, index: int) -> list: ...
291
-
292
225
  def row_count(self) -> int: ...
293
-
294
226
  def layout(self) -> Layout: ...
295
-
296
227
  def close(self) -> None: ...
297
-
298
228
  def is_open(self) -> bool: ...
299
-
300
229
  def file_flags(self) -> int: ...
301
-
302
230
  def compression_level(self) -> int: ...
303
-
304
231
  def version_string(self) -> str: ...
305
-
306
232
  def creation_time(self) -> int: ...
307
-
308
233
  def __enter__(self) -> ReaderDirectAccess: ...
309
-
310
234
  def __exit__(self, *args) -> None: ...
311
-
312
235
  def __len__(self) -> int: ...
313
-
314
236
  def __getitem__(self, arg: int, /) -> list: ...
315
-
316
237
  def __repr__(self) -> str: ...
238
+ def read_arrow_batch(
239
+ self,
240
+ start_row: int = 0,
241
+ batch_size: int = 512000,
242
+ columns: list[str] | None = None,
243
+ ) -> object: ... # returns pa.RecordBatch or None at EOF
317
244
 
318
245
  class SamplerMode(enum.Enum):
319
246
  TRUNCATE = 0
@@ -340,56 +267,33 @@ SATURATE: SamplerErrorPolicy = SamplerErrorPolicy.SATURATE
340
267
  class SamplerCompileResult:
341
268
  @property
342
269
  def success(self) -> bool: ...
343
-
344
270
  @property
345
271
  def error_msg(self) -> str: ...
346
-
347
272
  @property
348
273
  def error_position(self) -> int: ...
349
-
350
274
  def __bool__(self) -> bool: ...
351
-
352
275
  def __repr__(self) -> str: ...
353
276
 
354
277
  class Sampler:
355
278
  def __init__(self, reader: Reader) -> None: ...
356
-
357
279
  def set_conditional(self, expr: str) -> SamplerCompileResult: ...
358
-
359
280
  def get_conditional(self) -> str: ...
360
-
361
281
  def set_selection(self, expr: str) -> SamplerCompileResult: ...
362
-
363
282
  def get_selection(self) -> str: ...
364
-
365
283
  def set_mode(self, mode: SamplerMode) -> None: ...
366
-
367
284
  def get_mode(self) -> SamplerMode: ...
368
-
369
285
  def set_error_policy(self, policy: SamplerErrorPolicy) -> None: ...
370
-
371
286
  def get_error_policy(self) -> SamplerErrorPolicy: ...
372
-
373
287
  def output_layout(self) -> Layout: ...
374
-
375
288
  def next(self) -> bool: ...
376
-
377
289
  def row(self) -> list: ...
378
-
379
290
  def source_row_pos(self) -> int: ...
380
-
381
291
  def bulk(self) -> list: ...
382
-
383
292
  def is_conditional_passthrough(self) -> bool: ...
384
-
385
293
  def is_selection_passthrough(self) -> bool: ...
386
-
387
294
  def window_capacity(self) -> int: ...
388
-
389
295
  def disassemble(self) -> str: ...
390
-
391
296
  def __iter__(self) -> Sampler: ...
392
-
393
297
  def __next__(self) -> object: ...
394
298
 
395
299
  def read_columns(filename: str) -> dict:
@@ -397,14 +301,30 @@ def read_columns(filename: str) -> dict:
397
301
  Read a BCSV file into a dict of numpy arrays (numeric) and lists (strings)
398
302
  """
399
303
 
400
- def write_columns(filename: str, columns: dict, col_order: list, col_types: list, row_codec: str = 'delta', compression_level: int = 1, flags: FileFlags = FileFlags.BATCH_COMPRESS) -> None:
304
+ def write_columns(
305
+ filename: str,
306
+ columns: dict,
307
+ col_order: list,
308
+ col_types: list,
309
+ row_codec: str = "delta",
310
+ compression_level: int = 1,
311
+ flags: FileFlags = FileFlags.BATCH_COMPRESS,
312
+ ) -> None:
401
313
  """Write a dict of numpy arrays/lists to a BCSV file"""
402
314
 
403
- def read_to_arrow(filename: str, columns: list | None = None, chunk_size: int = 0) -> object:
315
+ def read_to_arrow(
316
+ filename: str, columns: list | None = None, chunk_size: int = 0
317
+ ) -> object:
404
318
  """
405
319
  Read a BCSV file into a pyarrow.Table via Arrow C Data Interface (zero-copy).
406
320
  Set chunk_size > 0 for chunked reading (returns Table from multiple batches).
407
321
  """
408
322
 
409
- def write_from_arrow(filename: str, table: object, row_codec: str = 'delta', compression_level: int = 1, flags: FileFlags = FileFlags.BATCH_COMPRESS) -> None:
323
+ def write_from_arrow(
324
+ filename: str,
325
+ table: object,
326
+ row_codec: str = "delta",
327
+ compression_level: int = 1,
328
+ flags: FileFlags = FileFlags.BATCH_COMPRESS,
329
+ ) -> None:
410
330
  """Write a pyarrow Table/RecordBatch to a BCSV file"""
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.5.7'
22
- __version_tuple__ = version_tuple = (1, 5, 7)
21
+ __version__ = version = '1.5.10.dev0'
22
+ __version_tuple__ = version_tuple = (1, 5, 10, 'dev0')
23
23
 
24
- __commit_id__ = commit_id = 'g1303a72af'
24
+ __commit_id__ = commit_id = 'g6ec074145'