1 The Problem
We want a converter that reads Markdown (the simple text format with # headings and **bold**) and turns it into HTML a browser can show. It teaches parsing — reading text, recognising patterns, and transforming each piece — the skill behind every format converter and compiler.
2 How to Think About It
Think about how Markdown maps to HTML, before any code:
# is a heading; a blank line separates paragraphs; everything else is body text. → 3. Emit the matching HTML tag for each. → 4. Join it all into a finished page.
3 The Build — explained part by part
Here is the complete converter. Each part is explained below.
# Convert simple Markdown to HTML.
def convert_line(line):
line = line.rstrip()
if line.startswith("### "):
return f"<h3>{line[4:]}</h3>"
if line.startswith("## "):
return f"<h2>{line[3:]}</h2>"
if line.startswith("# "):
return f"<h1>{line[2:]}</h1>"
if line == "":
return ""
# Replace **bold** with <strong> tags.
while "**" in line:
line = line.replace("**", "<strong>", 1).replace("**", "</strong>", 1)
return f"<p>{line}</p>"
def convert(markdown_text):
lines = markdown_text.split("\n")
html_lines = [convert_line(line) for line in lines]
return "\n".join(html for html in html_lines if html)
if __name__ == "__main__":
sample = "# Hello\nThis is **bold** text.\n## A subheading"
print(convert(sample))
### before ## before # ) because ### also starts with #.line[4:] — slice off the first 4 characters (
“### ”) to keep just the heading text.the while "**" loop — turn each pair of
** into an opening then closing <strong> tag. Replacing one at a time, alternating, pairs them correctly.convert(markdown_text) — split the whole document into lines, convert each, and join the non-empty results into the final HTML.
if __name__ == "__main__": — a sample run so you can see it work immediately. This block only runs when the file is run directly, not when imported by the tests.
# before ### — then every heading becomes h1, because ### also starts with #."# " — then #tag in text is wrongly treated as a heading."# " with the trailing space, as real Markdown requires.line[1:] for "# " leaves a leading space.line[2:].4 Test & Prove Each Part
We test each Markdown pattern converts to the right HTML — headings, paragraphs, and bold.
from md2html import convert_line, convert
def test_h1():
"""# Title becomes an h1."""
assert convert_line("# Title") == "<h1>Title</h1>"
def test_h2():
"""## Sub becomes an h2."""
assert convert_line("## Sub") == "<h2>Sub</h2>"
def test_paragraph():
"""Plain text becomes a paragraph."""
assert convert_line("Hello there") == "<p>Hello there</p>"
def test_bold():
"""**bold** becomes a strong tag."""
assert convert_line("a **b** c") == "<p>a <strong>b</strong> c</p>"
Run with pytest -v. Because convert_line handles one line, each test checks one rule. When parsing, testing each pattern in isolation makes bugs easy to pin down.
5 The Interface
What it expects
# Hello
This is **bold** text.What it returns
<h1>Hello</h1>
<p>This is <strong>bold</strong> text.</p>6 Run It & Automate It
python3 md2html.pySee the sample convert. Then try feeding it a real
.md file.Jenkins runs the tests automatically — each line explained below.
<h1>Hello</h1>
<p>This is <strong>bold</strong> text.</p>
<h2>A subheading</h2>### before ## before #.** replacement is not pairing. Replace one at a time, alternating open/close.// Jenkinsfile — automated tests on every change.
pipeline {
agent any
stages {
stage('Get the code') { steps { checkout scm } } // download the code
stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } } // test tool
stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } } // run every test
}
post {
success { echo 'All tests passed.' }
failure { echo 'A test failed — look above.' }
}
}
- Italics. Convert
*text*to<em>. (Teaches: another pattern.) - Links. Turn
[text](url)into an anchor. (Teaches: regular expressions.) - Read and write files. Convert a
.mdfile to a.htmlfile. (Teaches: file I/O.)