From 33487afd0ac19eb37ebab95ec5187d8f324807c6 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 21 Aug 2019 09:41:09 -0700 Subject: [PATCH 1/8] do extract_array earlier in series arith/comparison ops --- pandas/core/ops/__init__.py | 92 +++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 7e03b9544ee72..458e8fe2388e4 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -34,10 +34,11 @@ ABCIndexClass, ABCSeries, ABCSparseSeries, + ABCTimedeltaArray, + ABCTimedeltaIndex, ) from pandas.core.dtypes.missing import isna, notna -import pandas as pd from pandas._typing import ArrayLike from pandas.core.construction import array, extract_array from pandas.core.ops.array_ops import comp_method_OBJECT_ARRAY, define_na_arithmetic_op @@ -157,12 +158,13 @@ def maybe_upcast_for_op(obj, shape: Tuple[int, ...]): if isna(obj): # wrapping timedelta64("NaT") in Timedelta returns NaT, # which would incorrectly be treated as a datetime-NaT, so - # we broadcast and wrap in a Series + # we broadcast and wrap in a TimedeltaArray + obj = obj.astype("timedelta64[ns]") right = np.broadcast_to(obj, shape) - # Note: we use Series instead of TimedeltaIndex to avoid having - # to worry about catching NullFrequencyError. - return pd.Series(right) + from pandas.core.arrays import TimedeltaArray + + return TimedeltaArray(right) # In particular non-nanosecond timedelta64 needs to be cast to # nanoseconds, or else we get undesired behavior like @@ -173,7 +175,9 @@ def maybe_upcast_for_op(obj, shape: Tuple[int, ...]): # GH#22390 Unfortunately we need to special-case right-hand # timedelta64 dtypes because numpy casts integer dtypes to # timedelta64 when operating with timedelta64 - return pd.TimedeltaIndex(obj) + from pandas.core.arrays import TimedeltaArray + + return TimedeltaArray._from_sequence(obj) return obj @@ -520,13 +524,29 @@ def column_op(a, b): return result -def dispatch_to_extension_op(op, left, right): +def dispatch_to_extension_op(op, left, right, keep_null_freq: bool = False): """ Assume that left or right is a Series backed by an ExtensionArray, apply the operator defined by op. + + Parameters + ---------- + op : binary operator + left : ExtensionArray or np.ndarray + right : object + keep_null_freq : bool, default False + Whether to re-raise a NullFrequencyError unchanged, as opposed to + catching and raising TypeError. + + Returns + ------- + ExtensionArray or np.ndarray + 2-tuple of these if op is divmod or rdivmod """ + # NB: left and right should already be unboxed, so neither should be + # a Series or Index. - if left.dtype.kind in "mM": + if left.dtype.kind in "mM" and isinstance(left, np.ndarray): # We need to cast datetime64 and timedelta64 ndarrays to # DatetimeArray/TimedeltaArray. But we avoid wrapping others in # PandasArray as that behaves poorly with e.g. IntegerArray. @@ -535,15 +555,13 @@ def dispatch_to_extension_op(op, left, right): # The op calls will raise TypeError if the op is not defined # on the ExtensionArray - # unbox Series and Index to arrays - new_left = extract_array(left, extract_numpy=True) - new_right = extract_array(right, extract_numpy=True) - try: - res_values = op(new_left, new_right) + res_values = op(left, right) except NullFrequencyError: # DatetimeIndex and TimedeltaIndex with freq == None raise ValueError # on add/sub of integers (or int-like). We re-raise as a TypeError. + if keep_null_freq: + raise raise TypeError( "incompatible type for a datetime/timedelta " "operation [{name}]".format(name=op.__name__) @@ -615,25 +633,29 @@ def wrapper(left, right): if isinstance(right, ABCDataFrame): return NotImplemented + keep_null_freq = isinstance( + right, + (ABCDatetimeIndex, ABCDatetimeArray, ABCTimedeltaIndex, ABCTimedeltaArray), + ) + left, right = _align_method_SERIES(left, right) res_name = get_op_result_name(left, right) - right = maybe_upcast_for_op(right, left.shape) - if should_extension_dispatch(left, right): - result = dispatch_to_extension_op(op, left, right) + lvalues = extract_array(left, extract_numpy=True) + rvalues = extract_array(right, extract_numpy=True) - elif is_timedelta64_dtype(right) or isinstance( - right, (ABCDatetimeArray, ABCDatetimeIndex) - ): - # We should only get here with td64 right with non-scalar values - # for right upcast by maybe_upcast_for_op - assert not isinstance(right, (np.timedelta64, np.ndarray)) - result = op(left._values, right) + rvalues = maybe_upcast_for_op(rvalues, lvalues.shape) - else: - lvalues = extract_array(left, extract_numpy=True) - rvalues = extract_array(right, extract_numpy=True) + if should_extension_dispatch(left, rvalues): + result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq) + elif is_timedelta64_dtype(rvalues) or isinstance(rvalues, ABCDatetimeArray): + # We should only get here with td64 rvalues with non-scalar values + # for rvalues upcast by maybe_upcast_for_op + assert not isinstance(rvalues, (np.timedelta64, np.ndarray)) + result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq) + + else: with np.errstate(all="ignore"): result = na_op(lvalues, rvalues) @@ -708,25 +730,25 @@ def wrapper(self, other, axis=None): if len(self) != len(other): raise ValueError("Lengths must match to compare") - if should_extension_dispatch(self, other): - res_values = dispatch_to_extension_op(op, self, other) + lvalues = extract_array(self, extract_numpy=True) + rvalues = extract_array(other, extract_numpy=True) - elif is_scalar(other) and isna(other): + if should_extension_dispatch(lvalues, rvalues): + res_values = dispatch_to_extension_op(op, lvalues, rvalues) + + elif is_scalar(rvalues) and isna(rvalues): # numpy does not like comparisons vs None if op is operator.ne: - res_values = np.ones(len(self), dtype=bool) + res_values = np.ones(len(lvalues), dtype=bool) else: - res_values = np.zeros(len(self), dtype=bool) + res_values = np.zeros(len(lvalues), dtype=bool) else: - lvalues = extract_array(self, extract_numpy=True) - rvalues = extract_array(other, extract_numpy=True) - with np.errstate(all="ignore"): res_values = na_op(lvalues, rvalues) if is_scalar(res_values): raise TypeError( - "Could not compare {typ} type with Series".format(typ=type(other)) + "Could not compare {typ} type with Series".format(typ=type(rvalues)) ) result = self._constructor(res_values, index=self.index) From edd49dbf4ef039d97a0fc6d9a9ff6e411215d1e3 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 21 Aug 2019 13:35:37 -0700 Subject: [PATCH 2/8] mypy fixup --- pandas/core/ops/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 458e8fe2388e4..abba8cf529fe3 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -149,6 +149,8 @@ def maybe_upcast_for_op(obj, shape: Tuple[int, ...]): Be careful to call this *after* determining the `name` attribute to be attached to the result of the arithmetic operation. """ + from pandas.core.arrays import TimedeltaArray + if type(obj) is datetime.timedelta: # GH#22390 cast up to Timedelta to rely on Timedelta # implementation; otherwise operation against numeric-dtype @@ -161,9 +163,6 @@ def maybe_upcast_for_op(obj, shape: Tuple[int, ...]): # we broadcast and wrap in a TimedeltaArray obj = obj.astype("timedelta64[ns]") right = np.broadcast_to(obj, shape) - - from pandas.core.arrays import TimedeltaArray - return TimedeltaArray(right) # In particular non-nanosecond timedelta64 needs to be cast to @@ -175,8 +174,6 @@ def maybe_upcast_for_op(obj, shape: Tuple[int, ...]): # GH#22390 Unfortunately we need to special-case right-hand # timedelta64 dtypes because numpy casts integer dtypes to # timedelta64 when operating with timedelta64 - from pandas.core.arrays import TimedeltaArray - return TimedeltaArray._from_sequence(obj) return obj From 4423a2ff913002f7f90ef21eb318116225c24272 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Fri, 23 Aug 2019 11:11:49 -0700 Subject: [PATCH 3/8] TST: fix compression tests when run without virtualenv/condaenv (#28051) --- pandas/tests/io/test_compression.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/tests/io/test_compression.py b/pandas/tests/io/test_compression.py index 16ca1109f266c..d68b6a1effaa0 100644 --- a/pandas/tests/io/test_compression.py +++ b/pandas/tests/io/test_compression.py @@ -1,6 +1,7 @@ import contextlib import os import subprocess +import sys import textwrap import warnings @@ -139,7 +140,7 @@ def test_with_missing_lzma(): import pandas """ ) - subprocess.check_output(["python", "-c", code]) + subprocess.check_output([sys.executable, "-c", code]) def test_with_missing_lzma_runtime(): @@ -156,4 +157,4 @@ def test_with_missing_lzma_runtime(): df.to_csv('foo.csv', compression='xz') """ ) - subprocess.check_output(["python", "-c", code]) + subprocess.check_output([sys.executable, "-c", code]) From d88315b4426b3a78bfd249203644a5174970e5c6 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 4 Sep 2019 15:41:54 -0700 Subject: [PATCH 4/8] Fix #28880 --- pandas/_libs/tslibs/__init__.py | 2 +- pandas/_libs/tslibs/c_timestamp.pyx | 6 ++--- pandas/_libs/tslibs/nattype.pyx | 6 +++++ pandas/_libs/tslibs/np_datetime.pyx | 9 ++++++++ pandas/core/ops/__init__.py | 29 +++++++++++++++++-------- pandas/errors/__init__.py | 10 +-------- pandas/tests/arithmetic/test_numeric.py | 28 ++++++++++++++++++++++-- pandas/tests/tslibs/test_api.py | 1 + 8 files changed, 67 insertions(+), 24 deletions(-) diff --git a/pandas/_libs/tslibs/__init__.py b/pandas/_libs/tslibs/__init__.py index 67a323782a836..b5d0ba093d5b4 100644 --- a/pandas/_libs/tslibs/__init__.py +++ b/pandas/_libs/tslibs/__init__.py @@ -2,7 +2,7 @@ from .conversion import localize_pydatetime, normalize_date from .nattype import NaT, NaTType, iNaT, is_null_datetimelike -from .np_datetime import OutOfBoundsDatetime +from .np_datetime import NullFrequencyError, OutOfBoundsDatetime from .period import IncompatibleFrequency, Period from .timedeltas import Timedelta, delta_to_nanoseconds, ints_to_pytimedelta from .timestamps import Timestamp diff --git a/pandas/_libs/tslibs/c_timestamp.pyx b/pandas/_libs/tslibs/c_timestamp.pyx index 906dabba09486..6de288b4d3832 100644 --- a/pandas/_libs/tslibs/c_timestamp.pyx +++ b/pandas/_libs/tslibs/c_timestamp.pyx @@ -33,7 +33,7 @@ from pandas._libs.tslibs.util cimport ( from pandas._libs.tslibs.fields import get_start_end_field, get_date_name_field from pandas._libs.tslibs.nattype cimport c_NaT as NaT -from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime +from pandas._libs.tslibs.np_datetime import NullFrequencyError, OutOfBoundsDatetime from pandas._libs.tslibs.np_datetime cimport ( reverse_ops, cmp_scalar) from pandas._libs.tslibs.timezones cimport ( @@ -227,8 +227,8 @@ cdef class _Timestamp(datetime): # to be compat with Period return NaT elif self.freq is None: - raise ValueError("Cannot add integral value to Timestamp " - "without freq.") + raise NullFrequencyError("Cannot add integral value to Timestamp " + "without freq.") return self.__class__((self.freq * other).apply(self), freq=self.freq) diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index 020d1acf0b4ce..fcf75968c06da 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -150,6 +150,8 @@ cdef class _NaT(datetime): result = np.empty(other.shape, dtype="datetime64[ns]") result.fill("NaT") return result + raise TypeError("Cannot add NaT to ndarray with dtype {dtype}" + .format(dtype=other.dtype)) return NotImplemented @@ -201,6 +203,10 @@ cdef class _NaT(datetime): result.fill("NaT") return result + raise TypeError( + "Cannot subtract NaT from ndarray with dtype {dtype}" + .format(dtype=other.dtype)) + return NotImplemented def __pos__(self): diff --git a/pandas/_libs/tslibs/np_datetime.pyx b/pandas/_libs/tslibs/np_datetime.pyx index 7d362708015ce..7d2c735b860b9 100644 --- a/pandas/_libs/tslibs/np_datetime.pyx +++ b/pandas/_libs/tslibs/np_datetime.pyx @@ -94,6 +94,15 @@ cdef inline bint cmp_scalar(int64_t lhs, int64_t rhs, int op) except -1: return lhs >= rhs +class NullFrequencyError(ValueError): + """ + Error raised when a null `freq` attribute is used in an operation + that needs a non-null frequency, particularly `DatetimeIndex.shift`, + `TimedeltaIndex.shift`, `PeriodIndex.shift`. + """ + pass + + class OutOfBoundsDatetime(ValueError): pass diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index abba8cf529fe3..96a3945bec00e 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -9,7 +9,7 @@ import numpy as np -from pandas._libs import Timedelta, lib, ops as libops +from pandas._libs import Timedelta, Timestamp, lib, ops as libops from pandas.errors import NullFrequencyError from pandas.util._decorators import Appender @@ -149,13 +149,24 @@ def maybe_upcast_for_op(obj, shape: Tuple[int, ...]): Be careful to call this *after* determining the `name` attribute to be attached to the result of the arithmetic operation. """ - from pandas.core.arrays import TimedeltaArray + from pandas.core.arrays import DatetimeArray, TimedeltaArray if type(obj) is datetime.timedelta: # GH#22390 cast up to Timedelta to rely on Timedelta # implementation; otherwise operation against numeric-dtype # raises TypeError return Timedelta(obj) + elif isinstance(obj, np.datetime64): + # GH#28080 numpy casts integer-dtype to datetime64 when doing + # array[int] + datetime64, which we do not allow + if isna(obj): + # Avoid possible ambiguities with pd.NaT + obj = obj.astype("datetime64[ns]") + right = np.broadcast_to(obj, shape) + return DatetimeArray(right) + + return Timestamp(obj) + elif isinstance(obj, np.timedelta64): if isna(obj): # wrapping timedelta64("NaT") in Timedelta returns NaT, @@ -632,7 +643,7 @@ def wrapper(left, right): keep_null_freq = isinstance( right, - (ABCDatetimeIndex, ABCDatetimeArray, ABCTimedeltaIndex, ABCTimedeltaArray), + (ABCDatetimeIndex, ABCDatetimeArray, ABCTimedeltaIndex, ABCTimedeltaArray, Timestamp), ) left, right = _align_method_SERIES(left, right) @@ -643,14 +654,14 @@ def wrapper(left, right): rvalues = maybe_upcast_for_op(rvalues, lvalues.shape) - if should_extension_dispatch(left, rvalues): + if should_extension_dispatch(left, rvalues) or isinstance(rvalues, (ABCTimedeltaArray, ABCDatetimeArray, Timestamp)): result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq) - elif is_timedelta64_dtype(rvalues) or isinstance(rvalues, ABCDatetimeArray): - # We should only get here with td64 rvalues with non-scalar values - # for rvalues upcast by maybe_upcast_for_op - assert not isinstance(rvalues, (np.timedelta64, np.ndarray)) - result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq) + #elif isinstance(rvalues, (ABCTimedeltaArray, ABCDatetimeArray, Timestamp)): + # # We should only get here with td64 rvalues with non-scalar values + # # for rvalues upcast by maybe_upcast_for_op + # assert not isinstance(rvalues, (np.timedelta64, np.ndarray)) + # result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq) else: with np.errstate(all="ignore"): diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index 3177937ac4ba1..a85fc8bfb1414 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -4,7 +4,7 @@ Expose public exceptions & warnings """ -from pandas._libs.tslibs import OutOfBoundsDatetime +from pandas._libs.tslibs import NullFrequencyError, OutOfBoundsDatetime class PerformanceWarning(Warning): @@ -157,14 +157,6 @@ class MergeError(ValueError): """ -class NullFrequencyError(ValueError): - """ - Error raised when a null `freq` attribute is used in an operation - that needs a non-null frequency, particularly `DatetimeIndex.shift`, - `TimedeltaIndex.shift`, `PeriodIndex.shift`. - """ - - class AccessorRegistrationWarning(Warning): """Warning for attribute conflicts in accessor registration.""" diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index d686d9f90a5a4..2d4efae6a1660 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -73,10 +73,10 @@ def test_compare_invalid(self): # ------------------------------------------------------------------ -# Numeric dtypes Arithmetic with Timedelta Scalar +# Numeric dtypes Arithmetic with Datetime/Timedelta Scalar -class TestNumericArraylikeArithmeticWithTimedeltaLike: +class TestNumericArraylikeArithmeticWithDatetimeLike: # TODO: also check name retentention @pytest.mark.parametrize("box_cls", [np.array, pd.Index, pd.Series]) @@ -235,6 +235,30 @@ def test_add_sub_timedeltalike_invalid(self, numeric_idx, other, box): with pytest.raises(TypeError): other - left + @pytest.mark.parametrize( + "other", + [ + pd.Timestamp.now().to_pydatetime(), + pd.Timestamp.now(tz="UTC").to_pydatetime(), + pd.Timestamp.now().to_datetime64(), + pd.NaT, + ], + ) + @pytest.mark.filterwarnings("ignore:elementwise comp:DeprecationWarning") + def test_add_sub_timedeltalike_invalid(self, numeric_idx, other, box): + # GH#28080 numeric+datetime64 should raise; Timestamp raises + # NullFrequencyError instead of TypeError so is excluded. + left = tm.box_expected(numeric_idx, box) + + with pytest.raises(TypeError): + left + other + with pytest.raises(TypeError): + other + left + with pytest.raises(TypeError): + left - other + with pytest.raises(TypeError): + other - left + # ------------------------------------------------------------------ # Arithmetic diff --git a/pandas/tests/tslibs/test_api.py b/pandas/tests/tslibs/test_api.py index 47e398dfe3d16..7a8a6d511aa69 100644 --- a/pandas/tests/tslibs/test_api.py +++ b/pandas/tests/tslibs/test_api.py @@ -29,6 +29,7 @@ def test_namespace(): "NaTType", "iNaT", "is_null_datetimelike", + "NullFrequencyError", "OutOfBoundsDatetime", "Period", "IncompatibleFrequency", From c1e7bfbf1e1e46bb8a73ada06ebbeb3bba07d90b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 9 Sep 2019 10:59:19 -0700 Subject: [PATCH 5/8] post-merge fixup --- pandas/_libs/tslibs/__init__.py | 2 +- pandas/_libs/tslibs/c_timestamp.pyx | 2 +- pandas/_libs/tslibs/np_datetime.pyx | 9 --------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/pandas/_libs/tslibs/__init__.py b/pandas/_libs/tslibs/__init__.py index 913f892d93ccf..8d3b00e4a44b9 100644 --- a/pandas/_libs/tslibs/__init__.py +++ b/pandas/_libs/tslibs/__init__.py @@ -2,7 +2,7 @@ from .conversion import localize_pydatetime, normalize_date from .nattype import NaT, NaTType, iNaT, is_null_datetimelike -from .np_datetime import NullFrequencyError, OutOfBoundsDatetime +from .np_datetime import OutOfBoundsDatetime from .period import IncompatibleFrequency, Period from .timedeltas import Timedelta, delta_to_nanoseconds, ints_to_pytimedelta from .timestamps import Timestamp diff --git a/pandas/_libs/tslibs/c_timestamp.pyx b/pandas/_libs/tslibs/c_timestamp.pyx index 1a127f1048b92..dfa66d7e2d862 100644 --- a/pandas/_libs/tslibs/c_timestamp.pyx +++ b/pandas/_libs/tslibs/c_timestamp.pyx @@ -33,7 +33,7 @@ from pandas._libs.tslibs.util cimport ( from pandas._libs.tslibs.fields import get_start_end_field, get_date_name_field from pandas._libs.tslibs.nattype cimport c_NaT as NaT -from pandas._libs.tslibs.np_datetime import NullFrequencyError, OutOfBoundsDatetime +from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime from pandas._libs.tslibs.np_datetime cimport ( reverse_ops, cmp_scalar) from pandas._libs.tslibs.timezones cimport ( diff --git a/pandas/_libs/tslibs/np_datetime.pyx b/pandas/_libs/tslibs/np_datetime.pyx index 7d2c735b860b9..7d362708015ce 100644 --- a/pandas/_libs/tslibs/np_datetime.pyx +++ b/pandas/_libs/tslibs/np_datetime.pyx @@ -94,15 +94,6 @@ cdef inline bint cmp_scalar(int64_t lhs, int64_t rhs, int op) except -1: return lhs >= rhs -class NullFrequencyError(ValueError): - """ - Error raised when a null `freq` attribute is used in an operation - that needs a non-null frequency, particularly `DatetimeIndex.shift`, - `TimedeltaIndex.shift`, `PeriodIndex.shift`. - """ - pass - - class OutOfBoundsDatetime(ValueError): pass From 6ed4766187d1efe88f0fada6f7ba132a9a2db6f9 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 9 Sep 2019 11:07:58 -0700 Subject: [PATCH 6/8] blackify --- pandas/core/ops/__init__.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 545a96324ab74..16cdc4a2d4996 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -636,7 +636,13 @@ def wrapper(left, right): keep_null_freq = isinstance( right, - (ABCDatetimeIndex, ABCDatetimeArray, ABCTimedeltaIndex, ABCTimedeltaArray, Timestamp), + ( + ABCDatetimeIndex, + ABCDatetimeArray, + ABCTimedeltaIndex, + ABCTimedeltaArray, + Timestamp, + ), ) left, right = _align_method_SERIES(left, right) @@ -647,7 +653,9 @@ def wrapper(left, right): rvalues = maybe_upcast_for_op(rvalues, lvalues.shape) - if should_extension_dispatch(left, rvalues) or isinstance(rvalues, (ABCTimedeltaArray, ABCDatetimeArray, Timestamp)): + if should_extension_dispatch(left, rvalues) or isinstance( + rvalues, (ABCTimedeltaArray, ABCDatetimeArray, Timestamp) + ): result = dispatch_to_extension_op(op, lvalues, rvalues, keep_null_freq) else: From a74f1602df907a8cf3de5bd8ac5ed35298a09647 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 9 Sep 2019 14:33:49 -0700 Subject: [PATCH 7/8] fix name --- pandas/tests/arithmetic/test_numeric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index c3f9661910a4a..584e22f8488f5 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -245,7 +245,7 @@ def test_add_sub_timedeltalike_invalid(self, numeric_idx, other, box): ], ) @pytest.mark.filterwarnings("ignore:elementwise comp:DeprecationWarning") - def test_add_sub_timedeltalike_invalid(self, numeric_idx, other, box): + def test_add_sub_datetimelike_invalid(self, numeric_idx, other, box): # GH#28080 numeric+datetime64 should raise; Timestamp raises # NullFrequencyError instead of TypeError so is excluded. left = tm.box_expected(numeric_idx, box) From 34e31634b1fcb89642d442b745ec7971768b15aa Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 10 Sep 2019 08:05:41 -0700 Subject: [PATCH 8/8] whatsnew --- doc/source/whatsnew/v1.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 2d15a7e5ccadd..1946b855a6c2c 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -118,6 +118,7 @@ Datetimelike - Bug in :meth:`pandas.core.groupby.SeriesGroupBy.nunique` where ``NaT`` values were interfering with the count of unique values (:issue:`27951`) - Bug in :class:`Timestamp` subtraction when subtracting a :class:`Timestamp` from a ``np.datetime64`` object incorrectly raising ``TypeError`` (:issue:`28286`) - Addition and subtraction of integer or integer-dtype arrays with :class:`Timestamp` will now raise ``NullFrequencyError`` instead of ``ValueError`` (:issue:`28268`) +- Bug in :class:`Series` and :class:`DataFrame` with integer dtype failing to raise ``TypeError`` when adding or subtracting a ``np.datetime64`` object (:issue:`28080`) -