Exercise 1: Adding an IfNode — Possible Solution ==================================================================== THE NEW NODE CLASS ------------------------------ class IfNode: def __init__(self, cond_name, body_nodes): self.cond_name = cond_name self.body_nodes = body_nodes def render(self, ctx): if ctx.get(self.cond_name): return ''.join(node.render(ctx) for node in self.body_nodes) return '' This follows the chapter's own ForNode pattern exactly: it holds the name of the thing it depends on (a condition name instead of a collection name), plus its own already-parsed body_nodes. Nothing about the body needs re-parsing at render time -- that work already happened once, at compile time, exactly like ForNode's own body. WIRING IT INTO THE COMPILER ------------------------------ def compile_if(template): m = re.search(r'\{%\s*if\s+(\w+)\s*%\}(.*?)\{%\s*endif\s*%\}', template, re.S) nodes = [] if m: cond_name, body = m.groups() before, after = template[:m.start()], template[m.end():] nodes.extend(parse_to_nodes(before)) nodes.append(IfNode(cond_name, parse_to_nodes(body))) nodes.extend(parse_to_nodes(after)) return nodes return parse_to_nodes(template) TESTING IT ------------------------------ if_template = "
{% if logged_in %}Welcome back, {{ user }}.{% endif %}
" tree = compile_if(if_template) # parsed ONCE print(render_nodes(tree, {"logged_in": True, "user": "Sam"})) print(render_nodes(tree, {"logged_in": False, "user": "Sam"})) print(render_nodes(tree, {"user": "Sam"})) # no "logged_in" key at all RESULTS ------------------------------ Truthy flag:Welcome back, Sam.
Falsy flag: Missing flag: The truthy case renders the body in full, with its own VarNode correctly substituting "Sam". Both the explicit False case and the case where "logged_in" isn't in the context at all render an empty body -- ctx.get(self.cond_name) returns None for a missing key, and None is just as falsy as False, so no special "key doesn't exist" handling was needed. WHY THIS WORKS AS AN ANSWER ---------------------------- It adds a real node class following the chapter's own established Node-with-a-render-method shape, wires it into the same one-time compile step ForNode already uses (no re-parsing the body at render time), and verifies all three real cases a boolean flag can actually take -- true, explicitly false, and simply absent -- rather than only checking the happy path.