jw-pkg/src/python/jw/pkg/cmds/projects/CmdCreateFile.py

134 lines
4.3 KiB
Python
Raw Normal View History

from argparse import ArgumentParser, ArgumentTypeError, Namespace
from enum import Enum, auto
from typing import override
from ...lib.log import WARNING, log
from .Cmd import Cmd, Parent
from .lib.pkg_relations import VersionSyntax, pkg_relations
from .lib.templates import ListDict, RenderValues, tmpl_render
def key_value(s):
try:
key, value = s.split('=', 1)
except ValueError:
raise ArgumentTypeError('Expected KEY=VALUE')
return key, value
# TODO: Put the more elaborate stuff into lib
class Fmt(Enum):
Pyright = auto()
class CmdCreateFile(Cmd): # export
def __jw_required(
self,
*,
module: str | None = None,
include_self: bool = False,
) -> list[str]:
if module is None:
module = self.app.args.module
if module is None:
raise Exception('Can\'t get required packages without module name')
ret = pkg_relations(
self.app,
rel_type = 'requires',
flavours = ['run'],
subsections = ['jw'],
seed_pkgs = [module],
syntax = VersionSyntax.names_only,
no_subpackages = True,
recursive = True,
quote = False,
hide_self = False,
hide_jw_pkg = False,
)
if include_self and module not in ret:
ret = [module, *ret]
return ret
def __render(
self,
template_name: str,
values: list[RenderValues],
li_quote = False,
li_delimiter = '\n',
) -> str:
return tmpl_render(
template_name,
values,
li_quote = li_quote,
li_delimiter = li_delimiter,
search_path = self.app.args.search_path.split(':'),
)
def render_tmpl(self, module: str, extra_fields: RenderValues) -> str:
template_name = self.app.args.template_name
if template_name is None:
raise Exception('Can\'t render template without name')
return self.__render(
template_name, [extra_fields],
li_quote = self.app.args.quote,
li_delimiter = ',\n'
)
def render_pyright(self, module: str, extra_fields: RenderValues) -> str:
extra_paths = []
for m in self.__jw_required(include_self = True):
path = self.app.find_dir(m, search_subdirs = ['src/python', 'tools/python'])
if path is None:
log(WARNING, f'No project directory for module "{m}"')
continue
extra_paths.append(path)
values: ListDict = {
'extra_paths': extra_paths,
}
return self.__render(
'pyrightconfig.json', [values, extra_fields],
li_quote = True,
li_delimiter = ',\n'
)
def __init__(self, parent: Parent) -> None:
super().__init__(
parent, 'create-file', help = 'Generate a file from project metadata'
)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument(
'--format',
help = (
'Output format, for example: '
', '.join([fmt.name.lower() for fmt in Fmt])
)
)
parser.add_argument(
'--search-path',
default = '/etc/opt/jw-pkg/templates',
help = 'Template search path, colon separated',
)
parser.add_argument('--template-name', help = 'Template file name')
parser.add_argument(
'--quote',
action = 'store_true',
help = 'Enclose variable values in double quotes before substituting'
)
parser.add_argument(
'-f',
'--field',
action = 'append',
type = key_value,
default = [],
metavar = 'KEY=VALUE',
help = 'Additional fields to insert into the output file',
)
parser.add_argument('module', help = 'The module to generate the file for')
@override
async def _run(self, args: Namespace) -> None:
method = getattr(self, 'render_' + args.format, None)
if method is None: # Should be prevented by choices=[] but keeps linter happy
raise Exception(f'Unsupported output format {args.format}')
print(method(args.module, args.field))