diff --git a/pandas/core/base.py b/pandas/core/base.py index a3d3c3791e20c..a25651a73f507 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -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='', @@ -32,7 +32,7 @@ class StringMixin(object): # Formatting def __unicode__(self): - raise NotImplementedError + raise AbstractMethodError(self) def __str__(self): """ @@ -566,4 +566,4 @@ def duplicated(self, take_last=False): # abstracts def _update_inplace(self, result, **kwargs): - raise NotImplementedError + raise AbstractMethodError(self) diff --git a/pandas/core/categorical.py b/pandas/core/categorical.py index b79f2c9b4f6df..0d66a89b0a585 100644 --- a/pandas/core/categorical.py +++ b/pandas/core/categorical.py @@ -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 diff --git a/pandas/core/common.py b/pandas/core/common.py index 0fb35c2fb02fc..7c449ff9ef561 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -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', diff --git a/pandas/core/generic.py b/pandas/core/generic.py index bc65f1f62fa1a..a99df54650246 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -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 @@ -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 @@ -152,7 +153,7 @@ def _local_dir(self): @property def _constructor_sliced(self): - raise NotImplementedError + raise AbstractMethodError(self) #---------------------------------------------------------------------- # Axis @@ -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): """ @@ -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 @@ -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 diff --git a/pandas/core/groupby.py b/pandas/core/groupby.py index 6d98b3b99021b..c80cc21e79e67 100644 --- a/pandas/core/groupby.py +++ b/pandas/core/groupby.py @@ -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 @@ -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): @@ -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): @@ -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): """ @@ -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 @@ -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) @@ -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)): @@ -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) @@ -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, @@ -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 @@ -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 @@ -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: @@ -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): @@ -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): diff --git a/pandas/core/internals.py b/pandas/core/internals.py index 9b2d366bfb2be..269f692f8c4b7 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -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) @@ -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, @@ -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) @@ -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() diff --git a/pandas/core/panelnd.py b/pandas/core/panelnd.py index ec0a313ff5767..d021cb2d59ecf 100644 --- a/pandas/core/panelnd.py +++ b/pandas/core/panelnd.py @@ -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 diff --git a/pandas/core/series.py b/pandas/core/series.py index b71c269468d62..f9c56db018639 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -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: diff --git a/pandas/io/html.py b/pandas/io/html.py index 9f5c10ce128d2..b806b5147c4a5 100644 --- a/pandas/io/html.py +++ b/pandas/io/html.py @@ -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 @@ -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. @@ -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. @@ -270,7 +271,7 @@ def _parse_tables(self, doc, match, attrs): tables : list of node-like A list of
elements. """ - raise NotImplementedError + raise AbstractMethodError(self) def _parse_thead(self, table): """Return the header of a table. @@ -300,7 +301,7 @@ def _parse_thead(self, table): thead : node-like A ... element. """ - raise NotImplementedError + raise AbstractMethodError(self) def _parse_tbody(self, table): """Return the body of the table. @@ -315,7 +316,7 @@ def _parse_tbody(self, table): tbody : node-like A |
---|