Commit ad5afd6e authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #12769, #12924 -- Corrected the pickling of curried and lazy objects,...

Fixed #12769, #12924 -- Corrected the pickling of curried and lazy objects, which was preventing queries with translated or related fields from being pickled. And lo, Alex Gaynor didst slayeth the dragon.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12866 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent b31b2d4d
Loading
Loading
Loading
Loading
+0 −28
Original line number Diff line number Diff line
@@ -119,34 +119,6 @@ class Field(object):
        messages.update(error_messages or {})
        self.error_messages = messages

    def __getstate__(self):
        """
        Pickling support.
        """
        from django.utils.functional import Promise
        obj_dict = self.__dict__.copy()
        items = []
        translated_keys = []
        for k, v in self.error_messages.items():
            if isinstance(v, Promise):
                args = getattr(v, '_proxy____args', None)
                if args:
                    translated_keys.append(k)
                    v = args[0]
            items.append((k,v))
        obj_dict['_translated_keys'] = translated_keys
        obj_dict['error_messages'] = dict(items)
        return obj_dict

    def __setstate__(self, obj_dict):
        """
        Unpickling support.
        """
        translated_keys = obj_dict.pop('_translated_keys')
        self.__dict__.update(obj_dict)
        for k in translated_keys:
            self.error_messages[k] = _(self.error_messages[k])

    def __cmp__(self, other):
        # This is needed because bisect does not take a comparison function.
        return cmp(self.creation_counter, other.creation_counter)
+4 −4
Original line number Diff line number Diff line
@@ -88,8 +88,8 @@ class RelatedField(object):
    def contribute_to_class(self, cls, name):
        sup = super(RelatedField, self)

        # Add an accessor to allow easy determination of the related query path for this field
        self.related_query_name = curry(self._get_related_query_name, cls._meta)
        # Store the opts for related_query_name()
        self.opts = cls._meta

        if hasattr(sup, 'contribute_to_class'):
            sup.contribute_to_class(cls, name)
@@ -198,12 +198,12 @@ class RelatedField(object):
            v = v[0]
        return v

    def _get_related_query_name(self, opts):
    def related_query_name(self):
        # This method defines the name that can be used to identify this
        # related object in a table-spanning query. It uses the lower-cased
        # object_name by default, but this can be overridden with the
        # "related_name" option.
        return self.rel.related_name or opts.object_name.lower()
        return self.rel.related_name or self.opts.object_name.lower()

class SingleRelatedObjectDescriptor(object):
    # This class provides the functionality that makes the related-object
+9 −0
Original line number Diff line number Diff line
@@ -147,6 +147,12 @@ def lazy(func, *resultclasses):
    the lazy evaluation code is triggered. Results are not memoized; the
    function is evaluated on every access.
    """
    # When lazy() is called by the __reduce_ex__ machinery to reconstitute the
    # __proxy__ class it can't call with *args, so the first item will just be
    # a tuple.
    if len(resultclasses) == 1 and isinstance(resultclasses[0], tuple):
        resultclasses = resultclasses[0]

    class __proxy__(Promise):
        """
        Encapsulate a function call and act as a proxy for methods that are
@@ -162,6 +168,9 @@ def lazy(func, *resultclasses):
            if self.__dispatch is None:
                self.__prepare_class__()

        def __reduce_ex__(self, protocol):
            return (lazy, (self.__func, resultclasses), self.__dict__)

        def __prepare_class__(cls):
            cls.__dispatch = {}
            for resultclass in resultclasses:
+14 −20
Original line number Diff line number Diff line
"""
Internationalization support.
"""
from django.utils.functional import lazy
from django.conf import settings
from django.utils.encoding import force_unicode
from django.utils.functional import lazy, curry
from django.utils.translation import trans_real, trans_null


__all__ = ['gettext', 'gettext_noop', 'gettext_lazy', 'ngettext',
        'ngettext_lazy', 'string_concat', 'activate', 'deactivate',
@@ -19,32 +22,23 @@ __all__ = ['gettext', 'gettext_noop', 'gettext_lazy', 'ngettext',
# replace the functions with their real counterparts (once we do access the
# settings).

def delayed_loader(*args, **kwargs):
def delayed_loader(real_name, *args, **kwargs):
    """
    Replace each real_* function with the corresponding function from either
    trans_real or trans_null (e.g. real_gettext is replaced with
    trans_real.gettext or trans_null.gettext). This function is run once, the
    first time any i18n method is called. It replaces all the i18n methods at
    once at that time.
    Call the real, underlying function.  We have a level of indirection here so
    that modules can use the translation bits without actually requiring
    Django's settings bits to be configured before import.
    """
    import traceback
    from django.conf import settings
    if settings.USE_I18N:
        import trans_real as trans
        trans = trans_real
    else:
        import trans_null as trans
    caller = traceback.extract_stack(limit=2)[0][2]
    g = globals()
    for name in __all__:
        if hasattr(trans, name):
            g['real_%s' % name] = getattr(trans, name)
        trans = trans_null

    # Make the originally requested function call on the way out the door.
    return g['real_%s' % caller](*args, **kwargs)
    return getattr(trans, real_name)(*args, **kwargs)

g = globals()
for name in __all__:
    g['real_%s' % name] = delayed_loader
    g['real_%s' % name] = curry(delayed_loader, name)
del g, delayed_loader

def gettext_noop(message):
@@ -102,10 +96,10 @@ def templatize(src):
def deactivate_all():
    return real_deactivate_all()

def string_concat(*strings):
def _string_concat(*strings):
    """
    Lazy variant of string concatenation, needed for translations that are
    constructed from multiple parts.
    """
    return u''.join([force_unicode(s) for s in strings])
string_concat = lazy(string_concat, unicode)
string_concat = lazy(_string_concat, unicode)
+0 −1
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ class TranslationTests(TestCase):
        unicode(string_concat(...)) should not raise a TypeError - #4796
        """
        import django.utils.translation
        self.assertEqual(django.utils.translation, reload(django.utils.translation))
        self.assertEqual(u'django', unicode(django.utils.translation.string_concat("dja", "ngo")))

    def test_safe_status(self):
Loading