make_function3.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. # Copyright (c) 2015-2021, 2024 by Rocky Bernstein
  2. #
  3. # This program is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. """
  16. All the crazy things we have to do to handle Python functions in 3.0-3.5 or so.
  17. The saga of changes before and after is in other files.
  18. """
  19. from xdis import CO_GENERATOR, code_has_star_arg, code_has_star_star_arg, iscode
  20. from uncompyle6.parser import ParserError as ParserError2
  21. from uncompyle6.parsers.treenode import SyntaxTree
  22. from uncompyle6.scanner import Code
  23. from uncompyle6.semantics.helper import (
  24. find_all_globals,
  25. find_globals_and_nonlocals,
  26. find_none,
  27. print_docstring,
  28. )
  29. from uncompyle6.semantics.parser_error import ParserError
  30. from uncompyle6.show import maybe_show_tree_param_default
  31. # FIXME: DRY the below code...
  32. def make_function3_annotate(
  33. self, node, is_lambda, nested=1, code_node=None, annotate_last=-1
  34. ):
  35. """
  36. Dump function definition, doc string, and function
  37. body. This code is specialized for Python 3"""
  38. def build_param(ast, name, default):
  39. """build parameters:
  40. - handle defaults
  41. - handle format tuple parameters
  42. """
  43. if default:
  44. value = self.traverse(default, indent="")
  45. maybe_show_tree_param_default(self, name, value)
  46. result = "%s=%s" % (name, value)
  47. if result[-2:] == "= ": # default was 'LOAD_CONST None'
  48. result += "None"
  49. return result
  50. else:
  51. return name
  52. # MAKE_FUNCTION_... or MAKE_CLOSURE_...
  53. assert node[-1].kind.startswith("MAKE_")
  54. annotate_tuple = None
  55. for annotate_last in range(len(node) - 1, -1, -1):
  56. if node[annotate_last] == "annotate_tuple":
  57. annotate_tuple = node[annotate_last]
  58. break
  59. annotate_args = {}
  60. if (
  61. annotate_tuple == "annotate_tuple"
  62. and annotate_tuple[0] in ("LOAD_CONST", "LOAD_NAME")
  63. and isinstance(annotate_tuple[0].attr, tuple)
  64. ):
  65. annotate_tup = annotate_tuple[0].attr
  66. i = -1
  67. j = annotate_last - 1
  68. l = -len(node)
  69. while j >= l and node[j].kind in ("annotate_arg", "annotate_tuple"):
  70. annotate_args[annotate_tup[i]] = node[j][0]
  71. i -= 1
  72. j -= 1
  73. args_node = node[-1]
  74. if isinstance(args_node.attr, tuple):
  75. # positional args are before kwargs
  76. defparams = node[: args_node.attr[0]]
  77. pos_args, kw_args, annotate_argc = args_node.attr
  78. else:
  79. defparams = node[: args_node.attr]
  80. kw_args = 0
  81. pass
  82. annotate_dict = {}
  83. for name in annotate_args.keys():
  84. n = self.traverse(annotate_args[name], indent="")
  85. annotate_dict[name] = n
  86. if (3, 0) <= self.version < (3, 3):
  87. lambda_index = -2
  88. elif self.version < (3, 4):
  89. lambda_index = -3
  90. else:
  91. lambda_index = None
  92. if lambda_index and is_lambda and iscode(node[lambda_index].attr):
  93. assert node[lambda_index].kind == "LOAD_LAMBDA"
  94. code = node[lambda_index].attr
  95. else:
  96. code = code_node.attr
  97. assert iscode(code)
  98. code = Code(code, self.scanner, self.currentclass)
  99. # add defaults values to parameter names
  100. argc = code.co_argcount
  101. kwonlyargcount = code.co_kwonlyargcount
  102. paramnames = list(code.co_varnames[:argc])
  103. if kwonlyargcount > 0:
  104. kwargs = list(code.co_varnames[argc : argc + kwonlyargcount])
  105. try:
  106. ast = self.build_ast(
  107. code._tokens,
  108. code._customize,
  109. code,
  110. is_lambda=is_lambda,
  111. noneInNames=("None" in code.co_names),
  112. )
  113. except (ParserError, ParserError2) as p:
  114. self.write(str(p))
  115. if not self.tolerate_errors:
  116. self.ERROR = p
  117. return
  118. indent = self.indent
  119. if is_lambda:
  120. self.write("lambda ")
  121. else:
  122. self.write("(")
  123. last_line = self.f.getvalue().split("\n")[-1]
  124. l = len(last_line)
  125. indent = " " * l
  126. line_number = self.line_number
  127. i = len(paramnames) - len(defparams)
  128. suffix = ""
  129. for param in paramnames[:i]:
  130. self.write(suffix, param)
  131. suffix = ", "
  132. if param in annotate_dict:
  133. self.write(": %s" % annotate_dict[param])
  134. if line_number != self.line_number:
  135. suffix = ",\n" + indent
  136. line_number = self.line_number
  137. # value, string = annotate_args[param]
  138. # if string:
  139. # self.write(': "%s"' % value)
  140. # else:
  141. # self.write(': %s' % value)
  142. suffix = ", " if i > 0 else ""
  143. for n in node:
  144. if n == "pos_arg":
  145. self.write(suffix)
  146. param = paramnames[i]
  147. self.write(param)
  148. if param in annotate_args:
  149. aa = annotate_args[param]
  150. if isinstance(aa, tuple):
  151. aa = aa[0]
  152. self.write(': "%s"' % aa)
  153. elif isinstance(aa, SyntaxTree):
  154. self.write(": ")
  155. self.preorder(aa)
  156. self.write("=")
  157. i += 1
  158. self.preorder(n)
  159. if line_number != self.line_number:
  160. suffix = ",\n" + indent
  161. line_number = self.line_number
  162. else:
  163. suffix = ", "
  164. if code_has_star_arg(code):
  165. star_arg = code.co_varnames[argc + kwonlyargcount]
  166. if annotate_dict and star_arg in annotate_dict:
  167. self.write(suffix, "*%s: %s" % (star_arg, annotate_dict[star_arg]))
  168. else:
  169. self.write(suffix, "*%s" % star_arg)
  170. argc += 1
  171. # self.println(indent, '#flags:\t', int(code.co_flags))
  172. ends_in_comma = False
  173. if kwonlyargcount > 0:
  174. if not code_has_star_arg(code):
  175. if argc > 0:
  176. self.write(", *, ")
  177. else:
  178. self.write("*, ")
  179. pass
  180. ends_in_comma = True
  181. else:
  182. if argc > 0:
  183. self.write(", ")
  184. ends_in_comma = True
  185. kw_args = [None] * kwonlyargcount
  186. for n in node:
  187. if n == "kwargs":
  188. n = n[0]
  189. if n == "kwarg":
  190. name = eval(n[0].pattr)
  191. idx = kwargs.index(name)
  192. default = self.traverse(n[1], indent="")
  193. if annotate_dict and name in annotate_dict:
  194. kw_args[idx] = "%s: %s=%s" % (name, annotate_dict[name], default)
  195. else:
  196. kw_args[idx] = "%s=%s" % (name, default)
  197. pass
  198. pass
  199. # handling other args
  200. other_kw = [c == None for c in kw_args]
  201. for i, flag in enumerate(other_kw):
  202. if flag:
  203. n = kwargs[i]
  204. if n in annotate_dict:
  205. kw_args[i] = "%s: %s" % (n, annotate_dict[n])
  206. else:
  207. kw_args[i] = "%s" % n
  208. self.write(", ".join(kw_args))
  209. ends_in_comma = False
  210. else:
  211. if argc == 0:
  212. ends_in_comma = True
  213. if code_has_star_star_arg(code):
  214. if not ends_in_comma:
  215. self.write(", ")
  216. star_star_arg = code.co_varnames[argc + kwonlyargcount]
  217. if annotate_dict and star_star_arg in annotate_dict:
  218. self.write("**%s: %s" % (star_star_arg, annotate_dict[star_star_arg]))
  219. else:
  220. self.write("**%s" % star_star_arg)
  221. if is_lambda:
  222. self.write(": ")
  223. else:
  224. self.write(")")
  225. if "return" in annotate_tuple[0].attr:
  226. if (line_number != self.line_number) and not no_paramnames:
  227. self.write("\n" + indent)
  228. line_number = self.line_number
  229. self.write(" -> ")
  230. if "return" in annotate_dict:
  231. self.write(annotate_dict["return"])
  232. else:
  233. # value, string = annotate_args['return']
  234. # if string:
  235. # self.write(' -> "%s"' % value)
  236. # else:
  237. # self.write(' -> %s' % value)
  238. self.preorder(node[annotate_last - 1])
  239. self.println(":")
  240. if (
  241. len(code.co_consts) > 0 and code.co_consts[0] is not None and not is_lambda
  242. ): # ugly
  243. # docstring exists, dump it
  244. print_docstring(self, self.indent, code.co_consts[0])
  245. code._tokens = None # save memory
  246. assert ast == "stmts"
  247. all_globals = find_all_globals(ast, set())
  248. globals, nonlocals = find_globals_and_nonlocals(
  249. ast, set(), set(), code, self.version
  250. )
  251. for g in sorted((all_globals & self.mod_globs) | globals):
  252. self.println(self.indent, "global ", g)
  253. for nl in sorted(nonlocals):
  254. self.println(self.indent, "nonlocal ", nl)
  255. self.mod_globs -= all_globals
  256. has_none = "None" in code.co_names
  257. rn = has_none and not find_none(ast)
  258. self.gen_source(
  259. ast, code.co_name, code._customize, is_lambda=is_lambda, returnNone=rn
  260. )
  261. code._tokens = code._customize = None # save memory
  262. def make_function3(self, node, is_lambda, nested=1, code_node=None):
  263. """Dump function definition, doc string, and function body in
  264. Python version 3.0 and above
  265. """
  266. # For Python 3.3, the evaluation stack in MAKE_FUNCTION is:
  267. # * default argument objects in positional order
  268. # * pairs of name and default argument, with the name just below
  269. # the object on the stack, for keyword-only parameters
  270. # * parameter annotation objects
  271. # * a tuple listing the parameter names for the annotations
  272. # (only if there are only annotation objects)
  273. # * the code associated with the function (at TOS1)
  274. # * the qualified name of the function (at TOS)
  275. # For Python 3.0 .. 3.2 the evaluation stack is:
  276. # The function object is defined to have argc default parameters,
  277. # which are found below TOS.
  278. # * first come positional args in the order they are given in the source,
  279. # * next come the keyword args in the order they given in the source,
  280. # * finally is the code associated with the function (at TOS)
  281. #
  282. # Note: There is no qualified name at TOS
  283. # MAKE_CLOSURE adds an additional closure slot
  284. # In Python 3.6 stack entries change again. I understand
  285. # 3.7 changes some of those changes. Yes, it is hard to follow
  286. # and I am sure I haven't been able to keep up.
  287. # Thank you, Python.
  288. def build_param(ast, name, default, annotation=None):
  289. """build parameters:
  290. - handle defaults
  291. - handle format tuple parameters
  292. """
  293. value = self.traverse(default, indent="")
  294. if annotation:
  295. result = "%s: %s=%s" % (name, annotation, value)
  296. else:
  297. result = "%s=%s" % (name, value)
  298. # The below can probably be removed. This is probably
  299. # a holdover from days when LOAD_CONST erroneously
  300. # didn't handle LOAD_CONST None properly
  301. if result[-2:] == "= ": # default was 'LOAD_CONST None'
  302. result += "None"
  303. return result
  304. # MAKE_FUNCTION_... or MAKE_CLOSURE_...
  305. assert node[-1].kind.startswith("MAKE_")
  306. # Python 3.3+ adds a qualified name at TOS (-1)
  307. # moving down the LOAD_LAMBDA instruction
  308. if (3, 0) <= self.version <= (3, 2):
  309. lambda_index = -2
  310. elif (3, 3) <= self.version:
  311. lambda_index = -3
  312. else:
  313. lambda_index = None
  314. args_node = node[-1]
  315. annotate_dict = {}
  316. # Get a list of tree nodes that constitute the values for the "default
  317. # parameters"; these are default values that appear before any *, and are
  318. # not to be confused with keyword parameters which may appear after *.
  319. args_attr = args_node.attr
  320. if isinstance(args_attr, tuple):
  321. if len(args_attr) == 3:
  322. pos_args, kw_args, annotate_argc = args_attr
  323. else:
  324. pos_args, kw_args, annotate_argc, closure = args_attr
  325. i = -4
  326. kw_pairs = 0
  327. if closure:
  328. # FIXME: fill in
  329. i -= 1
  330. if annotate_argc:
  331. # Turn into subroutine and DRY with other use
  332. annotate_node = node[i]
  333. if annotate_node == "expr":
  334. annotate_node = annotate_node[0]
  335. annotate_name_node = annotate_node[-1]
  336. if annotate_node == "dict" and annotate_name_node.kind.startswith(
  337. "BUILD_CONST_KEY_MAP"
  338. ):
  339. types = [
  340. self.traverse(n, indent="") for n in annotate_node[:-2]
  341. ]
  342. names = annotate_node[-2].attr
  343. l = len(types)
  344. assert l == len(names)
  345. for i in range(l):
  346. annotate_dict[names[i]] = types[i]
  347. pass
  348. pass
  349. i -= 1
  350. if kw_args:
  351. kw_node = node[i]
  352. if kw_node == "expr":
  353. kw_node = kw_node[0]
  354. if kw_node == "dict":
  355. kw_pairs = kw_node[-1].attr
  356. # FIXME: there is probably a better way to classify this.
  357. have_kwargs = node[0].kind.startswith("kwarg") or node[0] == "no_kwargs"
  358. if len(node) >= 4:
  359. lc_index = -4
  360. else:
  361. lc_index = -3
  362. pass
  363. if len(node) > 2 and (have_kwargs or node[lc_index].kind != "load_closure"):
  364. # Find the index in "node" where the first default
  365. # parameter value is located. Note this is in contrast to
  366. # key-word arguments, pairs of (name, value), which appear after "*".
  367. # "default_values_start" is this location.
  368. default_values_start = 0
  369. if node[0] == "no_kwargs":
  370. default_values_start += 1
  371. # If in a lambda named args are a sequence of kwarg, not bundled.
  372. # If not in a lambda, named args are after kwargs; kwargs are bundled as one node.
  373. if node[default_values_start] == "kwarg":
  374. assert node[lambda_index] == "LOAD_LAMBDA"
  375. i = default_values_start
  376. defparams = []
  377. while node[i] == "kwarg":
  378. defparams.append(node[i][1])
  379. i += 1
  380. else:
  381. if node[default_values_start] == "kwargs":
  382. default_values_start += 1
  383. defparams = node[
  384. default_values_start : default_values_start + args_node.attr[0]
  385. ]
  386. else:
  387. defparams = node[: args_node.attr[0]]
  388. kw_args = 0
  389. else:
  390. defparams = node[: args_node.attr]
  391. kw_args = 0
  392. pass
  393. if lambda_index and is_lambda and iscode(node[lambda_index].attr):
  394. assert node[lambda_index].kind == "LOAD_LAMBDA"
  395. code = node[lambda_index].attr
  396. else:
  397. code = code_node.attr
  398. assert iscode(code)
  399. scanner_code = Code(code, self.scanner, self.currentclass)
  400. # add defaults values to parameter names
  401. argc = code.co_argcount
  402. kwonlyargcount = code.co_kwonlyargcount
  403. paramnames = list(scanner_code.co_varnames[:argc])
  404. if kwonlyargcount > 0:
  405. if is_lambda:
  406. kwargs = []
  407. for i in range(kwonlyargcount):
  408. paramnames.append(scanner_code.co_varnames[argc + i])
  409. pass
  410. else:
  411. kwargs = list(scanner_code.co_varnames[argc : argc + kwonlyargcount])
  412. # defaults are for last n parameters when not in a lambda, thus reverse
  413. paramnames.reverse()
  414. defparams.reverse()
  415. try:
  416. ast = self.build_ast(
  417. scanner_code._tokens,
  418. scanner_code._customize,
  419. scanner_code,
  420. is_lambda=is_lambda,
  421. noneInNames=("None" in code.co_names),
  422. )
  423. except (ParserError, ParserError2) as p:
  424. self.write(str(p))
  425. if not self.tolerate_errors:
  426. self.ERROR = p
  427. return
  428. i = len(paramnames) - len(defparams)
  429. # build parameters
  430. params = []
  431. if defparams:
  432. for i, defparam in enumerate(defparams):
  433. params.append(
  434. build_param(
  435. ast, paramnames[i], defparam, annotate_dict.get(paramnames[i])
  436. )
  437. )
  438. for param in paramnames[i + 1 :]:
  439. if param in annotate_dict:
  440. params.append("%s: %s" % (param, annotate_dict[param]))
  441. else:
  442. params.append(param)
  443. else:
  444. for param in paramnames:
  445. if param in annotate_dict:
  446. params.append("%s: %s" % (param, annotate_dict[param]))
  447. else:
  448. params.append(param)
  449. params.reverse() # back to correct order
  450. if code_has_star_arg(code):
  451. star_arg = code.co_varnames[argc + kwonlyargcount]
  452. if annotate_dict and star_arg in annotate_dict:
  453. params.append("*%s: %s" % (star_arg, annotate_dict[star_arg]))
  454. else:
  455. params.append("*%s" % star_arg)
  456. pass
  457. if is_lambda:
  458. params.reverse()
  459. if not is_lambda:
  460. argc += 1
  461. pass
  462. elif is_lambda and kwonlyargcount > 0:
  463. params.insert(0, "*")
  464. kwonlyargcount = 0
  465. # dump parameter list (with default values)
  466. if is_lambda:
  467. self.write("lambda ", ", ".join(params))
  468. # If the last statement is None (which is the
  469. # same thing as "return None" in a lambda) and the
  470. # next to last statement is a "yield". Then we want to
  471. # drop the (return) None since that was just put there
  472. # to have something to after the yield finishes.
  473. # FIXME: this is a bit hoaky and not general
  474. if (
  475. len(ast) > 1
  476. and self.traverse(ast[-1]) == "None"
  477. and self.traverse(ast[-2]).strip().startswith("yield")
  478. ):
  479. del ast[-1]
  480. # Now pick out the expr part of the last statement
  481. ast_expr = ast[-1]
  482. while ast_expr.kind != "expr":
  483. ast_expr = ast_expr[0]
  484. ast[-1] = ast_expr
  485. pass
  486. else:
  487. # FIXME: add annotations here
  488. self.write("(", ", ".join(params))
  489. # self.println(indent, '#flags:\t', int(code.co_flags))
  490. # FIXME: Could we remove ends_in_comma and its tests if we just
  491. # created a parameter list and at the very end did a join on that?
  492. # Unless careful, We might lose line breaks though.
  493. ends_in_comma = False
  494. if kwonlyargcount > 0:
  495. if not (4 & code.co_flags):
  496. if argc > 0:
  497. self.write(", *, ")
  498. else:
  499. self.write("*, ")
  500. pass
  501. ends_in_comma = True
  502. else:
  503. if argc > 0 and node[0] != "kwarg":
  504. self.write(", ")
  505. ends_in_comma = True
  506. kw_args = [None] * kwonlyargcount
  507. if self.version <= (3, 3):
  508. kw_nodes = node[0]
  509. else:
  510. kw_nodes = node[args_node.attr[0]]
  511. if kw_nodes == "kwargs":
  512. for n in kw_nodes:
  513. name = eval(n[0].pattr)
  514. default = self.traverse(n[1], indent="")
  515. idx = kwargs.index(name)
  516. kw_args[idx] = "%s=%s" % (name, default)
  517. pass
  518. pass
  519. # FIXME: something weird is going on and the below
  520. # might not be right. On 3.4 kw_nodes != "kwarg"
  521. # because of some sort of type mismatch. I think
  522. # the test is for versions earlier than 3.3
  523. # on 3.5 if we have "kwarg" we still want to do this.
  524. # Perhaps we should be testing that kw_nodes is iterable?
  525. if kw_nodes != "kwarg" or self.version == 3.5:
  526. other_kw = [c == None for c in kw_args]
  527. for i, flag in enumerate(other_kw):
  528. if flag:
  529. if i < len(kwargs):
  530. kw_args[i] = "%s" % kwargs[i]
  531. else:
  532. del kw_args[i]
  533. pass
  534. self.write(", ".join(kw_args))
  535. ends_in_comma = False
  536. pass
  537. pass
  538. else:
  539. if argc == 0:
  540. ends_in_comma = True
  541. if code_has_star_star_arg(code):
  542. if not ends_in_comma:
  543. self.write(", ")
  544. star_star_arg = code.co_varnames[argc + kwonlyargcount]
  545. if annotate_dict and star_star_arg in annotate_dict:
  546. self.write("**%s: %s" % (star_star_arg, annotate_dict[star_star_arg]))
  547. else:
  548. self.write("**%s" % star_star_arg)
  549. if is_lambda:
  550. self.write(": ")
  551. else:
  552. self.write(")")
  553. if annotate_dict and "return" in annotate_dict:
  554. self.write(" -> %s" % annotate_dict["return"])
  555. self.println(":")
  556. if (
  557. len(code.co_consts) > 0 and code.co_consts[0] is not None and not is_lambda
  558. ): # ugly
  559. # docstring exists, dump it
  560. print_docstring(self, self.indent, code.co_consts[0])
  561. assert ast == "stmts"
  562. all_globals = find_all_globals(ast, set())
  563. globals, nonlocals = find_globals_and_nonlocals(
  564. ast, set(), set(), code, self.version
  565. )
  566. for g in sorted((all_globals & self.mod_globs) | globals):
  567. self.println(self.indent, "global ", g)
  568. for nl in sorted(nonlocals):
  569. self.println(self.indent, "nonlocal ", nl)
  570. self.mod_globs -= all_globals
  571. has_none = "None" in code.co_names
  572. rn = has_none and not find_none(ast)
  573. self.gen_source(
  574. ast,
  575. code.co_name,
  576. scanner_code._customize,
  577. is_lambda=is_lambda,
  578. returnNone=rn,
  579. debug_opts=self.debug_opts,
  580. )
  581. # In obscure cases, a function may be a generator but the "yield"
  582. # was optimized away. Here, we need to put in unreachable code to
  583. # add in "yield" just so that the compiler will mark
  584. # the GENERATOR bit of the function. See for example
  585. # Python 3.x's test_generator.py test program.
  586. if not is_lambda and code.co_flags & CO_GENERATOR:
  587. need_bogus_yield = True
  588. for token in scanner_code._tokens:
  589. if token in ("YIELD_VALUE", "YIELD_FROM"):
  590. need_bogus_yield = False
  591. break
  592. pass
  593. if need_bogus_yield:
  594. self.template_engine(("%|if False:\n%+%|yield None%-",), node)
  595. scanner_code._tokens = None # save memory
  596. scanner_code._customize = None # save memory