From 22be3d7612b518268b84ce37d3b20f828efb4b6f Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Mon, 19 Oct 2009 21:13:22 +0000 Subject: [PATCH] Fixed #12049 - LazyObject-wrapped User breaks queries in template tags Thanks to chipx86 for the report and patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@11634 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/utils/functional.py | 14 ++++++++++++++ .../context_processors/tests.py | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/django/utils/functional.py b/django/utils/functional.py index 4fbc8cd630..52969cd43a 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -297,6 +297,11 @@ class SimpleLazyObject(LazyObject): def __init__(self, func): """ Pass in a callable that returns the object to be wrapped. + + If copies are made of the resulting SimpleLazyObject, which can happen + in various circumstances within Django, then you must ensure that the + callable can be safely run more than once and will return the same + value. """ self.__dict__['_setupfunc'] = func # For some reason, we have to inline LazyObject.__init__ here to avoid @@ -307,5 +312,14 @@ class SimpleLazyObject(LazyObject): if self._wrapped is None: self._setup() return str(self._wrapped) + def __deepcopy__(self, memo): + if self._wrapped is None: + result = self.__class__(self._setupfunc) + memo[id(self)] = result + return result + else: + import copy + return copy.deepcopy(self._wrapped, memo) + def _setup(self): self._wrapped = self._setupfunc() diff --git a/tests/regressiontests/context_processors/tests.py b/tests/regressiontests/context_processors/tests.py index c7fc4e5a93..31ecc83ab3 100644 --- a/tests/regressiontests/context_processors/tests.py +++ b/tests/regressiontests/context_processors/tests.py @@ -3,6 +3,8 @@ Tests for Django's bundled context processors. """ from django.conf import settings +from django.contrib.auth.models import Group +from django.db.models import Q from django.test import TestCase from django.template import Template @@ -81,3 +83,20 @@ class AuthContextProcessorTests(TestCase): self.assertContains(response, "username: super") # bug #12037 is tested by the {% url %} in the template: self.assertContains(response, "url: /userpage/super/") + + # See if this object can be used for queries where a Q() comparing + # a user can be used with another Q() (in an AND or OR fashion). + # This simulates what a template tag might do with the user from the + # context. Note that we don't need to execute a query, just build it. + # + # The failure case (bug #12049) on Python 2.4 with a LazyObject-wrapped + # User is a fatal TypeError: "function() takes at least 2 arguments + # (0 given)" deep inside deepcopy(). + # + # Python 2.5 and 2.6 succeeded, but logged internally caught exception + # spew: + # + # Exception RuntimeError: 'maximum recursion depth exceeded while + # calling a Python object' in + # ignored" + query = Q(user=response.context['user']) & Q(someflag=True)