Skip to content

Commit 3bf7773

Browse files
committed
Introduce LinkedFile resource as start to showing code samples.
1 parent 22768b8 commit 3bf7773

File tree

3 files changed

+58
-16
lines changed

3 files changed

+58
-16
lines changed

src/psc/gallery/examples/interest_calculator/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22
title: Compound Interest Calculator
33
subtitle: Enter some numbers, get some numbers.
44
author: meg-1
5+
linked_files:
6+
- calculator.py
7+
- styles.css
58
---
69
The *body* description.

src/psc/resources.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
from psc.here import HERE
1818
from psc.here import PYODIDE
1919

20-
2120
EXCLUSIONS = ("pyscript.css", "pyscript.js", "favicon.png")
2221

2322

2423
def tag_filter(
25-
tag: Tag,
26-
exclusions: tuple[str, ...] = EXCLUSIONS,
24+
tag: Tag,
25+
exclusions: tuple[str, ...] = EXCLUSIONS,
2726
) -> bool:
2827
"""Filter nodes from example that should not get included."""
2928
attr = "href" if tag.name == "link" else "src"
@@ -81,6 +80,27 @@ class Resource:
8180
extra_head: str = ""
8281

8382

83+
linked_file_mapping = dict(
84+
py="python",
85+
css="css",
86+
html="html"
87+
)
88+
89+
90+
@dataclass
91+
class LinkedFile:
92+
"""A source file on disk that gets attached to an example."""
93+
94+
path: Path
95+
language: str = field(init=False)
96+
body: str = field(init=False)
97+
98+
def __post_init__(self) -> None:
99+
"""Read the file contents into the body."""
100+
self.language = linked_file_mapping[self.path.suffix[1:]]
101+
self.body = self.path.read_text()
102+
103+
84104
@dataclass
85105
class Example(Resource):
86106
"""Create an example from an HTML location on disk.
@@ -91,9 +111,10 @@ class Example(Resource):
91111
Meaning, HERE / "examples" / name / "index.html".
92112
"""
93113

94-
subtitle: str = ""
95-
description: str = ""
96-
author: str | None = None
114+
subtitle: str = field(init=False)
115+
description: str = field(init=False)
116+
author: str = field(init=False)
117+
linked_files: list[LinkedFile] = field(default_factory=list)
97118

98119
def __post_init__(self) -> None:
99120
"""Extract most of the data from the HTML file."""
@@ -107,13 +128,22 @@ def __post_init__(self) -> None:
107128
self.description = str(md.render(md_fm.content))
108129

109130
# Main, extra head example's HTML file.
110-
index_html_file = HERE / "gallery/examples" / self.name / "index.html"
131+
this_example_path = HERE / "gallery/examples" / self.name
132+
index_html_file = this_example_path / "index.html"
111133
if not index_html_file.exists(): # pragma: nocover
112134
raise ValueError(f"No example at {self.name}")
113-
soup = BeautifulSoup(index_html_file.read_text(), "html5lib")
135+
index_html_text = index_html_file.read_text()
136+
soup = BeautifulSoup(index_html_text, "html5lib")
114137
self.extra_head = get_head_nodes(soup)
115138
self.body = get_body_content(soup)
116139

140+
# Process any linked files
141+
linked_paths = [*["index.html"], *md_fm.get("linked_files", [])]
142+
for linked_name in linked_paths:
143+
linked_path = this_example_path / linked_name
144+
linked_file = LinkedFile(path=linked_path)
145+
self.linked_files.append(linked_file)
146+
117147

118148
@dataclass
119149
class Author(Resource):

tests/test_resources.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from bs4 import BeautifulSoup
66

77
from psc.here import HERE
8-
from psc.resources import Example
8+
from psc.resources import Example, LinkedFile
99
from psc.resources import Page
1010
from psc.resources import Resources
1111
from psc.resources import get_body_content
@@ -15,7 +15,6 @@
1515
from psc.resources import is_local
1616
from psc.resources import tag_filter
1717

18-
1918
IS_LOCAL = is_local()
2019

2120

@@ -122,14 +121,24 @@ def test_example_bad_path() -> None:
122121

123122
def test_example() -> None:
124123
"""Construct an ``Example`` and ensure it has all the template bits."""
125-
this_example = Example(name="hello_world")
126-
assert this_example.title == "Hello World"
124+
this_example = Example(name="interest_calculator")
125+
assert this_example.title == "Compound Interest Calculator"
127126
assert (
128-
this_example.subtitle
129-
== "The classic hello world, but in Python -- in a browser!"
127+
this_example.subtitle
128+
== "Enter some numbers, get some numbers."
130129
)
131-
assert "hello_world.css" in this_example.extra_head
132-
assert "<h1>Hello ...</h1>" in this_example.body
130+
assert "styles.css" in this_example.extra_head
131+
assert "Welcome to the" in this_example.body
132+
133+
linked_files = this_example.linked_files
134+
assert "html" == linked_files[0].language
135+
136+
137+
def test_linked_file() -> None:
138+
"""Correctly create a LinkedFile."""
139+
linked_path = HERE / "gallery/examples/altair/index.html"
140+
linked_file = LinkedFile(path=linked_path)
141+
assert "html" == linked_file.language
133142

134143

135144
def test_markdown_page() -> None:

0 commit comments

Comments
 (0)