update docstrings

This commit is contained in:
Soren I. Bjornstad 2021-08-27 10:06:52 -05:00
parent 0321b7d7b1
commit 97a4611d56
5 changed files with 75 additions and 7 deletions

View File

@ -13,15 +13,26 @@ from tzk.util import BuildError, fail, numerize
class CliCommand(ABC):
"""
Base class for subcommands of tzk.
"""
#: The text of the subcommand to be used on the command line.
cmd = None # type: str
#: Help string for argparse to display for this subcommand.
help = None # type: str
@abstractclassmethod
def setup_arguments(cls, parser: argparse.ArgumentParser) -> None:
"""
Given the :arg:`parser`, add any arguments this subcommand wants to accept.
"""
raise NotImplementedError
@abstractmethod
def execute(self, parser: argparse.Namespace) -> None:
def execute(self, args: argparse.Namespace) -> None:
"""
Given the :arg:`args` passed to this subcommand, do whatever the command does.
"""
raise NotImplementedError
@ -182,21 +193,27 @@ class BuildCommand(CliCommand):
def execute(self, args: argparse.Namespace) -> None:
self._precheck(args.product)
# Find the build steps for the product the user specified.
steps = cm().products[args.product]
print(f"tzk: Starting build of product '{args.product}'.")
print(f"tzk: Found {len(steps)} build {numerize(len(steps), 'step')}.")
# For each build step...
for idx, step in enumerate(steps, 1):
# Explain what we're doing. Use first line of the builder's docstring
# as a summary, if present.
if hasattr(step, '__doc__'):
short_description = step.__doc__.strip().split('\n')[0].rstrip('.')
print(f"tzk: Step {idx}/{len(steps)}: {short_description}")
else:
print(f"tzk: Step {idx}/{len(steps)}")
# If the user asked to skip this builder on the command line, do so.
if step.__name__ in args.skip_builder:
print(f"tzk: Skipping step {idx} due to --skip-builder parameter.")
continue
# Execute step and handle any errors.
try:
step()
except BuildError as e:
@ -231,10 +248,12 @@ def launch():
args = parser.parse_args()
if not hasattr(args, '_cls'):
# no subcommand was given
parser.print_help()
sys.exit(0)
# For all operations except 'init', we start in the wiki folder.
# (For init, we're actually *creating* the wiki folder.)
if not args._cls.cmd == "init":
if not cm().wiki_folder:
fail("No 'wiki_folder' option found in config. Set this option to the name "
@ -256,4 +275,4 @@ def launch():
if __name__ == '__main__':
launch()
launch()

View File

@ -1,3 +1,6 @@
"""
config.py - read and manage the TZK config file
"""
import datetime
import functools
import importlib
@ -57,6 +60,14 @@ class ConfigurationManager:
def cm(cache=[]):
"""
Call this function to retrieve the singleton ConfigurationManager object,
reading and initializing it if necessary.
Since so much happens when the ConfigurationManager is initialized,
this has to go in a function so that autodoc doesn't blow up
when it tries to import the module.
"""
if not cache:
cache.append(ConfigurationManager())
return cache[0]

View File

@ -1,11 +1,17 @@
import subprocess
from typing import Sequence
def exec(*args: str):
return subprocess.check_call(["git", *args])
def rc(*args: str):
def exec(*args: str) -> None:
"Execute a Git command, raising CalledProcessError if the exit code is nonzero."
subprocess.check_call(["git", *args])
def rc(*args: str) -> int:
"Execute a Git command, returning the exit code."
return subprocess.call(["git", *args])
def read(*args: str):
def read(*args: str) -> str:
"Execute a Git command, returning the output as a string."
return subprocess.check_output(["git", *args], text=True).strip()

View File

@ -20,6 +20,7 @@ def _tw_path() -> str:
@functools.lru_cache(1)
def _whoami() -> str:
"Try to guess the user's name."
try:
return subprocess.check_output(("whoami",), text=True).strip()
except subprocess.CalledProcessError:
@ -27,6 +28,19 @@ def _whoami() -> str:
def exec(args: Sequence[Sequence[str]], base_wiki_folder: str = None) -> int:
"""
Execute a series of TiddlyWiki commands.
:param args: A list of lists of CLI commands to send to TiddlyWiki.
The first element of each list is a TiddlyWiki CLI command,
without the ``--``, e.g., ``savewikifolder``.
The following elements of the list are arguments to that command.
:param base_wiki_folder: If the wiki to execute commands against is not the one
in the current directory, provide its path here.
The current directory is the source wiki's root directory
during the execution of builders,
unless explicitly changed.
"""
call_args = [_tw_path()]
if base_wiki_folder is not None:
call_args.append(base_wiki_folder)
@ -98,6 +112,10 @@ def _save_wikifolder_to_config(wiki_name: str) -> bool:
def _add_filesystem_plugins(wiki_name: str) -> None:
"""
Add the "tiddlywiki/filesystem" and "tiddlywiki/tiddlyweb" plugins
required for Node.js client-server operation to the new wiki's tiddlywiki.info.
"""
print("tzk: Adding filesystem plugins to tiddlywiki.info...")
info_path = Path.cwd() / wiki_name / "tiddlywiki.info"
with info_path.open("r") as f:
@ -108,6 +126,9 @@ def _add_filesystem_plugins(wiki_name: str) -> None:
def _init_gitignore() -> None:
"""
Create a basic gitignore for the new wiki.
"""
print("tzk: Creating gitignore...")
GITIGNORE = dedent("""
__pycache__/
@ -122,6 +143,9 @@ def _init_gitignore() -> None:
def _initial_commit() -> None:
"""
Create a new Git repo and commit everything we've done so far.
"""
print("tzk: Initializing new Git repository for wiki...")
git.exec("init")
@ -131,6 +155,9 @@ def _initial_commit() -> None:
def install(wiki_name: str, tw_version_spec: str, author: Optional[str]):
"""
Install TiddlyWiki on Node.js in the current directory and set up a new wiki.
"""
# assert: caller has checked npm and git are installed
warnings = False

View File

@ -1,3 +1,6 @@
"""
util.py - miscellaneous utility functions
"""
from contextlib import contextmanager
import os
import sys
@ -9,11 +12,13 @@ class BuildError(Exception):
def fail(msg: str, exit_code: int = 1) -> NoReturn:
"Print message to stderr and quit with exit code 1."
print(msg, file=sys.stderr)
sys.exit(exit_code)
def numerize(number: int, singular: str, plural: str = None):
def numerize(number: int, singular: str, plural: str = None) -> str:
"Render a string in the singular or plural as appropriate."
if plural is None:
plural = singular + 's'