Store and Resolve
You've got the two halves now: a dictionary store (Phase 1) and a base62 code generator (Phase 2). This phase joins them into the two functions a shortener lives or dies by - shorten() and resolve() - and deals with the messy real-world cases that a naive version gets wrong.
The two functions
shorten(long_url) takes a long URL, mints the next code, files the pair, and returns the code.
resolve(code) takes a code and returns the long URL it points to - or tells you it doesn't know that code, without crashing.
Here's the first honest version. Run it end to end:
=
=
return
=
, =
return
# state: the store, plus the counter that feeds the generator
=
= 0
global
=
=
+= 1
return
return # None instead of a crash on a miss
# take three URLs for a spin
=
=
=
Run it. Three URLs go in, three short codes come back (0, 1, 2), and each code resolves to the right URL. That's a working shortener. The global counter line lets shorten() advance the shared counter that lives outside the function - without it, Python would treat counter as a brand-new local and the codes would never move past 0.
Edge case one: an unknown code
Someone will hand you a code you never minted - a typo, a guess, a link you've since dropped. resolve() already handles it: store.get(code, None) returns None on a miss instead of raising KeyError. But returning None and acting on it are different things. The caller needs to notice the miss and say something useful.
=
return
=
Run it. The real code resolves; the two bogus ones report "unknown code" and the program keeps running. That if url is None check is the difference between a service that shrugs off bad input and one that 500s on it.
Edge case two: the same URL twice
Here's the one people miss. Submit the same long URL twice and the naive version mints two different codes for it:
=
=
return
=
, =
return
, = , 0
global
= ; = ; += 1
return
=
= # exact same URL
Run it and you'll see two different codes - 0 and 1 - for the identical URL. Whether that's a bug depends on what you want. It's harmless (both codes resolve correctly), but it wastes codes and means you can't tell a user "you already shortened this." Most real shorteners return the existing code when they recognize a URL.
Fixing it needs a second lookup - URL back to code - so we can check "have I seen this URL before?" A second dictionary, keyed the other way, does it:
=
=
return
=
, =
return
= # code -> url (for resolve)
= # url -> code (for dedup)
= 0
global
# seen it? hand back the old code
return
=
=
=
+= 1
return
return
=
= # same URL again
=
Run it. The repeated URL now comes back with the same code both times, while a genuinely new URL gets a fresh one. The if long_url in url_to_code check is the whole fix - one membership test before minting.
Where we are
shorten() and resolve() work together, unknown codes are handled without crashing, and the same URL twice returns one code. That's a complete shortener - the logic is done. What it lacks is a way for a person to use it without editing the source. In Phase 4 we wrap it in a small command loop so you can type URLs at it and get codes back, then map out where to take the project once it's off the page.