create builder infrastructure

This commit is contained in:
Soren I. Bjornstad 2021-08-26 11:51:11 -05:00
parent ba8f7d6df1
commit f63f1a61ea
3 changed files with 84 additions and 1 deletions

35
builders.py Normal file
View File

@ -0,0 +1,35 @@
import functools
def _lazy_evaluable(func):
"""
Decorator which makes a function lazy-evaluable: that is, when it's
initially called, it returns a zero-argument lambda with the arguments
initially passed wrapped up in it. Calling that lambda has the effect
of executing the function.
We use this in TZK to allow the user to use function calls in her config
to define the build steps, while not requiring her to write a bunch of
ugly and confusing lambda:'s in the config. The functions that will be called
are prepared during the config and executed later.
"""
@functools.wraps(func)
def new_func(*args, **kwargs):
my_args = args
my_kwargs = kwargs
@functools.wraps(new_func)
def inner():
func(*my_args, **my_kwargs)
return inner
return new_func
# Now a more descriptive name that doesn't expose inner workings
# if the user wants to write her own builder.
tzk_builder = _lazy_evaluable
@tzk_builder
def printer(username: str):
if username == 'Maud':
raise Exception("No Mauds allowed!")
print(f"Hallelujah, {username} built a wiki!")
printer.name = "Display the user's name"

View File

@ -16,9 +16,10 @@ class ConfigurationManager:
if child.is_file() and child.name.endswith('.py'): if child.is_file() and child.name.endswith('.py'):
mod_name = child.name.rsplit('.', 1)[0] mod_name = child.name.rsplit('.', 1)[0]
if mod_name == 'tzk_config': if mod_name == 'tzk_config':
sys.path.insert(0, Path("__file__").parent)
sys.path.insert(0, str(self.config_path)) sys.path.insert(0, str(self.config_path))
self.conf_mod = importlib.import_module(mod_name) self.conf_mod = importlib.import_module(mod_name)
del sys.path[0] del sys.path[0:1]
break break
else: else:
fail( fail(

47
tzk.py
View File

@ -3,6 +3,7 @@ import argparse
import os import os
import shutil import shutil
import sys import sys
import traceback
from typing import Optional from typing import Optional
from config import cm from config import cm
@ -146,6 +147,52 @@ class InitCommand(CliCommand):
tw.install(args.wiki_name, args.tiddlywiki_version_spec, args.author) tw.install(args.wiki_name, args.tiddlywiki_version_spec, args.author)
class BuildCommand(CliCommand):
cmd = "build"
help = ("Build another wiki or derivative product, "
"such as a public version of the wiki, "
"from this TZK repository.")
@classmethod
def setup_arguments(cls, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"product",
metavar="PRODUCT",
help="Name of the product you want to build (defined in your config file).",
)
def _precheck(self, product: str) -> None:
if cm.products is None:
fail("No 'products' dictionary is defined in your config file.")
if product not in cm.products:
fail(f"No '{product}' product found in the products dictionary "
f"in your config file. (Available: {', '.join(cm.products.keys())})")
if not cm.products[product]:
fail(f"No build steps are defined in the '{product}' product "
f"in your config file.")
def execute(self, args: argparse.Namespace) -> None:
self._precheck(args.product)
steps = cm.products[args.product]
print(f"tzk: Starting build of product '{args.product}'.")
print(f"tzk: Found {len(steps)} build steps.")
for idx, step in enumerate(steps, 1):
if hasattr(step, 'name'):
print(f"\ntzk: Step {idx}/{len(steps)}: {step.name}")
else:
print(f"\ntzk: Step {idx}/{len(steps)}")
try:
step()
except Exception as e:
print(f"\ntzk: Build of product '{args.product}' failed on step {failed}. "
f"The original error follows:")
traceback.print_exc()
sys.exit(1)
print(f"\ntzk: Build of product '{args.product}' completed successfully.")
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers() subparsers = parser.add_subparsers()