1
0
mirror of https://github.com/venthur/blag.git synced 2025-11-26 05:02:58 +00:00

Compare commits

..

21 Commits
0.0.2 ... 0.0.4

Author SHA1 Message Date
Bastian Venthur
2d3bb0c0f3 bumped version 2021-03-19 11:37:06 +01:00
Bastian Venthur
01b203ff5c updated requirements 2021-03-19 11:36:34 +01:00
Bastian Venthur
6f70f7ca93 Merge pull request #1 from venthur/devserver
Devserver
2021-03-19 11:30:55 +01:00
Bastian Venthur
7f832a1445 added missing devserver.py m( 2021-03-19 11:24:58 +01:00
Bastian Venthur
aac2d70fed updated readme 2021-03-19 11:22:56 +01:00
Bastian Venthur
dc6547290b added devserver 2021-03-19 11:22:11 +01:00
Bastian Venthur
b077e22984 WIP 2021-03-19 09:20:34 +01:00
Bastian Venthur
af5825b412 added info messages and set loglevel to info 2021-03-17 10:41:49 +01:00
Bastian Venthur
7d69c37032 Use description tag for Atom feed if provided 2021-03-17 10:39:26 +01:00
Bastian Venthur
dbd1679038 remove dist directory before building next version 2021-03-12 21:11:19 +01:00
Bastian Venthur
7eafaba49a updated readme 2021-03-12 21:06:54 +01:00
Bastian Venthur
a98b2071fd updated readme 2021-03-12 20:42:15 +01:00
Bastian Venthur
98e124dfc1 rely on makefile again 2021-03-12 20:15:24 +01:00
Bastian Venthur
3fe9a1ae16 simplify venv commands 2021-03-12 19:44:21 +01:00
Bastian Venthur
12c3315808 docyment PY variable 2021-03-12 19:44:12 +01:00
Bastian Venthur
7decb8fddd Try python for windows 2021-03-12 19:28:45 +01:00
Bastian Venthur
7cb373af94 try python 2021-03-12 16:58:12 +01:00
Bastian Venthur
65fdb3405a another attempt 2021-03-12 16:54:25 +01:00
Bastian Venthur
6a57641ec2 fixed usage of PY 2021-03-12 16:44:32 +01:00
Bastian Venthur
96e2eb76d4 bleh 2021-03-12 16:43:23 +01:00
Bastian Venthur
59d7d2bb71 test makefile again 2021-03-12 16:28:48 +01:00
11 changed files with 276 additions and 37 deletions

View File

@@ -28,15 +28,10 @@ jobs:
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run tests - name: Run tests
run: | run: |
pytest make test
- name: Run linter - name: Run linter
run: | run: |
flake8 make lint

View File

@@ -1,31 +1,34 @@
# system python interpreter. used only to create virtual environment
PY = python3
VENV = venv VENV = venv
BIN=$(VENV)/bin
ifeq ($(OS), Windows_NT) ifeq ($(OS), Windows_NT)
BIN=$(VENV)/Scripts BIN=$(VENV)/Scripts
else PY=python
BIN=$(VENV)/bin
endif endif
all: lint test all: lint test
$(VENV): requirements.txt requirements-dev.txt setup.py $(VENV): requirements.txt requirements-dev.txt setup.py
python3 -m venv $(VENV) $(PY) -m venv $(VENV)
$(BIN)/python3 -m pip install --upgrade -r requirements.txt $(BIN)/pip install --upgrade -r requirements.txt
$(BIN)/python3 -m pip install --upgrade -r requirements-dev.txt $(BIN)/pip install --upgrade -r requirements-dev.txt
$(BIN)/python3 -m pip install -e . $(BIN)/pip install -e .
touch $(VENV) touch $(VENV)
test: $(VENV) test: $(VENV)
$(BIN)/python3 -m pytest $(BIN)/pytest
.PHONY: test .PHONY: test
lint: $(VENV) lint: $(VENV)
$(BIN)/python3 -m flake8 $(BIN)/flake8
.PHONY: lint .PHONY: lint
release: $(VENV) release: $(VENV)
$(BIN)/python3 setup.py sdist bdist_wheel rm -rf dist
$(BIN)/python setup.py sdist bdist_wheel
$(BIN)/twine upload dist/* $(BIN)/twine upload dist/*
.PHONY: release .PHONY: release

View File

@@ -1,11 +1,33 @@
# blag -- a simple, blog-aware static site generator # blag
## Installation blag is a blog-aware, static site generator, written in [Python][].
blag is named after [the blag of the webcomic xkcd][blagxkcd].
[python]: https://python.org
[blagxkcd]: https://blog.xkcd.com
## 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
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/
## Quickstart
```bash ```bash
$ pip install blag $ pip install blag # 1. install blag
$ blag quickstart # 2. create a new site
$ vim content/hello-world.md # 3. create some content
$ blag build # 4. build the website
``` ```
## Usage
TBD

View File

@@ -19,10 +19,11 @@ from jinja2 import Environment, ChoiceLoader, FileSystemLoader, PackageLoader
import feedgenerator import feedgenerator
from blag.markdown import markdown_factory, convert_markdown from blag.markdown import markdown_factory, convert_markdown
from blag.devserver import serve
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG, level=logging.INFO,
format='%(asctime)s %(levelname)s %(name)s %(message)s', format='%(asctime)s %(levelname)s %(name)s %(message)s',
) )
@@ -50,7 +51,10 @@ def parse_args(args=None):
commands = parser.add_subparsers(dest='command') commands = parser.add_subparsers(dest='command')
commands.required = True commands.required = True
build_parser = commands.add_parser('build') build_parser = commands.add_parser(
'build',
help='Build website.',
)
build_parser.set_defaults(func=build) build_parser.set_defaults(func=build)
build_parser.add_argument( build_parser.add_argument(
'-i', '--input-dir', '-i', '--input-dir',
@@ -73,9 +77,38 @@ def parse_args(args=None):
help='Static directory (default: static)', help='Static directory (default: static)',
) )
quickstart_parser = commands.add_parser('quickstart') quickstart_parser = commands.add_parser(
'quickstart',
help="Quickstart blag, creating necessary configuration.",
)
quickstart_parser.set_defaults(func=quickstart) quickstart_parser.set_defaults(func=quickstart)
serve_parser = commands.add_parser(
'serve',
help="Start development server.",
)
serve_parser.set_defaults(func=serve)
serve_parser.add_argument(
'-i', '--input-dir',
default='content',
help='Input directory (default: content)',
)
serve_parser.add_argument(
'-o', '--output-dir',
default='build',
help='Ouptut directory (default: build)',
)
serve_parser.add_argument(
'-t', '--template-dir',
default='templates',
help='Template directory (default: templates)',
)
serve_parser.add_argument(
'-s', '--static-dir',
default='static',
help='Static directory (default: static)',
)
return parser.parse_args(args) return parser.parse_args(args)
@@ -205,12 +238,13 @@ def process_markdown(convertibles, input_dir, output_dir,
articles, pages : List[Tuple[str, Dict]] articles, pages : List[Tuple[str, Dict]]
""" """
logger.info("Converting Markdown files...")
md = markdown_factory() md = markdown_factory()
articles = [] articles = []
pages = [] pages = []
for src, dst in convertibles: for src, dst in convertibles:
logger.debug(f'Processing {src}') logger.info(f'Processing {src}')
with open(f'{input_dir}/{src}', 'r') as fh: with open(f'{input_dir}/{src}', 'r') as fh:
body = fh.read() body = fh.read()
@@ -243,6 +277,25 @@ def generate_feed(
blog_description, blog_description,
blog_author, blog_author,
): ):
"""Generate Atom feed.
Parameters
----------
articles : list[list[str, dict]]
list of relative output path and article dictionary
output_dir : str
where the feed is stored
base_url : str
base url
blog_title : str
blog title
blog_description : str
blog description
blog_author : str
blog author
"""
logger.info('Generating Atom feed.')
feed = feedgenerator.Atom1Feed( feed = feedgenerator.Atom1Feed(
link=base_url, link=base_url,
title=blog_title, title=blog_title,
@@ -251,11 +304,15 @@ def generate_feed(
) )
for dst, context in articles: for dst, context in articles:
# if article has a description, use that. otherwise fall back to
# the title
description = context.get('description', context['title'])
feed.add_item( feed.add_item(
title=context['title'], title=context['title'],
author_name=blog_author, author_name=blog_author,
link=base_url + dst, link=base_url + dst,
description=context['title'], description=description,
content=context['content'], content=context['content'],
pubdate=context['date'], pubdate=context['date'],
) )
@@ -277,6 +334,7 @@ def generate_archive(articles, template, output_dir):
def generate_tags(articles, tags_template, tag_template, output_dir): def generate_tags(articles, tags_template, tag_template, output_dir):
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

60
blag/devserver.py Normal file
View File

@@ -0,0 +1,60 @@
import os
import logging
import time
import multiprocessing
from http.server import SimpleHTTPRequestHandler, HTTPServer
from functools import partial
from blag import blag
logger = logging.getLogger(__name__)
def get_last_modified(dirs):
"""Get the last modified time.
This method recursively goes through `dirs` and returns the most
recent modification time time found.
Parameters
----------
dirs : list[str]
list of directories to search
Returns
-------
int : most recent modification time found in `dirs`
"""
last_mtime = 0
for dir in dirs:
for root, dirs, files in os.walk(dir):
for f in files:
mtime = os.stat(os.path.join(root, f)).st_mtime
if mtime > last_mtime:
last_mtime = mtime
return last_mtime
def autoreload(args):
dirs = [args.input_dir, args.template_dir, args.static_dir]
logger.info(f'Monitoring {dirs} for changes...')
last_mtime = get_last_modified(dirs)
while True:
mtime = get_last_modified(dirs)
if mtime > last_mtime:
last_mtime = mtime
logger.debug('Change detected, rebuilding...')
blag.build(args)
time.sleep(1)
def serve(args):
httpd = HTTPServer(('', 8000), partial(SimpleHTTPRequestHandler,
directory=args.output_dir))
proc = multiprocessing.Process(target=autoreload, args=(args,))
proc.start()
httpd.serve_forever()

View File

@@ -1 +1 @@
__VERSION__ = '0.0.2' __VERSION__ = '0.0.4'

View File

@@ -1,5 +1,5 @@
twine==3.3.0 twine==3.4.1
wheel==0.36.2 wheel==0.36.2
pytest==6.2.1 pytest==6.2.2
pytest-cov==2.10.1 pytest-cov==2.11.1
flake8==3.8.4 flake8==3.9.0

View File

@@ -1,4 +1,4 @@
markdown==3.3.3 markdown==3.3.4
feedgenerator==1.9.1 feedgenerator==1.9.1
jinja2==2.11.2 jinja2==2.11.3
pygments==2.7.3 pygments==2.8.1

View File

@@ -10,7 +10,7 @@ meta['long_description'] = open('./README.md').read()
setup( setup(
name='blag', name='blag',
version=meta['__VERSION__'], version=meta['__VERSION__'],
description='simple blog-aware static site generator', description='blog-aware, static site generator',
long_description=meta['long_description'], long_description=meta['long_description'],
long_description_content_type='text/markdown', long_description_content_type='text/markdown',
keywords='markdown blag blog static site generator cli', keywords='markdown blag blog static site generator cli',

View File

@@ -1,5 +1,6 @@
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
import os import os
from datetime import datetime
import pytest import pytest
@@ -18,6 +19,76 @@ def test_generate_feed(outdir):
assert os.path.exists(f'{outdir}/atom.xml') assert os.path.exists(f'{outdir}/atom.xml')
def test_feed(outdir):
articles = [
[
'dest1.html',
{
'title': 'title1',
'date': datetime(2019, 6, 6),
'content': 'content1',
}
],
[
'dest2.html',
{
'title': 'title2',
'date': datetime(1980, 5, 9),
'content': 'content2',
}
],
]
blag.generate_feed(articles, outdir, 'https://example.com/', 'blog title',
'blog description', 'blog author')
with open(f'{outdir}/atom.xml') as fh:
feed = fh.read()
assert '<title>blog title</title>' in feed
# enable when https://github.com/getpelican/feedgenerator/issues/22
# is fixed
# assert '<subtitle>blog description</subtitle>' in feed
assert '<author><name>blog author</name></author>' in feed
# article 1
assert '<title>title1</title>' in feed
assert '<summary type="html">title1' in feed
assert '<published>2019-06-06' in feed
assert '<content type="html">content1' in feed
assert '<link href="https://example.com/dest1.html"' in feed
# article 2
assert '<title>title2</title>' in feed
assert '<summary type="html">title2' in feed
assert '<published>1980-05-09' in feed
assert '<content type="html">content2' in feed
assert '<link href="https://example.com/dest2.html"' in feed
def test_generate_feed_with_description(outdir):
# if a description is provided, it will be used as the summary in
# the feed, otherwise we simply use the title of the article
articles = [[
'dest.html',
{
'title': 'title',
'description': 'description',
'date': datetime(2019, 6, 6),
'content': 'content',
}
]]
blag.generate_feed(articles, outdir, ' ', ' ', ' ', ' ')
with open(f'{outdir}/atom.xml') as fh:
feed = fh.read()
assert '<title>title</title>' in feed
assert '<summary type="html">description' in feed
assert '<published>2019-06-06' in feed
assert '<content type="html">content' in feed
def test_parse_args_build(): def test_parse_args_build():
# test default args # test default args
args = blag.parse_args(['build']) args = blag.parse_args(['build'])

30
tests/test_devserver.py Normal file
View File

@@ -0,0 +1,30 @@
import time
import pytest
from tempfile import TemporaryDirectory
from blag import devserver
@pytest.fixture
def tempdir():
with TemporaryDirectory() as dir:
yield dir
def test_get_last_modified(tempdir):
# take initial time
t1 = devserver.get_last_modified([tempdir])
# wait a bit, create a file and measure again
time.sleep(0.1)
with open(f'{tempdir}/test', 'w') as fh:
fh.write('boo')
t2 = devserver.get_last_modified([tempdir])
# wait a bit and take time again
time.sleep(0.1)
t3 = devserver.get_last_modified([tempdir])
assert t2 > t1
assert t2 == t3