1.Django模板层的核心组件
1.模板引擎(Engine)
- django.template.engine.Engine类是Django模板引擎的核心。它负责编译和渲染模板。
- 使用Engine类,可以创建Template对象,代表一个已编译的模板。
2.模板(Template)
- django.template.base.Template类表示一个编译后的模板。
- 模板中包含HTML代码和嵌入式的模板标签和变量。
3.模板标签和过滤器
- 模板标签和过滤器是模板语言的一部分,用于在模板中执行逻辑、迭代、条件判断等操作。
- 例如,{% for item in items %}…{% endfor %} 是一个用于迭代的标签。
4.模板上下文(Context)
- django.template.context.Context类用于存储模板渲染时的变量和其对应的值。
- 上下文将模板中的变量映射到实际的数值。
5.模板加载器(Loader)
- django.template.loaders模块中的类负责从文件系统、数据库等地方加载模板源代码。
- 默认的加载器将从应用程序的templates目录中加载模板。
6.模板响应(Response)
- django.template.response.TemplateResponse类封装了渲染后的模板和其他相关信息,用于生成HTTP响应。
7.模板引擎设置
- TEMPLATES设置定义了Django中使用的模板引擎的配置。
- 可以配置多个引擎,每个引擎可以有不同的加载器、上下文处理器等。
1.Django中模板引擎加载过程源码解析
-
模板引擎初始化(django.template.engine.Engine
):
django.template.engine.Engine 类是负责配置和初始化模板引擎的核心类。在这个类的构造函数中,它接受 TEMPLATES 设置的值,解析其中的配置,并完成引擎的初始化。# 位于 django.template.engine 模块 class Engine(BaseEngine): def __init__(self, dirs=None, app_dirs=False, **options): options = options.copy() options['libraries'] = options.get('libraries', {}) options['file_charset'] = options.get('file_charset', settings.FILE_CHARSET) if app_dirs: if 'APP_DIRS' not in options: options['APP_DIRS'] = True if dirs is None: dirs = [] for app in reversed(Apps.get_app_configs()): template_dir = os.path.join(app.path, 'templates') if template_dir not in dirs: dirs.append(template_dir) super().__init__(dirs, **options)
-
获取模板引擎配置(django.template.loader.engine
)`:
django.template.loader.engine 模块提供了用于获取和配置模板引擎的函数。其中,find_template_engine 函数负责根据 TEMPLATES 设置中的配置找到并返回合适的模板引擎。# 位于 django.template.loader.engine 模块 def find_template_engine(using=None): """ Find and return the first template engine in TEMPLATES that matches the given 'using' argument. """ for engine in engines.all(): if engine['NAME'] == using: return engine if using is None and len(engines) > 0: return engines.all()[0] raise ImproperlyConfigured("No DjangoTemplates backend is configured.")
-
加载模板引擎(django.template.loader)
:
django.template.loader 模块中的 get_template 函数用于加载模板,实际上会调用 find_template_engine 函数来获取合适的引擎。# 位于 django.template.loader 模块 def get_template(template_name, using=None): """ Load and return a template for the given name. Raise TemplateDoesNotExist if no such template exists. """ return engines[using].get_template(template_name)
2.Context类、Engine类和Template类源码解读
-
Context类(上下文类)
是 Django 模板层中的关键类,位于django/template/context.py
文件中,用于存储和处理模板渲染时的变量和其对应的值。通过上下文类,可以在模板渲染的过程中传递变量、处理变量的作用域,并提供了一些额外的功能,如上下文的管理和状态的保存。# 从代码中截取关键部分 class BaseContext: def __init__(self, dict_=None): self._reset_dicts(dict_) # ... 省略其他方法 ... # push 方法:用于将一个或多个上下文字典推入堆栈,形成新的上下文。这允许在模板渲染过程中创建新的上下文,使变量的作用域得以管理。 def push(self, *args, **kwargs): dicts = [] for d in args: if isinstance(d, BaseContext): dicts += d.dicts[1:] else: dicts.append(d) return ContextDict(self, *dicts, **kwargs) # push 方法:用于将当前上下文字典从堆栈中弹出。这用于结束对上下文的特定修改,回到上一个上下文状态。 def pop(self): if len(self.dicts) == 1: raise ContextPopException return self.dicts.pop() # 通过 __getitem__、__setitem__、__delitem__ 方法,可以在当前上下文中进行变量的访问、设置和删除。 def __setitem__(self, key, value): self.dicts[-1][key] = value def __getitem__(self, key): for d in reversed(self.dicts): if key in d: return d[key] raise KeyError(key) def __delitem__(self, key): del self.dicts[-1][key] # ... 省略其他方法 ... class Context(BaseContext): def __init__(self, dict_=None, autoescape=True, use_l10n=None, use_tz=None): # 定义了一些额外的属性,如 autoescape、use_l10n、use_tz 等,用于处理模板渲染时的相关配置 self.autoescape = autoescape self.use_l10n = use_l10n self.use_tz = use_tz self.template_name = "unknown" self.render_context = RenderContext() self.template = None super().__init__(dict_)
-
Engine 类
位于django/template/engine.py
文件中,它是负责配置和初始化模板引擎的核心类。Django支持多个模板引擎,例如django.template.backends.django.DjangoTemplates是Django自带的模板引擎。class Engine: # default_builtins 属性包含了默认的内置标签和过滤器。 default_builtins = [ 'django.template.defaulttags', # django内置标签 { % for % } 'django.template.defaultfilters', 'django.template.loader_tags', ] # 构造函数,用于初始化模板引擎的配置。接受多个参数,包括模板目录、是否包含应用程序模板目录、上下文处理器、调试模式、加载器、字符集等。 def __init__(self, dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None, autoescape=True): ... def get_default(): # 静态方法,返回第一个配置的 DjangoTemplates 引擎实例。用于获取默认的模板引擎 @cached_property def template_context_processors(self): # cached_property 属性,返回最终的上下文处理器列表。包括内置的处理器和用户配置的处理器。 context_processors = _builtin_context_processors context_processors += tuple(self.context_processors) return tuple(import_string(path) for path in context_processors) def get_template_builtins(self, builtins): # 返回最终的模板内置标签和过滤器列表 return [import_library(x) for x in builtins] def get_template_libraries(self, libraries): # 返回最终的模板标签库字典 loaded = {} for name, path in libraries.items(): loaded[name] = import_library(path) return loaded @cached_property def template_loaders(self): # cached_property 属性,返回模板加载器的列表。调用 get_template_loaders 方法获取加载器列表。 return self.get_template_loaders(self.loaders) def get_template_loaders(self, template_loaders): # 返回模板加载器的列表。 loaders = [] for template_loader in template_loaders: loader = self.find_template_loader(template_loader) if loader is not None: loaders.append(loader) return loaders def find_template_loader(self, loader): # 根据给定的加载器配置返回加载器实例。 if isinstance(loader, (tuple, list)): loader, *args = loader else: args = [] if isinstance(loader, str): loader_class = import_string(loader) return loader_class(self, *args) else: raise ImproperlyConfigured( "Invalid value in template loaders configuration: %r" % loader) def find_template(self, name, dirs=None, skip=None): # 查找模板并返回模板对象和模板的起源。支持查找多个目录。 tried = [] for loader in self.template_loaders: try: template = loader.get_template(name, skip=skip) return template, template.origin except TemplateDoesNotExist as e: tried.extend(e.tried) raise TemplateDoesNotExist(name, tried=tried) def from_string(self, template_code): # 返回编译后的模板对象,用于处理从字符串中生成的模板代码。 return Template(template_code, engine=self) def get_template(self, template_name): # 返回编译后的模板对象,用于处理从模板名称中加载的模板。支持模板继承。 template, origin = self.find_template(template_name) if not hasattr(template, 'render'): # template needs to be compiled template = Template(template, origin, template_name, engine=self) return template def render_to_string(self, template_name, context=None): # 渲染指定名称的模板,并返回渲染后的字符串。可用于测试。 if isinstance(template_name, (list, tuple)): t = self.select_template(template_name) else: t = self.get_template(template_name) if isinstance(context, Context): return t.render(context) else: return t.render(Context(context, autoescape=self.autoescape)) def select_template(self, template_name_list): # 给定模板名称列表,返回第一个可加载的模板对象 if not template_name_list: raise TemplateDoesNotExist("No template names provided") not_found = [] for template_name in template_name_list: try: return self.get_template(template_name) except TemplateDoesNotExist as exc: if exc.args[0] not in not_found: not_found.append(exc.args[0]) continue # If we get here, none of the templates could be loaded raise TemplateDoesNotExist(', '.join(not_found))
-
Django的Template类表示已经被编译的模板,源码位于
django/template/base.py
文件中。# django.template.base module from django.template.context import Context from django.template.engine import Engine class Template: # Template类的初始化方法接收一个模板字符串 (template_string) 和一个可选的模板引擎 (engine)。如果未提供模板引擎,则使用默认引擎。 def __init__(self, template_string, engine=None): self.template_string = template_string self.engine = engine or Engine.get_default() # render方法用于渲染模板,接收一个可选的上下文 (context) 和一个可选的请求对象 (request)。 def render(self, context=None, request=None): if context is None: context = {} if not isinstance(context, Context): context = Context(context) # 调用engine.render方法,该方法负责实际的模板编译和渲染工作。 return self.engine.render(self.template_string, context, request)
使用示例:
用户通常只需直接使用Template类。
使用Template(template_string)创建编译后的模板对象。
使用包含渲染变量的Context调用模板对象的render()方法。>>> from django import template >>> s = '<html>{% if test %}<h1>{{ varvalue }}</h1>{% endif %}</html>' >>> t = template.Template(s) >>> c = template.Context({'test':True, 'varvalue': 'Hello'}) >>> t.render(c) '<html><h1>Hello</h1></html>' >>> c = template.Context({'test':False, 'varvalue': 'Hello'}) >>> t.render(c) '<html></html>'
-
在Django中,
Context
类、Engine
类和Template
类是与模板系统相关的三个重要组件,它们之间的关系如下:首先,Engine加载并编译模板,然后你创建一个Context实例,将要在模板中使用的数据添加到该实例中。最后,通过调用Template类的render方法,将Context中的数据渲染到模板中,得到最终的输出。
4.Token类、Lexer类和Parser类源码解读
-
在Django中,
Token类
、Lexer类
和Parser类
是与模板系统密切相关的三个组件,它们分别负责处理和解析模板内容。在模板解析过程中,Lexer类会生成一个Token实例的列表,其中每个实例都代表一个标记,Parser类接收Token列表,进行语法分析,生成Node对象的列表,模板系统最终使用这些Node对象来渲染模板,根据模板中的变量、条件和循环等逻辑生成最终的输出。 -
Django模板引擎中的
Token类
。源码定义位于django/template/base.py
文件中。这个类主要用于表示模板中的标记,标记可以是文本、变量或块语句。class Token: # 初始化一个 Token 实例 def __init__(self, token_type, contents, position=None, lineno=None): self.token_type, self.contents = token_type, contents self.lineno = lineno self.position = position # _str__ 方法返回一个可读的字符串表示 Token def __str__(self): token_name = self.token_type.name.capitalize() return ('<%s token: "%s...">' % (token_name, self.contents[:20].replace('\n', ''))) # split_contents 方法用于将标记内容拆分为单独的片段 def split_contents(self): split = [] bits = smart_split(self.contents) for bit in bits: if bit.startswith(('_("', "_('")): sentinel = bit[2] + ')' trans_bit = [bit] while not bit.endswith(sentinel): bit = next(bits) trans_bit.append(bit) bit = ' '.join(trans_bit) split.append(bit) return split
-
Lexer类
是模板解析器中的词法分析器,负责将模板字符串转换为一系列的标记(Token实例)。源码定义位于django/template/base.py
文件中。
class Lexer:
def __init__(self, template_string):
self.template_string = template_string
self.verbatim = False
# tokenize()方法将模板字符串拆分成标记,生成一个Token实例的列表。
def tokenize(self):
in_tag = False
lineno = 1
result = []
for bit in tag_re.split(self.template_string):
if bit:
result.append(self.create_token(bit, None, lineno, in_tag))
in_tag = not in_tag
lineno += bit.count('\n')
return result
Parser类
是模板解析器中的语法分析器,负责将一系列标记(Token实例)转换为编译后的模板,即Node对象的列表,源码定义位于django/template/base.py
文件中。
class Parser:
# parse()方法将这些标记转换为编译后的模板。每个Node对象代表模板中的一部分,可以是文本、变量、条件语句或循环语句等。
def parse(self, parse_until=None):
# parse_until参数指定终止条件
if parse_until is None:
parse_until = []
nodelist = NodeList() # nodelist 用于存储解析后的节点
# 进入一个循环,遍历self.tokens中的每个标记
while self.tokens:
# self.next_token()获取下一个标记。
token = self.next_token()
# 如果是文本标记(TokenType.TEXT),将其转换为TextNode并添加到nodelist。
if token.token_type.value == 0: # TokenType.TEXT 如 <html>
self.extend_nodelist(nodelist, TextNode(token.contents), token)
# 如果是变量标记(TokenType.VAR),将其编译为VariableNode并添加到nodelist。
elif token.token_type.value == 1: # TokenType.VAR 如 {{ value }}
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)
# 如果是块标记(TokenType.BLOCK),解析标签名并查找相应的标签处理函数。然后将其编译为节点并添加到nodelist。
elif token.token_type.value == 2: # TokenType.BLOCK 如 {% if %} xxxx {% endif %}
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:
# 检查提取的标签名是否在 parse_until 列表中
self.prepend_token(token)
return nodelist
# 将当前块标记的命令(标签名)和标记添加到命令栈 command_stack 中
self.command_stack.append((command, token))
try:
compile_func = self.tags[command]
except KeyError:
# 如果未找到标签处理函数,说明遇到了未知的块标记。通过 self.invalid_block_tag() 方法抛出相应的异常。
self.invalid_block_tag(token, command, parse_until)
try:
# 用获取到的标签处理函数 compile_func 编译块标记,得到编译结果 compiled_result。
compiled_result = compile_func(self, token)
except Exception as e:
raise self.error(token, e)
self.extend_nodelist(nodelist, compiled_result, token)
self.command_stack.pop()
# 如果在解析结束时仍存在未匹配的块标记,通过 self.unclosed_block_tag(parse_until) 抛出相应的异常。
if parse_until:
self.unclosed_block_tag(parse_until)
return nodelist
5.Variable类、FilterExpression类和VariableNode类源码解读
-
在Django框架中,
Variable 类
通常用于在模板中表示变量。模板系统是Django中用于生成动态HTML内容的一部分,它允许在模板中使用变量、表达式和控制结构。源码定义位于django/template/base.py
文件中。from django.template.base import Variable class Variable: def resolve(self, context): """ 解析变量并返回其值 参数: - context: 包含模板上下文信息的字典 返回: - 解析后的变量值 """ # 在给定的上下文中解析变量并返回其值 return context.get(self.var, '') #使用示例 >>> c = {'article': {'section':'News'}} >>> Variable('article.section').resolve(c) 'News' >>> Variable('article').resolve(c) {'section': 'News'} >>> class AClass: pass >>> c = AClass() >>> c.article = AClass() >>> c.article.section = 'News'
-
在Django框架中,
FilterExpression 类
用于表示模板中的过滤器表达式。过滤器允许你在模板中对变量进行转换或格式化。源码定义位于django/template/base.py
文件中。# 从 django.template.base 模块导入 FilterExpression 类 from django.template.base import FilterExpression class FilterExpression: def __init__(self, token, parser): """ 构造函数 参数: - token: 包含过滤器表达式的 Token 对象 - parser: 当前模板的解析器对象 """ self.token = token self.parser = parser self.var = parser.compile_filter(token.contents) self.filters = [] def add(self, filter_func, args, kwargs): """ 添加过滤器 参数: - filter_func: 过滤器函数 - args: 过滤器的位置参数 - kwargs: 过滤器的关键字参数 """ self.filters.append((filter_func, args, kwargs)) def resolve(self, context, ignore_failures=False): """ 解析过滤器表达式并返回结果 参数: - context: 包含模板上下文信息的字典 - ignore_failures: 是否忽略解析过程中的错误 返回: - 解析后的结果 """ value = self.var.resolve(context, ignore_failures) # 依次应用过滤器 for filter_func, args, kwargs in self.filters: args = [arg.resolve(context, True) for arg in args] kwargs = {key: value.resolve(context, True) for key, value in kwargs.items()} value = filter_func(value, *args, **kwargs) return value # 运行示例 >>> token = 'variable|default:"Default value"|date:"Y-m-d"' >>> p = Parser('') >>> fe = FilterExpression(token, p) >>> len(fe.filters) 2 >>> fe.var <Variable: 'variable'> ``
6.do_if()函数和do_for()函数的处理逻辑解读
-
do_if()
函数在解析{% if ... %}
标签时被调用,它的工作是处理标签的内容并返回相应的节点。这个节点将被添加到模板的节点列表中,最终构建出表示整个模板结构的树状结构。源码定义位于django/template/defaulttags.py
文件中。 -
do_if()
函数在哪里被调用?
使用@register.tag('if')
装饰器将 do_if 函数注册为一个名为 ‘if’ 的自定义标签。在 Parser 类中,当解析器遇到一个标签时,它会尝试找到该标签对应的处理函数。class Parser: def parse(self, parse_until=None): # 省略... elif token.token_type.value == 2: # TokenType.BLOCK 如 {% if %} xxxx {% endif %} # 找到tags标签对应的处理函数,即如果command为if,则为do_if()函数 compile_func = self.tags[command] # 省略... # 执行do_if()函数 compiled_result = compile_func(self, token) # 将 do_if 函数注册为一个名为 'if' 的自定义标签处理函数。 @register.tag('if') def do_if(parser, token): # {% if ... %} # 提取出标签中除了 {% if 之外的部分,即条件表达式和其它参数。 bits = token.split_contents()[1:] # 解析条件表达式,该表达式可以包含多个变量、比较操作符和逻辑运算符。 condition = TemplateIfParser(parser, bits).parse() # 解析标签内的内容块,包括可能的 elif、else 和 endif。 nodelist = parser.parse(('elif', 'else', 'endif')) # 将解析得到的条件表达式和内容块以元组的形式添加到 conditions_nodelists 列表中。 conditions_nodelists = [(condition, nodelist)] # 获取下一个标签,并检查它是否是 elif、else 或 endif。 token = parser.next_token() # 如果是 elif,重复步骤上述步骤 # {% elif ... %} (repeatable) while token.contents.startswith('elif'): bits = token.split_contents()[1:] condition = TemplateIfParser(parser, bits).parse() nodelist = parser.parse(('elif', 'else', 'endif')) conditions_nodelists.append((condition, nodelist)) token = parser.next_token() # 如果是 else,解析 else 块。 # {% else %} (optional) if token.contents == 'else': nodelist = parser.parse(('endif',)) conditions_nodelists.append((None, nodelist)) token = parser.next_token() # 如果是 endif,结束循环。 # {% endif %} if token.contents != 'endif': raise TemplateSyntaxError('Malformed template tag at line {}: "{}"'.format(token.lineno, token.contents)) # 创建并返回一个 IfNode 对象,该对象包含了所有条件表达式和对应的内容块。 return IfNode(conditions_nodelists)
-
do_for()
函数的作用是解析 {% for … %} 标签及其内容,构建出一个带有循环条件、循环变量等信息的数据结构(ForNode 对象),该数据结构在模板渲染时会用于执行相应的循环逻辑。这使得在模板中可以使用循环语句,遍历序列中的每个元素。源码位于django/template/defaulttags.py
文件中。@register.tag('for') def do_for(parser, token): # 将标签内容拆分成一个列表,以便进一步解析。 bits = token.split_contents() is_reversed = False # 检查是否包含一些不允许在 for 标签中使用的关键字('reversed', 'as', 'with', 'if', 'else')。 for invalid in ('reversed', 'as', 'with', 'if', 'else'): if invalid in bits: raise TemplateSyntaxError("'%s' is not allowed in 'for' tag" % invalid) # 确保标签的格式符合 {% for <variable> in <sequence> %} 或 {% for <variable> in <sequence> reversed %} 的形式 if len(bits) < 4 or len(bits) > 5: raise TemplateSyntaxError("'for' statements should have at least four " "words: %s" % ' '.join(bits)) # 解析序列(sequence)和循环变量(loopvars) sequence = parser.compile_filter(bits[2]) parser.next_token() loopvars = [] while parser.tokens[0].contents != 'for': # 这里解析循环变量 loopvars.append(parser.tokens.pop(0)) # 判断是否有reversed关键字 if loopvars[-1].contents == 'reversed': loopvars.pop() is_reversed = True # 确保至少有一个循环变量 if not loopvars: raise TemplateSyntaxError("'for' statement requires at least one loop variable") # 存储循环变量的名字 unpack = None if loopvars[-1].contents == 'as': loopvars.pop() # 解析被循环的元素 if parser.tokens[0].contents == 'var': parser.tokens.pop(0) unpack = parser.tokens.pop(0).contents else: raise TemplateSyntaxError("'%s' is not a valid name for a sequence of values" % parser.tokens[0].contents) nodelist_loop = parser.parse(('empty', 'endfor')) token = parser.next_token() # Check for empty if token.contents == 'empty': # 解析空块 nodelist_empty = parser.parse(('endfor',)) parser.delete_first_token() else: nodelist_empty = None return ForNode( vars=loopvars, sequence=sequence, is_reversed=is_reversed, unpack=unpack, nodelist_loop=nodelist_loop, nodelist_empty=nodelist_empty, )
7.IfNode和ForNode的渲染追踪
-
IfNode
是用于处理 {% if %} {% elif %} {% else %} {% endif %} 这类条件语句的节点。源码位于django/template/defaulttags.py
文件中。from django.template import Node, NodeList, Variable, VariableDoesNotExist class IfNode(Node): def __init__(self, conditions, nodelist_true, nodelist_false=None): self.conditions = conditions self.nodelist_true = nodelist_true self.nodelist_false = nodelist_false def render(self, context): for condition, nodelist in self.conditions: if condition.eval(context): return nodelist.render(context) if self.nodelist_false: return self.nodelist_false.render(context) return '' class IfParser(Parser): def parse(self, parse_until=None): # 解析 if 语句的条件和内容 conditions = [] while self.tokens.current_token.type not in (TOKEN_BLOCK, TOKEN_VARIABLE): # 解析条件 self.tokens.next_token() condition = self.parse_expression() self.tokens.expect(TOKEN_BLOCK, 'endif') # 解析条件成立时的内容 nodelist_true = self.parse(parse_until) # 添加条件和对应内容到列表 conditions.append((condition, nodelist_true)) # 检查是否有 elif,如果有继续解析 if self.tokens.current_token.type == TOKEN_BLOCK and self.tokens.current_token.contents == 'elif': continue # 如果没有 elif,则检查是否有 else elif self.tokens.current_token.type == TOKEN_BLOCK and self.tokens.current_token.contents == 'else': # 解析条件不成立时的内容 self.tokens.next_token() nodelist_false = self.parse(parse_until) # 返回 IfNode 实例 return IfNode(conditions, nodelist_true, nodelist_false) # 如果没有 elif 和 else,则返回 IfNode 实例 else: return IfNode(conditions, nodelist_true)
-
IfNode
是用于处理 {% if %} {% elif %} {% else %} {% endif %} 这类条件语句的节点。源码位于django/template/defaulttags.py
文件中。class ForNode(Node): def __init__(self, loopvars, sequence, is_reversed, nodelist_loop): self.loopvars = loopvars self.sequence = sequence self.is_reversed = is_reversed self.nodelist_loop = nodelist_loop def render(self, context): # 获取循环的序列 sequence = self.sequence.resolve(context, True) # 处理反向循环 if self.is_reversed: sequence = reversed(sequence) # 循环处理 values = [] for item in sequence: # 将循环变量加入上下文 context.push() for i, var in enumerate(self.loopvars): context[var] = item[i] # 渲染循环体 values.append(self.nodelist_loop.render(context)) context.pop() return ''.join(values) class ForParser(Parser): def parse(self, parse_until=None): # 解析 for 语句的循环变量和序列 self.tokens.expect(TOKEN_BLOCK, 'for') loopvars = self.parse_assignments() self.tokens.expect(TOKEN_BLOCK, 'in') sequence = self.parse_expression() # 解析循环体 self.tokens.expect(TOKEN_BLOCK, 'endfor') nodelist_loop = self.parse(parse_until) # 返回 ForNode 实例 return ForNode(loopvars, sequence, False, nodelist_loop)