From 24928c6f5dcee575866f7d34567ad583e445a320 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Mon, 1 Jun 2026 07:45:22 +0200 Subject: [PATCH] cmds / lib: Fix more static checker findings Fix more errors and warnings produced by "make check" as reported by CI and a pyright upgrade. Signed-off-by: Jan Lindemann --- src/python/jw/pkg/cmds/CmdPkg.py | 4 +- src/python/jw/pkg/cmds/CmdPlatform.py | 4 +- src/python/jw/pkg/cmds/CmdPosix.py | 4 +- src/python/jw/pkg/cmds/CmdProjects.py | 4 +- src/python/jw/pkg/cmds/CmdSecrets.py | 4 +- .../pkg/cmds/projects/CmdCreatePkgConfig.py | 33 ++++--- .../jw/pkg/cmds/projects/CmdListRepos.py | 3 +- .../jw/pkg/cmds/projects/CmdPythonpath.py | 6 +- .../jw/pkg/cmds/projects/CmdRequiredOsPkg.py | 3 +- .../jw/pkg/cmds/projects/lib/pkg_relations.py | 60 +++++------ src/python/jw/pkg/lib/App.py | 5 +- src/python/jw/pkg/lib/ExecContext.py | 4 +- src/python/jw/pkg/lib/FileContext.py | 4 +- src/python/jw/pkg/lib/base.py | 5 +- src/python/jw/pkg/lib/ec/Local.py | 5 +- src/python/jw/pkg/lib/log.py | 99 ++++++++++--------- 16 files changed, 124 insertions(+), 123 deletions(-) diff --git a/src/python/jw/pkg/cmds/CmdPkg.py b/src/python/jw/pkg/cmds/CmdPkg.py index 0bfe5b53..5d4c07b7 100644 --- a/src/python/jw/pkg/cmds/CmdPkg.py +++ b/src/python/jw/pkg/cmds/CmdPkg.py @@ -9,5 +9,5 @@ class CmdPkg(CmdBase): # export super().__init__(parent, 'pkg', help = 'System package manager wrapper') self.load_subcommands() - def add_arguments(self, p: ArgumentParser) -> None: - super().add_arguments(p) + def add_arguments(self, parser: ArgumentParser) -> None: + super().add_arguments(parser) diff --git a/src/python/jw/pkg/cmds/CmdPlatform.py b/src/python/jw/pkg/cmds/CmdPlatform.py index c497d614..7e804dde 100644 --- a/src/python/jw/pkg/cmds/CmdPlatform.py +++ b/src/python/jw/pkg/cmds/CmdPlatform.py @@ -11,5 +11,5 @@ class CmdPlatform(CmdBase): # export ) self.load_subcommands() - def add_arguments(self, p: ArgumentParser) -> None: - super().add_arguments(p) + def add_arguments(self, parser: ArgumentParser) -> None: + super().add_arguments(parser) diff --git a/src/python/jw/pkg/cmds/CmdPosix.py b/src/python/jw/pkg/cmds/CmdPosix.py index afcd75d5..2d5e19ec 100644 --- a/src/python/jw/pkg/cmds/CmdPosix.py +++ b/src/python/jw/pkg/cmds/CmdPosix.py @@ -16,5 +16,5 @@ class CmdPosix(CmdBase): # export ) self.load_subcommands() - def add_arguments(self, p: ArgumentParser) -> None: - super().add_arguments(p) + def add_arguments(self, parser: ArgumentParser) -> None: + super().add_arguments(parser) diff --git a/src/python/jw/pkg/cmds/CmdProjects.py b/src/python/jw/pkg/cmds/CmdProjects.py index 321cfa39..5021f19c 100644 --- a/src/python/jw/pkg/cmds/CmdProjects.py +++ b/src/python/jw/pkg/cmds/CmdProjects.py @@ -15,8 +15,8 @@ class CmdProjects(CmdBase): # export ) self.load_subcommands() - def add_arguments(self, p: ArgumentParser) -> None: - super().add_arguments(p) + def add_arguments(self, parser: ArgumentParser) -> None: + super().add_arguments(parser) async def _run(self, args): # Missing subcommand diff --git a/src/python/jw/pkg/cmds/CmdSecrets.py b/src/python/jw/pkg/cmds/CmdSecrets.py index 506b14bb..0bee4d45 100644 --- a/src/python/jw/pkg/cmds/CmdSecrets.py +++ b/src/python/jw/pkg/cmds/CmdSecrets.py @@ -9,5 +9,5 @@ class CmdSecrets(CmdBase): # export super().__init__(parent, 'secrets', help = 'Manage package secrets') self.load_subcommands() - def add_arguments(self, p: ArgumentParser) -> None: - super().add_arguments(p) + def add_arguments(self, parser: ArgumentParser) -> None: + super().add_arguments(parser) diff --git a/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py b/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py index 2dd6e2a5..3f7f4397 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py +++ b/src/python/jw/pkg/cmds/projects/CmdCreatePkgConfig.py @@ -3,13 +3,14 @@ from argparse import ArgumentParser, Namespace from .Cmd import Cmd, Parent from .lib.templates import tmpl_render - class CmdCreatePkgConfig(Cmd): # export def __init__(self, parent: Parent) -> None: - super().__init__(parent, - 'create-pkg-config', - help='Generate a pkg-config file for a module') + super().__init__( + parent, + 'create-pkg-config', + help = 'Generate a pkg-config file for a module' + ) @staticmethod def __cleanup_requires(string: str) -> str: @@ -28,17 +29,17 @@ class CmdCreatePkgConfig(Cmd): # export def add_arguments(self, parser: ArgumentParser) -> None: super().add_arguments(parser) - parser.add_argument('-F', '--project-descr-file', default=None) - parser.add_argument('-d', '--description', default=None) - parser.add_argument('-n', '--name', default=None) - parser.add_argument('-s', '--summary', default=None) - parser.add_argument('-p', '--prefix', default=None) - parser.add_argument('-v', '--version', default=None) - parser.add_argument('-c', '--cflags', default=None) - parser.add_argument('-l', '--libflags', default=None) - parser.add_argument('-r', '--requires-run', default=None) - parser.add_argument('-R', '--requires-build', default=None) - parser.add_argument('-V', '--variables', nargs='*') + parser.add_argument('-F', '--project-descr-file', default = None) + parser.add_argument('-d', '--description', default = None) + parser.add_argument('-n', '--name', default = None) + parser.add_argument('-s', '--summary', default = None) + parser.add_argument('-p', '--prefix', default = None) + parser.add_argument('-v', '--version', default = None) + parser.add_argument('-c', '--cflags', default = None) + parser.add_argument('-l', '--libflags', default = None) + parser.add_argument('-r', '--requires-run', default = None) + parser.add_argument('-R', '--requires-build', default = None) + parser.add_argument('-V', '--variables', nargs = '*') async def _run(self, args: Namespace) -> None: project_conf_var_keys = [ @@ -47,7 +48,7 @@ class CmdCreatePkgConfig(Cmd): # export 'requires_run', 'requires_build', ] - merged: dict[str, str|None] = {} + merged: dict[str, str | None] = {} for key in project_conf_var_keys: val = getattr(args, key) if val is not None and args.project_descr_file: diff --git a/src/python/jw/pkg/cmds/projects/CmdListRepos.py b/src/python/jw/pkg/cmds/projects/CmdListRepos.py index 0aa1cecb..4f98db4f 100644 --- a/src/python/jw/pkg/cmds/projects/CmdListRepos.py +++ b/src/python/jw/pkg/cmds/projects/CmdListRepos.py @@ -3,7 +3,6 @@ import re from argparse import ArgumentParser, Namespace -from ...lib.base import Input from ...lib.log import DEBUG, log from ...lib.Uri import Uri from ...lib.util import get_password, get_username, run_curl_into @@ -78,7 +77,7 @@ class CmdListRepos(Cmd): # export print('\n'.join(result.stdout_str.splitlines())) return case 'https': - from jw.pkg.lib.base import InputMode + from jw.pkg.lib.base import Input, InputMode cmd_input: Input = InputMode.NonInteractive if re.match(r'https://github.com', args.base_url): diff --git a/src/python/jw/pkg/cmds/projects/CmdPythonpath.py b/src/python/jw/pkg/cmds/projects/CmdPythonpath.py index cd9f1176..3ef5b26d 100644 --- a/src/python/jw/pkg/cmds/projects/CmdPythonpath.py +++ b/src/python/jw/pkg/cmds/projects/CmdPythonpath.py @@ -10,9 +10,9 @@ class CmdPythonpath(Cmd): # export parent, 'pythonpath', help = 'Generate PYTHONPATH for given modules' ) - def add_arguments(self, p: ArgumentParser) -> None: - super().add_arguments(p) - p.add_argument('module', help = 'Modules', nargs = '*') + def add_arguments(self, parser: ArgumentParser) -> None: + super().add_arguments(parser) + parser.add_argument('module', help = 'Modules', nargs = '*') async def _run(self, args: Namespace) -> None: deps = self.app.get_project_refs( diff --git a/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py b/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py index 5d812b84..89d93fad 100644 --- a/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py +++ b/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py @@ -61,7 +61,6 @@ class CmdRequiredOsPkg(Cmd): # export vals = self.app.get_values(deps, ['pkg.requires.' + sec], [flavour]) if vals: requires |= set(vals) - if args.quote: - out = [f'"{dep}"' for dep in requires] + out = [f'"{dep}"' for dep in requires] if args.quote else requires # TODO: add all not in build tree as -devel print(' '.join(out)) diff --git a/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py b/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py index 20990632..b3ee1a68 100644 --- a/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py +++ b/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py @@ -68,6 +68,7 @@ def pkg_relations( if cur_pkg in visited or cur_pkg in ignore: continue for subsec in subsections: + version: str | None = None section = 'pkg.' + rel_type + '.' + subsec visited.add(cur_pkg) value = app.get_value(cur_pkg, section, flavour) @@ -111,37 +112,38 @@ def pkg_relations( raise Exception('Unknown version specifier in ' + spec) if len(dep) != 3 or not expand_semver_revision_range: expanded_deps = [dep] + continue + assert version is not None + expanded_deps = [] + semver = re.split(r'[.-]', version) + if len(semver) != 4: + expanded_deps = [dep] else: - expanded_deps = [] - semver = re.split(r'[.-]', version) - if len(semver) != 4: - expanded_deps = [dep] - else: - release = int(semver[2]) - major_minor = f'{semver[0]}.{semver[1]}' - match dep[1]: - case '>' | '>=': - expanded_deps.append([dep[0], dep[1], dep[2]]) - expanded_deps.append( - [dep[0], '<', f'{major_minor}.{release + 1}'] - ) - case '<' | '<=': - expanded_deps.append([dep[0], dep[1], dep[2]]) - case '=': - expanded_deps.append( - [dep[0], '>=', f'{major_minor}.{release}'] - ) - expanded_deps.append( - [dep[0], '<', f'{major_minor}.{release + 1}'] - ) - case _: - raise NotImplementedError( - ( - 'Expanding SemVer range ' - f'"{dep[0]} {dep[1]} {dep[3]}" ' - 'is not yet implemented' - ) + release = int(semver[2]) + major_minor = f'{semver[0]}.{semver[1]}' + match dep[1]: + case '>' | '>=': + expanded_deps.append([dep[0], dep[1], dep[2]]) + expanded_deps.append( + [dep[0], '<', f'{major_minor}.{release + 1}'] + ) + case '<' | '<=': + expanded_deps.append([dep[0], dep[1], dep[2]]) + case '=': + expanded_deps.append( + [dep[0], '>=', f'{major_minor}.{release}'] + ) + expanded_deps.append( + [dep[0], '<', f'{major_minor}.{release + 1}'] + ) + case _: + raise NotImplementedError( + ( + 'Expanding SemVer range ' + f'"{dep[0]} {dep[1]} {dep[3]}" ' + 'is not yet implemented' ) + ) for expanded_dep in expanded_deps: if hide_self and dep_name in seed_pkgs: continue diff --git a/src/python/jw/pkg/lib/App.py b/src/python/jw/pkg/lib/App.py index 4385b431..354e3d40 100644 --- a/src/python/jw/pkg/lib/App.py +++ b/src/python/jw/pkg/lib/App.py @@ -180,8 +180,8 @@ class App: # export class NoopCompleter(BaseCompleter): - def __call__(self, **kwargs): - return () + def __call__(self, *args, **kwargs): + return None import argcomplete @@ -224,6 +224,7 @@ class App: # export NOTICE, f'Writing profile statistics to {self.__args.write_profile}' ) + assert self.__args.write_profile is not None, 'args.write_profile' pr.dump_stats(self.__args.write_profile) if exit_status: diff --git a/src/python/jw/pkg/lib/ExecContext.py b/src/python/jw/pkg/lib/ExecContext.py index 8e501275..4ffcb390 100644 --- a/src/python/jw/pkg/lib/ExecContext.py +++ b/src/python/jw/pkg/lib/ExecContext.py @@ -587,8 +587,8 @@ class ExecContext(Base): cmd = ['mv', src, dst] await self.run(cmd, cmd_input = InputMode.NonInteractive) - async def _mkdir(self, name: str, mode: int) -> None: - cmd = ['mkdir', name, '-m', self.__mode_str(mode)] + async def _mkdir(self, path: str, mode: int) -> None: + cmd = ['mkdir', path, '-m', self.__mode_str(mode)] await self.run(cmd, cmd_input = InputMode.NonInteractive) async def _mktemp(self, tmpl: str, directory: bool) -> str: diff --git a/src/python/jw/pkg/lib/FileContext.py b/src/python/jw/pkg/lib/FileContext.py index 462cdf98..4c06a17c 100644 --- a/src/python/jw/pkg/lib/FileContext.py +++ b/src/python/jw/pkg/lib/FileContext.py @@ -177,10 +177,10 @@ class FileContext(abc.ABC): ) -> Result: mode_str = None if mode is None else oct(mode).replace('0o', '0') if self.__out_pipe is not None: - result = await self.__out_pipe.run(content) + content = (await self.__out_pipe.run(content)).stdout return await self._put( self._chroot(path), - result.stdout, + content, wd = wd, throw = throw, verbose = verbose, diff --git a/src/python/jw/pkg/lib/base.py b/src/python/jw/pkg/lib/base.py index dd42f431..c2c732bd 100644 --- a/src/python/jw/pkg/lib/base.py +++ b/src/python/jw/pkg/lib/base.py @@ -84,16 +84,14 @@ class Result: if err is None: return False import re - return re.search(pattern, err) is not None def __summarize(self, cmd: list[str] | None, wd: str | None = None) -> str: + from .util import pretty_cmd if cmd is None: cmd = self.__cmd call = '' if cmd is not None: - from .util import pretty_cmd - if wd is None: wd = self.__wd call = f'"{pretty_cmd(cmd, wd)}" ' @@ -164,7 +162,6 @@ class StatResult(NamedTuple): def from_os(cls, rhs: os.stat_result) -> StatResult: import grp import pwd - return StatResult( rhs.st_mode, pwd.getpwuid(rhs.st_uid).pw_name, diff --git a/src/python/jw/pkg/lib/ec/Local.py b/src/python/jw/pkg/lib/ec/Local.py index 8320f078..7466c4c8 100644 --- a/src/python/jw/pkg/lib/ec/Local.py +++ b/src/python/jw/pkg/lib/ec/Local.py @@ -161,7 +161,6 @@ class Local(Base): async def _erase(self, path: str) -> None: if os.path.isdir(path): import shutil - shutil.rmtree(path) return os.unlink(path) @@ -169,8 +168,8 @@ class Local(Base): async def _rename(self, src: str, dst: str) -> None: os.rename(src, dst) - async def _mkdir(self, name: str, mode: int) -> None: - os.mkdir(name, mode) + async def _mkdir(self, path: str, mode: int) -> None: + os.mkdir(path, mode) async def _stat(self, path: str, follow_symlinks: bool) -> StatResult: return StatResult.from_os(os.stat(path, follow_symlinks = follow_symlinks)) diff --git a/src/python/jw/pkg/lib/log.py b/src/python/jw/pkg/lib/log.py index 59c82913..776c5e8f 100644 --- a/src/python/jw/pkg/lib/log.py +++ b/src/python/jw/pkg/lib/log.py @@ -2,62 +2,65 @@ import datetime import sys import syslog -# fmt: disable # don't conflate -EMERG = int(syslog.LOG_EMERG) -ALERT = int(syslog.LOG_ALERT) -CRIT = int(syslog.LOG_CRIT) -ERR = int(syslog.LOG_ERR) -WARNING = int(syslog.LOG_WARNING) -NOTICE = int(syslog.LOG_NOTICE) -INFO = int(syslog.LOG_INFO) -DEBUG = int(syslog.LOG_DEBUG) -DEVEL = int(syslog.LOG_DEBUG + 1) -OFF = DEVEL + 1 +from typing import TYPE_CHECKING -_log_level = NOTICE -_last_tstamp = datetime.datetime.now() +if TYPE_CHECKING: + + from typing import Final + +# fmt: disable # don't conflate +# yapf: disable # don't conflate +EMERG = int(syslog.LOG_EMERG) +ALERT = int(syslog.LOG_ALERT) +CRIT = int(syslog.LOG_CRIT) +ERR = int(syslog.LOG_ERR) +WARNING = int(syslog.LOG_WARNING) +NOTICE = int(syslog.LOG_NOTICE) +INFO = int(syslog.LOG_INFO) +DEBUG = int(syslog.LOG_DEBUG) +DEVEL = int(syslog.LOG_DEBUG + 1) +OFF = DEVEL + 1 + +_log_level = NOTICE +_last_tstamp = datetime.datetime.now() _first_tstamp = _last_tstamp + +_LOG_LEVEL_NAME_BY_VALUE: Final[dict[int, str]] = { + EMERG: 'EMERG', + ALERT: 'ALERT', + CRIT: 'CRIT', + ERR: 'ERR', + WARNING: 'WARNING', + NOTICE: 'NOTICE', + INFO: 'INFO', + DEBUG: 'DEBUG', + DEVEL: 'DEVEL', + OFF: 'OFF', +} +# yapf: enable # fmt: enable -def _log_level_name_by_value(): - if _log_level_name_by_value.map is None: - _log_level_name_by_value.map = { - EMERG: 'EMERG', - ALERT: 'ALERT', - CRIT: 'CRIT', - ERR: 'ERR', - WARNING: 'WARNING', - NOTICE: 'NOTICE', - INFO: 'INFO', - DEBUG: 'DEBUG', - DEVEL: 'DEVEL', - OFF: 'OFF', - } - return _log_level_name_by_value.map - -_log_level_name_by_value.map: dict[int, str] | None = None # type: ignore - -def _log_level_value_by_name(): - if _log_level_value_by_name.map is None: - _log_level_value_by_name.map = {} - for value, name in _log_level_name_by_value().items(): - _log_level_value_by_name.map[name] = value - _log_level_value_by_name.map[name.lower()] = value - return _log_level_value_by_name.map - -_log_level_value_by_name.map: dict[str, int] | None = None # type: ignore +_LOG_LEVEL_VALUE_BY_NAME: Final[dict[str, int]] = { + alias: value + for value, name in _LOG_LEVEL_NAME_BY_VALUE.items() + for alias in (name, name.lower()) +} def get_log_level_name(level: int) -> str: - return _log_level_name_by_value()[level] + return _LOG_LEVEL_NAME_BY_VALUE[level] def parse_log_level(level: str | int) -> int: - try: - ret = int(level) - if ret >= 0 and ret <= DEVEL: - return ret - except ValueError: - return _log_level_value_by_name()[level] - raise Exception('Invalid log level ', level) + + def __int_level(level: int) -> int: + if level >= 0 and level <= DEVEL: + return level + raise Exception(f'Invalid log level number {level}') + + if isinstance(level, int): + return __int_level(level) + if level in _LOG_LEVEL_VALUE_BY_NAME: + return _LOG_LEVEL_VALUE_BY_NAME[level] + return __int_level(int(level)) def set_log_level(level: str | int | None = None) -> int: global _log_level