Browse Source

Fixes Proxy.__module__,__doc__,__name__. Thanks to Marius Gedminas. Closes #1087

Ask Solem 12 years ago
parent
commit
a3bba87336
2 changed files with 49 additions and 3 deletions
  1. 44 3
      celery/local.py
  2. 5 0
      celery/tests/utilities/test_local.py

+ 44 - 3
celery/local.py

@@ -15,6 +15,47 @@ from __future__ import absolute_import
 import importlib
 import sys
 
+__module__ = __name__  # used by Proxy class body
+
+
+def _default_cls_attr(name, type_, cls_value):
+    # Proxy uses properties to forward the standard
+    # class attributes __module__, __name__ and __doc__ to the real
+    # object, but these needs to be a string when accessed from
+    # the Proxy class directly.  This is a hack to make that work.
+    # -- See Issue #1087.
+
+    def __new__(cls, getter):
+        instance = type_.__new__(cls, cls_value)
+        instance.__getter = getter
+        return instance
+
+    def __get__(self, obj, cls=None):
+        return self.__getter(obj) if obj is not None else self
+
+    def __set__(self, obj, value):
+        raise AttributeError('readonly attribute')
+
+    return type(name, (type_, ), {
+        '__new__': __new__, '__get__': __get__, '__set__': __set__,
+    })
+
+
+class _cls_spec(str):
+
+    def __new__(cls, getter):
+        s = str.__new__(cls, getter.__module__)
+        s.__getter = getter
+        return s
+
+    def __get__(self, obj, cls=None):
+        if obj is not None:
+            return self.__getter(obj)
+        return self
+
+    def __set__(self, obj, value):
+        raise AttributeError('cannot set attribute')
+
 
 def symbol_by_name(name, aliases={}, imp=None, package=None,
                    sep='.', default=None, **kwargs):
@@ -97,18 +138,18 @@ class Proxy(object):
         if name is not None:
             object.__setattr__(self, '__custom_name__', name)
 
-    @property
+    @_default_cls_attr('name', str, __name__)
     def __name__(self):
         try:
             return self.__custom_name__
         except AttributeError:
             return self._get_current_object().__name__
 
-    @property
+    @_default_cls_attr('module', str, __module__)
     def __module__(self):
         return self._get_current_object().__module__
 
-    @property
+    @_default_cls_attr('doc', str, __doc__)
     def __doc__(self):
         return self._get_current_object().__doc__
 

+ 5 - 0
celery/tests/utilities/test_local.py

@@ -22,6 +22,11 @@ class test_try_import(Case):
 
 class test_Proxy(Case):
 
+    def test_std_class_attributes(self):
+        self.assertEqual(Proxy.__name__, 'Proxy')
+        self.assertEqual(Proxy.__module__, 'celery.local')
+        self.assertIsInstance(Proxy.__doc__, str)
+
     def test_name(self):
 
         def real():