Commits

Jason Moiron committed c3a39e2 Draft Merge

Merge pull request #1 from jezdez/master

Use a copy of Django's available_attrs function to only override the attributes that exist when using the functors.wraps decorator.

Comments (0)

Files changed (5)

 from uuid import uuid4
 
 try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
-try:
     from hashlib import md5
 except ImportError:
     from md5 import md5
 import localstore
 import signals
 from johnny import settings
+from johnny.decorators import wraps, available_attrs
 from transaction import TransactionManager
 
 import django
 def timer(func):
     times = []
 
-    @wraps(func)
+    @wraps(func, assigned=available_attrs(func))
     def foo(*args, **kwargs):
         t0 = time.time()
         ret = func(*args, **kwargs)
         from django.db.models.sql.constants import MULTI
         from django.db.models.sql.datastructures import EmptyResultSet
 
-        @wraps(original)
+        @wraps(original, assigned=available_attrs(original))
         def newfun(cls, *args, **kwargs):
             if args:
                 result_type = args[0]
         return newfun
 
     def _monkey_write(self, original):
-        @wraps(original)
+        @wraps(original, assigned=available_attrs(original))
         def newfun(cls, *args, **kwargs):
             db = getattr(cls, 'using', 'default')
             from django.db.models.sql import compiler
         from django.db.models.sql.constants import MULTI
         from django.db.models.sql.datastructures import EmptyResultSet
 
-        @wraps(original)
+        @wraps(original, assigned=available_attrs(original))
         def newfun(cls, result_type=MULTI):
             try:
                 sql, params = cls.as_sql()

johnny/decorators.py

+try:
+    from functools import wraps, WRAPPER_ASSIGNMENTS
+except ImportError:
+    from django.utils.functional import wraps, WRAPPER_ASSIGNMENTS
+
+
+def available_attrs(fn):
+    """
+    Return the list of functools-wrappable attributes on a callable.
+    This is required as a workaround for http://bugs.python.org/issue3445.
+    """
+    return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))

johnny/tests/base.py

 """Base test class for Johnny Cache Tests."""
 
 import sys
-from django.utils.functional import wraps
 
 import django
 from django.test import TestCase, TransactionTestCase
 from django.db.models.loading import load_app
 
 from johnny import settings as johnny_settings
+from johnny.decorators import wraps, available_attrs
 
 # order matters here;  I guess we aren't deferring foreign key checking :\
 johnny_fixtures = ['authors.json', 'genres.json', 'publishers.json', 'books.json']
     hit = hit or _hit
     miss = miss or _miss
     def deco(func):
-        @wraps(func)
+        @wraps(func, assigned=available_attrs(func))
         def wrapped(*args, **kwargs):
             from johnny.signals import qc_hit, qc_miss
             qc_hit.connect(hit)

johnny/transaction.py

 except:
     DEFUALT_DB_ALIAS = None
 
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
+from johnny.decorators import wraps, available_attrs
 
 
 class TransactionManager(object):
         self._clear_sid_stack(using)
 
     def _patched(self, original, commit=True):
-        @wraps(original)
+        @wraps(original, assigned=available_attrs(original))
         def newfun(using=None):
             #1.2 version
             original(using=using)
             self._flush(commit=commit, using=using)
 
-        @wraps(original)
+        @wraps(original, assigned=available_attrs(original))
         def newfun11():
             #1.1 version
             original()
 
     def _sid_key(self, sid, using=None):
         if using is not None:
-            prefix = 'trans_savepoint_%s'%using
+            prefix = 'trans_savepoint_%s' % using
         else:
             prefix = 'trans_savepoint'
 
         del self.local[backup]
 
     def _savepoint(self, original):
-        @wraps(original)
+        @wraps(original, assigned=available_attrs(original))
         def newfun(using=None):
             if using != None:
                 sid = original(using=using)
 
 """Extra johnny utilities."""
 
-try:
-    from functools import wraps
-except ImportError:
-    from django.utils.functional import wraps
+from johnny.cache import get_backend, local, patch, unpatch
+from johnny.decorators import wraps, available_attrs
 
-from johnny.cache import get_backend, local, patch, unpatch
 
 __all__ = ["celery_enable_all", "celery_task_wrapper", "johnny_task_wrapper"]
 
+
 def prerun_handler(*args, **kwargs):
     """Celery pre-run handler.  Enables johnny-cache."""
     patch()
     """
     from celery.utils import fun_takes_kwargs
 
-    @wraps(f)
+    @wraps(f, assigned=available_attrs(f))
     def newf(*args, **kwargs):
         backend = get_backend()
         was_patched = backend._patched