Exercise 3: A Third Block & Duplicate Block Names — Possible Solution
====================================================================
ADDING THE SIDEBAR BLOCK TO BASE
------------------------------
BASE = '''
{% block title %}Default Title{% endblock %}
{% block content %}Default content{% endblock %}
'''
CHILD = '''{% extends "base" %}
{% block content %}Only content overridden.{% endblock %}'''
print(render_inheritance(CHILD, {"base": BASE}))
RESULT
------------------------------
Default Title
Only content overridden.
CHILD never defines a "sidebar" block at all, so extract_blocks(CHILD)
never adds a "sidebar" key to child_blocks. The replace_block()
substitution's own fallback -- child_blocks.get(m.group(1), m.group(2))
-- returns m.group(2), the parent's own captured default body, for
any block name not found in that dict. "sidebar" falls back exactly
the same way "title" already does in the chapter's own example: this
isn't special-cased anywhere, it's the same one fallback rule applying
a third time.
WHY A DUPLICATE BLOCK NAME WOULD SILENTLY DISCARD ONE
----------------------------------------------------------
extract_blocks() builds its result as a plain dict, keyed by block
name:
def extract_blocks(template):
blocks = {}
for m in re.finditer(r'\{%\s*block\s+(\w+)\s*%\}(.*?)\{%\s*endblock\s*%\}', template, re.S):
blocks[m.group(1)] = m.group(2)
return blocks
A dict can only ever hold one value per key. If a child template
defined two separate {% block content %}...{% endblock %} pairs, the
loop above would run twice for the name "content" -- and the second
assignment, blocks['content'] = ..., simply overwrites the first.
Nothing raises an error and nothing warns that a block was discarded;
the dict has no memory that a first value was ever there at all.
VERIFIED DIRECTLY
------------------------------
DUPLICATE_CHILD = '''{% extends "base" %}
{% block content %}First content block.{% endblock %}
{% block content %}Second content block.{% endblock %}'''
print(extract_blocks(DUPLICATE_CHILD))
# {'content': 'Second content block.'}
Only "Second content block." survives -- the first block's own text
is gone entirely, with no trace it ever existed, purely because it
lost a same-key dict write to whichever block happened to be parsed
second in source order.
WHY THIS WORKS AS AN ANSWER
----------------------------
It extends the chapter's own BASE template using the exact same
{% block name %}...{% endblock %} pattern already established, verifies
the real fallback behavior with a genuine printed result rather than
just asserting it, and traces the duplicate-block-name question back
to the concrete dict-overwrite mechanism inside extract_blocks() --
with a real, run test confirming which of the two blocks actually
survives and why.