Skip to content

cut/paste AccessorProperty and PandasDelegate to core.accessor #17651

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

Merged
merged 3 commits into from
Sep 24, 2017
Merged
Show file tree
Hide file tree
Changes from 2 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
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)
97 changes: 4 additions & 93 deletions pandas/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
deprecate_kwarg, Substitution)
from pandas.core.common import AbstractMethodError, _maybe_box_datetimelike

from pandas.core.accessor import DirNamesMixin
from pandas.core.accessor import (DirNamesMixin,
PandasDelegate as _PandasDelegate)

_shared_docs = dict()
_indexops_doc_kwargs = dict(klass='IndexOpsMixin', inplace='',
Expand Down Expand Up @@ -153,98 +154,8 @@ 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 PandasDelegate(_PandasDelegate, PandasObject):
Copy link
Contributor

Choose a reason for hiding this comment

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

not a fan of this
rather u can simply directly import core.accessor and use it as a mix in class where needed

Copy link
Member Author

Choose a reason for hiding this comment

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

Works for me. Coming right up.

pass


class GroupByError(Exception):
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
2 changes: 1 addition & 1 deletion pandas/core/indexes/accessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
is_timedelta64_dtype, is_categorical_dtype,
is_list_like)

from pandas.core.base import PandasDelegate, NoNewAttributesMixin
from pandas.core.base import NoNewAttributesMixin, PandasDelegate
from pandas.core.indexes.datetimes import DatetimeIndex
from pandas._libs.period import IncompatibleFrequency # noqa
from pandas.core.indexes.period import PeriodIndex
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
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 @@ -2901,19 +2902,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