/
templatetags.py
213 lines (174 loc) · 6.42 KB
/
templatetags.py
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
"""
A smarter {% if %} tag for django templates.
While retaining current Django functionality, it also handles equality,
greater than and less than operators. Some common case examples::
{% if articles|length >= 5 %}...{% endif %}
{% if "ifnotequal tag" != "beautiful" %}...{% endif %}
"""
import unittest
from django import template
from django.template import FilterExpression
from django.template.loader import get_template
import six
from pyjade.runtime import iteration
register = template.Library()
@register.tag(name="__pyjade_attrs")
def do_evaluate(parser, token):
'''Calls an arbitrary method on an object.'''
code = token.contents
firstspace = code.find(' ')
if firstspace >= 0:
code = code[firstspace+1:]
return Evaluator(code)
class Evaluator(template.Node):
'''Calls an arbitrary method of an object'''
def __init__(self, code):
self.code = code
def render(self, context):
'''Evaluates the code in the page and returns the result'''
modules = {
'pyjade': __import__('pyjade')
}
context['false'] = False
context['true'] = True
return str(eval('pyjade.runtime.attrs(%s)'%self.code,modules,context))
@register.tag(name="__pyjade_set")
def do_set(parser, token):
'''Calls an arbitrary method on an object.'''
code = token.contents
firstspace = code.find(' ')
if firstspace >= 0:
code = code[firstspace+1:]
return Setter(code)
class Setter(template.Node):
'''Calls an arbitrary method of an object'''
def __init__(self, code):
self.code = code
def render(self, context):
'''Evaluates the code in the page and returns the result'''
modules = {
}
context['false'] = False
context['true'] = True
new_ctx = eval('dict(%s)'%self.code,modules,context)
context.update(new_ctx)
return ''
register.filter('__pyjade_iter', iteration)
# Support for macros in Django, taken from https://gist.github.com/skyl/1715202
# Author: Skylar Saveland
def _setup_macros_dict(parser):
## Metadata of each macro are stored in a new attribute
## of 'parser' class. That way we can access it later
## in the template when processing 'usemacro' tags.
try:
## Only try to access it to eventually trigger an exception
parser._macros
except AttributeError:
parser._macros = {}
class DefineMacroNode(template.Node):
def __init__(self, name, nodelist, args):
self.name = name
self.nodelist = nodelist
self.args = []
self.kwargs = {}
for a in args:
a = a.rstrip(',')
if "=" not in a:
self.args.append(a)
else:
name, value = a.split("=")
self.kwargs[name] = value
def render(self, context):
## empty string - {% macro %} tag does no output
return ''
@register.tag(name="__pyjade_kwacro")
def do_macro(parser, token):
try:
args = token.split_contents()
tag_name, macro_name, args = args[0], args[1], args[2:]
except IndexError:
m = ("'%s' tag requires at least one argument (macro name)"
% token.contents.split()[0])
raise template.TemplateSyntaxError(m)
# TODO: could do some validations here,
# for now, "blow your head clean off"
nodelist = parser.parse(('end__pyjade_kwacro', ))
parser.delete_first_token()
## Metadata of each macro are stored in a new attribute
## of 'parser' class. That way we can access it later
## in the template when processing 'usemacro' tags.
_setup_macros_dict(parser)
parser._macros[macro_name] = DefineMacroNode(macro_name, nodelist, args)
return parser._macros[macro_name]
class LoadMacrosNode(template.Node):
def render(self, context):
## empty string - {% loadmacros %} tag does no output
return ''
@register.tag(name="__pyjade_loadkwacros")
def do_loadmacros(parser, token):
try:
tag_name, filename = token.split_contents()
except IndexError:
m = ("'%s' tag requires at least one argument (macro name)"
% token.contents.split()[0])
raise template.TemplateSyntaxError(m)
if filename[0] in ('"', "'") and filename[-1] == filename[0]:
filename = filename[1:-1]
t = get_template(filename)
macros = t.nodelist.get_nodes_by_type(DefineMacroNode)
## Metadata of each macro are stored in a new attribute
## of 'parser' class. That way we can access it later
## in the template when processing 'usemacro' tags.
_setup_macros_dict(parser)
for macro in macros:
parser._macros[macro.name] = macro
return LoadMacrosNode()
class UseMacroNode(template.Node):
def __init__(self, macro, fe_args, fe_kwargs):
self.macro = macro
self.fe_args = fe_args
self.fe_kwargs = fe_kwargs
def render(self, context):
for i, arg in enumerate(self.macro.args):
try:
fe = self.fe_args[i]
context[arg] = fe.resolve(context)
except IndexError:
context[arg] = ""
for name, default in six.iteritems(self.macro.kwargs):
if name in self.fe_kwargs:
context[name] = self.fe_kwargs[name].resolve(context)
else:
context[name] = FilterExpression(default,
self.macro.parser
).resolve(context)
return self.macro.nodelist.render(context)
@register.tag(name="__pyjade_usekwacro")
def do_usemacro(parser, token):
try:
args = token.split_contents()
tag_name, macro_name, values = args[0], args[1], args[2:]
except IndexError:
m = ("'%s' tag requires at least one argument (macro name)"
% token.contents.split()[0])
raise template.TemplateSyntaxError(m)
try:
macro = parser._macros[macro_name]
except (AttributeError, KeyError):
m = "Macro '%s' is not defined" % macro_name
raise template.TemplateSyntaxError(m)
fe_kwargs = {}
fe_args = []
for val in values:
val = val.rstrip(',')
if "=" in val:
# kwarg
name, value = val.split("=")
fe_kwargs[name] = FilterExpression(value, parser)
else: # arg
# no validation, go for it ...
fe_args.append(FilterExpression(val, parser))
macro.parser = parser
return UseMacroNode(macro, fe_args, fe_kwargs)
if __name__ == '__main__':
unittest.main()