Source code for tornadobabel.extract
# -*- coding: utf-8 -*-
"""
extract
Extract messages from tornado templates
:copyright: (c) 2012 by Openlabs Technologies & Consulting (P) Limited
:license: BSD, see LICENSE for more details.
"""
import ast
from tornado import escape
from tornado.template import (_UNSET, _DEFAULT_AUTOESCAPE, _TemplateReader,
_parse, _File, _Expression)
GETTEXT_FUNCTIONS = ('_', '_N', 'gettext', 'ngettext')
[docs]class DummyTemplate(object):
"""A template object just used to parse the template string.
The class resembles tornado.Template but stops at parsing the file.
"""
def __init__(self, template_string, name="<string>", loader=None,
compress_whitespace=None, autoescape=_UNSET):
self.name = name
self.compress_whitespace = True
if autoescape is not _UNSET:
self.autoescape = autoescape
elif loader:
self.autoescape = loader.autoescape
else:
self.autoescape = _DEFAULT_AUTOESCAPE
self.namespace = loader.namespace if loader else {}
reader = _TemplateReader(name, escape.native_str(template_string))
self.file = _File(self, _parse(reader, self))
[docs]def walk(node):
"""
Given a template node, walk over all its descendants
>>> t = DummyTemplate("{{ _('Hello') }}")
>>> len(list(walk(t.file)))
3
>>> t = DummyTemplate("{% if a %}{{ _('Hello') }}{% end %}")
>>> len(list(walk(t.file)))
5
"""
for child in node.each_child():
if child.each_child():
for grandchild in walk(child):
yield grandchild
yield child
[docs]def extract_from_node(expression, gettext_functions=None):
"""Extract localizable strings from the given Template Expression
:param expression: A node of type tornado.template._Expression
:param gettext_functions: A list of gettext function names that should be
parsed from template
:return: iterator returning tuple in the format babel wants
"""
if gettext_functions is None:
gettext_functions = GETTEXT_FUNCTIONS
for node in ast.walk(ast.parse(expression.expression)):
# Recursively walk through all descendant nodes
if isinstance(node, ast.Call):
# If the type is a function call
if not (
isinstance(node.func, ast.Name) and \
node.func.id in gettext_functions):
continue
strings = []
for arg in node.args:
if isinstance(arg, ast.Str):
strings.append(arg.s)
else:
strings.append(None)
for arg in node.keywords:
strings.append(None)
if node.starargs is not None:
strings.append(None)
if node.kwargs is not None:
strings.append(None)
if len(strings) == 1:
strings, = strings
else:
strings = tuple(strings)
yield expression.line, node.func.id, strings
[docs]def extract_tornado(fileobj, keywords, comment_tags, options):
"""Extract messages from Python source code.
:param fileobj: the seekable, file-like object the messages should be
extracted from
:param keywords: a list of keywords (i.e. function names) that should be
recognized as translation functions
:param comment_tags: a list of translator tags to search for and include
in the results. (Not implemented yet)
:param options: a dictionary of additional options (optional)
:return: an iterator over ``(lineno, funcname, message, comments)`` tuples
:rtype: ``iterator``
"""
template = DummyTemplate(
fileobj.read(),
file.name or options.get('name', '<string>'),
)
for node in walk(template.file):
if isinstance(node, _Expression):
for lineno, func, message in extract_from_node(node):
# TODO: Implement the comment feature, right now an empty
# iterable is returned
yield lineno, func, message, []