From 02b8894371eb2bd6a508a342f6cce7a0733cb748 Mon Sep 17 00:00:00 2001 From: pauleveritt Date: Sun, 20 Nov 2022 10:22:25 -0500 Subject: [PATCH 1/3] Add an "author" resource type. --- src/psc/gallery/authors/meg-1.md | 6 ++++ src/psc/gallery/authors/pauleveritt.md | 6 ++++ src/psc/resources.py | 40 ++++++++++++++++++++------ tests/test_resources.py | 40 +++++++++++++++++++------- 4 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 src/psc/gallery/authors/meg-1.md create mode 100644 src/psc/gallery/authors/pauleveritt.md diff --git a/src/psc/gallery/authors/meg-1.md b/src/psc/gallery/authors/meg-1.md new file mode 100644 index 0000000..004f312 --- /dev/null +++ b/src/psc/gallery/authors/meg-1.md @@ -0,0 +1,6 @@ +--- +title: Margaret +--- + +An I.T. student. +Currently focusing on programming with Python and learning Django development. diff --git a/src/psc/gallery/authors/pauleveritt.md b/src/psc/gallery/authors/pauleveritt.md new file mode 100644 index 0000000..03db8f9 --- /dev/null +++ b/src/psc/gallery/authors/pauleveritt.md @@ -0,0 +1,6 @@ +--- +title: Paul Everitt +--- + +Python and Web Developer Advocate at @JetBrains for @PyCharm and @WebStormIDE. +Python oldster, Zope/Plone/Pyramid mafia. Girls lacrosse, formerly running. \ No newline at end of file diff --git a/src/psc/resources.py b/src/psc/resources.py index 808b4bf..96a8a9a 100644 --- a/src/psc/resources.py +++ b/src/psc/resources.py @@ -17,13 +17,12 @@ from psc.here import HERE from psc.here import PYODIDE - EXCLUSIONS = ("pyscript.css", "pyscript.js", "favicon.png") def tag_filter( - tag: Tag, - exclusions: tuple[str, ...] = EXCLUSIONS, + tag: Tag, + exclusions: tuple[str, ...] = EXCLUSIONS, ) -> bool: """Filter nodes from example that should not get included.""" attr = "href" if tag.name == "link" else "src" @@ -113,6 +112,19 @@ def __post_init__(self) -> None: self.body = get_body_content(soup) +@dataclass +class Author(Resource): + """Information about an author, from Markdown.""" + + def __post_init__(self) -> None: + """Initialize the rest of the fields from the Markdown.""" + md_file = HERE / "gallery/authors" / f"{self.path}.md" + md_fm = frontmatter.load(md_file) + self.title = md_fm.get("title", "") + md = MarkdownIt() + self.body = str(md.render(md_fm.content)) + + @dataclass class Page(Resource): """A Markdown+frontmatter driven content page.""" @@ -153,15 +165,19 @@ def __post_init__(self) -> None: class Resources: """Container for all resources in site.""" + authors: dict[PurePath, Author] = field(default_factory=dict) examples: dict[PurePath, Example] = field(default_factory=dict) pages: dict[PurePath, Page] = field(default_factory=dict) -def get_sorted_examples() -> list[PurePath]: +def get_sorted_paths(target_dir: Path, only_dirs: bool = True) -> list[PurePath]: """Return an alphabetized listing of the examples.""" - examples_dir = HERE / "gallery/examples" - examples = [e for e in examples_dir.iterdir() if e.is_dir()] - return sorted(examples, key=attrgetter("name")) + if only_dirs: + paths = [e for e in target_dir.iterdir() if e.is_dir()] + else: + paths = [e for e in target_dir.iterdir()] + + return sorted(paths, key=attrgetter("name")) def get_resources() -> Resources: @@ -169,11 +185,19 @@ def get_resources() -> Resources: resources = Resources() # Load the examples - for example in get_sorted_examples(): + examples = HERE / "gallery/examples" + for example in get_sorted_paths(examples): this_path = PurePath(example.name) this_example = Example(path=this_path) resources.examples[this_path] = this_example + # Load the authors + authors = HERE / "gallery/authors" + for author in get_sorted_paths(authors, only_dirs=False): + this_path = PurePath(author.stem) + this_author = Author(path=this_path) + resources.authors[this_path] = this_author + # Load the Pages pages_dir = HERE / "pages" pages = [e for e in pages_dir.iterdir()] diff --git a/tests/test_resources.py b/tests/test_resources.py index 8c12619..f9ec592 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -5,16 +5,16 @@ import pytest from bs4 import BeautifulSoup -from psc.resources import Example +from psc.here import HERE +from psc.resources import Example, Resources from psc.resources import Page from psc.resources import get_body_content from psc.resources import get_head_nodes from psc.resources import get_resources -from psc.resources import get_sorted_examples +from psc.resources import get_sorted_paths from psc.resources import is_local from psc.resources import tag_filter - IS_LOCAL = is_local() @@ -30,6 +30,11 @@ def head_soup() -> BeautifulSoup: return BeautifulSoup(head, "html5lib") +@pytest.fixture(scope="module") +def resources() -> Resources: + return get_resources() + + def test_tag_filter(head_soup: BeautifulSoup) -> None: """Helper function to filter link and script from head.""" excluded_link = head_soup.select("link")[0] @@ -118,8 +123,8 @@ def test_example() -> None: this_example = Example(path=PurePath("hello_world")) assert this_example.title == "Hello World" assert ( - this_example.subtitle - == "The classic hello world, but in Python -- in a browser!" + this_example.subtitle + == "The classic hello world, but in Python -- in a browser!" ) assert "hello_world.css" in this_example.extra_head assert "

Hello ...

" in this_example.body @@ -155,23 +160,29 @@ def test_missing_page() -> None: def test_sorted_examples() -> None: - """Ensure a stable listing.""" - examples = get_sorted_examples() + """Ensure a stable listing of dirs.""" + examples = get_sorted_paths(HERE / "gallery/examples") first_example = examples[0] assert "altair" == first_example.name -def test_get_resources() -> None: +def test_sorted_authors() -> None: + """Ensure a stable listing of files.""" + authors = get_sorted_paths(HERE / "gallery/authors", only_dirs=False) + first_author = authors[0] + assert "meg-1.md" == first_author.name + + +def test_get_resources(resources: Resources) -> None: """Ensure the dict-of-dicts is generated with PurePath keys.""" - resources = get_resources() # Example hello_world_path = PurePath("hello_world") hello_world = resources.examples[hello_world_path] assert hello_world.title == "Hello World" assert ( - hello_world.subtitle - == "The classic hello world, but in Python -- in a browser!" + hello_world.subtitle + == "The classic hello world, but in Python -- in a browser!" ) # Page @@ -186,3 +197,10 @@ def test_is_local_broken_path() -> None: test_path = Path("/xxx") actual = is_local(test_path) assert not actual + + +def test_authors(resources: Resources) -> None: + """Get the list of authors as defined in Markdown files.""" + authors = resources.authors + first_author = list(authors.values())[0] + assert "meg-1" == first_author.path.name From 87770dfb6e846c75f4381e1e00ecc5963711bb90 Mon Sep 17 00:00:00 2001 From: pauleveritt Date: Sun, 20 Nov 2022 10:57:09 -0500 Subject: [PATCH 2/3] A route for authors and author. --- src/psc/app.py | 38 +++++++++++++++++++++++++++++++- src/psc/resources.py | 5 ++--- src/psc/templates/author.jinja2 | 8 +++++++ src/psc/templates/authors.jinja2 | 29 ++++++++++++++++++++++++ src/psc/templates/gallery.jinja2 | 2 +- src/psc/templates/layout.jinja2 | 3 +++ tests/test_author_pages.py | 22 ++++++++++++++++++ 7 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 src/psc/templates/author.jinja2 create mode 100644 src/psc/templates/authors.jinja2 create mode 100644 tests/test_author_pages.py diff --git a/src/psc/app.py b/src/psc/app.py index d86e90d..099dfee 100644 --- a/src/psc/app.py +++ b/src/psc/app.py @@ -20,7 +20,6 @@ from psc.resources import Resources from psc.resources import get_resources - templates = Jinja2Templates(directory=HERE / "templates") @@ -61,6 +60,40 @@ async def gallery(request: Request) -> _TemplateResponse: ) +async def authors(request: Request) -> _TemplateResponse: + """Handle the author listing page.""" + these_authors: Iterator[Example] = request.app.state.resources.authors.values() + root_path = ".." + + return templates.TemplateResponse( + "authors.jinja2", + dict( + title="Authors", + authors=these_authors, + root_path=root_path, + request=request, + ), + ) + + +async def author(request: Request) -> _TemplateResponse: + """Handle an author page.""" + author_path = PurePath(request.path_params["author_name"]) + resources: Resources = request.app.state.resources + this_author = resources.authors[author_path] + root_path = "../../.." + + return templates.TemplateResponse( + "example.jinja2", + dict( + title=this_author.title, + body=this_author.body, + request=request, + root_path=root_path, + ), + ) + + async def example(request: Request) -> _TemplateResponse: """Handle an example page.""" example_path = PurePath(request.path_params["example_name"]) @@ -113,6 +146,9 @@ async def content_page(request: Request) -> _TemplateResponse: Route("/favicon.png", favicon), Route("/gallery/index.html", gallery), Route("/gallery", gallery), + Route("/authors/index.html", authors), + Route("/authors", authors), + Route("/authors/{author_name}.html", author), Route("/gallery/examples/{example_name}/index.html", example), Route("/gallery/examples/{example_name}/", example), Route("/pages/{page_name}.html", content_page), diff --git a/src/psc/resources.py b/src/psc/resources.py index 96a8a9a..e91a82b 100644 --- a/src/psc/resources.py +++ b/src/psc/resources.py @@ -90,18 +90,17 @@ class Example(Resource): Meaning, HERE / "examples" / name / "index.html". """ - description: str = "" subtitle: str = "" def __post_init__(self) -> None: """Extract most of the data from the HTML file.""" - # Title, subtitle, description come from the example's MD file. + # Title, subtitle, body come from the example's MD file. index_md_file = HERE / "gallery/examples" / self.path / "index.md" md_fm = frontmatter.load(index_md_file) self.title = md_fm.get("title", "") self.subtitle = md_fm.get("subtitle", "") md = MarkdownIt() - self.description = str(md.render(md_fm.content)) + self.body = str(md.render(md_fm.content)) # Main, extra head example's HTML file. index_html_file = HERE / "gallery/examples" / self.path / "index.html" diff --git a/src/psc/templates/author.jinja2 b/src/psc/templates/author.jinja2 new file mode 100644 index 0000000..8547fa8 --- /dev/null +++ b/src/psc/templates/author.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} +{% block extra_head %} +{% block main %} +
+

{{ title }}

+
{{ body | safe }}
+
+{% endblock %} diff --git a/src/psc/templates/authors.jinja2 b/src/psc/templates/authors.jinja2 new file mode 100644 index 0000000..3c0b845 --- /dev/null +++ b/src/psc/templates/authors.jinja2 @@ -0,0 +1,29 @@ +{% extends "layout.jinja2" %} +{% block main %} +
+
+

+ PyScript Authors +

+

+ All the contributors to authors and more. +

+
+
+
+
+ {% for author in authors %} +
+ +
+ {% endfor %} +
+
+{% endblock %} diff --git a/src/psc/templates/gallery.jinja2 b/src/psc/templates/gallery.jinja2 index 40b1194..2892fdb 100644 --- a/src/psc/templates/gallery.jinja2 +++ b/src/psc/templates/gallery.jinja2 @@ -20,7 +20,7 @@

{{ example.subtitle }}

- {{ example.description | safe }} + {{ example.body | safe }}
diff --git a/src/psc/templates/layout.jinja2 b/src/psc/templates/layout.jinja2 index 605d572..2f36559 100644 --- a/src/psc/templates/layout.jinja2 +++ b/src/psc/templates/layout.jinja2 @@ -26,6 +26,9 @@ Gallery + + Authors + Join diff --git a/tests/test_author_pages.py b/tests/test_author_pages.py new file mode 100644 index 0000000..d818a0b --- /dev/null +++ b/tests/test_author_pages.py @@ -0,0 +1,22 @@ +"""Use the routes to render listing of authors and each one.""" +from psc.fixtures import PageT + + +def test_authors_page(client_page: PageT) -> None: + """The listing of authors works.""" + soup = client_page("/authors/index.html") + page_title = soup.select_one("title") + assert page_title and "Authors | PyScript Collective" == page_title.text + + authors = soup.select_one("article.tile p.title") + assert "Margaret" == authors.text.strip() + + +def test_author_page(client_page: PageT) -> None: + """The page for an author works.""" + soup = client_page("/authors/meg-1.html") + page_title = soup.select_one("title") + assert page_title and "Margaret | PyScript Collective" == page_title.text + + author = soup.select_one("main h1") + assert "Margaret" == author.text.strip() From 6622b993d75bca0a8d3d152849d2269fe73d8237 Mon Sep 17 00:00:00 2001 From: pauleveritt Date: Sun, 20 Nov 2022 12:04:05 -0500 Subject: [PATCH 3/3] Link the author in each example. Switch from PurePath keys to just strings. --- src/psc/__main__.py | 8 +-- src/psc/app.py | 25 +++++--- src/psc/gallery/authors/pauleveritt.md | 2 +- .../examples/interest_calculator/index.md | 3 +- src/psc/resources.py | 58 +++++++++---------- src/psc/templates/authors.jinja2 | 2 +- src/psc/templates/example.jinja2 | 3 + src/psc/templates/gallery.jinja2 | 32 +++++----- tests/test_author_pages.py | 6 +- tests/test_resources.py | 39 ++++++------- 10 files changed, 96 insertions(+), 82 deletions(-) diff --git a/src/psc/__main__.py b/src/psc/__main__.py index cb3a100..162c6f5 100644 --- a/src/psc/__main__.py +++ b/src/psc/__main__.py @@ -102,15 +102,15 @@ def build() -> None: # pragma: no cover # Now for each page resources = get_resources() for page in resources.pages.values(): - response = test_client.get(f"/pages/{page.path.stem}.html") - output = public / f"pages/{page.path.stem}.html" + response = test_client.get(f"/pages/{page.name}.html") + output = public / f"pages/{page.name}.html" output.write_text(response.text) # And for each example for example in resources.examples.values(): - url = f"/gallery/examples/{example.path.stem}/index.html" + url = f"/gallery/examples/{example.name}/index.html" response = test_client.get(url) - output = public / f"gallery/examples/{example.path.stem}/index.html" + output = public / f"gallery/examples/{example.name}/index.html" output.write_text(response.text) diff --git a/src/psc/app.py b/src/psc/app.py index 099dfee..7b5da85 100644 --- a/src/psc/app.py +++ b/src/psc/app.py @@ -1,7 +1,6 @@ """Provide a web server to browse the examples.""" import contextlib from collections.abc import Iterator -from pathlib import PurePath from typing import AsyncContextManager from starlette.applications import Starlette @@ -20,6 +19,7 @@ from psc.resources import Resources from psc.resources import get_resources + templates = Jinja2Templates(directory=HERE / "templates") @@ -46,8 +46,10 @@ async def homepage(request: Request) -> _TemplateResponse: async def gallery(request: Request) -> _TemplateResponse: """Handle the gallery listing page.""" - these_examples: Iterator[Example] = request.app.state.resources.examples.values() + resources = request.app.state.resources + these_examples: Iterator[Example] = resources.examples.values() root_path = ".." + these_authors = resources.authors return templates.TemplateResponse( "gallery.jinja2", @@ -56,6 +58,7 @@ async def gallery(request: Request) -> _TemplateResponse: examples=these_examples, root_path=root_path, request=request, + authors=these_authors, ), ) @@ -78,9 +81,9 @@ async def authors(request: Request) -> _TemplateResponse: async def author(request: Request) -> _TemplateResponse: """Handle an author page.""" - author_path = PurePath(request.path_params["author_name"]) + author_name = request.path_params["author_name"] resources: Resources = request.app.state.resources - this_author = resources.authors[author_path] + this_author = resources.authors[author_name] root_path = "../../.." return templates.TemplateResponse( @@ -96,10 +99,15 @@ async def author(request: Request) -> _TemplateResponse: async def example(request: Request) -> _TemplateResponse: """Handle an example page.""" - example_path = PurePath(request.path_params["example_name"]) + example_name = request.path_params["example_name"] resources: Resources = request.app.state.resources - this_example = resources.examples[example_path] + this_example = resources.examples[example_name] root_path = "../../.." + author_name = this_example.author + if author_name: + this_author = resources.authors.get(author_name, None) + else: + this_author = None # Set the pyscript URL to the CDN if we are being built from # the ``psc build`` command. @@ -119,15 +127,16 @@ async def example(request: Request) -> _TemplateResponse: request=request, root_path=root_path, pyscript_url=pyscript_url, + author=this_author, ), ) async def content_page(request: Request) -> _TemplateResponse: """Handle a content page.""" - page_path = PurePath(request.path_params["page_name"]) + page_name = request.path_params["page_name"] resources: Resources = request.app.state.resources - this_page = resources.pages[page_path] + this_page = resources.pages[page_name] return templates.TemplateResponse( "page.jinja2", diff --git a/src/psc/gallery/authors/pauleveritt.md b/src/psc/gallery/authors/pauleveritt.md index 03db8f9..63d665c 100644 --- a/src/psc/gallery/authors/pauleveritt.md +++ b/src/psc/gallery/authors/pauleveritt.md @@ -3,4 +3,4 @@ title: Paul Everitt --- Python and Web Developer Advocate at @JetBrains for @PyCharm and @WebStormIDE. -Python oldster, Zope/Plone/Pyramid mafia. Girls lacrosse, formerly running. \ No newline at end of file +Python oldster, Zope/Plone/Pyramid mafia. Girls lacrosse, formerly running. diff --git a/src/psc/gallery/examples/interest_calculator/index.md b/src/psc/gallery/examples/interest_calculator/index.md index 35b0a54..bffb179 100644 --- a/src/psc/gallery/examples/interest_calculator/index.md +++ b/src/psc/gallery/examples/interest_calculator/index.md @@ -1,5 +1,6 @@ --- title: Compound Interest Calculator -subtitle: The classic hello world, but in Python -- in a browser! +subtitle: Enter some numbers, get some numbers. +author: meg-1 --- The *body* description. diff --git a/src/psc/resources.py b/src/psc/resources.py index e91a82b..5717f8a 100644 --- a/src/psc/resources.py +++ b/src/psc/resources.py @@ -17,12 +17,13 @@ from psc.here import HERE from psc.here import PYODIDE + EXCLUSIONS = ("pyscript.css", "pyscript.js", "favicon.png") def tag_filter( - tag: Tag, - exclusions: tuple[str, ...] = EXCLUSIONS, + tag: Tag, + exclusions: tuple[str, ...] = EXCLUSIONS, ) -> bool: """Filter nodes from example that should not get included.""" attr = "href" if tag.name == "link" else "src" @@ -74,7 +75,7 @@ def get_body_content(s: BeautifulSoup, test_path: Path = PYODIDE) -> str: class Resource: """Base dataclass used for all resources.""" - path: PurePath + name: str title: str = "" body: str = "" extra_head: str = "" @@ -91,21 +92,24 @@ class Example(Resource): """ subtitle: str = "" + description: str = "" + author: str | None = None def __post_init__(self) -> None: """Extract most of the data from the HTML file.""" # Title, subtitle, body come from the example's MD file. - index_md_file = HERE / "gallery/examples" / self.path / "index.md" + index_md_file = HERE / "gallery/examples" / self.name / "index.md" md_fm = frontmatter.load(index_md_file) self.title = md_fm.get("title", "") + self.author = md_fm.get("author", "") self.subtitle = md_fm.get("subtitle", "") md = MarkdownIt() - self.body = str(md.render(md_fm.content)) + self.description = str(md.render(md_fm.content)) # Main, extra head example's HTML file. - index_html_file = HERE / "gallery/examples" / self.path / "index.html" + index_html_file = HERE / "gallery/examples" / self.name / "index.html" if not index_html_file.exists(): # pragma: nocover - raise ValueError(f"No example at {self.path}") + raise ValueError(f"No example at {self.name}") soup = BeautifulSoup(index_html_file.read_text(), "html5lib") self.extra_head = get_head_nodes(soup) self.body = get_body_content(soup) @@ -117,7 +121,7 @@ class Author(Resource): def __post_init__(self) -> None: """Initialize the rest of the fields from the Markdown.""" - md_file = HERE / "gallery/authors" / f"{self.path}.md" + md_file = HERE / "gallery/authors" / f"{self.name}.md" md_fm = frontmatter.load(md_file) self.title = md_fm.get("title", "") md = MarkdownIt() @@ -129,14 +133,13 @@ class Page(Resource): """A Markdown+frontmatter driven content page.""" subtitle: str = "" - body: str = "" def __post_init__(self) -> None: """Extract content from either Markdown or HTML file.""" - md_file = HERE / "pages" / f"{self.path}.md" - html_file = HERE / "pages" / f"{self.path}.html" + md_file = HERE / "pages" / f"{self.name}.md" + html_file = HERE / "pages" / f"{self.name}.html" - # If this self.path resolves to a Markdown file, use it first + # If this self.name resolves to a Markdown file, use it first if md_file.exists(): md_fm = frontmatter.load(md_file) self.title = md_fm.get("title", "") @@ -157,16 +160,16 @@ def __post_init__(self) -> None: if body_node and isinstance(body_node, Tag): self.body = body_node.prettify() else: # pragma: no cover - raise ValueError(f"No page at {self.path}") + raise ValueError(f"No page at {self.name}") @dataclass class Resources: """Container for all resources in site.""" - authors: dict[PurePath, Author] = field(default_factory=dict) - examples: dict[PurePath, Example] = field(default_factory=dict) - pages: dict[PurePath, Page] = field(default_factory=dict) + authors: dict[str, Author] = field(default_factory=dict) + examples: dict[str, Example] = field(default_factory=dict) + pages: dict[str, Page] = field(default_factory=dict) def get_sorted_paths(target_dir: Path, only_dirs: bool = True) -> list[PurePath]: @@ -183,26 +186,23 @@ def get_resources() -> Resources: """Factory to construct all the resources in the site.""" resources = Resources() - # Load the examples - examples = HERE / "gallery/examples" - for example in get_sorted_paths(examples): - this_path = PurePath(example.name) - this_example = Example(path=this_path) - resources.examples[this_path] = this_example - # Load the authors authors = HERE / "gallery/authors" for author in get_sorted_paths(authors, only_dirs=False): - this_path = PurePath(author.stem) - this_author = Author(path=this_path) - resources.authors[this_path] = this_author + this_author = Author(name=author.stem) + resources.authors[author.stem] = this_author + + # Load the examples + examples = HERE / "gallery/examples" + for example in get_sorted_paths(examples): + this_example = Example(example.stem) + resources.examples[example.stem] = this_example # Load the Pages pages_dir = HERE / "pages" pages = [e for e in pages_dir.iterdir()] for page in pages: - this_path = PurePath(page.stem) - this_page = Page(path=this_path) - resources.pages[this_path] = this_page + this_page = Page(name=page.stem) + resources.pages[page.stem] = this_page return resources diff --git a/src/psc/templates/authors.jinja2 b/src/psc/templates/authors.jinja2 index 3c0b845..683613b 100644 --- a/src/psc/templates/authors.jinja2 +++ b/src/psc/templates/authors.jinja2 @@ -16,7 +16,7 @@

{{ author.title }} + href="{{ root_path }}/authors/{{ author.name }}.html/">{{ author.title }}

{{ author.body | safe }} diff --git a/src/psc/templates/example.jinja2 b/src/psc/templates/example.jinja2 index a766282..1e3fa11 100644 --- a/src/psc/templates/example.jinja2 +++ b/src/psc/templates/example.jinja2 @@ -6,6 +6,9 @@ {% block main %}

{{ title }}

+ {% if author %} +

By {{ author.title }}

+ {% endif %}

{{ subtitle }}

{{ body | safe }}
diff --git a/src/psc/templates/gallery.jinja2 b/src/psc/templates/gallery.jinja2 index 2892fdb..2f483fd 100644 --- a/src/psc/templates/gallery.jinja2 +++ b/src/psc/templates/gallery.jinja2 @@ -11,20 +11,22 @@
-
- {% for example in examples %} -
- -
- {% endfor %} -
+ {% for row in examples | batch(3) %} +
+ {% for example in row %} +
+ +
+ {% endfor %} +
+ {% endfor %}
{% endblock %} diff --git a/tests/test_author_pages.py b/tests/test_author_pages.py index d818a0b..58b2d64 100644 --- a/tests/test_author_pages.py +++ b/tests/test_author_pages.py @@ -9,7 +9,8 @@ def test_authors_page(client_page: PageT) -> None: assert page_title and "Authors | PyScript Collective" == page_title.text authors = soup.select_one("article.tile p.title") - assert "Margaret" == authors.text.strip() + if authors: + assert "Margaret" == authors.text.strip() def test_author_page(client_page: PageT) -> None: @@ -19,4 +20,5 @@ def test_author_page(client_page: PageT) -> None: assert page_title and "Margaret | PyScript Collective" == page_title.text author = soup.select_one("main h1") - assert "Margaret" == author.text.strip() + if author: + assert "Margaret" == author.text.strip() diff --git a/tests/test_resources.py b/tests/test_resources.py index f9ec592..bfc7460 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -1,13 +1,13 @@ """Construct the various kinds of resources: example, page, contributor.""" from pathlib import Path -from pathlib import PurePath import pytest from bs4 import BeautifulSoup from psc.here import HERE -from psc.resources import Example, Resources +from psc.resources import Example from psc.resources import Page +from psc.resources import Resources from psc.resources import get_body_content from psc.resources import get_head_nodes from psc.resources import get_resources @@ -15,6 +15,7 @@ from psc.resources import is_local from psc.resources import tag_filter + IS_LOCAL = is_local() @@ -32,6 +33,7 @@ def head_soup() -> BeautifulSoup: @pytest.fixture(scope="module") def resources() -> Resources: + """Cache the generation of resources for this test file.""" return get_resources() @@ -115,16 +117,16 @@ def test_get_py_config_no_body() -> None: def test_example_bad_path() -> None: """Point at an example that does not exist, get ValueError.""" with pytest.raises(FileNotFoundError): - Example(path=PurePath("XXXX")) + Example(name="XXX") def test_example() -> None: """Construct an ``Example`` and ensure it has all the template bits.""" - this_example = Example(path=PurePath("hello_world")) + this_example = Example(name="hello_world") assert this_example.title == "Hello World" assert ( - this_example.subtitle - == "The classic hello world, but in Python -- in a browser!" + this_example.subtitle + == "The classic hello world, but in Python -- in a browser!" ) assert "hello_world.css" in this_example.extra_head assert "

Hello ...

" in this_example.body @@ -132,14 +134,14 @@ def test_example() -> None: def test_markdown_page() -> None: """Make an instance of a Page resource and test it.""" - this_page = Page(path=PurePath("about")) + this_page = Page(name="about") assert this_page.title == "About the PyScript Collective" assert "

Helping" in this_page.body def test_html_page() -> None: """Make an instance of a .html Page resource and test it.""" - this_page = Page(path=PurePath("contributing")) + this_page = Page(name="contributing") assert this_page.title == "Contributing" assert this_page.subtitle == "How to get involved in the PyScript Collective." assert 'id="viewer"' in this_page.body @@ -147,7 +149,7 @@ def test_html_page() -> None: def test_page_optional_subtitle() -> None: """Frontmatter does not specify a subtitle.""" - this_page = Page(path=PurePath("contact")) + this_page = Page(name="contact") assert this_page.title == "Contact Us" assert this_page.subtitle == "" @@ -155,7 +157,7 @@ def test_page_optional_subtitle() -> None: def test_missing_page() -> None: """Make a missing Page resource and test that it raises exception.""" with pytest.raises(ValueError) as exc: - Page(path=PurePath("xxx")) + Page(name="xxx") assert str(exc.value) == "No page at xxx" @@ -175,19 +177,14 @@ def test_sorted_authors() -> None: def test_get_resources(resources: Resources) -> None: """Ensure the dict-of-dicts is generated with PurePath keys.""" - # Example - hello_world_path = PurePath("hello_world") - hello_world = resources.examples[hello_world_path] - assert hello_world.title == "Hello World" - assert ( - hello_world.subtitle - == "The classic hello world, but in Python -- in a browser!" - ) + interest_calculator = resources.examples["interest_calculator"] + assert interest_calculator.title == "Compound Interest Calculator" + assert interest_calculator.subtitle == "Enter some numbers, get some numbers." + assert "meg-1" == interest_calculator.author # Page - about_path = PurePath("about") - about = resources.pages[about_path] + about = resources.pages["about"] assert about.title == "About the PyScript Collective" assert "

Helping" in about.body @@ -203,4 +200,4 @@ def test_authors(resources: Resources) -> None: """Get the list of authors as defined in Markdown files.""" authors = resources.authors first_author = list(authors.values())[0] - assert "meg-1" == first_author.path.name + assert "meg-1" == first_author.name