Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
## Unreleased: pdoc next

- Fix handling of URL-escaped module names ([#787](https://github.com/mitmproxy/pdoc/pull/787), @iFreilicht)
- Embed local images referenced in docstrings with an HTML image tag (`<img src="./image.png">`) in addition to Markdown (`![image](./image.png)`) ([#785](https://github.com/mitmproxy/pdoc/pull/785), @earshinov)

## 2024-12-12: pdoc 15.0.1

Expand Down
29 changes: 17 additions & 12 deletions pdoc/docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,28 @@ def convert(docstring: str, docformat: str, source_file: Path | None) -> str:


def embed_images(docstring: str, source_file: Path) -> str:
def local_image_to_data_uri(href: str) -> str:
image_path = source_file.parent / href
image_data = image_path.read_bytes()
image_mime = mimetypes.guess_type(image_path)[0]
image_data_b64 = base64.b64encode(image_data).decode()
return f"data:{image_mime};base64,{image_data_b64}"

def embed_local_image(m: re.Match) -> str:
image_path = source_file.parent / m["href"]
try:
image_data = image_path.read_bytes()
image_mime = mimetypes.guess_type(image_path)[0]
href = local_image_to_data_uri(m["href"])
except Exception:
return m[0]
else:
data = base64.b64encode(image_data).decode()
return f"![{m['alt']}](data:{image_mime};base64,{data})"

return re.sub(
r"!\[\s*(?P<alt>.*?)\s*]\(\s*(?P<href>.+?)\s*\)",
embed_local_image,
docstring,
)
# TODO: Could probably do more here, e.g. support rST or raw HTML replacements.
return m["before"] + href + m["after"]

# TODO: Could probably do more here, e.g. support rST replacements.
for regex in [
r"(?P<before>!\[\s*.*?\s*]\(\s*)(?P<href>.+?)(?P<after>\s*\))",
r"""(?P<before>src=['"])(?P<href>.+?)(?P<after>['"])""",
]:
docstring = re.sub(regex, embed_local_image, docstring)
return docstring


def google(docstring: str) -> str:
Expand Down
109 changes: 63 additions & 46 deletions test/testdata/demo_long.html

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions test/testdata/demo_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@

FOO_CONSTANT: int = 42
"""
A happy constant. ✨
A happy constant. ✨
pdoc documents constants with their type annotation and default value.
"""

FOO_SINGLETON: "Foo"
"""
This variable is annotated with a type only, but not assigned to a value.
We also haven't defined the associated type (`Foo`) yet,
We also haven't defined the associated type (`Foo`) yet,
so the type annotation in the code in the source code is actually a string literal:

```python
Expand Down Expand Up @@ -255,6 +255,12 @@ def embed_image():
```

![pdoc logo](../../docs/logo.png)

```
<img src="../docs/logo.png" alt="pdoc logo" width="150">
```

<img src="../../docs/logo.png" alt="pdoc logo" width="150">
"""


Expand Down
3 changes: 2 additions & 1 deletion test/testdata/demo_long.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<module demo_long # # Test Module

This …
<var FOO_CONSTANT: int = 42 # A happy constant. ✨ …>
<var FOO_CONSTANT: int = 42 # A happy constant. ✨
…>
<var FOO_SINGLETON: demo_long.Foo # This variable is ann…>
<var NO_DOCSTRING: int>
<function def a_simple_function(a: str) -> str: ... # This is a basic modu…>
Expand Down