From 8eb7a03402cfee23f7686a53f7bdabddddd09ee5 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 15 Mar 2025 14:15:18 +0100 Subject: [PATCH] Apply ruff format and lint --- blog/pyproject.toml | 11 ++++++ blog/src/blog/__init__.py | 35 +++++++++++------- blog/src/blog/__main__.py | 11 +++++- blog/src/blog/blog_pages.py | 39 +++++++++++++++----- blog/src/blog/gemtext.py | 28 +++++++-------- blog/src/blog/html.py | 71 ++++++++++++++++++++++++------------- blog/src/blog/meta.py | 2 +- blog/src/blog/page.py | 1 - blog/test_html_rendering.py | 2 -- 9 files changed, 136 insertions(+), 64 deletions(-) diff --git a/blog/pyproject.toml b/blog/pyproject.toml index 4281d72..b1f9c31 100644 --- a/blog/pyproject.toml +++ b/blog/pyproject.toml @@ -32,3 +32,14 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel.force-include] "content" = "content" "static" = "static" + +[tool.ruff.lint] +select = ["ALL"] +ignore = [ + "E741", # Ambiguous variable name (single letter variable) + "ANN", # Missing type annotations + "D1", # Missing docstrings + "B011", "PT015", "S101", # I am wrong in liking asserts, but I do + "COM812", # Trailing comma missing, incompatible with ruff format + "D400", "D415", "D212", "D205" # I like doctests without docstrings +] diff --git a/blog/src/blog/__init__.py b/blog/src/blog/__init__.py index 511e241..5417f13 100644 --- a/blog/src/blog/__init__.py +++ b/blog/src/blog/__init__.py @@ -1,12 +1,11 @@ import importlib.resources -import pathlib import re import bicephalus import htmlgenerator as h -from blog import blog_pages, page, html, pretty, gemtext +from blog import blog_pages, gemtext, html, page, pretty STATIC = importlib.resources.files("static").iterdir().__next__().parent @@ -19,7 +18,7 @@ class SimplePage(page.BasePage): self.title = title def get_gemini_content(self): - file = (STATIC / self.url[1:] / "index.gmi") + file = STATIC / self.url[1:] / "index.gmi" return ( bicephalus.Status.OK, "text/gemini", @@ -30,19 +29,29 @@ class SimplePage(page.BasePage): return ( bicephalus.Status.OK, "text/html", - pretty.pretty_html(h.render( - h.HTML( - h.HEAD( - h.TITLE(self.title), - ), - h.BODY(*html.gemini_to_html(gemtext.parse(self.get_gemini_content()[2]))) - ), {})), + pretty.pretty_html( + h.render( + h.HTML( + h.HEAD( + h.TITLE(self.title), + ), + h.BODY( + *html.gemini_to_html( + gemtext.parse(self.get_gemini_content()[2]) + ) + ), + ), + {}, + ) + ), ) def handler(request: bicephalus.Request) -> bicephalus.Response: if not request.path.endswith("/"): - return bicephalus.Response(request.path + "/", None, bicephalus.Status.PERMANENT_REDIRECTION) + return bicephalus.Response( + request.path + "/", None, bicephalus.Status.PERMANENT_REDIRECTION + ) if request.path == "/": return blog_pages.Root(request).response() if re.match(r"/\d{4}/\d{2}/.*/", request.path): @@ -56,6 +65,8 @@ def handler(request: bicephalus.Request) -> bicephalus.Response: if request.path == "/laspelis/": return SimplePage(request, request.path, "laspelis").response() if re.match(r"/laspelis/\d+/", request.path): - return SimplePage(request, request.path.removesuffix("/") + "/", request.path).response() + return SimplePage( + request, request.path.removesuffix("/") + "/", request.path + ).response() return page.NotFound(request).response() diff --git a/blog/src/blog/__main__.py b/blog/src/blog/__main__.py index 8ed5f19..4bbcec5 100644 --- a/blog/src/blog/__main__.py +++ b/blog/src/blog/__main__.py @@ -15,7 +15,15 @@ def main(): otel.configure(log_level=logging.INFO) parser = argparse.ArgumentParser() - parser.add_argument("--key-cert", nargs=2, metavar=("KEY", "CERT",), help="Path to a key and a file") + parser.add_argument( + "--key-cert", + nargs=2, + metavar=( + "KEY", + "CERT", + ), + help="Path to a key and a file", + ) parser.add_argument("schema") parser.add_argument("host") args = parser.parse_args() @@ -32,5 +40,6 @@ def main(): bicephalus_main.main(blog.handler, ssl_context, 8000) sys.exit(0) + if __name__ == "__main__": main() diff --git a/blog/src/blog/blog_pages.py b/blog/src/blog/blog_pages.py index 9c6d5ee..64a9154 100644 --- a/blog/src/blog/blog_pages.py +++ b/blog/src/blog/blog_pages.py @@ -10,18 +10,21 @@ import htmlgenerator as h from feedgen import feed -from blog import html, page, gemtext, meta, pretty +from blog import gemtext, html, meta, page, pretty CONTENT = importlib.resources.files("content").iterdir().__next__().parent + def gemini_links(): return "\n".join([f"=> {url} {text}" for text, url in meta.LINKS]) class Entry: def __init__(self, path: pathlib.Path): - assert path.is_relative_to(CONTENT), f"bad path {path} not relative to {CONTENT}" + assert path.is_relative_to(CONTENT), ( + f"bad path {path} not relative to {CONTENT}" + ) self.path = path self.content = path.read_text() self.relative_path = path.relative_to(CONTENT) @@ -36,7 +39,19 @@ class Entry: @property def uri(self): - return f"/{self.relative_path.parts[0]}/{self.relative_path.parts[1]}/{self.relative_path.stem}/" + """ + >>> Entry(CONTENT / "2003/11/toda-saga-tiene-su-inicio.gmi").uri + '/2003/11/toda-saga-tiene-su-inicio/' + """ + return "/".join( + [ + "", + self.relative_path.parts[0], + self.relative_path.parts[1], + self.relative_path.stem, + "", + ] + ) @property def edit_url(self): @@ -88,7 +103,9 @@ class Root(page.BasePage): return ( bicephalus.Status.OK, "text/html", - html.html_template(*itertools.chain(posts), path=self.request.path, full=True), + html.html_template( + *itertools.chain(posts), path=self.request.path, full=True + ), ) def feed(self): @@ -101,7 +118,13 @@ class Root(page.BasePage): fe = fg.add_entry() url = f"{meta.SCHEMA}://{meta.HOST}/{entry.uri}" fe.link(href=url) - fe.published(datetime.datetime.combine(entry.posted, datetime.datetime.min.time(), tzinfo=datetime.timezone.utc)) + fe.published( + datetime.datetime.combine( + entry.posted, + datetime.datetime.min.time(), + tzinfo=datetime.UTC, + ) + ) fe.title(entry.title) html = h.render(h.BaseElement(*entry.html()), {}) html = pretty.pretty_html(html) @@ -126,9 +149,9 @@ class EntryPage(page.BasePage): => gemini://{meta.HOST} alex.corcoles.net {meta.EMAIL_TEXT} - """) + - self.entry.content + - textwrap.dedent(f"""\ + """) + + self.entry.content + + textwrap.dedent(f"""\ => {self.entry.edit_url} Editar """) ) diff --git a/blog/src/blog/gemtext.py b/blog/src/blog/gemtext.py index 66298e3..43cffd2 100644 --- a/blog/src/blog/gemtext.py +++ b/blog/src/blog/gemtext.py @@ -1,29 +1,28 @@ import dataclasses import re -import typing def parse(s): - """ + r""" >>> parse('''# Header 1 - ... + ... ... ## Header 2 - ... + ... ... ### Header 3 - ... + ... ... * List 1 ... * List 2 - ... + ... ... > First line quote. ... > Second line of quote. - ... + ... ... ``` ... Fenced ... Lines ... ``` - ... + ... ... Paragraph. - ... + ... ... Another paragraph. ... ''') [Header(level=1, text='Header 1'), @@ -38,13 +37,12 @@ def parse(s): BlockQuote(lines=[BlockQuoteLine(text='First line quote.'), BlockQuoteLine(text='Second line of quote.')]), Line(text=''), - Pre(content='Fenced\\nLines\\n'), + Pre(content='Fenced\nLines\n'), Line(text=''), Line(text='Paragraph.'), Line(text=''), Line(text='Another paragraph.')] """ - lines = s.splitlines() i = 0 @@ -121,7 +119,7 @@ class Link: """ url: str - text: typing.Optional[str] + text: str | None def __init__(self, line: str): assert Link.is_link(line) @@ -133,6 +131,7 @@ class Link: def is_link(line: str): return line.startswith("=>") + @dataclasses.dataclass class Header: """ @@ -158,6 +157,7 @@ class Header: def is_header(line: str): return re.match("#{1,3} .*", line) + @dataclasses.dataclass class ListItem: """ @@ -210,12 +210,12 @@ class Line: @dataclasses.dataclass class List: - items: typing.List[ListItem] + items: list[ListItem] @dataclasses.dataclass class BlockQuote: - lines: typing.List[BlockQuoteLine] + lines: list[BlockQuoteLine] @dataclasses.dataclass diff --git a/blog/src/blog/html.py b/blog/src/blog/html.py index 513dcf9..f2319fb 100644 --- a/blog/src/blog/html.py +++ b/blog/src/blog/html.py @@ -3,7 +3,7 @@ import textwrap import htmlgenerator as h -from blog import meta, pretty, gemtext +from blog import gemtext, meta, pretty def html_template(*content, page_title=None, path, full): @@ -13,7 +13,9 @@ def html_template(*content, page_title=None, path, full): title = h.BaseElement(*title) - links = list(itertools.chain(*[(h.A(text, href=href), ", ") for text, href in meta.LINKS])) + links = list( + itertools.chain(*[(h.A(text, href=href), ", ") for text, href in meta.LINKS]) + ) links += [h.BaseElement(f" {meta.EMAIL_TEXT}")] @@ -21,18 +23,30 @@ def html_template(*content, page_title=None, path, full): if full: full_part = [ h.H2(meta.SUBTITLE), - h.P(h.A("Buscar con DuckDuckGo en esta página", href="https://html.duckduckgo.com/html/?q=site:alex.corcoles.net")), + h.P( + h.A( + "Buscar con DuckDuckGo en esta página", + href="https://html.duckduckgo.com/html/?q=site:alex.corcoles.net", + ) + ), h.P(*links), ] gemini_url = f"gemini://alex.corcoles.net{path}" - return pretty.pretty_html(h.render( - h.HTML( - h.HEAD( - h.TITLE(meta.TITLE + (f" - {page_title}" if page_title else "")), - h.LINK(rel="alternate", type="application/rss+xml", title=meta.TITLE, href=f"{meta.SCHEMA}://{meta.HOST}/feed/"), - h.STYLE(textwrap.dedent(""" + return pretty.pretty_html( + h.render( + h.HTML( + h.HEAD( + h.TITLE(meta.TITLE + (f" - {page_title}" if page_title else "")), + h.LINK( + rel="alternate", + type="application/rss+xml", + title=meta.TITLE, + href=f"{meta.SCHEMA}://{meta.HOST}/feed/", + ), + h.STYLE( + textwrap.dedent(""" body { max-width: 40em; margin-left: auto; @@ -53,23 +67,28 @@ def html_template(*content, page_title=None, path, full): line-height: 1.6em; font-size: 20px; } - """).lstrip()) - ), - h.BODY( - h.P( - "Contenido tambien disponible en Gemini en ", - h.A(gemini_url, href=gemini_url), - ". ", - h.A("Información sobre Gemini.", href="https://geminiprotocol.net/"), + """).lstrip() + ), + ), + h.BODY( + h.P( + "Contenido tambien disponible en Gemini en ", + h.A(gemini_url, href=gemini_url), + ". ", + h.A( + "Información sobre Gemini.", + href="https://geminiprotocol.net/", + ), + ), + h.H1(title), + *full_part, + *content, ), - h.H1(title), - *full_part, - *content, + doctype="html", ), - doctype="html", - ), - {}, - )) + {}, + ) + ) def gemini_to_html(parsed): @@ -93,7 +112,9 @@ def gemini_to_html(parsed): url = gem_element.url if url.startswith("gemini://"): if url.startswith("gemini://alex.corcoles.net/"): - url = url.replace("gemini://alex.corcoles.net/", f"{meta.SCHEMA}://{meta.HOST}/") + url = url.replace( + "gemini://alex.corcoles.net/", f"{meta.SCHEMA}://{meta.HOST}/" + ) else: url = url.replace("gemini://", "https://portal.mozz.us/gemini/") diff --git a/blog/src/blog/meta.py b/blog/src/blog/meta.py index 6796671..b600545 100644 --- a/blog/src/blog/meta.py +++ b/blog/src/blog/meta.py @@ -12,4 +12,4 @@ LINKS = ( ("Stack Exchange", "https://stackexchange.com/users/13361/alex"), ) -EMAIL_TEXT = "envíame un email cogiendo el dominio de esta web y cambiando el primer punto por una arroba" +EMAIL_TEXT = "envíame mail al dominio que uso cambiando el primer punto por una arroba" diff --git a/blog/src/blog/page.py b/blog/src/blog/page.py index fcc4841..83bf187 100644 --- a/blog/src/blog/page.py +++ b/blog/src/blog/page.py @@ -22,7 +22,6 @@ class BasePage: class NotFound(BasePage): def get_gemini_content(self): - # TODO: does not work! return ( bicephalus.Status.NOT_FOUND, "text/gemini", diff --git a/blog/test_html_rendering.py b/blog/test_html_rendering.py index 54e1011..2f62435 100644 --- a/blog/test_html_rendering.py +++ b/blog/test_html_rendering.py @@ -1,5 +1,3 @@ -import pathlib - import pytest from blog import blog_pages -- 2.47.3