Skip to content

Commit

Permalink
cut/paste AccessorProperty and PandasDelegate to core.accessor (panda…
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel authored and alanbato committed Nov 10, 2017
1 parent dcaf488 commit 9767693
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 110 deletions.
95 changes: 95 additions & 0 deletions pandas/core/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
that can be mixed into or pinned onto other pandas classes.
"""
from pandas.core.common import AbstractMethodError


class DirNamesMixin(object):
Expand Down Expand Up @@ -33,3 +34,97 @@ def __dir__(self):
rv = set(dir(type(self)))
rv = (rv - self._dir_deletions()) | self._dir_additions()
return sorted(rv)


class AccessorProperty(object):
"""Descriptor for implementing accessor properties like Series.str
"""

def __init__(self, accessor_cls, construct_accessor=None):
self.accessor_cls = accessor_cls
self.construct_accessor = (construct_accessor or
accessor_cls._make_accessor)
self.__doc__ = accessor_cls.__doc__

def __get__(self, instance, owner=None):
if instance is None:
# this ensures that Series.str.<method> is well defined
return self.accessor_cls
return self.construct_accessor(instance)

def __set__(self, instance, value):
raise AttributeError("can't set attribute")

def __delete__(self, instance):
raise AttributeError("can't delete attribute")


class PandasDelegate(object):
""" an abstract base class for delegating methods/properties """

@classmethod
def _make_accessor(cls, data):
raise AbstractMethodError("_make_accessor should be implemented"
"by subclass and return an instance"
"of `cls`.")

def _delegate_property_get(self, name, *args, **kwargs):
raise TypeError("You cannot access the "
"property {name}".format(name=name))

def _delegate_property_set(self, name, value, *args, **kwargs):
raise TypeError("The property {name} cannot be set".format(name=name))

def _delegate_method(self, name, *args, **kwargs):
raise TypeError("You cannot call method {name}".format(name=name))

@classmethod
def _add_delegate_accessors(cls, delegate, accessors, typ,
overwrite=False):
"""
add accessors to cls from the delegate class
Parameters
----------
cls : the class to add the methods/properties to
delegate : the class to get methods/properties & doc-strings
acccessors : string list of accessors to add
typ : 'property' or 'method'
overwrite : boolean, default False
overwrite the method/property in the target class if it exists
"""

def _create_delegator_property(name):

def _getter(self):
return self._delegate_property_get(name)

def _setter(self, new_values):
return self._delegate_property_set(name, new_values)

_getter.__name__ = name
_setter.__name__ = name

return property(fget=_getter, fset=_setter,
doc=getattr(delegate, name).__doc__)

def _create_delegator_method(name):

def f(self, *args, **kwargs):
return self._delegate_method(name, *args, **kwargs)

f.__name__ = name
f.__doc__ = getattr(delegate, name).__doc__

return f

for name in accessors:

if typ == 'property':
f = _create_delegator_property(name)
else:
f = _create_delegator_method(name)

# don't overwrite existing methods/properties
if overwrite or not hasattr(cls, name):
setattr(cls, name, f)
94 changes: 0 additions & 94 deletions pandas/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,100 +153,6 @@ def __setattr__(self, key, value):
object.__setattr__(self, key, value)


class PandasDelegate(PandasObject):
""" an abstract base class for delegating methods/properties """

@classmethod
def _make_accessor(cls, data):
raise AbstractMethodError("_make_accessor should be implemented"
"by subclass and return an instance"
"of `cls`.")

def _delegate_property_get(self, name, *args, **kwargs):
raise TypeError("You cannot access the "
"property {name}".format(name=name))

def _delegate_property_set(self, name, value, *args, **kwargs):
raise TypeError("The property {name} cannot be set".format(name=name))

def _delegate_method(self, name, *args, **kwargs):
raise TypeError("You cannot call method {name}".format(name=name))

@classmethod
def _add_delegate_accessors(cls, delegate, accessors, typ,
overwrite=False):
"""
add accessors to cls from the delegate class
Parameters
----------
cls : the class to add the methods/properties to
delegate : the class to get methods/properties & doc-strings
acccessors : string list of accessors to add
typ : 'property' or 'method'
overwrite : boolean, default False
overwrite the method/property in the target class if it exists
"""

def _create_delegator_property(name):

def _getter(self):
return self._delegate_property_get(name)

def _setter(self, new_values):
return self._delegate_property_set(name, new_values)

_getter.__name__ = name
_setter.__name__ = name

return property(fget=_getter, fset=_setter,
doc=getattr(delegate, name).__doc__)

def _create_delegator_method(name):

def f(self, *args, **kwargs):
return self._delegate_method(name, *args, **kwargs)

f.__name__ = name
f.__doc__ = getattr(delegate, name).__doc__

return f

for name in accessors:

if typ == 'property':
f = _create_delegator_property(name)
else:
f = _create_delegator_method(name)

# don't overwrite existing methods/properties
if overwrite or not hasattr(cls, name):
setattr(cls, name, f)


class AccessorProperty(object):
"""Descriptor for implementing accessor properties like Series.str
"""

def __init__(self, accessor_cls, construct_accessor=None):
self.accessor_cls = accessor_cls
self.construct_accessor = (construct_accessor or
accessor_cls._make_accessor)
self.__doc__ = accessor_cls.__doc__

def __get__(self, instance, owner=None):
if instance is None:
# this ensures that Series.str.<method> is well defined
return self.accessor_cls
return self.construct_accessor(instance)

def __set__(self, instance, value):
raise AttributeError("can't set attribute")

def __delete__(self, instance):
raise AttributeError("can't delete attribute")


class GroupByError(Exception):
pass

Expand Down
5 changes: 3 additions & 2 deletions pandas/core/categorical.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
from pandas.core.common import is_null_slice, _maybe_box_datetimelike

from pandas.core.algorithms import factorize, take_1d, unique1d
from pandas.core.base import (PandasObject, PandasDelegate,
from pandas.core.accessor import PandasDelegate
from pandas.core.base import (PandasObject,
NoNewAttributesMixin, _shared_docs)
import pandas.core.common as com
from pandas.core.missing import interpolate_2d
Expand Down Expand Up @@ -2065,7 +2066,7 @@ def repeat(self, repeats, *args, **kwargs):
# The Series.cat accessor


class CategoricalAccessor(PandasDelegate, NoNewAttributesMixin):
class CategoricalAccessor(PandasDelegate, PandasObject, NoNewAttributesMixin):
"""
Accessor object for categorical properties of the Series values.
Expand Down
5 changes: 3 additions & 2 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
from pandas.core.indexes.datetimes import DatetimeIndex
from pandas.core.indexes.timedeltas import TimedeltaIndex

import pandas.core.base as base
from pandas.core import accessor
import pandas.core.common as com
import pandas.core.nanops as nanops
import pandas.core.ops as ops
Expand Down Expand Up @@ -5897,7 +5897,8 @@ def isin(self, values):

# ----------------------------------------------------------------------
# Add plotting methods to DataFrame
plot = base.AccessorProperty(gfx.FramePlotMethods, gfx.FramePlotMethods)
plot = accessor.AccessorProperty(gfx.FramePlotMethods,
gfx.FramePlotMethods)
hist = gfx.hist_frame
boxplot = gfx.boxplot_frame

Expand Down
5 changes: 3 additions & 2 deletions pandas/core/indexes/accessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
is_timedelta64_dtype, is_categorical_dtype,
is_list_like)

from pandas.core.base import PandasDelegate, NoNewAttributesMixin
from pandas.core.accessor import PandasDelegate
from pandas.core.base import NoNewAttributesMixin, PandasObject
from pandas.core.indexes.datetimes import DatetimeIndex
from pandas._libs.period import IncompatibleFrequency # noqa
from pandas.core.indexes.period import PeriodIndex
Expand Down Expand Up @@ -81,7 +82,7 @@ def maybe_to_datetimelike(data, copy=False):
"datetimelike index".format(type(data)))


class Properties(PandasDelegate, NoNewAttributesMixin):
class Properties(PandasDelegate, PandasObject, NoNewAttributesMixin):

def __init__(self, values, index, name, orig=None):
self.values = values
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
import pandas.core.sorting as sorting
from pandas.io.formats.printing import pprint_thing
from pandas.core.ops import _comp_method_OBJECT_ARRAY
from pandas.core import strings
from pandas.core import strings, accessor
from pandas.core.config import get_option


Expand Down Expand Up @@ -159,7 +159,7 @@ class Index(IndexOpsMixin, PandasObject):
_accessors = frozenset(['str'])

# String Methods
str = base.AccessorProperty(strings.StringMethods)
str = accessor.AccessorProperty(strings.StringMethods)

def __new__(cls, data=None, dtype=None, copy=False, name=None,
fastpath=False, tupleize_cols=True, **kwargs):
Expand Down
3 changes: 2 additions & 1 deletion pandas/core/indexes/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from pandas.util._decorators import Appender, cache_readonly
from pandas.core.config import get_option
from pandas.core.indexes.base import Index, _index_shared_docs
from pandas.core import accessor
import pandas.core.base as base
import pandas.core.missing as missing
import pandas.core.indexes.base as ibase
Expand All @@ -27,7 +28,7 @@
_index_doc_kwargs.update(dict(target_klass='CategoricalIndex'))


class CategoricalIndex(Index, base.PandasDelegate):
class CategoricalIndex(Index, accessor.PandasDelegate):
"""
Immutable Index implementing an ordered, sliceable set. CategoricalIndex
Expand Down
11 changes: 6 additions & 5 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
from pandas.compat import zip, u, OrderedDict, StringIO
from pandas.compat.numpy import function as nv

from pandas.core import accessor
import pandas.core.ops as ops
import pandas.core.algorithms as algorithms

Expand Down Expand Up @@ -2903,19 +2904,19 @@ def to_period(self, freq=None, copy=True):

# -------------------------------------------------------------------------
# Datetimelike delegation methods
dt = base.AccessorProperty(CombinedDatetimelikeProperties)
dt = accessor.AccessorProperty(CombinedDatetimelikeProperties)

# -------------------------------------------------------------------------
# Categorical methods
cat = base.AccessorProperty(CategoricalAccessor)
cat = accessor.AccessorProperty(CategoricalAccessor)

# String Methods
str = base.AccessorProperty(strings.StringMethods)
str = accessor.AccessorProperty(strings.StringMethods)

# ----------------------------------------------------------------------
# Add plotting methods to Series
plot = base.AccessorProperty(gfx.SeriesPlotMethods,
gfx.SeriesPlotMethods)
plot = accessor.AccessorProperty(gfx.SeriesPlotMethods,
gfx.SeriesPlotMethods)
hist = gfx.hist_series


Expand Down
5 changes: 3 additions & 2 deletions pandas/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
CategoricalIndex, Timestamp)
from pandas.compat import StringIO, PYPY, long
from pandas.compat.numpy import np_array_datetime64_compat
from pandas.core.base import PandasDelegate, NoNewAttributesMixin
from pandas.core.accessor import PandasDelegate
from pandas.core.base import PandasObject, NoNewAttributesMixin
from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin
from pandas._libs.tslib import iNaT

Expand Down Expand Up @@ -105,7 +106,7 @@ def bar(self, *args, **kwargs):
""" a test bar method """
pass

class Delegate(PandasDelegate):
class Delegate(PandasDelegate, PandasObject):

def __init__(self, obj):
self.obj = obj
Expand Down

0 comments on commit 9767693

Please sign in to comment.