Login

Variable._resolve_lookup monkeypatch

Author:
showell
Posted:
November 15, 2009
Language:
Python
Version:
1.1
Score:
0 (after 0 ratings)

There are times when you want to hook into the Variable class of django.template to get extra debugging, or even to change its behavior. The code shown is working code that does just that. You can monkeypatch template variable resolution by calling patch_resolver(). I recommend using it for automated tests at first.

My particular version of _resolve_lookup does two things--it provides some simple tracing, and it also simplifies variable resolution. In particular, I do not allow index lookups on lists, since we never use that feature in our codebase, and I do not silently catch so many exceptions as the original version, since we want to err on the side of failure for tests. (If you want to do monkeypatching in production, you obviously want to be confident in your tests and have good error catching.)

As far as tracing is concerned, right now I am doing very basic "print" statements, but once you have these hooks in place, you can do more exotic things like warn when the same object is being dereferenced too many times, or even build up a graph of object interrelationships. (I leave that as an exercise to the reader.)

If you want to see what the core _resolve_lookup method looks like, you can find it in django/templates/init.py in your installation, or also here (scroll down to line 701 or so):

source

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    from django.template import Variable

    def patch_resolver():
        Variable._resolve_lookup = our_resolver

    def our_resolver(self, context):
        current = context
        how_resolved_steps = []
        for bit in self.lookups:
            how_resolved, current = resolve_bit(current, bit)
            how_resolved_steps.append((how_resolved, current))
        debug(self.lookups, how_resolved_steps)
        return current
        
    def resolve_bit(current, bit):
        # Strict, does not allow list lookups, for example
        try:
            return 'dictionary', current[bit]
        except (TypeError, AttributeError, KeyError):
            pass

        current = getattr(current, bit)
        if not callable(current):
            return 'attribute', current
        if getattr(current, 'alters_data', False):
            raise Exception('trying to alter data')
        return 'called attribute', current()

    def debug(lookups, how_resolved_steps):
        name = 'context'
        for i, (how_resolved, value) in enumerate(how_resolved_steps):
            bit = lookups[i]
            if how_resolved == 'dictionary':
                name += "['%s']" % bit
            elif how_resolved == 'attribute':
                name += '.%s' % bit
            else:
                name += '.%s()' % bit
        print '%.40s' % name, value

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 3 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 11 months ago
  5. Help text hyperlinks by sa2812 11 months, 3 weeks ago

Comments

Please login first before commenting.