1 The Problem
We want a URL shortener: give it a long link, it returns a short code; give back the code, it returns the original link. It teaches two-way lookups (code↔URL), generating unique keys, and persisting a small store — the core of any link service.
2 How to Think About It
Think about the mapping, before any code:
3 The Build — explained part by part
Here is the complete shortener. Each part is explained below.
import json
import os
import secrets
import string
FILE = "links.json"
def load():
if os.path.exists(FILE):
with open(FILE) as f:
return json.load(f)
return {}
def save(links):
with open(FILE, "w") as f:
json.dump(links, f)
def make_code(length=6):
# A random short code from letters and digits.
chars = string.ascii_letters + string.digits
return "".join(secrets.choice(chars) for _ in range(length))
def shorten(links, url):
code = make_code()
while code in links: # avoid a clash with an existing code
code = make_code()
links[code] = url
save(links)
return code
def expand(links, code):
return links.get(code) # None if the code is unknown
if __name__ == "__main__":
links = load()
code = shorten(links, "https://example.com/a/very/long/link")
print(f"Short code: {code}")
print(f"Expands to: {expand(links, code)}")
make_code(length=6) — build a random 6-character code from letters and digits using
secrets (secure, unguessable).while code in links: code = make_code() — the safety step: if our random code happens to already exist, generate another. This guarantees every code is unique.
links[code] = url — store the mapping, then
save it to the file.links.get(code) — look up a code. Using
.get returns None for an unknown code instead of crashing, so we can handle “not found” gracefully.
while code in links until you get a fresh one.links[code] to expand — an unknown code crashes with KeyError.links.get(code) so missing codes return None.random instead of secrets — predictable codes can be guessed.secrets.choice for unguessable codes.4 Test & Prove Each Part
We test the round-trip (shorten then expand) and that codes are unique and the right length.
import secrets
import string
def make_code(length=6):
chars = string.ascii_letters + string.digits
return "".join(secrets.choice(chars) for _ in range(length))
def shorten(links, url):
code = make_code()
while code in links:
code = make_code()
links[code] = url
return code
def expand(links, code):
return links.get(code)
def test_round_trip():
"""Shorten then expand returns the original URL."""
links = {}
code = shorten(links, "https://example.com")
assert expand(links, code) == "https://example.com"
def test_code_length():
"""Codes are 6 characters by default."""
assert len(make_code()) == 6
def test_unknown_code():
"""An unknown code returns None."""
assert expand({}, "missing") is None
Run with pytest -v. The round-trip test is the most important: it proves the two halves (shorten and expand) fit together. Testing a there-and-back journey is a powerful way to verify a mapping.
5 The Interface
What it expects
shorten("https://example.com/very/long")What it returns
Short code: aB3xK9
Expands to: https://example.com/very/long6 Run It & Automate It
python3 shortener.pyShorten a link and expand it back. Links are saved in
links.json.Jenkins runs the tests automatically — each line explained below.
Short code: aB3xK9
Expands to: https://example.com/a/very/long/linkKeyError when expanding.links.get(code) instead of links[code].save(links) after shortening.// 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.' }
}
}
- Custom codes. Let users pick their own code if it is free. (Teaches: checking availability.)
- Click counts. Track how many times each code is expanded. (Teaches: richer values.)
- Make it a web service. Combine with the Simple Web Server project. (Teaches: connecting projects.)