|
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