Skip to content

Commit

Permalink
init subcommand: post-installation system initialization (#16)
Browse files Browse the repository at this point in the history
## Summary

Adds subcommand `init` with its own subcommands:

* `conf`: write default/example conf files to default directory or to a specified directory

* `comp`: write shell completion files for user or system or to a specified directory

Part of #15 

## Changes

* system path prefixes: ignore isolation in system context + refactoring for flexibility

* added subcommand `init` for post-installation initialization beginning with: shell completion files

* init comp: ensure directory exists before writing file

* added subcommand `init conf` to write default/example conf files to default directory or specified directory.

  * includes (related) upgrade to argcmdr 1.0.0
  * display `python -m fate` rather than `fate` when run that way
  • Loading branch information
jesteria authored Feb 15, 2023
1 parent 47f6cea commit bcaa19b
Show file tree
Hide file tree
Showing 15 changed files with 847 additions and 125 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ packages = [{include = "fate", from = "src"}]

[tool.poetry.dependencies]
python = "^3.8"
argcmdr = "^0.13.2"
argcmdr = "^1.0.0"
argcomplete = "^2.0"
croniter = "^1.3.5"
Jinja2 = "^3.1.2"
loguru = "^0.6.0"
Expand Down
2 changes: 1 addition & 1 deletion src/fate/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .base import runcmd # noqa: F401
from .base import exit_on_error, runcmd # noqa: F401

from .root import main, Main, daemon, serve # noqa: F401
5 changes: 3 additions & 2 deletions src/fate/cli/base/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .execution import runcmd # noqa: F401
from .main import Main # noqa: F401
from .common import exit_on_error # noqa: F401
from .execution import runcmd # noqa: F401
from .main import Main # noqa: F401
25 changes: 22 additions & 3 deletions src/fate/cli/base/common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import enum
import functools

import fate.conf

Expand Down Expand Up @@ -32,6 +33,24 @@ def __exit__(self, exc_type, exc_value, _traceback):
self.parser.exit(78, f'{self.parser.prog}: error: {exc_value}\n')


def exit_on_error(method):
"""Decorator to apply context manager `ExitOnError` to instance
methods of classes of type `argcmdr.Command`.
Note: The decorator wrapper depends upon instance attribute
`parser`. As such, affected command classes must extend the default
`__init__` (*i.e.* `super()`); or, decorated methods must be invoked
*after* command initialization (*i.e.* not as part of its `__init__`).
"""
@functools.wraps(method)
def wrapped(self, *args, **kwargs):
with ExitOnError(self.parser):
return method(self, *args, **kwargs)

return wrapped


class CommandInterface:

class CommandStatus(enum.Enum):
Expand Down Expand Up @@ -59,7 +78,7 @@ def __str__(self):

@property
def conf(self):
if not self.__parents__:
if (root := self.root) is None:
# this is the root command
# retrieve and store conf here
try:
Expand All @@ -70,11 +89,11 @@ def conf(self):
return conf

# defer to root
return self.root.conf
return root.conf

@property
def exit_on_error(self):
return ExitOnError(self.args.__parser__)
return ExitOnError(self.parser)

@staticmethod
def write_result(path, contents):
Expand Down
2 changes: 1 addition & 1 deletion src/fate/cli/base/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def get_command(self, args):
def prepare(self, args, parser):
"""Execute and report on task command execution."""
try:
command_spec = self.call(args, 'get_command')
command_spec = self.delegate('get_command')
except self.local.CommandNotFound as exc:
hint = ('\nhint: whitespace in program name suggests a misconfiguration'
if re.search(r'\s', exc.program) else '')
Expand Down
10 changes: 7 additions & 3 deletions src/fate/cli/base/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import os.path
import sys

import argcmdr

from .common import CommandInterface
Expand All @@ -7,11 +10,12 @@ class Main(CommandInterface, argcmdr.RootCommand):
"""manage the periodic execution of commands"""

@classmethod
def base_parser(cls):
parser = super().base_parser()
def _new_parser_(cls):
parser = super()._new_parser_()

# enforce program name when invoked via "python -m fate"
if parser.prog == '__main__.py':
parser.prog = 'fate'
command = os.path.basename(sys.executable)
parser.prog = f'{command} -m fate'

return parser
4 changes: 1 addition & 3 deletions src/fate/cli/command/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,7 @@ def onerror(self, exc):
error_msg = error and f': {error}'
error_name = exc.__class__.__name__

parser = self.args.__parser__

parser.exit(1, f'{parser.prog}: fatal: {error_name}{error_msg}\n')
self.parser.exit(1, f'{self.parser.prog}: fatal: {error_name}{error_msg}\n')

def __call__(self, args, parser):
"""Execute the command."""
Expand Down
Loading

0 comments on commit bcaa19b

Please sign in to comment.