Skip to content

Commit

Permalink
BUG: Catch TypeError when calling _get_dtype
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gfyoung committed Apr 7, 2017
1 parent 327d561 commit 9777867
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 9 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.20.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,7 @@ Conversion
- Bug in ``DataFrame.fillna()`` where the argument ``downcast`` was ignored when fillna value was of type ``dict`` (:issue:`15277`)
- Bug in ``.asfreq()``, where frequency was not set for empty ``Series`` (:issue:`14320`)
- Bug in ``DataFrame`` construction with nulls and datetimes in a list-like (:issue:`15869`)
- 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`)

Indexing
^^^^^^^^
Expand Down
20 changes: 20 additions & 0 deletions pandas/tests/types/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pandas.types.dtypes import DatetimeTZDtype, PeriodDtype, CategoricalDtype
from pandas.types.common import pandas_dtype, is_dtype_equal

import pandas.types.common as com
import pandas.util.testing as tm


Expand Down Expand Up @@ -80,3 +81,22 @@ def test_dtype_equal_strict():
assert not is_dtype_equal(
pandas_dtype('datetime64[ns, US/Eastern]'),
pandas_dtype('datetime64[ns, CET]'))


get_dtype_funcs = [
(com.is_dtype_equal, 2),
(com.is_string_dtype, 1),
(com.is_string_like_dtype, 1),
(com.is_datetime64_ns_dtype, 1),
(com.is_timedelta64_ns_dtype, 1)
]


@pytest.mark.parametrize('func,none_count',
get_dtype_funcs)
def test_get_dtype_error_catch(func, none_count):
# see gh-15941
#
# No exception should be raised.

assert not func(*[None] * none_count)
111 changes: 102 additions & 9 deletions pandas/types/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,40 @@ def is_categorical_dtype(arr_or_dtype):


def is_string_dtype(arr_or_dtype):
dtype = _get_dtype(arr_or_dtype)
return dtype.kind in ('O', 'S', 'U') and not is_period_dtype(dtype)
"""
Check whether the provided array or dtype is of the string dtype.
Parameters
----------
arr_or_dtype : ndarray, dtype, type
The array or dtype to check.
Returns
-------
boolean : Whether or not the array or dtype is of the string dtype.
Examples
--------
>>> is_string_dtype(str)
True
>>> is_string_dtype(object)
True
>>> is_string_dtype(int)
False
>>>
>>> is_string_dtype(np.array(['a', 'b']))
True
>>> is_string_dtype(np.array([1, 2]))
False
"""

# TODO: gh-15585: consider making the checks stricter.

try:
dtype = _get_dtype(arr_or_dtype)
return dtype.kind in ('O', 'S', 'U') and not is_period_dtype(dtype)
except TypeError:
return False


def is_period_arraylike(arr):
Expand Down Expand Up @@ -237,8 +269,40 @@ def is_datetime64_ns_dtype(arr_or_dtype):


def is_timedelta64_ns_dtype(arr_or_dtype):
tipo = _get_dtype(arr_or_dtype)
return tipo == _TD_DTYPE
"""
Check whether the provided array or dtype is of the timedelta64[ns] dtype.
This is a very specific dtype, so generic ones like `np.timedelta64`
will return False if passed into this function.
Parameters
----------
arr_or_dtype : ndarray, dtype, type
The array or dtype to check.
Returns
-------
boolean : Whether or not the array or dtype
is of the timedelta64[ns] dtype.
Examples
--------
>>> is_timedelta64_ns_dtype(np.dtype('m8[ns]')
True
>>> is_timedelta64_ns_dtype(np.dtype('m8[ps]') # Wrong frequency
False
>>>
>>> is_timedelta64_ns_dtype(np.array([1, 2], dtype='m8[ns]'))
True
>>> is_timedelta64_ns_dtype(np.array([1, 2], dtype=np.timedelta64))
False
"""

try:
tipo = _get_dtype(arr_or_dtype)
return tipo == _TD_DTYPE
except TypeError:
return False


def is_datetime_or_timedelta_dtype(arr_or_dtype):
Expand All @@ -260,8 +324,7 @@ def _is_unorderable_exception(e):
Returns
-------
is_orderable_exc : Whether or not the exception raised is an
unorderable exception.
boolean : Whether or not the exception raised is an unorderable exception.
"""

if PY36:
Expand Down Expand Up @@ -342,9 +405,39 @@ def is_numeric_dtype(arr_or_dtype):


def is_string_like_dtype(arr_or_dtype):
# exclude object as its a mixed dtype
dtype = _get_dtype(arr_or_dtype)
return dtype.kind in ('S', 'U')
"""
Check whether the provided array or dtype is of a string-like dtype.
Unlike `is_string_dtype`, the object dtype is excluded because it
is a mixed dtype.
Parameters
----------
arr_or_dtype : ndarray, dtype, type
The array or dtype to check.
Returns
-------
boolean : Whether or not the array or dtype is of the string dtype.
Examples
--------
>>> is_string_like_dtype(str)
True
>>> is_string_like_dtype(object)
False
>>>
>>> is_string_like_dtype(np.array(['a', 'b']))
True
>>> is_string_like_dtype(np.array([1, 2]))
False
"""

try:
dtype = _get_dtype(arr_or_dtype)
return dtype.kind in ('S', 'U')
except TypeError:
return False


def is_float_dtype(arr_or_dtype):
Expand Down

0 comments on commit 9777867

Please sign in to comment.