[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
+]
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
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",
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):
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()
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()
bicephalus_main.main(blog.handler, ssl_context, 8000)
sys.exit(0)
+
if __name__ == "__main__":
main()
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)
@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):
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):
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)
=> 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
""")
)
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'),
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
"""
url: str
- text: typing.Optional[str]
+ text: str | None
def __init__(self, line: str):
assert Link.is_link(line)
def is_link(line: str):
return line.startswith("=>")
+
@dataclasses.dataclass
class Header:
"""
def is_header(line: str):
return re.match("#{1,3} .*", line)
+
@dataclasses.dataclass
class ListItem:
"""
@dataclasses.dataclass
class List:
- items: typing.List[ListItem]
+ items: list[ListItem]
@dataclasses.dataclass
class BlockQuote:
- lines: typing.List[BlockQuoteLine]
+ lines: list[BlockQuoteLine]
@dataclasses.dataclass
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):
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}")]
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;
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):
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/")
("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"
class NotFound(BasePage):
def get_gemini_content(self):
- # TODO: does not work!
return (
bicephalus.Status.NOT_FOUND,
"text/gemini",
-import pathlib
-
import pytest
from blog import blog_pages