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 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() 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( 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_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, 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, } self.__update_dict(values, extra_fields) return self.__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( '--search-path', default = '/etc/opt/jw-pkg/templates', help = 'Template search path, colon separated', ) 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))