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

fixed for mypy --strict testing

This commit is contained in:
Bastian Venthur
2022-08-31 22:59:55 +02:00
parent 2adc7b3bd4
commit ebac0a8fc4
12 changed files with 102 additions and 88 deletions

View File

@@ -1 +1 @@
from blag.version import __VERSION__ # noqa from blag.version import __VERSION__ as __VERSION__ # noqa

View File

@@ -6,6 +6,7 @@
# remove when we don't support py38 anymore # remove when we don't support py38 anymore
from __future__ import annotations from __future__ import annotations
from typing import Any
import argparse import argparse
import os import os
import shutil import shutil
@@ -34,7 +35,7 @@ logging.basicConfig(
) )
def main(arguments: list[str] = None) -> None: def main(arguments: list[str] | None = None) -> None:
"""Main entrypoint for the CLI. """Main entrypoint for the CLI.
This method parses the CLI arguments and executes the respective This method parses the CLI arguments and executes the respective
@@ -54,7 +55,7 @@ def main(arguments: list[str] = None) -> None:
args.func(args) args.func(args)
def parse_args(args: list[str] = None) -> argparse.Namespace: def parse_args(args: list[str] | None = None) -> argparse.Namespace:
"""Parse command line arguments. """Parse command line arguments.
Parameters Parameters
@@ -175,8 +176,8 @@ def get_config(configfile: str) -> configparser.SectionProxy:
def environment_factory( def environment_factory(
template_dir: str = None, template_dir: str | None = None,
globals_: dict = None, globals_: dict[str, object] | None = None,
) -> Environment: ) -> Environment:
"""Environment factory. """Environment factory.
@@ -188,7 +189,7 @@ def environment_factory(
Parameters Parameters
---------- ----------
template_dir : str template_dir : str
globals_ : dict globals_ : dict[str, object]
Returns Returns
------- -------
@@ -278,7 +279,7 @@ def process_markdown(
output_dir: str, output_dir: str,
page_template: Template, page_template: Template,
article_template: Template, article_template: Template,
) -> tuple[list[tuple[str, dict]], list[tuple[str, dict]]]: ) -> tuple[list[tuple[str, dict[str, Any]]], list[tuple[str, dict[str, Any]]]]:
"""Process markdown files. """Process markdown files.
This method processes the convertibles, converts them to html and This method processes the convertibles, converts them to html and
@@ -298,7 +299,7 @@ def process_markdown(
Returns Returns
------- -------
articles, pages : list[tuple[str, dict]] articles, pages : list[tuple[str, dict[str, Any]]]
""" """
logger.info("Converting Markdown files...") logger.info("Converting Markdown files...")
@@ -334,7 +335,7 @@ def process_markdown(
def generate_feed( def generate_feed(
articles: list[tuple[str, dict]], articles: list[tuple[str, dict[str, Any]]],
output_dir: str, output_dir: str,
base_url: str, base_url: str,
blog_title: str, blog_title: str,
@@ -345,7 +346,7 @@ def generate_feed(
Parameters Parameters
---------- ----------
articles : list[tuple[str, dict]] articles : list[tuple[str, dict[str, Any]]]
list of relative output path and article dictionary list of relative output path and article dictionary
output_dir : str output_dir : str
where the feed is stored where the feed is stored
@@ -386,7 +387,7 @@ def generate_feed(
def generate_archive( def generate_archive(
articles: list[tuple[str, dict]], articles: list[tuple[str, dict[str, Any]]],
template: Template, template: Template,
output_dir: str, output_dir: str,
) -> None: ) -> None:
@@ -394,7 +395,7 @@ def generate_archive(
Parameters Parameters
---------- ----------
articles : list[tuple[str, dict]] articles : list[tuple[str, dict[str, Any]]]
List of articles. Each article has the destination path and a List of articles. Each article has the destination path and a
dictionary with the content. dictionary with the content.
template : jinja2.Template instance template : jinja2.Template instance
@@ -413,7 +414,7 @@ def generate_archive(
def generate_tags( def generate_tags(
articles: list[tuple[str, dict]], articles: list[tuple[str, dict[str, Any]]],
tags_template: Template, tags_template: Template,
tag_template: Template, tag_template: Template,
output_dir: str, output_dir: str,
@@ -422,7 +423,7 @@ def generate_tags(
Parameters Parameters
---------- ----------
articles : list[tuple[str, dict]] articles : list[tuple[str, dict[str, Any]]]
List of articles. Each article has the destination path and a List of articles. Each article has the destination path and a
dictionary with the content. dictionary with the content.
tags_template, tag_template : jinja2.Template instance tags_template, tag_template : jinja2.Template instance
@@ -431,11 +432,10 @@ def generate_tags(
""" """
logger.info("Generating Tag-pages.") logger.info("Generating Tag-pages.")
os.makedirs(f'{output_dir}/tags', exist_ok=True) os.makedirs(f'{output_dir}/tags', exist_ok=True)
# get tags number of occurrences # get tags number of occurrences
all_tags: dict = {} all_tags: dict[str, int] = {}
for _, context in articles: for _, context in articles:
tags = context.get('tags', []) tags: list[str] = context.get('tags', [])
for tag in tags: for tag in tags:
all_tags[tag] = all_tags.get(tag, 0) + 1 all_tags[tag] = all_tags.get(tag, 0) + 1
# sort by occurrence # sort by occurrence
@@ -448,17 +448,17 @@ def generate_tags(
fh.write(result) fh.write(result)
# get tags and archive per tag # get tags and archive per tag
all_tags = {} all_tags2: dict[str, list[dict[str, Any]]] = {}
for dst, context in articles: for dst, context in articles:
tags = context.get('tags', []) tags = context.get('tags', [])
for tag in tags: for tag in tags:
archive = all_tags.get(tag, []) archive: list[dict[str, Any]] = all_tags2.get(tag, [])
entry = context.copy() entry = context.copy()
entry['dst'] = dst entry['dst'] = dst
archive.append(entry) archive.append(entry)
all_tags[tag] = archive all_tags2[tag] = archive
for tag, archive in all_tags.items(): for tag, archive in all_tags2.items():
result = tag_template.render(dict(archive=archive, tag=tag)) result = tag_template.render(dict(archive=archive, tag=tag))
with open(f'{output_dir}/tags/{tag}.html', 'w') as fh: with open(f'{output_dir}/tags/{tag}.html', 'w') as fh:
fh.write(result) fh.write(result)

View File

@@ -41,7 +41,10 @@ def markdown_factory() -> Markdown:
return md return md
def convert_markdown(md: Markdown, markdown: str) -> tuple[str, dict]: def convert_markdown(
md: Markdown,
markdown: str,
) -> tuple[str, dict[str, str]]:
"""Convert markdown into html and extract meta data. """Convert markdown into html and extract meta data.
Some meta data is treated special: Some meta data is treated special:
@@ -56,13 +59,13 @@ def convert_markdown(md: Markdown, markdown: str) -> tuple[str, dict]:
Returns Returns
------- -------
str, dict : str, dict[str, str] :
html and metadata html and metadata
""" """
md.reset() md.reset()
content = md.convert(markdown) content = md.convert(markdown)
meta = md.Meta meta = md.Meta # type: ignore
# markdowns metadata consists as list of strings -- one item per # markdowns metadata consists as list of strings -- one item per
# line. let's convert into single strings. # line. let's convert into single strings.
@@ -90,7 +93,7 @@ class MarkdownLinkTreeprocessor(Treeprocessor):
""" """
def run(self, root: Element): def run(self, root: Element) -> Element:
for element in root.iter(): for element in root.iter():
if element.tag == 'a': if element.tag == 'a':
url = element.get('href') url = element.get('href')
@@ -102,7 +105,7 @@ class MarkdownLinkTreeprocessor(Treeprocessor):
element.set('href', converted) element.set('href', converted)
return root return root
def convert(self, url: str): def convert(self, url: str) -> str:
scheme, netloc, path, query, fragment = urlsplit(url) scheme, netloc, path, query, fragment = urlsplit(url)
logger.debug( logger.debug(
f'{url}: {scheme=} {netloc=} {path=} {query=} {fragment=}' f'{url}: {scheme=} {netloc=} {path=} {query=} {fragment=}'
@@ -120,7 +123,7 @@ class MarkdownLinkExtension(Extension):
"""markdown.extension that converts relative .md- to .html-links. """markdown.extension that converts relative .md- to .html-links.
""" """
def extendMarkdown(self, md: Markdown): def extendMarkdown(self, md: Markdown) -> None:
md.treeprocessors.register( md.treeprocessors.register(
MarkdownLinkTreeprocessor(md), 'mdlink', 0, MarkdownLinkTreeprocessor(md), 'mdlink', 0,
) )

View File

@@ -32,7 +32,7 @@ def get_input(question: str, default: str) -> str:
return reply return reply
def quickstart(args: argparse.Namespace) -> None: def quickstart(args: argparse.Namespace | None) -> None:
"""Quickstart. """Quickstart.
This method asks the user some questions and generates a This method asks the user some questions and generates a

View File

@@ -10,6 +10,7 @@ exclude = venv,build,docs
[mypy] [mypy]
files = blag,tests files = blag,tests
strict = True
[mypy-feedgenerator.*] [mypy-feedgenerator.*]
ignore_missing_imports = True ignore_missing_imports = True

View File

@@ -1,13 +1,16 @@
from argparse import Namespace
from typing import Iterator, Callable
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
import os import os
import pytest import pytest
from jinja2 import Environment, Template
from blag import blag from blag import blag
@pytest.fixture @pytest.fixture
def environment(): def environment() -> Iterator[Environment]:
site = { site = {
'base_url': 'site base_url', 'base_url': 'site base_url',
'title': 'site title', 'title': 'site title',
@@ -19,32 +22,32 @@ def environment():
@pytest.fixture @pytest.fixture
def page_template(environment): def page_template(environment: Environment) -> Iterator[Template]:
yield environment.get_template('page.html') yield environment.get_template('page.html')
@pytest.fixture @pytest.fixture
def article_template(environment): def article_template(environment: Environment) -> Iterator[Template]:
yield environment.get_template('article.html') yield environment.get_template('article.html')
@pytest.fixture @pytest.fixture
def archive_template(environment): def archive_template(environment: Environment) -> Iterator[Template]:
yield environment.get_template('archive.html') yield environment.get_template('archive.html')
@pytest.fixture @pytest.fixture
def tags_template(environment): def tags_template(environment: Environment) -> Iterator[Template]:
yield environment.get_template('tags.html') yield environment.get_template('tags.html')
@pytest.fixture @pytest.fixture
def tag_template(environment): def tag_template(environment: Environment) -> Iterator[Template]:
yield environment.get_template('tag.html') yield environment.get_template('tag.html')
@pytest.fixture @pytest.fixture
def cleandir(): def cleandir() -> Iterator[str]:
"""Create a temporary workind directory and cwd. """Create a temporary workind directory and cwd.
""" """
@@ -70,14 +73,9 @@ author = a. u. thor
@pytest.fixture @pytest.fixture
def args(cleandir): def args(cleandir: Callable[[], Iterator[str]]) -> Iterator[Namespace]:
class NameSpace: args = Namespace(
def __init__(self, **kwargs):
for name in kwargs:
setattr(self, name, kwargs[name])
args = NameSpace(
input_dir='content', input_dir='content',
output_dir='build', output_dir='build',
static_dir='static', static_dir='static',

View File

@@ -1,37 +1,41 @@
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
import os import os
from datetime import datetime from datetime import datetime
from typing import Any
from argparse import Namespace
import pytest import pytest
from pytest import CaptureFixture, LogCaptureFixture
from jinja2 import Template
from blag import __VERSION__
from blag import blag from blag import blag
def test_generate_feed(cleandir): def test_generate_feed(cleandir: str) -> None:
articles = [] articles: list[tuple[str, dict[str, Any]]] = []
blag.generate_feed(articles, 'build', ' ', ' ', ' ', ' ') blag.generate_feed(articles, 'build', ' ', ' ', ' ', ' ')
assert os.path.exists('build/atom.xml') assert os.path.exists('build/atom.xml')
def test_feed(cleandir): def test_feed(cleandir: str) -> None:
articles = [ articles: list[tuple[str, dict[str, Any]]] = [
[ (
'dest1.html', 'dest1.html',
{ {
'title': 'title1', 'title': 'title1',
'date': datetime(2019, 6, 6), 'date': datetime(2019, 6, 6),
'content': 'content1', 'content': 'content1',
} }
], ),
[ (
'dest2.html', 'dest2.html',
{ {
'title': 'title2', 'title': 'title2',
'date': datetime(1980, 5, 9), 'date': datetime(1980, 5, 9),
'content': 'content2', 'content': 'content2',
} }
], ),
] ]
blag.generate_feed(articles, 'build', 'https://example.com/', blag.generate_feed(articles, 'build', 'https://example.com/',
@@ -60,10 +64,10 @@ def test_feed(cleandir):
assert '<link href="https://example.com/dest2.html"' in feed assert '<link href="https://example.com/dest2.html"' in feed
def test_generate_feed_with_description(cleandir): def test_generate_feed_with_description(cleandir: str) -> None:
# if a description is provided, it will be used as the summary in # if a description is provided, it will be used as the summary in
# the feed, otherwise we simply use the title of the article # the feed, otherwise we simply use the title of the article
articles = [[ articles: list[tuple[str, dict[str, Any]]] = [(
'dest.html', 'dest.html',
{ {
'title': 'title', 'title': 'title',
@@ -71,7 +75,7 @@ def test_generate_feed_with_description(cleandir):
'date': datetime(2019, 6, 6), 'date': datetime(2019, 6, 6),
'content': 'content', 'content': 'content',
} }
]] )]
blag.generate_feed(articles, 'build', ' ', ' ', ' ', ' ') blag.generate_feed(articles, 'build', ' ', ' ', ' ', ' ')
with open('build/atom.xml') as fh: with open('build/atom.xml') as fh:
@@ -83,7 +87,7 @@ def test_generate_feed_with_description(cleandir):
assert '<content type="html">content' in feed assert '<content type="html">content' in feed
def test_parse_args_build(): def test_parse_args_build() -> None:
# test default args # test default args
args = blag.parse_args(['build']) args = blag.parse_args(['build'])
assert args.input_dir == 'content' assert args.input_dir == 'content'
@@ -116,7 +120,7 @@ def test_parse_args_build():
assert args.static_dir == 'foo' assert args.static_dir == 'foo'
def test_get_config(): def test_get_config() -> None:
config = """ config = """
[main] [main]
base_url = https://example.com/ base_url = https://example.com/
@@ -166,8 +170,8 @@ author = a. u. thor
assert config_parsed['base_url'] == 'https://example.com/' assert config_parsed['base_url'] == 'https://example.com/'
def test_environment_factory(): def test_environment_factory() -> None:
globals_ = { globals_: dict[str, object] = {
'foo': 'bar', 'foo': 'bar',
'test': 'me' 'test': 'me'
} }
@@ -176,7 +180,11 @@ def test_environment_factory():
assert env.globals['test'] == 'me' assert env.globals['test'] == 'me'
def test_process_markdown(cleandir, page_template, article_template): def test_process_markdown(
cleandir: str,
page_template: Template,
article_template: Template,
) -> None:
page1 = """\ page1 = """\
title: some page title: some page
@@ -202,10 +210,9 @@ foo bar
convertibles = [] convertibles = []
for i, txt in enumerate((page1, article1, article2)): for i, txt in enumerate((page1, article1, article2)):
i = str(i) with open(f'content/{str(i)}', 'w') as fh:
with open(f'content/{i}', 'w') as fh:
fh.write(txt) fh.write(txt)
convertibles.append([i, i]) convertibles.append((str(i), str(i)))
articles, pages = blag.process_markdown( articles, pages = blag.process_markdown(
convertibles, convertibles,
@@ -230,7 +237,7 @@ foo bar
assert 'content' in context assert 'content' in context
def test_build(args): def test_build(args: Namespace) -> None:
page1 = """\ page1 = """\
title: some page title: some page
@@ -259,10 +266,9 @@ foo bar
# write some convertibles # write some convertibles
convertibles = [] convertibles = []
for i, txt in enumerate((page1, article1, article2)): for i, txt in enumerate((page1, article1, article2)):
i = str(i) with open(f'{args.input_dir}/{str(i)}.md', 'w') as fh:
with open(f'{args.input_dir}/{i}.md', 'w') as fh:
fh.write(txt) fh.write(txt)
convertibles.append([i, i]) convertibles.append((str(i), str(i)))
# some static files # some static files
with open(f'{args.static_dir}/test', 'w') as fh: with open(f'{args.static_dir}/test', 'w') as fh:
@@ -291,21 +297,21 @@ foo bar
assert os.path.exists(f'{args.output_dir}/tags/bar.html') assert os.path.exists(f'{args.output_dir}/tags/bar.html')
def test_main(cleandir): def test_main(cleandir: str) -> None:
blag.main(['build']) blag.main(['build'])
def test_cli_version(capsys): def test_cli_version(capsys: CaptureFixture[str]) -> None:
with pytest.raises(SystemExit) as ex: with pytest.raises(SystemExit) as ex:
blag.main(['--version']) blag.main(['--version'])
# normal system exit # normal system exit
assert ex.value.code == 0 assert ex.value.code == 0
# proper version reported # proper version reported
out, _ = capsys.readouterr() out, _ = capsys.readouterr()
assert blag.__VERSION__ in out assert __VERSION__ in out
def test_cli_verbose(cleandir, caplog): def test_cli_verbose(cleandir: str, caplog: LogCaptureFixture) -> None:
blag.main(['build']) blag.main(['build'])
assert 'DEBUG' not in caplog.text assert 'DEBUG' not in caplog.text

View File

@@ -1,12 +1,13 @@
import time import time
import threading import threading
from argparse import Namespace
import pytest import pytest
from blag import devserver from blag import devserver
def test_get_last_modified(cleandir): def test_get_last_modified(cleandir: str) -> None:
# take initial time # take initial time
t1 = devserver.get_last_modified(['content']) t1 = devserver.get_last_modified(['content'])
@@ -24,7 +25,7 @@ def test_get_last_modified(cleandir):
assert t2 == t3 assert t2 == t3
def test_autoreload_builds_immediately(args): def test_autoreload_builds_immediately(args: Namespace) -> None:
# create a dummy file that can be build # create a dummy file that can be build
with open('content/test.md', 'w') as fh: with open('content/test.md', 'w') as fh:
fh.write('boo') fh.write('boo')
@@ -45,7 +46,7 @@ def test_autoreload_builds_immediately(args):
@pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") # noqa @pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") # noqa
def test_autoreload(args): def test_autoreload(args: Namespace) -> None:
t = threading.Thread(target=devserver.autoreload, t = threading.Thread(target=devserver.autoreload,
args=(args, ), args=(args, ),
daemon=True,) daemon=True,)

View File

@@ -1,4 +1,5 @@
from datetime import datetime from datetime import datetime
from typing import Any
import pytest import pytest
import markdown import markdown
@@ -26,7 +27,7 @@ from blag.markdown import convert_markdown, markdown_factory
('[test][]\n[test]: /a/test.md', '/a/test.html'), ('[test][]\n[test]: /a/test.md', '/a/test.html'),
('[test][]\n[test]: /a/test.md "test"', '/a/test.html'), ('[test][]\n[test]: /a/test.md "test"', '/a/test.html'),
]) ])
def test_convert_markdown_links(input_, expected): def test_convert_markdown_links(input_: str, expected: str) -> None:
md = markdown_factory() md = markdown_factory()
html, _ = convert_markdown(md, input_) html, _ = convert_markdown(md, input_)
assert expected in html assert expected in html
@@ -40,7 +41,7 @@ def test_convert_markdown_links(input_, expected):
# no path # no path
('[test]()', ''), ('[test]()', ''),
]) ])
def test_dont_convert_normal_links(input_, expected): def test_dont_convert_normal_links(input_: str, expected: str) -> None:
md = markdown_factory() md = markdown_factory()
html, _ = convert_markdown(md, input_) html, _ = convert_markdown(md, input_)
assert expected in html assert expected in html
@@ -54,18 +55,18 @@ def test_dont_convert_normal_links(input_, expected):
('date: 2020-01-01 12:10', {'date': ('date: 2020-01-01 12:10', {'date':
datetime(2020, 1, 1, 12, 10).astimezone()}), datetime(2020, 1, 1, 12, 10).astimezone()}),
]) ])
def test_convert_metadata(input_, expected): def test_convert_metadata(input_: str, expected: dict[str, Any]) -> None:
md = markdown_factory() md = markdown_factory()
_, meta = convert_markdown(md, input_) _, meta = convert_markdown(md, input_)
assert expected == meta assert expected == meta
def test_markdown_factory(): def test_markdown_factory() -> None:
md = markdown_factory() md = markdown_factory()
assert isinstance(md, markdown.Markdown) assert isinstance(md, markdown.Markdown)
def test_smarty(): def test_smarty() -> None:
md = markdown_factory() md = markdown_factory()
md1 = """ md1 = """
@@ -79,7 +80,7 @@ this --- is -- a test ...
assert 'hellip' in html assert 'hellip' in html
def test_smarty_code(): def test_smarty_code() -> None:
md = markdown_factory() md = markdown_factory()
md1 = """ md1 = """

View File

@@ -1,19 +1,21 @@
from pytest import MonkeyPatch
from blag.quickstart import get_input, quickstart from blag.quickstart import get_input, quickstart
def test_get_input_default_answer(monkeypatch): def test_get_input_default_answer(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr('builtins.input', lambda x: '') monkeypatch.setattr('builtins.input', lambda x: '')
answer = get_input("foo", "bar") answer = get_input("foo", "bar")
assert answer == 'bar' assert answer == 'bar'
def test_get_input(monkeypatch): def test_get_input(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr('builtins.input', lambda x: 'baz') monkeypatch.setattr('builtins.input', lambda x: 'baz')
answer = get_input("foo", "bar") answer = get_input("foo", "bar")
assert answer == 'baz' assert answer == 'baz'
def test_quickstart(cleandir, monkeypatch): def test_quickstart(cleandir: str, monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr('builtins.input', lambda x: 'foo') monkeypatch.setattr('builtins.input', lambda x: 'foo')
quickstart(None) quickstart(None)
with open('config.ini', 'r') as fh: with open('config.ini', 'r') as fh:

View File

@@ -1,7 +1,9 @@
import datetime import datetime
from jinja2 import Template
def test_page(page_template):
def test_page(page_template: Template) -> None:
ctx = { ctx = {
'content': 'this is the content', 'content': 'this is the content',
'title': 'this is the title', 'title': 'this is the title',
@@ -11,7 +13,7 @@ def test_page(page_template):
assert 'this is the title' in result assert 'this is the title' in result
def test_article(article_template): def test_article(article_template: Template) -> None:
ctx = { ctx = {
'content': 'this is the content', 'content': 'this is the content',
'title': 'this is the title', 'title': 'this is the title',
@@ -23,7 +25,7 @@ def test_article(article_template):
assert '1980-05-09' in result assert '1980-05-09' in result
def test_archive(archive_template): def test_archive(archive_template: Template) -> None:
entry = { entry = {
'title': 'this is a title', 'title': 'this is a title',
'dst': 'https://example.com/link', 'dst': 'https://example.com/link',
@@ -41,7 +43,7 @@ def test_archive(archive_template):
assert 'https://example.com/link' in result assert 'https://example.com/link' in result
def test_tags(tags_template): def test_tags(tags_template: Template) -> None:
tags = [('foo', 42)] tags = [('foo', 42)]
ctx = { ctx = {
'tags': tags, 'tags': tags,
@@ -54,7 +56,7 @@ def test_tags(tags_template):
assert '42' in result assert '42' in result
def test_tag(tag_template): def test_tag(tag_template: Template) -> None:
entry = { entry = {
'title': 'this is a title', 'title': 'this is a title',
'dst': 'https://example.com/link', 'dst': 'https://example.com/link',

View File

@@ -1,5 +1,5 @@
import blag import blag
def test_version(): def test_version() -> None:
assert isinstance(blag.__VERSION__, str) assert isinstance(blag.__VERSION__, str)