summaryrefslogtreecommitdiff
path: root/thrust
diff options
context:
space:
mode:
authorEthel Morgan <eth@ethulhu.co.uk>2020-07-06 18:23:10 +0100
committerEthel Morgan <eth@ethulhu.co.uk>2020-07-06 18:23:10 +0100
commitb1e6491f77421ae4623391a7f53af7f3e6c13f34 (patch)
treeacc4ce7ae214b92dbf2c269c70e94b68dac1d640 /thrust
parent04be5845dbaa6f8dec45a80dbe199861608b96f2 (diff)
import website from previous repo
Diffstat (limited to '')
-rwxr-xr-xthrust133
1 files changed, 133 insertions, 0 deletions
diff --git a/thrust b/thrust
new file mode 100755
index 0000000..d30d9bd
--- /dev/null
+++ b/thrust
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+"""Thrust is a static site generator."""
+
+import sys
+import textwrap
+
+from typing import Any, Dict
+
+import jinja2
+import jinja2.ext
+import jinja2.nodes
+import markdown
+import yaml
+
+
+# cribbed from https://jinja.palletsprojects.com/en/2.11.x/extensions/#cache.
+class MarkdownExtension(jinja2.ext.Extension):
+ """MarkdownExtension provides {% markdown %} and {% endmarkdown %}."""
+ # a set of names that trigger the extension.
+ tags = {'markdown'}
+
+ def __init__(self, environment):
+ super(MarkdownExtension, self).__init__(environment)
+
+ # add the defaults to the environment
+ environment.extend(markdown=markdown.Markdown())
+
+ def parse(self, parser):
+ # the first token is the token that started the tag.
+ # we only listen to `markdown`, so it will always be that.
+ # we keep the line number for the nodes we create later on.
+ lineno = next(parser.stream).lineno
+
+ # parse the body of the `{% markdown %}` up to `{% endmarkdown %}`.
+ body = parser.parse_statements(['name:endmarkdown'], drop_needle=True)
+
+ # return a `CallBlock` node that calls self._markdown_support.
+ return jinja2.nodes.CallBlock(
+ self.call_method('_markdown_support'), [], [], body).set_lineno(lineno)
+
+ def _markdown_support(self, caller):
+ """Helper callback."""
+ block = caller()
+ block = textwrap.dedent(block)
+ return self.environment.markdown.convert(block)
+
+
+class Thrust:
+ """Thrust is a document templater."""
+
+ def __init__(self, template: str, data: Dict[str, Any]):
+ self.template = template
+ self.data = data
+
+ @classmethod
+ def from_path(cls, path: str) -> 'Thrust':
+ with open(path) as f:
+ return cls.parse(f.read())
+
+ @classmethod
+ def parse(cls, src: str) -> 'Thrust':
+ """Parse a source file to maybe return a valid Thrust object."""
+ lines = src.split('\n')
+
+ parts = []
+ for line in lines:
+ if line == '---':
+ parts.append([])
+ else:
+ parts[-1].append(line)
+ if len(parts) != 2:
+ raise ValueError(
+ f'file needs 2 "---" deliniated parts, found {len(parts)}')
+
+ data = yaml.safe_load('\n'.join(parts[0]).replace('\t', ' '))
+ if data is None:
+ data = {}
+
+ template = '\n'.join(parts[1])
+ return cls(template, data)
+
+ @property
+ def _environment(self) -> jinja2.Environment:
+ environment = jinja2.Environment(
+ loader=jinja2.FileSystemLoader('.'),
+ extensions=[MarkdownExtension],
+ trim_blocks=True,
+ )
+ environment.markdown = markdown.Markdown(
+ extensions=['attr_list', 'extra', 'toc'])
+
+ def loadthrust_filter(path: str) -> str:
+ return Thrust.from_path(path)
+ environment.filters['loadthrust'] = loadthrust_filter
+
+ def markdown_filter(src: str) -> str:
+ return environment.markdown.convert(src)
+ environment.filters['markdown'] = markdown_filter
+
+ def template_filter(tmpl: str) -> str:
+ return environment.from_string(tmpl).render(self.data)
+ environment.filters['template'] = template_filter
+
+ def prefix_filter(s: str, prefix: str, first=True) -> str:
+ if not first:
+ lines = s.splitlines()
+ return '\n'.join(lines[0:1] + [f'{prefix}{l}' for l in lines[1:]])
+ return '\n'.join([f'{prefix}{l}' for l in s.splitlines()])
+ environment.filters['prefix'] = prefix_filter
+
+ return environment
+
+ def render(self) -> str:
+ """Render the data in the template."""
+ return self._environment.from_string(self.template).render(self.data)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) != 2:
+ print(f'usage: {sys.argv[0]} <in-path>')
+ sys.exit(1)
+
+ in_path = sys.argv[1]
+
+ try:
+ thrust = Thrust.from_path(in_path)
+ print(thrust.render())
+ except ValueError as error:
+ print(f'{in_path}: {error}', file=sys.stderr)
+ sys.exit(1)
+ except jinja2.exceptions.TemplateError as error:
+ print(error, file=sys.stderr)
+ sys.exit(1)