Make TimedeltaIndex +/- pd.NaT return TimedeltaIndex
State: closed

- [x] closes #19124
- [x] tests added / passed
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Labels:
name: Datetime
description: Datetime data dtype
name: Missing-data
description: np.nan, pd.NaT, pd.NA, dropna, isnull, interpolate
name: Timedelta
description: Timedelta data type

Comments:
Travis http error
you need to rebase
Looks like Travis is timing out in a bunch of places.  Will hold off on pushing for now.  Once this gets in I've got a patch ready for #19158 that will un-xfail that one test.
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/19139?src=pr&el=h1) Report
> Merging [#19139](https://codecov.io/gh/pandas-dev/pandas/pull/19139?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/aa9e002463d104b6dc37cdb32b2726bafcb5aa36?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/19139/graphs/tree.svg?width=650&height=150&token=eZ4WkYLtcO&src=pr)](https://codecov.io/gh/pandas-dev/pandas/pull/19139?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #19139      +/-   ##
==========================================
- Coverage   91.52%   91.52%   -0.01%     
==========================================
  Files         147      147              
  Lines       48827    48795      -32     
==========================================
- Hits        44691    44661      -30     
+ Misses       4136     4134       -2
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.9% <100%> (-0.01%)` | :arrow_down: |
| #single | `41.61% <0%> (-0.09%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/19139?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/indexes/timedeltas.py](https://codecov.io/gh/pandas-dev/pandas/pull/19139/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy90aW1lZGVsdGFzLnB5) | `90.6% <100%> (-0.04%)` | :arrow_down: |
| [pandas/core/dtypes/common.py](https://codecov.io/gh/pandas-dev/pandas/pull/19139/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2NvbW1vbi5weQ==) | `94.58% <0%> (-0.74%)` | :arrow_down: |
| [pandas/core/indexes/interval.py](https://codecov.io/gh/pandas-dev/pandas/pull/19139/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9pbnRlcnZhbC5weQ==) | `92.29% <0%> (-0.09%)` | :arrow_down: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/19139/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `84.41% <0%> (-0.04%)` | :arrow_down: |
| [pandas/core/dtypes/dtypes.py](https://codecov.io/gh/pandas-dev/pandas/pull/19139/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2R0eXBlcy5weQ==) | `96.11% <0%> (-0.02%)` | :arrow_down: |
| [pandas/core/series.py](https://codecov.io/gh/pandas-dev/pandas/pull/19139/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvc2VyaWVzLnB5) | `94.61% <0%> (ø)` | :arrow_up: |
| [pandas/core/dtypes/cast.py](https://codecov.io/gh/pandas-dev/pandas/pull/19139/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2Nhc3QucHk=) | `88.35% <0%> (+0.36%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/19139?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/19139?src=pr&el=footer). Last update [aa9e002...5a54a92](https://codecov.io/gh/pandas-dev/pandas/pull/19139?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

ping.  Once this goes in I can fix the bug that surfaced in `pd.NaT - Series`
> can you just parametrize around tdi and Series, too much repeated code (you can xfail that case using pytest.param)

Just tried this and pytest is raising errors at collect-time, complaining about pytest.param.  This would be a non-issue if weren't for the xfail.  After this branch is merged I've got a branch ready that fixes the xfailing error.  Since that PR will have to change this test anyway, let's parametrize it in that PR.
ping
Please read my previous comment.
I read your comment. its doesn't make any sense.
> I read your comment. its doesn't make any sense.

Then ask for clarification, buddy.  Just repeating the request without acknowledging that I tried to implement and discuss it is pretty discouraging.

Implementing the parameterization you're asking for looks like this:

```
@pytest.mark.parametrize('box, assert_func', [
    (TimedeltaIndex, tm.assert_index_equal),
    pytest.param((Series, tm.assert_series_equal),
                 marks=pytest.mark.xfail(reason='NaT - Series returns NaT'))
])
def test_nat_arithmetic_td64_vector(box, assert_func):
    [...]
```

(Even if it didn't cause an error, this is 5 lines of boilerplate to eliminate a 6 line test.)

`python -m pytest pandas/tests/scalar/test_nat.py` then raises at test-collection time:

```
/usr/local/lib/python2.7/site-packages/_pytest/python.py:832: in parametrize
    param.values, argnames))
E   ValueError: In "parametrize" the number of values (((<class 'pandas.core.series.Series'>, <function assert_series_equal at 0x10c0a6140>),)) must be equal to the number of names (['box', 'assert_func'])
```

So the options here are:

- Wait for this to get fixed on pytest's end
- kludge some other boilerplate to avoid needing to use pytest.param here
- Merge as is, at which point I can make a PR that fixes the bug that makes the xfail necessary, and that PR can parametrize the test.
```
@pytest.mark.parametrize('box, assert_func', [
    (TimedeltaIndex, tm.assert_index_equal),
    pytest.param(Series, tm.assert_series_equal,
                 marks=pytest.mark.xfail(reason='NaT - Series returns NaT'))
])
def test_nat_arithmetic_td64_vector(box, assert_func):
    [...]
```

pass as args not a tuple. I agree its a little bit odd that pytest doesn't accept a tuple here, and I know that this is adding an xfail that you are going to remove later, but would rather do that than miss adding the tests with coverage.
> pass as args not a tuple.

Not sure what you mean here.

> this is adding an xfail that you are going to remove later, but would rather do that than miss adding the tests with coverage.

The xfail and the coverage exist in the PR-as-is.  If you can clarify the "args not a tuple" thing I'll try to implement it, since that is a faster way of getting bugs fixed than arguing about it.  But compromising with me on this is an even faster way to get even more bugs fixed, and it ends with the same parametrized tests you have in mind.
do this
```
pytest.param(Series, tm.assert_series_equal,
                 marks=pytest.mark.xfail(reason='NaT - Series returns NaT'))
```

not this
```
pytest.param((Series, tm.assert_series_equal),
                 marks=pytest.mark.xfail(reason='NaT - Series returns NaT'))
```
thank you for fixing!