Skip to content

Commit 327687c

Browse files
committed
BUG: Catch TypeError when calling _get_dtype
The following functions were not catching the TypeError raised by _get_dtype: 1) is_string_dtype 2) is_string_like_dtype 3) is_timedelta64_ns_dtype Thus, when "None" was passed in, an Exception was raised instead of returning False, as other functions did.
1 parent a3cc121 commit 327687c

File tree

3 files changed

+131
-9
lines changed

3 files changed

+131
-9
lines changed

doc/source/whatsnew/v0.20.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,7 @@ Conversion
11451145
- Bug in ``.asfreq()``, where frequency was not set for empty ``Series`` (:issue:`14320`)
11461146
- Bug in ``DataFrame`` construction with nulls and datetimes in a list-like (:issue:`15869`)
11471147
- Bug in ``DataFrame.fillna()`` with tz-aware datetimes (:issue:`15855`)
1148+
- Bug in ``is_string_dtype``, ``is_timedelta64_ns_dtype``, and ``is_string_like_dtype`` in which an error was raised when ``None`` was passed in (:issue:`15941`)
11481149

11491150
Indexing
11501151
^^^^^^^^

pandas/tests/types/test_common.py

+28
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pandas.types.dtypes import DatetimeTZDtype, PeriodDtype, CategoricalDtype
77
from pandas.types.common import pandas_dtype, is_dtype_equal
88

9+
import pandas.types.common as com
910
import pandas.util.testing as tm
1011

1112

@@ -80,3 +81,30 @@ def test_dtype_equal_strict():
8081
assert not is_dtype_equal(
8182
pandas_dtype('datetime64[ns, US/Eastern]'),
8283
pandas_dtype('datetime64[ns, CET]'))
84+
85+
# see gh-15941: no exception should be raised
86+
assert not is_dtype_equal(None, None)
87+
88+
89+
def get_is_dtype_funcs():
90+
"""
91+
Get all functions in pandas.types.common that
92+
begin with 'is_' and end with 'dtype'
93+
94+
Assumes that we have imported the module
95+
"pandas.types.common" as com.
96+
"""
97+
98+
fnames = [f for f in dir(com) if (f.startswith('is_') and
99+
f.endswith('dtype'))]
100+
return [getattr(com, fname) for fname in fnames]
101+
102+
103+
@pytest.mark.parametrize('func',
104+
get_is_dtype_funcs())
105+
def test_get_dtype_error_catch(func):
106+
# see gh-15941
107+
#
108+
# No exception should be raised.
109+
110+
assert not func(None)

pandas/types/common.py

+102-9
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,40 @@ def is_categorical_dtype(arr_or_dtype):
144144

145145

146146
def is_string_dtype(arr_or_dtype):
147-
dtype = _get_dtype(arr_or_dtype)
148-
return dtype.kind in ('O', 'S', 'U') and not is_period_dtype(dtype)
147+
"""
148+
Check whether the provided array or dtype is of the string dtype.
149+
150+
Parameters
151+
----------
152+
arr_or_dtype : ndarray, dtype, type
153+
The array or dtype to check.
154+
155+
Returns
156+
-------
157+
boolean : Whether or not the array or dtype is of the string dtype.
158+
159+
Examples
160+
--------
161+
>>> is_string_dtype(str)
162+
True
163+
>>> is_string_dtype(object)
164+
True
165+
>>> is_string_dtype(int)
166+
False
167+
>>>
168+
>>> is_string_dtype(np.array(['a', 'b']))
169+
True
170+
>>> is_string_dtype(np.array([1, 2]))
171+
False
172+
"""
173+
174+
# TODO: gh-15585: consider making the checks stricter.
175+
176+
try:
177+
dtype = _get_dtype(arr_or_dtype)
178+
return dtype.kind in ('O', 'S', 'U') and not is_period_dtype(dtype)
179+
except TypeError:
180+
return False
149181

150182

151183
def is_period_arraylike(arr):
@@ -237,8 +269,40 @@ def is_datetime64_ns_dtype(arr_or_dtype):
237269

238270

239271
def is_timedelta64_ns_dtype(arr_or_dtype):
240-
tipo = _get_dtype(arr_or_dtype)
241-
return tipo == _TD_DTYPE
272+
"""
273+
Check whether the provided array or dtype is of the timedelta64[ns] dtype.
274+
275+
This is a very specific dtype, so generic ones like `np.timedelta64`
276+
will return False if passed into this function.
277+
278+
Parameters
279+
----------
280+
arr_or_dtype : ndarray, dtype, type
281+
The array or dtype to check.
282+
283+
Returns
284+
-------
285+
boolean : Whether or not the array or dtype
286+
is of the timedelta64[ns] dtype.
287+
288+
Examples
289+
--------
290+
>>> is_timedelta64_ns_dtype(np.dtype('m8[ns]')
291+
True
292+
>>> is_timedelta64_ns_dtype(np.dtype('m8[ps]') # Wrong frequency
293+
False
294+
>>>
295+
>>> is_timedelta64_ns_dtype(np.array([1, 2], dtype='m8[ns]'))
296+
True
297+
>>> is_timedelta64_ns_dtype(np.array([1, 2], dtype=np.timedelta64))
298+
False
299+
"""
300+
301+
try:
302+
tipo = _get_dtype(arr_or_dtype)
303+
return tipo == _TD_DTYPE
304+
except TypeError:
305+
return False
242306

243307

244308
def is_datetime_or_timedelta_dtype(arr_or_dtype):
@@ -260,8 +324,7 @@ def _is_unorderable_exception(e):
260324
261325
Returns
262326
-------
263-
is_orderable_exc : Whether or not the exception raised is an
264-
unorderable exception.
327+
boolean : Whether or not the exception raised is an unorderable exception.
265328
"""
266329

267330
if PY36:
@@ -342,9 +405,39 @@ def is_numeric_dtype(arr_or_dtype):
342405

343406

344407
def is_string_like_dtype(arr_or_dtype):
345-
# exclude object as its a mixed dtype
346-
dtype = _get_dtype(arr_or_dtype)
347-
return dtype.kind in ('S', 'U')
408+
"""
409+
Check whether the provided array or dtype is of a string-like dtype.
410+
411+
Unlike `is_string_dtype`, the object dtype is excluded because it
412+
is a mixed dtype.
413+
414+
Parameters
415+
----------
416+
arr_or_dtype : ndarray, dtype, type
417+
The array or dtype to check.
418+
419+
Returns
420+
-------
421+
boolean : Whether or not the array or dtype is of the string dtype.
422+
423+
Examples
424+
--------
425+
>>> is_string_like_dtype(str)
426+
True
427+
>>> is_string_like_dtype(object)
428+
False
429+
>>>
430+
>>> is_string_like_dtype(np.array(['a', 'b']))
431+
True
432+
>>> is_string_like_dtype(np.array([1, 2]))
433+
False
434+
"""
435+
436+
try:
437+
dtype = _get_dtype(arr_or_dtype)
438+
return dtype.kind in ('S', 'U')
439+
except TypeError:
440+
return False
348441

349442

350443
def is_float_dtype(arr_or_dtype):

0 commit comments

Comments
 (0)