Search code examples
python-2.7decoratorfunctoolsdictionary-missing

functools.wrapper - AttributeError: attribute '__doc__' of 'type' objects is not writable


While executing the code below, I'm getting AttributeError: attribute '__doc__' of 'type' objects is not writable.

from functools import wraps

def memoize(f):
    """ Memoization decorator for functions taking one or more arguments.
        Saves repeated api calls for a given value, by caching it.
    """
    @wraps(f)
    class memodict(dict):
       """memodict"""
       def __init__(self, f):
           self.f = f
       def __call__(self, *args):
           return self[args]
       def __missing__(self, key):
           ret = self[key] = self.f(*key)
           return ret
     return memodict(f)

@memoize
def a():
    """blah"""
    pass

Traceback:

AttributeError Traceback (most recent call last)
<ipython-input-37-2afb130b1dd6> in <module>()
     17             return ret
     18     return memodict(f)
---> 19 @memoize
     20 def a():
     21     """blah"""

<ipython-input-37-2afb130b1dd6> in memoize(f)
      7     """
      8     @wraps(f)
----> 9     class memodict(dict):
     10         """memodict"""
     11         def __init__(self, f):

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.pyc in update_wrapper(wrapper, wrapped, assigned, updated)
     31     """
     32     for attr in assigned:
---> 33         setattr(wrapper, attr, getattr(wrapped, attr))
     34     for attr in updated:
     35         getattr(wrapper, attr).update(getattr(wrapped, attr, {}))

AttributeError: attribute '__doc__' of 'type' objects is not writable

Even though the doc string is provided, I don't know what's wrong with this.

It's works fine if not wrapped, but I need to do this.


Solution

  • @wraps(f) is primarily designed to be used as a function decorator, rather than as a class decorator, so using it as the latter may lead to the occasional odd quirk.

    The specific error message you're receiving relates to a limitation of builtin types on Python 2:

    >>> class C(object): pass
    ... 
    >>> C.__doc__ = "Not allowed"
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: attribute '__doc__' of 'type' objects is not writable
    

    If you use Python 3, switch to a classic class in Python 2 (by inheriting from UserDict.UserDict rather than the dict builtin), or use a closure to manage the result cache rather than a class instance, the decorator will be able to copy the docstring over from the underlying function.