1
0
mirror of https://github.com/venthur/blag.git synced 2025-11-25 20:52:43 +00:00

11 Commits

Author SHA1 Message Date
Bastian Venthur
60bd86ec22 finalized benchmark 2025-01-22 15:17:28 +01:00
Bastian Venthur
69b729a471 Merge branch 'performance' into rebuild_needed_only 2025-01-22 14:46:42 +01:00
Bastian Venthur
f09e841200 Merge branch 'master' into rebuild_needed_only 2025-01-22 14:46:29 +01:00
Bastian Venthur
c94f9793b0 improved performance test 2025-01-22 14:46:14 +01:00
Bastian Venthur
5cddb62603 Merge branch 'master' into performance 2025-01-22 14:14:46 +01:00
Bastian Venthur
a3da95ea19 added benchmark 2024-10-29 12:13:05 +01:00
Bastian Venthur
c4f3c09730 WIP 2024-10-29 10:16:48 +01:00
Bastian Venthur
5943dab690 wip 2024-10-29 10:16:48 +01:00
Bastian Venthur
71c866c9b3 Merge branch 'master' into rebuild_needed_only 2023-11-12 17:16:46 +01:00
Bastian Venthur
c87a300c71 Merge branch 'master' into rebuild_needed_only 2022-08-07 21:50:15 +02:00
Bastian Venthur
a939e6503c rebuild only files that need re-building
we compare the mtimes of src and dst
2022-08-06 21:56:41 +02:00
18 changed files with 183 additions and 163 deletions

View File

@@ -1,9 +1,12 @@
name: CI/CD Pipeline
on:
- push
- pull_request
jobs:
test:
name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
@@ -18,11 +21,10 @@ jobs:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
@@ -35,8 +37,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
@@ -49,8 +51,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
@@ -64,8 +66,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"

View File

@@ -3,13 +3,13 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.12"
python: "3.11"
mkdocs:
configuration: mkdocs.yml
python:
install:
- path: .
extra_requirements:
- dev
- requirements: requirements.txt
- requirements: requirements-dev.txt
- path: .

View File

@@ -1,18 +1,8 @@
# Changelog
## [unreleased] --
## [unreleased]
* Added Python 3.14 compatibility
* Removed requirements.txt and requirements-dev.txt
* Added footnotes extension for markdown
## [2.3.3] -- 2025-04-27
* Fixed Atom feed encoding to 'utf-8'
* Added Python 3.13 to github actions
* `requirements*.txt` files are handled by pip-tools. Updates are handled by
make update-requirements based on the dependencies described in the
pyproject.toml
## [2.3.2] -- 2024-10-13

View File

@@ -16,9 +16,11 @@ endif
.PHONY: all
all: lint mypy test test-release
$(VENV): pyproject.toml
$(VENV): requirements.txt requirements-dev.txt pyproject.toml
$(PY) -m venv $(VENV)
$(BIN)/pip install --upgrade -e .['dev']
$(BIN)/pip install --upgrade -r requirements.txt
$(BIN)/pip install --upgrade -r requirements-dev.txt
$(BIN)/pip install -e .['dev']
touch $(VENV)
.PHONY: test
@@ -63,6 +65,10 @@ serve-docs: $(VENV)
manpage: $(VENV)
help2man $(BIN)/blag --no-info -n "blog-aware, static site generator" -o debian/blag.1
.PHONY: benchmark
benchmark: $(VENV)
$(BIN)/pytest --no-cov -capture=no -rA tests/benchmark.py
.PHONY: clean
clean:
rm -rf build dist *.egg-info

View File

@@ -21,7 +21,6 @@ blag is named after [the blag of the webcomic xkcd][blagxkcd].
* Theming support using [Jinja2][] templates
* Generation of Atom feeds for blog content
* Fenced code blocks and syntax highlighting using [Pygments][]
* Markdown footnotes
* Integrated devserver
* Available on [PyPI][]

View File

@@ -323,6 +323,18 @@ def process_markdown(
for src, dst in convertibles:
logger.debug(f"Processing {src}")
# see first if the dst actually needs re-building. for that we compare
# the mtimes and assume mtime_dst > mtime_src means that it needs not
# to be rebuilt
if os.path.exists(f"{output_dir}/{dst}"):
mtime_src = os.stat(f"{input_dir}/{src}").st_mtime
mtime_dst = os.stat(f"{output_dir}/{dst}").st_mtime
if mtime_dst >= mtime_src:
logger.debug(
"Skipping, as target exists and is newer than source."
)
continue
with open(f"{input_dir}/{src}") as fh:
body = fh.read()
@@ -396,7 +408,7 @@ def generate_feed(
)
with open(f"{output_dir}/atom.xml", "w") as fh:
feed.write(fh, encoding='utf-8')
feed.write(fh, encoding="utf8")
def generate_index(

View File

@@ -34,7 +34,6 @@ def markdown_factory() -> Markdown:
"fenced_code",
"codehilite",
"smarty",
"footnotes",
MarkdownLinkExtension(),
],
output_format="html",

View File

@@ -5,80 +5,80 @@ td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.hll { background-color: #49483e }
.c { color: #959077 } /* Comment */
.err { color: #ED007E; background-color: #1E0010 } /* Error */
.esc { color: #F8F8F2 } /* Escape */
.g { color: #F8F8F2 } /* Generic */
.k { color: #66D9EF } /* Keyword */
.l { color: #AE81FF } /* Literal */
.n { color: #F8F8F2 } /* Name */
.o { color: #FF4689 } /* Operator */
.x { color: #F8F8F2 } /* Other */
.p { color: #F8F8F2 } /* Punctuation */
.err { color: #ed007e; background-color: #1e0010 } /* Error */
.esc { color: #f8f8f2 } /* Escape */
.g { color: #f8f8f2 } /* Generic */
.k { color: #66d9ef } /* Keyword */
.l { color: #ae81ff } /* Literal */
.n { color: #f8f8f2 } /* Name */
.o { color: #ff4689 } /* Operator */
.x { color: #f8f8f2 } /* Other */
.p { color: #f8f8f2 } /* Punctuation */
.ch { color: #959077 } /* Comment.Hashbang */
.cm { color: #959077 } /* Comment.Multiline */
.cp { color: #959077 } /* Comment.Preproc */
.cpf { color: #959077 } /* Comment.PreprocFile */
.c1 { color: #959077 } /* Comment.Single */
.cs { color: #959077 } /* Comment.Special */
.gd { color: #FF4689 } /* Generic.Deleted */
.ge { color: #F8F8F2; font-style: italic } /* Generic.Emph */
.ges { color: #F8F8F2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.gr { color: #F8F8F2 } /* Generic.Error */
.gh { color: #F8F8F2 } /* Generic.Heading */
.gi { color: #A6E22E } /* Generic.Inserted */
.go { color: #66D9EF } /* Generic.Output */
.gp { color: #FF4689; font-weight: bold } /* Generic.Prompt */
.gs { color: #F8F8F2; font-weight: bold } /* Generic.Strong */
.gd { color: #ff4689 } /* Generic.Deleted */
.ge { color: #f8f8f2; font-style: italic } /* Generic.Emph */
.ges { color: #f8f8f2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.gr { color: #f8f8f2 } /* Generic.Error */
.gh { color: #f8f8f2 } /* Generic.Heading */
.gi { color: #a6e22e } /* Generic.Inserted */
.go { color: #66d9ef } /* Generic.Output */
.gp { color: #ff4689; font-weight: bold } /* Generic.Prompt */
.gs { color: #f8f8f2; font-weight: bold } /* Generic.Strong */
.gu { color: #959077 } /* Generic.Subheading */
.gt { color: #F8F8F2 } /* Generic.Traceback */
.kc { color: #66D9EF } /* Keyword.Constant */
.kd { color: #66D9EF } /* Keyword.Declaration */
.kn { color: #FF4689 } /* Keyword.Namespace */
.kp { color: #66D9EF } /* Keyword.Pseudo */
.kr { color: #66D9EF } /* Keyword.Reserved */
.kt { color: #66D9EF } /* Keyword.Type */
.ld { color: #E6DB74 } /* Literal.Date */
.m { color: #AE81FF } /* Literal.Number */
.s { color: #E6DB74 } /* Literal.String */
.na { color: #A6E22E } /* Name.Attribute */
.nb { color: #F8F8F2 } /* Name.Builtin */
.nc { color: #A6E22E } /* Name.Class */
.no { color: #66D9EF } /* Name.Constant */
.nd { color: #A6E22E } /* Name.Decorator */
.ni { color: #F8F8F2 } /* Name.Entity */
.ne { color: #A6E22E } /* Name.Exception */
.nf { color: #A6E22E } /* Name.Function */
.nl { color: #F8F8F2 } /* Name.Label */
.nn { color: #F8F8F2 } /* Name.Namespace */
.nx { color: #A6E22E } /* Name.Other */
.py { color: #F8F8F2 } /* Name.Property */
.nt { color: #FF4689 } /* Name.Tag */
.nv { color: #F8F8F2 } /* Name.Variable */
.ow { color: #FF4689 } /* Operator.Word */
.pm { color: #F8F8F2 } /* Punctuation.Marker */
.w { color: #F8F8F2 } /* Text.Whitespace */
.mb { color: #AE81FF } /* Literal.Number.Bin */
.mf { color: #AE81FF } /* Literal.Number.Float */
.mh { color: #AE81FF } /* Literal.Number.Hex */
.mi { color: #AE81FF } /* Literal.Number.Integer */
.mo { color: #AE81FF } /* Literal.Number.Oct */
.sa { color: #E6DB74 } /* Literal.String.Affix */
.sb { color: #E6DB74 } /* Literal.String.Backtick */
.sc { color: #E6DB74 } /* Literal.String.Char */
.dl { color: #E6DB74 } /* Literal.String.Delimiter */
.sd { color: #E6DB74 } /* Literal.String.Doc */
.s2 { color: #E6DB74 } /* Literal.String.Double */
.se { color: #AE81FF } /* Literal.String.Escape */
.sh { color: #E6DB74 } /* Literal.String.Heredoc */
.si { color: #E6DB74 } /* Literal.String.Interpol */
.sx { color: #E6DB74 } /* Literal.String.Other */
.sr { color: #E6DB74 } /* Literal.String.Regex */
.s1 { color: #E6DB74 } /* Literal.String.Single */
.ss { color: #E6DB74 } /* Literal.String.Symbol */
.bp { color: #F8F8F2 } /* Name.Builtin.Pseudo */
.fm { color: #A6E22E } /* Name.Function.Magic */
.vc { color: #F8F8F2 } /* Name.Variable.Class */
.vg { color: #F8F8F2 } /* Name.Variable.Global */
.vi { color: #F8F8F2 } /* Name.Variable.Instance */
.vm { color: #F8F8F2 } /* Name.Variable.Magic */
.il { color: #AE81FF } /* Literal.Number.Integer.Long */
.gt { color: #f8f8f2 } /* Generic.Traceback */
.kc { color: #66d9ef } /* Keyword.Constant */
.kd { color: #66d9ef } /* Keyword.Declaration */
.kn { color: #ff4689 } /* Keyword.Namespace */
.kp { color: #66d9ef } /* Keyword.Pseudo */
.kr { color: #66d9ef } /* Keyword.Reserved */
.kt { color: #66d9ef } /* Keyword.Type */
.ld { color: #e6db74 } /* Literal.Date */
.m { color: #ae81ff } /* Literal.Number */
.s { color: #e6db74 } /* Literal.String */
.na { color: #a6e22e } /* Name.Attribute */
.nb { color: #f8f8f2 } /* Name.Builtin */
.nc { color: #a6e22e } /* Name.Class */
.no { color: #66d9ef } /* Name.Constant */
.nd { color: #a6e22e } /* Name.Decorator */
.ni { color: #f8f8f2 } /* Name.Entity */
.ne { color: #a6e22e } /* Name.Exception */
.nf { color: #a6e22e } /* Name.Function */
.nl { color: #f8f8f2 } /* Name.Label */
.nn { color: #f8f8f2 } /* Name.Namespace */
.nx { color: #a6e22e } /* Name.Other */
.py { color: #f8f8f2 } /* Name.Property */
.nt { color: #ff4689 } /* Name.Tag */
.nv { color: #f8f8f2 } /* Name.Variable */
.ow { color: #ff4689 } /* Operator.Word */
.pm { color: #f8f8f2 } /* Punctuation.Marker */
.w { color: #f8f8f2 } /* Text.Whitespace */
.mb { color: #ae81ff } /* Literal.Number.Bin */
.mf { color: #ae81ff } /* Literal.Number.Float */
.mh { color: #ae81ff } /* Literal.Number.Hex */
.mi { color: #ae81ff } /* Literal.Number.Integer */
.mo { color: #ae81ff } /* Literal.Number.Oct */
.sa { color: #e6db74 } /* Literal.String.Affix */
.sb { color: #e6db74 } /* Literal.String.Backtick */
.sc { color: #e6db74 } /* Literal.String.Char */
.dl { color: #e6db74 } /* Literal.String.Delimiter */
.sd { color: #e6db74 } /* Literal.String.Doc */
.s2 { color: #e6db74 } /* Literal.String.Double */
.se { color: #ae81ff } /* Literal.String.Escape */
.sh { color: #e6db74 } /* Literal.String.Heredoc */
.si { color: #e6db74 } /* Literal.String.Interpol */
.sx { color: #e6db74 } /* Literal.String.Other */
.sr { color: #e6db74 } /* Literal.String.Regex */
.s1 { color: #e6db74 } /* Literal.String.Single */
.ss { color: #e6db74 } /* Literal.String.Symbol */
.bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.fm { color: #a6e22e } /* Name.Function.Magic */
.vc { color: #f8f8f2 } /* Name.Variable.Class */
.vg { color: #f8f8f2 } /* Name.Variable.Global */
.vi { color: #f8f8f2 } /* Name.Variable.Instance */
.vm { color: #f8f8f2 } /* Name.Variable.Magic */
.il { color: #ae81ff } /* Literal.Number.Integer.Long */

View File

@@ -5,9 +5,9 @@ td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.hll { background-color: #ffffcc }
.c { color: #3D7B7B; font-style: italic } /* Comment */
.err { border: 1px solid #F00 } /* Error */
.err { border: 1px solid #FF0000 } /* Error */
.k { color: #008000; font-weight: bold } /* Keyword */
.o { color: #666 } /* Operator */
.o { color: #666666 } /* Operator */
.ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
.cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
.cp { color: #9C6500 } /* Comment.Preproc */
@@ -24,34 +24,34 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left:
.gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.gt { color: #04D } /* Generic.Traceback */
.gt { color: #0044DD } /* Generic.Traceback */
.kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.kp { color: #008000 } /* Keyword.Pseudo */
.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.kt { color: #B00040 } /* Keyword.Type */
.m { color: #666 } /* Literal.Number */
.m { color: #666666 } /* Literal.Number */
.s { color: #BA2121 } /* Literal.String */
.na { color: #687822 } /* Name.Attribute */
.nb { color: #008000 } /* Name.Builtin */
.nc { color: #00F; font-weight: bold } /* Name.Class */
.no { color: #800 } /* Name.Constant */
.nd { color: #A2F } /* Name.Decorator */
.nc { color: #0000FF; font-weight: bold } /* Name.Class */
.no { color: #880000 } /* Name.Constant */
.nd { color: #AA22FF } /* Name.Decorator */
.ni { color: #717171; font-weight: bold } /* Name.Entity */
.ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
.nf { color: #00F } /* Name.Function */
.nf { color: #0000FF } /* Name.Function */
.nl { color: #767600 } /* Name.Label */
.nn { color: #00F; font-weight: bold } /* Name.Namespace */
.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.nt { color: #008000; font-weight: bold } /* Name.Tag */
.nv { color: #19177C } /* Name.Variable */
.ow { color: #A2F; font-weight: bold } /* Operator.Word */
.w { color: #BBB } /* Text.Whitespace */
.mb { color: #666 } /* Literal.Number.Bin */
.mf { color: #666 } /* Literal.Number.Float */
.mh { color: #666 } /* Literal.Number.Hex */
.mi { color: #666 } /* Literal.Number.Integer */
.mo { color: #666 } /* Literal.Number.Oct */
.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mb { color: #666666 } /* Literal.Number.Bin */
.mf { color: #666666 } /* Literal.Number.Float */
.mh { color: #666666 } /* Literal.Number.Hex */
.mi { color: #666666 } /* Literal.Number.Integer */
.mo { color: #666666 } /* Literal.Number.Oct */
.sa { color: #BA2121 } /* Literal.String.Affix */
.sb { color: #BA2121 } /* Literal.String.Backtick */
.sc { color: #BA2121 } /* Literal.String.Char */
@@ -66,9 +66,9 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left:
.s1 { color: #BA2121 } /* Literal.String.Single */
.ss { color: #19177C } /* Literal.String.Symbol */
.bp { color: #008000 } /* Name.Builtin.Pseudo */
.fm { color: #00F } /* Name.Function.Magic */
.fm { color: #0000FF } /* Name.Function.Magic */
.vc { color: #19177C } /* Name.Variable.Class */
.vg { color: #19177C } /* Name.Variable.Global */
.vi { color: #19177C } /* Name.Variable.Instance */
.vm { color: #19177C } /* Name.Variable.Magic */
.il { color: #666 } /* Literal.Number.Integer.Long */
.il { color: #666666 } /* Literal.Number.Integer.Long */

View File

@@ -151,10 +151,3 @@ header h2 {
display: inline;
font-size: 1.2rem;
}
hr {
border: none;
border-top: 1px solid var(--foreground-dim);
opacity: 0.3;
margin: 2rem 0;
}

View File

@@ -1,3 +1,3 @@
"""Version information for the blag package."""
__VERSION__ = "2.3.3"
__VERSION__ = "2.3.2"

10
debian/changelog vendored
View File

@@ -1,13 +1,3 @@
blag (2.3.3) unstable; urgency=medium
* Fixed Atom feed encoding to 'utf-8'
* Added Python 3.13 to github actions
* `requirements*.txt` files are handled by pip-tools. Updates are handled by
make update-requirements based on the dependencies described in the
pyproject.toml
-- Bastian Venthur <venthur@debian.org> Sun, 27 Apr 2025 11:42:13 +0200
blag (2.3.2) unstable; urgency=medium
* Ignore FileNotFoundError when trying to get the last modified time of

View File

@@ -10,13 +10,13 @@ authors = [
description = "blog-aware, static site generator"
keywords = ["markdown", "blag", "blog", "static site generator", "cli"]
readme = "README.md"
license-files = ["LICENSE"]
license = { file="LICENSE" }
requires-python = ">=3.10"
dynamic = ["version"]
dependencies = [
"markdown",
"feedgenerator",
"jinja2",
"markdown",
"pygments",
]
@@ -34,13 +34,13 @@ dev = [
"mkdocs",
"mkdocs-material",
"mkdocstrings[python]",
"mypy",
"twine",
"wheel",
"pytest",
"pytest-cov",
"ruff",
"twine",
"mypy",
"types-markdown",
"wheel",
]
[tool.setuptools.dynamic]

11
requirements-dev.txt Normal file
View File

@@ -0,0 +1,11 @@
build==1.2.2.post1
mkdocs==1.6.1
mkdocs-material==9.5.49
mkdocstrings[python]==0.27.0
twine==6.0.1
wheel==0.45.1
pytest==8.3.4
pytest-cov==6.0.0
ruff==0.8.4
mypy==1.14.0
types-markdown==3.7.0.20241204

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
markdown==3.7
feedgenerator==2.1.0
jinja2==3.1.5
pygments==2.18.0

37
tests/benchmark.py Normal file
View File

@@ -0,0 +1,37 @@
"""Benchmark the performance of the blag build command."""
import logging
import os
from argparse import Namespace
from pytest import LogCaptureFixture
import blag
from blag.blag import build
def test_performance(args: Namespace, caplog: LogCaptureFixture) -> None:
"""Test performance of the build command."""
caplog.set_level(logging.ERROR)
FILES = 10000
print(f"Generating {FILES} markdown files")
# create random markdown files in the content directory
with open(os.path.join(blag.__path__[0], "content", "testpage.md")) as fh:
markdown = fh.read()
for i in range(FILES):
with open(f"content/{i}.md", "w") as f:
f.write(markdown)
f.write(str(i))
from time import time
t = time()
build(args)
t_first = time() - t
t = time()
build(args)
t_second = time() - t
print(f"First run: {t_first:.2f}s, second run: {t_second:.2f}s")
print(f"Speedup: {t_first/t_second:.2f}")

View File

@@ -99,15 +99,6 @@ def test_generate_feed_with_description(cleandir: str) -> None:
assert '<content type="html">content' in feed
def test_feed_is_unicode(cleandir: str) -> None:
"""Test generate_feed."""
articles: list[tuple[str, dict[str, Any]]] = []
blag.generate_feed(articles, "build", " ", " ", " ", " ")
with open("build/atom.xml") as fh:
feed = fh.read()
assert 'encoding="utf-8"' in feed
def test_parse_args_build() -> None:
"""Test parse_args with build."""
# test default args

View File

@@ -111,17 +111,3 @@ this --- is -- a test ...
assert "mdash" not in html
assert "ndash" not in html
assert "hellip" not in html
def test_footnotes() -> None:
"""Test footnote extension."""
md = markdown_factory()
md1 = """
this is a footnote[^1]
[^1]: this is the footnotetext
"""
html, meta = convert_markdown(md, md1)
assert "<hr>" in html
assert "<ol>" in html
assert "footnotetext" in html