Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions blag/blag.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@

import blag
from blag.devserver import serve
from blag.markdown import convert_markdown, markdown_factory
from blag.markdown import (
MarkdownExtensionLoadError,
check_extensions,
convert_markdown,
markdown_factory,
)
from blag.quickstart import quickstart
from blag.version import __VERSION__

Expand Down Expand Up @@ -242,6 +247,12 @@ def build(args: argparse.Namespace) -> None:
shutil.copytree(args.static_dir, args.output_dir, dirs_exist_ok=True)

config = get_config("config.ini")
extra_extensions = get_extra_extensions(config)
try:
check_extensions(extra_extensions)
except MarkdownExtensionLoadError:
logger.error(f"Problem loading extensions: {extra_extensions}")
sys.exit(1)

env = environment_factory(args.template_dir, dict(site=config))

Expand All @@ -268,6 +279,7 @@ def build(args: argparse.Namespace) -> None:
args.output_dir,
page_template,
article_template,
extra_extensions,
)

generate_feed(
Expand All @@ -283,12 +295,21 @@ def build(args: argparse.Namespace) -> None:
generate_tags(articles, tags_template, tag_template, args.output_dir)


def get_extra_extensions(config: configparser.SectionProxy) -> list[str]:
"""Parse named extensions from config and return a list."""
if extensions := config.get("extensions"):
return list(set(extensions.split(",")))
else:
return []


def process_markdown(
convertibles: list[tuple[str, str]],
input_dir: str,
output_dir: str,
page_template: Template,
article_template: Template,
extra_extensions: list[str] = [],
) -> tuple[list[tuple[str, dict[str, Any]]], list[tuple[str, dict[str, Any]]]]:
"""Process markdown files.

Expand All @@ -308,6 +329,9 @@ def process_markdown(
output_dir
page_template, archive_template
templates for pages and articles
extra_extensions
additional supported markdown extensions.
see: https://python-markdown.github.io/extensions/

Returns
-------
Expand All @@ -316,7 +340,7 @@ def process_markdown(

"""
logger.info("Converting Markdown files...")
md = markdown_factory()
md = markdown_factory(extra_extensions)

articles = []
pages = []
Expand Down Expand Up @@ -396,7 +420,7 @@ def generate_feed(
)

with open(f"{output_dir}/atom.xml", "w") as fh:
feed.write(fh, encoding='utf-8')
feed.write(fh, encoding="utf-8")


def generate_index(
Expand Down
2 changes: 1 addition & 1 deletion blag/devserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def get_last_modified(dirs: list[str]) -> float:
return last_mtime


def autoreload(args: argparse.Namespace, wait: int=1) -> NoReturn:
def autoreload(args: argparse.Namespace, wait: int = 1) -> NoReturn:
"""Start the autoreloader.

This method monitors the given directories for changes (i.e. the
Expand Down
32 changes: 26 additions & 6 deletions blag/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@
logger = logging.getLogger(__name__)


def markdown_factory() -> Markdown:
class MarkdownExtensionLoadError(Exception):
"""An error thrown when extensions cannot be loaded."""

pass


def markdown_factory(extra_extensions: list[str] = []) -> Markdown:
"""Create a Markdown instance.

This method exists only to ensure we use the same Markdown instance
Expand All @@ -28,14 +34,20 @@ def markdown_factory() -> Markdown:
markdown.Markdown

"""
default_extensions = [
"codehilite",
"fenced_code",
"footnotes",
"meta",
"smarty",
]
# Ensure the extra don't duplicate the default
all_extensions = list(set(default_extensions + extra_extensions))

md = Markdown(
extensions=[
"meta",
"fenced_code",
"codehilite",
"smarty",
"footnotes",
MarkdownLinkExtension(),
*all_extensions,
],
output_format="html",
)
Expand Down Expand Up @@ -132,3 +144,11 @@ def extendMarkdown(self, md: Markdown) -> None:
"mdlink",
0,
)


def check_extensions(extensions: list[str]) -> None:
"""Attempt to load the named extensions."""
try:
Markdown(extensions=extensions)
except ModuleNotFoundError:
raise MarkdownExtensionLoadError
17 changes: 17 additions & 0 deletions docs/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,23 @@ freely in you templates. However, some metadata elements are treated special:
header and in the atom feed.


### Markdown Extensions

`blag` loads several Markdown extensions by default, including `meta`,
`fenced_code`, `codehilite`, and `smarty`. If you wish to use additional
extensions, you may add a line to your configuration file as a comma-separated
list, like this:

```
extensions = footnotes,tables
```

You can view the full list of officially supported extensions for the
Python-Markdown libary here:

<https://python-markdown.github.io/extensions/#officially-supported-extensions>


## Devserver

blag provides a devserver which you can use for local web-development. The
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@ def args(cleandir: Callable[[], Iterator[str]]) -> Iterator[Namespace]:
output_dir="build",
static_dir="static",
template_dir="templates",
extensions=["footnotes"],
)
yield args
39 changes: 39 additions & 0 deletions tests/test_blag.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,45 @@ def test_get_config() -> None:
assert config_parsed["base_url"] == "https://example.com/"


def test_get_extra_extensions() -> None:
"""Test parsing extensions string from config."""
# No extensions in config
config = """
[main]
base_url = https://example.com/
title = title
description = description
author = a. u. thor
"""
with TemporaryDirectory() as dir:
configfile = f"{dir}/config.ini"
with open(configfile, "w") as fh:
fh.write(config)

config_parsed = blag.get_config(configfile)
extra_extensions = blag.get_extra_extensions(config_parsed)
assert isinstance(extra_extensions, list)
assert len(extra_extensions) == 0

# Some extensions in the config
config = """
[main]
base_url = https://example.com/
title = title
description = description
author = a. u. thor
extensions = footnotes,tables
"""
with TemporaryDirectory() as dir:
configfile = f"{dir}/config.ini"
with open(configfile, "w") as fh:
fh.write(config)
config_parsed = blag.get_config(configfile)
extra_extensions = blag.get_extra_extensions(config_parsed)
assert "footnotes" in extra_extensions
assert "tables" in extra_extensions


def test_environment_factory(cleandir: str) -> None:
"""Test environment_factory."""
globals_: dict[str, object] = {"foo": "bar", "test": "me"}
Expand Down
16 changes: 15 additions & 1 deletion tests/test_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
import markdown
import pytest

from blag.markdown import convert_markdown, markdown_factory
from blag.markdown import (
MarkdownExtensionLoadError,
check_extensions,
convert_markdown,
markdown_factory,
)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -125,3 +130,12 @@ def test_footnotes() -> None:
assert "<hr>" in html
assert "<ol>" in html
assert "footnotetext" in html

def test_check_extensions() -> None:
"""Test that bad extensions throw an error, good extensions don't."""
ok_extensions = ["footnotes", "toc"]
check_extensions(ok_extensions)

bad_extensions = ["foobar", "bazbom"]
with pytest.raises(MarkdownExtensionLoadError):
check_extensions(bad_extensions)