|
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 |
|
@@ -547,41 +571,6 @@ def searchsorted(self, key, side='left'):
|
547 | 571 | #### needs tests/doc-string
|
548 | 572 | return self.values.searchsorted(key, side=side)
|
549 | 573 |
|
550 |
| - # string methods |
551 |
| - def _make_str_accessor(self): |
552 |
| - from pandas.core.series import Series |
553 |
| - from pandas.core.index import Index |
554 |
| - if isinstance(self, Series) and not com.is_object_dtype(self.dtype): |
555 |
| - # this really should exclude all series with any non-string values, |
556 |
| - # but that isn't practical for performance reasons until we have a |
557 |
| - # str dtype (GH 9343) |
558 |
| - raise AttributeError("Can only use .str accessor with string " |
559 |
| - "values, which use np.object_ dtype in " |
560 |
| - "pandas") |
561 |
| - elif isinstance(self, Index): |
562 |
| - # see scc/inferrence.pyx which can contain string values |
563 |
| - allowed_types = ('string', 'unicode', 'mixed', 'mixed-integer') |
564 |
| - if self.inferred_type not in allowed_types: |
565 |
| - message = ("Can only use .str accessor with string values " |
566 |
| - "(i.e. inferred_type is 'string', 'unicode' or 'mixed')") |
567 |
| - raise AttributeError(message) |
568 |
| - if self.nlevels > 1: |
569 |
| - message = "Can only use .str accessor with Index, not MultiIndex" |
570 |
| - raise AttributeError(message) |
571 |
| - return StringMethods(self) |
572 |
| - |
573 |
| - str = AccessorProperty(StringMethods, _make_str_accessor) |
574 |
| - |
575 |
| - def _dir_additions(self): |
576 |
| - return set() |
577 |
| - |
578 |
| - def _dir_deletions(self): |
579 |
| - try: |
580 |
| - getattr(self, 'str') |
581 |
| - except AttributeError: |
582 |
| - return set(['str']) |
583 |
| - return set() |
584 |
| - |
585 | 574 | _shared_docs['drop_duplicates'] = (
|
586 | 575 | """Return %(klass)s with duplicate values removed
|
587 | 576 |
|
|
0 commit comments