Skip to content

Commit fd6de1d

Browse files
Merge remote-tracking branch 'upstream/main' into api-copy-index-subclasses
2 parents 02af4fb + 0e978b6 commit fd6de1d

37 files changed

+392
-143
lines changed

.github/workflows/unit-tests.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
# Prevent the include jobs from overriding other jobs
3232
pattern: [""]
3333
pandas_future_infer_string: ["1"]
34+
pandas_future_python_scalars: ["0"]
3435
include:
3536
- name: "Downstream Compat"
3637
env_file: actions-313-downstream_compat.yaml
@@ -75,6 +76,10 @@ jobs:
7576
env_file: actions-313.yaml
7677
pandas_future_infer_string: "0"
7778
platform: ubuntu-24.04
79+
- name: "PANDAS_FUTURE_PYTHON_SCALARS=1"
80+
env_file: actions-313.yaml
81+
pandas_future_python_scalars: "1"
82+
platform: ubuntu-24.04
7883
- name: "Numpy Dev"
7984
env_file: actions-313-numpydev.yaml
8085
pattern: "not slow and not network and not single_cpu"
@@ -92,6 +97,7 @@ jobs:
9297
LC_ALL: ${{ matrix.lc_all || '' }}
9398
PANDAS_CI: '1'
9499
PANDAS_FUTURE_INFER_STRING: ${{ matrix.pandas_future_infer_string || '1' }}
100+
PANDAS_FUTURE_PYTHON_SCALARS: ${{ matrix.pandas_future_python_scalars || '0' }}
95101
TEST_ARGS: ${{ matrix.test_args || '' }}
96102
PYTEST_WORKERS: 'auto'
97103
PYTEST_TARGET: ${{ matrix.pytest_target || 'pandas' }}

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,7 @@ Timedelta
11611161
- Accuracy improvement in :meth:`Timedelta.to_pytimedelta` to round microseconds consistently for large nanosecond based Timedelta (:issue:`57841`)
11621162
- Bug in :class:`Timedelta` constructor failing to raise when passed an invalid keyword (:issue:`53801`)
11631163
- Bug in :meth:`DataFrame.cumsum` which was raising ``IndexError`` if dtype is ``timedelta64[ns]`` (:issue:`57956`)
1164+
- Bug in adding or subtracting a :class:`Timedelta` object with non-nanosecond unit to a python ``datetime.datetime`` object giving incorrect results; this now works correctly for Timedeltas inside the ``datetime.timedelta`` implementation bounds (:issue:`53643`)
11641165
- Bug in multiplication operations with ``timedelta64`` dtype failing to raise ``TypeError`` when multiplying by ``bool`` objects or dtypes (:issue:`58054`)
11651166
- Bug in multiplication operations with ``timedelta64`` dtype incorrectly raising when multiplying by numpy-nullable dtypes or pyarrow integer dtypes (:issue:`58054`)
11661167

pandas/_config/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ def using_string_dtype() -> bool:
3535
return _mode_options["infer_string"]
3636

3737

38+
def using_python_scalars() -> bool:
39+
_mode_options = _global_config["future"]
40+
return _mode_options["python_scalars"]
41+
42+
3843
def is_nan_na() -> bool:
3944
_mode_options = _global_config["future"]
4045
return not _mode_options["distinguish_nan_and_na"]

pandas/_libs/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ foreach ext_name, ext_dict : libs_sources
160160
ext_dict.get('sources'),
161161
cython_args: cython_args,
162162
include_directories: [inc_np, inc_pd],
163-
dependencies: ext_dict.get('deps', ''),
163+
dependencies: ext_dict.get('deps', []),
164164
subdir: 'pandas/_libs',
165165
install: true,
166166
)

pandas/_libs/tslibs/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ foreach ext_name, ext_dict : tslibs_sources
4040
ext_dict.get('sources'),
4141
cython_args: cython_args,
4242
include_directories: [inc_np, inc_pd],
43-
dependencies: ext_dict.get('deps', ''),
43+
dependencies: ext_dict.get('deps', []),
4444
subdir: 'pandas/_libs/tslibs',
4545
install: true,
4646
)

pandas/_libs/tslibs/timedeltas.pyx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,9 +1022,23 @@ cdef _timedelta_from_value_and_reso(cls, int64_t value, NPY_DATETIMEUNIT reso):
10221022
elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
10231023
td_base = _Timedelta.__new__(cls, microseconds=int(value))
10241024
elif reso == NPY_DATETIMEUNIT.NPY_FR_ms:
1025-
td_base = _Timedelta.__new__(cls, milliseconds=0)
1025+
if -86_399_999_913_600_000 <= value < 86_400_000_000_000_000:
1026+
# i.e. we are in range for pytimedelta. By passing the
1027+
# 'correct' value here we can
1028+
# make pydatetime + Timedelta operations work correctly,
1029+
# xref GH#53643
1030+
td_base = _Timedelta.__new__(cls, milliseconds=value)
1031+
else:
1032+
td_base = _Timedelta.__new__(cls, milliseconds=0)
10261033
elif reso == NPY_DATETIMEUNIT.NPY_FR_s:
1027-
td_base = _Timedelta.__new__(cls, seconds=0)
1034+
if -86_399_999_913_600 <= value < 86_400_000_000_000:
1035+
# i.e. we are in range for pytimedelta. By passing the
1036+
# 'correct' value here we can
1037+
# make pydatetime + Timedelta operations work correctly,
1038+
# xref GH#53643
1039+
td_base = _Timedelta.__new__(cls, seconds=value)
1040+
else:
1041+
td_base = _Timedelta.__new__(cls, seconds=0)
10281042
# Other resolutions are disabled but could potentially be implemented here:
10291043
# elif reso == NPY_DATETIMEUNIT.NPY_FR_m:
10301044
# td_base = _Timedelta.__new__(Timedelta, minutes=int(value))

pandas/conftest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,6 +2094,11 @@ def using_infer_string() -> bool:
20942094
return pd.options.future.infer_string is True
20952095

20962096

2097+
@pytest.fixture
2098+
def using_python_scalars() -> bool:
2099+
return pd.options.future.python_scalars is True
2100+
2101+
20972102
_warsaws: list[Any] = ["Europe/Warsaw", "dateutil/Europe/Warsaw"]
20982103
if pytz is not None:
20992104
_warsaws.append(pytz.timezone("Europe/Warsaw"))

pandas/core/arraylike.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from pandas._libs import lib
1616
from pandas._libs.ops_dispatch import maybe_dispatch_ufunc_to_dunder_op
1717

18+
from pandas.core.dtypes.cast import maybe_unbox_numpy_scalar
1819
from pandas.core.dtypes.generic import ABCNDFrame
1920

2021
from pandas.core import roperator
@@ -529,4 +530,6 @@ def dispatch_reduction_ufunc(self, ufunc: np.ufunc, method: str, *inputs, **kwar
529530

530531
# By default, numpy's reductions do not skip NaNs, so we have to
531532
# pass skipna=False
532-
return getattr(self, method_name)(skipna=False, **kwargs)
533+
result = getattr(self, method_name)(skipna=False, **kwargs)
534+
result = maybe_unbox_numpy_scalar(result)
535+
return result

pandas/core/arrays/masked.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212

1313
import numpy as np
1414

15-
from pandas._config import is_nan_na
15+
from pandas._config import (
16+
is_nan_na,
17+
using_python_scalars,
18+
)
1619

1720
from pandas._libs import (
1821
algos as libalgos,
@@ -28,7 +31,9 @@
2831

2932
from pandas.core.dtypes.astype import astype_is_view
3033
from pandas.core.dtypes.base import ExtensionDtype
31-
from pandas.core.dtypes.cast import maybe_downcast_to_dtype
34+
from pandas.core.dtypes.cast import (
35+
maybe_downcast_to_dtype,
36+
)
3237
from pandas.core.dtypes.common import (
3338
is_bool,
3439
is_integer_dtype,
@@ -1537,7 +1542,10 @@ def _reduce(
15371542
if isna(result):
15381543
return self._wrap_na_result(name=name, axis=0, mask_size=(1,))
15391544
else:
1540-
result = result.reshape(1)
1545+
if using_python_scalars():
1546+
result = np.array([result])
1547+
else:
1548+
result = result.reshape(1)
15411549
mask = np.zeros(1, dtype=bool)
15421550
return self._maybe_mask_result(result, mask)
15431551

pandas/core/common.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
ABCExtensionArray,
4646
ABCIndex,
4747
ABCMultiIndex,
48+
ABCNumpyExtensionArray,
4849
ABCSeries,
4950
)
5051
from pandas.core.dtypes.inference import iterable_not_string
@@ -128,7 +129,8 @@ def is_bool_indexer(key: Any) -> bool:
128129
and convert to an ndarray.
129130
"""
130131
if isinstance(
131-
key, (ABCSeries, np.ndarray, ABCIndex, ABCExtensionArray)
132+
key,
133+
(ABCSeries, np.ndarray, ABCIndex, ABCExtensionArray, ABCNumpyExtensionArray),
132134
) and not isinstance(key, ABCMultiIndex):
133135
if key.dtype == np.object_:
134136
key_array = np.asarray(key)

0 commit comments

Comments
 (0)