fastpycsv 5.2.0__cp313-cp313-win_amd64.whl

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.
fastpycsv/__init__.py ADDED
@@ -0,0 +1,71 @@
1
+ import importlib.machinery as _machinery
2
+ import importlib.util as _util
3
+ import os as _os
4
+ import sys as _sys
5
+
6
+ __path__ = [_os.path.dirname(__file__)]
7
+
8
+
9
+ def _load_native_module():
10
+ module_name = __name__ + ".fastpycsv"
11
+ existing = _sys.modules.get(module_name)
12
+ if existing is not None:
13
+ return existing
14
+
15
+ for suffix in _machinery.EXTENSION_SUFFIXES:
16
+ path = _os.path.join(__path__[0], "fastpycsv" + suffix)
17
+ if not _os.path.exists(path):
18
+ continue
19
+
20
+ spec = _util.spec_from_file_location(module_name, path)
21
+ if spec is None or spec.loader is None:
22
+ continue
23
+
24
+ module = _util.module_from_spec(spec)
25
+ _sys.modules[module_name] = module
26
+ spec.loader.exec_module(module)
27
+ return module
28
+
29
+ raise ImportError("cannot find the fastpycsv native extension next to the Python package")
30
+
31
+
32
+ _native = _load_native_module()
33
+
34
+ CSVFileInfo = _native.CSVFileInfo
35
+ DataType = _native.DataType
36
+ Field = _native.Field
37
+ Reader = _native.Reader
38
+ Row = _native.Row
39
+ csv_data_types = _native.csv_data_types
40
+ all_of = _native.all_of
41
+ equal = _native.equal
42
+ greater = _native.greater
43
+ greater_equal = _native.greater_equal
44
+ get_file_info = _native.get_file_info
45
+ less = _native.less
46
+ less_equal = _native.less_equal
47
+ parse_no_header = _native.parse_no_header
48
+ from ._numpy import read_numpy, read_numpy_batches
49
+ from ._reader import reader
50
+ from ._writer import write_csv
51
+
52
+ __all__ = [
53
+ "CSVFileInfo",
54
+ "DataType",
55
+ "Field",
56
+ "Reader",
57
+ "Row",
58
+ "all_of",
59
+ "csv_data_types",
60
+ "equal",
61
+ "greater",
62
+ "greater_equal",
63
+ "get_file_info",
64
+ "less",
65
+ "less_equal",
66
+ "parse_no_header",
67
+ "read_numpy",
68
+ "read_numpy_batches",
69
+ "reader",
70
+ "write_csv",
71
+ ]
fastpycsv/_format.py ADDED
@@ -0,0 +1,40 @@
1
+ """CSVFormat construction helpers for the public Python facade."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional, Sequence
6
+
7
+ from .fastpycsv import Format as _NativeFormat
8
+ from .fastpycsv import VariableColumnPolicy as _NativeVariableColumnPolicy
9
+
10
+
11
+ def _make_format(
12
+ delimiter: str = ",",
13
+ quotechar: Optional[str] = '"',
14
+ doublequote: bool = True,
15
+ skipinitialspace: bool = False,
16
+ strict: bool = False,
17
+ fieldnames: Optional[Sequence[str]] = None,
18
+ no_header: bool = True,
19
+ ) -> _NativeFormat:
20
+ if len(delimiter) != 1:
21
+ raise TypeError("delimiter must be a one-character string")
22
+ if quotechar is not None and len(quotechar) != 1:
23
+ raise TypeError("quotechar must be a one-character string or None")
24
+ if not doublequote:
25
+ raise NotImplementedError("fastpycsv.reader does not support doublequote=False")
26
+
27
+ fmt = _NativeFormat().delimiter(delimiter)
28
+ if no_header:
29
+ fmt.no_header()
30
+ if quotechar is None:
31
+ fmt.quote(False)
32
+ else:
33
+ fmt.quote(quotechar)
34
+ if skipinitialspace:
35
+ fmt.trim([" "])
36
+ if strict:
37
+ fmt.variable_columns(_NativeVariableColumnPolicy.THROW)
38
+ if fieldnames is not None:
39
+ fmt.column_names(list(fieldnames))
40
+ return fmt
fastpycsv/_numpy.py ADDED
@@ -0,0 +1,115 @@
1
+ """NumPy export facade sharing reader() CSV format handling."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional, Sequence
6
+
7
+ from ._format import _make_format
8
+ from ._reader import _CSVSource
9
+ from .fastpycsv import _read_numpy as _native_read_numpy
10
+ from .fastpycsv import _read_numpy_batches as _native_read_numpy_batches
11
+
12
+
13
+ def _numpy_format(
14
+ delimiter: str,
15
+ quotechar: Optional[str],
16
+ doublequote: bool,
17
+ skipinitialspace: bool,
18
+ strict: bool,
19
+ consume_header: bool,
20
+ fieldnames: Optional[Sequence[str]],
21
+ ):
22
+ return _make_format(
23
+ delimiter,
24
+ quotechar,
25
+ doublequote,
26
+ skipinitialspace,
27
+ strict,
28
+ fieldnames=fieldnames,
29
+ no_header=(not consume_header) or fieldnames is not None,
30
+ )
31
+
32
+
33
+ def read_numpy(
34
+ csvfile,
35
+ columns=None,
36
+ *,
37
+ cast: bool = True,
38
+ predicate=None,
39
+ delimiter: str = ",",
40
+ quotechar: Optional[str] = '"',
41
+ doublequote: bool = True,
42
+ skipinitialspace: bool = False,
43
+ strict: bool = False,
44
+ consume_header: bool = True,
45
+ fieldnames: Optional[Sequence[str]] = None,
46
+ ):
47
+ fmt = _numpy_format(
48
+ delimiter,
49
+ quotechar,
50
+ doublequote,
51
+ skipinitialspace,
52
+ strict,
53
+ consume_header,
54
+ fieldnames,
55
+ )
56
+ source = _CSVSource(csvfile)
57
+ try:
58
+ return _native_read_numpy(source.name, fmt, columns, cast, predicate)
59
+ finally:
60
+ source.cleanup()
61
+
62
+
63
+ def read_numpy_batches(
64
+ csvfile,
65
+ columns=None,
66
+ *,
67
+ predicate=None,
68
+ cast: bool = True,
69
+ batch_size: int = 50000,
70
+ schema: str = "sample",
71
+ delimiter: str = ",",
72
+ quotechar: Optional[str] = '"',
73
+ doublequote: bool = True,
74
+ skipinitialspace: bool = False,
75
+ strict: bool = False,
76
+ consume_header: bool = True,
77
+ fieldnames: Optional[Sequence[str]] = None,
78
+ ):
79
+ fmt = _numpy_format(
80
+ delimiter,
81
+ quotechar,
82
+ doublequote,
83
+ skipinitialspace,
84
+ strict,
85
+ consume_header,
86
+ fieldnames,
87
+ )
88
+ source = _CSVSource(csvfile)
89
+ try:
90
+ reader = _native_read_numpy_batches(source.name, fmt, columns, predicate, cast, batch_size, schema)
91
+ except Exception:
92
+ source.cleanup()
93
+ raise
94
+ return _NumpyBatchIterator(reader, source)
95
+
96
+
97
+ class _NumpyBatchIterator:
98
+ def __init__(self, reader, source: _CSVSource):
99
+ self._reader = reader
100
+ self._source = source
101
+
102
+ def __iter__(self):
103
+ return self
104
+
105
+ def __next__(self):
106
+ try:
107
+ return next(self._reader)
108
+ except StopIteration:
109
+ self._source.cleanup()
110
+ raise
111
+
112
+ def __del__(self):
113
+ source = getattr(self, "_source", None)
114
+ if source is not None:
115
+ source.cleanup()
fastpycsv/_reader.py ADDED
@@ -0,0 +1,185 @@
1
+ """Lazy stdlib-adjacent reader facade over the native fastpycsv extension."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import tempfile
7
+ from typing import Iterable, Optional, Sequence
8
+
9
+ from ._format import _make_format
10
+ from .fastpycsv import _RowsReader
11
+
12
+
13
+ def _is_path(value: object) -> bool:
14
+ return isinstance(value, (str, bytes, os.PathLike))
15
+
16
+
17
+ def _at_start(value: object) -> bool:
18
+ try:
19
+ return value.tell() == 0
20
+ except (AttributeError, OSError):
21
+ return False
22
+
23
+
24
+ class _TempCSV:
25
+ def __init__(self, rows: Iterable[str]):
26
+ handle = tempfile.NamedTemporaryFile("w", encoding="utf-8", newline="", delete=False)
27
+ self.name = handle.name
28
+ with handle:
29
+ for row in rows:
30
+ handle.write(row)
31
+
32
+ def cleanup(self) -> None:
33
+ try:
34
+ os.unlink(self.name)
35
+ except FileNotFoundError:
36
+ pass
37
+
38
+
39
+ class _CSVSource:
40
+ def __init__(self, csvfile):
41
+ self._temp = None
42
+ self.name = self._filename(csvfile)
43
+
44
+ def cleanup(self) -> None:
45
+ if self._temp is not None:
46
+ self._temp.cleanup()
47
+
48
+ def _filename(self, csvfile) -> str:
49
+ if _is_path(csvfile):
50
+ return os.fspath(csvfile)
51
+ if (
52
+ hasattr(csvfile, "name")
53
+ and _is_path(csvfile.name)
54
+ and not getattr(csvfile, "closed", False)
55
+ and _at_start(csvfile)
56
+ ):
57
+ return os.fspath(csvfile.name)
58
+
59
+ self._temp = _TempCSV(csvfile)
60
+ return self._temp.name
61
+
62
+
63
+ class _MaterializedRows:
64
+ def __init__(self, parent: "_BaseReader", iterator):
65
+ self._parent = parent
66
+ self._iterator = iterator
67
+
68
+ def __iter__(self):
69
+ return self
70
+
71
+ def __next__(self):
72
+ try:
73
+ return next(self._iterator)
74
+ except StopIteration:
75
+ self._parent._source.cleanup()
76
+ raise
77
+
78
+ def all(self):
79
+ try:
80
+ return self._iterator.all()
81
+ finally:
82
+ self._parent._source.cleanup()
83
+
84
+ def chunks(self, size: int):
85
+ if size <= 0:
86
+ raise ValueError("chunk size must be greater than zero")
87
+
88
+ def chunk_iterator():
89
+ while True:
90
+ batch = self._iterator.read_chunk(size)
91
+ if not batch:
92
+ self._parent._source.cleanup()
93
+ return
94
+ yield batch
95
+
96
+ return chunk_iterator()
97
+
98
+
99
+ class _BaseReader:
100
+ def _init_reader(self, csvfile, fmt, cast: bool, batch_size: int) -> None:
101
+ self._source = _CSVSource(csvfile)
102
+ self._iterator = iter(_RowsReader(self._source.name, fmt, cast, batch_size))
103
+
104
+ @property
105
+ def fieldnames(self):
106
+ return list(self._iterator.fieldnames)
107
+
108
+ def get_col_names(self):
109
+ return self.fieldnames
110
+
111
+ def __iter__(self):
112
+ return self
113
+
114
+ def __next__(self):
115
+ try:
116
+ row = next(self._iterator)
117
+ except StopIteration:
118
+ self._source.cleanup()
119
+ raise
120
+ return row
121
+
122
+ def filter(self, predicate, *, append: bool = True):
123
+ if predicate is None:
124
+ raise TypeError("reader.filter() expects a fastpycsv predicate; create a fresh reader for an unfiltered pass")
125
+ self._iterator.filter(predicate, append)
126
+ return self
127
+
128
+ def lists(self, columns=None):
129
+ return _MaterializedRows(self, self._iterator.lists(columns))
130
+
131
+ def tuples(self, columns=None):
132
+ return _MaterializedRows(self, self._iterator.tuples(columns))
133
+
134
+ def dicts(self, columns=None):
135
+ return _MaterializedRows(self, self._iterator.dicts(columns))
136
+
137
+ def to_lists(self, columns=None):
138
+ return self.lists(columns).all()
139
+
140
+ def to_tuples(self, columns=None):
141
+ return self.tuples(columns).all()
142
+
143
+ def to_dicts(self, columns=None):
144
+ return self.dicts(columns).all()
145
+
146
+ def __del__(self):
147
+ source = getattr(self, "_source", None)
148
+ if source is not None:
149
+ source.cleanup()
150
+
151
+
152
+ class _Reader(_BaseReader):
153
+ def __init__(
154
+ self,
155
+ csvfile,
156
+ *,
157
+ delimiter: str = ",",
158
+ quotechar: Optional[str] = '"',
159
+ doublequote: bool = True,
160
+ skipinitialspace: bool = False,
161
+ strict: bool = False,
162
+ cast: bool = False,
163
+ typed: Optional[bool] = None,
164
+ consume_header: bool = True,
165
+ fieldnames: Optional[Sequence[str]] = None,
166
+ batch_size: int = 8192,
167
+ ):
168
+ if typed is not None:
169
+ cast = typed
170
+ fmt = _make_format(
171
+ delimiter,
172
+ quotechar,
173
+ doublequote,
174
+ skipinitialspace,
175
+ strict,
176
+ fieldnames=fieldnames,
177
+ no_header=(not consume_header) or fieldnames is not None,
178
+ )
179
+ self._init_reader(csvfile, fmt, cast, batch_size)
180
+
181
+
182
+ def reader(csvfile, dialect="excel", **fmtparams) -> _Reader:
183
+ if dialect != "excel":
184
+ raise NotImplementedError("fastpycsv.reader currently supports only the default excel dialect")
185
+ return _Reader(csvfile, **fmtparams)
fastpycsv/_writer.py ADDED
@@ -0,0 +1,38 @@
1
+ """CSV writer facade over the native fastpycsv extension."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Iterable, Optional, Sequence
7
+
8
+ from . import fastpycsv as _native
9
+
10
+
11
+ def write_csv(
12
+ csvfile,
13
+ rows: Iterable[object],
14
+ *,
15
+ fieldnames: Optional[Sequence[object]] = None,
16
+ write_header: bool = True,
17
+ quote_minimal: bool = True,
18
+ ) -> None:
19
+ """Write rows to a CSV file.
20
+
21
+ `rows` may contain lazy fastpycsv rows, dictionaries, or ordinary Python
22
+ iterables. Fields are stringified before writing; `None` becomes an empty
23
+ CSV field.
24
+ """
25
+
26
+ if hasattr(csvfile, "write"):
27
+ write = getattr(_native, "_write_csv_filelike", None)
28
+ if write is None:
29
+ raise AttributeError("fastpycsv native extension has not been rebuilt with file-like write_csv support")
30
+
31
+ write(csvfile, rows, fieldnames, write_header, quote_minimal)
32
+ return
33
+
34
+ write = getattr(_native, "_write_csv", None)
35
+ if write is None:
36
+ raise AttributeError("fastpycsv native extension has not been rebuilt with write_csv support")
37
+
38
+ write(os.fspath(csvfile), rows, fieldnames, write_header, quote_minimal)
fastpycsv/compat.py ADDED
@@ -0,0 +1,5 @@
1
+ """Compatibility imports for the split fastpycsv Python facade."""
2
+
3
+ from ._reader import reader
4
+
5
+ __all__ = ["reader"]
Binary file
@@ -0,0 +1,336 @@
1
+ Metadata-Version: 2.2
2
+ Name: fastpycsv
3
+ Version: 5.2.0
4
+ Summary: Fast ETL-focused CSV parsing for Python, backed by Vince's CSV Parser
5
+ Keywords: csv,parser,etl,dataframe,numpy
6
+ Author: Vincent La
7
+ Maintainer: Vincent La
8
+ License: MIT License
9
+
10
+ Copyright (c) 2017-2026 Vincent La
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+
30
+ Classifier: Development Status :: 5 - Production/Stable
31
+ Classifier: Intended Audience :: Developers
32
+ Classifier: Intended Audience :: Science/Research
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Classifier: Programming Language :: C++
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3 :: Only
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Programming Language :: Python :: 3.14
44
+ Classifier: Programming Language :: Python :: Implementation :: CPython
45
+ Classifier: Topic :: Database
46
+ Classifier: Topic :: Scientific/Engineering
47
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
48
+ Project-URL: Homepage, https://github.com/vincentlaucsb/csv-parser
49
+ Project-URL: Documentation, https://vincentlaucsb.github.io/csv-parser/python/
50
+ Project-URL: Source, https://github.com/vincentlaucsb/csv-parser
51
+ Project-URL: Issues, https://github.com/vincentlaucsb/csv-parser/issues
52
+ Requires-Python: >=3.9
53
+ Provides-Extra: numpy
54
+ Requires-Dist: numpy>=2; extra == "numpy"
55
+ Provides-Extra: pandas
56
+ Requires-Dist: numpy>=2; extra == "pandas"
57
+ Requires-Dist: pandas; extra == "pandas"
58
+ Description-Content-Type: text/markdown
59
+
60
+ # fastpycsv
61
+
62
+ <p align="center">
63
+ <img src="assets/fastpycsv-logo.png" alt="fastpycsv logo" width="240">
64
+ </p>
65
+
66
+ `fastpycsv` is a fast Python CSV toolkit backed by Vince's CSV Parser. It is built
67
+ for ETL-style workflows where users need to scan large CSV files, filter rows,
68
+ extract selected columns, materialize NumPy arrays, or write cleaned CSV output
69
+ without routing every job through pandas or pyarrow.
70
+
71
+ The package is named `fastpycsv`; it does not replace or shadow Python's stdlib
72
+ `csv` module.
73
+
74
+ Long-form Python documentation lives in `python/docs/` and is written in
75
+ Sphinx/MyST-flavored Markdown so it can be published with Read the Docs later.
76
+
77
+ Main API:
78
+
79
+ - `fastpycsv.reader()` yields lazy, list-like row objects over the native parser.
80
+ - `fastpycsv.read_numpy()` exports selected columns as eager NumPy arrays.
81
+ - `fastpycsv.read_numpy_batches()` streams selected columns as bounded-memory NumPy
82
+ batches.
83
+ - `fastpycsv.write_csv()` streams lazy rows or Python iterables through the native
84
+ CSV writer.
85
+
86
+ ## Building
87
+
88
+ Install the local package directly from the repository root:
89
+
90
+ ```powershell
91
+ python -m pip install -e E:\GitHub\csv-parser
92
+ ```
93
+
94
+ For a regular non-editable local install:
95
+
96
+ ```powershell
97
+ python -m pip install E:\GitHub\csv-parser
98
+ ```
99
+
100
+ The package build uses CMake through `scikit-build-core` and builds the native
101
+ `fastpycsv` extension for the Python interpreter running `pip`.
102
+
103
+ Build the extension with `BUILD_PYTHON=ON`:
104
+
105
+ ```powershell
106
+ cmake -S . -B build/fastpycsv -DBUILD_PYTHON=ON -DCMAKE_BUILD_TYPE=Release
107
+ cmake --build build/fastpycsv --target fastpycsv --config Release
108
+ ```
109
+
110
+ For an existing top-level build configured without `BUILD_PYTHON=ON`, build the
111
+ `fastpycsv` target directly. It bootstraps a separate `build/fastpycsv` tree with
112
+ `BUILD_PYTHON=ON`, so the main build does not need to be reconfigured:
113
+
114
+ ```powershell
115
+ cmake --build build/x64-Release --target fastpycsv --config Release
116
+ ```
117
+
118
+ To force a specific interpreter, configure the main build with:
119
+
120
+ ```powershell
121
+ cmake -S . -B build/x64-Release -DFASTPYCSV_BOOTSTRAP_PYTHON_EXECUTABLE=C:/Python314/python.exe
122
+ ```
123
+
124
+ The `fastpycsv` build fetches nanobind with CMake `FetchContent` only when the
125
+ Python binding is requested. A normal C++ library/test build does not require
126
+ the nanobind checkout.
127
+
128
+ ## Reader API
129
+
130
+ Use `fastpycsv.reader()` for lazy row objects. By default, it consumes the first row
131
+ as column names:
132
+
133
+ ```python
134
+ import fastpycsv
135
+
136
+ with open("data.csv", newline="", encoding="utf-8") as handle:
137
+ for row in fastpycsv.reader(handle):
138
+ assert isinstance(row["name"], str)
139
+ values = row.as_list() # Materialize explicitly when needed.
140
+ ```
141
+
142
+ Pass `consume_header=False` when the first input row should be emitted as data:
143
+
144
+ ```python
145
+ with open("headerless.csv", newline="", encoding="utf-8") as handle:
146
+ for row in fastpycsv.reader(handle, consume_header=False):
147
+ print(row[0])
148
+ ```
149
+
150
+ Call `row.as_dict()` only when you explicitly want to materialize a row as a
151
+ plain Python dictionary.
152
+
153
+ When plain Python row objects are the desired output, use the materialized row
154
+ iterators instead of converting each lazy row manually:
155
+
156
+ ```python
157
+ predicate = fastpycsv.all_of(
158
+ fastpycsv.equal("region", "el paso", case_sensitive=False),
159
+ fastpycsv.less("price", "15000"),
160
+ )
161
+ reader = fastpycsv.reader("data.csv").filter(predicate)
162
+
163
+ for row in reader.lists(["id", "price"]):
164
+ ...
165
+
166
+ for batch in fastpycsv.reader("data.csv").dicts(["id", "price"]).chunks(50_000):
167
+ ...
168
+
169
+ rows = fastpycsv.reader("data.csv").tuples(["id", "price"]).all()
170
+ ```
171
+
172
+ The `.lists()`, `.tuples()`, and `.dicts()` iterators stream one row at a time.
173
+ Their `.chunks(size)` methods keep memory bounded while amortizing native
174
+ conversion overhead, and `.all()` is the eager bulk-export path. Native
175
+ `reader.filter(predicate)` uses the same `fastpycsv.equal()`, `less()`, `greater()`,
176
+ and `all_of()` predicate objects as the NumPy APIs, and validates predicate
177
+ column names before rows are consumed.
178
+
179
+ Most users can ignore `reader(batch_size=...)`. It does not make ordinary
180
+ iteration return batches:
181
+
182
+ ```python
183
+ for row in fastpycsv.reader("data.csv"):
184
+ ... # one row at a time
185
+ ```
186
+
187
+ Use `.chunks(size)` when you want Python lists of rows. `batch_size` only tunes
188
+ how many rows fastpycsv asks the native parser to process at once while doing
189
+ filtered or projected materialization, such as `.filter(...).dicts(...).all()`.
190
+ The default is a good starting point; change it only after benchmarking a large
191
+ filtered export.
192
+
193
+ Use `fastpycsv.write_csv()` to stream lazy rows or ordinary Python iterables back
194
+ out through csv-parser's native writer:
195
+
196
+ ```python
197
+ reader = fastpycsv.reader("vehicles.csv")
198
+
199
+ fastpycsv.write_csv(
200
+ "cheap_el_paso_fords.csv",
201
+ (row for row in reader if row["region"] == "el paso" and row["manufacturer"] == "ford"),
202
+ fieldnames=["id", "price", "year", "region"],
203
+ )
204
+ ```
205
+
206
+ Dictionary rows infer their header from the first row when `fieldnames` is not
207
+ provided. `None` is written as an empty field; all other Python field values are
208
+ stringified.
209
+
210
+ Pass `cast=True` only when you want csv-parser's scalar classification exposed
211
+ as Python values. Empty fields become `None`, boolean fields become `bool`,
212
+ integral fields become `int`, floating point fields become `float`, timestamp
213
+ fields become `datetime.datetime`, and all other fields remain `str`.
214
+
215
+ ```python
216
+ reader = fastpycsv.reader(["id,amount,active\n", "1,2.5,true\n"], cast=True)
217
+ rows = list(reader)
218
+ assert reader.fieldnames == ["id", "amount", "active"]
219
+ assert rows[0].as_list() == [1, 2.5, True]
220
+ ```
221
+
222
+ Use `fastpycsv.read_numpy(path, columns=None, *, cast=True, predicate=None)` when
223
+ you want eager column arrays suitable for pandas:
224
+
225
+ ```python
226
+ import pandas as pd
227
+
228
+ frame = pd.DataFrame(fastpycsv.read_numpy("data.csv"))
229
+ ```
230
+
231
+ `read_numpy()` returns a `dict` keyed by column name. String columns use NumPy
232
+ 2.x `StringDType`, nullable numeric and boolean columns widen to `float64` with
233
+ `NaN`, and non-null integer, float, and boolean columns use dense NumPy arrays.
234
+ It does not produce object arrays. The C++ export path batches rows through
235
+ `DataFrame` column views and `DataFrameExecutor`; remaining fixed costs are
236
+ mostly NumPy `StringDType` construction for string-heavy data and pandas'
237
+ DataFrame materialization after the arrays have been built.
238
+
239
+ Use `fastpycsv.read_numpy_batches(path, columns=None, *, predicate=None, cast=True,
240
+ batch_size=50000, schema="sample")` when you want streaming dictionaries of
241
+ NumPy arrays instead of one eager full-file result. `schema="sample"` infers
242
+ dtypes from the first bounded batch and then streams once, `schema="global"`
243
+ does a full pre-scan for stable dtypes matching `read_numpy()`, and
244
+ `schema="batch"` infers each emitted batch independently for true one-pass
245
+ bounded-memory streaming. With `cast=False`, batches are string-only and skip
246
+ schema inference. Explicit `dtypes={column: dtype}` overrides are not implemented
247
+ yet and are tracked as a follow-up.
248
+
249
+ For simple row filters, create native predicates and pass them to
250
+ `read_numpy()` or `read_numpy_batches()`:
251
+
252
+ ```python
253
+ predicate = fastpycsv.equal("region", "el paso", case_sensitive=False)
254
+ arrays = fastpycsv.read_numpy("vehicles.csv", columns=["price", "year"], predicate=predicate)
255
+ ```
256
+
257
+ The facade supports the common `delimiter`, `quotechar`, `doublequote=True`,
258
+ `skipinitialspace`, `strict`, `consume_header`, and `fieldnames` options.
259
+ Unsupported dialect features intentionally fail fast instead of silently
260
+ diverging from stdlib behavior.
261
+
262
+ Lower-level extension objects remain available where they have a clear Python
263
+ use, but `fastpycsv` intentionally keeps C++ configuration machinery out of the
264
+ stable Python facade.
265
+
266
+ ## Benchmarks
267
+
268
+ To compare reader throughput locally:
269
+
270
+ ```powershell
271
+ python python/benchmarks/compare_readers.py path/to/input.csv
272
+ ```
273
+
274
+ To compare the streaming EDA script against pandas using the pyarrow CSV engine:
275
+
276
+ ```powershell
277
+ python python/benchmarks/compare_eda.py path/to/input.csv
278
+ ```
279
+
280
+ To compare a streaming `fastpycsv` filter against pyarrow on the Used Cars-style
281
+ `region == "el paso"` workload:
282
+
283
+ ```powershell
284
+ python python/benchmarks/compare_filter.py path/to/vehicles.csv
285
+ ```
286
+
287
+ To compare selected-column CSV-to-NumPy materialization against pyarrow:
288
+
289
+ ```powershell
290
+ python python/benchmarks/compare_numpy_materialization.py path/to/vehicles.csv
291
+ ```
292
+
293
+ To compare ordinary Python object materialization against pyarrow and Polars:
294
+
295
+ ```powershell
296
+ python python/benchmarks/compare_python_materialization.py path/to/vehicles.csv
297
+ ```
298
+
299
+ This compares full CSV and first+last-column subset materialization for
300
+ row-oriented `list[dict]` outputs and column-oriented `dict[str, list]` outputs.
301
+
302
+ The NumPy benchmark includes pyarrow's direct CSV reader, Dataset/Scanner path,
303
+ streaming CSV reader, and Polars lazy scanning. The Dataset row is pyarrow's
304
+ closest API for parallel predicate/projection work, but it can require explicit
305
+ column types: otherwise pyarrow may infer a narrow integer type from early rows
306
+ and later fail on wider values. The benchmark pins obvious numeric columns such
307
+ as `price`, `year`, and `odometer` so the comparison measures throughput instead
308
+ of schema-inference failure handling.
309
+
310
+ This runs each workload in a fresh Python process and reports wall time,
311
+ throughput, and peak resident/working-set memory.
312
+
313
+ The benchmark helper searches `build/` and `out/` for a compatible built
314
+ `fastpycsv` extension and errors clearly if it is missing. Use the same Python
315
+ version that built `fastpycsv`; a `cp310` extension, for example, will not import
316
+ under Python 3.14.
317
+
318
+ The benchmark matrix compares stdlib `csv.reader`, lazy `fastpycsv.reader` rows with
319
+ strings, lazy `fastpycsv.reader` rows with `cast=True`, stdlib `csv.DictReader`, raw
320
+ `fastpycsv.read_numpy()` array export, and `pandas.DataFrame(fastpycsv.read_numpy(...))`.
321
+ It reports file path, size, rows, columns, elapsed seconds, MiB/s, and rows/s.
322
+
323
+ ## Exploratory Summary
324
+
325
+ For a small dependency-free EDA pass over a CSV:
326
+
327
+ ```powershell
328
+ python python/examples/eda_summary.py path/to/input.csv
329
+ ```
330
+
331
+ The script streams lazy `fastpycsv` rows once and reports per-column mean, sample
332
+ standard deviation, null count, and top observed values. Histograms use a
333
+ bounded approximate heavy-hitter sketch by default to keep memory usage stable
334
+ on high-cardinality columns. Use `--top-n N` to change the report size,
335
+ `--top-capacity N` to change the per-column sketch size, `--exact-values` for
336
+ exact value counts, or `--no-header` for headerless files.
@@ -0,0 +1,11 @@
1
+ fastpycsv/__init__.py,sha256=KXGyFcQFPW28TzYWcPXq43n9sFrIwOtkwLzFq-zcuUo,1819
2
+ fastpycsv/_format.py,sha256=iGb0F-dj29GiGjmY3NDBrsCUwxxgJ7EnLHGnWC1mIq8,1311
3
+ fastpycsv/_numpy.py,sha256=Y0M4pxvJtdY5n8zT7f-_gjnQ28FdAI5eH9jGZIXIEFc,2874
4
+ fastpycsv/_reader.py,sha256=bdg_fgWY4j_NykD384tNWemwv7DPPOfttDi_QBfpOtA,5264
5
+ fastpycsv/_writer.py,sha256=1bQ6-cvJe-NcL31W87J56vVh2rGX25EMEpHUQMumdpc,1211
6
+ fastpycsv/compat.py,sha256=a2kGFJzFiNScOKwXFZrV5iwbY-7dviYEuMBhAQY1nCM,123
7
+ fastpycsv/fastpycsv.cp313-win_amd64.pyd,sha256=PSBAfePOpO5YshSvxayy_TtNfjpS5ygejLEYRKsrUMQ,619008
8
+ fastpycsv-5.2.0.dist-info/METADATA,sha256=pfyAoo-2KiwE_X4VSmKHYSWBKrM4L1EdjVJFekzdqTU,13452
9
+ fastpycsv-5.2.0.dist-info/WHEEL,sha256=UZrbbE4r80xj7Ncfa6JoeTVe-77bdXLkKUA63V8pKWQ,106
10
+ fastpycsv-5.2.0.dist-info/licenses/LICENSE,sha256=SxqZ9a3mGockrO5T6dlEi7rehS2hmC5C6Afd3jhdMLE,1093
11
+ fastpycsv-5.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: scikit-build-core 0.12.2
3
+ Root-Is-Purelib: false
4
+ Tag: cp313-cp313-win_amd64
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-2026 Vincent La
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.