class Parser
from django.template.base import Parser
Ancestors (MRO)
Attribute | Type | Defined in |
---|---|---|
__dict__ |
getset_descriptor
|
django.template.base.Parser |
__weakref__ |
getset_descriptor
|
django.template.base.Parser |
def __init__(self, tokens, libraries=None, builtins=None, origin=None)
django.template.base.Parser
Initialize self. See help(type(self)) for accurate signature.
def __init__(self, tokens, libraries=None, builtins=None, origin=None):
# Reverse the tokens so delete_first_token(), prepend_token(), and
# next_token() can operate at the end of the list in constant time.
self.tokens = list(reversed(tokens))
self.tags = {}
self.filters = {}
self.command_stack = []
# Custom template tags may store additional data on the parser that
# will be made available on the template instance. Library authors
# should use a key to namespace any added data. The 'django' namespace
# is reserved for internal use.
self.extra_data = {}
if libraries is None:
libraries = {}
if builtins is None:
builtins = []
self.libraries = libraries
for builtin in builtins:
self.add_library(builtin)
self.origin = origin
def __repr__(self)
django.template.base.Parser
Return repr(self).
def __repr__(self):
return "<%s tokens=%r>" % (self.__class__.__qualname__, self.tokens)
def add_library(self, lib)
django.template.base.Parser
def add_library(self, lib):
self.tags.update(lib.tags)
self.filters.update(lib.filters)
def compile_filter(self, token)
django.template.base.Parser
Convenient wrapper for FilterExpression
def compile_filter(self, token):
"""
Convenient wrapper for FilterExpression
"""
return FilterExpression(token, self)
def delete_first_token(self)
django.template.base.Parser
def delete_first_token(self):
del self.tokens[-1]
def error(self, token, e)
django.template.base.Parser
Return an exception annotated with the originating token. Since the parser can be called recursively, check if a token is already set. This ensures the innermost token is highlighted if an exception occurs, e.g. a compile error within the body of an if statement.
def error(self, token, e):
"""
Return an exception annotated with the originating token. Since the
parser can be called recursively, check if a token is already set. This
ensures the innermost token is highlighted if an exception occurs,
e.g. a compile error within the body of an if statement.
"""
if not isinstance(e, Exception):
e = TemplateSyntaxError(e)
if not hasattr(e, "token"):
e.token = token
return e
def extend_nodelist(self, nodelist, node, token)
django.template.base.Parser
def extend_nodelist(self, nodelist, node, token):
# Check that non-text nodes don't appear before an extends tag.
if node.must_be_first and nodelist.contains_nontext:
raise self.error(
token,
"%r must be the first tag in the template." % node,
)
if not isinstance(node, TextNode):
nodelist.contains_nontext = True
# Set origin and token here since we can't modify the node __init__()
# method.
node.token = token
node.origin = self.origin
nodelist.append(node)
def find_filter(self, filter_name)
django.template.base.Parser
def find_filter(self, filter_name):
if filter_name in self.filters:
return self.filters[filter_name]
else:
raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name)
def invalid_block_tag(self, token, command, parse_until=None)
django.template.base.Parser
def invalid_block_tag(self, token, command, parse_until=None):
if parse_until:
raise self.error(
token,
"Invalid block tag on line %d: '%s', expected %s. Did you "
"forget to register or load this tag?"
% (
token.lineno,
command,
get_text_list(["'%s'" % p for p in parse_until], "or"),
),
)
raise self.error(
token,
"Invalid block tag on line %d: '%s'. Did you forget to register "
"or load this tag?" % (token.lineno, command),
)
def next_token(self)
django.template.base.Parser
def next_token(self):
return self.tokens.pop()
def parse(self, parse_until=None)
django.template.base.Parser
Iterate through the parser tokens and compiles each one into a node. If parse_until is provided, parsing will stop once one of the specified tokens has been reached. This is formatted as a list of tokens, e.g. ['elif', 'else', 'endif']. If no matching token is reached, raise an exception with the unclosed block tag details.
def parse(self, parse_until=None):
"""
Iterate through the parser tokens and compiles each one into a node.
If parse_until is provided, parsing will stop once one of the
specified tokens has been reached. This is formatted as a list of
tokens, e.g. ['elif', 'else', 'endif']. If no matching token is
reached, raise an exception with the unclosed block tag details.
"""
if parse_until is None:
parse_until = []
nodelist = NodeList()
while self.tokens:
token = self.next_token()
# Use the raw values here for TokenType.* for a tiny performance boost.
token_type = token.token_type.value
if token_type == 0: # TokenType.TEXT
self.extend_nodelist(nodelist, TextNode(token.contents), token)
elif token_type == 1: # TokenType.VAR
if not token.contents:
raise self.error(
token, "Empty variable tag on line %d" % token.lineno
)
try:
filter_expression = self.compile_filter(token.contents)
except TemplateSyntaxError as e:
raise self.error(token, e)
var_node = VariableNode(filter_expression)
self.extend_nodelist(nodelist, var_node, token)
elif token_type == 2: # TokenType.BLOCK
try:
command = token.contents.split()[0]
except IndexError:
raise self.error(token, "Empty block tag on line %d" % token.lineno)
if command in parse_until:
# A matching token has been reached. Return control to
# the caller. Put the token back on the token list so the
# caller knows where it terminated.
self.prepend_token(token)
return nodelist
# Add the token to the command stack. This is used for error
# messages if further parsing fails due to an unclosed block
# tag.
self.command_stack.append((command, token))
# Get the tag callback function from the ones registered with
# the parser.
try:
compile_func = self.tags[command]
except KeyError:
self.invalid_block_tag(token, command, parse_until)
# Compile the callback into a node object and add it to
# the node list.
try:
compiled_result = compile_func(self, token)
except Exception as e:
raise self.error(token, e)
self.extend_nodelist(nodelist, compiled_result, token)
# Compile success. Remove the token from the command stack.
self.command_stack.pop()
if parse_until:
self.unclosed_block_tag(parse_until)
return nodelist
def prepend_token(self, token)
django.template.base.Parser
def prepend_token(self, token):
self.tokens.append(token)
def skip_past(self, endtag)
django.template.base.Parser
def skip_past(self, endtag):
while self.tokens:
token = self.next_token()
if token.token_type == TokenType.BLOCK and token.contents == endtag:
return
self.unclosed_block_tag([endtag])
def unclosed_block_tag(self, parse_until)
django.template.base.Parser
def unclosed_block_tag(self, parse_until):
command, token = self.command_stack.pop()
msg = "Unclosed tag on line %d: '%s'. Looking for one of: %s." % (
token.lineno,
command,
", ".join(parse_until),
)
raise self.error(token, msg)