Skip to content

Instantly share code, notes, and snippets.

@voldmar
Created November 26, 2011 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save voldmar/1395817 to your computer and use it in GitHub Desktop.
Save voldmar/1395817 to your computer and use it in GitHub Desktop.
Django command for getting info about template tags and filters within your project
# coding: utf-8
'''Django command for getting info about template tags and filters within your project
It suports two output formats: human readable (with extra doc) and vim quickfix format
Usage: ./manage.py tag_info tag_or_filter_name
'''
import os
import sys
from optparse import make_option
import inspect
import textwrap
from django.core.management.base import BaseCommand, CommandError
from django.template import get_templatetags_modules, get_library, InvalidTemplateLibrary, builtins
from django.utils.importlib import import_module
FORMATS = {
'vim': '{path}:{line}:{name}',
'human': '{name} in line {line} of {path}',
}
class Command(BaseCommand):
help = 'Show all signals receivers'
option_list = BaseCommand.option_list + (
make_option('--format',
dest='line_format',
default='human',
choices=FORMATS.keys(),
help='Line format (available choices: {0})'.format(', '.join(FORMATS)),
),
make_option('--show-doc',
action='store_true',
dest='show_doc',
help='Show docstring for found tag or filter, if --format=human (default: False)',
),
)
def handle(self, name, *args, **options):
line_format = options['line_format']
show_doc = options['show_doc']
if line_format not in FORMATS:
raise CommandError('format must be on of {0}, not {1}'.format(line_format, FORMATS.keys()))
msg = FORMATS[line_format]
libraries = set(builtins[:])
for module in set(get_templatetags_modules()):
templatetags_module = import_module(module)
files = os.listdir(os.path.dirname(templatetags_module.__file__))
lib_names = [lib_name for lib_name, ext in map(os.path.splitext, files) if ext == '.py' and not lib_name.startswith('_')]
for lib_name in lib_names:
try:
libraries.add(get_library(lib_name))
except InvalidTemplateLibrary:
pass
def print_info(obj):
# Here be dragons
#
# Very strange Python and Django magic: `inclusion_tag` and
# `simple_tag` returns curried function named `generic_tag_compiler`
# that has Node as it fourth arg and Node has `render` method with
# free variables containing our original functions
#
# After that knowledge other is very easy but lil’ tricky
template = None
if obj.func_name == '_curried':
cls = obj.func_closure[0].cell_contents[3]
# It evaluates to string for inslusion_tag
template = cls.render.im_func.func_closure[0].cell_contents
orig = cls.render.im_func.func_closure[1].cell_contents
obj = orig
# import ipdb; ipdb.set_trace()
if line_format == 'human' and show_doc:
args, varargs, keywords, defaults = inspect.getargspec(obj)
doc = inspect.getdoc(obj)
spec = []
if args:
if defaults:
spec.append(', '.join(args[:len(defaults)]))
spec.append(', '.join('{0}={1!r}'.format(key, value) for key, value in zip(args[len(defaults):], defaults)))
else:
spec.append(', '.join(args))
if varargs:
spec.append('*' + varargs)
if keywords:
spec.append('**' + keywords)
print '{name}({spec})'.format(name=obj.func_name, spec=', '.join(spec)),
if template:
print '- decorated by inclusion_tag("{template}")'.format(template=template)
else:
print
print ' # Line {line} of {path}'.format(name=obj.func_name, line=inspect.getsourcelines(obj)[1], path=inspect.getsourcefile(obj))
if doc:
for line in doc.splitlines():
print ' ' + line
else:
print msg.format(name=obj.func_name, line=inspect.getsourcelines(obj)[1], path=inspect.getsourcefile(obj))
for lib in libraries:
if name in lib.tags:
print_info(lib.tags[name])
if name in lib.filters:
print_info(lib.filters[name])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment