update docstrings
This commit is contained in:
parent
0321b7d7b1
commit
97a4611d56
@ -13,15 +13,26 @@ from tzk.util import BuildError, fail, numerize
|
|||||||
|
|
||||||
|
|
||||||
class CliCommand(ABC):
|
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
|
cmd = None # type: str
|
||||||
|
#: Help string for argparse to display for this subcommand.
|
||||||
help = None # type: str
|
help = None # type: str
|
||||||
|
|
||||||
@abstractclassmethod
|
@abstractclassmethod
|
||||||
def setup_arguments(cls, parser: argparse.ArgumentParser) -> None:
|
def setup_arguments(cls, parser: argparse.ArgumentParser) -> None:
|
||||||
|
"""
|
||||||
|
Given the :arg:`parser`, add any arguments this subcommand wants to accept.
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@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
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
@ -182,21 +193,27 @@ class BuildCommand(CliCommand):
|
|||||||
def execute(self, args: argparse.Namespace) -> None:
|
def execute(self, args: argparse.Namespace) -> None:
|
||||||
self._precheck(args.product)
|
self._precheck(args.product)
|
||||||
|
|
||||||
|
# Find the build steps for the product the user specified.
|
||||||
steps = cm().products[args.product]
|
steps = cm().products[args.product]
|
||||||
print(f"tzk: Starting build of product '{args.product}'.")
|
print(f"tzk: Starting build of product '{args.product}'.")
|
||||||
print(f"tzk: Found {len(steps)} build {numerize(len(steps), 'step')}.")
|
print(f"tzk: Found {len(steps)} build {numerize(len(steps), 'step')}.")
|
||||||
|
|
||||||
|
# For each build step...
|
||||||
for idx, step in enumerate(steps, 1):
|
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__'):
|
if hasattr(step, '__doc__'):
|
||||||
short_description = step.__doc__.strip().split('\n')[0].rstrip('.')
|
short_description = step.__doc__.strip().split('\n')[0].rstrip('.')
|
||||||
print(f"tzk: Step {idx}/{len(steps)}: {short_description}")
|
print(f"tzk: Step {idx}/{len(steps)}: {short_description}")
|
||||||
else:
|
else:
|
||||||
print(f"tzk: Step {idx}/{len(steps)}")
|
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:
|
if step.__name__ in args.skip_builder:
|
||||||
print(f"tzk: Skipping step {idx} due to --skip-builder parameter.")
|
print(f"tzk: Skipping step {idx} due to --skip-builder parameter.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Execute step and handle any errors.
|
||||||
try:
|
try:
|
||||||
step()
|
step()
|
||||||
except BuildError as e:
|
except BuildError as e:
|
||||||
@ -231,10 +248,12 @@ def launch():
|
|||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if not hasattr(args, '_cls'):
|
if not hasattr(args, '_cls'):
|
||||||
|
# no subcommand was given
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# For all operations except 'init', we start in the wiki folder.
|
# 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 args._cls.cmd == "init":
|
||||||
if not cm().wiki_folder:
|
if not cm().wiki_folder:
|
||||||
fail("No 'wiki_folder' option found in config. Set this option to the name "
|
fail("No 'wiki_folder' option found in config. Set this option to the name "
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""
|
||||||
|
config.py - read and manage the TZK config file
|
||||||
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
import importlib
|
import importlib
|
||||||
@ -57,6 +60,14 @@ class ConfigurationManager:
|
|||||||
|
|
||||||
|
|
||||||
def cm(cache=[]):
|
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:
|
if not cache:
|
||||||
cache.append(ConfigurationManager())
|
cache.append(ConfigurationManager())
|
||||||
return cache[0]
|
return cache[0]
|
||||||
|
14
tzk/git.py
14
tzk/git.py
@ -1,11 +1,17 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
from typing import Sequence
|
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])
|
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()
|
return subprocess.check_output(["git", *args], text=True).strip()
|
||||||
|
27
tzk/tw.py
27
tzk/tw.py
@ -20,6 +20,7 @@ def _tw_path() -> str:
|
|||||||
|
|
||||||
@functools.lru_cache(1)
|
@functools.lru_cache(1)
|
||||||
def _whoami() -> str:
|
def _whoami() -> str:
|
||||||
|
"Try to guess the user's name."
|
||||||
try:
|
try:
|
||||||
return subprocess.check_output(("whoami",), text=True).strip()
|
return subprocess.check_output(("whoami",), text=True).strip()
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
@ -27,6 +28,19 @@ def _whoami() -> str:
|
|||||||
|
|
||||||
|
|
||||||
def exec(args: Sequence[Sequence[str]], base_wiki_folder: str = None) -> int:
|
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()]
|
call_args = [_tw_path()]
|
||||||
if base_wiki_folder is not None:
|
if base_wiki_folder is not None:
|
||||||
call_args.append(base_wiki_folder)
|
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:
|
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...")
|
print("tzk: Adding filesystem plugins to tiddlywiki.info...")
|
||||||
info_path = Path.cwd() / wiki_name / "tiddlywiki.info"
|
info_path = Path.cwd() / wiki_name / "tiddlywiki.info"
|
||||||
with info_path.open("r") as f:
|
with info_path.open("r") as f:
|
||||||
@ -108,6 +126,9 @@ def _add_filesystem_plugins(wiki_name: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _init_gitignore() -> None:
|
def _init_gitignore() -> None:
|
||||||
|
"""
|
||||||
|
Create a basic gitignore for the new wiki.
|
||||||
|
"""
|
||||||
print("tzk: Creating gitignore...")
|
print("tzk: Creating gitignore...")
|
||||||
GITIGNORE = dedent("""
|
GITIGNORE = dedent("""
|
||||||
__pycache__/
|
__pycache__/
|
||||||
@ -122,6 +143,9 @@ def _init_gitignore() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _initial_commit() -> 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...")
|
print("tzk: Initializing new Git repository for wiki...")
|
||||||
git.exec("init")
|
git.exec("init")
|
||||||
|
|
||||||
@ -131,6 +155,9 @@ def _initial_commit() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def install(wiki_name: str, tw_version_spec: str, author: Optional[str]):
|
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
|
# assert: caller has checked npm and git are installed
|
||||||
warnings = False
|
warnings = False
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""
|
||||||
|
util.py - miscellaneous utility functions
|
||||||
|
"""
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
@ -9,11 +12,13 @@ class BuildError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
def fail(msg: str, exit_code: int = 1) -> NoReturn:
|
def fail(msg: str, exit_code: int = 1) -> NoReturn:
|
||||||
|
"Print message to stderr and quit with exit code 1."
|
||||||
print(msg, file=sys.stderr)
|
print(msg, file=sys.stderr)
|
||||||
sys.exit(exit_code)
|
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:
|
if plural is None:
|
||||||
plural = singular + 's'
|
plural = singular + 's'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user