Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/_testing/__init__.py: 44%

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

214 statements  

1from __future__ import annotations 

2 

3from concurrent.futures import ThreadPoolExecutor 

4from decimal import Decimal 

5import operator 

6import os 

7from sys import byteorder 

8import threading 

9from typing import ( 

10 TYPE_CHECKING, 

11 Any, 

12) 

13 

14import numpy as np 

15 

16from pandas._config import using_string_dtype 

17from pandas._config.localization import ( 

18 can_set_locale, 

19 get_locales, 

20 set_locale, 

21) 

22 

23from pandas.compat import HAS_PYARROW 

24 

25import pandas as pd 

26from pandas import ( 

27 ArrowDtype, 

28 DataFrame, 

29 Index, 

30 MultiIndex, 

31 RangeIndex, 

32 Series, 

33) 

34from pandas._testing._io import ( 

35 round_trip_pathlib, 

36 round_trip_pickle, 

37 write_to_compressed, 

38) 

39from pandas._testing._warnings import ( 

40 assert_produces_warning, 

41 maybe_produces_warning, 

42) 

43from pandas._testing.asserters import ( 

44 assert_almost_equal, 

45 assert_attr_equal, 

46 assert_categorical_equal, 

47 assert_class_equal, 

48 assert_contains_all, 

49 assert_copy, 

50 assert_datetime_array_equal, 

51 assert_dict_equal, 

52 assert_equal, 

53 assert_extension_array_equal, 

54 assert_frame_equal, 

55 assert_index_equal, 

56 assert_indexing_slices_equivalent, 

57 assert_interval_array_equal, 

58 assert_is_sorted, 

59 assert_metadata_equivalent, 

60 assert_numpy_array_equal, 

61 assert_period_array_equal, 

62 assert_series_equal, 

63 assert_sp_array_equal, 

64 assert_timedelta_array_equal, 

65 raise_assert_detail, 

66) 

67from pandas._testing.compat import ( 

68 get_dtype, 

69 get_obj, 

70) 

71from pandas._testing.contexts import ( 

72 decompress_file, 

73 raises_chained_assignment_error, 

74 set_timezone, 

75 with_csv_dialect, 

76) 

77from pandas.core.arrays import ( 

78 ArrowExtensionArray, 

79 BaseMaskedArray, 

80 NumpyExtensionArray, 

81) 

82from pandas.core.arrays._mixins import NDArrayBackedExtensionArray 

83from pandas.core.construction import extract_array 

84 

85if TYPE_CHECKING: 

86 from collections.abc import ( 

87 Callable, 

88 Iterable, 

89 ) 

90 from contextlib import AbstractContextManager 

91 

92 from pandas._typing import ( 

93 Dtype, 

94 NpDtype, 

95 ) 

96 

97 from pandas.core.arrays import ExtensionArray 

98 

99 

100UNSIGNED_INT_NUMPY_DTYPES: list[NpDtype] = ["uint8", "uint16", "uint32", "uint64"] 

101UNSIGNED_INT_EA_DTYPES: list[Dtype] = ["UInt8", "UInt16", "UInt32", "UInt64"] 

102SIGNED_INT_NUMPY_DTYPES: list[NpDtype] = [int, "int8", "int16", "int32", "int64"] 

103SIGNED_INT_EA_DTYPES: list[Dtype] = ["Int8", "Int16", "Int32", "Int64"] 

104ALL_INT_NUMPY_DTYPES = UNSIGNED_INT_NUMPY_DTYPES + SIGNED_INT_NUMPY_DTYPES 

105ALL_INT_EA_DTYPES = UNSIGNED_INT_EA_DTYPES + SIGNED_INT_EA_DTYPES 

106ALL_INT_DTYPES: list[Dtype] = [*ALL_INT_NUMPY_DTYPES, *ALL_INT_EA_DTYPES] 

107 

108FLOAT_NUMPY_DTYPES: list[NpDtype] = [float, "float32", "float64"] 

109FLOAT_EA_DTYPES: list[Dtype] = ["Float32", "Float64"] 

110ALL_FLOAT_DTYPES: list[Dtype] = [*FLOAT_NUMPY_DTYPES, *FLOAT_EA_DTYPES] 

111 

112COMPLEX_DTYPES: list[Dtype] = [complex, "complex64", "complex128"] 

113if using_string_dtype(): 

114 STRING_DTYPES: list[Dtype] = ["U"] 

115else: 

116 STRING_DTYPES: list[Dtype] = [str, "str", "U"] # type: ignore[no-redef] 

117COMPLEX_FLOAT_DTYPES: list[Dtype] = [*COMPLEX_DTYPES, *FLOAT_NUMPY_DTYPES] 

118 

119DATETIME64_DTYPES: list[Dtype] = ["datetime64[ns]", "M8[ns]"] 

120TIMEDELTA64_DTYPES: list[Dtype] = ["timedelta64[ns]", "m8[ns]"] 

121 

122BOOL_DTYPES: list[Dtype] = [bool, "bool"] 

123BYTES_DTYPES: list[Dtype] = [bytes, "bytes"] 

124OBJECT_DTYPES: list[Dtype] = [object, "object"] 

125 

126ALL_REAL_NUMPY_DTYPES = FLOAT_NUMPY_DTYPES + ALL_INT_NUMPY_DTYPES 

127ALL_REAL_EXTENSION_DTYPES = FLOAT_EA_DTYPES + ALL_INT_EA_DTYPES 

128ALL_REAL_DTYPES: list[Dtype] = [*ALL_REAL_NUMPY_DTYPES, *ALL_REAL_EXTENSION_DTYPES] 

129ALL_NUMERIC_DTYPES: list[Dtype] = [*ALL_REAL_DTYPES, *COMPLEX_DTYPES] 

130 

131ALL_NUMPY_DTYPES = ( 

132 ALL_REAL_NUMPY_DTYPES 

133 + COMPLEX_DTYPES 

134 + STRING_DTYPES 

135 + DATETIME64_DTYPES 

136 + TIMEDELTA64_DTYPES 

137 + BOOL_DTYPES 

138 + OBJECT_DTYPES 

139 + BYTES_DTYPES 

140) 

141 

142NARROW_NP_DTYPES = [ 

143 np.float16, 

144 np.float32, 

145 np.int8, 

146 np.int16, 

147 np.int32, 

148 np.uint8, 

149 np.uint16, 

150 np.uint32, 

151] 

152 

153PYTHON_DATA_TYPES = [ 

154 str, 

155 int, 

156 float, 

157 complex, 

158 list, 

159 tuple, 

160 range, 

161 dict, 

162 set, 

163 frozenset, 

164 bool, 

165 bytes, 

166 bytearray, 

167 memoryview, 

168] 

169 

170ENDIAN = {"little": "<", "big": ">"}[byteorder] 

171 

172NULL_OBJECTS = [None, np.nan, pd.NaT, float("nan"), pd.NA, Decimal("NaN")] 

173NP_NAT_OBJECTS = [ 

174 cls("NaT", unit) 

175 for cls in [np.datetime64, np.timedelta64] 

176 for unit in [ 

177 "Y", 

178 "M", 

179 "W", 

180 "D", 

181 "h", 

182 "m", 

183 "s", 

184 "ms", 

185 "us", 

186 "ns", 

187 "ps", 

188 "fs", 

189 "as", 

190 ] 

191] 

192 

193if HAS_PYARROW: 

194 import pyarrow as pa 

195 

196 UNSIGNED_INT_PYARROW_DTYPES = [pa.uint8(), pa.uint16(), pa.uint32(), pa.uint64()] 

197 SIGNED_INT_PYARROW_DTYPES = [pa.int8(), pa.int16(), pa.int32(), pa.int64()] 

198 ALL_INT_PYARROW_DTYPES = UNSIGNED_INT_PYARROW_DTYPES + SIGNED_INT_PYARROW_DTYPES 

199 ALL_INT_PYARROW_DTYPES_STR_REPR = [ 

200 str(ArrowDtype(typ)) for typ in ALL_INT_PYARROW_DTYPES 

201 ] 

202 

203 # pa.float16 doesn't seem supported 

204 # https://github.com/apache/arrow/blob/master/python/pyarrow/src/arrow/python/helpers.cc#L86 

205 FLOAT_PYARROW_DTYPES = [pa.float32(), pa.float64()] 

206 FLOAT_PYARROW_DTYPES_STR_REPR = [ 

207 str(ArrowDtype(typ)) for typ in FLOAT_PYARROW_DTYPES 

208 ] 

209 DECIMAL_PYARROW_DTYPES = [pa.decimal128(7, 3)] 

210 STRING_PYARROW_DTYPES = [pa.string()] 

211 BINARY_PYARROW_DTYPES = [pa.binary()] 

212 

213 TIME_PYARROW_DTYPES = [ 

214 pa.time32("s"), 

215 pa.time32("ms"), 

216 pa.time64("us"), 

217 pa.time64("ns"), 

218 ] 

219 DATE_PYARROW_DTYPES = [pa.date32(), pa.date64()] 

220 DATETIME_PYARROW_DTYPES = [ 

221 pa.timestamp(unit=unit, tz=tz) 

222 for unit in ["s", "ms", "us", "ns"] 

223 for tz in [None, "UTC", "US/Pacific", "US/Eastern"] 

224 ] 

225 TIMEDELTA_PYARROW_DTYPES = [pa.duration(unit) for unit in ["s", "ms", "us", "ns"]] 

226 

227 BOOL_PYARROW_DTYPES = [pa.bool_()] 

228 

229 # TODO: Add container like pyarrow types: 

230 # https://arrow.apache.org/docs/python/api/datatypes.html#factory-functions 

231 ALL_PYARROW_DTYPES = ( 

232 ALL_INT_PYARROW_DTYPES 

233 + FLOAT_PYARROW_DTYPES 

234 + DECIMAL_PYARROW_DTYPES 

235 + STRING_PYARROW_DTYPES 

236 + BINARY_PYARROW_DTYPES 

237 + TIME_PYARROW_DTYPES 

238 + DATE_PYARROW_DTYPES 

239 + DATETIME_PYARROW_DTYPES 

240 + TIMEDELTA_PYARROW_DTYPES 

241 + BOOL_PYARROW_DTYPES 

242 ) 

243 ALL_REAL_PYARROW_DTYPES_STR_REPR = ( 

244 ALL_INT_PYARROW_DTYPES_STR_REPR + FLOAT_PYARROW_DTYPES_STR_REPR 

245 ) 

246else: 

247 FLOAT_PYARROW_DTYPES_STR_REPR = [] 

248 ALL_INT_PYARROW_DTYPES_STR_REPR = [] 

249 ALL_PYARROW_DTYPES = [] 

250 ALL_REAL_PYARROW_DTYPES_STR_REPR = [] 

251 

252ALL_REAL_NULLABLE_DTYPES = ( 

253 FLOAT_NUMPY_DTYPES + ALL_REAL_EXTENSION_DTYPES + ALL_REAL_PYARROW_DTYPES_STR_REPR 

254) 

255 

256arithmetic_dunder_methods = [ 

257 "__add__", 

258 "__radd__", 

259 "__sub__", 

260 "__rsub__", 

261 "__mul__", 

262 "__rmul__", 

263 "__floordiv__", 

264 "__rfloordiv__", 

265 "__truediv__", 

266 "__rtruediv__", 

267 "__pow__", 

268 "__rpow__", 

269 "__mod__", 

270 "__rmod__", 

271] 

272 

273comparison_dunder_methods = ["__eq__", "__ne__", "__le__", "__lt__", "__ge__", "__gt__"] 

274 

275comparison_ops = [ 

276 operator.eq, 

277 operator.ne, 

278 operator.gt, 

279 operator.ge, 

280 operator.lt, 

281 operator.le, 

282] 

283 

284numeric_reductions = [ 

285 "count", 

286 "sum", 

287 "max", 

288 "min", 

289 "mean", 

290 "prod", 

291 "std", 

292 "var", 

293 "median", 

294 "kurt", 

295 "skew", 

296 "sem", 

297] 

298 

299boolean_reductions = ["all", "any"] 

300 

301all_reductions = numeric_reductions + boolean_reductions 

302 

303numeric_accumulations = ["cumsum", "cumprod", "cummin", "cummax"] 

304 

305 

306# ----------------------------------------------------------------------------- 

307# Comparators 

308 

309 

310def box_expected(expected: Any, box_cls: Any, transpose: bool = True) -> Any: 

311 """ 

312 Helper function to wrap the expected output of a test in a given box_class. 

313 

314 Parameters 

315 ---------- 

316 expected : np.ndarray, Index, Series 

317 box_cls : {Index, Series, DataFrame} 

318 

319 Returns 

320 ------- 

321 subclass of box_cls 

322 """ 

323 if box_cls is pd.array: 

324 if isinstance(expected, RangeIndex): 

325 # pd.array would return an IntegerArray 

326 expected = NumpyExtensionArray(np.asarray(expected._values)) 

327 else: 

328 expected = pd.array(expected, copy=False) 

329 elif box_cls is Index: 

330 expected = Index(expected, copy=False) 

331 elif box_cls is Series: 

332 expected = Series(expected) 

333 elif box_cls is DataFrame: 

334 expected = Series(expected).to_frame() 

335 if transpose: 

336 # for vector operations, we need a DataFrame to be a single-row, 

337 # not a single-column, in order to operate against non-DataFrame 

338 # vectors of the same length. But convert to two rows to avoid 

339 # single-row special cases in datetime arithmetic 

340 expected = expected.T 

341 expected = pd.concat([expected] * 2, ignore_index=True) 

342 elif box_cls is np.ndarray or box_cls is np.array: 

343 expected = np.array(expected) 

344 elif box_cls is to_array: 

345 expected = to_array(expected) 

346 else: 

347 raise NotImplementedError(box_cls) 

348 return expected 

349 

350 

351def to_array(obj: Any) -> ExtensionArray | np.ndarray: 

352 """ 

353 Similar to pd.array, but does not cast numpy dtypes to nullable dtypes. 

354 """ 

355 # temporary implementation until we get pd.array in place 

356 dtype = getattr(obj, "dtype", None) 

357 

358 if dtype is None: 

359 return np.asarray(obj) 

360 

361 return extract_array(obj, extract_numpy=True) 

362 

363 

364class SubclassedSeries(Series): 

365 _metadata = ["testattr", "name"] 

366 

367 @property 

368 def _constructor(self) -> Callable: # type: ignore[override] 

369 # For testing, those properties return a generic callable, and not 

370 # the actual class. In this case that is equivalent, but it is to 

371 # ensure we don't rely on the property returning a class 

372 # See https://github.com/pandas-dev/pandas/pull/46018 and 

373 # https://github.com/pandas-dev/pandas/issues/32638 and linked issues 

374 return lambda *args, **kwargs: SubclassedSeries(*args, **kwargs) 

375 

376 @property 

377 def _constructor_expanddim(self) -> Callable: 

378 return lambda *args, **kwargs: SubclassedDataFrame(*args, **kwargs) 

379 

380 

381class SubclassedDataFrame(DataFrame): 

382 _metadata = ["testattr"] 

383 

384 @property 

385 def _constructor(self) -> Callable: # type: ignore[override] 

386 return lambda *args, **kwargs: SubclassedDataFrame(*args, **kwargs) 

387 

388 # error: Cannot override writeable attribute with read-only property 

389 @property 

390 def _constructor_sliced(self) -> Callable: # type: ignore[override] 

391 return lambda *args, **kwargs: SubclassedSeries(*args, **kwargs) 

392 

393 

394def convert_rows_list_to_csv_str(rows_list: list[str]) -> str: 

395 """ 

396 Convert list of CSV rows to single CSV-formatted string for current OS. 

397 

398 This method is used for creating expected value of to_csv() method. 

399 

400 Parameters 

401 ---------- 

402 rows_list : List[str] 

403 Each element represents the row of csv. 

404 

405 Returns 

406 ------- 

407 str 

408 Expected output of to_csv() in current OS. 

409 """ 

410 sep = os.linesep 

411 return sep.join(rows_list) + sep 

412 

413 

414def external_error_raised( 

415 expected_exception: type[Exception], 

416) -> AbstractContextManager: 

417 """ 

418 Helper function to mark pytest.raises that have an external error message. 

419 

420 Parameters 

421 ---------- 

422 expected_exception : Exception 

423 Expected error to raise. 

424 

425 Returns 

426 ------- 

427 Callable 

428 Regular `pytest.raises` function with `match` equal to `None`. 

429 """ 

430 import pytest 

431 

432 return pytest.raises(expected_exception, match=None) 

433 

434 

435def get_op_from_name(op_name: str) -> Callable: 

436 """ 

437 The operator function for a given op name. 

438 

439 Parameters 

440 ---------- 

441 op_name : str 

442 The op name, in form of "add" or "__add__". 

443 

444 Returns 

445 ------- 

446 function 

447 A function performing the operation. 

448 """ 

449 short_opname = op_name.strip("_") 

450 try: 

451 op = getattr(operator, short_opname) 

452 except AttributeError: 

453 # Assume it is the reverse operator 

454 rop = getattr(operator, short_opname[1:]) 

455 op = lambda x, y: rop(y, x) 

456 

457 return op 

458 

459 

460# ----------------------------------------------------------------------------- 

461# Indexing test helpers 

462 

463 

464def getitem(x: Any) -> Any: 

465 return x 

466 

467 

468def setitem(x: Any) -> Any: 

469 return x 

470 

471 

472def loc(x: Any) -> Any: 

473 return x.loc 

474 

475 

476def iloc(x: Any) -> Any: 

477 return x.iloc 

478 

479 

480def at(x: Any) -> Any: 

481 return x.at 

482 

483 

484def iat(x: Any) -> Any: 

485 return x.iat 

486 

487 

488# ----------------------------------------------------------------------------- 

489 

490_UNITS = ["s", "ms", "us", "ns"] 

491 

492 

493def get_finest_unit(left: str, right: str) -> str: 

494 """ 

495 Find the higher of two datetime64 units. 

496 """ 

497 if _UNITS.index(left) >= _UNITS.index(right): 

498 return left 

499 return right 

500 

501 

502def _pa_buffer_addresses(pa_data: pa.ChunkedArray) -> set[int]: 

503 """ 

504 Addresses of the non-empty buffers backing a pyarrow ChunkedArray. 

505 

506 Zero-length buffers are excluded because distinct empty arrays can be 

507 handed out the same address, which would look like sharing. 

508 """ 

509 return { 

510 buf.address 

511 for chunk in pa_data.iterchunks() 

512 for buf in chunk.buffers() 

513 if buf is not None and buf.size > 0 

514 } 

515 

516 

517def shares_memory(left: Any, right: Any) -> bool: 

518 """ 

519 Pandas-compat for np.shares_memory. 

520 """ 

521 if isinstance(left, np.ndarray) and isinstance(right, np.ndarray): 

522 return np.shares_memory(left, right) 

523 elif isinstance(left, np.ndarray): 

524 # Call with reversed args to get to unpacking logic below. 

525 return shares_memory(right, left) 

526 

527 if isinstance(left, RangeIndex): 

528 return False 

529 if isinstance(left, MultiIndex): 

530 return any(shares_memory(codes, right) for codes in left._codes) 

531 if isinstance(left, (Index, Series)): 

532 if isinstance(right, (Index, Series)): 

533 return shares_memory(left._values, right._values) 

534 return shares_memory(left._values, right) 

535 

536 if isinstance(left, NDArrayBackedExtensionArray): 

537 return shares_memory(left._ndarray, right) 

538 if isinstance(left, pd.core.arrays.SparseArray): 

539 return shares_memory(left.sp_values, right) 

540 if isinstance(left, pd.core.arrays.IntervalArray): 

541 return shares_memory(left._left, right) or shares_memory(left._right, right) 

542 

543 if isinstance(left, ArrowExtensionArray): 

544 if isinstance(right, ArrowExtensionArray): 

545 # https://github.com/pandas-dev/pandas/pull/43930#discussion_r736862669 

546 left_addrs = _pa_buffer_addresses(left._pa_array) 

547 right_addrs = _pa_buffer_addresses(right._pa_array) 

548 return not left_addrs.isdisjoint(right_addrs) 

549 else: 

550 # if we have one ArrowExtensionArray and one other array, assume 

551 # they can only share memory if they share the same numpy buffer 

552 return np.shares_memory(left, right) 

553 

554 if isinstance(left, BaseMaskedArray): 

555 # By convention, we'll say these share memory if they share *either* 

556 # the _data or the _mask 

557 if isinstance(right, BaseMaskedArray): 

558 return shares_memory(left._data, right._data) or shares_memory( 

559 left._mask, right._mask 

560 ) 

561 return shares_memory(left._data, right) or shares_memory(left._mask, right) 

562 

563 if isinstance(left, DataFrame) and len(left._mgr.blocks) == 1: 

564 arr = left._mgr.blocks[0].values 

565 return shares_memory(arr, right) 

566 

567 raise NotImplementedError(type(left), type(right)) 

568 

569 

570def run_multithreaded( 

571 closure: Callable, 

572 max_workers: int, 

573 arguments: Iterable | None = None, 

574 pass_barrier: bool = False, 

575) -> None: 

576 with ThreadPoolExecutor(max_workers=max_workers) as tpe: 

577 if arguments is None: 

578 arguments = [] 

579 else: 

580 arguments = list(arguments) 

581 

582 if pass_barrier: 

583 barrier = threading.Barrier(max_workers) 

584 arguments.append(barrier) 

585 

586 try: 

587 futures = [] 

588 for _ in range(max_workers): 

589 futures.append(tpe.submit(closure, *arguments)) # noqa: PERF401 

590 except RuntimeError as e: 

591 import pytest 

592 

593 pytest.skip( 

594 f"Spawning {max_workers} threads failed with " 

595 f"error {e!r} (likely due to resource limits on the " 

596 "system running the tests)" 

597 ) 

598 finally: 

599 if len(futures) < max_workers and pass_barrier: 

600 barrier.abort() 

601 for f in futures: 

602 f.result() 

603 

604 

605__all__ = [ 

606 "ALL_INT_EA_DTYPES", 

607 "ALL_INT_NUMPY_DTYPES", 

608 "ALL_NUMPY_DTYPES", 

609 "ALL_REAL_NUMPY_DTYPES", 

610 "BOOL_DTYPES", 

611 "BYTES_DTYPES", 

612 "COMPLEX_DTYPES", 

613 "DATETIME64_DTYPES", 

614 "ENDIAN", 

615 "FLOAT_EA_DTYPES", 

616 "FLOAT_NUMPY_DTYPES", 

617 "NARROW_NP_DTYPES", 

618 "NP_NAT_OBJECTS", 

619 "NULL_OBJECTS", 

620 "OBJECT_DTYPES", 

621 "SIGNED_INT_EA_DTYPES", 

622 "SIGNED_INT_NUMPY_DTYPES", 

623 "STRING_DTYPES", 

624 "TIMEDELTA64_DTYPES", 

625 "UNSIGNED_INT_EA_DTYPES", 

626 "UNSIGNED_INT_NUMPY_DTYPES", 

627 "SubclassedDataFrame", 

628 "SubclassedSeries", 

629 "assert_almost_equal", 

630 "assert_attr_equal", 

631 "assert_categorical_equal", 

632 "assert_class_equal", 

633 "assert_contains_all", 

634 "assert_copy", 

635 "assert_datetime_array_equal", 

636 "assert_dict_equal", 

637 "assert_equal", 

638 "assert_extension_array_equal", 

639 "assert_frame_equal", 

640 "assert_index_equal", 

641 "assert_indexing_slices_equivalent", 

642 "assert_interval_array_equal", 

643 "assert_is_sorted", 

644 "assert_metadata_equivalent", 

645 "assert_numpy_array_equal", 

646 "assert_period_array_equal", 

647 "assert_produces_warning", 

648 "assert_series_equal", 

649 "assert_sp_array_equal", 

650 "assert_timedelta_array_equal", 

651 "at", 

652 "box_expected", 

653 "can_set_locale", 

654 "convert_rows_list_to_csv_str", 

655 "decompress_file", 

656 "external_error_raised", 

657 "get_dtype", 

658 "get_finest_unit", 

659 "get_locales", 

660 "get_obj", 

661 "get_op_from_name", 

662 "getitem", 

663 "iat", 

664 "iloc", 

665 "loc", 

666 "maybe_produces_warning", 

667 "raise_assert_detail", 

668 "raises_chained_assignment_error", 

669 "round_trip_pathlib", 

670 "round_trip_pickle", 

671 "run_multithreaded", 

672 "set_locale", 

673 "set_timezone", 

674 "setitem", 

675 "shares_memory", 

676 "to_array", 

677 "with_csv_dialect", 

678 "write_to_compressed", 

679]