diff --git a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py index b59576fd..58898b67 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py +++ b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py @@ -65,7 +65,7 @@ class CmdCreateFile(Cmd): # export self.__update_dict(values, extra_fields) return tmpl_render( - 'pyrightconfig.json', values, li_quote = True, li_delimiter = ',\n' + 'pyrightconfig.json', [values], li_quote = True, li_delimiter = ',\n' ) def __init__(self, parent: Parent) -> None: diff --git a/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py b/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py index 372fabd4..30f075c6 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py +++ b/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py @@ -1,8 +1,9 @@ from __future__ import annotations +from typing import TYPE_CHECKING + from .Cmd import Cmd, Parent from .lib.templates import tmpl_render -from typing import TYPE_CHECKING if TYPE_CHECKING: from argparse import ArgumentParser, Namespace @@ -61,24 +62,26 @@ class CmdCreatePkgConfig(Cmd): # export contents = tmpl_render( 'pkg-config', - { - 'prefix': args.prefix, - 'name': args.name, - 'description': merged['summary'], - 'version': args.version, - }, + [ + { + 'prefix': args.prefix, + 'name': args.name, + 'description': merged['summary'] or '', + 'version': args.version, + } + ] ) if args.cflags is not None: contents += f'Cflags: {args.cflags}\n' if args.libflags is not None: contents += f'Libs: {args.libflags}\n' - if merged['requires_run'] is not None: - contents += f'Requires: {self.__cleanup_requires(merged["requires_run"])}' - if merged['requires_build'] is not None: - contents += ( - f'Requires.private: {self.__cleanup_requires(merged["requires_build"])}' - ) + val = merged.get('requires_run') + if val is not None: + contents += f'Requires: {self.__cleanup_requires(val)}' + val = merged.get('requires_build') + if val is not None: + contents += (f'Requires.private: {self.__cleanup_requires(val)}') # not sure what to do with requires_devel print(contents) diff --git a/src/python/jw/pkg/cmds/projects/lib/templates.py b/src/python/jw/pkg/cmds/projects/lib/templates.py index d7388b95..37fd3d2b 100644 --- a/src/python/jw/pkg/cmds/projects/lib/templates.py +++ b/src/python/jw/pkg/cmds/projects/lib/templates.py @@ -1,7 +1,79 @@ import textwrap +from typing import Iterable, TypeAlias, TypeGuard -def format_lines( - template: str, values: dict[str, str], li_quote: bool, li_delimiter: str +TupleList: TypeAlias = Iterable[tuple[str, str]] +ListDict: TypeAlias = dict[str, list[str]] +StrDict: TypeAlias = dict[str, str] +RenderValues: TypeAlias = ListDict | StrDict | TupleList + +def is_str_dict(values: RenderValues) -> TypeGuard[StrDict]: + if not isinstance(values, dict): + return False + for key, val in values.items(): + if not isinstance(key, str): + return False + if not isinstance(val, str): + return False + return True + +def is_list_dict(values: RenderValues) -> TypeGuard[ListDict]: + if not isinstance(values, dict): + return False + for key, val in values.items(): + if not isinstance(key, str): + return False + if isinstance(val, str): + continue + if not isinstance(val, list): + return False + for entry in val: + if not isinstance(entry, str): + return False + return True + +def is_tuple_list(values: RenderValues) -> TypeGuard[TupleList]: + if not isinstance(values, list): + return False + for item in values: + if not isinstance(item, tuple): + return False + if not len(item) == 2: + return False + if not isinstance(item[0], str): + return False + if not isinstance(item[1], str): + return False + return True + +def render_values_to_list_dict(values: RenderValues) -> ListDict: + + def __tuple_list_to_dict(src: TupleList) -> ListDict: + ret: ListDict = {} + for key, val in src: + entry = ret.setdefault(key, []) + entry.append(val) + return ret + + if is_list_dict(values): + return values + if is_tuple_list(values): + return __tuple_list_to_dict(values) + raise Exception('Unsupported template value layout') + +def merge_values(*values: RenderValues) -> ListDict: + ret: ListDict = {} + for rhs in values: + rhs_dict = render_values_to_list_dict(rhs) + for key, val in rhs_dict.items(): + entry = ret.setdefault(key, []) + if isinstance(val, list): + entry += val + else: + entry.append(val) + return ret + +def format_list_dict( + template: str, values: ListDict | dict[str, str], li_quote: bool, li_delimiter: str ) -> str: def __format_value(val): @@ -9,15 +81,18 @@ def format_lines( return str(val) return f'"{val}"' + fmt_dict: dict[str, str] = {} for key, value in values.items(): if isinstance(value, (list, tuple)): - values[key] = li_delimiter.join(map(__format_value, value)) + fmt_dict[key] = li_delimiter.join(map(__format_value, value)) + elif isinstance(value, str): + fmt_dict[key] = value - ret = [] + ret: list[str] = [] parts = template.splitlines(keepends = True) for line in parts: - for key, value in values.items(): + for key, value in fmt_dict.items(): marker = '{' + key + '}' if marker in line: indent = line[:line.index(marker)] @@ -27,6 +102,11 @@ def format_lines( return ''.join(ret) +def format_lines( + template: str, values: list[RenderValues], li_quote: bool, li_delimiter: str +) -> str: + return format_list_dict(template, merge_values(*values), li_quote, li_delimiter) + _templates = { 'pkg-config': """\ @@ -66,7 +146,7 @@ _templates = { def tmpl_render( template_name: str, - values, + values: list[RenderValues], li_quote = False, li_delimiter = '\n', search_path: list[str] = []