Skip to content

Commit 3580266

Browse files
committed
Add type hints to dtypes/dtypes.py (CategoricalDtype)
1 parent 17247ed commit 3580266

File tree

2 files changed

+62
-40
lines changed

2 files changed

+62
-40
lines changed

pandas/core/arrays/categorical.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ def ordered(self):
429429
return self.dtype.ordered
430430

431431
@property
432-
def dtype(self):
432+
def dtype(self) -> 'CategoricalDtype':
433433
"""
434434
The :class:`~pandas.api.types.CategoricalDtype` for this instance
435435
"""

pandas/core/dtypes/dtypes.py

+61-39
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
""" define extension dtypes """
22
import re
3-
from typing import Any, Dict, Optional, Tuple, Type
3+
import typing
4+
from typing import Any, Dict, List, Optional, Union, Tuple, Type
45
import warnings
56

67
import numpy as np
@@ -11,14 +12,16 @@
1112

1213
from pandas.core.dtypes.generic import (
1314
ABCCategoricalIndex, ABCDateOffset, ABCIndexClass)
15+
from pandas.errors import AbstractMethodError
1416

1517
from .base import ExtensionDtype, _DtypeOpsMixin
1618
from .inference import is_list_like
1719

1820
str_type = str
1921

2022

21-
def register_extension_dtype(cls):
23+
def register_extension_dtype(cls: Type['ExtensionDtype'],
24+
) -> Type['ExtensionDtype']:
2225
"""
2326
Register an ExtensionType with pandas as class decorator.
2427
@@ -60,20 +63,22 @@ class Registry:
6063
These are tried in order.
6164
"""
6265
def __init__(self):
63-
self.dtypes = []
66+
self.dtypes = [] # type: List[Type[ExtensionDtype]]
6467

65-
def register(self, dtype):
68+
def register(self, dtype: Type['ExtensionDtype']) -> None:
6669
"""
6770
Parameters
6871
----------
6972
dtype : ExtensionDtype
7073
"""
71-
if not issubclass(dtype, (PandasExtensionDtype, ExtensionDtype)):
74+
if not issubclass(dtype, ExtensionDtype):
7275
raise ValueError("can only register pandas extension dtypes")
7376

7477
self.dtypes.append(dtype)
7578

76-
def find(self, dtype):
79+
def find(self,
80+
dtype: Type['ExtensionDtype'],
81+
) -> Optional[Type[ExtensionDtype]]:
7782
"""
7883
Parameters
7984
----------
@@ -126,16 +131,20 @@ class PandasExtensionDtype(_DtypeOpsMixin):
126131
isnative = 0
127132
_cache = {} # type: Dict[str_type, 'PandasExtensionDtype']
128133

129-
def __unicode__(self):
134+
def __unicode__(self) -> str_type:
130135
return self.name
131136

132-
def __str__(self):
137+
@property
138+
def name(self) -> str_type:
139+
raise AbstractMethodError(self)
140+
141+
def __str__(self) -> str_type:
133142
"""
134143
Return a string representation for a particular Object
135144
"""
136145
return self.__unicode__()
137146

138-
def __bytes__(self):
147+
def __bytes__(self) -> bytes:
139148
"""
140149
Return a string representation for a particular object.
141150
"""
@@ -144,22 +153,22 @@ def __bytes__(self):
144153
encoding = get_option("display.encoding")
145154
return self.__unicode__().encode(encoding, 'replace')
146155

147-
def __repr__(self):
156+
def __repr__(self) -> str_type:
148157
"""
149158
Return a string representation for a particular object.
150159
"""
151160
return str(self)
152161

153-
def __hash__(self):
162+
def __hash__(self) -> int:
154163
raise NotImplementedError("sub-classes should implement an __hash__ "
155164
"method")
156165

157-
def __getstate__(self):
166+
def __getstate__(self) -> Dict[str_type, Any]:
158167
# pickle support; we don't want to pickle the cache
159168
return {k: getattr(self, k, None) for k in self._metadata}
160169

161170
@classmethod
162-
def reset_cache(cls):
171+
def reset_cache(cls) -> None:
163172
""" clear the cache """
164173
cls._cache = {}
165174

@@ -223,17 +232,24 @@ class CategoricalDtype(PandasExtensionDtype, ExtensionDtype):
223232
_metadata = ('categories', 'ordered')
224233
_cache = {} # type: Dict[str_type, PandasExtensionDtype]
225234

226-
def __init__(self, categories=None, ordered=None):
235+
def __init__(self, categories=None, ordered: bool = None):
227236
self._finalize(categories, ordered, fastpath=False)
228237

229238
@classmethod
230-
def _from_fastpath(cls, categories=None, ordered=None):
239+
def _from_fastpath(cls,
240+
categories=None,
241+
ordered: bool = None
242+
) -> 'CategoricalDtype':
231243
self = cls.__new__(cls)
232244
self._finalize(categories, ordered, fastpath=True)
233245
return self
234246

235247
@classmethod
236-
def _from_categorical_dtype(cls, dtype, categories=None, ordered=None):
248+
def _from_categorical_dtype(cls,
249+
dtype: 'CategoricalDtype',
250+
categories=None,
251+
ordered: bool = None,
252+
) -> 'CategoricalDtype':
237253
if categories is ordered is None:
238254
return dtype
239255
if categories is None:
@@ -243,8 +259,12 @@ def _from_categorical_dtype(cls, dtype, categories=None, ordered=None):
243259
return cls(categories, ordered)
244260

245261
@classmethod
246-
def _from_values_or_dtype(cls, values=None, categories=None, ordered=None,
247-
dtype=None):
262+
def _from_values_or_dtype(cls,
263+
values=None,
264+
categories=None,
265+
ordered: bool = None,
266+
dtype: 'CategoricalDtype' = None,
267+
) -> 'CategoricalDtype':
248268
"""
249269
Construct dtype from the input parameters used in :class:`Categorical`.
250270
@@ -326,7 +346,11 @@ def _from_values_or_dtype(cls, values=None, categories=None, ordered=None,
326346

327347
return dtype
328348

329-
def _finalize(self, categories, ordered, fastpath=False):
349+
def _finalize(self,
350+
categories,
351+
ordered: Optional[bool],
352+
fastpath: bool = False,
353+
) -> None:
330354

331355
if ordered is not None:
332356
self.validate_ordered(ordered)
@@ -338,14 +362,14 @@ def _finalize(self, categories, ordered, fastpath=False):
338362
self._categories = categories
339363
self._ordered = ordered
340364

341-
def __setstate__(self, state):
365+
def __setstate__(self, state: 'Dict[str_type, Any]') -> None:
342366
# for pickle compat. __get_state__ is defined in the
343367
# PandasExtensionDtype superclass and uses the public properties to
344368
# pickle -> need to set the settable private ones here (see GH26067)
345369
self._categories = state.pop('categories', None)
346370
self._ordered = state.pop('ordered', False)
347371

348-
def __hash__(self):
372+
def __hash__(self) -> int:
349373
# _hash_categories returns a uint64, so use the negative
350374
# space for when we have unknown categories to avoid a conflict
351375
if self.categories is None:
@@ -356,7 +380,7 @@ def __hash__(self):
356380
# We *do* want to include the real self.ordered here
357381
return int(self._hash_categories(self.categories, self.ordered))
358382

359-
def __eq__(self, other):
383+
def __eq__(self, other: Any) -> bool:
360384
"""
361385
Rules for CDT equality:
362386
1) Any CDT is equal to the string 'category'
@@ -403,7 +427,7 @@ def __repr__(self):
403427
return tpl.format(data, self.ordered)
404428

405429
@staticmethod
406-
def _hash_categories(categories, ordered=True):
430+
def _hash_categories(categories, ordered: Union[bool, None] = True) -> int:
407431
from pandas.core.util.hashing import (
408432
hash_array, _combine_hash_arrays, hash_tuples
409433
)
@@ -453,20 +477,17 @@ def construct_array_type(cls):
453477
return Categorical
454478

455479
@classmethod
456-
def construct_from_string(cls, string):
480+
def construct_from_string(cls, string: str_type) -> 'CategoricalDtype':
457481
"""
458482
attempt to construct this type from a string, raise a TypeError if
459483
it's not possible """
460-
try:
461-
if string == 'category':
462-
return cls()
463-
else:
464-
raise TypeError("cannot construct a CategoricalDtype")
465-
except AttributeError:
466-
pass
484+
if string == 'category':
485+
return cls()
486+
else:
487+
raise TypeError("cannot construct a CategoricalDtype")
467488

468489
@staticmethod
469-
def validate_ordered(ordered):
490+
def validate_ordered(ordered: bool) -> None:
470491
"""
471492
Validates that we have a valid ordered parameter. If
472493
it is not a boolean, a TypeError will be raised.
@@ -486,7 +507,7 @@ def validate_ordered(ordered):
486507
raise TypeError("'ordered' must either be 'True' or 'False'")
487508

488509
@staticmethod
489-
def validate_categories(categories, fastpath=False):
510+
def validate_categories(categories, fastpath: bool = False):
490511
"""
491512
Validates that we have good categories
492513
@@ -500,7 +521,7 @@ def validate_categories(categories, fastpath=False):
500521
-------
501522
categories : Index
502523
"""
503-
from pandas import Index
524+
from pandas.core.indexes.base import Index
504525

505526
if not fastpath and not is_list_like(categories):
506527
msg = "Parameter 'categories' must be list-like, was {!r}"
@@ -519,9 +540,9 @@ def validate_categories(categories, fastpath=False):
519540
if isinstance(categories, ABCCategoricalIndex):
520541
categories = categories.categories
521542

522-
return categories
543+
return typing.cast(Index, categories)
523544

524-
def update_dtype(self, dtype):
545+
def update_dtype(self, dtype: 'CategoricalDtype') -> 'CategoricalDtype':
525546
"""
526547
Returns a CategoricalDtype with categories and ordered taken from dtype
527548
if specified, otherwise falling back to self if unspecified
@@ -560,17 +581,18 @@ def categories(self):
560581
"""
561582
An ``Index`` containing the unique categories allowed.
562583
"""
563-
return self._categories
584+
from pandas import Index
585+
return typing.cast(Index, self._categories)
564586

565587
@property
566-
def ordered(self):
588+
def ordered(self) -> Optional[bool]:
567589
"""
568590
Whether the categories have an ordered relationship.
569591
"""
570592
return self._ordered
571593

572594
@property
573-
def _is_boolean(self):
595+
def _is_boolean(self) -> bool:
574596
from pandas.core.dtypes.common import is_bool_dtype
575597

576598
return is_bool_dtype(self.categories)

0 commit comments

Comments
 (0)