Skip to content

FIX: Describe NotImplementedErrors or use AbstractMethodError #9887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pandas/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import pandas.lib as lib
from pandas.util.decorators import Appender, cache_readonly
from pandas.core.strings import StringMethods

from pandas.core.common import AbstractMethodError

_shared_docs = dict()
_indexops_doc_kwargs = dict(klass='IndexOpsMixin', inplace='',
Expand All @@ -32,7 +32,7 @@ class StringMixin(object):
# Formatting

def __unicode__(self):
raise NotImplementedError
raise AbstractMethodError(self)

def __str__(self):
"""
Expand Down Expand Up @@ -566,4 +566,4 @@ def duplicated(self, take_last=False):
# abstracts

def _update_inplace(self, result, **kwargs):
raise NotImplementedError
raise AbstractMethodError(self)
3 changes: 2 additions & 1 deletion pandas/core/categorical.py
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,8 @@ def fillna(self, fill_value=None, method=None, limit=None):
if fill_value is None:
fill_value = np.nan
if limit is not None:
raise NotImplementedError
raise NotImplementedError("specifying a limit for fillna has not "
"been implemented yet")

values = self._codes

Expand Down
11 changes: 11 additions & 0 deletions pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ class AmbiguousIndexError(PandasError, KeyError):
pass


class AbstractMethodError(NotImplementedError):
"""Raise this error instead of NotImplementedError for abstract methods
while keeping compatibility with Python 2 and Python 3.
"""
def __init__(self, class_instance):
self.class_instance = class_instance

def __str__(self):
return "This method must be defined on the concrete class of " \
+ self.class_instance.__class__.__name__

_POSSIBLY_CAST_DTYPES = set([np.dtype(t).name
for t in ['O', 'int8',
'uint8', 'int16', 'uint16', 'int32',
Expand Down
15 changes: 9 additions & 6 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
from pandas.core.common import (isnull, notnull, is_list_like,
_values_from_object, _maybe_promote,
_maybe_box_datetimelike, ABCSeries,
SettingWithCopyError, SettingWithCopyWarning)
SettingWithCopyError, SettingWithCopyWarning,
AbstractMethodError)
import pandas.core.nanops as nanops
from pandas.util.decorators import Appender, Substitution, deprecate_kwarg
from pandas.core import config
Expand Down Expand Up @@ -137,7 +138,7 @@ def _init_mgr(self, mgr, axes=None, dtype=None, copy=False):

@property
def _constructor(self):
raise NotImplementedError
raise AbstractMethodError(self)

def __unicode__(self):
# unicode representation based upon iterating over self
Expand All @@ -152,7 +153,7 @@ def _local_dir(self):

@property
def _constructor_sliced(self):
raise NotImplementedError
raise AbstractMethodError(self)

#----------------------------------------------------------------------
# Axis
Expand Down Expand Up @@ -1100,7 +1101,7 @@ def _iget_item_cache(self, item):
return lower

def _box_item_values(self, key, values):
raise NotImplementedError
raise AbstractMethodError(self)

def _maybe_cache_changed(self, item, value):
"""
Expand Down Expand Up @@ -3057,7 +3058,8 @@ def first(self, offset):
"""
from pandas.tseries.frequencies import to_offset
if not isinstance(self.index, DatetimeIndex):
raise NotImplementedError
raise NotImplementedError("'first' only supports a DatetimeIndex "
"index")

if len(self.index) == 0:
return self
Expand Down Expand Up @@ -3091,7 +3093,8 @@ def last(self, offset):
"""
from pandas.tseries.frequencies import to_offset
if not isinstance(self.index, DatetimeIndex):
raise NotImplementedError
raise NotImplementedError("'last' only supports a DatetimeIndex "
"index")

if len(self.index) == 0:
return self
Expand Down
35 changes: 20 additions & 15 deletions pandas/core/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
notnull, _DATELIKE_DTYPES, is_numeric_dtype,
is_timedelta64_dtype, is_datetime64_dtype,
is_categorical_dtype, _values_from_object,
is_datetime_or_timedelta_dtype, is_bool_dtype)
is_datetime_or_timedelta_dtype, is_bool_dtype,
AbstractMethodError)
from pandas.core.config import option_context
import pandas.lib as lib
from pandas.lib import Timestamp
Expand Down Expand Up @@ -279,7 +280,7 @@ def _set_grouper(self, obj, sort=False):
return self.grouper

def _get_binner_for_grouping(self, obj):
raise NotImplementedError
raise AbstractMethodError(self)

@property
def groups(self):
Expand Down Expand Up @@ -670,7 +671,7 @@ def _python_apply_general(self, f):
not_indexed_same=mutated)

def aggregate(self, func, *args, **kwargs):
raise NotImplementedError
raise AbstractMethodError(self)

@Appender(_agg_doc)
def agg(self, func, *args, **kwargs):
Expand All @@ -680,7 +681,7 @@ def _iterate_slices(self):
yield self.name, self._selected_obj

def transform(self, func, *args, **kwargs):
raise NotImplementedError
raise AbstractMethodError(self)

def mean(self):
"""
Expand Down Expand Up @@ -1127,7 +1128,7 @@ def _python_agg_general(self, func, *args, **kwargs):
return self._wrap_aggregated_output(output)

def _wrap_applied_output(self, *args, **kwargs):
raise NotImplementedError
raise AbstractMethodError(self)

def _concat_objects(self, keys, values, not_indexed_same=False):
from pandas.tools.merge import concat
Expand Down Expand Up @@ -1484,7 +1485,8 @@ def aggregate(self, values, how, axis=0):
swapped = True
values = values.swapaxes(0, axis)
if arity > 1:
raise NotImplementedError
raise NotImplementedError("arity of more than 1 is not "
"supported for the 'how' argument")
out_shape = (self.ngroups,) + values.shape[1:]

is_numeric = is_numeric_dtype(values.dtype)
Expand Down Expand Up @@ -1556,7 +1558,8 @@ def _aggregate(self, result, counts, values, agg_func, is_numeric):
comp_ids, _, ngroups = self.group_info
if values.ndim > 3:
# punting for now
raise NotImplementedError
raise NotImplementedError("number of dimensions is currently "
"limited to 3")
elif values.ndim > 2:
for i, chunk in enumerate(values.transpose(2, 0, 1)):

Expand Down Expand Up @@ -1815,7 +1818,8 @@ def _aggregate(self, result, counts, values, agg_func, is_numeric=True):

if values.ndim > 3:
# punting for now
raise NotImplementedError
raise NotImplementedError("number of dimensions is currently "
"limited to 3")
elif values.ndim > 2:
for i, chunk in enumerate(values.transpose(2, 0, 1)):
agg_func(result[:, :, i], counts, chunk, self.bins)
Expand Down Expand Up @@ -2622,7 +2626,8 @@ def aggregate(self, arg, *args, **kwargs):
if self._selection is not None:
subset = obj
if isinstance(subset, DataFrame):
raise NotImplementedError
raise NotImplementedError("Aggregating on a DataFrame is "
"not supported")

for fname, agg_how in compat.iteritems(arg):
colg = SeriesGroupBy(subset, selection=self._selection,
Expand Down Expand Up @@ -2671,7 +2676,7 @@ def _aggregate_multiple_funcs(self, arg):
from pandas.tools.merge import concat

if self.axis != 0:
raise NotImplementedError
raise NotImplementedError("axis other than 0 is not supported")

obj = self._obj_with_exclusions

Expand Down Expand Up @@ -2721,7 +2726,7 @@ def _aggregate_generic(self, func, *args, **kwargs):
return self._wrap_generic_output(result, obj)

def _wrap_aggregated_output(self, output, names=None):
raise NotImplementedError
raise AbstractMethodError(self)

def _aggregate_item_by_item(self, func, *args, **kwargs):
# only for axis==0
Expand Down Expand Up @@ -3283,7 +3288,7 @@ def _iterate_slices(self):
slice_axis = self._selection_list
slicer = lambda x: self._selected_obj[x]
else:
raise NotImplementedError
raise NotImplementedError("axis other than 0 is not supported")

for val in slice_axis:
if val in self.exclusions:
Expand Down Expand Up @@ -3348,10 +3353,10 @@ def _aggregate_item_by_item(self, func, *args, **kwargs):
new_axes[self.axis] = self.grouper.result_index
return Panel._from_axes(result, new_axes)
else:
raise NotImplementedError
raise ValueError("axis value must be greater than 0")

def _wrap_aggregated_output(self, output, names=None):
raise NotImplementedError
raise AbstractMethodError(self)


class NDArrayGroupBy(GroupBy):
Expand Down Expand Up @@ -3405,7 +3410,7 @@ def _chop(self, sdata, slice_obj):
return sdata.iloc[slice_obj]

def apply(self, f):
raise NotImplementedError
raise AbstractMethodError(self)


class ArraySplitter(DataSplitter):
Expand Down
12 changes: 8 additions & 4 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ def fillna(self, value, limit=None, inplace=False, downcast=None):
mask = isnull(self.values)
if limit is not None:
if self.ndim > 2:
raise NotImplementedError
raise NotImplementedError("number of dimensions for 'fillna' "
"is currently limited to 2")
mask[mask.cumsum(self.ndim-1)>limit]=False

value = self._try_fill(value)
Expand Down Expand Up @@ -1681,7 +1682,8 @@ def _slice(self, slicer):
def fillna(self, value, limit=None, inplace=False, downcast=None):
# we may need to upcast our fill to match our dtype
if limit is not None:
raise NotImplementedError
raise NotImplementedError("specifying a limit for 'fillna' has "
"not been implemented yet")

values = self.values if inplace else self.values.copy()
return [self.make_block_same_class(values=values.fillna(fill_value=value,
Expand Down Expand Up @@ -1848,7 +1850,8 @@ def fillna(self, value, limit=None,
value = self._try_fill(value)
if limit is not None:
if self.ndim > 2:
raise NotImplementedError
raise NotImplementedError("number of dimensions for 'fillna' "
"is currently limited to 2")
mask[mask.cumsum(self.ndim-1)>limit]=False

np.putmask(values, mask, value)
Expand Down Expand Up @@ -2011,7 +2014,8 @@ def interpolate(self, method='pad', axis=0, inplace=False,
def fillna(self, value, limit=None, inplace=False, downcast=None):
# we may need to upcast our fill to match our dtype
if limit is not None:
raise NotImplementedError
raise NotImplementedError("specifying a limit for 'fillna' has "
"not been implemented yet")
if issubclass(self.dtype.type, np.floating):
value = float(value)
values = self.values if inplace else self.values.copy()
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/panelnd.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def _combine_with_constructor(self, other, func):
for f in ['to_frame', 'to_excel', 'to_sparse', 'groupby', 'join', 'filter',
'dropna', 'shift']:
def func(self, *args, **kwargs):
raise NotImplementedError
raise NotImplementedError("this operation is not supported")
setattr(klass, f, func)

# add the aggregate operations
Expand Down
3 changes: 2 additions & 1 deletion pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
dtype = self._validate_dtype(dtype)

if isinstance(data, MultiIndex):
raise NotImplementedError
raise NotImplementedError("initializing a Series from a "
"MultiIndex is not supported")
elif isinstance(data, Index):
# need to copy to avoid aliasing issues
if name is None:
Expand Down
17 changes: 9 additions & 8 deletions pandas/io/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
raise_with_traceback, binary_type)
from pandas.core import common as com
from pandas import Series
from pandas.core.common import AbstractMethodError

_IMPORTS = False
_HAS_BS4 = False
Expand Down Expand Up @@ -229,7 +230,7 @@ def _text_getter(self, obj):
text : str or unicode
The text from an individual DOM node.
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _parse_td(self, obj):
"""Return the td elements from a row element.
Expand All @@ -243,7 +244,7 @@ def _parse_td(self, obj):
columns : list of node-like
These are the elements of each row, i.e., the columns.
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _parse_tables(self, doc, match, attrs):
"""Return all tables from the parsed DOM.
Expand All @@ -270,7 +271,7 @@ def _parse_tables(self, doc, match, attrs):
tables : list of node-like
A list of <table> elements to be parsed into raw data.
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _parse_tr(self, table):
"""Return the list of row elements from the parsed table element.
Expand All @@ -285,7 +286,7 @@ def _parse_tr(self, table):
rows : list of node-like
A list row elements of a table, usually <tr> or <th> elements.
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _parse_thead(self, table):
"""Return the header of a table.
Expand All @@ -300,7 +301,7 @@ def _parse_thead(self, table):
thead : node-like
A <thead>...</thead> element.
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _parse_tbody(self, table):
"""Return the body of the table.
Expand All @@ -315,7 +316,7 @@ def _parse_tbody(self, table):
tbody : node-like
A <tbody>...</tbody> element.
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _parse_tfoot(self, table):
"""Return the footer of the table if any.
Expand All @@ -330,7 +331,7 @@ def _parse_tfoot(self, table):
tfoot : node-like
A <tfoot>...</tfoot> element.
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _build_doc(self):
"""Return a tree-like object that can be used to iterate over the DOM.
Expand All @@ -339,7 +340,7 @@ def _build_doc(self):
-------
obj : tree-like
"""
raise NotImplementedError
raise AbstractMethodError(self)

def _build_table(self, table):
header = self._parse_raw_thead(table)
Expand Down
Loading