Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/io/parsers/readers.py: 14%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

942 statements  

1""" 

2Module contains tools for processing files into DataFrames or other objects 

3 

4GH#48849 provides a convenient way of deprecating keyword arguments 

5""" 

6 

7from __future__ import annotations 

8 

9import codecs 

10from collections import ( 

11 abc, 

12 defaultdict, 

13) 

14from concurrent.futures import ThreadPoolExecutor 

15import contextlib 

16import csv 

17import io 

18import mmap 

19import os 

20import queue 

21import sys 

22import threading 

23from typing import ( 

24 IO, 

25 TYPE_CHECKING, 

26 Any, 

27 Generic, 

28 Literal, 

29 Self, 

30 TypedDict, 

31 Unpack, 

32 cast, 

33 overload, 

34) 

35import warnings 

36 

37import numpy as np 

38 

39from pandas._config import get_option 

40 

41from pandas._libs import lib 

42from pandas._libs.parsers import STR_NA_VALUES 

43from pandas.compat._cpu import available_cpu_count 

44from pandas.errors import ( 

45 AbstractMethodError, 

46 EmptyDataError, 

47 Pandas4Warning, 

48 ParserError, 

49 ParserWarning, 

50) 

51from pandas.util._decorators import ( 

52 set_module, 

53) 

54from pandas.util._exceptions import find_stack_level 

55from pandas.util._validators import ( 

56 check_dtype_backend, 

57 validate_bool_kwarg, 

58) 

59 

60from pandas.core.dtypes.common import ( 

61 is_float, 

62 is_integer, 

63 is_list_like, 

64 pandas_dtype, 

65) 

66from pandas.core.dtypes.dtypes import ArrowDtype 

67from pandas.core.dtypes.inference import is_file_like 

68 

69from pandas import Series 

70from pandas.core.arrays import ( 

71 DatetimeArray, 

72 TimedeltaArray, 

73) 

74from pandas.core.frame import DataFrame 

75from pandas.core.indexes.api import ( 

76 RangeIndex, 

77 ensure_index, 

78) 

79from pandas.core.internals.api import _make_block 

80from pandas.core.internals.managers import BlockManager 

81 

82from pandas.io.common import ( 

83 IOHandles, 

84 get_handle, 

85 infer_compression, 

86 stringify_path, 

87 validate_header_arg, 

88) 

89from pandas.io.parsers.arrow_parser_wrapper import ArrowParserWrapper 

90from pandas.io.parsers.base_parser import ( 

91 ParserBase, 

92 is_index_col, 

93 parser_defaults, 

94) 

95from pandas.io.parsers.c_parser_wrapper import ( 

96 CParserWrapper, 

97 _concatenate_chunks, 

98) 

99from pandas.io.parsers.python_parser import ( 

100 FixedWidthFieldParser, 

101 PythonParser, 

102) 

103 

104if TYPE_CHECKING: 

105 from collections.abc import ( 

106 Callable, 

107 Hashable, 

108 Iterable, 

109 Mapping, 

110 Sequence, 

111 ) 

112 from types import TracebackType 

113 

114 from pandas._typing import ( 

115 CompressionOptions, 

116 CSVEngine, 

117 DtypeArg, 

118 DtypeBackend, 

119 FilePath, 

120 HashableT, 

121 IndexLabel, 

122 ReadCsvBuffer, 

123 StorageOptions, 

124 UsecolsArgType, 

125 ) 

126 

127 class _read_shared(TypedDict, Generic[HashableT], total=False): 

128 # annotations shared between read_csv/fwf/table's overloads 

129 # NOTE: Keep in sync with the annotations of the implementation 

130 sep: str | lib.NoDefault | None 

131 delimiter: str | lib.NoDefault | None 

132 header: int | Sequence[int] | Literal["infer"] | None 

133 names: Sequence[Hashable] | lib.NoDefault | None 

134 index_col: IndexLabel | Literal[False] | None 

135 usecols: UsecolsArgType 

136 dtype: DtypeArg | None 

137 engine: CSVEngine | None 

138 converters: Mapping[HashableT, Callable] | None 

139 true_values: list | None 

140 false_values: list | None 

141 skipinitialspace: bool 

142 skiprows: list[int] | int | Callable[[Hashable], bool] | None 

143 skipfooter: int 

144 nrows: int | None 

145 na_values: ( 

146 Hashable | Iterable[Hashable] | Mapping[Hashable, Iterable[Hashable]] | None 

147 ) 

148 keep_default_na: bool 

149 na_filter: bool 

150 skip_blank_lines: bool 

151 parse_dates: bool | Sequence[Hashable] | None 

152 date_format: str | dict[Hashable, str] | None 

153 dayfirst: bool 

154 cache_dates: bool 

155 compression: CompressionOptions 

156 thousands: str | None 

157 decimal: str 

158 lineterminator: str | None 

159 quotechar: str 

160 quoting: int 

161 doublequote: bool 

162 escapechar: str | None 

163 comment: str | None 

164 encoding: str | None 

165 encoding_errors: str | None 

166 dialect: str | csv.Dialect | type[csv.Dialect] | None 

167 on_bad_lines: str 

168 low_memory: bool 

169 memory_map: bool 

170 float_precision: Literal["high", "legacy", "round_trip"] | None 

171 storage_options: StorageOptions | None 

172 dtype_backend: DtypeBackend | lib.NoDefault 

173 

174else: 

175 _read_shared = dict 

176 

177 

178class _C_Parser_Defaults(TypedDict): 

179 na_filter: Literal[True] 

180 low_memory: Literal[True] 

181 memory_map: Literal[False] 

182 float_precision: None 

183 

184 

185_c_parser_defaults: _C_Parser_Defaults = { 

186 "na_filter": True, 

187 "low_memory": True, 

188 "memory_map": False, 

189 "float_precision": None, 

190} 

191 

192 

193class _Fwf_Defaults(TypedDict): 

194 colspecs: Literal["infer"] 

195 infer_nrows: Literal[100] 

196 widths: None 

197 

198 

199_fwf_defaults: _Fwf_Defaults = {"colspecs": "infer", "infer_nrows": 100, "widths": None} 

200_c_unsupported = {"skipfooter"} 

201_python_unsupported = {"low_memory", "float_precision"} 

202 

203# Documented as `bool` and consumed for their truthiness, so an unvalidated 

204# non-bool like "False" silently means the opposite (GH#68341). 

205_bool_kwargs = frozenset( 

206 { 

207 "cache_dates", 

208 "dayfirst", 

209 "doublequote", 

210 "iterator", 

211 "keep_default_na", 

212 "low_memory", 

213 "memory_map", 

214 "na_filter", 

215 "skip_blank_lines", 

216 "skipinitialspace", 

217 } 

218) 

219 

220 

221def _is_bool_like(value: object) -> bool: 

222 # Only 0 and 1 stand in for the bools, numpy ints included; a larger int is 

223 # truthy but not a bool, see test_bool_kwarg_int_not_zero_or_one (GH#68341) 

224 return lib.is_bool(value) or (is_integer(value) and value in (0, 1)) 

225 

226 

227def _validate_bool_kwargs(kwds: Mapping[str, Any]) -> None: 

228 # iterate kwds, not the frozenset, so the kwarg named is the first in 

229 # signature order rather than whichever one hash randomization picks 

230 for kwd, value in kwds.items(): 

231 if kwd in _bool_kwargs and not _is_bool_like(value): 

232 # always raises; validate_bool_kwarg owns the message 

233 validate_bool_kwarg(value, kwd, none_allowed=False) 

234 

235 

236# Minimum file size (bytes) to attempt parallel CSV reading. 

237# Below this threshold the overhead of splitting and threading outweighs the benefit. 

238# Break-even is around 1-2 MB; 5 MB leaves margin for cold-cache reads. 

239_PARALLEL_READ_MIN_BYTES = 5 * 1024 * 1024 # 5 MB 

240# Minimum rows per parallel chunk, bounding how finely a file is split. 

241_PARALLEL_MIN_CHUNK_ROWS = 2000 

242# Target bytes per parallel chunk. Between pyarrow's 1 MB read block and 

243# DuckDB's 8 MB per-thread unit. 

244_PARALLEL_CHUNK_BYTES = 4 * 1024 * 1024 # 4 MB 

245# Ceiling on the (column x chunk) pieces the byte-driven count may create, 

246# bounding the per-column cost (a GIL-held allocation and dtype inference) 

247# that every chunk repeats however wide the frame is. 

248_PARALLEL_MAX_COLUMN_PIECES = 1800 

249# Size of the last chunk relative to a full one. Chunks are handed out first 

250# come, first served, so the read ends when the last chunk *started* finishes; 

251# tapering leaves a worker that arrives late something short to take. 1.0 

252# disables the taper. 

253_PARALLEL_TAPER_RATIO = 0.2 

254 

255# Ceiling on the *default* parallel-read worker count: parallel CSV reading 

256# sees diminishing returns beyond a handful of workers, and a low default 

257# avoids oversubscribing the machine. mode.max_threads overrides it in either 

258# direction. 

259_MAX_DEFAULT_WORKERS = 6 

260_pyarrow_unsupported = { 

261 "skipfooter", 

262 "float_precision", 

263 "chunksize", 

264 "comment", 

265 "nrows", 

266 "thousands", 

267 "memory_map", 

268 "dialect", 

269 "quoting", 

270 "lineterminator", 

271 "converters", 

272 "iterator", 

273 "dayfirst", 

274 "skipinitialspace", 

275 "low_memory", 

276 "na_filter", 

277} 

278 

279 

280@overload 

281def validate_integer(name: str, val: None, min_val: int = ...) -> None: ... 

282 

283 

284@overload 

285def validate_integer(name: str, val: float, min_val: int = ...) -> int: ... 

286 

287 

288@overload 

289def validate_integer(name: str, val: int | None, min_val: int = ...) -> int | None: ... 

290 

291 

292def validate_integer( 

293 name: str, val: int | float | None, min_val: int = 0 

294) -> int | None: 

295 """ 

296 Checks whether the 'name' parameter for parsing is either 

297 an integer OR float that can SAFELY be cast to an integer 

298 without losing accuracy. Raises a ValueError if that is 

299 not the case. 

300 

301 Parameters 

302 ---------- 

303 name : str 

304 Parameter name (used for error reporting) 

305 val : int or float 

306 The value to check 

307 min_val : int 

308 Minimum allowed value (val < min_val will result in a ValueError) 

309 """ 

310 if val is None: 

311 return val 

312 

313 msg = f"'{name:s}' must be an integer >={min_val:d}" 

314 if is_float(val): 

315 if int(val) != val: 

316 raise ValueError(msg) 

317 val = int(val) 

318 elif not (is_integer(val) and val >= min_val): 

319 raise ValueError(msg) 

320 

321 return int(val) 

322 

323 

324def _validate_names(names: Sequence[Hashable] | None) -> None: 

325 """ 

326 Raise ValueError if the `names` parameter contains duplicates or has an 

327 invalid data type. 

328 

329 Parameters 

330 ---------- 

331 names : array-like or None 

332 An array containing a list of the names used for the output DataFrame. 

333 

334 Raises 

335 ------ 

336 ValueError 

337 If names are not unique or are not ordered (e.g. set). 

338 """ 

339 if names is not None: 

340 if len(names) != len(set(names)): 

341 raise ValueError("Duplicate names are not allowed.") 

342 if not ( 

343 is_list_like(names, allow_sets=False) or isinstance(names, abc.KeysView) 

344 ): 

345 raise ValueError("Names should be an ordered collection.") 

346 

347 

348def _read( 

349 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], kwds 

350) -> DataFrame | TextFileReader: 

351 """Generic reader of line files.""" 

352 # before the `iterator` peek below, which reads it for truthiness 

353 _validate_bool_kwargs(kwds) 

354 

355 # if we pass a date_format and parse_dates=False, we should not parse the 

356 # dates GH#44366 

357 if kwds.get("parse_dates", None) is None: 

358 if kwds.get("date_format", None) is None: 

359 kwds["parse_dates"] = False 

360 else: 

361 kwds["parse_dates"] = True 

362 

363 # Extract some of the arguments (pass chunksize on). 

364 iterator = kwds.get("iterator", False) 

365 chunksize = kwds.get("chunksize", None) 

366 

367 # Check type of encoding_errors 

368 errors = kwds.get("encoding_errors", "strict") 

369 if not isinstance(errors, str): 

370 raise ValueError( 

371 f"encoding_errors must be a string, got {type(errors).__name__}" 

372 ) 

373 

374 if kwds.get("engine") == "pyarrow": 

375 if iterator: 

376 raise ValueError( 

377 "The 'iterator' option is not supported with the 'pyarrow' engine" 

378 ) 

379 

380 if chunksize is not None: 

381 raise ValueError( 

382 "The 'chunksize' option is not supported with the 'pyarrow' engine" 

383 ) 

384 else: 

385 chunksize = validate_integer("chunksize", chunksize, 1) 

386 

387 nrows = kwds.get("nrows", None) 

388 

389 # Check for duplicates in names. 

390 _validate_names(kwds.get("names", None)) 

391 

392 if kwds.get("float_precision") is not None: 

393 warnings.warn( 

394 "The 'float_precision' argument is deprecated. " 

395 "Use the default float precision instead.", 

396 Pandas4Warning, 

397 stacklevel=find_stack_level(), 

398 ) 

399 

400 # For large local uncompressed files with the C engine, attempt parallel reading. 

401 # Each worker gets its own view of the mmapped file and its own TextReader so 

402 # the GIL-free tokenisation and type-conversion code in parsers.pyx runs truly 

403 # in parallel. 

404 if not iterator and chunksize is None and nrows is None: 

405 _n_workers = _default_n_workers() 

406 if _n_workers > 1 and _can_parallelize_csv(filepath_or_buffer, kwds): 

407 _filepath = stringify_path(filepath_or_buffer) 

408 assert isinstance(_filepath, str) # guaranteed by _can_parallelize_csv 

409 try: 

410 result = _read_csv_parallel(_filepath, kwds, _n_workers) 

411 except (ParserError, UnicodeDecodeError, OverflowError): 

412 # e.g. the one-line sample that infers the column names ends 

413 # inside a quoted field (GH#66259). The serial path below 

414 # handles anything the parallel path cannot -- and raises in 

415 # turn if it too fails. 

416 # Other exceptions from outside the chunk reads propagate: they 

417 # signal a parallel-path bug, not ineligible input. 

418 result = None 

419 if result is not None: 

420 return result 

421 

422 # Create the parser. 

423 parser = TextFileReader(filepath_or_buffer, **kwds) 

424 

425 if chunksize or iterator: 

426 return parser 

427 

428 with parser: 

429 return parser.read(nrows) 

430 

431 

432def _default_n_workers() -> int: 

433 """ 

434 Default worker count for a parallel ``read_csv``. 

435 

436 ``mode.max_threads`` wins whenever it is set (except on Emscripten, which 

437 cannot spawn threads at all). Otherwise it is the smallest of the machine's 

438 logical CPU count, ``_MAX_DEFAULT_WORKERS``, and the CPUs actually available 

439 to the process (CPU affinity / cgroup limits) -- so that an embedded or 

440 containerised pandas does not oversubscribe its allocation. 

441 """ 

442 max_threads = get_option("mode.max_threads") 

443 if sys.platform == "emscripten": 

444 # WASM cannot spawn threads, regardless of mode.max_threads. 

445 return 1 

446 if max_threads is not None: 

447 return max_threads 

448 n_workers = min(os.cpu_count() or 1, _MAX_DEFAULT_WORKERS) 

449 # os.cpu_count() counts the machine's CPUs, not the ones this process may 

450 # use, so it alone would put _MAX_DEFAULT_WORKERS parse threads on a 

451 # single-CPU container. 

452 available = available_cpu_count() 

453 if available is not None: 

454 n_workers = min(n_workers, available) 

455 return n_workers 

456 

457 

458def _can_parallelize_csv(filepath_or_buffer, kwds: dict) -> bool: 

459 """ 

460 Return True when a ``read_csv`` call is eligible for parallel execution. 

461 

462 Parallel reading works by splitting the file's data section into 

463 byte-range chunks at newline boundaries and processing them concurrently 

464 with threads. The following conditions must all hold: 

465 

466 * *filepath_or_buffer* is a local file path (not a URL or file object). 

467 * The file is not compressed. 

468 * The C engine is selected (``engine='c'``, the default). 

469 * The call is not in iterator / chunked mode. 

470 * The entire file is being read (no ``nrows`` limit). 

471 * ``skiprows`` is a plain integer (or absent) - list/callable forms require 

472 tracking absolute line numbers across chunks. 

473 * ``header`` is a single integer or ``None`` - multi-level headers complicate 

474 the preamble boundary. 

475 * ``index_col`` is ``None`` or ``False`` - a column-based index would need its 

476 name propagated to non-first chunks. 

477 * ``usecols`` is ``None`` - column selection changes the mapping between raw 

478 column positions and names in non-first chunks. 

479 * The separator is a single ASCII character or ``r"\\s+"``, and ``quotechar`` 

480 is a single ASCII character - anything else forces the python engine 

481 inside ``TextFileReader``. 

482 * The encoding is ``utf-8`` / ``utf-8-sig`` - chunk workers feed raw file 

483 bytes to the C tokenizer, which decodes words as UTF-8. ``ascii`` is 

484 excluded so a non-ASCII byte still raises rather than being masked. 

485 * ``comment`` is ``None`` and the preamble has no blank lines - full-line 

486 comments and blank lines shift where pandas locates the header relative 

487 to the physical line count used by :func:`_find_data_start_offset`. 

488 * ``escapechar`` is ``None`` and ``dialect`` is not used - an escaped 

489 literal newline would be split mid-field. 

490 * ``parse_dates`` is unset - datetime format inference is per-chunk and may 

491 disagree with the serial whole-column inference. 

492 * ``low_memory`` is truthy - a falsy ``low_memory`` guarantees whole-file 

493 type inference, which per-chunk parallel reading cannot honour 

494 (``low_memory=True`` already documents per-chunk inference divergence). 

495 * ``storage_options`` is ``None`` - it raises for local paths in the serial 

496 path, and that error must not be masked. 

497 * ``on_bad_lines`` is not ``"warn"`` - chunk workers would report 

498 chunk-relative (i.e. wrong) line numbers. 

499 * Both the file and its data section (i.e. excluding the header preamble) 

500 are at least ``_PARALLEL_READ_MIN_BYTES`` bytes large. 

501 

502 Note that a chunk boundary landing on a newline embedded inside a quoted 

503 field leaves that chunk's parser inside an open quote at EOF, so the chunk 

504 fails and the read falls back to serial - it does not corrupt data 

505 silently. 

506 """ 

507 # Must be a local file path, not a URL or file-like object. 

508 if is_file_like(filepath_or_buffer): 

509 return False 

510 filepath = stringify_path(filepath_or_buffer) 

511 if not isinstance(filepath, str): 

512 return False 

513 if "://" in filepath: 

514 return False 

515 

516 # storage_options on a local path raises ValueError in get_handle; the 

517 # parallel path strips it and would mask that error. 

518 if kwds.get("storage_options") is not None: 

519 return False 

520 

521 # Uncompressed files only (splitting is only meaningful for raw bytes). 

522 compression = kwds.get("compression", "infer") 

523 if isinstance(compression, dict): 

524 compression = compression.get("method") 

525 if compression is not None: 

526 try: 

527 if infer_compression(filepath, compression) is not None: 

528 return False 

529 except ValueError: 

530 # unrecognized compression argument; let the serial path raise 

531 return False 

532 

533 # Iterator / chunked mode: caller handles chunking themselves. 

534 if kwds.get("iterator", False) or kwds.get("chunksize") is not None: 

535 return False 

536 

537 # Only the C engine benefits; pyarrow has its own parallelism. 

538 # engine=None means "use the default", which is "c". 

539 if kwds.get("engine") not in ("c", None): 

540 return False 

541 

542 # nrows: we'd need to distribute the row budget across chunks - skip for now. 

543 if kwds.get("nrows") is not None: 

544 return False 

545 

546 # The chunk splitter scans for raw \n bytes. A custom lineterminator 

547 # (which the C engine does honour) would misalign chunk boundaries. 

548 lineterminator = kwds.get("lineterminator") 

549 if lineterminator is not None and lineterminator != "\n": 

550 return False 

551 

552 # Chunk workers feed raw file bytes to the C tokenizer, which decodes 

553 # words as UTF-8. The serial path instead transcodes through a 

554 # TextIOWrapper, so anything that is not already valid UTF-8 (latin-1, 

555 # cp1252, UTF-16/32, ...) must take the serial path. "ascii" is excluded 

556 # for the same reason: the workers would decode a non-ASCII byte as UTF-8 

557 # and succeed, masking the UnicodeDecodeError the serial path raises for 

558 # bytes outside the declared encoding (GH#64347). 

559 encoding = kwds.get("encoding") 

560 if encoding is not None: 

561 try: 

562 codec_name = codecs.lookup(encoding).name 

563 except LookupError: 

564 return False 

565 if codec_name not in ("utf-8", "utf-8-sig"): 

566 return False 

567 

568 # Separators that force the python-engine fallback inside TextFileReader 

569 # (sep=None sniffing, or multi-char/regex seps other than r"\s+") cannot 

570 # use the C-engine buffer-loading fast path. 

571 delimiter = kwds.get("delimiter", ",") 

572 if delimiter is None or (len(delimiter) > 1 and delimiter != r"\s+"): 

573 return False 

574 

575 # A separator or quotechar wider than one byte forces the python engine 

576 # too, and warns on the way. The eligibility check has to catch these 

577 # here: by the time the name-inference read reports the python engine, it 

578 # has already raised that warning, which the serial read then raises 

579 # again. GH#66259 

580 if len(delimiter) == 1 and ord(delimiter) > 127: 

581 return False 

582 quotechar = kwds.get("quotechar") 

583 if isinstance(quotechar, str) and len(quotechar) == 1 and ord(quotechar) > 127: 

584 return False 

585 

586 # Full-line comments before/inside the preamble shift the header location 

587 # in ways _find_data_start_offset cannot see. 

588 if kwds.get("comment") is not None: 

589 return False 

590 

591 # An escaped literal newline (escapechar followed by "\n") would be split 

592 # mid-field. 

593 if kwds.get("escapechar") is not None: 

594 return False 

595 

596 # dialect is merged into the kwds inside TextFileReader, i.e. after this 

597 # function runs, so a dialect-specified escapechar would bypass the check 

598 # above. Be conservative and take the serial path. 

599 if kwds.get("dialect") is not None: 

600 return False 

601 

602 # Datetime format inference is per-chunk and may disagree with the serial 

603 # whole-column inference. 

604 if kwds.get("parse_dates"): 

605 return False 

606 

607 # A falsy low_memory guarantees whole-file type inference, which per-chunk 

608 # parallel reading cannot honour (GH#64347). Test truthiness, not identity: 

609 # low_memory is not coerced to bool, and the serial path in 

610 # CParserWrapper.read also branches on truthiness, so low_memory=0 / 

611 # np.False_ must take the serial path too (GH#66327). 

612 if not kwds.get("low_memory", True): 

613 return False 

614 

615 # on_bad_lines="warn" includes line numbers in its warnings; chunk workers 

616 # would report chunk-relative (i.e. wrong) ones. 

617 if kwds.get("on_bad_lines") == ParserBase.BadLineHandleMethod.BLHM_WARN: 

618 return False 

619 

620 # Callable / list skiprows require tracking absolute line numbers. 

621 skiprows = kwds.get("skiprows") 

622 if skiprows is not None and not isinstance(skiprows, (int, np.integer)): 

623 return False 

624 

625 # Multi-level (list) headers span multiple preamble lines - skip for now. 

626 if isinstance(kwds.get("header", "infer"), (list, tuple)): 

627 return False 

628 

629 # skipfooter: the C engine doesn't support it anyway, but be explicit. 

630 if kwds.get("skipfooter", 0) > 0: 

631 return False 

632 

633 # A column-based index_col would need its name propagated to non-first chunks. 

634 index_col = kwds.get("index_col", None) 

635 if index_col is not None and index_col is not False: 

636 return False 

637 

638 # usecols changes the column name ↔ position mapping in non-first chunks. 

639 if kwds.get("usecols") is not None: 

640 return False 

641 

642 # Only bother for files large enough to amortise the threading overhead. 

643 try: 

644 file_size = os.path.getsize(filepath) 

645 if file_size < _PARALLEL_READ_MIN_BYTES: 

646 return False 

647 except OSError: 

648 return False 

649 

650 # Check that the *data section* (after header/skiprows preamble) is large 

651 # enough to split into at least 2 chunks. Without this, _read_csv_parallel 

652 # would have to return None for files whose preamble eats most of the bytes. 

653 _header = kwds.get("header", "infer") 

654 if _header == "infer": 

655 _header = 0 if kwds.get("names") is None else None 

656 _skiprows = int(kwds.get("skiprows") or 0) 

657 data_start = _find_data_start_offset(filepath, _header, _skiprows) 

658 if file_size - data_start < _PARALLEL_READ_MIN_BYTES: 

659 return False 

660 

661 # Blank lines in the preamble shift where pandas locates the header 

662 # relative to the physical line count used by _find_data_start_offset. 

663 with open(filepath, "rb") as fd: 

664 preamble = fd.read(data_start) 

665 if any(not line.rstrip(b"\r") for line in preamble.splitlines()): 

666 return False 

667 

668 return True 

669 

670 

671def _find_data_start_offset( 

672 filepath: str, 

673 header: int | None, 

674 skiprows: int, 

675) -> int: 

676 """ 

677 Return the byte offset of the first data row in *filepath*. 

678 

679 Reads ``int(skiprows) + (header + 1 if header is not None else 0)`` 

680 physical lines from the start of the file and returns the position after 

681 the last preamble byte. This is the byte from which each non-first chunk 

682 must begin. 

683 """ 

684 if header is None: 

685 header_lines = 0 

686 else: 

687 header_lines = int(header) + 1 # header=0 → 1 header line 

688 

689 preamble_lines = int(skiprows) + header_lines 

690 with open(filepath, "rb") as fd: 

691 for _ in range(preamble_lines): 

692 if not fd.readline(): 

693 break 

694 return fd.tell() 

695 

696 

697def _chunk_size_weights(n_chunks: int, n_tapered: int) -> list[float]: 

698 """ 

699 Relative sizes for *n_chunks* chunks whose last *n_tapered* shrink. 

700 

701 The chunks before the tail are full size; across the tail the size falls 

702 linearly to ``_PARALLEL_TAPER_RATIO`` of one, so the final chunk is the 

703 smallest piece of work in the file. 

704 """ 

705 weights = [1.0] * n_chunks 

706 n_tapered = min(n_tapered, n_chunks) 

707 for position in range(n_tapered): 

708 share = (position + 1) / n_tapered 

709 weights[n_chunks - n_tapered + position] = ( 

710 1.0 - (1.0 - _PARALLEL_TAPER_RATIO) * share 

711 ) 

712 return weights 

713 

714 

715def _find_chunk_byte_offsets( 

716 filepath: str, 

717 n_chunks: int, 

718 data_start: int, 

719 n_workers: int = 0, 

720) -> list[int]: 

721 """ 

722 Compute byte offsets that partition the data portion of *filepath* into 

723 *n_chunks* pieces aligned to newline boundaries. 

724 

725 The pieces are equal-sized, except that when *n_workers* is given and the 

726 chunks take more than one round the last ``2 * n_workers`` taper down: the 

727 read finishes when the last chunk a worker picked up does, so ending on 

728 small chunks costs less than ending on a full one. Within a single round 

729 every chunk runs at once, so the makespan is the largest of them and any 

730 taper would only inflate it. 

731 

732 Returns a list of ``n + 1`` offsets where the byte range 

733 ``[offsets[i], offsets[i+1])`` defines chunk *i*. 

734 """ 

735 file_size = os.path.getsize(filepath) 

736 data_size = file_size - data_start 

737 offsets = [data_start] 

738 

739 if n_chunks <= 1 or data_size <= 0: 

740 offsets.append(file_size) 

741 return offsets 

742 

743 n_tapered = 2 * n_workers if n_chunks > n_workers else 0 

744 weights = _chunk_size_weights(n_chunks, n_tapered) 

745 total_weight = sum(weights) 

746 running_weight = 0.0 

747 with open(filepath, "rb") as fd: 

748 for i in range(1, n_chunks): 

749 running_weight += weights[i - 1] 

750 target = data_start + int(data_size * running_weight / total_weight) 

751 if target >= file_size: 

752 break 

753 fd.seek(target) 

754 fd.readline() # advance past the partial line at the split point 

755 pos = fd.tell() 

756 if pos >= file_size: 

757 break 

758 if pos == offsets[-1]: 

759 # This target fell inside the line the previous boundary 

760 # already ended; skip it rather than abandoning the boundaries 

761 # after it, so one long line cannot collapse the whole split. 

762 continue 

763 offsets.append(pos) 

764 

765 offsets.append(file_size) 

766 return offsets 

767 

768 

769def _raise_collected(warning_sink: list[tuple[str, type[Warning]]]) -> None: 

770 """Raise each distinct warning the parallel read's parsers collected.""" 

771 for warn_msg, warn_category in dict.fromkeys(warning_sink): 

772 warnings.warn(warn_msg, warn_category, stacklevel=find_stack_level()) 

773 

774 

775def _read_csv_parallel( 

776 filepath: str, 

777 kwds: dict, 

778 n_workers: int, 

779) -> DataFrame | None: 

780 """ 

781 Read a large CSV file in parallel using *n_workers* threads. 

782 

783 Every read this makes - the one-line name inference and each chunk - raises 

784 its own copy of the same ``ParserWarning``, and a worker raising one lands 

785 the stacklevel walk in threading internals. So the reads collect their 

786 warnings into a sink (see :attr:`TextReader.warning_sink`) and each distinct 

787 one is raised once here, from the caller's frame - except when the caller 

788 goes on to read the file serially, since that read raises them itself. 

789 GH#66259 

790 

791 The file's data section (everything after the header / skiprows preamble) 

792 is split into byte-range chunks aligned to newline boundaries, sized from 

793 *n_workers* and from the file's own rows, bytes and column count. Each 

794 chunk is parsed by an independent 

795 :class:`TextFileReader` / C-engine instance. Because the hot paths in 

796 ``pandas/_libs/parsers.pyx`` (tokenisation, int/float/bool conversion) 

797 are wrapped in ``with nogil:`` blocks, threads achieve real CPU-level 

798 parallelism. 

799 

800 The caller must ensure the file is eligible via :func:`_can_parallelize_csv` 

801 before calling this function. 

802 

803 Returns ``None`` when parallel reading turns out not to be applicable 

804 after all (the data section cannot be split, the engine falls back to 

805 python, the one-line sample used to infer column names has no columns, 

806 per-chunk dtype inference disagrees in a way that would not match the 

807 serial result, or any chunk fails to parse); the caller then reads 

808 serially. 

809 """ 

810 warning_sink: list[tuple[str, type[Warning]]] = [] 

811 try: 

812 result = _read_csv_chunks(filepath, kwds, n_workers, warning_sink) 

813 except (ParserError, UnicodeDecodeError, OverflowError): 

814 # The caller answers these with a serial read of the whole file. 

815 raise 

816 except Exception: 

817 # Nothing re-reads the file after this, so it is here or nowhere. This 

818 # over-warns when a serial read would have died before reaching the 

819 # warning's column; losing the warning otherwise is the worse trade. 

820 _raise_collected(warning_sink) 

821 raise 

822 if result is not None: 

823 _raise_collected(warning_sink) 

824 return result 

825 

826 

827def _read_csv_chunks( 

828 filepath: str, 

829 kwds: dict, 

830 n_workers: int, 

831 warning_sink: list[tuple[str, type[Warning]]], 

832) -> DataFrame | None: 

833 """ 

834 Body of :func:`_read_csv_parallel`; see there for what the arguments mean 

835 and when the result is ``None``. Appends the ``ParserWarning``\\s its 

836 parsers would have raised to *warning_sink* instead of raising them. 

837 """ 

838 # Resolve the effective header value (mirrors TextFileReader.__init__). 

839 header = kwds.get("header", "infer") 

840 if header == "infer": 

841 header = 0 if kwds.get("names") is None else None 

842 

843 skiprows = int(kwds.get("skiprows") or 0) 

844 

845 # Byte offset at which real data rows begin. 

846 data_start = _find_data_start_offset(filepath, header, skiprows) 

847 

848 # Oversubscribe the workers so one slow chunk cannot strand a core. Take 

849 # the median of sampled line lengths - a single probe lets one atypical 

850 # line skew the estimate. The count is raised to follow the file's size 

851 # once the column count is known, below. 

852 data_size = os.path.getsize(filepath) - data_start 

853 line_lens = [] 

854 with open(filepath, "rb") as fh: 

855 for probe in range(9): 

856 fh.seek(data_start + data_size * probe // 9) 

857 fh.readline(1 << 20) # advance past the partial line at the probe 

858 line = fh.readline(1 << 20) 

859 if line: 

860 line_lens.append(len(line)) 

861 line_lens.sort() 

862 bytes_per_row = line_lens[len(line_lens) // 2] if line_lens else data_size 

863 est_rows = data_size // bytes_per_row 

864 n_target = min(n_workers * 3, est_rows // _PARALLEL_MIN_CHUNK_ROWS) 

865 if n_target < 2: 

866 # Too few rows to split, unless each half is big enough to amortise 

867 # the per-column costs anyway. 

868 if data_size < 2 * _PARALLEL_READ_MIN_BYTES: 

869 return None 

870 n_target = 2 

871 

872 # ------------------------------------------------------------------ 

873 # Infer column names from the preamble + one data line (very fast). 

874 # ------------------------------------------------------------------ 

875 base_kwds: dict = { 

876 **kwds, 

877 "compression": None, 

878 "memory_map": False, 

879 "storage_options": None, 

880 } 

881 

882 with open(filepath, "rb") as fd: 

883 preamble = fd.read(data_start) 

884 first_line = fd.readline() 

885 # The tokenizer also ends a line at a bare \r, which readline does not, so 

886 # data_start could be misplaced without the row count check below noticing. 

887 if b"\r" in (preamble + first_line).replace(b"\r\n", b""): 

888 return None 

889 

890 # Only the column names and the row count are kept, so the sample is parsed 

891 # as strings: a value that converts on its own line but not for the whole 

892 # column (an int above 2**63 under dtype_backend="pyarrow", say) would 

893 # otherwise raise here for a file the serial path reads fine. GH#66259 

894 name_buf = io.BytesIO(preamble + first_line) 

895 name_kwds = { 

896 **base_kwds, 

897 "dtype": str, 

898 "converters": None, 

899 "dtype_backend": lib.no_default, 

900 } 

901 try: 

902 name_reader = TextFileReader(name_buf, **name_kwds) 

903 except EmptyDataError: 

904 # The one data line we sliced off is blank (e.g. header=None on a file 

905 # whose first physical line is empty), so the name-inference read sees 

906 # no columns. The serial path skips the blank line and reads the file 

907 # fine, so fall back rather than propagate. GH#66259 

908 return None 

909 if not isinstance(name_reader._engine, CParserWrapper): 

910 # TextFileReader fell back to the python engine for a reason the 

911 # eligibility checks did not anticipate; load_buffer needs the C engine. 

912 name_reader.close() 

913 return None 

914 name_reader._engine._warning_sink = warning_sink 

915 name_reader._engine._reader.warning_sink = warning_sink 

916 if name_reader._engine._reader.leading_cols: 

917 # Data rows have more fields than the header (implicit index). The 

918 # chunk workers would fail on the extra field; bail out up front. 

919 name_reader.close() 

920 return None 

921 assert name_reader._engine.orig_names is not None 

922 col_names: list = list(name_reader._engine.orig_names) 

923 # name_buf holds the preamble plus exactly one data line. Parsing it must 

924 # yield exactly one row; anything else means the preamble's physical line 

925 # count disagrees with its logical row count (e.g. a quoted embedded 

926 # newline in the header), so data_start is wrong. 

927 n_first_rows = len(name_reader.read()) 

928 name_reader.close() 

929 if n_first_rows != 1: 

930 return None 

931 

932 # A dict ``dtype`` is applied per raw header name: when a name is repeated, 

933 # the serial path assigns that dtype to every de-duplicated column (``a`` 

934 # and ``a.1``), but the workers receive the already-de-duplicated 

935 # ``col_names`` and would match only ``a``. ``col_names`` no longer shows 

936 # the duplication, so recover the raw header names (parse the header line 

937 # as data) and fall back to serial when they collide. GH#64347 

938 if header is not None and isinstance(kwds.get("dtype"), dict): 

939 header_line = preamble.splitlines(keepends=True)[-1] 

940 raw_reader = TextFileReader( 

941 io.BytesIO(header_line), 

942 **{ 

943 **base_kwds, 

944 "header": None, 

945 "names": None, 

946 "usecols": None, 

947 "index_col": False, 

948 "dtype": str, 

949 "converters": None, 

950 "skiprows": None, 

951 "na_filter": False, 

952 }, 

953 ) 

954 try: 

955 raw_names = list(raw_reader.read().iloc[0]) 

956 finally: 

957 raw_reader.close() 

958 if len(set(raw_names)) != len(raw_names): 

959 return None 

960 

961 # n_target so far scales with the worker count, which says nothing about 

962 # the file: a 128 MB file gets the same split as one just over the size 

963 # gate. Raise it to follow the bytes, bounded by the piece budget and by 

964 # the row floor, since a file can be byte-rich and row-poor. Only ever 

965 # raised, so no file comes out coarser than before. 

966 size_target = min( 

967 data_size // _PARALLEL_CHUNK_BYTES, 

968 _PARALLEL_MAX_COLUMN_PIECES // max(len(col_names), 1), 

969 est_rows // _PARALLEL_MIN_CHUNK_ROWS, 

970 ) 

971 # A count that is not a multiple of the worker count spends its last round 

972 # mostly idle: 31 chunks over 6 workers is 5.17 rounds' work that takes 6. 

973 # Round up, and only this term, so neither the worker-derived floor nor a 

974 # file below one block moves. 

975 if size_target > n_workers: 

976 size_target = -(-size_target // n_workers) * n_workers 

977 n_target = max(n_target, size_target) 

978 

979 offsets = _find_chunk_byte_offsets(filepath, n_target, data_start, n_workers) 

980 n_chunks = len(offsets) - 1 

981 if n_chunks < 2: 

982 # e.g. a data section with no interior newlines (one giant line) 

983 return None 

984 # Spare threads would only spin up to find the queue empty. 

985 n_workers = min(n_workers, n_chunks) 

986 

987 # ------------------------------------------------------------------ 

988 # Dispatch all chunks in parallel. Each worker gets a zero-copy 

989 # memoryview slice of the mmapped file and calls load_buffer() so 

990 # tokenisation is fully GIL-free. 

991 # ------------------------------------------------------------------ 

992 chunk_kwds: dict = { 

993 **base_kwds, 

994 "header": None, 

995 "names": col_names, 

996 "skiprows": None, 

997 # A worker's byte slice already bounds peak memory, and skipping 

998 # low_memory avoids a per-worker GIL-held concatenate. 

999 "low_memory": False, 

1000 } 

1001 

1002 # Each thread reuses one parser to drain the queue, so no worker stalls 

1003 # the gather on a straggler and no chunk pays parser construction. 

1004 chunk_queue: queue.SimpleQueue = queue.SimpleQueue() 

1005 for chunk_idx in range(n_chunks): 

1006 chunk_queue.put(chunk_idx) 

1007 results: list = [None] * n_chunks 

1008 chunk_failed = threading.Event() 

1009 workers_readers: list = [] 

1010 

1011 def _worker(mv: memoryview) -> None: 

1012 reader = TextFileReader(io.BytesIO(b""), **chunk_kwds) 

1013 assert isinstance(reader._engine, CParserWrapper) 

1014 # Buffers are reused across the queued chunks and freed at close; 

1015 # trimming between chunks would stall other workers on reallocs. 

1016 reader._engine._reader.trim_after_read = False 

1017 # Hand back string columns as raw pending handles: the gather below 

1018 # combines each column's chunks into one ExtensionArray, so the 

1019 # GIL-held pyarrow wrap happens once per column rather than once per 

1020 # chunk in every worker. 

1021 reader._engine.wrap_deferred = False 

1022 # Row-blocked conversion of numeric columns pays off once enough 

1023 # workers contend for memory bandwidth (see TextReader.block_workers). 

1024 reader._engine._reader.block_workers = n_workers 

1025 reader._engine._warning_sink = warning_sink 

1026 reader._engine._reader.warning_sink = warning_sink 

1027 workers_readers.append(reader) 

1028 while not chunk_failed.is_set(): 

1029 try: 

1030 chunk_idx = chunk_queue.get_nowait() 

1031 except queue.Empty: 

1032 return 

1033 data = mv[offsets[chunk_idx] : offsets[chunk_idx + 1]] 

1034 # The serial C parser strips a UTF-8 BOM at byte 0 of the file 

1035 # only; strip_bom mirrors that for the chunk starting there. 

1036 reader._engine._reader.load_buffer(data, strip_bom=offsets[chunk_idx] == 0) 

1037 # the reader holds its own reference for the parse duration 

1038 del data 

1039 # On a reused parser a zero-row chunk would raise StopIteration 

1040 # and close the reader; reset so it returns empty meta instead. 

1041 reader._engine._first_chunk = True 

1042 try: 

1043 # Raw column arrays, not DataFrames: per-chunk frame assembly 

1044 # holds the GIL and the concat would redo the block machinery. 

1045 _, chunk_columns, col_dict = reader._engine.read() 

1046 except Exception: 

1047 chunk_failed.set() 

1048 return 

1049 results[chunk_idx] = (chunk_columns, col_dict) 

1050 

1051 def _copy_pieces(jobs: list[tuple[np.ndarray, int, np.ndarray]]) -> None: 

1052 # ndarray slice assignment releases the GIL for the memcpy, so the 

1053 # gather parallelises across the pool. 

1054 for dst, pos, piece in jobs: 

1055 dst[pos : pos + len(piece)] = piece 

1056 

1057 def _piece_ndarray(piece) -> np.ndarray | None: 

1058 # The ndarray behind pieces whose final block is ndarray-backed: 

1059 # plain ndarrays plus tz-naive datetime64/timedelta64. 

1060 if isinstance(piece, np.ndarray): 

1061 if piece.dtype == object: 

1062 # Object pieces take the leftover path below, which runs 

1063 # the same constructor inference as a serial read. 

1064 return None 

1065 return piece 

1066 if isinstance(piece, (DatetimeArray, TimedeltaArray)) and isinstance( 

1067 piece.dtype, np.dtype 

1068 ): 

1069 return piece._ndarray 

1070 return None 

1071 

1072 dtype_arg = kwds.get("dtype") 

1073 # Mirror TextFileReader.read: a dict or str/object dtype needs a 

1074 # Series-per-column wrap, so it skips direct block assembly. 

1075 needs_series_wrap = isinstance(dtype_arg, dict) or ( 

1076 dtype_arg is not None and pandas_dtype(dtype_arg) in (np.str_, np.object_) 

1077 ) 

1078 

1079 block_specs: list[tuple] = [] 

1080 leftover_pos: list[int] = [] 

1081 col_list: list = [] 

1082 chunk_dicts: list[dict] = [] 

1083 columns: list[Hashable] = [] 

1084 total = 0 

1085 readers_closing = False 

1086 

1087 # mmap the file once and pass zero-copy memoryview slices to each thread 

1088 # instead of having each thread open/seek/read its own copy. mmap dups 

1089 # the file descriptor, so the file object can be closed right away. Map it 

1090 # last, so that every path out of here runs the close below. GH#66259 

1091 with open(filepath, "rb") as fh: 

1092 mm = mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ) 

1093 try: 

1094 with ( 

1095 contextlib.ExitStack() as views, 

1096 ThreadPoolExecutor(max_workers=n_workers) as pool, 

1097 ): 

1098 # One view per worker: slicing a shared memoryview from several 

1099 # threads races on free-threaded builds (GH#71418). 

1100 worker_views = [ 

1101 views.enter_context(memoryview(mm)) for _ in range(n_workers) 

1102 ] 

1103 for fut in [pool.submit(_worker, mv) for mv in worker_views]: 

1104 fut.result() 

1105 if chunk_failed.is_set(): 

1106 # Let the serial read raise: a chunk's error can be an artifact 

1107 # of the split (a boundary inside a quoted newline), and even a 

1108 # genuine one differs from serial in which error comes first 

1109 # and in the row positions it reports. 

1110 return None 

1111 

1112 # A column of only NA tokens and ints too large for int64 converts 

1113 # to no numeric dtype, and is then emitted with its NA tokens left 

1114 # as literal strings (GH#14983). Which chunks hit that depends on 

1115 # how the file was split, and the resulting dtype is str either 

1116 # way, so the reconciliation below cannot see the difference -- the 

1117 # values would just silently disagree with a serial read. GH#66259 

1118 if any( 

1119 reader._engine._reader.na_left_literal for reader in workers_readers 

1120 ): 

1121 return None 

1122 

1123 # Freeing parser buffers costs a few ms of madvise; run it on the 

1124 # pool, overlapped with the gather copies below. 

1125 close_futures = [pool.submit(reader.close) for reader in workers_readers] 

1126 readers_closing = True 

1127 

1128 # Zero-row chunks carry empty-meta object dtypes that would 

1129 # needlessly trip the dtype reconciliation below. 

1130 chunk_results = [ 

1131 res 

1132 for res in cast("list[tuple]", results) 

1133 if res[1] and len(next(iter(res[1].values()))) > 0 

1134 ] 

1135 if not chunk_results: 

1136 # every chunk parsed to zero rows (e.g. all blank lines); 

1137 # let the serial path build the empty frame 

1138 return None 

1139 columns = list(chunk_results[0][0]) 

1140 chunk_dicts = [col_dict for _, col_dict in chunk_results] 

1141 col_list = list(chunk_dicts[0]) 

1142 if col_list: 

1143 total = sum(len(chunk_dict[col_list[0]]) for chunk_dict in chunk_dicts) 

1144 

1145 # Per-chunk dtype inference can disagree with whole-file inference 

1146 # (a lone non-numeric row makes only its own chunk object). Mixed 

1147 # signed-int/float chunks gather to the float64 serial would give; 

1148 # anything else must be re-read serially. ArrowDtype is excluded 

1149 # because pyarrow widens int to double with a *checked* cast, which 

1150 # raises above 2**53 where the serial whole-file read just infers 

1151 # double. GH#66259 

1152 for name in col_list: 

1153 chunk_dtypes = {chunk_dict[name].dtype for chunk_dict in chunk_dicts} 

1154 if len(chunk_dtypes) > 1 and not all( 

1155 dt.kind in "if" and not isinstance(dt, ArrowDtype) 

1156 for dt in chunk_dtypes 

1157 ): 

1158 return None 

1159 

1160 if not needs_series_wrap: 

1161 # Gather same-dtype ndarray columns straight into one 

1162 # preallocated consolidated block per dtype; anything else 

1163 # falls to _concatenate_chunks below. 

1164 groups: dict[np.dtype, list[int]] = {} 

1165 col_nd_pieces: dict[int, list[np.ndarray]] = {} 

1166 for pos, name in enumerate(col_list): 

1167 nd_pieces = [ 

1168 _piece_ndarray(chunk_dict[name]) for chunk_dict in chunk_dicts 

1169 ] 

1170 first = nd_pieces[0] 

1171 if first is not None and all( 

1172 piece is not None and piece.dtype == first.dtype 

1173 for piece in nd_pieces 

1174 ): 

1175 groups.setdefault(first.dtype, []).append(pos) 

1176 col_nd_pieces[pos] = cast("list[np.ndarray]", nd_pieces) 

1177 else: 

1178 leftover_pos.append(pos) 

1179 

1180 copy_jobs: list[tuple[np.ndarray, int, np.ndarray]] = [] 

1181 for group_dtype, positions in groups.items(): 

1182 values2d = np.empty((len(positions), total), dtype=group_dtype) 

1183 for row, pos in enumerate(positions): 

1184 offset = 0 

1185 for piece in col_nd_pieces[pos]: 

1186 copy_jobs.append((values2d[row], offset, piece)) 

1187 offset += len(piece) 

1188 block_specs.append((values2d, np.array(positions, dtype=np.intp))) 

1189 

1190 # Batch the copies instead of submitting one task per 

1191 # (column, chunk): on a wide frame that is n_columns * n_chunks 

1192 # tasks, each moving a sliver of one column, and the dispatch 

1193 # then costs many times the memcpy it schedules. 

1194 batch = -(-len(copy_jobs) // (n_workers * 4)) or 1 

1195 copy_futures = [ 

1196 pool.submit(_copy_pieces, copy_jobs[start : start + batch]) 

1197 for start in range(0, len(copy_jobs), batch) 

1198 ] 

1199 for fut in copy_futures: 

1200 fut.result() 

1201 for fut in close_futures: 

1202 fut.result() 

1203 finally: 

1204 if not readers_closing: 

1205 for reader in workers_readers: 

1206 try: 

1207 reader.close() 

1208 except Exception: 

1209 pass 

1210 # The closes above drop the chunk slices the readers hold, so the 

1211 # mapping is normally unmapped right here. suppress: a traceback that 

1212 # still pins a slice would make close() raise BufferError and mask the 

1213 # exception on its way out; the mapping then goes when it is collected. 

1214 with contextlib.suppress(BufferError): 

1215 mm.close() 

1216 

1217 index = RangeIndex(total) 

1218 

1219 if needs_series_wrap: 

1220 data = _concatenate_chunks(chunk_dicts, columns, warn_mixed=False) 

1221 if isinstance(dtype_arg, dict): 

1222 dtype: defaultdict = defaultdict(lambda: None) 

1223 dtype.update(dtype_arg) 

1224 else: 

1225 dtype = defaultdict(lambda: dtype_arg) 

1226 new_col_dict = { 

1227 k: Series( 

1228 v, 

1229 index=index, 

1230 dtype=( 

1231 dtype[k] 

1232 if pandas_dtype(dtype[k]) in (np.str_, np.object_) 

1233 else None 

1234 ), 

1235 copy=False, 

1236 ) 

1237 for k, v in data.items() 

1238 } 

1239 return DataFrame(new_col_dict, columns=columns, index=index, copy=False) 

1240 

1241 if leftover_pos: 

1242 leftover_names = [col_list[pos] for pos in leftover_pos] 

1243 leftover_dicts = [ 

1244 {name: chunk_dict[name] for name in leftover_names} 

1245 for chunk_dict in chunk_dicts 

1246 ] 

1247 gathered = _concatenate_chunks(leftover_dicts, leftover_names, warn_mixed=False) 

1248 for pos, name in zip(leftover_pos, leftover_names, strict=True): 

1249 values = gathered[name] 

1250 if isinstance(values, np.ndarray) and values.dtype == object: 

1251 # Match the serial constructor's inference for object 

1252 # columns (object -> str dtype under future.infer_string). 

1253 values = Series(values, index=index, copy=False)._values 

1254 if isinstance(values, np.ndarray): 

1255 # _make_block expects numpy values already in 2D block shape 

1256 values = values.reshape(1, -1) 

1257 block_specs.append((values, np.array([pos], dtype=np.intp))) 

1258 

1259 block_objs = [_make_block(values, placement) for values, placement in block_specs] 

1260 mgr = BlockManager(block_objs, [ensure_index(columns), index]) 

1261 return DataFrame._from_mgr(mgr, mgr.axes) 

1262 

1263 

1264@overload 

1265def read_csv( 

1266 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1267 *, 

1268 iterator: Literal[True], 

1269 chunksize: int | None = ..., 

1270 **kwds: Unpack[_read_shared[HashableT]], 

1271) -> TextFileReader: ... 

1272 

1273 

1274@overload 

1275def read_csv( 

1276 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1277 *, 

1278 iterator: bool = ..., 

1279 chunksize: int, 

1280 **kwds: Unpack[_read_shared[HashableT]], 

1281) -> TextFileReader: ... 

1282 

1283 

1284@overload 

1285def read_csv( 

1286 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1287 *, 

1288 iterator: Literal[False] = ..., 

1289 chunksize: None = ..., 

1290 **kwds: Unpack[_read_shared[HashableT]], 

1291) -> DataFrame: ... 

1292 

1293 

1294@overload 

1295def read_csv( 

1296 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1297 *, 

1298 iterator: bool = ..., 

1299 chunksize: int | None = ..., 

1300 **kwds: Unpack[_read_shared[HashableT]], 

1301) -> DataFrame | TextFileReader: ... 

1302 

1303 

1304@set_module("pandas") 

1305def read_csv( 

1306 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1307 *, 

1308 sep: str | lib.NoDefault | None = lib.no_default, 

1309 delimiter: str | lib.NoDefault | None = lib.no_default, 

1310 # Column and Index Locations and Names 

1311 header: int | Sequence[int] | Literal["infer"] | None = "infer", 

1312 names: Sequence[Hashable] | lib.NoDefault | None = lib.no_default, 

1313 index_col: IndexLabel | Literal[False] | None = None, 

1314 usecols: UsecolsArgType = None, 

1315 # General Parsing Configuration 

1316 dtype: DtypeArg | None = None, 

1317 engine: CSVEngine | None = None, 

1318 converters: Mapping[HashableT, Callable] | None = None, 

1319 true_values: list | None = None, 

1320 false_values: list | None = None, 

1321 skipinitialspace: bool = False, 

1322 skiprows: list[int] | int | Callable[[Hashable], bool] | None = None, 

1323 skipfooter: int = 0, 

1324 nrows: int | None = None, 

1325 # NA and Missing Data Handling 

1326 na_values: ( 

1327 Hashable | Iterable[Hashable] | Mapping[Hashable, Iterable[Hashable]] | None 

1328 ) = None, 

1329 keep_default_na: bool = True, 

1330 na_filter: bool = True, 

1331 skip_blank_lines: bool = True, 

1332 # Datetime Handling 

1333 parse_dates: bool | Sequence[Hashable] | None = None, 

1334 date_format: str | dict[Hashable, str] | None = None, 

1335 dayfirst: bool = False, 

1336 cache_dates: bool = True, 

1337 # Iteration 

1338 iterator: bool = False, 

1339 chunksize: int | None = None, 

1340 # Quoting, Compression, and File Format 

1341 compression: CompressionOptions = "infer", 

1342 thousands: str | None = None, 

1343 decimal: str = ".", 

1344 lineterminator: str | None = None, 

1345 quotechar: str = '"', 

1346 quoting: int = csv.QUOTE_MINIMAL, 

1347 doublequote: bool = True, 

1348 escapechar: str | None = None, 

1349 comment: str | None = None, 

1350 encoding: str | None = None, 

1351 encoding_errors: str | None = "strict", 

1352 dialect: str | csv.Dialect | type[csv.Dialect] | None = None, 

1353 # Error Handling 

1354 on_bad_lines: str = "error", 

1355 # Internal 

1356 low_memory: bool = _c_parser_defaults["low_memory"], 

1357 memory_map: bool = False, 

1358 float_precision: Literal["high", "legacy", "round_trip"] | None = None, 

1359 storage_options: StorageOptions | None = None, 

1360 dtype_backend: DtypeBackend | lib.NoDefault = lib.no_default, 

1361) -> DataFrame | TextFileReader: 

1362 """ 

1363 Read a comma-separated values (csv) file into DataFrame. 

1364 

1365 Also supports optionally iterating or breaking of the file 

1366 into chunks. 

1367 

1368 Additional help can be found in the online docs for 

1369 `IO Tools <https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html>`_. 

1370 

1371 Parameters 

1372 ---------- 

1373 filepath_or_buffer : str, path object or file-like object 

1374 Any valid string path is acceptable. The string could be a URL. Valid 

1375 URL schemes include http, ftp, s3, gs, and file. For file URLs, a host is 

1376 expected. A local file could be: file://localhost/path/to/table.csv. 

1377 

1378 Certain URL schemes may require additional packages. For example, S3 

1379 URLs require the ``s3fs`` library. See 

1380 :ref:`install.optional_dependencies` for a full list. 

1381 

1382 If you want to pass in a path object, pandas accepts any ``os.PathLike``. 

1383 

1384 By file-like object, we refer to objects with a ``read()`` method that 

1385 accepts an optional size argument, such as a file handle (e.g. via 

1386 builtin ``open`` function) or ``StringIO``. 

1387 sep : str, default ',' 

1388 Character or regex pattern to treat as the delimiter. ``sep=None`` detects 

1389 the separator from the first valid row of the file with Python's builtin 

1390 sniffer tool, ``csv.Sniffer``; it is supported only by the Python parsing 

1391 engine, which will be used automatically. 

1392 In addition, separators longer than 1 character and different from 

1393 ``'\\s+'`` will be interpreted as regular expressions and will force 

1394 the use of the Python parsing engine. Note that regex delimiters are prone 

1395 to ignoring quoted data. Regex example: ``'\\r\\t'``. 

1396 delimiter : str, optional 

1397 Alias for ``sep``. 

1398 header : int, Sequence of int, 'infer' or None, default 'infer' 

1399 Row index or indices in the file to use as DataFrame column labels. 

1400 Indexing starts at 0 and counts only non-blank, non-commented lines 

1401 when ``skip_blank_lines=True``, so ``header=0`` denotes the first 

1402 line of data rather than the first line of the file. Valid arguments 

1403 are: 

1404 

1405 * ``int``: line index at which column labels are read. 

1406 * Sequence of ``int``: line indices at which column labels are read 

1407 and combined into a :class:`~pandas.MultiIndex` on the columns, 

1408 e.g. ``[0, 1, 3]``. Intervening rows that are not specified will 

1409 be skipped (e.g. row 2 in this example). 

1410 * ``None``: no row in the file is interpreted as column labels. 

1411 Columns are labelled by integer position, or by the values passed 

1412 to ``names`` when provided. Use this for files with no header 

1413 row. If the file has a header row that should be overridden by 

1414 ``names``, pass ``header=0`` instead. 

1415 * ``'infer'`` (default): equivalent to ``header=0`` when no 

1416 ``names`` are passed, otherwise equivalent to ``header=None``. 

1417 

1418 When inferred from the file contents, headers are kept distinct from 

1419 each other by renaming duplicate names with a numeric suffix of the form 

1420 ``".{count}"`` starting from 1, e.g. ``"foo"`` and ``"foo.1"``. 

1421 Empty headers are named ``"Unnamed: {i}"`` or `` 

1422 "Unnamed: {i}_level_{level}"`` 

1423 in the case of MultiIndex columns. 

1424 names : Sequence of Hashable, optional 

1425 Sequence of column labels to apply. If the file contains a header row, 

1426 then you should explicitly pass ``header=0`` to override the column names. 

1427 Duplicates in this list are not allowed. 

1428 index_col : Hashable, Sequence of Hashable or False, optional 

1429 Column(s) to use as row label(s), denoted either by column labels or column 

1430 indices. If a sequence of labels or indices is given, 

1431 :class:`~pandas.MultiIndex` 

1432 will be formed for the row labels. 

1433 

1434 Note: ``index_col=False`` can be used to force pandas to *not* use the first 

1435 column as the index, e.g., when you have a malformed file with delimiters at 

1436 the end of each line. 

1437 usecols : Sequence of Hashable or Callable, optional 

1438 Subset of columns to select, denoted either 

1439 by column labels or column indices. 

1440 If list-like, all elements must either 

1441 be positional (i.e. integer indices into the document columns) or strings 

1442 that correspond to column names provided either by the user in ``names`` or 

1443 inferred from the document header row(s). 

1444 If ``names`` are given, the document 

1445 header row(s) are not taken into account. For example, a valid list-like 

1446 ``usecols`` parameter would be ``[0, 1, 2]`` or ``['foo', 'bar', 'baz']``. 

1447 Element order is ignored, so ``usecols=[0, 1]`` is the same as ``[1, 0]``. 

1448 To instantiate a :class:`~pandas.DataFrame` from ``data`` with element order 

1449 preserved use ``pd.read_csv(data, usecols=['foo', 'bar'])[['foo', 'bar']]`` 

1450 for columns in ``['foo', 'bar']`` order or 

1451 ``pd.read_csv(data, usecols=['foo', 'bar'])[['bar', 'foo']]`` 

1452 for ``['bar', 'foo']`` order. 

1453 

1454 If callable, the callable function will be evaluated against the column 

1455 names, returning names where the callable function evaluates to ``True``. An 

1456 example of a valid callable argument would be ``lambda x: x.upper() in 

1457 ['AAA', 'BBB', 'DDD']``. Using this parameter results in much faster 

1458 parsing time and lower memory usage. 

1459 dtype : dtype or dict of {Hashable : dtype}, optional 

1460 Data type(s) to apply to either the whole dataset or individual columns. 

1461 E.g., ``{'a': np.float64, 'b': np.int32, 'c': 'Int64'}`` 

1462 Use ``str`` or ``object`` together with suitable ``na_values`` settings 

1463 to preserve and not interpret ``dtype``. 

1464 Specifying a ``dtype`` does not change how missing values are parsed; 

1465 values recognized as missing (see ``na_values`` and ``keep_default_na``) 

1466 are still read as ``NaN`` (or the dtype's NA value) rather than cast to 

1467 the requested ``dtype``. 

1468 If ``converters`` are specified, they will be applied INSTEAD 

1469 of ``dtype`` conversion. Specify a ``defaultdict`` as input where 

1470 the default determines the ``dtype`` 

1471 of the columns which are not explicitly 

1472 listed. 

1473 engine : {'c', 'python', 'pyarrow'}, optional 

1474 Parser engine to use. The C and pyarrow engines are faster, 

1475 while the python engine 

1476 is currently more feature-complete. The pyarrow engine is 

1477 multithreaded, and the C engine reads sufficiently large files in 

1478 parallel. Some features of the "pyarrow" engine 

1479 are unsupported or may not work correctly. 

1480 converters : dict of {Hashable : Callable}, optional 

1481 Functions for converting values in specified columns. Keys can either 

1482 be column labels or column indices. The function is applied to the raw 

1483 text read from the file, so an empty field is passed as an empty string 

1484 ``''``; ``na_values`` and ``keep_default_na`` are then applied to the 

1485 value the function returns. 

1486 true_values : list, optional 

1487 Values to consider as ``True`` in addition 

1488 to case-insensitive variants of 'True'. 

1489 false_values : list, optional 

1490 Values to consider as ``False`` in addition to case-insensitive 

1491 variants of 'False'. 

1492 skipinitialspace : bool, default False 

1493 Skip spaces after delimiter. 

1494 skiprows : int, list of int or Callable, optional 

1495 Line numbers to skip (0-indexed) or number of lines to skip (``int``) 

1496 at the start of the file. 

1497 

1498 If callable, the callable function will be evaluated against the row 

1499 indices, returning ``True`` if the row should be skipped and ``False`` 

1500 otherwise. 

1501 An example of a valid callable argument would be ``lambda x: x in [0, 2]``. 

1502 skipfooter : int, default 0 

1503 Number of lines at bottom of file to skip (Unsupported with ``engine='c'``). 

1504 nrows : int, optional 

1505 Number of rows of file to read. Useful for reading pieces of large files. 

1506 Refers to the number of data rows in the returned DataFrame, excluding: 

1507 

1508 * The header row containing column names. 

1509 * Rows before the header row, if ``header=1`` or larger. 

1510 

1511 Example usage: 

1512 

1513 * To read the first 999,999 (non-header) rows: 

1514 ``read_csv(..., nrows=999999)`` 

1515 

1516 * To read rows 1,000,000 through 1,999,999: 

1517 ``read_csv(..., skiprows=1000000, nrows=999999)`` 

1518 

1519 na_values : Hashable, Iterable of Hashable or dict of {Hashable : Iterable}, 

1520 optional 

1521 Additional strings to recognize as ``NA``/``NaN``. If ``dict`` 

1522 passed, specific 

1523 per-column ``NA`` values. By default the following values 

1524 are interpreted as 

1525 ``NaN``: empty string, "NaN", "N/A", "NULL", and other common 

1526 representations of missing data. 

1527 keep_default_na : bool, default True 

1528 Whether or not to include the default ``NaN`` values when parsing the data. 

1529 Depending on whether ``na_values`` is passed in, the behavior is as follows: 

1530 

1531 * If ``keep_default_na`` is ``True``, and ``na_values`` 

1532 are specified, ``na_values`` 

1533 is appended to the default ``NaN`` values used for parsing. 

1534 * If ``keep_default_na`` is ``True``, and ``na_values`` are not specified, only 

1535 the default ``NaN`` values are used for parsing. 

1536 * If ``keep_default_na`` is ``False``, and ``na_values`` are specified, only 

1537 the ``NaN`` values specified ``na_values`` are used for parsing. 

1538 * If ``keep_default_na`` is ``False``, and ``na_values`` are not specified, no 

1539 strings will be parsed as ``NaN``. 

1540 

1541 Note that if ``na_filter`` is passed in as ``False``, 

1542 the ``keep_default_na`` and 

1543 ``na_values`` parameters will be ignored. 

1544 na_filter : bool, default True 

1545 Detect missing value markers (empty strings and the value of ``na_values``). In 

1546 data without any ``NA`` values, passing ``na_filter=False`` can improve the 

1547 performance of reading a large file. 

1548 skip_blank_lines : bool, default True 

1549 If ``True``, skip over blank lines rather than interpreting as ``NaN`` values. 

1550 parse_dates : bool, None, list of Hashable, default None 

1551 The behavior is as follows: 

1552 

1553 * ``bool``. If ``True`` -> try parsing the index. 

1554 * ``None``. Behaves like ``True`` if ``date_format`` is specified. 

1555 * ``list`` of ``int`` or names. 

1556 e.g. If ``[1, 2, 3]`` -> try parsing columns 1, 2, 3 

1557 each as a separate date column. 

1558 

1559 If a column or index cannot be represented as an array of ``datetime``, 

1560 say because of an unparsable value or a mixture of timezones, the column 

1561 or index will be returned unaltered as an ``object`` data type. For 

1562 non-standard ``datetime`` parsing, use :func:`~pandas.to_datetime` after 

1563 :func:`~pandas.read_csv`. 

1564 

1565 Note: A fast-path exists for iso8601-formatted dates. 

1566 date_format : str or dict of column -> format, optional 

1567 Format to use for parsing dates and/or times when 

1568 used in conjunction with ``parse_dates``. 

1569 The strftime to parse time, e.g. :const:`"%d/%m/%Y"`. See 

1570 `strftime documentation 

1571 <https://docs.python.org/3/library/datetime.html 

1572 #strftime-and-strptime-behavior>`_ for more information on choices, though 

1573 note that :const:`"%f"`` will parse all the way up to nanoseconds. 

1574 You can also pass: 

1575 

1576 - "ISO8601", to parse any `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ 

1577 time string (not necessarily in exactly the same format); 

1578 - "mixed", to infer the format for each element individually. This is risky, 

1579 and you should probably use it along with `dayfirst`. 

1580 

1581 .. versionadded:: 2.0.0 

1582 dayfirst : bool, default False 

1583 DD/MM format dates, international and European format. 

1584 cache_dates : bool, default True 

1585 If ``True``, use a cache of unique, converted dates to apply the ``datetime`` 

1586 conversion. May produce significant speed-up when parsing duplicate 

1587 date strings, especially ones with timezone offsets. 

1588 

1589 iterator : bool, default False 

1590 Return ``TextFileReader`` object for iteration or getting chunks with 

1591 ``get_chunk()``. 

1592 chunksize : int, optional 

1593 Number of lines to read from the file per chunk. Passing a value will cause the 

1594 function to return a ``TextFileReader`` object for iteration. 

1595 See the `IO Tools docs 

1596 <https://pandas.pydata.org/docs/user_guide/io.html#io-chunking>`_ 

1597 for more information on ``iterator`` and ``chunksize``. 

1598 

1599 compression : str or dict, default 'infer' 

1600 For on-the-fly decompression of on-disk data. 

1601 If 'infer' and 'filepath_or_buffer' is 

1602 path-like, then detect compression from the following extensions: '.gz', 

1603 '.bz2', '.zip', '.xz', '.zst', '.tar', '.tar.gz', '.tar.xz' or '.tar.bz2' 

1604 (otherwise no compression). 

1605 If using 'zip' or 'tar', the ZIP file must contain only 

1606 one data file to be read in. 

1607 Set to ``None`` for no decompression. 

1608 Can also be a dict with key ``'method'`` set 

1609 to one of {``'zip'``, ``'gzip'``, ``'bz2'``, 

1610 ``'zstd'``, ``'xz'``, ``'tar'``} and 

1611 other key-value pairs are forwarded to 

1612 ``zipfile.ZipFile``, ``gzip.GzipFile``, 

1613 ``bz2.BZ2File``, ``zstandard.ZstdDecompressor``, ``lzma.LZMAFile`` or 

1614 ``tarfile.TarFile``, respectively. 

1615 As an example, the following could be passed for 

1616 Zstandard decompression using a 

1617 custom compression dictionary: 

1618 ``compression={'method': 'zstd', 'dict_data': my_compression_dict}``. 

1619 

1620 thousands : str (length 1), optional 

1621 Character acting as the thousands separator in numerical values. 

1622 decimal : str (length 1), default '.' 

1623 Character to recognize as decimal point (e.g., use ',' for European data). 

1624 lineterminator : str (length 1), optional 

1625 Character used to denote a line break. Only valid with C parser. 

1626 quotechar : str (length 1), optional 

1627 Character used to denote the start and end of a quoted item. Quoted 

1628 items can include the ``delimiter`` and it will be ignored. 

1629 quoting : {0, 1, 2, 3}, default csv.QUOTE_MINIMAL 

1630 Control field quoting behavior per ``csv.QUOTE_*`` constants. Use one of 

1631 ``0`` or ``csv.QUOTE_MINIMAL``, ``1`` or ``csv.QUOTE_ALL``, 

1632 ``2`` or ``csv.QUOTE_NONNUMERIC``, or ``3`` or ``csv.QUOTE_NONE``. 

1633 Default is ``csv.QUOTE_MINIMAL`` (i.e., 0) which implies that 

1634 only fields containing special 

1635 characters are quoted (e.g., characters defined 

1636 in ``quotechar``, ``delimiter``, 

1637 or ``lineterminator``. 

1638 doublequote : bool, default True 

1639 When ``quotechar`` is specified and ``quoting`` is not ``QUOTE_NONE``, indicate 

1640 whether or not to interpret two consecutive ``quotechar`` elements INSIDE a 

1641 field as a single ``quotechar`` element. 

1642 escapechar : str (length 1), optional 

1643 Character used to escape other characters. 

1644 comment : str (length 1), optional 

1645 Character indicating that the remainder of line should not be parsed. 

1646 If found at the beginning 

1647 of a line, the line will be ignored altogether. This parameter must be a 

1648 single character. Like empty lines (as long as ``skip_blank_lines=True``), 

1649 fully commented lines are ignored by the parameter ``header`` but not by 

1650 ``skiprows``. For example, if ``comment='#'``, parsing 

1651 ``#empty\\na,b,c\\n1,2,3`` with ``header=0`` will result in ``'a,b,c'`` being 

1652 treated as the header. 

1653 encoding : str, optional, default 'utf-8' 

1654 Encoding to use for UTF when reading/writing (ex. ``'utf-8'``). `List of Python 

1655 standard encodings 

1656 <https://docs.python.org/3/library/codecs.html#standard-encodings>`_ . 

1657 

1658 encoding_errors : str, optional, default 'strict' 

1659 How encoding errors are treated. `List of possible values 

1660 <https://docs.python.org/3/library/codecs.html#error-handlers>`_ . 

1661 

1662 dialect : str or csv.Dialect, optional 

1663 If provided, this parameter will override values (default or not) for the 

1664 following parameters: ``delimiter``, ``doublequote``, ``escapechar``, 

1665 ``skipinitialspace``, ``quotechar``, and ``quoting``. If it is necessary to 

1666 override values, a ``ParserWarning`` will be issued. See ``csv.Dialect`` 

1667 documentation for more details. 

1668 on_bad_lines : {'error', 'warn', 'skip'} or Callable, default 'error' 

1669 Specifies what to do upon encountering a bad line (a line with too many fields). 

1670 Allowed values are: 

1671 

1672 - ``'error'``, raise an Exception when a bad line is encountered. 

1673 - ``'warn'``, raise a warning when a bad line is 

1674 encountered and skip that line. 

1675 - ``'skip'``, skip bad lines without raising or warning when 

1676 they are encountered. 

1677 - Callable, function that will process a single bad line. 

1678 - With ``engine='python'``, function with signature 

1679 ``(bad_line: list[str]) -> list[str] | None``. 

1680 ``bad_line`` is a list of strings split by the ``sep``. 

1681 If the function returns ``None``, the bad line will be ignored. 

1682 If the function returns a new ``list`` of strings with 

1683 more elements than 

1684 expected, a ``ParserWarning`` will be emitted while 

1685 dropping extra elements. 

1686 - With ``engine='pyarrow'``, function with signature 

1687 as described in pyarrow documentation: `invalid_row_handler 

1688 <https://arrow.apache.org/docs/python 

1689 /generated/pyarrow.csv.ParseOptions.html 

1690 #pyarrow.csv.ParseOptions.invalid_row_handler>`_. 

1691 

1692 .. versionchanged:: 2.2.0 

1693 

1694 Callable for ``engine='pyarrow'`` 

1695 

1696 low_memory : bool, default True 

1697 Internally process the file in chunks, resulting in lower memory use 

1698 while parsing, but possibly mixed type inference. Because each chunk 

1699 is type-inferred independently, the same literal can be parsed as an 

1700 ``int`` in one chunk and a ``str`` in another; after concatenation the 

1701 column has ``object`` dtype and contains both representations, so 

1702 comparisons such as ``df["col"] == 12345`` or 

1703 ``df["col"] == "12345"`` will match only a subset of the rows holding 

1704 that value. A :class:`~pandas.errors.DtypeWarning` is emitted when 

1705 this occurs. To ensure no mixed types either set ``False``, or specify 

1706 the type with the ``dtype`` parameter. 

1707 Note that the entire file is read into a single :class:`~pandas.DataFrame` 

1708 regardless, use the ``chunksize`` or ``iterator`` 

1709 parameter to return the data in 

1710 chunks. (Only valid with C parser). 

1711 memory_map : bool, default False 

1712 If a filepath is provided for ``filepath_or_buffer``, map the file object 

1713 directly onto memory and access the data directly from there. Using this 

1714 option can improve performance because there is no longer any I/O overhead. 

1715 float_precision : {'high', 'legacy', 'round_trip'}, optional 

1716 Specifies which converter the C engine should use for floating-point 

1717 values. The options are ``None`` or ``'high'`` for the ordinary converter, 

1718 ``'legacy'`` for the original lower precision pandas converter, and 

1719 ``'round_trip'`` for the round-trip converter. 

1720 

1721 .. deprecated:: 3.1.0 

1722 All float precision modes now use the same converter. 

1723 The ``float_precision`` argument will be removed in a future version. 

1724 

1725 storage_options : dict, optional 

1726 Extra options that make sense for a particular storage connection, e.g. 

1727 host, port, username, password, etc. For HTTP(S) URLs the key-value pairs 

1728 are forwarded to ``urllib.request.Request`` as header options. For other 

1729 URLs (e.g. starting with "s3://", and "gcs://") the key-value pairs are 

1730 forwarded to ``fsspec.open``. Please see ``fsspec`` and ``urllib`` for more 

1731 details, and for more examples on storage options refer `here 

1732 <https://pandas.pydata.org/docs/user_guide/io.html? 

1733 highlight=storage_options#reading-writing-remote-files>`_. 

1734 

1735 dtype_backend : {'numpy_nullable', 'pyarrow'} 

1736 Back-end data type applied to the resultant :class:`DataFrame` 

1737 (still experimental). If not specified, the default behavior 

1738 is to not use nullable data types. If specified, the behavior 

1739 is as follows: 

1740 

1741 * ``"numpy_nullable"``: returns nullable-dtype-backed :class:`DataFrame` 

1742 * ``"pyarrow"``: returns 

1743 pyarrow-backed nullable :class:`ArrowDtype` :class:`DataFrame` 

1744 

1745 .. versionadded:: 2.0 

1746 

1747 Returns 

1748 ------- 

1749 DataFrame or TextFileReader 

1750 A comma-separated values (csv) file is returned as two-dimensional 

1751 data structure with labeled axes. 

1752 

1753 See Also 

1754 -------- 

1755 DataFrame.to_csv : Write DataFrame to a comma-separated values (csv) file. 

1756 read_table : Read general delimited file into DataFrame. 

1757 read_fwf : Read a table of fixed-width formatted lines into DataFrame. 

1758 

1759 Notes 

1760 ----- 

1761 Column labels read from a header row are always strings. To use column 

1762 labels of another type, pass them explicitly with ``names`` and set 

1763 ``header=0`` when the file contains a header row. 

1764 

1765 Sufficiently large local uncompressed files read with the C engine may be 

1766 parsed by multiple threads in parallel. Use the ``mode.max_threads`` 

1767 option to cap or disable this; see :ref:`io.csv.parallel` for details. 

1768 

1769 Examples 

1770 -------- 

1771 >>> pd.read_csv("data.csv") # doctest: +SKIP 

1772 Name Value 

1773 0 foo 1 

1774 1 bar 2 

1775 2 #baz 3 

1776 

1777 Index and header can be specified via the `index_col` and `header` arguments. 

1778 

1779 >>> pd.read_csv("data.csv", header=None) # doctest: +SKIP 

1780 0 1 

1781 0 Name Value 

1782 1 foo 1 

1783 2 bar 2 

1784 3 #baz 3 

1785 

1786 >>> pd.read_csv("data.csv", index_col="Value") # doctest: +SKIP 

1787 Name 

1788 Value 

1789 1 foo 

1790 2 bar 

1791 3 #baz 

1792 

1793 Column types are inferred but can be explicitly specified using the dtype argument. 

1794 

1795 >>> pd.read_csv("data.csv", dtype={"Value": float}) # doctest: +SKIP 

1796 Name Value 

1797 0 foo 1.0 

1798 1 bar 2.0 

1799 2 #baz 3.0 

1800 

1801 True, False, and NA values, and thousands separators have defaults, 

1802 but can be explicitly specified, too. Supply the values you would like 

1803 as strings or lists of strings! 

1804 

1805 >>> pd.read_csv("data.csv", na_values=["foo", "bar"]) # doctest: +SKIP 

1806 Name Value 

1807 0 NaN 1 

1808 1 NaN 2 

1809 2 #baz 3 

1810 

1811 Comment lines in the input file can be skipped using the `comment` argument. 

1812 

1813 >>> pd.read_csv("data.csv", comment="#") # doctest: +SKIP 

1814 Name Value 

1815 0 foo 1 

1816 1 bar 2 

1817 

1818 By default, columns with dates will be read as ``object`` rather than ``datetime``. 

1819 

1820 >>> df = pd.read_csv("tmp.csv") # doctest: +SKIP 

1821 

1822 >>> df # doctest: +SKIP 

1823 col 1 col 2 col 3 

1824 0 10 10/04/2018 Sun 15 Jan 2023 

1825 1 20 15/04/2018 Fri 12 May 2023 

1826 

1827 >>> df.dtypes # doctest: +SKIP 

1828 col 1 int64 

1829 col 2 object 

1830 col 3 object 

1831 dtype: object 

1832 

1833 Specific columns can be parsed as dates by using the `parse_dates` and 

1834 `date_format` arguments. 

1835 

1836 >>> df = pd.read_csv( 

1837 ... "tmp.csv", 

1838 ... parse_dates=[1, 2], 

1839 ... date_format={"col 2": "%d/%m/%Y", "col 3": "%a %d %b %Y"}, 

1840 ... ) # doctest: +SKIP 

1841 

1842 >>> df.dtypes # doctest: +SKIP 

1843 col 1 int64 

1844 col 2 datetime64[ns] 

1845 col 3 datetime64[ns] 

1846 dtype: object 

1847 """ 

1848 # locals() should never be modified 

1849 kwds = locals().copy() 

1850 del kwds["filepath_or_buffer"] 

1851 del kwds["sep"] 

1852 

1853 kwds_defaults = _refine_defaults_read( 

1854 dialect, 

1855 delimiter, 

1856 engine, 

1857 sep, 

1858 on_bad_lines, 

1859 names, 

1860 defaults={"delimiter": ","}, 

1861 dtype_backend=dtype_backend, 

1862 lineterminator=lineterminator, 

1863 ) 

1864 kwds.update(kwds_defaults) 

1865 

1866 return _read(filepath_or_buffer, kwds) 

1867 

1868 

1869@overload 

1870def read_table( 

1871 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1872 *, 

1873 iterator: Literal[True], 

1874 chunksize: int | None = ..., 

1875 **kwds: Unpack[_read_shared[HashableT]], 

1876) -> TextFileReader: ... 

1877 

1878 

1879@overload 

1880def read_table( 

1881 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1882 *, 

1883 iterator: bool = ..., 

1884 chunksize: int, 

1885 **kwds: Unpack[_read_shared[HashableT]], 

1886) -> TextFileReader: ... 

1887 

1888 

1889@overload 

1890def read_table( 

1891 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1892 *, 

1893 iterator: Literal[False] = ..., 

1894 chunksize: None = ..., 

1895 **kwds: Unpack[_read_shared[HashableT]], 

1896) -> DataFrame: ... 

1897 

1898 

1899@overload 

1900def read_table( 

1901 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1902 *, 

1903 iterator: bool = ..., 

1904 chunksize: int | None = ..., 

1905 **kwds: Unpack[_read_shared[HashableT]], 

1906) -> DataFrame | TextFileReader: ... 

1907 

1908 

1909@set_module("pandas") 

1910def read_table( 

1911 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

1912 *, 

1913 sep: str | lib.NoDefault | None = lib.no_default, 

1914 delimiter: str | lib.NoDefault | None = lib.no_default, 

1915 # Column and Index Locations and Names 

1916 header: int | Sequence[int] | Literal["infer"] | None = "infer", 

1917 names: Sequence[Hashable] | lib.NoDefault | None = lib.no_default, 

1918 index_col: IndexLabel | Literal[False] | None = None, 

1919 usecols: UsecolsArgType = None, 

1920 # General Parsing Configuration 

1921 dtype: DtypeArg | None = None, 

1922 engine: CSVEngine | None = None, 

1923 converters: Mapping[HashableT, Callable] | None = None, 

1924 true_values: list | None = None, 

1925 false_values: list | None = None, 

1926 skipinitialspace: bool = False, 

1927 skiprows: list[int] | int | Callable[[Hashable], bool] | None = None, 

1928 skipfooter: int = 0, 

1929 nrows: int | None = None, 

1930 # NA and Missing Data Handling 

1931 na_values: ( 

1932 Hashable | Iterable[Hashable] | Mapping[Hashable, Iterable[Hashable]] | None 

1933 ) = None, 

1934 keep_default_na: bool = True, 

1935 na_filter: bool = True, 

1936 skip_blank_lines: bool = True, 

1937 # Datetime Handling 

1938 parse_dates: bool | Sequence[Hashable] | None = None, 

1939 date_format: str | dict[Hashable, str] | None = None, 

1940 dayfirst: bool = False, 

1941 cache_dates: bool = True, 

1942 # Iteration 

1943 iterator: bool = False, 

1944 chunksize: int | None = None, 

1945 # Quoting, Compression, and File Format 

1946 compression: CompressionOptions = "infer", 

1947 thousands: str | None = None, 

1948 decimal: str = ".", 

1949 lineterminator: str | None = None, 

1950 quotechar: str = '"', 

1951 quoting: int = csv.QUOTE_MINIMAL, 

1952 doublequote: bool = True, 

1953 escapechar: str | None = None, 

1954 comment: str | None = None, 

1955 encoding: str | None = None, 

1956 encoding_errors: str | None = "strict", 

1957 dialect: str | csv.Dialect | type[csv.Dialect] | None = None, 

1958 # Error Handling 

1959 on_bad_lines: str = "error", 

1960 # Internal 

1961 low_memory: bool = _c_parser_defaults["low_memory"], 

1962 memory_map: bool = False, 

1963 float_precision: Literal["high", "legacy", "round_trip"] | None = None, 

1964 storage_options: StorageOptions | None = None, 

1965 dtype_backend: DtypeBackend | lib.NoDefault = lib.no_default, 

1966) -> DataFrame | TextFileReader: 

1967 """ 

1968 Read general delimited file into DataFrame. 

1969 

1970 Also supports optionally iterating or breaking of the file 

1971 into chunks. 

1972 

1973 Additional help can be found in the online docs for 

1974 `IO Tools <https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html>`_. 

1975 

1976 Parameters 

1977 ---------- 

1978 filepath_or_buffer : str, path object or file-like object 

1979 Any valid string path is acceptable. The string could be a URL. Valid 

1980 URL schemes include http, ftp, s3, gs, and file. For file URLs, a host is 

1981 expected. A local file could be: file://localhost/path/to/table.csv. 

1982 

1983 Certain URL schemes may require additional packages. For example, S3 

1984 URLs require the ``s3fs`` library. See 

1985 :ref:`install.optional_dependencies` for a full list. 

1986 

1987 If you want to pass in a path object, pandas accepts any ``os.PathLike``. 

1988 

1989 By file-like object, we refer to objects with a ``read()`` method that 

1990 accepts an optional size argument, such as a file handle (e.g. via 

1991 builtin ``open`` function) or ``StringIO``. 

1992 sep : str, default '\\t' (tab-stop) 

1993 Character or regex pattern to treat as the delimiter. ``sep=None`` detects 

1994 the separator from the first valid row of the file with Python's builtin 

1995 sniffer tool, ``csv.Sniffer``; it is supported only by the Python parsing 

1996 engine, which will be used automatically. 

1997 In addition, separators longer than 1 character and different from 

1998 ``'\\s+'`` will be interpreted as regular expressions and will force 

1999 the use of the Python parsing engine. Note that regex delimiters are prone 

2000 to ignoring quoted data. Regex example: ``'\\r\\t'``. 

2001 delimiter : str, optional 

2002 Alias for ``sep``. 

2003 header : int, Sequence of int, 'infer' or None, default 'infer' 

2004 Row index or indices in the file to use as DataFrame column labels. 

2005 Indexing starts at 0 and counts only non-blank, non-commented lines 

2006 when ``skip_blank_lines=True``, so ``header=0`` denotes the first 

2007 line of data rather than the first line of the file. Valid arguments 

2008 are: 

2009 

2010 * ``int``: line index at which column labels are read. 

2011 * Sequence of ``int``: line indices at which column labels are read 

2012 and combined into a :class:`~pandas.MultiIndex` on the columns, 

2013 e.g. ``[0, 1, 3]``. Intervening rows that are not specified will 

2014 be skipped (e.g. row 2 in this example). 

2015 * ``None``: no row in the file is interpreted as column labels. 

2016 Columns are labelled by integer position, or by the values passed 

2017 to ``names`` when provided. Use this for files with no header 

2018 row. If the file has a header row that should be overridden by 

2019 ``names``, pass ``header=0`` instead. 

2020 * ``'infer'`` (default): equivalent to ``header=0`` when no 

2021 ``names`` are passed, otherwise equivalent to ``header=None``. 

2022 

2023 When inferred from the file contents, headers are kept distinct from 

2024 each other by renaming duplicate names with a numeric suffix of the form 

2025 ``".{count}"`` starting from 1, e.g. ``"foo"`` and ``"foo.1"``. 

2026 Empty headers are named 

2027 ``"Unnamed: {i}"`` or ``"Unnamed: {i}_level_{level}"`` 

2028 in the case of MultiIndex columns. 

2029 names : Sequence of Hashable, optional 

2030 Sequence of column labels to apply. If the file contains a header row, 

2031 then you should explicitly pass ``header=0`` to override the column names. 

2032 Duplicates in this list are not allowed. 

2033 index_col : Hashable, Sequence of Hashable or False, optional 

2034 Column(s) to use as row label(s), denoted either by column labels or column 

2035 indices. If a sequence of labels or indices is given, 

2036 :class:`~pandas.MultiIndex` 

2037 will be formed for the row labels. 

2038 

2039 Note: ``index_col=False`` can be used to force pandas to *not* use the first 

2040 column as the index, e.g., when you have a malformed file with delimiters at 

2041 the end of each line. 

2042 usecols : Sequence of Hashable or Callable, optional 

2043 Subset of columns to select, denoted either by column labels or column indices. 

2044 If list-like, all elements must either 

2045 be positional (i.e. integer indices into the document columns) or strings 

2046 that correspond to column names provided either by the user in ``names`` or 

2047 inferred from the document header row(s). If ``names`` are given, the document 

2048 header row(s) are not taken into account. For example, a valid list-like 

2049 ``usecols`` parameter would be ``[0, 1, 2]`` or ``['foo', 'bar', 'baz']``. 

2050 Element order is ignored, so ``usecols=[0, 1]`` is the same as ``[1, 0]``. 

2051 To instantiate a :class:`~pandas.DataFrame` from ``data`` with element order 

2052 preserved use ``pd.read_csv(data, usecols=['foo', 'bar'])[['foo', 'bar']]`` 

2053 for columns in ``['foo', 'bar']`` order or 

2054 ``pd.read_csv(data, usecols=['foo', 'bar'])[['bar', 'foo']]`` 

2055 for ``['bar', 'foo']`` order. 

2056 

2057 If callable, the callable function will be evaluated against the column 

2058 names, returning names where the callable function evaluates to ``True``. An 

2059 example of a valid callable argument would be ``lambda x: x.upper() in 

2060 ['AAA', 'BBB', 'DDD']``. Using this parameter results in much faster 

2061 parsing time and lower memory usage. 

2062 dtype : dtype or dict of {Hashable : dtype}, optional 

2063 Data type(s) to apply to either the whole dataset or individual columns. 

2064 E.g., ``{'a': np.float64, 'b': np.int32, 'c': 'Int64'}`` 

2065 Use ``str`` or ``object`` together with suitable ``na_values`` settings 

2066 to preserve and not interpret ``dtype``. 

2067 Specifying a ``dtype`` does not change how missing values are parsed; 

2068 values recognized as missing (see ``na_values`` and ``keep_default_na``) 

2069 are still read as ``NaN`` (or the dtype's NA value) rather than cast to 

2070 the requested ``dtype``. 

2071 If ``converters`` are specified, they will be applied INSTEAD 

2072 of ``dtype`` conversion. Specify a ``defaultdict`` as input where 

2073 the default determines the ``dtype`` of the columns which 

2074 are not explicitly listed. 

2075 engine : {'c', 'python', 'pyarrow'}, optional 

2076 Parser engine to use. The C and pyarrow engines are faster, 

2077 while the python engine 

2078 is currently more feature-complete. The pyarrow engine is 

2079 multithreaded, and the C engine reads sufficiently large files in 

2080 parallel. The 'pyarrow' engine is an *experimental* engine, 

2081 and some features are unsupported, or may not work correctly, with this engine. 

2082 converters : dict of {Hashable : Callable}, optional 

2083 Functions for converting values in specified columns. Keys can either 

2084 be column labels or column indices. The function is applied to the raw 

2085 text read from the file, so an empty field is passed as an empty string 

2086 ``''``; ``na_values`` and ``keep_default_na`` are then applied to the 

2087 value the function returns. 

2088 true_values : list, optional 

2089 Values to consider as ``True`` in addition to 

2090 case-insensitive variants of 'True'. 

2091 false_values : list, optional 

2092 Values to consider as ``False`` in addition 

2093 to case-insensitive variants of 'False'. 

2094 skipinitialspace : bool, default False 

2095 Skip spaces after delimiter. 

2096 skiprows : int, list of int or Callable, optional 

2097 Line numbers to skip (0-indexed) or number of lines to skip (``int``) 

2098 at the start of the file. 

2099 

2100 If callable, the callable function will be evaluated against the row 

2101 indices, returning ``True`` if the row 

2102 should be skipped and ``False`` otherwise. 

2103 An example of a valid callable argument would be ``lambda x: x in [0, 2]``. 

2104 skipfooter : int, default 0 

2105 Number of lines at bottom of file to skip (Unsupported with ``engine='c'``). 

2106 nrows : int, optional 

2107 Number of rows of file to read. Useful for reading pieces of large files. 

2108 Refers to the number of data rows in the returned DataFrame, excluding: 

2109 

2110 * The header row containing column names. 

2111 * Rows before the header row, if ``header=1`` or larger. 

2112 

2113 Example usage: 

2114 

2115 * To read the first 999,999 (non-header) rows: 

2116 ``read_csv(..., nrows=999999)`` 

2117 

2118 * To read rows 1,000,000 through 1,999,999: 

2119 ``read_csv(..., skiprows=1000000, nrows=999999)`` 

2120 

2121 na_values : Hashable, Iterable of Hashable or dict of {Hashable : Iterable}, 

2122 optional 

2123 Additional strings to recognize as ``NA``/``NaN``. 

2124 If ``dict`` passed, specific 

2125 per-column ``NA`` values. By default the following values are interpreted as 

2126 ``NaN``: empty string, "NaN", "N/A", "NULL", and other 

2127 common representations of missing data. 

2128 keep_default_na : bool, default True 

2129 Whether or not to include the default ``NaN`` values when parsing the data. 

2130 Depending on whether ``na_values`` is passed in, the behavior is as follows: 

2131 

2132 * If ``keep_default_na`` is ``True``, 

2133 and ``na_values`` are specified, ``na_values`` 

2134 is appended to the default ``NaN`` values used for parsing. 

2135 * If ``keep_default_na`` is ``True``, and ``na_values`` are not specified, only 

2136 the default ``NaN`` values are used for parsing. 

2137 * If ``keep_default_na`` is ``False``, and ``na_values`` are specified, only 

2138 the ``NaN`` values specified ``na_values`` are used for parsing. 

2139 * If ``keep_default_na`` is ``False``, and ``na_values`` are not specified, no 

2140 strings will be parsed as ``NaN``. 

2141 

2142 Note that if ``na_filter`` is passed in as 

2143 ``False``, the ``keep_default_na`` and 

2144 ``na_values`` parameters will be ignored. 

2145 na_filter : bool, default True 

2146 Detect missing value markers (empty strings and the value of ``na_values``). In 

2147 data without any ``NA`` values, passing ``na_filter=False`` can improve the 

2148 performance of reading a large file. 

2149 skip_blank_lines : bool, default True 

2150 If ``True``, skip over blank lines rather than interpreting as ``NaN`` values. 

2151 parse_dates : bool, None, list of Hashable, default None 

2152 The behavior is as follows: 

2153 

2154 * ``bool``. If ``True`` -> try parsing the index. 

2155 * ``None``. Behaves like ``True`` if ``date_format`` is specified. 

2156 * ``list`` of ``int`` or names. 

2157 e.g. If ``[1, 2, 3]`` -> try parsing columns 1, 2, 3 

2158 each as a separate date column. 

2159 

2160 If a column or index cannot be represented as an array of ``datetime``, 

2161 say because of an unparsable value or a mixture of timezones, the column 

2162 or index will be returned unaltered as an ``object`` data type. For 

2163 non-standard ``datetime`` parsing, use :func:`~pandas.to_datetime` after 

2164 :func:`~pandas.read_csv`. 

2165 

2166 Note: A fast-path exists for iso8601-formatted dates. 

2167 date_format : str or dict of column -> format, optional 

2168 Format to use for parsing dates and/or times when used 

2169 in conjunction with ``parse_dates``. 

2170 The strftime to parse time, e.g. :const:`"%d/%m/%Y"`. See 

2171 `strftime documentation 

2172 <https://docs.python.org/3/library/datetime.html 

2173 #strftime-and-strptime-behavior>`_ for more information on choices, though 

2174 note that :const:`"%f"`` will parse all the way up to nanoseconds. 

2175 You can also pass: 

2176 

2177 - "ISO8601", to parse any `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ 

2178 time string (not necessarily in exactly the same format); 

2179 - "mixed", to infer the format for each element individually. This is risky, 

2180 and you should probably use it along with `dayfirst`. 

2181 

2182 .. versionadded:: 2.0.0 

2183 dayfirst : bool, default False 

2184 DD/MM format dates, international and European format. 

2185 cache_dates : bool, default True 

2186 If ``True``, use a cache of unique, converted dates to apply the ``datetime`` 

2187 conversion. May produce significant speed-up when parsing duplicate 

2188 date strings, especially ones with timezone offsets. 

2189 

2190 iterator : bool, default False 

2191 Return ``TextFileReader`` object for iteration or getting chunks with 

2192 ``get_chunk()``. 

2193 chunksize : int, optional 

2194 Number of lines to read from the file per chunk. Passing a value will cause the 

2195 function to return a ``TextFileReader`` object for iteration. 

2196 See the `IO Tools docs 

2197 <https://pandas.pydata.org/docs/dev/user_guide/io.html#io-chunking>`_ 

2198 for more information on ``iterator`` and ``chunksize``. 

2199 

2200 compression : str or dict, default 'infer' 

2201 For on-the-fly decompression of on-disk data. If 'infer' 

2202 and 'filepath_or_buffer' is 

2203 path-like, then detect compression from the following extensions: '.gz', 

2204 '.bz2', '.zip', '.xz', '.zst', '.tar', '.tar.gz', '.tar.xz' or '.tar.bz2' 

2205 (otherwise no compression). 

2206 If using 'zip' or 'tar', the ZIP file must contain 

2207 only one data file to be read in. 

2208 Set to ``None`` for no decompression. 

2209 Can also be a dict with key ``'method'`` set 

2210 to one of {``'zip'``, ``'gzip'``, ``'bz2'``, 

2211 ``'zstd'``, ``'xz'``, ``'tar'``} and 

2212 other key-value pairs are forwarded to 

2213 ``zipfile.ZipFile``, ``gzip.GzipFile``, 

2214 ``bz2.BZ2File``, ``zstandard.ZstdDecompressor``, ``lzma.LZMAFile`` or 

2215 ``tarfile.TarFile``, respectively. 

2216 As an example, the following could be passed for 

2217 Zstandard decompression using a 

2218 custom compression dictionary: 

2219 ``compression={'method': 'zstd', 'dict_data': my_compression_dict}``. 

2220 

2221 thousands : str (length 1), optional 

2222 Character acting as the thousands separator in numerical values. 

2223 decimal : str (length 1), default '.' 

2224 Character to recognize as decimal point (e.g., use ',' for European data). 

2225 lineterminator : str (length 1), optional 

2226 Character used to denote a line break. Only valid with C parser. 

2227 quotechar : str (length 1), optional 

2228 Character used to denote the start and end of a quoted item. Quoted 

2229 items can include the ``delimiter`` and it will be ignored. 

2230 quoting : {0, 1, 2, 3}, default csv.QUOTE_MINIMAL 

2231 Control field quoting behavior per ``csv.QUOTE_*`` constants. Use one of 

2232 ``0`` or ``csv.QUOTE_MINIMAL``, ``1`` or ``csv.QUOTE_ALL``, 

2233 ``2`` or ``csv.QUOTE_NONNUMERIC``, or ``3`` or ``csv.QUOTE_NONE``. 

2234 Default is ``csv.QUOTE_MINIMAL`` (i.e., 0) which 

2235 implies that only fields containing special 

2236 characters are quoted (e.g., characters defined 

2237 in ``quotechar``, ``delimiter``, 

2238 or ``lineterminator``. 

2239 doublequote : bool, default True 

2240 When ``quotechar`` is specified and ``quoting`` is not ``QUOTE_NONE``, indicate 

2241 whether or not to interpret two consecutive ``quotechar`` elements INSIDE a 

2242 field as a single ``quotechar`` element. 

2243 escapechar : str (length 1), optional 

2244 Character used to escape other characters. 

2245 comment : str (length 1), optional 

2246 Character indicating that the remainder of line should not be parsed. 

2247 If found at the beginning 

2248 of a line, the line will be ignored altogether. This parameter must be a 

2249 single character. Like empty lines (as long as ``skip_blank_lines=True``), 

2250 fully commented lines are ignored by the parameter ``header`` but not by 

2251 ``skiprows``. For example, if ``comment='#'``, parsing 

2252 ``#empty\\na,b,c\\n1,2,3`` with ``header=0`` will result in ``'a,b,c'`` being 

2253 treated as the header. 

2254 encoding : str, optional, default 'utf-8' 

2255 Encoding to use for UTF when reading/writing (ex. ``'utf-8'``). `List of Python 

2256 standard encodings 

2257 <https://docs.python.org/3/library/codecs.html#standard-encodings>`_ . 

2258 

2259 encoding_errors : str, optional, default 'strict' 

2260 How encoding errors are treated. `List of possible values 

2261 <https://docs.python.org/3/library/codecs.html#error-handlers>`_ . 

2262 

2263 dialect : str or csv.Dialect, optional 

2264 If provided, this parameter will override values (default or not) for the 

2265 following parameters: ``delimiter``, ``doublequote``, ``escapechar``, 

2266 ``skipinitialspace``, ``quotechar``, and ``quoting``. If it is necessary to 

2267 override values, a ``ParserWarning`` will be issued. See ``csv.Dialect`` 

2268 documentation for more details. 

2269 on_bad_lines : {'error', 'warn', 'skip'} or Callable, default 'error' 

2270 Specifies what to do upon encountering a bad 

2271 line (a line with too many fields). 

2272 Allowed values are: 

2273 

2274 - ``'error'``, raise an Exception when a bad line is encountered. 

2275 - ``'warn'``, raise a warning when a bad line is encountered and 

2276 skip that line. 

2277 - ``'skip'``, skip bad lines without raising or warning when they 

2278 are encountered. 

2279 - Callable, function that will process a single bad line. 

2280 - With ``engine='python'``, function with signature 

2281 ``(bad_line: list[str]) -> list[str] | None``. 

2282 ``bad_line`` is a list of strings split by the ``sep``. 

2283 If the function returns ``None``, the bad line will be ignored. 

2284 If the function returns a new ``list`` of strings with more elements than 

2285 expected, a ``ParserWarning`` will be emitted while 

2286 dropping extra elements. 

2287 - With ``engine='pyarrow'``, function with signature 

2288 as described in pyarrow documentation: `invalid_row_handler 

2289 <https://arrow.apache.org/docs/ 

2290 python/generated/pyarrow.csv.ParseOptions.html 

2291 #pyarrow.csv.ParseOptions.invalid_row_handler>`_. 

2292 

2293 .. versionadded:: 2.2.0 

2294 

2295 Callable for ``engine='pyarrow'`` 

2296 

2297 low_memory : bool, default True 

2298 Internally process the file in chunks, resulting in lower memory use 

2299 while parsing, but possibly mixed type inference. Because each chunk 

2300 is type-inferred independently, the same literal can be parsed as an 

2301 ``int`` in one chunk and a ``str`` in another; after concatenation the 

2302 column has ``object`` dtype and contains both representations, so 

2303 comparisons such as ``df["col"] == 12345`` or 

2304 ``df["col"] == "12345"`` will match only a subset of the rows holding 

2305 that value. A :class:`~pandas.errors.DtypeWarning` is emitted when 

2306 this occurs. To ensure no mixed types either set ``False``, or specify 

2307 the type with the ``dtype`` parameter. 

2308 Note that the entire file is read into a single :class:`~pandas.DataFrame` 

2309 regardless, use the ``chunksize`` or ``iterator`` parameter 

2310 to return the data in 

2311 chunks. (Only valid with C parser). 

2312 memory_map : bool, default False 

2313 If a filepath is provided for ``filepath_or_buffer``, map the file object 

2314 directly onto memory and access the data directly from there. Using this 

2315 option can improve performance because there is no longer any I/O overhead. 

2316 float_precision : {'high', 'legacy', 'round_trip'}, optional 

2317 Specifies which converter the C engine should use for floating-point 

2318 values. The options are ``None`` or ``'high'`` for the ordinary converter, 

2319 ``'legacy'`` for the original lower precision pandas converter, and 

2320 ``'round_trip'`` for the round-trip converter. 

2321 

2322 .. deprecated:: 3.1.0 

2323 All float precision modes now use the same converter. 

2324 The ``float_precision`` argument will be removed in a future version. 

2325 

2326 storage_options : dict, optional 

2327 Extra options that make sense for a particular storage connection, e.g. 

2328 host, port, username, password, etc. For HTTP(S) URLs the key-value pairs 

2329 are forwarded to ``urllib.request.Request`` as header options. For other 

2330 URLs (e.g. starting with "s3://", and "gcs://") the key-value pairs are 

2331 forwarded to ``fsspec.open``. Please see ``fsspec`` and ``urllib`` for more 

2332 details, and for more examples on storage options refer `here 

2333 <https://pandas.pydata.org/docs/user_guide/io.html? 

2334 highlight=storage_options#reading-writing-remote-files>`_. 

2335 

2336 dtype_backend : {'numpy_nullable', 'pyarrow'} 

2337 Back-end data type applied to the resultant :class:`DataFrame` 

2338 (still experimental). If not specified, the default behavior 

2339 is to not use nullable data types. If specified, the behavior 

2340 is as follows: 

2341 

2342 * ``"numpy_nullable"``: returns nullable-dtype-backed :class:`DataFrame` 

2343 * ``"pyarrow"``: returns pyarrow-backed nullable 

2344 :class:`ArrowDtype` :class:`DataFrame` 

2345 

2346 .. versionadded:: 2.0 

2347 

2348 Returns 

2349 ------- 

2350 DataFrame or TextFileReader 

2351 A comma-separated values (csv) file is returned as two-dimensional 

2352 data structure with labeled axes. 

2353 

2354 See Also 

2355 -------- 

2356 DataFrame.to_csv : Write DataFrame to a comma-separated values (csv) file. 

2357 read_csv : Read a comma-separated values (csv) file into DataFrame. 

2358 read_fwf : Read a table of fixed-width formatted lines into DataFrame. 

2359 

2360 Notes 

2361 ----- 

2362 Column labels read from a header row are always strings. To use column 

2363 labels of another type, pass them explicitly with ``names`` and set 

2364 ``header=0`` when the file contains a header row. 

2365 

2366 Examples 

2367 -------- 

2368 >>> pd.read_table("data.csv") # doctest: +SKIP 

2369 Name Value 

2370 0 foo 1 

2371 1 bar 2 

2372 2 #baz 3 

2373 

2374 Index and header can be specified via the `index_col` and `header` arguments. 

2375 

2376 >>> pd.read_table("data.csv", header=None) # doctest: +SKIP 

2377 0 1 

2378 0 Name Value 

2379 1 foo 1 

2380 2 bar 2 

2381 3 #baz 3 

2382 

2383 >>> pd.read_table("data.csv", index_col="Value") # doctest: +SKIP 

2384 Name 

2385 Value 

2386 1 foo 

2387 2 bar 

2388 3 #baz 

2389 

2390 Column types are inferred but can be explicitly specified using the dtype argument. 

2391 

2392 >>> pd.read_table("data.csv", dtype={"Value": float}) # doctest: +SKIP 

2393 Name Value 

2394 0 foo 1.0 

2395 1 bar 2.0 

2396 2 #baz 3.0 

2397 

2398 True, False, and NA values, and thousands separators have defaults, 

2399 but can be explicitly specified, too. Supply the values you would like 

2400 as strings or lists of strings! 

2401 

2402 >>> pd.read_table("data.csv", na_values=["foo", "bar"]) # doctest: +SKIP 

2403 Name Value 

2404 0 NaN 1 

2405 1 NaN 2 

2406 2 #baz 3 

2407 

2408 Comment lines in the input file can be skipped using the `comment` argument. 

2409 

2410 >>> pd.read_table("data.csv", comment="#") # doctest: +SKIP 

2411 Name Value 

2412 0 foo 1 

2413 1 bar 2 

2414 

2415 By default, columns with dates will be read as ``object`` rather than ``datetime``. 

2416 

2417 >>> df = pd.read_table("tmp.csv") # doctest: +SKIP 

2418 

2419 >>> df # doctest: +SKIP 

2420 col 1 col 2 col 3 

2421 0 10 10/04/2018 Sun 15 Jan 2023 

2422 1 20 15/04/2018 Fri 12 May 2023 

2423 

2424 >>> df.dtypes # doctest: +SKIP 

2425 col 1 int64 

2426 col 2 object 

2427 col 3 object 

2428 dtype: object 

2429 

2430 Specific columns can be parsed as dates by using the `parse_dates` and 

2431 `date_format` arguments. 

2432 

2433 >>> df = pd.read_table( 

2434 ... "tmp.csv", 

2435 ... parse_dates=[1, 2], 

2436 ... date_format={"col 2": "%d/%m/%Y", "col 3": "%a %d %b %Y"}, 

2437 ... ) # doctest: +SKIP 

2438 

2439 >>> df.dtypes # doctest: +SKIP 

2440 col 1 int64 

2441 col 2 datetime64[ns] 

2442 col 3 datetime64[ns] 

2443 dtype: object 

2444 """ 

2445 # locals() should never be modified 

2446 kwds = locals().copy() 

2447 del kwds["filepath_or_buffer"] 

2448 del kwds["sep"] 

2449 

2450 kwds_defaults = _refine_defaults_read( 

2451 dialect, 

2452 delimiter, 

2453 engine, 

2454 sep, 

2455 on_bad_lines, 

2456 names, 

2457 defaults={"delimiter": "\t"}, 

2458 dtype_backend=dtype_backend, 

2459 lineterminator=lineterminator, 

2460 ) 

2461 kwds.update(kwds_defaults) 

2462 

2463 return _read(filepath_or_buffer, kwds) 

2464 

2465 

2466@overload 

2467def read_fwf( 

2468 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

2469 *, 

2470 colspecs: Sequence[tuple[int, int]] | str | None = ..., 

2471 widths: Sequence[int] | None = ..., 

2472 infer_nrows: int = ..., 

2473 iterator: Literal[True], 

2474 chunksize: int | None = ..., 

2475 **kwds: Unpack[_read_shared[HashableT]], 

2476) -> TextFileReader: ... 

2477 

2478 

2479@overload 

2480def read_fwf( 

2481 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

2482 *, 

2483 colspecs: Sequence[tuple[int, int]] | str | None = ..., 

2484 widths: Sequence[int] | None = ..., 

2485 infer_nrows: int = ..., 

2486 iterator: bool = ..., 

2487 chunksize: int, 

2488 **kwds: Unpack[_read_shared[HashableT]], 

2489) -> TextFileReader: ... 

2490 

2491 

2492@overload 

2493def read_fwf( 

2494 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

2495 *, 

2496 colspecs: Sequence[tuple[int, int]] | str | None = ..., 

2497 widths: Sequence[int] | None = ..., 

2498 infer_nrows: int = ..., 

2499 iterator: Literal[False] = ..., 

2500 chunksize: None = ..., 

2501 **kwds: Unpack[_read_shared[HashableT]], 

2502) -> DataFrame: ... 

2503 

2504 

2505@set_module("pandas") 

2506def read_fwf( 

2507 filepath_or_buffer: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str], 

2508 *, 

2509 colspecs: Sequence[tuple[int, int]] | str | None = "infer", 

2510 widths: Sequence[int] | None = None, 

2511 infer_nrows: int = 100, 

2512 iterator: bool = False, 

2513 chunksize: int | None = None, 

2514 **kwds: Unpack[_read_shared[HashableT]], 

2515) -> DataFrame | TextFileReader: 

2516 r""" 

2517 Read a table of fixed-width formatted lines into DataFrame. 

2518 

2519 Also supports optionally iterating or breaking of the file 

2520 into chunks. 

2521 

2522 Additional help can be found in the `online docs for IO Tools 

2523 <https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html>`_. 

2524 

2525 Parameters 

2526 ---------- 

2527 filepath_or_buffer : str, path object, or file-like object 

2528 Any valid string path is acceptable. The string could be a URL. Valid 

2529 URL schemes include http, ftp, s3, and file. For file URLs, a host is 

2530 expected. A local file could be: ``file://localhost/path/to/table.csv``. 

2531 

2532 Certain URL schemes may require additional packages. For example, S3 

2533 URLs require the ``s3fs`` library. See 

2534 :ref:`install.optional_dependencies` for a full list. 

2535 

2536 If you want to pass in a path object, pandas accepts any ``os.PathLike``. 

2537 

2538 By file-like object, we refer to objects with a ``read()`` method that 

2539 accepts an optional size argument, such as a file handle (e.g. via 

2540 builtin ``open`` function) or ``StringIO``. 

2541 colspecs : list of tuple (int, int) or 'infer'. optional 

2542 A list of tuples giving the extents of the fixed-width 

2543 fields of each line as half-open intervals (i.e., [from, to] ). 

2544 String value 'infer' can be used to instruct the parser to try 

2545 detecting the column specifications from the first 100 rows of 

2546 the data which are not being skipped via skiprows (default='infer'). 

2547 widths : list of int, optional 

2548 A list of field widths which can be used instead of 'colspecs' if 

2549 the intervals are contiguous. 

2550 infer_nrows : int, default 100 

2551 The number of rows to consider when letting the parser determine the 

2552 ``colspecs``. Pass ``np.inf`` to infer column specifications from the 

2553 entire file, which is useful when columns vary in width and a value 

2554 wider than anything in the first ``infer_nrows`` rows would otherwise 

2555 be truncated. 

2556 iterator : bool, default False 

2557 Return ``TextFileReader`` object for iteration or getting chunks with 

2558 ``get_chunk()``. 

2559 chunksize : int, optional 

2560 Number of lines to read from the file per chunk. 

2561 **kwds : optional 

2562 Optional keyword arguments can be passed to ``TextFileReader``. 

2563 

2564 Returns 

2565 ------- 

2566 DataFrame or TextFileReader 

2567 A comma-separated values (csv) file is returned as two-dimensional 

2568 data structure with labeled axes. 

2569 

2570 See Also 

2571 -------- 

2572 DataFrame.to_csv : Write DataFrame to a comma-separated values (csv) file. 

2573 read_csv : Read a comma-separated values (csv) file into DataFrame. 

2574 

2575 Examples 

2576 -------- 

2577 >>> pd.read_fwf("data.csv") # doctest: +SKIP 

2578 """ 

2579 # Check input arguments. 

2580 if colspecs is None and widths is None: 

2581 raise ValueError("Must specify either colspecs or widths") 

2582 if colspecs not in (None, "infer") and widths is not None: 

2583 raise ValueError("You must specify only one of 'widths' and 'colspecs'") 

2584 

2585 # Compute 'colspecs' from 'widths', if specified. 

2586 if widths is not None: 

2587 colspecs, col = [], 0 

2588 for w in widths: 

2589 colspecs.append((col, col + w)) 

2590 col += w 

2591 

2592 # for mypy 

2593 assert colspecs is not None 

2594 

2595 # GH#40830 

2596 # Ensure length of `colspecs` matches length of `names` 

2597 names = kwds.get("names") 

2598 if names is not None and names is not lib.no_default: 

2599 if len(names) != len(colspecs) and colspecs != "infer": 

2600 # need to check len(index_col) as it might contain 

2601 # unnamed indices, in which case it's name is not required 

2602 len_index = 0 

2603 if kwds.get("index_col") is not None: 

2604 index_col: Any = kwds.get("index_col") 

2605 if index_col is not False: 

2606 if not is_list_like(index_col): 

2607 len_index = 1 

2608 else: 

2609 # for mypy: handled in the if-branch 

2610 assert index_col is not lib.no_default 

2611 

2612 len_index = len(index_col) 

2613 if kwds.get("usecols") is None and len(names) + len_index != len(colspecs): 

2614 # If usecols is used colspec may be longer than names 

2615 raise ValueError("Length of colspecs must match length of names") 

2616 

2617 check_dtype_backend(kwds.setdefault("dtype_backend", lib.no_default)) 

2618 return _read( 

2619 filepath_or_buffer, 

2620 kwds 

2621 | { 

2622 "colspecs": colspecs, 

2623 "infer_nrows": infer_nrows, 

2624 "engine": "python-fwf", 

2625 "iterator": iterator, 

2626 "chunksize": chunksize, 

2627 }, 

2628 ) 

2629 

2630 

2631class TextFileReader(abc.Iterator): 

2632 """ 

2633 Iterator over chunks of a delimited text file. 

2634 

2635 Returned by :func:`read_csv`, :func:`read_table`, and :func:`read_fwf` 

2636 when ``iterator=True`` or ``chunksize`` is given. Not intended to be 

2637 constructed directly by users. 

2638 

2639 A ``TextFileReader`` can be iterated over to yield successive chunks as 

2640 :class:`DataFrame` objects, used as a context manager to ensure the 

2641 underlying file handle is closed, or queried explicitly via 

2642 :meth:`~TextFileReader.read` and :meth:`~TextFileReader.get_chunk`. 

2643 

2644 Passed ``dialect`` overrides any of the related parser options. 

2645 

2646 Parameters 

2647 ---------- 

2648 f : str, path object, or file-like object 

2649 Source to read from. Accepts the same inputs as :func:`read_csv`. 

2650 engine : {'c', 'python', 'pyarrow', 'python-fwf'}, optional 

2651 Parser engine to use. If not specified, defaults to ``'python'``. 

2652 **kwds 

2653 Any keyword argument accepted by :func:`read_csv`, :func:`read_table`, 

2654 or :func:`read_fwf`. 

2655 

2656 See Also 

2657 -------- 

2658 read_csv : Read a comma-separated values (csv) file into a DataFrame. 

2659 read_table : Read general delimited file into a DataFrame. 

2660 read_fwf : Read a table of fixed-width formatted lines into a DataFrame. 

2661 

2662 Examples 

2663 -------- 

2664 >>> with pd.read_csv("data.csv", chunksize=1000) as reader: # doctest: +SKIP 

2665 ... for chunk in reader: 

2666 ... process(chunk) 

2667 

2668 >>> with pd.read_csv("data.csv", iterator=True) as reader: # doctest: +SKIP 

2669 ... first = reader.get_chunk(5) 

2670 """ 

2671 

2672 def __init__( 

2673 self, 

2674 f: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str] | list, 

2675 engine: CSVEngine | None = None, 

2676 **kwds, 

2677 ) -> None: 

2678 if engine is not None: 

2679 engine_specified = True 

2680 else: 

2681 engine = "python" 

2682 engine_specified = False 

2683 self.engine = engine 

2684 self._engine_specified = kwds.get("engine_specified", engine_specified) 

2685 

2686 # before _validate_skipfooter, which reads `iterator` for truthiness 

2687 _validate_bool_kwargs(kwds) 

2688 _validate_skipfooter(kwds) 

2689 

2690 dialect = _extract_dialect(kwds) 

2691 if dialect is not None: 

2692 if engine == "pyarrow": 

2693 raise ValueError( 

2694 "The 'dialect' option is not supported with the 'pyarrow' engine" 

2695 ) 

2696 kwds = _merge_with_dialect_properties(dialect, kwds) 

2697 # again, for the values the dialect supplied 

2698 _validate_bool_kwargs(kwds) 

2699 

2700 if kwds.get("header", "infer") == "infer": 

2701 kwds["header"] = 0 if kwds.get("names") is None else None 

2702 

2703 self.orig_options = kwds 

2704 

2705 # miscellanea 

2706 self._currow = 0 

2707 

2708 options = self._get_options_with_defaults(engine) 

2709 options["storage_options"] = kwds.get("storage_options", None) 

2710 

2711 self.chunksize = options.pop("chunksize", None) 

2712 self.nrows = options.pop("nrows", None) 

2713 

2714 self._check_file_or_buffer(f, engine) 

2715 self.options, self.engine = self._clean_options(options, engine) 

2716 

2717 if "has_index_names" in kwds: 

2718 self.options["has_index_names"] = kwds["has_index_names"] 

2719 

2720 self.handles: IOHandles | None = None 

2721 self._engine = self._make_engine(f, self.engine) 

2722 

2723 def close(self) -> None: 

2724 """ 

2725 Close the underlying file handle and parser engine. 

2726 

2727 Called automatically when the ``TextFileReader`` is used as a context 

2728 manager. 

2729 

2730 See Also 

2731 -------- 

2732 TextFileReader.read : Read rows from the file into a DataFrame. 

2733 TextFileReader.get_chunk : Read the next chunk of rows. 

2734 

2735 Examples 

2736 -------- 

2737 >>> reader = pd.read_csv("data.csv", chunksize=5) # doctest: +SKIP 

2738 >>> reader.close() # doctest: +SKIP 

2739 """ 

2740 if self.handles is not None: 

2741 self.handles.close() 

2742 self._engine.close() 

2743 

2744 def _get_options_with_defaults(self, engine: CSVEngine) -> dict[str, Any]: 

2745 kwds = self.orig_options 

2746 

2747 options = {} 

2748 default: object | None 

2749 

2750 for argname, default in parser_defaults.items(): 

2751 value = kwds.get(argname, default) 

2752 

2753 # see gh-12935 

2754 if ( 

2755 engine == "pyarrow" 

2756 and argname in _pyarrow_unsupported 

2757 and value != default 

2758 and value != getattr(value, "value", default) 

2759 ): 

2760 raise ValueError( 

2761 f"The {argname!r} option is not supported with the 'pyarrow' engine" 

2762 ) 

2763 options[argname] = value 

2764 

2765 for argname, default in _c_parser_defaults.items(): 

2766 if argname in kwds: 

2767 value = kwds[argname] 

2768 

2769 if engine != "c" and value != default: 

2770 # TODO: Refactor this logic, its pretty convoluted 

2771 if "python" in engine and argname not in _python_unsupported: 

2772 pass 

2773 elif "pyarrow" in engine and argname not in _pyarrow_unsupported: 

2774 pass 

2775 else: 

2776 raise ValueError( 

2777 f"The {argname!r} option is not supported with the " 

2778 f"{engine!r} engine" 

2779 ) 

2780 else: 

2781 value = default 

2782 options[argname] = value 

2783 

2784 if engine == "python-fwf": 

2785 for argname, default in _fwf_defaults.items(): 

2786 options[argname] = kwds.get(argname, default) 

2787 

2788 return options 

2789 

2790 def _check_file_or_buffer(self, f, engine: CSVEngine) -> None: 

2791 if hasattr(f, "encoding"): 

2792 file_encoding = f.encoding 

2793 orig_reader_enc = self.orig_options.get("encoding", None) 

2794 any_none = file_encoding is None or orig_reader_enc is None 

2795 if file_encoding != orig_reader_enc and not any_none: 

2796 file_path = getattr(f, "name", None) 

2797 raise ValueError( 

2798 f"The specified reader encoding {orig_reader_enc} is different " 

2799 f"from the encoding {file_encoding} of file {file_path}." 

2800 ) 

2801 

2802 def _clean_options( 

2803 self, options: dict[str, Any], engine: CSVEngine 

2804 ) -> tuple[dict[str, Any], CSVEngine]: 

2805 result = options.copy() 

2806 

2807 fallback_reason = None 

2808 

2809 # C engine not supported yet 

2810 if engine == "c": 

2811 if options["skipfooter"] > 0: 

2812 fallback_reason = "the 'c' engine does not support skipfooter" 

2813 engine = "python" 

2814 

2815 sep = options["delimiter"] 

2816 

2817 if sep is None: 

2818 # sniffing the separator with csv.Sniffer is python-engine only 

2819 if engine in ("c", "pyarrow"): 

2820 fallback_reason = f"the '{engine}' engine does not support sep=None" 

2821 engine = "python" 

2822 elif len(sep) > 1: 

2823 if engine == "c" and sep == r"\s+": 

2824 # delim_whitespace passed on to pandas._libs.parsers.TextReader 

2825 result["delim_whitespace"] = True 

2826 del result["delimiter"] 

2827 elif engine not in ("python", "python-fwf"): 

2828 # wait until regex engine integrated 

2829 fallback_reason = ( 

2830 f"the '{engine}' engine does not support " 

2831 "separators > 1 char, including regex separators" 

2832 ) 

2833 engine = "python" 

2834 else: 

2835 # The C engine always tokenizes a utf-8 byte stream: text handles 

2836 # are re-encoded to utf-8 before reaching it, whatever `encoding` 

2837 # says. So the separator has to be a single utf-8 byte; the 

2838 # platform's filesystem encoding has nothing to do with it. 

2839 encodeable = True 

2840 try: 

2841 if len(sep.encode("utf-8")) > 1: 

2842 encodeable = False 

2843 except UnicodeEncodeError: 

2844 # e.g. a lone surrogate 

2845 encodeable = False 

2846 if not encodeable and engine not in ("python", "python-fwf"): 

2847 fallback_reason = ( 

2848 "the separator encoded in utf-8 " 

2849 f"is > 1 char long, and the '{engine}' engine " 

2850 "does not support such separators" 

2851 ) 

2852 engine = "python" 

2853 

2854 quotechar = options["quotechar"] 

2855 if quotechar is not None and isinstance(quotechar, (str, bytes)): 

2856 if ( 

2857 len(quotechar) == 1 

2858 and ord(quotechar) > 127 

2859 and engine not in ("python", "python-fwf") 

2860 ): 

2861 fallback_reason = ( 

2862 "ord(quotechar) > 127, meaning the " 

2863 "quotechar is larger than one byte, " 

2864 f"and the '{engine}' engine does not support such quotechars" 

2865 ) 

2866 engine = "python" 

2867 

2868 if fallback_reason and self._engine_specified: 

2869 raise ValueError(fallback_reason) 

2870 

2871 if engine == "c": 

2872 for arg in _c_unsupported: 

2873 del result[arg] 

2874 

2875 if "python" in engine: 

2876 for arg in _python_unsupported: 

2877 if fallback_reason and result[arg] != _c_parser_defaults.get(arg): 

2878 raise ValueError( 

2879 "Falling back to the 'python' engine because " 

2880 f"{fallback_reason}, but this causes {arg!r} to be " 

2881 "ignored as it is not supported by the 'python' engine." 

2882 ) 

2883 del result[arg] 

2884 

2885 if fallback_reason: 

2886 warnings.warn( 

2887 ( 

2888 "Falling back to the 'python' engine because " 

2889 f"{fallback_reason}; you can avoid this warning by specifying " 

2890 "engine='python'." 

2891 ), 

2892 ParserWarning, 

2893 stacklevel=find_stack_level(), 

2894 ) 

2895 

2896 index_col = options["index_col"] 

2897 names = options["names"] 

2898 converters = options["converters"] 

2899 na_values = options["na_values"] 

2900 skiprows = options["skiprows"] 

2901 

2902 validate_header_arg(options["header"]) 

2903 

2904 if index_col is True: 

2905 raise ValueError("The value of index_col couldn't be 'True'") 

2906 if is_index_col(index_col): 

2907 if not isinstance(index_col, (list, tuple, np.ndarray)): 

2908 index_col = [index_col] 

2909 result["index_col"] = index_col 

2910 

2911 names = list(names) if names is not None else names 

2912 

2913 # type conversion-related 

2914 if converters is not None: 

2915 if not isinstance(converters, dict): 

2916 raise TypeError( 

2917 "Type converters must be a dict or subclass, " 

2918 f"input was a {type(converters).__name__}" 

2919 ) 

2920 else: 

2921 converters = {} 

2922 

2923 # Converting values to NA 

2924 keep_default_na = options["keep_default_na"] 

2925 floatify = engine != "pyarrow" 

2926 na_values, na_fvalues = _clean_na_values( 

2927 na_values, keep_default_na, floatify=floatify 

2928 ) 

2929 

2930 # handle skiprows; this is internally handled by the 

2931 # c-engine, so only need for python and pyarrow parsers 

2932 if engine == "pyarrow": 

2933 if not is_integer(skiprows) and skiprows is not None: 

2934 # pyarrow expects skiprows to be passed as an integer 

2935 raise ValueError( 

2936 "skiprows argument must be an integer when using engine='pyarrow'" 

2937 ) 

2938 else: 

2939 if is_integer(skiprows): 

2940 skiprows = range(skiprows) 

2941 if skiprows is None: 

2942 skiprows = set() 

2943 elif not callable(skiprows): 

2944 skiprows = set(skiprows) 

2945 

2946 # put stuff back 

2947 result["names"] = names 

2948 result["converters"] = converters 

2949 result["na_values"] = na_values 

2950 result["na_fvalues"] = na_fvalues 

2951 result["skiprows"] = skiprows 

2952 

2953 return result, engine 

2954 

2955 def __next__(self) -> DataFrame: 

2956 try: 

2957 return self.get_chunk() 

2958 except StopIteration: 

2959 self.close() 

2960 raise 

2961 

2962 def _make_engine( 

2963 self, 

2964 f: FilePath | ReadCsvBuffer[bytes] | ReadCsvBuffer[str] | list | IO, 

2965 engine: CSVEngine = "c", 

2966 ) -> ParserBase: 

2967 mapping: dict[str, type[ParserBase]] = { 

2968 "c": CParserWrapper, 

2969 "python": PythonParser, 

2970 "pyarrow": ArrowParserWrapper, 

2971 "python-fwf": FixedWidthFieldParser, 

2972 } 

2973 

2974 if engine not in mapping: 

2975 raise ValueError( 

2976 f"Unknown engine: {engine} (valid options are {mapping.keys()})" 

2977 ) 

2978 if not isinstance(f, list): 

2979 # open file here 

2980 # The c and pyarrow engines can decode utf-8 bytes natively; 

2981 # wrapping in TextIOWrapper makes them slower, especially for 

2982 # memory_map and remote file-like objects (GH#46823). 

2983 is_text = engine in ("python", "python-fwf") or ( 

2984 engine == "c" 

2985 and self.options.get("encoding", "utf-8") not in (None, "utf-8") 

2986 ) 

2987 mode = "r" if is_text else "rb" 

2988 self.handles = get_handle( 

2989 f, 

2990 mode, 

2991 encoding=self.options.get("encoding", None), 

2992 compression=self.options.get("compression", None), 

2993 memory_map=self.options.get("memory_map", False), 

2994 is_text=is_text, 

2995 errors=self.options.get("encoding_errors", "strict"), 

2996 storage_options=self.options.get("storage_options", None), 

2997 ) 

2998 assert self.handles is not None 

2999 f = self.handles.handle 

3000 

3001 elif engine != "python": 

3002 msg = f"Invalid file path or buffer object type: {type(f)}" 

3003 raise ValueError(msg) 

3004 

3005 try: 

3006 return mapping[engine](f, **self.options) 

3007 except Exception: 

3008 if self.handles is not None: 

3009 self.handles.close() 

3010 raise 

3011 

3012 def _failover_to_python(self) -> None: 

3013 raise AbstractMethodError(self) 

3014 

3015 def read(self, nrows: int | None = None) -> DataFrame: 

3016 """ 

3017 Read rows from the file into a :class:`DataFrame`. 

3018 

3019 Advances the internal cursor by the number of rows returned, so 

3020 successive calls yield later rows of the file. Used with readers 

3021 created via :func:`read_csv` or :func:`read_table` with 

3022 ``iterator=True`` or ``chunksize`` set. 

3023 

3024 Parameters 

3025 ---------- 

3026 nrows : int, optional 

3027 Number of rows to read. If ``None``, read until end of file. 

3028 Ignored when the underlying engine is ``"pyarrow"``, which always 

3029 reads the full file. 

3030 

3031 Returns 

3032 ------- 

3033 DataFrame 

3034 The parsed rows. 

3035 

3036 See Also 

3037 -------- 

3038 TextFileReader.get_chunk : Read the next chunk of rows. 

3039 read_csv : Read a comma-separated values (csv) file into a DataFrame. 

3040 

3041 Examples 

3042 -------- 

3043 >>> with pd.read_csv("data.csv", iterator=True) as reader: # doctest: +SKIP 

3044 ... df = reader.read() 

3045 """ 

3046 if self.engine == "pyarrow": 

3047 try: 

3048 # error: "ParserBase" has no attribute "read" 

3049 df = self._engine.read() # type: ignore[attr-defined] 

3050 except Exception: 

3051 self.close() 

3052 raise 

3053 else: 

3054 nrows = validate_integer("nrows", nrows) 

3055 try: 

3056 # error: "ParserBase" has no attribute "read" 

3057 ( 

3058 index, 

3059 columns, 

3060 col_dict, 

3061 ) = self._engine.read( # type: ignore[attr-defined] 

3062 nrows 

3063 ) 

3064 except Exception: 

3065 self.close() 

3066 raise 

3067 

3068 if index is None: 

3069 if col_dict: 

3070 # Any column is actually fine: 

3071 new_rows = len(next(iter(col_dict.values()))) 

3072 index = RangeIndex(self._currow, self._currow + new_rows) 

3073 else: 

3074 new_rows = 0 

3075 else: 

3076 new_rows = len(index) 

3077 

3078 if hasattr(self, "orig_options"): 

3079 dtype_arg = self.orig_options.get("dtype", None) 

3080 else: 

3081 dtype_arg = None 

3082 

3083 if isinstance(dtype_arg, dict): 

3084 dtype = defaultdict(lambda: None) # type: ignore[var-annotated] 

3085 dtype.update(dtype_arg) 

3086 elif dtype_arg is not None and pandas_dtype(dtype_arg) in ( 

3087 np.str_, 

3088 np.object_, 

3089 ): 

3090 dtype = defaultdict(lambda: dtype_arg) 

3091 else: 

3092 dtype = None 

3093 

3094 if dtype is not None: 

3095 new_col_dict = {} 

3096 for k, v in col_dict.items(): 

3097 d = ( 

3098 dtype[k] 

3099 if pandas_dtype(dtype[k]) in (np.str_, np.object_) 

3100 else None 

3101 ) 

3102 new_col_dict[k] = Series(v, index=index, dtype=d, copy=False) 

3103 else: 

3104 new_col_dict = col_dict 

3105 

3106 df = DataFrame( 

3107 new_col_dict, 

3108 columns=columns, 

3109 index=index, 

3110 copy=False, 

3111 ) 

3112 

3113 self._currow += new_rows 

3114 return df 

3115 

3116 def get_chunk(self, size: int | None = None) -> DataFrame: 

3117 """ 

3118 Read the next chunk of rows from the file. 

3119 

3120 Convenience wrapper around :meth:`TextFileReader.read` that defaults 

3121 ``size`` to the ``chunksize`` passed to :func:`read_csv` or 

3122 :func:`read_table`, and respects a configured ``nrows`` limit by 

3123 raising ``StopIteration`` once it has been reached. 

3124 

3125 Parameters 

3126 ---------- 

3127 size : int, optional 

3128 Number of rows to read in this chunk. Defaults to the ``chunksize`` 

3129 passed when the reader was created. 

3130 

3131 Returns 

3132 ------- 

3133 DataFrame 

3134 The next chunk of parsed rows. 

3135 

3136 Raises 

3137 ------ 

3138 StopIteration 

3139 If ``nrows`` was set and has already been reached. 

3140 

3141 See Also 

3142 -------- 

3143 TextFileReader.read : Read rows from the file into a DataFrame. 

3144 read_csv : Read a comma-separated values (csv) file into a DataFrame. 

3145 

3146 Examples 

3147 -------- 

3148 >>> with pd.read_csv("data.csv", iterator=True) as reader: # doctest: +SKIP 

3149 ... first_five = reader.get_chunk(5) 

3150 """ 

3151 if size is None: 

3152 size = self.chunksize 

3153 if self.nrows is not None: 

3154 if self._currow >= self.nrows: 

3155 raise StopIteration 

3156 if size is None: 

3157 size = self.nrows - self._currow 

3158 else: 

3159 size = min(size, self.nrows - self._currow) 

3160 return self.read(nrows=size) 

3161 

3162 def __enter__(self) -> Self: 

3163 return self 

3164 

3165 def __exit__( 

3166 self, 

3167 exc_type: type[BaseException] | None, 

3168 exc_value: BaseException | None, 

3169 traceback: TracebackType | None, 

3170 ) -> None: 

3171 self.close() 

3172 

3173 

3174def TextParser(*args, **kwds) -> TextFileReader: 

3175 """ 

3176 Converts lists of lists/tuples into DataFrames with proper type inference 

3177 and optional (e.g. string to datetime) conversion. Also enables iterating 

3178 lazily over chunks of large files 

3179 

3180 Parameters 

3181 ---------- 

3182 data : file-like object or list 

3183 delimiter : separator character to use 

3184 dialect : str or csv.Dialect class / instance, optional 

3185 Ignored if delimiter is longer than 1 character 

3186 names : sequence, default 

3187 header : int, default 0 

3188 Row to use to parse column labels. Defaults to the first row. Prior 

3189 rows will be discarded 

3190 index_col : int or list, optional 

3191 Column or columns to use as the (possibly hierarchical) index 

3192 has_index_names: bool, default False 

3193 True if the cols defined in index_col have an index name and are 

3194 not in the header. 

3195 na_values : scalar, str, list-like, or dict, optional 

3196 Additional strings to recognize as NA/NaN. 

3197 keep_default_na : bool, default True 

3198 thousands : str, optional 

3199 Thousands separator 

3200 comment : str, optional 

3201 Comment out remainder of line 

3202 parse_dates : bool, default False 

3203 date_format : str or dict of column -> format, default ``None`` 

3204 

3205 .. versionadded:: 2.0.0 

3206 skiprows : list of integers 

3207 Row numbers to skip 

3208 skipfooter : int 

3209 Number of line at bottom of file to skip 

3210 converters : dict, optional 

3211 Dict of functions for converting values in certain columns. Keys can 

3212 either be integers or column labels, values are functions that take one 

3213 input argument, the cell (not column) content, and return the 

3214 transformed content. 

3215 encoding : str, optional 

3216 Encoding to use for UTF when reading/writing (ex. 'utf-8') 

3217 float_precision : str, optional 

3218 Specifies which converter the C engine should use for floating-point 

3219 values. The options are `None` or `high` for the ordinary converter, 

3220 `legacy` for the original lower precision pandas converter, and 

3221 `round_trip` for the round-trip converter. 

3222 

3223 .. deprecated:: 3.1.0 

3224 All float precision modes now use the same converter. 

3225 The ``float_precision`` argument will be removed in a future version. 

3226 """ 

3227 kwds["engine"] = "python" 

3228 return TextFileReader(*args, **kwds) 

3229 

3230 

3231def _clean_na_values(na_values, keep_default_na: bool = True, floatify: bool = True): 

3232 na_fvalues: set | dict 

3233 if na_values is None: 

3234 if keep_default_na: 

3235 na_values = STR_NA_VALUES 

3236 else: 

3237 na_values = set() 

3238 na_fvalues = set() 

3239 elif isinstance(na_values, dict): 

3240 old_na_values = na_values.copy() 

3241 na_values = {} # Prevent aliasing. 

3242 

3243 # Convert the values in the na_values dictionary 

3244 # into array-likes for further use. This is also 

3245 # where we append the default NaN values, provided 

3246 # that `keep_default_na=True`. 

3247 for k, v in old_na_values.items(): 

3248 if not is_list_like(v): 

3249 v = [v] 

3250 

3251 if keep_default_na: 

3252 v = set(v) | STR_NA_VALUES 

3253 

3254 na_values[k] = _stringify_na_values(v, floatify) 

3255 na_fvalues = {k: _floatify_na_values(v) for k, v in na_values.items()} 

3256 else: 

3257 if not is_list_like(na_values): 

3258 na_values = [na_values] 

3259 na_values = _stringify_na_values(na_values, floatify) 

3260 if keep_default_na: 

3261 na_values = na_values | STR_NA_VALUES 

3262 

3263 na_fvalues = _floatify_na_values(na_values) 

3264 

3265 return na_values, na_fvalues 

3266 

3267 

3268def _floatify_na_values(na_values) -> set[float]: 

3269 # create float versions of the na_values 

3270 result = set() 

3271 for v in na_values: 

3272 try: 

3273 v = float(v) 

3274 if not np.isnan(v): 

3275 result.add(v) 

3276 except (TypeError, ValueError, OverflowError): 

3277 pass 

3278 return result 

3279 

3280 

3281def _stringify_na_values(na_values, floatify: bool) -> set[str | float]: 

3282 """return a stringified and numeric for these values""" 

3283 result: list[str | float] = [] 

3284 for x in na_values: 

3285 result.append(str(x)) 

3286 result.append(x) 

3287 try: 

3288 v = float(x) 

3289 

3290 # we are like 999 here 

3291 if v == int(v): 

3292 v = int(v) 

3293 result.append(f"{v}.0") 

3294 result.append(str(v)) 

3295 

3296 if floatify: 

3297 result.append(v) 

3298 except (TypeError, ValueError, OverflowError): 

3299 pass 

3300 if floatify: 

3301 try: 

3302 result.append(int(x)) 

3303 except (TypeError, ValueError, OverflowError): 

3304 pass 

3305 return set(result) 

3306 

3307 

3308def _refine_defaults_read( 

3309 dialect: str | csv.Dialect | type[csv.Dialect] | None, 

3310 delimiter: str | lib.NoDefault | None, 

3311 engine: CSVEngine | None, 

3312 sep: str | lib.NoDefault | None, 

3313 on_bad_lines: str | Callable, 

3314 names: Sequence[Hashable] | lib.NoDefault | None, 

3315 defaults: dict[str, Any], 

3316 dtype_backend: DtypeBackend | lib.NoDefault, 

3317 lineterminator: str | bytes | None, 

3318): 

3319 """Validate/refine default values of input parameters of read_csv, read_table. 

3320 

3321 Parameters 

3322 ---------- 

3323 dialect : str or csv.Dialect 

3324 If provided, this parameter will override values (default or not) for the 

3325 following parameters: `delimiter`, `doublequote`, `escapechar`, 

3326 `skipinitialspace`, `quotechar`, and `quoting`. If it is necessary to 

3327 override values, a ParserWarning will be issued. See csv.Dialect 

3328 documentation for more details. 

3329 delimiter : str or object 

3330 Alias for sep, taking the same sentinel value when not provided. 

3331 engine : {'c', 'python'} 

3332 Parser engine to use. The C engine is faster while the python engine is 

3333 currently more feature-complete. 

3334 sep : str or object 

3335 A delimiter provided by the user (str) or a sentinel value, i.e. 

3336 pandas._libs.lib.no_default. 

3337 on_bad_lines : str, callable 

3338 An option for handling bad lines or a sentinel value(None). 

3339 names : array-like, optional 

3340 List of column names to use. If the file contains a header row, 

3341 then you should explicitly pass ``header=0`` to override the column names. 

3342 Duplicates in this list are not allowed. 

3343 defaults: dict 

3344 Default values of input parameters. 

3345 lineterminator : str, bytes or None 

3346 Line terminator passed by the user, if any. 

3347 

3348 Returns 

3349 ------- 

3350 kwds : dict 

3351 Input parameters with correct values. 

3352 """ 

3353 # fix types for sep, delimiter to Union(str, Any) 

3354 delim_default = defaults["delimiter"] 

3355 kwds: dict[str, Any] = {} 

3356 # gh-23761 

3357 # 

3358 # When a dialect is passed, it overrides any of the overlapping 

3359 # parameters passed in directly. We don't want to warn if the 

3360 # default parameters were passed in (since it probably means 

3361 # that the user didn't pass them in explicitly in the first place). 

3362 # 

3363 # "delimiter" is the annoying corner case because we alias it to 

3364 # "sep" before doing comparison to the dialect values later on. 

3365 # Thus, we need a flag to indicate that we need to "override" 

3366 # the comparison to dialect values by checking if default values 

3367 # for BOTH "delimiter" and "sep" were provided. 

3368 if dialect is not None: 

3369 kwds["sep_override"] = delimiter is lib.no_default and ( 

3370 sep is lib.no_default or sep == delim_default 

3371 ) 

3372 

3373 if delimiter is not lib.no_default and sep is not lib.no_default: 

3374 raise ValueError("Specified a sep and a delimiter; you can only specify one.") 

3375 

3376 kwds["names"] = None if names is lib.no_default else names 

3377 

3378 # Alias sep -> delimiter. Both use lib.no_default rather than None as the 

3379 # "not passed" sentinel, so an explicit None means sniff (GH#47024). 

3380 if delimiter is lib.no_default: 

3381 delimiter = sep 

3382 

3383 if isinstance(lineterminator, (bytes, bytearray)): 

3384 # the C engine accepts these; compare on the character it will use 

3385 lineterminator = lineterminator.decode("latin-1") 

3386 

3387 # GH#43528, GH#51801: the C engine silently mis-parses these, as the field 

3388 # separator is consumed as a line terminator before it can split a field. 

3389 # A custom lineterminator takes over that role, leaving "\n"/"\r" free to 

3390 # separate fields. 

3391 if delimiter in ("\n", "\r") and lineterminator in (None, delimiter): 

3392 raise ValueError( 

3393 f"Specified {delimiter!r} as separator or delimiter, but a line " 

3394 f"terminator cannot be used as a separator. To parse {delimiter!r} " 

3395 f"as a separator, pass a lineterminator other than {delimiter!r} " 

3396 "(engine='c' only).", 

3397 ) 

3398 

3399 if delimiter is lib.no_default: 

3400 # assign default separator value 

3401 kwds["delimiter"] = delim_default 

3402 else: 

3403 kwds["delimiter"] = delimiter 

3404 

3405 if engine is not None: 

3406 kwds["engine_specified"] = True 

3407 else: 

3408 kwds["engine"] = "c" 

3409 kwds["engine_specified"] = False 

3410 

3411 if on_bad_lines == "error": 

3412 kwds["on_bad_lines"] = ParserBase.BadLineHandleMethod.BLHM_ERROR 

3413 elif on_bad_lines == "warn": 

3414 kwds["on_bad_lines"] = ParserBase.BadLineHandleMethod.BLHM_WARN 

3415 elif on_bad_lines == "skip": 

3416 kwds["on_bad_lines"] = ParserBase.BadLineHandleMethod.BLHM_SKIP 

3417 elif callable(on_bad_lines): 

3418 if engine not in ["python", "pyarrow"]: 

3419 raise ValueError( 

3420 "on_bad_line can only be a callable function " 

3421 "if engine='python' or 'pyarrow'" 

3422 ) 

3423 kwds["on_bad_lines"] = on_bad_lines 

3424 else: 

3425 raise ValueError(f"Argument {on_bad_lines} is invalid for on_bad_lines") 

3426 

3427 check_dtype_backend(dtype_backend) 

3428 

3429 kwds["dtype_backend"] = dtype_backend 

3430 

3431 return kwds 

3432 

3433 

3434def _extract_dialect( 

3435 kwds: dict[str, str | csv.Dialect | type[csv.Dialect]], 

3436) -> csv.Dialect | type[csv.Dialect] | None: 

3437 """ 

3438 Extract concrete csv dialect instance. 

3439 

3440 Returns 

3441 ------- 

3442 csv.Dialect or None 

3443 """ 

3444 if kwds.get("dialect") is None: 

3445 return None 

3446 

3447 dialect = kwds["dialect"] 

3448 if isinstance(dialect, str) and dialect in csv.list_dialects(): 

3449 # get_dialect is typed to return a `_csv.Dialect` for some reason in typeshed 

3450 tdialect = cast("csv.Dialect", csv.get_dialect(dialect)) 

3451 _validate_dialect(tdialect) 

3452 

3453 else: 

3454 _validate_dialect(dialect) 

3455 tdialect = cast("csv.Dialect", dialect) 

3456 

3457 return tdialect 

3458 

3459 

3460MANDATORY_DIALECT_ATTRS = ( 

3461 "delimiter", 

3462 "doublequote", 

3463 "escapechar", 

3464 "skipinitialspace", 

3465 "quotechar", 

3466 "quoting", 

3467) 

3468 

3469 

3470def _validate_dialect(dialect: csv.Dialect | type[csv.Dialect] | str) -> None: 

3471 """ 

3472 Validate csv dialect instance. 

3473 

3474 Raises 

3475 ------ 

3476 ValueError 

3477 If incorrect dialect is provided. 

3478 """ 

3479 for param in MANDATORY_DIALECT_ATTRS: 

3480 if not hasattr(dialect, param): 

3481 raise ValueError(f"Invalid dialect {dialect} provided") 

3482 

3483 

3484def _merge_with_dialect_properties( 

3485 dialect: csv.Dialect | type[csv.Dialect], 

3486 defaults: dict[str, Any], 

3487) -> dict[str, Any]: 

3488 """ 

3489 Merge default kwargs in TextFileReader with dialect parameters. 

3490 

3491 Parameters 

3492 ---------- 

3493 dialect : csv.Dialect 

3494 Concrete csv dialect. See csv.Dialect documentation for more details. 

3495 defaults : dict 

3496 Keyword arguments passed to TextFileReader. 

3497 

3498 Returns 

3499 ------- 

3500 kwds : dict 

3501 Updated keyword arguments, merged with dialect parameters. 

3502 """ 

3503 kwds = defaults.copy() 

3504 

3505 for param in MANDATORY_DIALECT_ATTRS: 

3506 dialect_val = getattr(dialect, param) 

3507 

3508 parser_default = parser_defaults[param] 

3509 provided = kwds.get(param, parser_default) 

3510 

3511 # Messages for conflicting values between the dialect 

3512 # instance and the actual parameters provided. 

3513 conflict_msgs = [] 

3514 

3515 # Don't warn if the default parameter was passed in, 

3516 # even if it conflicts with the dialect (gh-23761). 

3517 if provided not in (parser_default, dialect_val): 

3518 msg = ( 

3519 f"Conflicting values for '{param}': '{provided}' was " 

3520 f"provided, but the dialect specifies '{dialect_val}'. " 

3521 "Using the dialect-specified value." 

3522 ) 

3523 

3524 # Annoying corner case for not warning about 

3525 # conflicts between dialect and delimiter parameter. 

3526 # Refer to the outer "_read_" function for more info. 

3527 if not (param == "delimiter" and kwds.pop("sep_override", False)): 

3528 conflict_msgs.append(msg) 

3529 

3530 if conflict_msgs: 

3531 warnings.warn( 

3532 "\n\n".join(conflict_msgs), ParserWarning, stacklevel=find_stack_level() 

3533 ) 

3534 kwds[param] = dialect_val 

3535 return kwds 

3536 

3537 

3538def _validate_skipfooter(kwds: dict[str, Any]) -> None: 

3539 """ 

3540 Check whether skipfooter is compatible with other kwargs in TextFileReader. 

3541 

3542 Parameters 

3543 ---------- 

3544 kwds : dict 

3545 Keyword arguments passed to TextFileReader. 

3546 

3547 Raises 

3548 ------ 

3549 ValueError 

3550 If skipfooter is not compatible with other parameters. 

3551 """ 

3552 if kwds.get("skipfooter"): 

3553 if kwds.get("iterator") or kwds.get("chunksize"): 

3554 raise ValueError("'skipfooter' not supported for iteration") 

3555 if kwds.get("nrows"): 

3556 raise ValueError("'skipfooter' not supported with 'nrows'")