Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/core/accessor.py: 64%
80 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"""
3accessor.py contains base classes for implementing accessor properties
4that can be mixed into or pinned onto other pandas classes.
6"""
7from __future__ import annotations
9import warnings
11from pandas.util._decorators import doc
12from pandas.util._exceptions import find_stack_level
15class DirNamesMixin:
16 _accessors: set[str] = set()
17 _hidden_attrs: frozenset[str] = frozenset()
19 def _dir_deletions(self) -> set[str]:
20 """
21 Delete unwanted __dir__ for this object.
22 """
23 return self._accessors | self._hidden_attrs
25 def _dir_additions(self) -> set[str]:
26 """
27 Add additional __dir__ for this object.
28 """
29 return {accessor for accessor in self._accessors if hasattr(self, accessor)}
31 def __dir__(self) -> list[str]:
32 """
33 Provide method name lookup and completion.
35 Notes
36 -----
37 Only provide 'public' methods.
38 """
39 rv = set(super().__dir__())
40 rv = (rv - self._dir_deletions()) | self._dir_additions()
41 return sorted(rv)
44class PandasDelegate:
45 """
46 Abstract base class for delegating methods/properties.
47 """
49 def _delegate_property_get(self, name, *args, **kwargs):
50 raise TypeError(f"You cannot access the property {name}")
52 def _delegate_property_set(self, name, value, *args, **kwargs):
53 raise TypeError(f"The property {name} cannot be set")
55 def _delegate_method(self, name, *args, **kwargs):
56 raise TypeError(f"You cannot call method {name}")
58 @classmethod
59 def _add_delegate_accessors(
60 cls, delegate, accessors, typ: str, overwrite: bool = False
61 ):
62 """
63 Add accessors to cls from the delegate class.
65 Parameters
66 ----------
67 cls
68 Class to add the methods/properties to.
69 delegate
70 Class to get methods/properties and doc-strings.
71 accessors : list of str
72 List of accessors to add.
73 typ : {'property', 'method'}
74 overwrite : bool, default False
75 Overwrite the method/property in the target class if it exists.
76 """
78 def _create_delegator_property(name):
79 def _getter(self):
80 return self._delegate_property_get(name)
82 def _setter(self, new_values):
83 return self._delegate_property_set(name, new_values)
85 _getter.__name__ = name
86 _setter.__name__ = name
88 return property(
89 fget=_getter, fset=_setter, doc=getattr(delegate, name).__doc__
90 )
92 def _create_delegator_method(name):
93 def f(self, *args, **kwargs):
94 return self._delegate_method(name, *args, **kwargs)
96 f.__name__ = name
97 f.__doc__ = getattr(delegate, name).__doc__
99 return f
101 for name in accessors:
103 if typ == "property":
104 f = _create_delegator_property(name)
105 else:
106 f = _create_delegator_method(name)
108 # don't overwrite existing methods/properties
109 if overwrite or not hasattr(cls, name):
110 setattr(cls, name, f)
113def delegate_names(delegate, accessors, typ: str, overwrite: bool = False):
114 """
115 Add delegated names to a class using a class decorator. This provides
116 an alternative usage to directly calling `_add_delegate_accessors`
117 below a class definition.
119 Parameters
120 ----------
121 delegate : object
122 The class to get methods/properties & doc-strings.
123 accessors : Sequence[str]
124 List of accessor to add.
125 typ : {'property', 'method'}
126 overwrite : bool, default False
127 Overwrite the method/property in the target class if it exists.
129 Returns
130 -------
131 callable
132 A class decorator.
134 Examples
135 --------
136 @delegate_names(Categorical, ["categories", "ordered"], "property")
137 class CategoricalAccessor(PandasDelegate):
138 [...]
139 """
141 def add_delegate_accessors(cls):
142 cls._add_delegate_accessors(delegate, accessors, typ, overwrite=overwrite)
143 return cls
145 return add_delegate_accessors
148# Ported with modifications from xarray
149# https://github.com/pydata/xarray/blob/master/xarray/core/extensions.py
150# 1. We don't need to catch and re-raise AttributeErrors as RuntimeErrors
151# 2. We use a UserWarning instead of a custom Warning
154class CachedAccessor:
155 """
156 Custom property-like object.
158 A descriptor for caching accessors.
160 Parameters
161 ----------
162 name : str
163 Namespace that will be accessed under, e.g. ``df.foo``.
164 accessor : cls
165 Class with the extension methods.
167 Notes
168 -----
169 For accessor, The class's __init__ method assumes that one of
170 ``Series``, ``DataFrame`` or ``Index`` as the
171 single argument ``data``.
172 """
174 def __init__(self, name: str, accessor) -> None:
175 self._name = name
176 self._accessor = accessor
178 def __get__(self, obj, cls):
179 if obj is None:
180 # we're accessing the attribute of the class, i.e., Dataset.geo
181 return self._accessor
182 accessor_obj = self._accessor(obj)
183 # Replace the property with the accessor object. Inspired by:
184 # https://www.pydanny.com/cached-property.html
185 # We need to use object.__setattr__ because we overwrite __setattr__ on
186 # NDFrame
187 object.__setattr__(obj, self._name, accessor_obj)
188 return accessor_obj
191@doc(klass="", others="")
192def _register_accessor(name, cls):
193 """
194 Register a custom accessor on {klass} objects.
196 Parameters
197 ----------
198 name : str
199 Name under which the accessor should be registered. A warning is issued
200 if this name conflicts with a preexisting attribute.
202 Returns
203 -------
204 callable
205 A class decorator.
207 See Also
208 --------
209 register_dataframe_accessor : Register a custom accessor on DataFrame objects.
210 register_series_accessor : Register a custom accessor on Series objects.
211 register_index_accessor : Register a custom accessor on Index objects.
213 Notes
214 -----
215 When accessed, your accessor will be initialized with the pandas object
216 the user is interacting with. So the signature must be
218 .. code-block:: python
220 def __init__(self, pandas_object): # noqa: E999
221 ...
223 For consistency with pandas methods, you should raise an ``AttributeError``
224 if the data passed to your accessor has an incorrect dtype.
226 >>> pd.Series(['a', 'b']).dt
227 Traceback (most recent call last):
228 ...
229 AttributeError: Can only use .dt accessor with datetimelike values
231 Examples
232 --------
233 In your library code::
235 import pandas as pd
237 @pd.api.extensions.register_dataframe_accessor("geo")
238 class GeoAccessor:
239 def __init__(self, pandas_obj):
240 self._obj = pandas_obj
242 @property
243 def center(self):
244 # return the geographic center point of this DataFrame
245 lat = self._obj.latitude
246 lon = self._obj.longitude
247 return (float(lon.mean()), float(lat.mean()))
249 def plot(self):
250 # plot this array's data on a map, e.g., using Cartopy
251 pass
253 Back in an interactive IPython session:
255 .. code-block:: ipython
257 In [1]: ds = pd.DataFrame({{"longitude": np.linspace(0, 10),
258 ...: "latitude": np.linspace(0, 20)}})
259 In [2]: ds.geo.center
260 Out[2]: (5.0, 10.0)
261 In [3]: ds.geo.plot() # plots data on a map
262 """
264 def decorator(accessor):
265 if hasattr(cls, name):
266 warnings.warn(
267 f"registration of accessor {repr(accessor)} under name "
268 f"{repr(name)} for type {repr(cls)} is overriding a preexisting "
269 f"attribute with the same name.",
270 UserWarning,
271 stacklevel=find_stack_level(),
272 )
273 setattr(cls, name, CachedAccessor(name, accessor))
274 cls._accessors.add(name)
275 return accessor
277 return decorator
280@doc(_register_accessor, klass="DataFrame")
281def register_dataframe_accessor(name):
282 from pandas import DataFrame
284 return _register_accessor(name, DataFrame)
287@doc(_register_accessor, klass="Series")
288def register_series_accessor(name):
289 from pandas import Series
291 return _register_accessor(name, Series)
294@doc(_register_accessor, klass="Index")
295def register_index_accessor(name):
296 from pandas import Index
298 return _register_accessor(name, Index)