Add init init-global to subparsers

This commit is contained in:
Roland Thomas Jr 2025-05-31 09:29:24 -04:00
parent 3d3a706784
commit 1585098513
Signed by: roland
GPG Key ID: 7C3C2B085A4C2872
6 changed files with 125 additions and 12 deletions

View File

@ -8,12 +8,13 @@ Licensed under the MIT License. See LICENSE file for details.
import asyncio import asyncio
import os import os
import sys import sys
from argparse import ArgumentParser, Namespace, _SubParsersAction
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from falyx.config import loader from falyx.config import loader
from falyx.falyx import Falyx from falyx.falyx import Falyx
from falyx.parsers import CommandArgumentParser from falyx.parsers import CommandArgumentParser, get_root_parser, get_subparsers
def find_falyx_config() -> Path | None: def find_falyx_config() -> Path | None:
@ -48,6 +49,42 @@ def init_config(parser: CommandArgumentParser) -> None:
) )
def init_callback(args: Namespace) -> None:
"""Callback for the init command."""
if args.command == "init":
from falyx.init import init_project
init_project(args.name)
elif args.command == "init_global":
from falyx.init import init_global
init_global()
def get_parsers() -> tuple[ArgumentParser, _SubParsersAction]:
root_parser: ArgumentParser = get_root_parser()
subparsers = get_subparsers(root_parser)
init_parser = subparsers.add_parser(
"init",
help="Initialize a new Falyx project",
description="Create a new Falyx project with mock configuration files.",
epilog="If no name is provided, the current directory will be used.",
)
init_parser.add_argument(
"name",
type=str,
help="Name of the new Falyx project",
default=".",
nargs="?",
)
subparsers.add_parser(
"init-global",
help="Initialize Falyx global configuration",
description="Create a global Falyx configuration at ~/.config/falyx/.",
)
return root_parser, subparsers
def main() -> Any: def main() -> Any:
bootstrap_path = bootstrap() bootstrap_path = bootstrap()
if not bootstrap_path: if not bootstrap_path:
@ -60,17 +97,23 @@ def main() -> Any:
init_project, init_project,
aliases=["init"], aliases=["init"],
argument_config=init_config, argument_config=init_config,
help_epilogue="If no name is provided, the current directory will be used.",
) )
flx.add_command( flx.add_command(
"G", "G",
"Initialize Falyx global configuration", "Initialize Falyx global configuration",
init_global, init_global,
aliases=["init-global"], aliases=["init-global"],
help_text="Create a global Falyx configuration at ~/.config/falyx/.",
) )
else: else:
flx = loader(bootstrap_path) flx = loader(bootstrap_path)
return asyncio.run(flx.run()) root_parser, subparsers = get_parsers()
return asyncio.run(
flx.run(root_parser=root_parser, subparsers=subparsers, callback=init_callback)
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -25,7 +25,7 @@ import asyncio
import logging import logging
import shlex import shlex
import sys import sys
from argparse import Namespace from argparse import ArgumentParser, Namespace, _SubParsersAction
from difflib import get_close_matches from difflib import get_close_matches
from enum import Enum from enum import Enum
from functools import cached_property from functools import cached_property
@ -1029,6 +1029,8 @@ class Falyx:
async def run( async def run(
self, self,
falyx_parsers: FalyxParsers | None = None, falyx_parsers: FalyxParsers | None = None,
root_parser: ArgumentParser | None = None,
subparsers: _SubParsersAction | None = None,
callback: Callable[..., Any] | None = None, callback: Callable[..., Any] | None = None,
) -> None: ) -> None:
"""Run Falyx CLI with structured subcommands.""" """Run Falyx CLI with structured subcommands."""
@ -1046,6 +1048,8 @@ class Falyx:
self.description, self.description,
self.epilog, self.epilog,
commands=self.commands, commands=self.commands,
root_parser=root_parser,
subparsers=subparsers,
) )
self.cli_args = falyx_parsers.parse_args() self.cli_args = falyx_parsers.parse_args()
self.options.from_namespace(self.cli_args, "cli_args") self.options.from_namespace(self.cli_args, "cli_args")

View File

@ -6,12 +6,14 @@ Licensed under the MIT License. See LICENSE file for details.
""" """
from .argparse import Argument, ArgumentAction, CommandArgumentParser from .argparse import Argument, ArgumentAction, CommandArgumentParser
from .parsers import FalyxParsers, get_arg_parsers from .parsers import FalyxParsers, get_arg_parsers, get_root_parser, get_subparsers
__all__ = [ __all__ = [
"Argument", "Argument",
"ArgumentAction", "ArgumentAction",
"CommandArgumentParser", "CommandArgumentParser",
"get_arg_parsers", "get_arg_parsers",
"get_root_parser",
"get_subparsers",
"FalyxParsers", "FalyxParsers",
] ]

View File

@ -40,7 +40,7 @@ class FalyxParsers:
return self.as_dict().get(name) return self.as_dict().get(name)
def get_arg_parsers( def get_root_parser(
prog: str | None = "falyx", prog: str | None = "falyx",
usage: str | None = None, usage: str | None = None,
description: str | None = "Falyx CLI - Run structured async command workflows.", description: str | None = "Falyx CLI - Run structured async command workflows.",
@ -55,9 +55,7 @@ def get_arg_parsers(
add_help: bool = True, add_help: bool = True,
allow_abbrev: bool = True, allow_abbrev: bool = True,
exit_on_error: bool = True, exit_on_error: bool = True,
commands: dict[str, Command] | None = None, ) -> ArgumentParser:
) -> FalyxParsers:
"""Returns the argument parser for the CLI."""
parser = ArgumentParser( parser = ArgumentParser(
prog=prog, prog=prog,
usage=usage, usage=usage,
@ -86,7 +84,70 @@ def get_arg_parsers(
help="Enable default lifecycle debug logging", help="Enable default lifecycle debug logging",
) )
parser.add_argument("--version", action="store_true", help="Show Falyx version") parser.add_argument("--version", action="store_true", help="Show Falyx version")
subparsers = parser.add_subparsers(dest="command") return parser
def get_subparsers(
parser: ArgumentParser,
title: str = "Falyx Commands",
description: str | None = "Available commands for the Falyx CLI.",
) -> _SubParsersAction:
"""Create and return a subparsers action for the given parser."""
if not isinstance(parser, ArgumentParser):
raise TypeError("parser must be an instance of ArgumentParser")
subparsers = parser.add_subparsers(
title=title,
description=description,
metavar="COMMAND",
dest="command",
)
return subparsers
def get_arg_parsers(
prog: str | None = "falyx",
usage: str | None = None,
description: str | None = "Falyx CLI - Run structured async command workflows.",
epilog: (
str | None
) = "Tip: Use 'falyx run ?[COMMAND]' to preview any command from the CLI.",
parents: Sequence[ArgumentParser] | None = None,
prefix_chars: str = "-",
fromfile_prefix_chars: str | None = None,
argument_default: Any = None,
conflict_handler: str = "error",
add_help: bool = True,
allow_abbrev: bool = True,
exit_on_error: bool = True,
commands: dict[str, Command] | None = None,
root_parser: ArgumentParser | None = None,
subparsers: _SubParsersAction | None = None,
) -> FalyxParsers:
"""Returns the argument parser for the CLI."""
if root_parser is None:
parser = get_root_parser(
prog=prog,
usage=usage,
description=description,
epilog=epilog,
parents=parents,
prefix_chars=prefix_chars,
fromfile_prefix_chars=fromfile_prefix_chars,
argument_default=argument_default,
conflict_handler=conflict_handler,
add_help=add_help,
allow_abbrev=allow_abbrev,
exit_on_error=exit_on_error,
)
else:
if not isinstance(root_parser, ArgumentParser):
raise TypeError("root_parser must be an instance of ArgumentParser")
parser = root_parser
if subparsers is None:
subparsers = get_subparsers(parser)
if not isinstance(subparsers, _SubParsersAction):
raise TypeError("subparsers must be an instance of _SubParsersAction")
run_description = ["Run a command by its key or alias.\n"] run_description = ["Run a command by its key or alias.\n"]
run_description.append("commands:") run_description.append("commands:")
@ -105,7 +166,9 @@ def get_arg_parsers(
epilog=run_epilog, epilog=run_epilog,
formatter_class=RawDescriptionHelpFormatter, formatter_class=RawDescriptionHelpFormatter,
) )
run_parser.add_argument("name", help="Run a command by its key or alias") run_parser.add_argument(
"name", help="Run a command by its key or alias", metavar="COMMAND"
)
run_parser.add_argument( run_parser.add_argument(
"--summary", "--summary",
action="store_true", action="store_true",
@ -143,6 +206,7 @@ def get_arg_parsers(
"command_args", "command_args",
nargs=REMAINDER, nargs=REMAINDER,
help="Arguments to pass to the command (if applicable)", help="Arguments to pass to the command (if applicable)",
metavar="ARGS",
) )
run_all_parser = subparsers.add_parser( run_all_parser = subparsers.add_parser(

View File

@ -1 +1 @@
__version__ = "0.1.42" __version__ = "0.1.43"

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "falyx" name = "falyx"
version = "0.1.42" version = "0.1.43"
description = "Reliable and introspectable async CLI action framework." description = "Reliable and introspectable async CLI action framework."
authors = ["Roland Thomas Jr <roland@rtj.dev>"] authors = ["Roland Thomas Jr <roland@rtj.dev>"]
license = "MIT" license = "MIT"