From 0b558635a8e8320e7b562d04e568d6240e44e777 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Fri, 5 Jun 2026 14:36:22 +0200 Subject: [PATCH 01/10] py-topdir.mk: Introduce $(PY_CHECK_ROOTS) Replace variable PY_SRC_ROOT by PY_CHECK_ROOTS. The name PY_SRC_ROOT was a bad choice, given that it isn't immediately obvious that it a) can contain multiple root locations to be checked, and that it b) specifically concerns static type checking. As of this commit, it's possible to limit the type checking scope with PY_CHECK_ROOTS as in PY_CHECK_ROOTS="src/python/jw/pkg/CmdBase.py src/python/jw/lib" \ make check Signed-off-by: Jan Lindemann --- make/py-topdir.mk | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/make/py-topdir.mk b/make/py-topdir.mk index 4a298642..471ee8b4 100644 --- a/make/py-topdir.mk +++ b/make/py-topdir.mk @@ -1,7 +1,9 @@ TD_COPY_FILES += pyproject.toml PY_CHECK_EXCLUDE ?= -PY_SRC_ROOT += $(wildcard $(TOPDIR)/src $(TOPDIR)/tools) +ifndef PY_CHECK_ROOTS + PY_CHECK_ROOTS += $(wildcard $(TOPDIR)/src $(TOPDIR)/tools) +endif ifndef PY_CHECK_RUFF PY_CHECK_RUFF := $(shell which ruff 2>/dev/null) @@ -25,27 +27,27 @@ check-format: py-check-format py-check: py-check-syntax py-check-format py-check-syntax: ifneq ($(PY_CHECK_RUFF),) - $(PY_CHECK_RUFF) check $(addprefix --exclude ,$(PY_CHECK_EXCLUDE)) $(PY_SRC_ROOT) + $(PY_CHECK_RUFF) check $(addprefix --exclude ,$(PY_CHECK_EXCLUDE)) $(PY_CHECK_ROOTS) endif - mypy $(addprefix --exclude ,$(PY_CHECK_EXCLUDE)) $(PY_SRC_ROOT) + mypy $(addprefix --exclude ,$(PY_CHECK_EXCLUDE)) $(PY_CHECK_ROOTS) ifneq ($(PY_CHECK_PYRIGHT),) - pyright + pyright $(PY_CHECK_ROOTS) endif py-check-format: ifneq ($(PY_CHECK_YAPF),) - $(PY_CHECK_YAPF) --diff --recursive . + $(PY_CHECK_YAPF) --diff --recursive $(PY_CHECK_ROOTS) endif py-format: find . -type f -name '*.py' -print0 | \ xargs -0 sed -i -E '1{/^# -\*- coding: utf-8 -\*-$$/{:a;N;/\n[[:space:]]*$$/ba;s/^# -\*- coding: utf-8 -\*-\n([[:space:]]*\n)*/ /;s/^ //}}' ifneq ($(PY_CHECK_YAPF),) - $(PY_CHECK_YAPF) --in-place --recursive . + $(PY_CHECK_YAPF) --in-place --recursive $(PY_CHECK_ROOTS) endif py-format-assignments: - find . \ + find $(PY_CHECK_ROOTS) \ -path './.git' -prune -o \ -type f -name '*.py' \ -execdir /usr/bin/sed -i 's/^\(\s\+[a-zA-Z0-9_]\+\)=\([^,[:space:]]\+\)\([,(]\)*\s*$$/\1 = \2\3/g' {} '+' @@ -53,12 +55,12 @@ py-format-assignments: py-check-annotation-imports: ifneq ($(PY_CHECK_RUFF),) - $(PY_CHECK_RUFF) check --select TC,FA --diff --unsafe-fixes . + $(PY_CHECK_RUFF) check --select TC,FA --diff --unsafe-fixes $(PY_CHECK_ROOTS) endif py-format-annotation-imports: ifneq ($(PY_CHECK_RUFF),) - $(PY_CHECK_RUFF) check --select TC,FA --fix --unsafe-fixes . + $(PY_CHECK_RUFF) check --select TC,FA --fix --unsafe-fixes $(PY_CHECK_ROOTS) endif clean.topdir: clean.py-check -- 2.54.0 From c0c9148281827320236b04e05419e1b5b60aa3bb Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Fri, 5 Jun 2026 15:02:56 +0200 Subject: [PATCH 02/10] cmds.projects.CmdCreateFile: Add subdirs src/python In order to allow Pyright to check the types provided by dependant repositories without installing them, pyrightconfig.json contains a list of paths to their root directories in "extraPaths". These paths are unusable for Pyright, though: For type checking to work, it needs to be pointed to the "jw" namespace package paths within those repos. This commit achieves that by appending the subdirs src/python and tools/python to them, provided they exist. TODO: This fix hardcodes the current project directory structure. Better would be a way to customize that via makefiles, where the paths can be more easily customized. Signed-off-by: Jan Lindemann --- src/python/jw/pkg/cmds/projects/CmdCreateFile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py index 16a0f305..b59576fd 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py +++ b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py @@ -54,7 +54,7 @@ class CmdCreateFile(Cmd): # export extra_paths = [] for m in prereq: - path = self.app.find_dir(m) + 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 -- 2.54.0 From 739679412287168781bfc55a533e705911e221ae Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Tue, 2 Jun 2026 20:52:37 +0200 Subject: [PATCH 03/10] conf/templates: Add subdir Add $(TOPDIR)/conf/templates as a location for templates, i.e. input files to the CmdCreateFile template rendering command. Signed-off-by: Jan Lindemann --- conf/templates/Makefile | 4 ++++ make/defs.mk | 1 + 2 files changed, 5 insertions(+) create mode 100644 conf/templates/Makefile diff --git a/conf/templates/Makefile b/conf/templates/Makefile new file mode 100644 index 00000000..b1ec41b6 --- /dev/null +++ b/conf/templates/Makefile @@ -0,0 +1,4 @@ +TOPDIR = ../.. + +include $(TOPDIR)/make/proj.mk +include $(JWBDIR)/make/dirs.mk diff --git a/make/defs.mk b/make/defs.mk index 1622367e..f8beacec 100644 --- a/make/defs.mk +++ b/make/defs.mk @@ -264,6 +264,7 @@ CVS_RSH ?= /usr/bin/ssh JW_PKG_DIR = $(JWBDIR) JW_PKG_CONF_BASE_DIR ?= $(firstword $(wildcard $(JW_PKG_DIR)/conf /etc/opt/jw-pkg)) JW_PKG_CONF_DIR ?= $(firstword $(wildcard $(JW_PKG_CONF_BASE_DIR)/project $(JW_PKG_CONF_BASE_DIR))) +JW_PKG_TMPL_DIR ?= $(JW_PKG_CONF_BASE_DIR)/templates PROJECT_STEM = $(PROJECT)-$(DIST_VERSION) # only works if checked out true to CVS, i.e. below proj -- 2.54.0 From 841fb8b361677c1201c11aa589216e8137c0581e Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Tue, 2 Jun 2026 21:08:00 +0200 Subject: [PATCH 04/10] cmds.projects.lib.templates.tmpl_render(): search_path Add an additional keyword-argument search_path to templates.tmpl_render(). It allows to specifiy a list of directory paths in which a template of a given name can be found. It defaults to [], in which case only the built-in templates are considered. Otherwise file locations are tried first, then the built-in templates. Signed-off-by: Jan Lindemann --- .../jw/pkg/cmds/projects/lib/templates.py | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/python/jw/pkg/cmds/projects/lib/templates.py b/src/python/jw/pkg/cmds/projects/lib/templates.py index eec74085..d7388b95 100644 --- a/src/python/jw/pkg/cmds/projects/lib/templates.py +++ b/src/python/jw/pkg/cmds/projects/lib/templates.py @@ -1,6 +1,8 @@ import textwrap -def format_lines(template, values, li_quote, li_delimiter): +def format_lines( + template: str, values: dict[str, str], li_quote: bool, li_delimiter: str +) -> str: def __format_value(val): if not li_quote: @@ -63,11 +65,31 @@ _templates = { } def tmpl_render( - template_name: str, values, li_quote = False, li_delimiter = '\n' + template_name: str, + values, + li_quote = False, + li_delimiter = '\n', + search_path: list[str] = [] ) -> str: - return format_lines( - textwrap.dedent(_templates[template_name]), - values, - li_quote = li_quote, - li_delimiter = li_delimiter, - ) + + def __format(template: str) -> str: + return format_lines( + template, + values, + li_quote = li_quote, + li_delimiter = li_delimiter, + ) + + for d in search_path: + path = d + '/' + template_name + try: + with open(path, 'r') as f: + template = f.read() + return __format(template) + except FileNotFoundError: + pass + + raw = _templates.get(template_name, None) + if raw is None: + raise Exception(f'Failed to find template "{template_name}"') + return __format(textwrap.dedent(raw)) -- 2.54.0 From 81e71bc5c1852aad81b37e4c9365c66e07f123e6 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Thu, 4 Jun 2026 07:27:52 +0200 Subject: [PATCH 05/10] cmds.projects.lib.templates.tmpl_render(): RenderValues tmpl_render()'s "values" argument currently understands dict[str,str|list[str]]. Enhance that to understand the broader RenderValues type, which also includes Iterable[tuple[str, str]], as produced by argparse.add_argument(action='append'). This commit also adds proper type-checking for the values argument. Before, its type had gone unchecked entirely. Signed-off-by: Jan Lindemann --- .../jw/pkg/cmds/projects/CmdCreateFile.py | 2 +- .../pkg/cmds/projects/CmdCreatePkgConfig.py | 29 +++--- .../jw/pkg/cmds/projects/lib/templates.py | 92 +++++++++++++++++-- 3 files changed, 103 insertions(+), 20 deletions(-) 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] = [] -- 2.54.0 From 01be5941034858d5abac94e6f764ee6ff3bb1a49 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Tue, 2 Jun 2026 21:18:21 +0200 Subject: [PATCH 06/10] cmds.projects.CmdCreateFile: Support --search-path Add --search-path to the list of CmdCreateFile's supported options. Its value is split by ":" and subsequently passed to the tmpl_render()'s search_path argument. Signed-off-by: Jan Lindemann --- .../jw/pkg/cmds/projects/CmdCreateFile.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py index 58898b67..18497b88 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py +++ b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py @@ -5,7 +5,7 @@ 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 +from .lib.templates import RenderValues, tmpl_render def key_value(s): try: @@ -37,6 +37,21 @@ class CmdCreateFile(Cmd): # export 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, @@ -64,7 +79,7 @@ class CmdCreateFile(Cmd): # export } self.__update_dict(values, extra_fields) - return tmpl_render( + return self.__render( 'pyrightconfig.json', [values], li_quote = True, li_delimiter = ',\n' ) @@ -80,6 +95,11 @@ class CmdCreateFile(Cmd): # export 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', -- 2.54.0 From da877c624f3da414abdf6d0b4fe22a80b203cdc7 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Thu, 4 Jun 2026 07:29:29 +0200 Subject: [PATCH 07/10] cmds.projects.CmdCreateFile: Add more options - Add an additional, more generic value that --format understands: "tmpl". If chosen, the template selected by the new option --template-name is rendered by replacing --field key=value pairs. - This commit also adds the option --quote, which makes the renderer enclose the rendered variable values in double quotes. Signed-off-by: Jan Lindemann --- .../jw/pkg/cmds/projects/CmdCreateFile.py | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py index 18497b88..ce714b62 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py +++ b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py @@ -1,11 +1,10 @@ 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 +from .lib.templates import ListDict, RenderValues, tmpl_render def key_value(s): try: @@ -18,24 +17,26 @@ def key_value(s): 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 __jw_required(self, module: str | None = None) -> 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') + return 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, + ) def __render( self, @@ -52,23 +53,19 @@ class CmdCreateFile(Cmd): # export 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, + 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 prereq: + for m in self.__jw_required(): 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}"') @@ -77,10 +74,10 @@ class CmdCreateFile(Cmd): # export values: ListDict = { 'extra_paths': extra_paths, } - self.__update_dict(values, extra_fields) - return self.__render( - 'pyrightconfig.json', [values], li_quote = True, li_delimiter = ',\n' + 'pyrightconfig.json', [values, extra_fields], + li_quote = True, + li_delimiter = ',\n' ) def __init__(self, parent: Parent) -> None: @@ -92,14 +89,22 @@ class CmdCreateFile(Cmd): # export super().add_arguments(parser) parser.add_argument( '--format', - choices = [fmt.name.lower() for fmt in Fmt], - help = 'Output 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', -- 2.54.0 From 9ba6c70deede1658f305395e41e14c5c87f32a1e Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Fri, 5 Jun 2026 14:19:23 +0200 Subject: [PATCH 08/10] py-topdir.mk: Add --config pyproject.toml ruff tries to recursivley use every config file it finds and stumbles over a template: /usr/bin/ruff check --select TC,FA --fix --unsafe-fixes . ruff failed Cause: Failed to parse /home/jan/local/src/jw.dev/proj/jw-pkg/conf/templates/pyproject.toml Cause: TOML parse error at line 3, column 3 | 3 | {mypypath} | ^ invalid key-value pair, expected key Limiting it to the toplevel pyproject.toml by explicitly specifying --config fixes the behaviour, so that's what this commit does. Signed-off-by: Jan Lindemann --- make/py-topdir.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/make/py-topdir.mk b/make/py-topdir.mk index 471ee8b4..8cadbb07 100644 --- a/make/py-topdir.mk +++ b/make/py-topdir.mk @@ -7,6 +7,9 @@ endif ifndef PY_CHECK_RUFF PY_CHECK_RUFF := $(shell which ruff 2>/dev/null) + ifneq ($(PY_CHECK_RUFF),) + PY_CHECK_RUFF += --config pyproject.toml + endif endif ifndef PY_CHECK_YAPF -- 2.54.0 From 850efda804cf7baad5fedad74ddf86709e42b557 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Tue, 2 Jun 2026 21:22:02 +0200 Subject: [PATCH 09/10] py-topdir.mk: Add machinery to generate pyproject.toml pyproject.toml is currently copied unchanged from conf/topdir to the toplevel directory. Set up machinery in py-topdir.mk to render it from a template in conf/templates instead, replacing {mypypath} in the process. Signed-off-by: Jan Lindemann --- conf/{topdir => templates}/pyproject.toml | 0 make/py-topdir.mk | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) rename conf/{topdir => templates}/pyproject.toml (100%) diff --git a/conf/topdir/pyproject.toml b/conf/templates/pyproject.toml similarity index 100% rename from conf/topdir/pyproject.toml rename to conf/templates/pyproject.toml diff --git a/make/py-topdir.mk b/make/py-topdir.mk index 8cadbb07..a58e1207 100644 --- a/make/py-topdir.mk +++ b/make/py-topdir.mk @@ -1,6 +1,12 @@ -TD_COPY_FILES += pyproject.toml +include $(JWBDIR)/make/ldlibpath.mk + +TD_GENERATE_FILES += pyproject.toml PY_CHECK_EXCLUDE ?= + +MYPY_CONFIG_PATH = $(subst :,:$$MYPY_CONFIG_FILE_DIR/,:$(PYTHONPATH)) +MYPY_PATH_DIRECTIVE = mypy_path = "$(MYPY_CONFIG_PATH)" + ifndef PY_CHECK_ROOTS PY_CHECK_ROOTS += $(wildcard $(TOPDIR)/src $(TOPDIR)/tools) endif @@ -70,7 +76,12 @@ clean.topdir: clean.py-check clean.py-check: rm -rf .mypy_cache +pyproject.toml: + $(PYTHON) $(JWB_SCRIPT_DIR)/jw-pkg.py -p $(PROJECTS_DIR) -t $(TOPDIR) --topdir-format unaltered projects create-file --format tmpl \ + --template-name $@ --search-path $(JW_PKG_CONF_BASE_DIR)/templates --field mypypath='$(MYPY_PATH_DIRECTIVE)' $(PROJECT) > $@.tmp + mv $@.tmp $@ + pyrightconfig.json: $(PYTHON) $(JWB_SCRIPT_DIR)/jw-pkg.py -p $(PROJECTS_DIR) -t $(TOPDIR) --topdir-format unaltered projects create-file --format pyright \ - $(PROJECT) --field base=$(JW_PKG_CONF_BASE_DIR)/project/pyrightconfig-base.json $(addprefix --field include=,$(wildcard src/python tools/python)) > $@.tmp + --field base=$(JW_PKG_CONF_BASE_DIR)/project/pyrightconfig-base.json $(addprefix --field include=,$(wildcard src/python tools/python)) $(PROJECT) > $@.tmp mv $@.tmp $@ -- 2.54.0 From 4b6966c480dd413189d743c1d8f1200bf0827536 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Thu, 4 Jun 2026 07:23:56 +0200 Subject: [PATCH 10/10] pyproject.toml: Add {mypypath} Add a [tool.mypy] section to pyproject.toml with a {mypypath} template variable. The already existing template generation mechanism in py-topdir.mk should fill that in with a path pointing to all Python modules managed by jw-pkg: [tool.mypy] {mypypath} Signed-off-by: Jan Lindemann --- conf/templates/pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conf/templates/pyproject.toml b/conf/templates/pyproject.toml index 88acff3d..a9f1e109 100644 --- a/conf/templates/pyproject.toml +++ b/conf/templates/pyproject.toml @@ -1,3 +1,7 @@ +[tool.mypy] + + {mypypath} + [tool.isort] lines_between_sections = 1 -- 2.54.0