diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..f076753 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,8 @@ +version: 2 + +python: + version: 3.8 + install: + - requirements: requirements.txt + - requirements: requirements-dev.txt + - path: . diff --git a/Makefile b/Makefile index 8afdc92..670fcbf 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,10 @@ PY = python3 VENV = venv BIN=$(VENV)/bin +DOCS_SRC = docs +DOCS_OUT = $(DOCS_SRC)/_build + + ifeq ($(OS), Windows_NT) BIN=$(VENV)/Scripts PY=python @@ -18,25 +22,30 @@ $(VENV): requirements.txt requirements-dev.txt setup.py $(BIN)/pip install -e . touch $(VENV) +.PHONY: test test: $(VENV) $(BIN)/pytest -.PHONY: test +.PHONY: lint lint: $(VENV) $(BIN)/flake8 -.PHONY: lint +.PHONY: release release: $(VENV) rm -rf dist $(BIN)/python setup.py sdist bdist_wheel $(BIN)/twine upload dist/* -.PHONY: release +.PHONY: docs +docs: $(VENV) + $(BIN)/sphinx-build $(DOCS_SRC) $(DOCS_OUT) + +.PHONY: clean clean: rm -rf build dist *.egg-info rm -rf $(VENV) + rm -rf $(DOCS_OUT) find . -type f -name *.pyc -delete find . -type d -name __pycache__ -delete # coverage rm -rf htmlcov .coverage -.PHONY: clean diff --git a/README.md b/README.md index 5f1c28a..01701b4 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ # blag -blag is a blog-aware, static site generator, written in [Python][]. An example -"deployment" can be found [here][venthur.de]. +blag is a blog-aware, static site generator, written in [Python][]. + +* an example "deployment" can be found [here][venthur.de] +* online [documentation][] is available on readthedocs blag is named after [the blag of the webcomic xkcd][blagxkcd]. [python]: https://python.org [blagxkcd]: https://blog.xkcd.com [venthur.de]: https://venthur.de +[documentation]: https://blag.readthedocs.io/en/latest/ ## Features @@ -17,12 +20,14 @@ blag is named after [the blag of the webcomic xkcd][blagxkcd]. * Generation of Atom feeds for blog content * Fenced code blocks and syntax highlighting using [Pygments][] * Integrated devserver +* Available on [PyPI][] blag runs on Linux, Mac and Windows and requires Python >= 3.8 [markdown]: https://daringfireball.net/projects/markdown/ [jinja2]: https://palletsprojects.com/p/jinja/ [pygments]: https://pygments.org/ +[pypi]: https://pypi.org/project/blag/ ## Quickstart diff --git a/blag/blag.py b/blag/blag.py index 7c3bfaa..e9c6ec8 100644 --- a/blag/blag.py +++ b/blag/blag.py @@ -1,13 +1,9 @@ #!/usr/bin/env python3 -"""Small static site generator. +"""blag's core methods. """ - -__author__ = "Bastian Venthur " - - import argparse import os import shutil @@ -29,6 +25,17 @@ logging.basicConfig( def main(args=None): + """Main entrypoint for the CLI. + + This method parses the CLI arguments and executes the respective + commands. + + Parameters + ---------- + args : list[str] + optional parameters, used for testing + + """ args = parse_args(args) args.func(args) @@ -36,14 +43,14 @@ def main(args=None): def parse_args(args=None): """Parse command line arguments. - Paramters - --------- + Parameters + ---------- args : List[str] optional parameters, used for testing Returns ------- - args + arparse.Namespace """ parser = argparse.ArgumentParser() @@ -113,6 +120,19 @@ def parse_args(args=None): def get_config(configfile): + """Load site configuration from configfile. + + Parameters + ---------- + configfile : str + path to configuration file + + + Returns + ------- + dict + + """ config = configparser.ConfigParser() config.read(configfile) # check for the mandatory options @@ -161,6 +181,16 @@ def environment_factory(template_dir=None, globals_=None): def build(args): + """Build the site. + + This is blag's main method that builds the site, generates the feed + etc. + + Parameters + ---------- + args : argparse.Namespace + + """ os.makedirs(f'{args.output_dir}', exist_ok=True) convertibles = [] for root, dirnames, filenames in os.walk(args.input_dir): @@ -322,6 +352,17 @@ def generate_feed( def generate_archive(articles, template, output_dir): + """Generate the archive page. + + Parameters + ---------- + articles : list[list[str, dict]] + List of articles. Each article has the destination path and a + dictionary with the content. + template : jinja2.Template instance + output_dir : str + + """ archive = [] for dst, context in articles: entry = context.copy() @@ -334,6 +375,17 @@ def generate_archive(articles, template, output_dir): def generate_tags(articles, tags_template, tag_template, output_dir): + """Generate the tags page. + + Parameters + ---------- + articles : list[list[str, dict]] + List of articles. Each article has the destination path and a + dictionary with the content. + tags_template, tag_template : jinja2.Template instance + output_dir : str + + """ logger.info("Generating Tag-pages.") os.makedirs(f'{output_dir}/tags', exist_ok=True) @@ -368,6 +420,16 @@ def generate_tags(articles, tags_template, tag_template, output_dir): def quickstart(args): + """Quickstart. + + This method asks the user some questions and generates a + configuration file that is needed in order to run blag. + + Parameters + ---------- + args : argparse.Namespace + + """ base_url = input("Hostname (and path) to the root? " "[https://example.com/]: ") title = input("Title of your website? ") diff --git a/blag/devserver.py b/blag/devserver.py index 56fda8c..2160709 100644 --- a/blag/devserver.py +++ b/blag/devserver.py @@ -1,3 +1,11 @@ +"""Development Server. + +This module provides functionality for blag's development server. It +automatically detects changes in certain directories and rebuilds the +site if necessary. + +""" + import os import logging import time @@ -24,7 +32,8 @@ def get_last_modified(dirs): Returns ------- - int : most recent modification time found in `dirs` + int + most recent modification time found in `dirs` """ last_mtime = 0 @@ -40,6 +49,17 @@ def get_last_modified(dirs): def autoreload(args): + """Start the autoreloader. + + This method monitors the given directories for changes (i.e. the + last modified time). If the last modified time has changed, a + rebuild is triggered. + + Parameters + ---------- + args : argparse.Namespace + + """ dirs = [args.input_dir, args.template_dir, args.static_dir] logger.info(f'Monitoring {dirs} for changes...') last_mtime = get_last_modified(dirs) @@ -53,6 +73,13 @@ def autoreload(args): def serve(args): + """Start the webserver and the autoreloader. + + Parameters + ---------- + args : arparse.Namespace + + """ httpd = HTTPServer(('', 8000), partial(SimpleHTTPRequestHandler, directory=args.output_dir)) proc = multiprocessing.Process(target=autoreload, args=(args,)) diff --git a/blag/markdown.py b/blag/markdown.py index 5144eb9..4dab425 100644 --- a/blag/markdown.py +++ b/blag/markdown.py @@ -1,3 +1,10 @@ +"""Markdown Processing. + +This module contains the methods responsible for blag's markdown +processing. + +""" + from datetime import datetime import logging from urllib.parse import urlsplit, urlunsplit @@ -34,6 +41,11 @@ def markdown_factory(): def convert_markdown(md, markdown): """Convert markdown into html and extract meta data. + Some meta data is treated special: + * `date` is converted into datetime with local timezone + * `tags` is interpreted as a comma-separeted list of strings. + All strings are stripped and converted to lower case. + Parameters ---------- md : markdown.Markdown instance @@ -98,6 +110,9 @@ class MarkdownLinkTreeprocessor(Treeprocessor): class MarkdownLinkExtension(Extension): + """markdown.extension that converts relative .md- to .html-links. + + """ def extendMarkdown(self, md): md.treeprocessors.register( MarkdownLinkTreeprocessor(md), 'mdlink', 0, diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..6a6e941 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,11 @@ +API +=== + +.. autosummary:: + :toctree: api + + blag.__init__ + blag.version + blag.blag + blag.markdown + blag.devserver diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..4cb1944 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,69 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +import blag + + +# -- Project information ----------------------------------------------------- + +project = 'blag' +copyright = '2021, Bastian Venthur' +author = 'Bastian Venthur' + +# The full version, including alpha/beta/rc tags +release = blag.__VERSION__ + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autosummary', + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +autodoc_default_options = { + 'members': True, + 'undoc-members': True, + 'private-members': True, + 'special-members': True, +} + +autosummary_generate = True diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..7b99e96 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,53 @@ +.. blag documentation master file, created by + sphinx-quickstart on Sun Mar 21 13:39:00 2021. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to blag! +================ + +blag is a blog-aware, static site generator, written in Python_. An example +"deployment" can be found here_. + +blag is named after the blag of the webcomic xkcd_. + +.. _python: https://python.org +.. _xkcd: https://blog.xkcd.com +.. _here: https://venthur.de + + +Features +-------- + +* Write content in Markdown_ +* Theming support using Jinja2_ templates +* Generation of Atom feeds for blog content +* Fenced code blocks and syntax highlighting using Pygments_ +* Integrated devserver +* Available on PyPI_ + +blag runs on Linux, Mac and Windows and requires Python >= 3.8 + +.. _markdown: https://daringfireball.net/projects/markdown/ +.. _jinja2: https://palletsprojects.com/p/jinja/ +.. _pygments: https://pygments.org/ +.. _pypi: https://pypi.org/project/blag/ + + +Documentation +============= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + quickstart.rst + api.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/quickstart.rst b/docs/quickstart.rst new file mode 100644 index 0000000..21c8573 --- /dev/null +++ b/docs/quickstart.rst @@ -0,0 +1,29 @@ +Quickstart +========== + +Install blag from PyPI_:: + + $ pip install blag + +.. _pypi: https://pypi.org/project/blag/ + +Run blag's quickstart command to create the configuration needed:: + + $ blag quickstart + +Create some content:: + + $ mkdir content + $ edit content/hello-world.md + +Generate the website:: + + $ blag build + +By default, blag will search for content in ``content`` and the output will be +generated in ``build``. Those directories can be changed via command line +arguments. See:: + + $ blag --help + + diff --git a/requirements-dev.txt b/requirements-dev.txt index c91dd77..0c04fa0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ +sphinx==3.5.3 twine==3.4.1 wheel==0.36.2 pytest==6.2.2 diff --git a/setup.cfg b/setup.cfg index b99f230..681fad8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,4 +6,4 @@ addopts = --cov-report=term-missing:skip-covered [flake8] -exclude = venv,build +exclude = venv,build,docs