Repository: mitmproxy/pdoc

Issue:
Modules with unicode characters in their name 404 on the interactive development server
#### Problem Description
When generating docs for modules with unicode characters in them, hosting them with `http.server` works fine. Links to those modules within the generated documentation work fine as well. However, using pdoc's builtin development server, links to these modules fail with errors like

```
Module 'cr%C3%A8me_br%C3%BBl%C3%A9e' not found
```

The issue is that URL escape sequences are not properly parsed. I will submit a PR for this shortly.

#### Steps to reproduce the behavior:
1. Create a module with unicode characters (like ä, é, ó, ß, ü) in its name
2. Run the pdoc development server
3. Try to click a link that links to this module

#### System Information
```
$ pdoc --version
pdoc: 15.0.1 (+6, commit 85503a1)
Python: 3.13.1
Platform: macOS-15.3.1-arm64-arm-64bit-Mach-O
```

Discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4248f72..7317814 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ## Unreleased: pdoc next
 
+- Fix handling of URL-escaped module names ([#787](https://github.com/mitmproxy/pdoc/pull/787), @iFreilicht)
 
 ## 2024-12-12: pdoc 15.0.1
 
diff --git a/pdoc/web.py b/pdoc/web.py
index d19ff7a..3d66a3e 100644
--- a/pdoc/web.py
+++ b/pdoc/web.py
@@ -14,6 +14,7 @@ from functools import cache
 import http.server
 import traceback
 from typing import Mapping
+import urllib.parse
 import warnings
 import webbrowser
 
@@ -60,6 +61,7 @@ class DocHandler(http.server.BaseHTTPRequestHandler):
             return "Not Found: Please normalize all module separators to '/'."
         else:
             module_name = path.lstrip("/").removesuffix(".html").replace("/", ".")
+            module_name = urllib.parse.unquote(module_name)
             if module_name not in self.server.all_modules:
                 self.send_response(404)
                 self.send_header("content-type", "text/html")


Test patch:
diff --git a/test/test_web.py b/test/test_web.py
index c007d76..5905234 100644
--- a/test/test_web.py
+++ b/test/test_web.py
@@ -69,6 +69,12 @@ def test_get_module():
     )
 
 
+def test_get_module_url_escape_sequences():
+    assert b"make_dataclass" in handle_request(
+        b"GET /%64atac%6Ca%73se%73.html HTTP/1.1\r\n\r\n"
+    )
+
+
 def test_get_dependency():
     assert b"a template engine written in pure Python" in handle_request(
         b"GET /jinja2.html HTTP/1.1\r\n\r\n"