Django

Code

Ticket #626: 626_import_error.patch

File 626_import_error.patch, 64.7 kB (added by adrian, 3 years ago)

Patch that causes circular-import problem.

  • django/core/template_loader.py

    old new  
    11"Wrapper for loading templates from storage of some sort (e.g. files or db)" 
    2 import template 
    3 from template_file import load_template_source 
    42 
     3from django.core import template 
     4from django.core.template.loaders.filesystem import load_template_source 
     5 
    56class ExtendsError(Exception): 
    67    pass 
    78 
     
    2425    Loads the given template_name and renders it with the given dictionary as 
    2526    context. The template_name may be a string to load a single template using 
    2627    get_template, or it may be a tuple to use select_template to find one of 
    27     the templates in the list.  Returns a string.  
     28    the templates in the list.  Returns a string. 
    2829    """ 
    2930    dictionary = dictionary or {} 
    3031    if isinstance(template_name, (list, tuple)): 
  • django/core/defaulttags.py

    old new  
    1 "Default tags used by the template system, available to all templates." 
    2  
    3 import sys 
    4 import template 
    5  
    6 class CommentNode(template.Node): 
    7     def render(self, context): 
    8         return '' 
    9  
    10 class CycleNode(template.Node): 
    11     def __init__(self, cyclevars): 
    12         self.cyclevars = cyclevars 
    13         self.cyclevars_len = len(cyclevars) 
    14         self.counter = -1 
    15  
    16     def render(self, context): 
    17         self.counter += 1 
    18         return self.cyclevars[self.counter % self.cyclevars_len] 
    19  
    20 class DebugNode(template.Node): 
    21     def render(self, context): 
    22         from pprint import pformat 
    23         output = [pformat(val) for val in context] 
    24         output.append('\n\n') 
    25         output.append(pformat(sys.modules)) 
    26         return ''.join(output) 
    27  
    28 class FilterNode(template.Node): 
    29     def __init__(self, filters, nodelist): 
    30         self.filters, self.nodelist = filters, nodelist 
    31  
    32     def render(self, context): 
    33         output = self.nodelist.render(context) 
    34         # apply filters 
    35         for f in self.filters: 
    36             output = template.registered_filters[f[0]][0](output, f[1]) 
    37         return output 
    38  
    39 class FirstOfNode(template.Node): 
    40     def __init__(self, vars): 
    41         self.vars = vars 
    42  
    43     def render(self, context): 
    44         for var in self.vars: 
    45             value = template.resolve_variable(var, context) 
    46             if value: 
    47                 return str(value) 
    48         return '' 
    49  
    50 class ForNode(template.Node): 
    51     def __init__(self, loopvar, sequence, reversed, nodelist_loop): 
    52         self.loopvar, self.sequence = loopvar, sequence 
    53         self.reversed = reversed 
    54         self.nodelist_loop = nodelist_loop 
    55  
    56     def __repr__(self): 
    57         if self.reversed: 
    58             reversed = ' reversed' 
    59         else: 
    60             reversed = '' 
    61         return "<For Node: for %s in %s, tail_len: %d%s>" % \ 
    62             (self.loopvar, self.sequence, len(self.nodelist_loop), reversed) 
    63  
    64     def __iter__(self): 
    65         for node in self.nodelist_loop: 
    66             yield node 
    67  
    68     def get_nodes_by_type(self, nodetype): 
    69         nodes = [] 
    70         if isinstance(self, nodetype): 
    71             nodes.append(self) 
    72         nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype)) 
    73         return nodes 
    74  
    75     def render(self, context): 
    76         nodelist = template.NodeList() 
    77         if context.has_key('forloop'): 
    78             parentloop = context['forloop'] 
    79         else: 
    80             parentloop = {} 
    81         context.push() 
    82         try: 
    83             values = template.resolve_variable_with_filters(self.sequence, context) 
    84         except template.VariableDoesNotExist: 
    85             values = [] 
    86         if values is None: 
    87             values = [] 
    88         len_values = len(values) 
    89         if self.reversed: 
    90             # From http://www.python.org/doc/current/tut/node11.html 
    91             def reverse(data): 
    92                 for index in range(len(data)-1, -1, -1): 
    93                     yield data[index] 
    94             values = reverse(values) 
    95         for i, item in enumerate(values): 
    96             context['forloop'] = { 
    97                 # shortcuts for current loop iteration number 
    98                 'counter0': i, 
    99                 'counter': i+1, 
    100                 # reverse counter iteration numbers 
    101                 'revcounter': len_values - i, 
    102                 'revcounter0': len_values - i - 1, 
    103                 # boolean values designating first and last times through loop 
    104                 'first': (i == 0), 
    105                 'last': (i == len_values - 1), 
    106                 'parentloop': parentloop, 
    107             } 
    108             context[self.loopvar] = item 
    109             for node in self.nodelist_loop: 
    110                 nodelist.append(node.render(context)) 
    111         context.pop() 
    112         return nodelist.render(context) 
    113  
    114 class IfChangedNode(template.Node): 
    115     def __init__(self, nodelist): 
    116         self.nodelist = nodelist 
    117         self._last_seen = None 
    118  
    119     def render(self, context): 
    120         content = self.nodelist.render(context) 
    121         if content != self._last_seen: 
    122             firstloop = (self._last_seen == None) 
    123             self._last_seen = content 
    124             context.push() 
    125             context['ifchanged'] = {'firstloop': firstloop} 
    126             content = self.nodelist.render(context) 
    127             context.pop() 
    128             return content 
    129         else: 
    130             return '' 
    131  
    132 class IfEqualNode(template.Node): 
    133     def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): 
    134         self.var1, self.var2 = var1, var2 
    135         self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false 
    136         self.negate = negate 
    137  
    138     def __repr__(self): 
    139         return "<IfEqualNode>" 
    140  
    141     def render(self, context): 
    142         val1 = template.resolve_variable(self.var1, context) 
    143         val2 = template.resolve_variable(self.var2, context) 
    144         if (self.negate and val1 != val2) or (not self.negate and val1 == val2): 
    145             return self.nodelist_true.render(context) 
    146         return self.nodelist_false.render(context) 
    147  
    148 class IfNode(template.Node): 
    149     def __init__(self, boolvars, nodelist_true, nodelist_false): 
    150         self.boolvars = boolvars 
    151         self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false 
    152  
    153     def __repr__(self): 
    154         return "<If node>" 
    155  
    156     def __iter__(self): 
    157         for node in self.nodelist_true: 
    158             yield node 
    159         for node in self.nodelist_false: 
    160             yield node 
    161  
    162     def get_nodes_by_type(self, nodetype): 
    163         nodes = [] 
    164         if isinstance(self, nodetype): 
    165             nodes.append(self) 
    166         nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype)) 
    167         nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype)) 
    168         return nodes 
    169  
    170     def render(self, context): 
    171         for ifnot, boolvar in self.boolvars: 
    172             try: 
    173                 value = template.resolve_variable_with_filters(boolvar, context) 
    174             except template.VariableDoesNotExist: 
    175                 value = None 
    176             if (value and not ifnot) or (ifnot and not value): 
    177                 return self.nodelist_true.render(context) 
    178         return self.nodelist_false.render(context) 
    179  
    180 class RegroupNode(template.Node): 
    181     def __init__(self, target_var, expression, var_name): 
    182         self.target_var, self.expression = target_var, expression 
    183         self.var_name = var_name 
    184  
    185     def render(self, context): 
    186         obj_list = template.resolve_variable_with_filters(self.target_var, context) 
    187         if obj_list == '': # target_var wasn't found in context; fail silently 
    188             context[self.var_name] = [] 
    189             return '' 
    190         output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} 
    191         for obj in obj_list: 
    192             grouper = template.resolve_variable_with_filters('var.%s' % self.expression, \ 
    193                 template.Context({'var': obj})) 
    194             if output and repr(output[-1]['grouper']) == repr(grouper): 
    195                 output[-1]['list'].append(obj) 
    196             else: 
    197                 output.append({'grouper': grouper, 'list': [obj]}) 
    198         context[self.var_name] = output 
    199         return '' 
    200  
    201 def include_is_allowed(filepath): 
    202     from django.conf.settings import ALLOWED_INCLUDE_ROOTS 
    203     for root in ALLOWED_INCLUDE_ROOTS: 
    204         if filepath.startswith(root): 
    205             return True 
    206     return False 
    207  
    208 class SsiNode(template.Node): 
    209     def __init__(self, filepath, parsed): 
    210         self.filepath, self.parsed = filepath, parsed 
    211  
    212     def render(self, context): 
    213         if not include_is_allowed(self.filepath): 
    214             return '' # Fail silently for invalid includes. 
    215         try: 
    216             fp = open(self.filepath, 'r') 
    217             output = fp.read() 
    218             fp.close() 
    219         except IOError: 
    220             output = '' 
    221         if self.parsed: 
    222             try: 
    223                 t = template.Template(output) 
    224                 return t.render(context) 
    225             except template.TemplateSyntaxError: 
    226                 return '' # Fail silently for invalid included templates. 
    227         return output 
    228  
    229 class LoadNode(template.Node): 
    230     def __init__(self, taglib): 
    231         self.taglib = taglib 
    232  
    233     def load_taglib(taglib): 
    234         mod = __import__("django.templatetags.%s" % taglib.split('.')[-1], '', '', ['']) 
    235         reload(mod) 
    236         return mod 
    237     load_taglib = staticmethod(load_taglib) 
    238  
    239     def render(self, context): 
    240         "Import the relevant module" 
    241         try: 
    242             self.__class__.load_taglib(self.taglib) 
    243         except ImportError: 
    244             pass # Fail silently for invalid loads. 
    245         return '' 
    246  
    247 class NowNode(template.Node): 
    248     def __init__(self, format_string): 
    249         self.format_string = format_string 
    250  
    251     def render(self, context): 
    252         from datetime import datetime 
    253         from django.utils.dateformat import DateFormat 
    254         df = DateFormat(datetime.now()) 
    255         return df.format(self.format_string) 
    256  
    257 class TemplateTagNode(template.Node): 
    258     mapping = {'openblock': template.BLOCK_TAG_START, 
    259                'closeblock': template.BLOCK_TAG_END, 
    260                'openvariable': template.VARIABLE_TAG_START, 
    261                'closevariable': template.VARIABLE_TAG_END} 
    262  
    263     def __init__(self, tagtype): 
    264         self.tagtype = tagtype 
    265  
    266     def render(self, context): 
    267         return self.mapping.get(self.tagtype, '') 
    268  
    269 class WidthRatioNode(template.Node): 
    270     def __init__(self, val_var, max_var, max_width): 
    271         self.val_var = val_var 
    272         self.max_var = max_var 
    273         self.max_width = max_width 
    274  
    275     def render(self, context): 
    276         try: 
    277             value = template.resolve_variable_with_filters(self.val_var, context) 
    278             maxvalue = template.resolve_variable_with_filters(self.max_var, context) 
    279         except template.VariableDoesNotExist: 
    280             return '' 
    281         try: 
    282             value = float(value) 
    283             maxvalue = float(maxvalue) 
    284             ratio = (value / maxvalue) * int(self.max_width) 
    285         except (ValueError, ZeroDivisionError): 
    286             return '' 
    287         return str(int(round(ratio))) 
    288  
    289 def do_comment(parser, token): 
    290     """ 
    291     Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` 
    292     """ 
    293     nodelist = parser.parse(('endcomment',)) 
    294     parser.delete_first_token() 
    295     return CommentNode() 
    296  
    297 def do_cycle(parser, token): 
    298     """ 
    299     Cycle among the given strings each time this tag is encountered 
    300  
    301     Within a loop, cycles among the given strings each time through 
    302     the loop:: 
    303  
    304         {% for o in some_list %} 
    305             <tr class="{% cycle row1,row2 %}"> 
    306                 ... 
    307             </tr> 
    308         {% endfor %} 
    309  
    310     Outside of a loop, give the values a unique name the first time you call 
    311     it, then use that name each sucessive time through:: 
    312  
    313             <tr class="{% cycle row1,row2,row3 as rowcolors %}">...</tr> 
    314             <tr class="{% cycle rowcolors %}">...</tr> 
    315             <tr class="{% cycle rowcolors %}">...</tr> 
    316  
    317     You can use any number of values, seperated by commas. Make sure not to 
    318     put spaces between the values -- only commas. 
    319     """ 
    320  
    321     # Note: This returns the exact same node on each {% cycle name %} call; that 
    322     # is, the node object returned from {% cycle a,b,c as name %} and the one 
    323     # returned from {% cycle name %} are the exact same object.  This shouldn't 
    324     # cause problems (heh), but if it does, now you know. 
    325     # 
    326     # Ugly hack warning: this stuffs the named template dict into parser so 
    327     # that names are only unique within each template (as opposed to using 
    328     # a global variable, which would make cycle names have to be unique across 
    329     # *all* templates. 
    330  
    331     args = token.contents.split() 
    332     if len(args) < 2: 
    333         raise template.TemplateSyntaxError("'Cycle' statement requires at least two arguments") 
    334  
    335     elif len(args) == 2 and "," in args[1]: 
    336         # {% cycle a,b,c %} 
    337         cyclevars = [v for v in args[1].split(",") if v]    # split and kill blanks 
    338         return CycleNode(cyclevars) 
    339         # {% cycle name %} 
    340  
    341     elif len(args) == 2: 
    342         name = args[1] 
    343         if not parser._namedCycleNodes.has_key(name): 
    344             raise template.TemplateSyntaxError("Named cycle '%s' does not exist" % name) 
    345         return parser._namedCycleNodes[name] 
    346  
    347     elif len(args) == 4: 
    348         # {% cycle a,b,c as name %} 
    349         if args[2] != 'as': 
    350             raise template.TemplateSyntaxError("Second 'cycle' argument must be 'as'") 
    351         cyclevars = [v for v in args[1].split(",") if v]    # split and kill blanks 
    352         name = args[3] 
    353         node = CycleNode(cyclevars) 
    354  
    355         if not hasattr(parser, '_namedCycleNodes'): 
    356             parser._namedCycleNodes = {} 
    357  
    358         parser._namedCycleNodes[name] = node 
    359         return node 
    360  
    361     else: 
    362         raise template.TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args) 
    363  
    364 def do_debug(parser, token): 
    365     "Print a whole load of debugging information, including the context and imported modules" 
    366     return DebugNode() 
    367  
    368 def do_filter(parser, token): 
    369     """ 
    370     Filter the contents of the blog through variable filters. 
    371  
    372     Filters can also be piped through each other, and they can have 
    373     arguments -- just like in variable syntax. 
    374  
    375     Sample usage:: 
    376  
    377         {% filter escape|lower %} 
    378             This text will be HTML-escaped, and will appear in lowercase. 
    379         {% endfilter %} 
    380     """ 
    381     _, rest = token.contents.split(None, 1) 
    382     _, filters = template.get_filters_from_token('var|%s' % rest) 
    383     nodelist = parser.parse(('endfilter',)) 
    384     parser.delete_first_token() 
    385     return FilterNode(filters, nodelist) 
    386  
    387 def do_firstof(parser, token): 
    388     """ 
    389     Outputs the first variable passed that is not False. 
    390  
    391     Outputs nothing if all the passed variables are False. 
    392  
    393     Sample usage:: 
    394  
    395         {% firstof var1 var2 var3 %} 
    396  
    397     This is equivalent to:: 
    398  
    399         {% if var1 %} 
    400             {{ var1 }} 
    401         {% else %}{% if var2 %} 
    402             {{ var2 }} 
    403         {% else %}{% if var3 %} 
    404             {{ var3 }} 
    405         {% endif %}{% endif %}{% endif %} 
    406  
    407     but obviously much cleaner! 
    408     """ 
    409     bits = token.contents.split()[1:] 
    410     if len(bits) < 1: 
    411         raise template.TemplateSyntaxError, "'firstof' statement requires at least one argument" 
    412     return FirstOfNode(bits) 
    413  
    414  
    415 def do_for(parser, token): 
    416     """ 
    417     Loop over each item in an array. 
    418  
    419     For example, to display a list of athletes given ``athlete_list``:: 
    420  
    421         <ul> 
    422         {% for athlete in athlete_list %} 
    423             <li>{{ athlete.name }}</li> 
    424         {% endfor %} 
    425         </ul> 
    426  
    427     You can also loop over a list in reverse by using 
    428     ``{% for obj in list reversed %}``. 
    429  
    430     The for loop sets a number of variables available within the loop: 
    431  
    432         ==========================  ================================================ 
    433         Variable                    Description 
    434         ==========================  ================================================ 
    435         ``forloop.counter``         The current iteration of the loop (1-indexed) 
    436         ``forloop.counter0``        The current iteration of the loop (0-indexed) 
    437         ``forloop.revcounter``      The number of iterations from the end of the  
    438                                     loop (1-indexed) 
    439         ``forloop.revcounter0``     The number of iterations from the end of the  
    440                                     loop (0-indexed) 
    441         ``forloop.first``           True if this is the first time through the loop 
    442         ``forloop.last``            True if this is the last time through the loop 
    443         ``forloop.parentloop``      For nested loops, this is the loop "above" the 
    444                                     current one 
    445         ==========================  ================================================ 
    446  
    447     """ 
    448     bits = token.contents.split() 
    449     if len(bits) == 5 and bits[4] != 'reversed': 
    450         raise template.TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents 
    451     if len(bits) not in (4, 5): 
    452         raise template.TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents 
    453     if bits[2] != 'in': 
    454         raise template.TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents 
    455     loopvar = bits[1] 
    456     sequence = bits[3] 
    457     reversed = (len(bits) == 5) 
    458     nodelist_loop = parser.parse(('endfor',)) 
    459     parser.delete_first_token() 
    460     return ForNode(loopvar, sequence, reversed, nodelist_loop) 
    461  
    462 def do_ifequal(parser, token, negate): 
    463     """ 
    464     Output the contents of the block if the two arguments equal/don't equal each other. 
    465  
    466     Examples:: 
    467  
    468         {% ifequal user.id comment.user_id %} 
    469             ... 
    470         {% endifequal %} 
    471  
    472         {% ifnotequal user.id comment.user_id %} 
    473             ... 
    474         {% else %} 
    475             ... 
    476         {% endifnotequal %} 
    477     """ 
    478     bits = token.contents.split() 
    479     if len(bits) != 3: 
    480         raise template.TemplateSyntaxError, "%r takes two arguments" % bits[0] 
    481     end_tag = 'end' + bits[0] 
    482     nodelist_true = parser.parse(('else', end_tag)) 
    483     token = parser.next_token() 
    484     if token.contents == 'else': 
    485         nodelist_false = parser.parse((end_tag,)) 
    486         parser.delete_first_token() 
    487     else: 
    488         nodelist_false = template.NodeList() 
    489     return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate) 
    490  
    491 def do_if(parser, token): 
    492     """ 
    493     The ``{% if %}`` tag evaluates a variable, and if that variable is "true" 
    494     (i.e. exists, is not empty, and is not a false boolean value) the contents 
    495     of the block are output: 
    496  
    497     :: 
    498  
    499         {% if althlete_list %} 
    500             Number of athletes: {{ althete_list|count }} 
    501         {% else %} 
    502             No athletes. 
    503         {% endif %} 
    504  
    505     In the above, if ``athlete_list`` is not empty, the number of athletes will 
    506     be displayed by the ``{{ athlete_list|count }}`` variable. 
    507  
    508     As you can see, the ``if`` tag can take an option ``{% else %}`` clause that 
    509     will be displayed if the test fails. 
    510  
    511     ``if`` tags may use ``or`` or ``not`` to test a number of variables or to 
    512     negate a given variable:: 
    513  
    514         {% if not athlete_list %} 
    515             There are no athletes. 
    516         {% endif %} 
    517  
    518         {% if athlete_list or coach_list %} 
    519             There are some athletes or some coaches. 
    520         {% endif %} 
    521  
    522         {% if not athlete_list or coach_list %} 
    523             There are no athletes or there are some coaches (OK, so 
    524             writing English translations of boolean logic sounds 
    525             stupid; it's not my fault). 
    526         {% endif %} 
    527  
    528     For simplicity, ``if`` tags do not allow ``and`` clauses; use nested ``if`` 
    529     tags instead:: 
    530  
    531         {% if athlete_list %} 
    532             {% if coach_list %} 
    533                 Number of athletes: {{ athlete_list|count }}. 
    534                 Number of coaches: {{ coach_list|count }}. 
    535             {% endif %} 
    536         {% endif %} 
    537     """ 
    538     bits = token.contents.split() 
    539     del bits[0] 
    540     if not bits: 
    541         raise template.TemplateSyntaxError, "'if' statement requires at least one argument" 
    542     # bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d'] 
    543     boolpairs = ' '.join(bits).split(' or ') 
    544     boolvars = [] 
    545     for boolpair in boolpairs: 
    546         if ' ' in boolpair: 
    547             not_, boolvar = boolpair.split() 
    548             if not_ != 'not': 
    549                 raise template.TemplateSyntaxError, "Expected 'not' in if statement" 
    550             boolvars.append((True, boolvar)) 
    551         else: 
    552             boolvars.append((False, boolpair)) 
    553     nodelist_true = parser.parse(('else', 'endif')) 
    554     token = parser.next_token() 
    555     if token.contents == 'else': 
    556         nodelist_false = parser.parse(('endif',)) 
    557         parser.delete_first_token() 
    558     else: 
    559         nodelist_false = template.NodeList() 
    560     return IfNode(boolvars, nodelist_true, nodelist_false) 
    561  
    562 def do_ifchanged(parser, token): 
    563     """ 
    564     Check if a value has changed from the last iteration of a loop. 
    565  
    566     The 'ifchanged' block tag is used within a loop. It checks its own rendered 
    567     contents against its previous state and only displays its content if the 
    568     value has changed:: 
    569  
    570         <h1>Archive for {{ year }}</h1> 
    571  
    572         {% for date in days %} 
    573         {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %} 
    574         <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a> 
    575         {% endfor %} 
    576     """ 
    577     bits = token.contents.split() 
    578     if len(bits) != 1: 
    579         raise template.TemplateSyntaxError, "'ifchanged' tag takes no arguments" 
    580     nodelist = parser.parse(('endifchanged',)) 
    581     parser.delete_first_token() 
    582     return IfChangedNode(nodelist) 
    583  
    584 def do_ssi(parser, token): 
    585     """ 
    586     Output the contents of a given file into the page. 
    587  
    588     Like a simple "include" tag, the ``ssi`` tag includes the contents 
    589     of another file -- which must be specified using an absolute page -- 
    590     in the current page:: 
    591  
    592         {% ssi /home/html/ljworld.com/includes/right_generic.html %} 
    593  
    594     If the optional "parsed" parameter is given, the contents of the included 
    595     file are evaluated as template code, with the current context:: 
    596  
    597         {% ssi /home/html/ljworld.com/includes/right_generic.html parsed %} 
    598     """ 
    599     bits = token.contents.split() 
    600     parsed = False 
    601     if len(bits) not in (2, 3): 
    602         raise template.TemplateSyntaxError, "'ssi' tag takes one argument: the path to the file to be included" 
    603     if len(bits) == 3: 
    604         if bits[2] == 'parsed': 
    605             parsed = True 
    606         else: 
    607             raise template.TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0] 
    608     return SsiNode(bits[1], parsed) 
    609  
    610 def do_load(parser, token): 
    611     """ 
    612     Load a custom template tag set. 
    613  
    614     For example, to load the template tags in ``django/templatetags/news/photos.py``:: 
    615  
    616         {% load news.photos %} 
    617     """ 
    618     bits = token.contents.split() 
    619     if len(bits) != 2: 
    620         raise template.TemplateSyntaxError, "'load' statement takes one argument" 
    621     taglib = bits[1] 
    622     # check at compile time that the module can be imported 
    623     try: 
    624         LoadNode.load_taglib(taglib) 
    625     except ImportError: 
    626         raise template.TemplateSyntaxError, "'%s' is not a valid tag library" % taglib 
    627     return LoadNode(taglib) 
    628  
    629 def do_now(parser, token): 
    630     """ 
    631     Display the date, formatted according to the given string. 
    632  
    633     Uses the same format as PHP's ``date()`` function; see http://php.net/date 
    634     for all the possible values. 
    635  
    636     Sample usage:: 
    637  
    638         It is {% now "jS F Y H:i" %} 
    639     """ 
    640     bits = token.contents.split('"') 
    641     if len(bits) != 3: 
    642         raise template.TemplateSyntaxError, "'now' statement takes one argument" 
    643     format_string = bits[1] 
    644     return NowNode(format_string) 
    645  
    646 def do_regroup(parser, token): 
    647     """ 
    648     Regroup a list of alike objects by a common attribute. 
    649  
    650     This complex tag is best illustrated by use of an example:  say that 
    651     ``people`` is a list of ``Person`` objects that have ``first_name``, 
    652     ``last_name``, and ``gender`` attributes, and you'd like to display a list 
    653     that looks like: 
    654  
    655         * Male: 
    656             * George Bush 
    657             * Bill Clinton 
    658         * Female: 
    659             * Margaret Thatcher 
    660             * Colendeeza Rice 
    661         * Unknown: 
    662             * Pat Smith 
    663  
    664     The following snippet of template code would accomplish this dubious task:: 
    665  
    666         {% regroup people by gender as grouped %} 
    667         <ul> 
    668         {% for group in grouped %} 
    669             <li>{{ group.grouper }} 
    670             <ul> 
    671                 {% for item in group.list %} 
    672                 <li>{{ item }}</li> 
    673                 {% endfor %} 
    674             </ul> 
    675         {% endfor %} 
    676         </ul> 
    677  
    678     As you can see, ``{% regroup %}`` populates a variable with a list of 
    679     objects with ``grouper`` and ``list`` attributes.  ``grouper`` contains the 
    680     item that was grouped by; ``list`` contains the list of objects that share 
    681     that ``grouper``.  In this case, ``grouper`` would be ``Male``, ``Female`` 
    682     and ``Unknown``, and ``list`` is the list of people with those genders. 
    683  
    684     Note that `{% regroup %}`` does not work when the list to be grouped is not 
    685     sorted by the key you are grouping by!  This means that if your list of 
    686     people was not sorted by gender, you'd need to make sure it is sorted before 
    687     using it, i.e.:: 
    688  
    689         {% regroup people|dictsort:"gender" by gender as grouped %} 
    690  
    691     """ 
    692     firstbits = token.contents.split(None, 3) 
    693     if len(firstbits) != 4: 
    694         raise template.TemplateSyntaxError, "'regroup' tag takes five arguments" 
    695     target_var = firstbits[1] 
    696     if firstbits[2] != 'by': 
    697         raise template.TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'" 
    698     lastbits_reversed = firstbits[3][::-1].split(None, 2) 
    699     if lastbits_reversed[1][::-1] != 'as': 
    700         raise template.TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'" 
    701     expression = lastbits_reversed[2][::-1] 
    702     var_name = lastbits_reversed[0][::-1] 
    703     return RegroupNode(target_var, expression, var_name) 
    704  
    705 def do_templatetag(parser, token): 
    706     """ 
    707     Output one of the bits used to compose template tags. 
    708  
    709     Since the template system has no concept of "escaping", to display one of 
    710     the bits used in template tags, you must use the ``{% templatetag %}`` tag. 
    711  
    712     The argument tells which template bit to output: 
    713  
    714         ==================  ======= 
    715         Argument            Outputs 
    716         ==================  ======= 
    717         ``openblock``       ``{%`` 
    718         ``closeblock``      ``%}`` 
    719         ``openvariable``    ``{{`` 
    720         ``closevariable``   ``}}`` 
    721         ==================  ======= 
    722     """ 
    723     bits = token.contents.split() 
    724     if len(bits) != 2: 
    725         raise template.TemplateSyntaxError, "'templatetag' statement takes one argument" 
    726     tag = bits[1] 
    727     if not TemplateTagNode.mapping.has_key(tag): 
    728         raise template.TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \ 
    729             (tag, TemplateTagNode.mapping.keys()) 
    730     return TemplateTagNode(tag) 
    731  
    732 def do_widthratio(parser, token): 
    733     """ 
    734     For creating bar charts and such, this tag calculates the ratio of a given 
    735     value to a maximum value, and then applies that ratio to a constant. 
    736  
    737     For example:: 
    738  
    739         <img src='bar.gif' height='10' width='{% widthratio this_value max_value 100 %}' /> 
    740  
    741     Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in 
    742     the above example will be 88 pixels wide (because 175/200 = .875; .875 * 
    743     100 = 87.5 which is rounded up to 88). 
    744     """ 
    745     bits = token.contents.split() 
    746     if len(bits) != 4: 
    747         raise template.TemplateSyntaxError("widthratio takes three arguments") 
    748     tag, this_value_var, max_value_var, max_width = bits 
    749     try: 
    750         max_width = int(max_width) 
    751     except ValueError: 
    752         raise template.TemplateSyntaxError("widthratio final argument must be an integer") 
    753     return WidthRatioNode(this_value_var, max_value_var, max_width) 
    754  
    755 template.register_tag('comment', do_comment) 
    756 template.register_tag('cycle', do_cycle) 
    757 template.register_tag('debug', do_debug) 
    758 template.register_tag('filter', do_filter) 
    759 template.register_tag('firstof', do_firstof) 
    760 template.register_tag('for', do_for) 
    761 template.register_tag('ifequal', lambda parser, token: do_ifequal(parser, token, False)) 
    762 template.register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True)) 
    763 template.register_tag('if', do_if) 
    764 template.register_tag('ifchanged', do_ifchanged) 
    765 template.register_tag('regroup', do_regroup) 
    766 template.register_tag('ssi', do_ssi) 
    767 template.register_tag('load', do_load) 
    768 template.register_tag('now', do_now) 
    769 template.register_tag('templatetag', do_templatetag) 
    770 template.register_tag('widthratio', do_widthratio) 
  • django/core/template_file.py

    old new  
    1 # Wrapper for loading templates from files 
    2  
    3 from django.conf.settings import TEMPLATE_DIRS, TEMPLATE_FILE_EXTENSION 
    4 from django.core.template import TemplateDoesNotExist 
    5 import os 
    6  
    7 def load_template_source(template_name, template_dirs=None): 
    8     if not template_dirs: 
    9         template_dirs = TEMPLATE_DIRS 
    10     tried = [] 
    11     for template_dir in template_dirs: 
    12         filepath = os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION 
    13         try: 
    14             return open(filepath).read() 
    15         except IOError: 
    16             tried.append(filepath) 
    17     if template_dirs: 
    18         error_msg = "Tried %s" % tried 
    19     else: 
    20         error_msg = "Your TEMPLATE_DIRS settings is empty. Change it to point to at least one template directory." 
    21     raise TemplateDoesNotExist, error_msg 
  • django/core/template.py

    old new  
    1 """ 
    2 This is the Django template system. 
    3  
    4 How it works: 
    5  
    6 The tokenize() function converts a template string (i.e., a string containing 
    7 markup with custom template tags) to tokens, which can be either plain text 
    8 (TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK). 
    9  
    10 The Parser() class takes a list of tokens in its constructor, and its parse() 
    11 method returns a compiled template -- which is, under the hood, a list of 
    12 Node objects. 
    13  
    14 Each Node is responsible for creating some sort of output -- e.g. simple text 
    15 (TextNode), variable values in a given context (VariableNode), results of basic 
    16 logic (IfNode), results of looping (ForNode), or anything else. The core Node 
    17 types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can 
    18 define their own custom node types. 
    19  
    20 Each Node has a render() method, which takes a Context and returns a string of 
    21 the rendered node. For example, the render() method of a Variable Node returns 
    22 the variable's value as a string. The render() method of an IfNode returns the 
    23 rendered output of whatever was inside the loop, recursively. 
    24  
    25 The Template class is a convenient wrapper that takes care of template 
    26 compilation and rendering. 
    27  
    28 Usage: 
    29  
    30 The only thing you should ever use directly in this file is the Template class. 
    31 Create a compiled template object with a template_string, then call render() 
    32 with a context. In the compilation stage, the TemplateSyntaxError exception 
    33 will be raised if the template doesn't have proper syntax. 
    34  
    35 Sample code: 
    36  
    37 >>> import template 
    38 >>> s = ''' 
    39 ... <html> 
    40 ... {% if test %} 
    41 ...     <h1>{{ varvalue }}</h1> 
    42 ... {% endif %} 
    43 ... </html> 
    44 ... ''' 
    45 >>> t = template.Template(s) 
    46  
    47 (t is now a compiled template, and its render() method can be called multiple 
    48 times with multiple contexts) 
    49  
    50 >>> c = template.Context({'test':True, 'varvalue': 'Hello'}) 
    51 >>> t.render(c) 
    52 '\n<html>\n\n    <h1>Hello</h1>\n\n</html>\n' 
    53 >>> c = template.Context({'test':False, 'varvalue': 'Hello'}) 
    54 >>> t.render(c) 
    55 '\n<html>\n\n</html>\n' 
    56 """ 
    57 import re 
    58 from django.conf.settings import DEFAULT_CHARSET 
    59  
    60 __all__ = ('Template','Context','compile_string') 
    61  
    62 TOKEN_TEXT = 0 
    63 TOKEN_VAR = 1 
    64 TOKEN_BLOCK = 2 
    65  
    66 # template syntax constants 
    67 FILTER_SEPARATOR = '|' 
    68 FILTER_ARGUMENT_SEPARATOR = ':' 
    69 VARIABLE_ATTRIBUTE_SEPARATOR = '.' 
    70 BLOCK_TAG_START = '{%' 
    71 BLOCK_TAG_END = '%}' 
    72 VARIABLE_TAG_START = '{{' 
    73 VARIABLE_TAG_END = '}}' 
    74  
    75 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' 
    76  
    77 # match a variable or block tag and capture the entire tag, including start/end delimiters 
    78 tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 
    79                                           re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END))) 
    80  
    81 # global dict used by register_tag; maps custom tags to callback functions 
    82 registered_tags = {} 
    83  
    84 # global dict used by register_filter; maps custom filters to callback functions 
    85 registered_filters = {} 
    86  
    87 class TemplateSyntaxError(Exception): 
    88     pass 
    89  
    90 class ContextPopException(Exception): 
    91     "pop() has been called more times than push()" 
    92     pass 
    93  
    94 class TemplateDoesNotExist(Exception): 
    95     pass 
    96  
    97 class VariableDoesNotExist(Exception): 
    98     pass 
    99  
    100 class SilentVariableFailure(Exception): 
    101     "Any function raising this exception will be ignored by resolve_variable" 
    102     pass 
    103  
    104 class Template: 
    105     def __init__(self, template_string): 
    106         "Compilation stage" 
    107         self.nodelist = compile_string(template_string) 
    108  
    109     def __iter__(self): 
    110         for node in self.nodelist: 
    111             for subnode in node: 
    112                 yield subnode 
    113  
    114     def render(self, context): 
    115         "Display stage -- can be called many times" 
    116         return self.nodelist.render(context) 
    117  
    118 def compile_string(template_string): 
    119     "Compiles template_string into NodeList ready for rendering" 
    120     tokens = tokenize(template_string) 
    121     parser = Parser(tokens) 
    122     return parser.parse() 
    123  
    124 class Context: 
    125     "A stack container for variable context" 
    126     def __init__(self, dict=None): 
    127         dict = dict or {} 
    128         self.dicts = [dict] 
    129  
    130     def __repr__(self): 
    131         return repr(self.dicts) 
    132  
    133     def __iter__(self): 
    134         for d in self.dicts: 
    135             yield d 
    136  
    137     def push(self): 
    138         self.dicts = [{}] + self.dicts 
    139  
    140     def pop(self): 
    141         if len(self.dicts) == 1: 
    142             raise ContextPopException 
    143         del self.dicts[0] 
    144  
    145     def __setitem__(self, key, value): 
    146         "Set a variable in the current context" 
    147         self.dicts[0][key] = value 
    148  
    149     def __getitem__(self, key): 
    150         "Get a variable's value, starting at the current context and going upward" 
    151         for dict in self.dicts: 
    152             if dict.has_key(key): 
    153                 return dict[key] 
    154         return '' 
    155  
    156     def __delitem__(self, key): 
    157         "Delete a variable from the current context" 
    158         del self.dicts[0][key] 
    159  
    160     def has_key(self, key): 
    161         for dict in self.dicts: 
    162             if dict.has_key(key): 
    163                 return True 
    164         return False 
    165  
    166     def update(self, other_dict): 
    167         "Like dict.update(). Pushes an entire dictionary's keys and values onto the context." 
    168         self.dicts = [other_dict] + self.dicts 
    169  
    170 class Token: 
    171     def __init__(self, token_type, contents): 
    172         "The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK" 
    173         self.token_type, self.contents = token_type, contents 
    174  
    175     def __str__(self): 
    176         return '<%s token: "%s...">' % ( 
    177             {TOKEN_TEXT:'Text', TOKEN_VAR:'Var', TOKEN_BLOCK:'Block'}[self.token_type], 
    178             self.contents[:20].replace('\n', '') 
    179             ) 
    180  
    181 def tokenize(template_string): 
    182     "Return a list of tokens from a given template_string" 
    183     # remove all empty strings, because the regex has a tendency to add them 
    184     bits = filter(None, tag_re.split(template_string)) 
    185     return map(create_token, bits) 
    186  
    187 def create_token(token_string): 
    188     "Convert the given token string into a new Token object and return it" 
    189     if token_string.startswith(VARIABLE_TAG_START): 
    190         return Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 
    191     elif token_string.startswith(BLOCK_TAG_START): 
    192         return Tok