Skip to content

Commit 8fadfa9

Browse files
committed
Merge pull request #9617 from shoyer/accessors-raise-AttributeError
BUG/API: Accessors like .cat raise AttributeError when invalid
2 parents 549b72f + 0b02747 commit 8fadfa9

File tree

6 files changed

+28
-19
lines changed

6 files changed

+28
-19
lines changed

doc/source/whatsnew/v0.16.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ API Changes
302302

303303
- Bar and horizontal bar plots no longer add a dashed line along the info axis. The prior style can be achieved with matplotlib's ``axhline`` or ``axvline`` methods (:issue:`9088`).
304304

305+
- ``Series`` accessors ``.dt``, ``.cat`` and ``.str`` now raise ``AttributeError`` instead of ``TypeError`` if the series does not contain the appropriate type of data (:issue:`9617`). This follows Python's built-in exception hierarchy more closely and ensures that tests like ``hasattr(s, 'cat')`` are consistent on both Python 2 and 3.
305306

306307
- ``Series`` now supports bitwise operation for integral types (:issue:`9016`). Previously even if the input dtypes were integral, the output dtype was coerced to ``bool``.
307308

pandas/core/generic.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ class NDFrame(PandasObject):
7878
copy : boolean, default False
7979
"""
8080
_internal_names = ['_data', '_cacher', '_item_cache', '_cache',
81-
'is_copy', 'str', '_subtyp', '_index', '_default_kind',
82-
'_default_fill_value','__array_struct__','__array_interface__']
81+
'is_copy', 'dt', 'cat', 'str', '_subtyp', '_index',
82+
'_default_kind', '_default_fill_value',
83+
'__array_struct__','__array_interface__']
8384
_internal_names_set = set(_internal_names)
8485
_metadata = []
8586
is_copy = None

pandas/core/series.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -2521,8 +2521,9 @@ def _make_str_accessor(self):
25212521
# this really should exclude all series with any non-string values,
25222522
# but that isn't practical for performance reasons until we have a
25232523
# str dtype (GH 9343)
2524-
raise TypeError("Can only use .str accessor with string values, "
2525-
"which use np.object_ dtype in pandas")
2524+
raise AttributeError("Can only use .str accessor with string "
2525+
"values, which use np.object_ dtype in "
2526+
"pandas")
25262527
return StringMethods(self)
25272528

25282529
str = base.AccessorProperty(StringMethods, _make_str_accessor)
@@ -2533,8 +2534,9 @@ def _make_str_accessor(self):
25332534
def _make_dt_accessor(self):
25342535
try:
25352536
return maybe_to_datetimelike(self)
2536-
except (Exception):
2537-
raise TypeError("Can only use .dt accessor with datetimelike values")
2537+
except Exception:
2538+
raise AttributeError("Can only use .dt accessor with datetimelike "
2539+
"values")
25382540

25392541
dt = base.AccessorProperty(CombinedDatetimelikeProperties, _make_dt_accessor)
25402542

@@ -2543,7 +2545,8 @@ def _make_dt_accessor(self):
25432545

25442546
def _make_cat_accessor(self):
25452547
if not com.is_categorical_dtype(self.dtype):
2546-
raise TypeError("Can only use .cat accessor with a 'category' dtype")
2548+
raise AttributeError("Can only use .cat accessor with a "
2549+
"'category' dtype")
25472550
return CategoricalAccessor(self.values, self.index)
25482551

25492552
cat = base.AccessorProperty(CategoricalAccessor, _make_cat_accessor)

pandas/tests/test_categorical.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -2631,8 +2631,11 @@ def test_cat_accessor_api(self):
26312631
self.assertIs(Series.cat, CategoricalAccessor)
26322632
s = Series(list('aabbcde')).astype('category')
26332633
self.assertIsInstance(s.cat, CategoricalAccessor)
2634-
with tm.assertRaisesRegexp(TypeError, "only use .cat accessor"):
2635-
Series([1]).cat
2634+
2635+
invalid = Series([1])
2636+
with tm.assertRaisesRegexp(AttributeError, "only use .cat accessor"):
2637+
invalid.cat
2638+
self.assertFalse(hasattr(invalid, 'cat'))
26362639

26372640
def test_pickle_v0_14_1(self):
26382641
cat = pd.Categorical(values=['a', 'b', 'c'],

pandas/tests/test_series.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,6 @@ def compare(s, name):
104104
else:
105105
tm.assert_series_equal(a,b)
106106

107-
# invalids
108-
for s in [Series(np.arange(5)),
109-
Series(list('abcde')),
110-
Series(np.random.randn(5))]:
111-
self.assertRaises(TypeError, lambda : s.dt)
112-
113107
# datetimeindex
114108
for s in [Series(date_range('20130101',periods=5)),
115109
Series(date_range('20130101',periods=5,freq='s')),
@@ -240,8 +234,13 @@ def test_dt_accessor_api(self):
240234
s = Series(date_range('2000-01-01', periods=3))
241235
self.assertIsInstance(s.dt, DatetimeProperties)
242236

243-
with tm.assertRaisesRegexp(TypeError, "only use .dt accessor"):
244-
Series([1]).dt
237+
for s in [Series(np.arange(5)),
238+
Series(list('abcde')),
239+
Series(np.random.randn(5))]:
240+
with tm.assertRaisesRegexp(AttributeError,
241+
"only use .dt accessor"):
242+
s.dt
243+
self.assertFalse(hasattr(s, 'dt'))
245244

246245
def test_binop_maybe_preserve_name(self):
247246

pandas/tests/test_strings.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ def test_api(self):
3737
self.assertIsInstance(Series(['']).str, strings.StringMethods)
3838

3939
# GH 9184
40-
with tm.assertRaisesRegexp(TypeError, "only use .str accessor"):
41-
Series([1]).str
40+
invalid = Series([1])
41+
with tm.assertRaisesRegexp(AttributeError, "only use .str accessor"):
42+
invalid.str
43+
self.assertFalse(hasattr(invalid, 'str'))
4244

4345
def test_iter(self):
4446
# GH3638

0 commit comments

Comments
 (0)