Skip to content

Commit 8239eff

Browse files
authored
Merge branch 'main' into fix-json-normalize-validation
2 parents 1e484c4 + a885d67 commit 8239eff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+625
-735
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,13 @@ Other API changes
750750
- :class:`Series` "flex" methods like :meth:`Series.add` no longer allow passing a :class:`DataFrame` for ``other``; use the DataFrame reversed method instead (:issue:`46179`)
751751
- :meth:`CategoricalIndex.append` no longer attempts to cast different-dtype indexes to the caller's dtype (:issue:`41626`)
752752
- :meth:`ExtensionDtype.construct_array_type` is now a regular method instead of a ``classmethod`` (:issue:`58663`)
753+
- Arithmetic operations between a :class:`Series`, :class:`Index`, or :class:`ExtensionArray` with a ``list`` now consistently wrap that list with an array equivalent to ``Series(my_list).array``. To do any other kind of type inference or casting, do so explicitly before operating (:issue:`62552`)
753754
- Comparison operations between :class:`Index` and :class:`Series` now consistently return :class:`Series` regardless of which object is on the left or right (:issue:`36759`)
754755
- Numpy functions like ``np.isinf`` that return a bool dtype when called on a :class:`Index` object now return a bool-dtype :class:`Index` instead of ``np.ndarray`` (:issue:`52676`)
756+
- Methods that can operate in-place (:meth:`~DataFrame.replace`, :meth:`~DataFrame.fillna`,
757+
:meth:`~DataFrame.ffill`, :meth:`~DataFrame.bfill`, :meth:`~DataFrame.interpolate`,
758+
:meth:`~DataFrame.where`, :meth:`~DataFrame.mask`, :meth:`~DataFrame.clip`) now return
759+
the modified DataFrame or Series (``self``) instead of ``None`` when ``inplace=True`` (:issue:`63207`)
755760

756761
.. ---------------------------------------------------------------------------
757762
.. _whatsnew_300.deprecations:

pandas/_libs/internals.pyi

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,3 @@ class BlockValuesRefs:
9494
def add_reference(self, blk: Block) -> None: ...
9595
def add_index_reference(self, index: Index) -> None: ...
9696
def has_reference(self) -> bool: ...
97-
98-
class SetitemMixin:
99-
def __setitem__(self, key, value) -> None: ...
100-
def __delitem__(self, key) -> None: ...

pandas/_libs/internals.pyx

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
from collections import defaultdict
2-
import sys
3-
import warnings
42

53
cimport cython
6-
from cpython cimport PY_VERSION_HEX
74
from cpython.object cimport PyObject
85
from cpython.pyport cimport PY_SSIZE_T_MAX
96
from cpython.slice cimport PySlice_GetIndicesEx
@@ -23,9 +20,6 @@ from numpy cimport (
2320
cnp.import_array()
2421

2522
from pandas._libs.algos import ensure_int64
26-
from pandas.compat import CHAINED_WARNING_DISABLED
27-
from pandas.errors import ChainedAssignmentError
28-
from pandas.errors.cow import _chained_assignment_msg
2923

3024
from pandas._libs.util cimport (
3125
is_array,
@@ -1002,47 +996,3 @@ cdef class BlockValuesRefs:
1002996
return self._has_reference_maybe_locked()
1003997
ELSE:
1004998
return self._has_reference_maybe_locked()
1005-
1006-
1007-
cdef extern from "Python.h":
1008-
"""
1009-
// python version < 3.14
1010-
#if PY_VERSION_HEX < 0x030E0000
1011-
// This function is unused and is declared to avoid a build warning
1012-
int __Pyx_PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *ref) {
1013-
return Py_REFCNT(ref) == 1;
1014-
}
1015-
#else
1016-
#define __Pyx_PyUnstable_Object_IsUniqueReferencedTemporary \
1017-
PyUnstable_Object_IsUniqueReferencedTemporary
1018-
#endif
1019-
"""
1020-
int PyUnstable_Object_IsUniqueReferencedTemporary\
1021-
"__Pyx_PyUnstable_Object_IsUniqueReferencedTemporary"(object o) except -1
1022-
1023-
1024-
# Python version compatibility for PyUnstable_Object_IsUniqueReferencedTemporary
1025-
cdef inline bint _is_unique_referenced_temporary(object obj) except -1:
1026-
if PY_VERSION_HEX >= 0x030E0000:
1027-
# Python 3.14+ has PyUnstable_Object_IsUniqueReferencedTemporary
1028-
return PyUnstable_Object_IsUniqueReferencedTemporary(obj)
1029-
else:
1030-
# Fallback for older Python versions using sys.getrefcount
1031-
return sys.getrefcount(obj) <= 1
1032-
1033-
1034-
cdef class SetitemMixin:
1035-
# class used in DataFrame and Series for checking for chained assignment
1036-
1037-
def __setitem__(self, key, value) -> None:
1038-
cdef bint is_unique = 0
1039-
if not CHAINED_WARNING_DISABLED:
1040-
is_unique = _is_unique_referenced_temporary(self)
1041-
if is_unique:
1042-
warnings.warn(
1043-
_chained_assignment_msg, ChainedAssignmentError, stacklevel=1
1044-
)
1045-
self._setitem(key, value)
1046-
1047-
def __delitem__(self, key) -> None:
1048-
self._delitem(key)

pandas/_libs/tslibs/timedeltas.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ from pandas._libs.tslibs.offsets import Day
8383

8484
from pandas._libs.tslibs.util cimport (
8585
is_array,
86+
is_bool_object,
8687
is_float_object,
8788
is_integer_object,
8889
)
@@ -2311,6 +2312,13 @@ class Timedelta(_Timedelta):
23112312
return self.__mul__(item)
23122313
return other * self.to_timedelta64()
23132314

2315+
elif is_bool_object(other):
2316+
# GH#62316
2317+
raise TypeError(
2318+
"Cannot multiply Timedelta by bool. "
2319+
"Explicitly cast to integer instead."
2320+
)
2321+
23142322
return NotImplemented
23152323

23162324
__rmul__ = __mul__

pandas/_testing/contexts.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@
1212
)
1313
import uuid
1414

15-
from pandas.compat import (
16-
CHAINED_WARNING_DISABLED,
17-
CHAINED_WARNING_DISABLED_INPLACE_METHOD,
18-
)
15+
from pandas.compat import CHAINED_WARNING_DISABLED
1916
from pandas.errors import ChainedAssignmentError
2017

2118
from pandas.io.common import get_handle
@@ -163,18 +160,10 @@ def with_csv_dialect(name: str, **kwargs) -> Generator[None]:
163160
csv.unregister_dialect(name)
164161

165162

166-
def raises_chained_assignment_error(
167-
extra_warnings=(), extra_match=(), inplace_method=False
168-
):
163+
def raises_chained_assignment_error(extra_warnings=(), extra_match=()):
169164
from pandas._testing import assert_produces_warning
170165

171-
WARNING_DISABLED = (
172-
CHAINED_WARNING_DISABLED_INPLACE_METHOD
173-
if inplace_method
174-
else CHAINED_WARNING_DISABLED
175-
)
176-
177-
if WARNING_DISABLED:
166+
if CHAINED_WARNING_DISABLED:
178167
if not extra_warnings:
179168
from contextlib import nullcontext
180169

pandas/compat/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
from pandas.compat._constants import (
1919
CHAINED_WARNING_DISABLED,
20-
CHAINED_WARNING_DISABLED_INPLACE_METHOD,
2120
IS64,
2221
ISMUSL,
2322
PY312,
@@ -154,7 +153,6 @@ def is_ci_environment() -> bool:
154153

155154
__all__ = [
156155
"CHAINED_WARNING_DISABLED",
157-
"CHAINED_WARNING_DISABLED_INPLACE_METHOD",
158156
"HAS_PYARROW",
159157
"IS64",
160158
"ISMUSL",

pandas/compat/_constants.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
PYPY = platform.python_implementation() == "PyPy"
1919
WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"])
2020
ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "")
21-
REF_COUNT = 2
22-
CHAINED_WARNING_DISABLED = PYPY or (PY314 and not sys._is_gil_enabled()) # type: ignore[attr-defined]
23-
CHAINED_WARNING_DISABLED_INPLACE_METHOD = PYPY or PY314
21+
# the refcount for self in a chained __setitem__/.(i)loc indexing/method call
22+
REF_COUNT = 2 if PY314 else 3
23+
REF_COUNT_IDX = 2
24+
REF_COUNT_METHOD = 1 if PY314 else 2
25+
CHAINED_WARNING_DISABLED = PYPY
2426

2527

2628
__all__ = [

pandas/compat/pickle_compat.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
PeriodArray,
2323
TimedeltaArray,
2424
)
25-
from pandas.core.generic import NDFrame
2625
from pandas.core.internals import BlockManager
2726

2827
if TYPE_CHECKING:
@@ -91,10 +90,6 @@ def load_reduce(self) -> None:
9190
cls = args[0]
9291
stack[-1] = NDArrayBacked.__new__(*args)
9392
return
94-
elif args and issubclass(args[0], NDFrame):
95-
cls = args[0]
96-
stack[-1] = cls.__new__(cls)
97-
return
9893
raise
9994

10095
dispatch[pickle.REDUCE[0]] = load_reduce # type: ignore[assignment]

pandas/core/arrays/masked.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,9 @@ def _maybe_mask_result(
10171017

10181018
return IntegerArray(result, mask, copy=False)
10191019

1020+
elif result.dtype == object:
1021+
result[mask] = self.dtype.na_value
1022+
return result
10201023
else:
10211024
result[mask] = np.nan
10221025
return result

pandas/core/common.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import contextlib
2323
from functools import partial
2424
import inspect
25+
import sys
2526
from typing import (
2627
TYPE_CHECKING,
2728
Any,
@@ -650,3 +651,29 @@ def fill_missing_names(names: Sequence[Hashable | None]) -> list[Hashable]:
650651
list of column names with the None values replaced.
651652
"""
652653
return [f"level_{i}" if name is None else name for i, name in enumerate(names)]
654+
655+
656+
def is_local_in_caller_frame(obj):
657+
"""
658+
Helper function used in detecting chained assignment.
659+
660+
If the pandas object (DataFrame/Series) is a local variable
661+
in the caller's frame, it should not be a case of chained
662+
assignment or method call.
663+
664+
For example:
665+
666+
def test():
667+
df = pd.DataFrame(...)
668+
df["a"] = 1 # not chained assignment
669+
670+
Inside ``df.__setitem__``, we call this function to check whether `df`
671+
(`self`) is a local variable in `test` frame (the frame calling setitem). If
672+
so, we know it is not a case of chained assignment (even when the refcount
673+
of `df` is below the threshold due to optimization of local variables).
674+
"""
675+
frame = sys._getframe(2)
676+
for v in frame.f_locals.values():
677+
if v is obj:
678+
return True
679+
return False

0 commit comments

Comments
 (0)