cmds.projects.CmdCreateFile: Add module

Add CmdCreateFile as a command to generate files from project metadata. It uses the new tmpl_render() engine, might serve as a central location to replace other code generating files, let's see how that evolves.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-05-27 15:46:00 +02:00
commit 49016373e1
Signed by: Jan Lindemann
GPG key ID: 3750640C9E25DD61

View file

@ -0,0 +1,102 @@
from argparse import ArgumentParser, ArgumentTypeError, Namespace
from enum import Enum, auto
from typing import Iterable, TypeAlias
from ...lib.log import WARNING, log
from .Cmd import Cmd, Parent
from .lib.pkg_relations import VersionSyntax, pkg_relations
from .lib.templates import 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()
TupleList: TypeAlias = Iterable[tuple[str, str]]
ListDict: TypeAlias = dict[str, list[str]]
class CmdCreateFile(Cmd): # export
def __tuple_list_to_dict(self, src: TupleList) -> ListDict:
ret: ListDict = {}
for key, val in src:
entry = ret.setdefault(key, [])
entry.append(val)
return ret
def __update_dict(self, dst: ListDict, src: TupleList) -> ListDict:
ret = dst
for key, val in src:
entry = ret.setdefault(key, [])
entry.append(val)
return ret
def render_pyright(self, module: str, extra_fields: TupleList) -> str:
prereq = 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,
)
extra_paths = []
for m in prereq:
path = self.app.find_dir(m)
if path is None:
log(WARNING, f'No project directory for module "{m}"')
continue
extra_paths.append(path)
paths = ',\n'.join([f'"{path}"' for path in extra_paths])
values: ListDict = {}
kwargs = {
'extra_paths': paths,
}
self.__update_dict(values, extra_fields)
self.__update_dict(values, kwargs.items())
kwargs.update(extra_fields)
return tmpl_render(
'pyrightconfig.json', values, li_quote = True, li_delimiter = ',\n'
)
def __init__(self, parent: Parent) -> None:
super().__init__(
parent, 'create-file', help = 'Generate a file from project metadata'
)
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument(
'--format',
choices = [fmt.name.lower() for fmt in Fmt],
help = 'Output format'
)
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')
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))