Skip to content

NotImplementedError messages doc update #7872 #7899

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 3 commits 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
4 changes: 2 additions & 2 deletions pandas/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class StringMixin(object):
# Formatting

def __unicode__(self):
raise NotImplementedError
raise NotImplementedError("StringMixin __unicode__ format")

def __str__(self):
"""
Expand Down Expand Up @@ -382,7 +382,7 @@ def _box_func(self):
"""
box function to get object from internal representation
"""
raise NotImplementedError
raise NotImplementedError("Box function to get object from internal representation")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are both abstract as well


def _box_values(self, values):
"""
Expand Down
9 changes: 7 additions & 2 deletions pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class SettingWithCopyWarning(Warning):
class AmbiguousIndexError(PandasError, KeyError):
pass

class AbstractMethodError(NotImplementedError):
def __init__(self,m):
self.message = m
def __str__(self):
return "This method must be defined on the concrete class - "+self.message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be nice to include the problematic class name in error message.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use inspect to do this (would need some testing esp. 2.6 & 3.x):

import inspect
class AbstractMethodError(NotImplementedError):
    def __str__(self):
        return "%s must be defined on the concrete class of %s." % (self.method, self.klass)
    def __init__(self, message=None):
        method_frame = inspect.currentframe().f_back
        self.klass = method_frame.f_locals['self'].__class__.__name__
        self.method = inspect.getframeinfo(method_frame).function

...
class A(object):
    def foo(self):
        raise AbstractMethodError()

In [11]: A().foo()
---------------------------------------------------------------------------
AbstractMethodError                       Traceback (most recent call last)
<ipython-input-11-fcb97135a52f> in <module>()
----> 1 A().foo()

<ipython-input-11-845149caecea> in foo(self)
      1 class A(object):
      2     def foo(self):
----> 3         raise AbstractMethodError()
      4 

AbstractMethodError: foo must be defined on the concrete class of A.

(perhaps there's a neater way?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't __class__.__name__ enough without inspect?

import pandas.core.common as com


class A(object):
    def test(self):
        raise NotImplementedError(com.pprint_thing(self.__class__.__name__))

class B(A):
    pass

B().test()
# NotImplementedError: B

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back, I'd hoped/wanted this to print A... dang.


_POSSIBLY_CAST_DTYPES = set([np.dtype(t).name
for t in ['O', 'int8',
Expand Down Expand Up @@ -134,7 +139,7 @@ def _isnull_new(obj):
return lib.checknull(obj)
# hack (for now) because MI registers as ndarray
elif isinstance(obj, pd.MultiIndex):
raise NotImplementedError("isnull is not defined for MultiIndex")
raise AbstractMethodError("isnull is not defined for MultiIndex")
elif isinstance(obj, (ABCSeries, np.ndarray)):
return _isnull_ndarraylike(obj)
elif isinstance(obj, ABCGeneric):
Expand All @@ -160,7 +165,7 @@ def _isnull_old(obj):
return lib.checknull_old(obj)
# hack (for now) because MI registers as ndarray
elif isinstance(obj, pd.MultiIndex):
raise NotImplementedError("isnull is not defined for MultiIndex")
raise AbstractMethodError("isnull is not defined for MultiIndex")
elif isinstance(obj, (ABCSeries, np.ndarray)):
return _isnull_ndarraylike_old(obj)
elif isinstance(obj, ABCGeneric):
Expand Down
14 changes: 7 additions & 7 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def _init_mgr(self, mgr, axes=None, dtype=None, copy=False):

@property
def _constructor(self):
raise NotImplementedError
raise NotImplementedError("NDFrame _constructor")

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

@property
def _constructor_sliced(self):
raise NotImplementedError
raise NotImplementedError("NDFrame _constructor_sliced")

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

def _box_item_values(self, key, values):
raise NotImplementedError
raise NotImplementedError("NDFrame _box_item_values")

def _maybe_cache_changed(self, item, value):
"""
Expand Down Expand Up @@ -1653,7 +1653,7 @@ def _needs_reindex_multi(self, axes, method, level):
method is None and level is None and not self._is_mixed_type)

def _reindex_multi(self, axes, copy, fill_value):
return NotImplemented
raise NotImplementedError("NDFrame _reindex_multi")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first 4 are abstract


_shared_docs['reindex_axis'] = (
"""Conform input object to new index with optional filling logic,
Expand Down Expand Up @@ -2179,7 +2179,7 @@ def fillna(self, value=None, method=None, axis=0, inplace=False,
raise ValueError('must specify a fill method or value')
if self._is_mixed_type and axis == 1:
if inplace:
raise NotImplementedError()
raise NotImplementedError("fillna with inplace=True and _is_mixed_type=True and axis=1")
result = self.T.fillna(method=method, limit=limit).T

# need to downcast here because of all of the transposes
Expand Down Expand Up @@ -2880,7 +2880,7 @@ def first(self, offset):
"""
from pandas.tseries.frequencies import to_offset
if not isinstance(self.index, DatetimeIndex):
raise NotImplementedError
raise NotImplementedError("Currently implemented for DatatimeIndex instance only")

if len(self.index) == 0:
return self
Expand Down Expand Up @@ -2914,7 +2914,7 @@ def last(self, offset):
"""
from pandas.tseries.frequencies import to_offset
if not isinstance(self.index, DatetimeIndex):
raise NotImplementedError
raise NotImplementedError("Currently implemented for DatatimeIndex instance only")

if len(self.index) == 0:
return self
Expand Down
28 changes: 14 additions & 14 deletions pandas/core/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def _set_grouper(self, obj, sort=False):
return self.grouper

def _get_binner_for_grouping(self, obj):
raise NotImplementedError
raise NotImplementedError("Binner for grouping")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are all abstract


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

def aggregate(self, func, *args, **kwargs):
raise NotImplementedError
raise NotImplementedError("Groupby aggregrate")

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

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

def mean(self):
"""
Expand Down Expand Up @@ -1041,7 +1041,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 NotImplementedError("Groupby wrap applied output")

def _concat_objects(self, keys, values, not_indexed_same=False):
from pandas.tools.merge import concat
Expand Down Expand Up @@ -1404,7 +1404,7 @@ def aggregate(self, values, how, axis=0):
swapped = True
values = values.swapaxes(0, axis)
if arity > 1:
raise NotImplementedError
raise NotImplementedError("BaseGrouper aggregate for arity > 1")
out_shape = (self.ngroups,) + values.shape[1:]

if is_numeric_dtype(values.dtype):
Expand Down Expand Up @@ -1459,7 +1459,7 @@ def _aggregate(self, result, counts, values, how, is_numeric):
comp_ids, _, ngroups = self.group_info
if values.ndim > 3:
# punting for now
raise NotImplementedError
raise NotImplementedError("BaseGrouper aggregrate for > 3 dimensions")
elif values.ndim > 2:
for i, chunk in enumerate(values.transpose(2, 0, 1)):

Expand Down Expand Up @@ -1695,7 +1695,7 @@ def _aggregate(self, result, counts, values, how, is_numeric=True):

if values.ndim > 3:
# punting for now
raise NotImplementedError
raise NotImplementedError("BinGrouper aggregate for > 3 dimensions")
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 @@ -2399,7 +2399,7 @@ def aggregate(self, arg, *args, **kwargs):
if self._selection is not None:
subset = obj
if isinstance(subset, DataFrame):
raise NotImplementedError
raise NotImplementedError("aggregate not implemented for subset being a Dataframe")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and next one are ok as NotImplementedError


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

if self.axis != 0:
raise NotImplementedError
raise NotImplementedError("Currently implemented for axis = 0")

obj = self._obj_with_exclusions

Expand Down Expand Up @@ -2509,7 +2509,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 NotImplementedError("NDFrameGroupBy wrap aggregated output")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstract


def _aggregate_item_by_item(self, func, *args, **kwargs):
# only for axis==0
Expand Down Expand Up @@ -3050,7 +3050,7 @@ def _iterate_slices(self):
slice_axis = self._selection_list
slicer = lambda x: self._selected_obj[x]
else:
raise NotImplementedError
raise NotImplementedError("Currently implemented for axis = 0")

for val in slice_axis:
if val in self.exclusions:
Expand Down Expand Up @@ -3115,10 +3115,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 NotImplementedError("Currently implemented for axis>0")

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


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

def apply(self, f):
raise NotImplementedError
raise NotImplementedError("DataSplitter apply function")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstract



class ArraySplitter(DataSplitter):
Expand Down
12 changes: 6 additions & 6 deletions pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pandas.core.common as com
from pandas.core.common import (_is_bool_indexer, is_integer_dtype,
_asarray_tuplesafe, is_list_like, isnull,
ABCSeries, ABCDataFrame, ABCPanel, is_float)
ABCSeries, ABCDataFrame, ABCPanel, is_float, AbstractMethodError)
import pandas.lib as lib

import numpy as np
Expand Down Expand Up @@ -55,7 +55,7 @@ def __call__(self, *args, **kwargs):
return self

def __iter__(self):
raise NotImplementedError('ix is not iterable')
raise AbstractMethodError('ix is not iterable')

def __getitem__(self, key):
if type(key) is tuple:
Expand Down Expand Up @@ -120,7 +120,7 @@ def __setitem__(self, key, value):
self._setitem_with_indexer(indexer, value)

def _has_valid_type(self, k, axis):
raise NotImplementedError()
raise AbstractMethodError("Valid type checking for _NDFrameIndexer is not implemented")

def _has_valid_tuple(self, key):
""" check the key for valid keys across my indexer """
Expand Down Expand Up @@ -644,7 +644,7 @@ def _align_frame(self, indexer, df):
def _align_panel(self, indexer, df):
is_frame = self.obj.ndim == 2
is_panel = self.obj.ndim >= 3
raise NotImplementedError("cannot set using an indexer with a Panel "
raise AbstractMethodError("cannot set using an indexer with a Panel "
"yet!")

def _getitem_tuple(self, tup):
Expand Down Expand Up @@ -1141,7 +1141,7 @@ def __getitem__(self, key):
return self._getitem_axis(key, axis=0)

def _getitem_axis(self, key, axis=0, validate_iterable=False):
raise NotImplementedError()
raise AbstractMethodError("Get item along given axis in _LocationIndexer is not implemented")

def _getbool_axis(self, key, axis=0):
labels = self.obj._get_axis(axis)
Expand Down Expand Up @@ -1299,7 +1299,7 @@ def _has_valid_type(self, key, axis):
if com._is_bool_indexer(key):
if hasattr(key, 'index') and isinstance(key.index, Index):
if key.index.inferred_type == 'integer':
raise NotImplementedError(
raise AbstractMethodError(
"iLocation based boolean indexing on an integer type "
"is not available"
)
Expand Down
10 changes: 5 additions & 5 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ 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("fillna function not implemented for more than 2 dimensions")
mask[mask.cumsum(self.ndim-1)>limit]=False

value = self._try_fill(value)
Expand Down Expand Up @@ -363,10 +363,10 @@ def convert(self, copy=True, **kwargs):
return [self.copy()] if copy else [self]

def _can_hold_element(self, value):
raise NotImplementedError()
raise NotImplementedError("Block _can_hold_element function")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these 2 are abstract


def _try_cast(self, value):
raise NotImplementedError()
raise NotImplementedError("Block _try_cast")

def _try_cast_result(self, result, dtype=None):
""" try to cast the result to our original type,
Expand Down Expand Up @@ -1519,7 +1519,7 @@ def fillna(self, value, limit=None,
value = self._try_fill(value)
if limit is not None:
if self.ndim > 2:
raise NotImplementedError
raise NotImplementedError("fillna function not implemented for more than 2 dimensions")
mask[mask.cumsum(self.ndim-1)>limit]=False

np.putmask(values, mask, value)
Expand Down Expand Up @@ -1741,7 +1741,7 @@ 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("fillna currently implemented only for limit=None")
if issubclass(self.dtype.type, np.floating):
value = float(value)
values = self.values if inplace else self.values.copy()
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,10 +595,10 @@ def conform(self, frame, axis='items'):
return frame.reindex(**self._extract_axes_for_slice(self, axes))

def head(self, n=5):
raise NotImplementedError
raise NotImplementedError("Returns the head content of a Panel")

def tail(self, n=5):
raise NotImplementedError
raise NotImplementedError("Returns the tail content of a Panel")

def _needs_reindex_multi(self, axes, method, level):
""" don't allow a multi reindex on Panel or above ndim """
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("%s is not implemented" % f)
setattr(klass, f, func)

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

if isinstance(data, MultiIndex):
raise NotImplementedError
raise NotImplementedError("Series initialization currently is not implemented for MultiIndex")
elif isinstance(data, Index):
# need to copy to avoid aliasing issues
if name is None:
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ def str_slice_replace(arr, start=None, stop=None, repl=None):
-------
replaced : array
"""
raise NotImplementedError
raise NotImplementedError("String slice replace not implemented")


def str_strip(arr, to_strip=None):
Expand Down Expand Up @@ -985,7 +985,7 @@ def slice(self, start=None, stop=None, step=1):

@copy(str_slice)
def slice_replace(self, i=None, j=None):
raise NotImplementedError
raise NotImplementedError("String slice replace is not implemented.")

@copy(str_decode)
def decode(self, encoding, errors="strict"):
Expand Down
4 changes: 2 additions & 2 deletions pandas/index.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,10 @@ cdef class IndexEngine:
self._ensure_mapping_populated()

def _call_monotonic(self, values):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both are abstract

raise NotImplementedError
raise NotImplementedError("IndexEngine _call_monotonic is not implemented")

cdef _make_hash_table(self, n):
raise NotImplementedError
raise NotImplementedError("IndexEngine _make_hash_table is not implemented")

cdef _check_type(self, object val):
hash(val)
Expand Down
2 changes: 1 addition & 1 deletion pandas/io/ga.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def get_profile(self, account_id=None, web_property_id=None, name=None,
return _get_match(profiles, name, id, **kwargs)

def create_query(self, *args, **kwargs):
raise NotImplementedError()
raise NotImplementedError("Create query in GDataReader is not implemented")

@Substitution(extras='')
@Appender(_GA_READER_DOC)
Expand Down
Loading