Skip to content

Instantly share code, notes, and snippets.

@boekkooi
Created January 22, 2014 06:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boekkooi/8554447 to your computer and use it in GitHub Desktop.
Save boekkooi/8554447 to your computer and use it in GitHub Desktop.
Django - A hack to apply custom templates to tags registered using inclusion_tag, this is related to https://code.djangoproject.com/ticket/9093
import sys
import ctypes
import types
from django.template import TemplateSyntaxError, Token, TOKEN_BLOCK
from django.template.defaulttags import register
if sys.version_info < (3,):
cb_func_code = 'func_code'
cb_func_closure = 'func_closure'
else:
cb_func_code = '__code__'
cb_func_closure = '__closure__'
@register.tag('using')
def do_using(parser, token):
"""
Adds custom template for template tags defined with inclusion_tag
For example::
{% using "my_template.html" for some_tag %}
"""
bits = token.split_contents()
if len(bits) < 4:
raise TemplateSyntaxError("using takes at least 4 arguments")
if bits[2] != 'for':
raise TemplateSyntaxError("Invalid syntax in using tag. Expecting 'for' keyword")
template_name = str(bits[1]).strip('"').strip("'")
tag_name = bits[3]
tag_content = token.contents[token.contents.find(tag_name):]
if tag_name not in parser.tags:
raise TemplateSyntaxError("using received an invalid tag: %s" % tag_name)
tag_compile = parser.tags[tag_name]
def render_injector(self, context):
"""
This method injects a custom file_name into the render function of the bound object
"""
func = types.MethodType(self.__class__.render, self)
func_closure = getattr(func, cb_func_closure)
template_idx = getattr(func, cb_func_code).co_freevars.index('file_name')
func_var = func_closure[template_idx]
# Prepare for c injection
c_target = ctypes.py_object(func_var)
c_org_value = ctypes.py_object(func_var.cell_contents)
c_new_value = ctypes.py_object(template_name)
# Do a very dirty HACK! to override the file_name
ctypes.pythonapi.PyCell_Set(c_target, c_new_value)
try:
return func(context)
finally:
ctypes.pythonapi.PyCell_Set(c_target, c_org_value)
# Compile a tag node
tag_node = tag_compile(parser, Token(TOKEN_BLOCK, tag_content))
# Validate render method
if not hasattr(tag_node.render, cb_func_closure) or \
not hasattr(tag_node.render, cb_func_code) or \
'file_name' not in getattr(tag_node.render, cb_func_code).co_freevars:
raise RuntimeError('using is unable to use callback invalid closure/function specified.')
# Set the render_injector as the render method
tag_node.render = types.MethodType(render_injector, tag_node)
return tag_node
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment