Skip to content

Commit 3124ef0

Browse files
authored
Merge branch 'main' into autofilter-feature
2 parents ea914ef + 5641979 commit 3124ef0

File tree

18 files changed

+432
-147
lines changed

18 files changed

+432
-147
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ Other Deprecations
807807
- Deprecated option "future.no_silent_downcasting", as it is no longer used. In a future version accessing this option will raise (:issue:`59502`)
808808
- Deprecated passing non-Index types to :meth:`Index.join`; explicitly convert to Index first (:issue:`62897`)
809809
- Deprecated silent casting of non-datetime 'other' to datetime in :meth:`Series.combine_first` (:issue:`62931`)
810+
- Deprecated silently casting strings to :class:`Timedelta` in binary operations with :class:`Timedelta` (:issue:`59653`)
810811
- Deprecated slicing on a :class:`Series` or :class:`DataFrame` with a :class:`DatetimeIndex` using a ``datetime.date`` object, explicitly cast to :class:`Timestamp` instead (:issue:`35830`)
811812
- Deprecated support for the Dataframe Interchange Protocol (:issue:`56732`)
812813
- Deprecated the 'inplace' keyword from :meth:`Resampler.interpolate`, as passing ``True`` raises ``AttributeError`` (:issue:`58690`)

pandas/_libs/tslibs/timedeltas.pyx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ def _binary_op_method_timedeltalike(op, name):
798798
return NotImplemented
799799

800800
try:
801-
other = Timedelta(other)
801+
other = _wrapped_to_timedelta(other)
802802
except ValueError:
803803
# failed to parse as timedelta
804804
return NotImplemented
@@ -2341,7 +2341,7 @@ class Timedelta(_Timedelta):
23412341
def __truediv__(self, other):
23422342
if _should_cast_to_timedelta(other):
23432343
# We interpret NaT as timedelta64("NaT")
2344-
other = Timedelta(other)
2344+
other = _wrapped_to_timedelta(other)
23452345
if other is NaT:
23462346
return np.nan
23472347
if other._creso != self._creso:
@@ -2374,7 +2374,7 @@ class Timedelta(_Timedelta):
23742374
def __rtruediv__(self, other):
23752375
if _should_cast_to_timedelta(other):
23762376
# We interpret NaT as timedelta64("NaT")
2377-
other = Timedelta(other)
2377+
other = _wrapped_to_timedelta(other)
23782378
if other is NaT:
23792379
return np.nan
23802380
if self._creso != other._creso:
@@ -2402,7 +2402,7 @@ class Timedelta(_Timedelta):
24022402
# just defer
24032403
if _should_cast_to_timedelta(other):
24042404
# We interpret NaT as timedelta64("NaT")
2405-
other = Timedelta(other)
2405+
other = _wrapped_to_timedelta(other)
24062406
if other is NaT:
24072407
return np.nan
24082408
if self._creso != other._creso:
@@ -2457,7 +2457,7 @@ class Timedelta(_Timedelta):
24572457
# just defer
24582458
if _should_cast_to_timedelta(other):
24592459
# We interpret NaT as timedelta64("NaT")
2460-
other = Timedelta(other)
2460+
other = _wrapped_to_timedelta(other)
24612461
if other is NaT:
24622462
return np.nan
24632463
if self._creso != other._creso:
@@ -2525,6 +2525,7 @@ def truediv_object_array(ndarray left, ndarray right):
25252525
if cnp.get_timedelta64_value(td64) == NPY_NAT:
25262526
# td here should be interpreted as a td64 NaT
25272527
if _should_cast_to_timedelta(obj):
2528+
_wrapped_to_timedelta(obj) # deprecate if allowing string
25282529
res_value = np.nan
25292530
else:
25302531
# if its a number then let numpy handle division, otherwise
@@ -2554,6 +2555,7 @@ def floordiv_object_array(ndarray left, ndarray right):
25542555
if cnp.get_timedelta64_value(td64) == NPY_NAT:
25552556
# td here should be interpreted as a td64 NaT
25562557
if _should_cast_to_timedelta(obj):
2558+
_wrapped_to_timedelta(obj) # deprecate allowing string
25572559
res_value = np.nan
25582560
else:
25592561
# if its a number then let numpy handle division, otherwise
@@ -2585,6 +2587,23 @@ cdef bint is_any_td_scalar(object obj):
25852587
)
25862588

25872589

2590+
cdef inline _wrapped_to_timedelta(object other):
2591+
# Helper for deprecating cases where we cast str to Timedelta
2592+
td = Timedelta(other)
2593+
if isinstance(other, str):
2594+
from pandas.errors import Pandas4Warning
2595+
warnings.warn(
2596+
# GH#59653
2597+
"Scalar operations between Timedelta and string are "
2598+
"deprecated and will raise in a future version. "
2599+
"Explicitly cast to Timedelta first.",
2600+
Pandas4Warning,
2601+
stacklevel=find_stack_level(),
2602+
)
2603+
# When this is enforced, remove str from _should_cast_to_timedelta
2604+
return td
2605+
2606+
25882607
cdef bint _should_cast_to_timedelta(object obj):
25892608
"""
25902609
Should we treat this object as a Timedelta for the purpose of a binary op

pandas/conftest.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ def index(request):
722722
- ...
723723
"""
724724
# copy to avoid mutation, e.g. setting .name
725-
return indices_dict[request.param].copy()
725+
return indices_dict[request.param].copy(deep=False)
726726

727727

728728
@pytest.fixture(
@@ -735,7 +735,7 @@ def index_flat(request):
735735
index fixture, but excluding MultiIndex cases.
736736
"""
737737
key = request.param
738-
return indices_dict[key].copy()
738+
return indices_dict[key].copy(deep=False)
739739

740740

741741
@pytest.fixture(
@@ -758,18 +758,15 @@ def index_with_missing(request):
758758
759759
MultiIndex is excluded because isna() is not defined for MultiIndex.
760760
"""
761-
762-
# GH 35538. Use deep copy to avoid illusive bug on np-dev
763-
# GHA pipeline that writes into indices_dict despite copy
764-
ind = indices_dict[request.param].copy(deep=True)
765-
vals = ind.values.copy()
761+
ind = indices_dict[request.param]
766762
if request.param in ["tuples", "mi-with-dt64tz-level", "multi"]:
767763
# For setting missing values in the top level of MultiIndex
768764
vals = ind.tolist()
769765
vals[0] = (None,) + vals[0][1:]
770766
vals[-1] = (None,) + vals[-1][1:]
771767
return MultiIndex.from_tuples(vals)
772768
else:
769+
vals = ind.values.copy()
773770
vals[0] = None
774771
vals[-1] = None
775772
return type(ind)(vals)
@@ -850,7 +847,7 @@ def index_or_series_obj(request):
850847
Fixture for tests on indexes, series and series with a narrow dtype
851848
copy to avoid mutation, e.g. setting .name
852849
"""
853-
return _index_or_series_objs[request.param].copy(deep=True)
850+
return _index_or_series_objs[request.param].copy(deep=False)
854851

855852

856853
_typ_objects_series = {
@@ -873,7 +870,7 @@ def index_or_series_memory_obj(request):
873870
series with empty objects type
874871
copy to avoid mutation, e.g. setting .name
875872
"""
876-
return _index_or_series_memory_objs[request.param].copy(deep=True)
873+
return _index_or_series_memory_objs[request.param].copy(deep=False)
877874

878875

879876
# ----------------------------------------------------------------

pandas/core/frame.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,12 @@
341341
of the left keys.
342342
* left_anti: use only keys from left frame that are not in right frame, similar
343343
to SQL left anti join; preserve key order.
344+
345+
.. versionadded:: 3.0
344346
* right_anti: use only keys from right frame that are not in left frame, similar
345347
to SQL right anti join; preserve key order.
348+
349+
.. versionadded:: 3.0
346350
on : Hashable or a sequence of the previous
347351
Column or index level names to join on. These must be found in both
348352
DataFrames. If `on` is None and not merging on indexes then this defaults
@@ -2927,6 +2931,7 @@ def to_parquet(
29272931
index: bool | None = ...,
29282932
partition_cols: list[str] | None = ...,
29292933
storage_options: StorageOptions = ...,
2934+
filesystem: Any = ...,
29302935
**kwargs,
29312936
) -> bytes: ...
29322937

@@ -2940,6 +2945,7 @@ def to_parquet(
29402945
index: bool | None = ...,
29412946
partition_cols: list[str] | None = ...,
29422947
storage_options: StorageOptions = ...,
2948+
filesystem: Any = ...,
29432949
**kwargs,
29442950
) -> None: ...
29452951

@@ -2953,6 +2959,7 @@ def to_parquet(
29532959
index: bool | None = None,
29542960
partition_cols: list[str] | None = None,
29552961
storage_options: StorageOptions | None = None,
2962+
filesystem: Any = None,
29562963
**kwargs,
29572964
) -> bytes | None:
29582965
"""
@@ -2992,6 +2999,12 @@ def to_parquet(
29922999
Must be None if path is not a string.
29933000
{storage_options}
29943001
3002+
filesystem : fsspec or pyarrow filesystem, default None
3003+
Filesystem object to use when reading the parquet file. Only implemented
3004+
for ``engine="pyarrow"``.
3005+
3006+
.. versionadded:: 2.1.0
3007+
29953008
**kwargs
29963009
Additional arguments passed to the parquet library. See
29973010
:ref:`pandas io <io.parquet>` for more details.
@@ -3053,6 +3066,7 @@ def to_parquet(
30533066
index=index,
30543067
partition_cols=partition_cols,
30553068
storage_options=storage_options,
3069+
filesystem=filesystem,
30563070
**kwargs,
30573071
)
30583072

0 commit comments

Comments
 (0)