|
7 | 7 | import pandas.core.nanops as nanops
|
8 | 8 | import pandas.lib as lib
|
9 | 9 | from pandas.util.decorators import Appender, cache_readonly, deprecate_kwarg
|
10 |
| -from pandas.core.strings import StringMethods |
11 | 10 | from pandas.core.common import AbstractMethodError
|
12 | 11 |
|
13 | 12 | _shared_docs = dict()
|
@@ -111,6 +110,31 @@ def _reset_cache(self, key=None):
|
111 | 110 | else:
|
112 | 111 | self._cache.pop(key, None)
|
113 | 112 |
|
| 113 | +class NoNewAttributesMixin(object): |
| 114 | + """Mixin which prevents adding new attributes. |
| 115 | +
|
| 116 | + Prevents additional attributes via xxx.attribute = "something" after a call to |
| 117 | + `self.__freeze()`. Mainly used to prevent the user from using wrong attrirbutes |
| 118 | + on a accessor (`Series.cat/.str/.dt`). |
| 119 | +
|
| 120 | + If you really want to add a new attribute at a later time, you need to use |
| 121 | + `object.__setattr__(self, key, value)`. |
| 122 | + """ |
| 123 | + |
| 124 | + def _freeze(self): |
| 125 | + """Prevents setting additional attributes""" |
| 126 | + object.__setattr__(self, "__frozen", True) |
| 127 | + |
| 128 | + |
| 129 | + # prevent adding any attribute via s.xxx.new_attribute = ... |
| 130 | + def __setattr__(self, key, value): |
| 131 | + # _cache is used by a decorator |
| 132 | + # dict lookup instead of getattr as getattr is false for getter which error |
| 133 | + if getattr(self, "__frozen", False) and not (key in type(self).__dict__ or key == "_cache"): |
| 134 | + raise AttributeError( "You cannot add any new attribute '{key}'".format(key=key)) |
| 135 | + object.__setattr__(self, key, value) |
| 136 | + |
| 137 | + |
114 | 138 | class PandasDelegate(PandasObject):
|
115 | 139 | """ an abstract base class for delegating methods/properties """
|
116 | 140 |
|
@@ -517,41 +541,6 @@ def searchsorted(self, key, side='left'):
|
517 | 541 | #### needs tests/doc-string
|
518 | 542 | return self.values.searchsorted(key, side=side)
|
519 | 543 |
|
520 |
| - # string methods |
521 |
| - def _make_str_accessor(self): |
522 |
| - from pandas.core.series import Series |
523 |
| - from pandas.core.index import Index |
524 |
| - if isinstance(self, Series) and not com.is_object_dtype(self.dtype): |
525 |
| - # this really should exclude all series with any non-string values, |
526 |
| - # but that isn't practical for performance reasons until we have a |
527 |
| - # str dtype (GH 9343) |
528 |
| - raise AttributeError("Can only use .str accessor with string " |
529 |
| - "values, which use np.object_ dtype in " |
530 |
| - "pandas") |
531 |
| - elif isinstance(self, Index): |
532 |
| - # see scc/inferrence.pyx which can contain string values |
533 |
| - allowed_types = ('string', 'unicode', 'mixed', 'mixed-integer') |
534 |
| - if self.inferred_type not in allowed_types: |
535 |
| - message = ("Can only use .str accessor with string values " |
536 |
| - "(i.e. inferred_type is 'string', 'unicode' or 'mixed')") |
537 |
| - raise AttributeError(message) |
538 |
| - if self.nlevels > 1: |
539 |
| - message = "Can only use .str accessor with Index, not MultiIndex" |
540 |
| - raise AttributeError(message) |
541 |
| - return StringMethods(self) |
542 |
| - |
543 |
| - str = AccessorProperty(StringMethods, _make_str_accessor) |
544 |
| - |
545 |
| - def _dir_additions(self): |
546 |
| - return set() |
547 |
| - |
548 |
| - def _dir_deletions(self): |
549 |
| - try: |
550 |
| - getattr(self, 'str') |
551 |
| - except AttributeError: |
552 |
| - return set(['str']) |
553 |
| - return set() |
554 |
| - |
555 | 544 | _shared_docs['drop_duplicates'] = (
|
556 | 545 | """Return %(klass)s with duplicate values removed
|
557 | 546 |
|
|
0 commit comments