Permalink
Browse files

Merge pull request #1 from drtyrsa/inclusion_tag

Inclusion tag
  • Loading branch information...
2 parents 1cdab0e + d068d1c commit e7904584c0206c445cda8c13d1f89168f584fdc8 @drtyrsa committed Nov 29, 2011
Showing with 238 additions and 130 deletions.
  1. +26 −16 src/easytags/library.py
  2. +44 −30 src/easytags/node.py
  3. +121 −69 src/easytags/tests/test_node.py
  4. +47 −15 src/easytags/tests/test_parser.py
@@ -8,7 +8,7 @@
from django.template import Library
-from node import EasyNode, EasyAsNode
+from node import EasyNode, EasyAsNode, EasyIncNode
class EasyLibrary(Library):
@@ -18,40 +18,50 @@ def _get_name_and_renderer(cls, name, renderer):
renderer = name
name = renderer.__name__
return name, renderer
-
+
def easytag(self, name = None, renderer = None):
return self._handle_decorator(EasyNode, name, renderer)
-
+
def easyastag(self, name = None, renderer = None):
return self._handle_decorator(EasyAsNode, name, renderer)
-
-
- def _handle_decorator(self, node_class, name, renderer):
+
+ def easyinctag(self, name = None, renderer = None, **kwargs):
+ if 'template_name' not in kwargs:
+ raise TypeError('Named argument "template_name" is required')
+ return self._handle_decorator(EasyIncNode, name, renderer, **kwargs)
+
+ def _handle_decorator(self, node_class, name, renderer, **kwargs):
if not name and not renderer:
return self.easytag
if not renderer:
if callable(name):
renderer = name
- return self._register_easytag(node_class, renderer.__name__, renderer)
+ return self._register_easytag(node_class, renderer.__name__, renderer, **kwargs)
else:
def dec(renderer):
- return self._register_easytag(node_class, name, renderer)
+ return self._register_easytag(node_class, name, renderer, **kwargs)
return dec
- return self._register_easytag(node_class, name, renderer)
-
- def _register_easytag(self, node_class, name, renderer):
+ return self._register_easytag(node_class, name, renderer, **kwargs)
+
+ def _register_easytag(self, node_class, name, renderer, **kwargs):
if not renderer:
renderer = name
name = renderer.__name__
-
+
def render_context(self, context, *args, **kwargs):
return renderer(context, *args, **kwargs)
-
+
get_argspec = classmethod(lambda cls: node_class.get_argspec(renderer))
-
- tag_node = type('%sEasyNode' % name, (node_class,), {
+
+ class_dict = {
'render_context': render_context,
'get_argspec': get_argspec,
- })
+ }
+
+ if 'template_name' in kwargs:
+ class_dict['template_name'] = kwargs['template_name']
+ class_dict['takes_context'] = kwargs.get('takes_context', False)
+
+ tag_node = type('%sEasyNode' % name, (node_class,), class_dict)
self.tag(name, tag_node.parse)
return renderer
View
@@ -8,72 +8,78 @@
from inspect import getargspec
-from django.template import Node, Variable, TemplateSyntaxError
+from django.template import Node, Variable, TemplateSyntaxError, Context
+from django.template.loader import render_to_string
is_kwarg = lambda bit: not bit[0] in (u'"', u"'") and u'=' in bit
-def get_args_kwargs_from_bits(bits):
+def get_args_kwargs_from_bits(parser, bits):
args = []
kwargs = {}
for bit in bits:
if is_kwarg(bit):
- splitted_bit = bit.split(u'=')
- kwargs[splitted_bit[0]] = u'='.join(splitted_bit[1:])
+ key, value = bit.split(u'=', 1)
+ kwargs[key] = parser.compile_filter(value)
else:
if not kwargs:
- args.append(bit)
+ args.append(parser.compile_filter(bit))
else:
raise TemplateSyntaxError(u"Args must be before kwargs.")
-
+
return {'args': tuple(args), 'kwargs': kwargs}
+def SmartVariable(var):
+ if hasattr(var, 'resolve'):
+ return var
+ return Variable(var)
+
class EasyNode(Node):
-
+
@classmethod
def parse_to_args_kwargs(cls, parser, token):
bits = token.split_contents()
- return get_args_kwargs_from_bits(bits[1:])
-
+ return get_args_kwargs_from_bits(parser, bits[1:])
+
@classmethod
def parse(cls, parser, token):
args_kwargs = cls.parse_to_args_kwargs(parser, token)
cls.is_args_kwargs_valid(args_kwargs)
return cls(args_kwargs)
-
+
@classmethod
def get_argspec(cls, func = None):
func = func or cls.render_context
return getargspec(func)
-
+
@classmethod
def is_args_kwargs_valid(cls, args_kwargs):
render_context_spec = cls.get_argspec()
-
+
args = args_kwargs['args']
kwargs = args_kwargs['kwargs']
-
+
valid_args_names = render_context_spec.args
if 'self' in valid_args_names: valid_args_names.remove('self')
if 'context' in valid_args_names: valid_args_names.remove('context')
-
+
n_args_kwargs = len(args) + len(kwargs)
-
- max_n_args_kwargs = len(valid_args_names)
+
+ max_n_args_kwargs = len(valid_args_names)
if not render_context_spec.varargs and not render_context_spec.keywords and n_args_kwargs > max_n_args_kwargs:
raise TemplateSyntaxError(u'Invalid number of args %s (max. %s)' % (n_args_kwargs, max_n_args_kwargs))
-
+
min_n_args_kwargs = max_n_args_kwargs - len(render_context_spec.defaults or ())
if n_args_kwargs < min_n_args_kwargs:
raise TemplateSyntaxError(u'Invalid number of args %s (min. %s)' % (n_args_kwargs, max_n_args_kwargs))
-
+
required_args_names = valid_args_names[len(args):min_n_args_kwargs]
for required_arg_name in required_args_names:
if not required_arg_name in kwargs:
raise TemplateSyntaxError(u'Required arg missing: %s' % required_arg_name)
-
+
first_kwarg_index = len(args)
if not render_context_spec.keywords:
valid_kwargs = valid_args_names[first_kwarg_index:]
@@ -84,23 +90,23 @@ def is_args_kwargs_valid(cls, args_kwargs):
defined_args = valid_args_names[:first_kwarg_index]
for kwarg in kwargs:
if kwarg in defined_args:
- raise TemplateSyntaxError(u'%s was defined twice.' % kwarg)
-
+ raise TemplateSyntaxError(u'%s was defined twice.' % kwarg)
+
def __init__(self, args_kwargs):
- self.args = [Variable(arg) for arg in args_kwargs['args']]
- self.kwargs = dict((key, Variable(value)) for key, value in args_kwargs['kwargs'].items())
-
+ self.args = [SmartVariable(arg) for arg in args_kwargs['args']]
+ self.kwargs = dict((key, SmartVariable(value)) for key, value in args_kwargs['kwargs'].iteritems())
+
def render(self, context):
args = [arg.resolve(context) for arg in self.args]
- kwargs = dict((str(key), value.resolve(context)) for key, value in self.kwargs.items())
+ kwargs = dict((str(key), value.resolve(context)) for key, value in self.kwargs.iteritems())
return self.render_context(context, *args, **kwargs)
-
+
def render_context(self, context, *args, **kwargs):
raise NotImplementedError
class EasyAsNode(EasyNode):
-
+
@classmethod
def parse_to_args_kwargs(cls, parser, token):
bits = token.split_contents()[1:]
@@ -109,17 +115,25 @@ def parse_to_args_kwargs(cls, parser, token):
bits = bits[:-2]
else:
varname = None
- args_kwargs = get_args_kwargs_from_bits(bits)
+ args_kwargs = get_args_kwargs_from_bits(parser, bits)
args_kwargs['varname'] = varname
return args_kwargs
-
+
def __init__(self, args_kwargs):
super(EasyAsNode, self).__init__(args_kwargs)
self.varname = args_kwargs['varname']
-
+
def render(self, context):
rendered = super(EasyAsNode, self).render(context)
if self.varname:
context[self.varname] = rendered
return u''
return rendered
+
+class EasyIncNode(EasyNode):
+ def render(self, context):
+ if not self.takes_context:
+ context = Context({})
+ rendered = super(EasyIncNode, self).render(context)
+ context.update(rendered)
+ return render_to_string(self.template_name, context)
Oops, something went wrong.

0 comments on commit e790458

Please sign in to comment.