From c4834406846315222b438c0a09ac62f8c288f276 Mon Sep 17 00:00:00 2001 From: Gregory Rome Date: Fri, 12 Apr 2019 17:52:22 -0500 Subject: [PATCH 1/7] #26065 Fix Type Annotations in pandas.core.arrays --- pandas/core/arrays/array_.py | 4 ++-- pandas/core/arrays/datetimelike.py | 13 ++++++++----- pandas/core/arrays/integer.py | 5 +++-- pandas/core/arrays/interval.py | 2 +- pandas/core/arrays/period.py | 24 ++++++++++++++---------- pandas/core/arrays/timedeltas.py | 5 +++-- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/pandas/core/arrays/array_.py b/pandas/core/arrays/array_.py index f549557440ebe..1b002ad12d526 100644 --- a/pandas/core/arrays/array_.py +++ b/pandas/core/arrays/array_.py @@ -1,4 +1,4 @@ -from typing import Optional, Sequence, Union +from typing import Optional, Sequence, Union, cast import numpy as np @@ -229,7 +229,7 @@ def array(data: Sequence[object], dtype = registry.find(dtype) or dtype if is_extension_array_dtype(dtype): - cls = dtype.construct_array_type() + cls = cast(ExtensionDtype, dtype).construct_array_type() return cls._from_sequence(data, dtype=dtype, copy=copy) if dtype is None: diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 6225dfcbe5c14..a7a0f67ebd571 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from datetime import datetime, timedelta import operator -from typing import Any, Sequence, Tuple, Type, Union +from typing import Any, Sequence, Type, Union, cast import warnings import numpy as np @@ -40,6 +40,7 @@ class AttributesMixin(object): + _data = None # type: np.ndarray @property def _attributes(self): @@ -57,7 +58,8 @@ def _get_attributes_dict(self): return {k: getattr(self, k, None) for k in self._attributes} @property - def _scalar_type(self) -> Union[Type, Tuple[Type]]: + def _scalar_type(self) -> Union[Type[Period], Type[Timestamp], + Type[Timedelta]]: """The scalar associated with this datelike * PeriodArray : Period @@ -479,12 +481,13 @@ def __setitem__( raise ValueError("setting an array element with a sequence.") if (not is_slice - and len(key) != len(value) + and len(cast(Sequence, key)) != len(value) and not com.is_bool_indexer(key)): msg = ("shape mismatch: value array of length '{}' does not " "match indexing result of length '{}'.") - raise ValueError(msg.format(len(key), len(value))) - if not is_slice and len(key) == 0: + raise ValueError(msg.format( + len(cast(Sequence, key)), len(value))) + if not is_slice and len(cast(Sequence, key)) == 0: return value = type(self)._from_sequence(value, dtype=self.dtype) diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index bbacfa3077054..b0515b7a2dc4e 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -1,5 +1,6 @@ import copy import sys +from typing import Type import warnings import numpy as np @@ -31,9 +32,9 @@ class _IntegerDtype(ExtensionDtype): The attributes name & type are set when these subclasses are created. """ - name = None + name = None # type: str base = None - type = None + type = None # type: Type na_value = np.nan def __repr__(self): diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 7b20c7e1b6336..d99712d4251ae 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -943,7 +943,7 @@ def mid(self): else False """ - @property + @property # type: ignore # Mypy does not support decorated properties @Appender(_interval_shared_docs['is_non_overlapping_monotonic'] % _shared_docs_kwargs) def is_non_overlapping_monotonic(self): diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 70312f2e61445..c1324f4b0f6db 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from datetime import timedelta import operator -from typing import Any, Callable, Optional, Sequence, Union +from typing import Any, Callable, List, Optional, Sequence, Union import numpy as np @@ -24,7 +24,7 @@ from pandas.core.dtypes.missing import isna, notna import pandas.core.algorithms as algos -from pandas.core.arrays import ExtensionArray, datetimelike as dtl +from pandas.core.arrays import datetimelike as dtl import pandas.core.common as com from pandas.tseries import frequencies @@ -95,7 +95,7 @@ class PeriodArray(dtl.DatetimeLikeArrayMixin, dtl.DatelikeOps): Parameters ---------- - values : Union[PeriodArray, Series[period], ndarary[int], PeriodIndex] + values : Union[PeriodArray, Series[period], ndarray[int], PeriodIndex] The data to store. These should be arrays that can be directly converted to ordinals without inference or copy (PeriodArray, ndarray[int64]), or a box around such an array (Series[period], @@ -136,7 +136,7 @@ class PeriodArray(dtl.DatetimeLikeArrayMixin, dtl.DatelikeOps): _scalar_type = Period # Names others delegate to us - _other_ops = [] + _other_ops = [] # type: List[str] _bool_ops = ['is_leap_year'] _object_ops = ['start_time', 'end_time', 'freq'] _field_ops = ['year', 'month', 'day', 'hour', 'minute', 'second', @@ -190,7 +190,8 @@ def _from_sequence( copy: bool = False, ) -> ABCPeriodArray: if dtype: - freq = dtype.freq + freq = dtype.freq # type: ignore + # freq is generated dynamically in PeriodDtype's __new__ method else: freq = None @@ -277,7 +278,7 @@ def _check_compatible_with(self, other): def dtype(self): return self._dtype - @property + @property # type: ignore # read-only property overwriting read/write def freq(self): """ Return the frequency object for this PeriodArray. @@ -539,17 +540,20 @@ def _sub_period(self, other): return new_data + Other_types = Union[ABCPeriodArray, ABCSeries, ABCPeriodIndex, + np.ndarray] + @Appender(dtl.DatetimeLikeArrayMixin._addsub_int_array.__doc__) def _addsub_int_array( self, - other: Union[ExtensionArray, np.ndarray, ABCIndexClass], + other: Other_types, op: Callable[[Any], Any] ) -> ABCPeriodArray: assert op in [operator.add, operator.sub] if op is operator.sub: other = -other - res_values = algos.checked_add_with_arr(self.asi8, other, - arr_mask=self._isnan) + res_values = algos.checked_add_with_arr(self.asi8, other, + arr_mask=self._isnan) res_values = res_values.view('i8') res_values[self._isnan] = iNaT return type(self)(res_values, freq=self.freq) @@ -782,7 +786,7 @@ def period_array( data = np.asarray(data) if freq: - dtype = PeriodDtype(freq) + dtype = PeriodDtype(freq) # type: Optional[PeriodDtype] else: dtype = None diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index d1bf59218edfe..8d6b8008f477b 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from datetime import timedelta import textwrap +from typing import List import warnings import numpy as np @@ -131,8 +132,8 @@ class TimedeltaArray(dtl.DatetimeLikeArrayMixin, dtl.TimelikeOps): _scalar_type = Timedelta __array_priority__ = 1000 # define my properties & methods for delegation - _other_ops = [] - _bool_ops = [] + _other_ops = [] # type: List[str] + _bool_ops = [] # type: List[str] _object_ops = ['freq'] _field_ops = ['days', 'seconds', 'microseconds', 'nanoseconds'] _datetimelike_ops = _field_ops + _object_ops + _bool_ops From b39f7fc2ab4d7d20b4b18fbcfdb17a9f9fc5db0c Mon Sep 17 00:00:00 2001 From: Gregory Rome Date: Fri, 12 Apr 2019 18:03:46 -0500 Subject: [PATCH 2/7] add changes to mypy.ini, correct a stray tab that got included --- mypy.ini | 24 ------------------------ pandas/core/arrays/period.py | 7 ++++--- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/mypy.ini b/mypy.ini index 052dd0d4c4fd1..90a8532335a8d 100644 --- a/mypy.ini +++ b/mypy.ini @@ -20,30 +20,6 @@ ignore_errors=True [mypy-pandas.core.apply] ignore_errors=True -[mypy-pandas.core.arrays.array_] -ignore_errors=True - -[mypy-pandas.core.arrays.datetimelike] -ignore_errors=True - -[mypy-pandas.core.arrays.integer] -ignore_errors=True - -[mypy-pandas.core.arrays.interval] -ignore_errors=True - -[mypy-pandas.core.arrays.numpy_] -ignore_errors=True - -[mypy-pandas.core.arrays.period] -ignore_errors=True - -[mypy-pandas.core.arrays.sparse] -ignore_errors=True - -[mypy-pandas.core.arrays.timedeltas] -ignore_errors=True - [mypy-pandas.core.base] ignore_errors=True diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index c1324f4b0f6db..e081b28f4d174 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -191,7 +191,8 @@ def _from_sequence( ) -> ABCPeriodArray: if dtype: freq = dtype.freq # type: ignore - # freq is generated dynamically in PeriodDtype's __new__ method + # freq is set in PeriodDtype's __new__ method, so mypy doesn't + # recognize it else: freq = None @@ -552,8 +553,8 @@ def _addsub_int_array( assert op in [operator.add, operator.sub] if op is operator.sub: other = -other - res_values = algos.checked_add_with_arr(self.asi8, other, - arr_mask=self._isnan) + res_values = algos.checked_add_with_arr(self.asi8, other, + arr_mask=self._isnan) res_values = res_values.view('i8') res_values[self._isnan] = iNaT return type(self)(res_values, freq=self.freq) From 7dd1157318146f80e94620b95eff000d93c3a19c Mon Sep 17 00:00:00 2001 From: Gregory Rome Date: Thu, 18 Apr 2019 15:06:37 -0500 Subject: [PATCH 3/7] Updates from code review --- pandas/_typing.py | 7 ++++++- pandas/core/arrays/datetimelike.py | 25 +++++++++++++------------ pandas/core/arrays/interval.py | 3 ++- pandas/core/arrays/period.py | 6 ++---- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/pandas/_typing.py b/pandas/_typing.py index dc15a44b65db9..911aa26c1988a 100644 --- a/pandas/_typing.py +++ b/pandas/_typing.py @@ -1,11 +1,16 @@ from pathlib import Path -from typing import IO, AnyStr, Union +from typing import IO, AnyStr, Type, Union import numpy as np +from pandas._libs import Timestamp +from pandas._libs.tslibs.period import Period +from pandas._libs.tslibs.timedeltas import Timedelta + from pandas.core.dtypes.dtypes import ExtensionDtype from pandas.core.dtypes.generic import ABCExtensionArray ArrayLike = Union[ABCExtensionArray, np.ndarray] +DatetimeLike = Type[Union[Period, Timestamp, Timedelta]] Dtype = Union[str, np.dtype, ExtensionDtype] FilePathOrBuffer = Union[str, Path, IO[AnyStr]] diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index a7a0f67ebd571..fab5108f99766 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from datetime import datetime, timedelta import operator -from typing import Any, Sequence, Type, Union, cast +from typing import Any, Sequence, Union, cast import warnings import numpy as np @@ -28,6 +28,7 @@ from pandas.core.dtypes.inference import is_array_like from pandas.core.dtypes.missing import isna +from pandas._typing import DatetimeLike from pandas.core import missing, nanops from pandas.core.algorithms import ( checked_add_with_arr, take, unique1d, value_counts) @@ -58,8 +59,7 @@ def _get_attributes_dict(self): return {k: getattr(self, k, None) for k in self._attributes} @property - def _scalar_type(self) -> Union[Type[Period], Type[Timestamp], - Type[Timedelta]]: + def _scalar_type(self) -> DatetimeLike: """The scalar associated with this datelike * PeriodArray : Period @@ -480,15 +480,16 @@ def __setitem__( if lib.is_scalar(key): raise ValueError("setting an array element with a sequence.") - if (not is_slice - and len(cast(Sequence, key)) != len(value) - and not com.is_bool_indexer(key)): - msg = ("shape mismatch: value array of length '{}' does not " - "match indexing result of length '{}'.") - raise ValueError(msg.format( - len(cast(Sequence, key)), len(value))) - if not is_slice and len(cast(Sequence, key)) == 0: - return + if not is_slice: + key = cast(Sequence, key) + if (len(key) != len(value) + and not com.is_bool_indexer(key)): + msg = ("shape mismatch: value array of length '{}' does " + "not match indexing result of length '{}'.") + raise ValueError(msg.format( + len(key), len(value))) + if len(key) == 0: + return value = type(self)._from_sequence(value, dtype=self.dtype) self._check_compatible_with(value) diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index d99712d4251ae..ed5615f3c66aa 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -943,7 +943,8 @@ def mid(self): else False """ - @property # type: ignore # Mypy does not support decorated properties + @property # type: ignore # https://github.com/python/mypy/issues/1362 + # Mypy does not support decorated properties @Appender(_interval_shared_docs['is_non_overlapping_monotonic'] % _shared_docs_kwargs) def is_non_overlapping_monotonic(self): diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index e081b28f4d174..016643d3426db 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -541,13 +541,11 @@ def _sub_period(self, other): return new_data - Other_types = Union[ABCPeriodArray, ABCSeries, ABCPeriodIndex, - np.ndarray] - @Appender(dtl.DatetimeLikeArrayMixin._addsub_int_array.__doc__) def _addsub_int_array( self, - other: Other_types, + other: Union[ABCPeriodArray, ABCSeries, + ABCPeriodIndex, np.ndarray], op: Callable[[Any], Any] ) -> ABCPeriodArray: assert op in [operator.add, operator.sub] From 026e26bdf917d3ae6466548116c2f744e0d0aa9e Mon Sep 17 00:00:00 2001 From: Gregory Rome Date: Thu, 18 Apr 2019 19:16:49 -0500 Subject: [PATCH 4/7] Added freq class variable to PeriodDtype --- pandas/core/arrays/period.py | 4 +--- pandas/core/dtypes/dtypes.py | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 016643d3426db..a1b65ac3f66b8 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -190,9 +190,7 @@ def _from_sequence( copy: bool = False, ) -> ABCPeriodArray: if dtype: - freq = dtype.freq # type: ignore - # freq is set in PeriodDtype's __new__ method, so mypy doesn't - # recognize it + freq = dtype.freq else: freq = None diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 0cff3ed649802..bb7157e3199fa 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -751,6 +751,7 @@ class PeriodDtype(ExtensionDtype, PandasExtensionDtype): _metadata = ('freq',) _match = re.compile(r"(P|p)eriod\[(?P.+)\]") _cache = {} # type: Dict[str_type, PandasExtensionDtype] + freq = None def __new__(cls, freq=None): """ From 5ed38ca0f358896e7c28461cf4ffbf617c46b280 Mon Sep 17 00:00:00 2001 From: Gregory Rome Date: Mon, 22 Apr 2019 08:33:25 -0500 Subject: [PATCH 5/7] Re-trigger test From 1f2d03dcfd2cb201d879da8bed7cd83c71eeef43 Mon Sep 17 00:00:00 2001 From: Gregory Rome Date: Mon, 22 Apr 2019 11:16:03 -0500 Subject: [PATCH 6/7] Remove freq class variable --- pandas/core/dtypes/dtypes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 24a9fbd7478ef..417683ad54420 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -767,7 +767,6 @@ class PeriodDtype(ExtensionDtype, PandasExtensionDtype): _metadata = ('freq',) _match = re.compile(r"(P|p)eriod\[(?P.+)\]") _cache = {} # type: Dict[str_type, PandasExtensionDtype] - freq = None def __new__(cls, freq=None): """ From 5f840e9da2ed3898dd78baace69b49e6db1048c6 Mon Sep 17 00:00:00 2001 From: Gregory Rome Date: Mon, 29 Apr 2019 13:59:11 -0500 Subject: [PATCH 7/7] Update comments --- pandas/_typing.py | 2 +- pandas/core/arrays/datetimelike.py | 6 +++--- pandas/core/arrays/interval.py | 4 ++-- pandas/core/arrays/period.py | 4 +++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pandas/_typing.py b/pandas/_typing.py index 911aa26c1988a..3959e38e6f08c 100644 --- a/pandas/_typing.py +++ b/pandas/_typing.py @@ -11,6 +11,6 @@ from pandas.core.dtypes.generic import ABCExtensionArray ArrayLike = Union[ABCExtensionArray, np.ndarray] -DatetimeLike = Type[Union[Period, Timestamp, Timedelta]] +DatetimeLikeScalar = Type[Union[Period, Timestamp, Timedelta]] Dtype = Union[str, np.dtype, ExtensionDtype] FilePathOrBuffer = Union[str, Path, IO[AnyStr]] diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 04fdca0268c4a..c32f8642dc2ed 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -27,7 +27,7 @@ from pandas.core.dtypes.inference import is_array_like from pandas.core.dtypes.missing import isna -from pandas._typing import DatetimeLike +from pandas._typing import DatetimeLikeScalar from pandas.core import missing, nanops from pandas.core.algorithms import ( checked_add_with_arr, take, unique1d, value_counts) @@ -58,7 +58,7 @@ def _get_attributes_dict(self): return {k: getattr(self, k, None) for k in self._attributes} @property - def _scalar_type(self) -> DatetimeLike: + def _scalar_type(self) -> DatetimeLikeScalar: """The scalar associated with this datelike * PeriodArray : Period @@ -487,7 +487,7 @@ def __setitem__( "not match indexing result of length '{}'.") raise ValueError(msg.format( len(key), len(value))) - if len(key) == 0: + elif not len(key): return value = type(self)._from_sequence(value, dtype=self.dtype) diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 8def347c5c466..c73ac0ab5a543 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -939,9 +939,9 @@ def mid(self): points) and is either monotonic increasing or monotonic decreasing, else False """ - - @property # type: ignore # https://github.com/python/mypy/issues/1362 + # https://github.com/python/mypy/issues/1362 # Mypy does not support decorated properties + @property # type: ignore @Appender(_interval_shared_docs['is_non_overlapping_monotonic'] % _shared_docs_kwargs) def is_non_overlapping_monotonic(self): diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 6d30df9529b59..8a6640e11ad74 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -276,7 +276,8 @@ def _check_compatible_with(self, other): def dtype(self): return self._dtype - @property # type: ignore # read-only property overwriting read/write + # read-only property overwriting read/write + @property # type: ignore def freq(self): """ Return the frequency object for this PeriodArray. @@ -779,6 +780,7 @@ def period_array( data = np.asarray(data) if freq: + # typed Optional here because the else block below assigns None dtype = PeriodDtype(freq) # type: Optional[PeriodDtype] else: dtype = None