jw.pkg: Massively tighten linter checks #53

Merged
Jan Lindemann merged 31 commits from jan/feature/20260809-pyrightconfig-json-mirror-strict-mypy-settings into master 2026-08-09 15:12:37 +02:00 AGit
92 changed files with 588 additions and 324 deletions

View file

@ -1,6 +1,14 @@
{
"typeCheckingMode": "basic",
"typeCheckingMode": "strict",
"pythonPlatform": "Linux",
"useLibraryCodeForTypes": true,
"verboseOutput": false
"verboseOutput": false,
"reportImplicitOverride": "error",
"reportDeprecated": "error",
"reportMatchNotExhaustive": "error",
"reportUnnecessaryTypeIgnoreComment": "error",
"reportImplicitStringConcatenation": "error",
"reportUninitializedInstanceVariable": "error"
}

View file

@ -2,6 +2,19 @@
{mypypath}
strict = true
warn_unreachable = true
enable_error_code = [
"explicit-override",
"ignore-without-code",
"possibly-undefined",
"truthy-bool",
"truthy-iterable",
"exhaustive-match",
]
[tool.isort]
lines_between_sections = 1

View file

@ -10,7 +10,7 @@ import sys
from enum import Enum, auto
from functools import cache
from typing import TYPE_CHECKING
from typing import Any, cast, override, TYPE_CHECKING
from .lib.App import App as Base
from .lib.Distro import Distro
@ -19,6 +19,7 @@ from .lib.ProjectConf import ProjectConf
if TYPE_CHECKING:
import argparse
from argparse import ArgumentParser
from typing import TypeAlias
@ -36,10 +37,10 @@ if TYPE_CHECKING:
class ResultCache(object):
def __init__(self):
self.__cache = {}
def __init__(self) -> None:
self.__cache: dict[str, Any] = {}
def run(self, func, args):
def run(self, func: Any, args: list[Any]) -> object:
d = self.__cache
depth = 0
keys = [func.__name__] + args
@ -85,7 +86,7 @@ class App(Base):
return path
case 'relative':
return os.path.relpath(path)
case None | 'absolute':
case 'absolute':
return os.path.abspath(path)
case _:
m = re.search(r'^make:(\S+)$', fmt)
@ -136,21 +137,21 @@ class App(Base):
if search_absdirs is None:
search_absdirs = []
def __format_relpath(path: str):
def __format_relpath(path: str) -> str:
if path.startswith('./'):
return path[2:]
if path.endswith('/.'):
return path[:-2]
return path
def __relpath(target: str, base: str):
def __relpath(target: str, base: str) -> str:
return __format_relpath(os.path.relpath(target, base))
def __format_pd(name: str, pd: str, pretty: bool):
def __format_pd(name: str, pd: str, pretty: bool) -> str | None:
if not pretty:
return pd
if self.__topdir_fmt == 'absolute':
return os.path.abspath(pd)
return str(os.path.abspath(pd))
if self.__topdir_fmt == 'unaltered':
return pd
if self.__topdir_fmt == 'relative':
@ -174,6 +175,7 @@ class App(Base):
path = pd + '/' + sd
if os.path.isdir(path):
ret = __format_pd(name, pd, pretty)
assert ret is not None
if sd and sd[0] != '/':
if ret == '.':
ret = ''
@ -200,11 +202,22 @@ class App(Base):
return None
def __get_project_refs_cached(
self, buf, visited, spec, section, key, add_self, scope, names_only
):
return self.__res_cache.run(
self.__get_project_refs,
[buf, visited, spec, section, key, add_self, scope, names_only],
self,
buf: list[str],
visited: set[str],
spec: str,
section: str,
key: str,
add_self: bool,
scope: Scope,
names_only: bool,
) -> list[str]:
return cast(
'list[str]',
self.__res_cache.run(
self.__get_project_refs,
[buf, visited, spec, section, key, add_self, scope, names_only],
),
)
def __get_project_refs(
@ -283,7 +296,7 @@ class App(Base):
for dep in deps:
self.__read_dep_graph([dep], sections, graph)
def __flip_dep_graph(self, graph: Graph):
def __flip_dep_graph(self, graph: Graph) -> Graph:
ret: Graph = {}
for project, deps in graph.items():
for d in deps:
@ -374,12 +387,14 @@ class App(Base):
default_pkg_filter = pkg_filter,
)
async def __aexit__(self, exc_type, exc, tb) -> None:
@override
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
if self.__exec_context is not None:
await self.__exec_context.close()
self.__exec_context = None
def _add_arguments(self, parser) -> None:
@override
def _add_arguments(self, parser: ArgumentParser) -> None:
super()._add_arguments(parser)
parser.add_argument('-t', '--topdir', default = None, help = 'Project Path')
parser.add_argument(
@ -421,6 +436,7 @@ class App(Base):
help = 'Default filter for all distribution package-related operations',
)
@override
async def _run(self, args: argparse.Namespace) -> None:
self.___topdir = args.topdir
self.___pretty_topdir = self.__format_topdir(self.___topdir, args.topdir_format)
@ -482,7 +498,7 @@ class App(Base):
return self.__exec_context
@property
def top_name(self):
def top_name(self) -> str | None:
return self.__top_name
@property
@ -534,7 +550,7 @@ class App(Base):
def tmpl_dir(self, name: str) -> str | None:
return self.find_dir(name, ['/tmpl'], ['/opt/' + name + '/share/tmpl'])
def strip_module_from_spec(self, mod):
def strip_module_from_spec(self, mod: str) -> str:
return re.sub(r'-dev$|-devel$|-run$', '', re.split('([=><]+)', mod)[0].strip())
@cache
@ -569,7 +585,7 @@ class App(Base):
return ret
@cache
def get_version(self, project) -> str:
def get_version(self, project: str) -> str:
ret = self.get_value(project, 'version', '')
if ret is None:
raise Exception(f"Can't get version of project {project}")
@ -598,9 +614,9 @@ class App(Base):
projects: list[str],
sections: list[str],
keys: str | list[str],
add_self: bool,
scope: Scope,
names_only = True,
scope: Scope = Scope.One,
add_self: bool = False,
names_only: bool = False,
) -> list[str]:
if isinstance(keys, str):
keys = [keys]

View file

@ -1,8 +1,10 @@
from typing import override
from .App import App
from .lib.Cmd import Cmd as Base
class CmdBase(Base):
@override # pyright: ignore[reportArgumentType]
@property
def app(self) -> App:
ret = super().app

View file

@ -1,11 +1,12 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import override, TYPE_CHECKING
from ..App import App as Parent
from ..CmdBase import CmdBase as Base
if TYPE_CHECKING:
from argparse import Namespace
from typing import Iterable
from ..lib.Distro import Distro
@ -20,7 +21,8 @@ class Cmd(Base): # export
) -> None:
super().__init__(parent, name, help, aliases = aliases)
async def _run(self, args):
@override
async def _run(self, args: 'Namespace') -> None:
# Missing subcommand
self.print_help(1)

View file

@ -1,11 +1,11 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
if TYPE_CHECKING:
from argparse import ArgumentParser
from argparse import ArgumentParser, Namespace
class CmdPkg(Cmd): # export
@ -18,12 +18,14 @@ class CmdPkg(Cmd): # export
)
self.load_subcommands()
async def _run(self, args):
@override
async def _run(self, args: Namespace) -> None:
import sys
# Missing subcommand
self.parser.print_help()
sys.exit(1)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -1,11 +1,11 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
if TYPE_CHECKING:
from argparse import ArgumentParser
from argparse import ArgumentParser, Namespace
class CmdPlatform(Cmd): # export
@ -15,12 +15,14 @@ class CmdPlatform(Cmd): # export
)
self.load_subcommands()
async def _run(self, args):
@override
async def _run(self, args: Namespace) -> None:
import sys
# Missing subcommand
self.parser.print_help()
sys.exit(1)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -1,11 +1,11 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
if TYPE_CHECKING:
from argparse import ArgumentParser
from argparse import ArgumentParser, Namespace
class CmdPosix(Cmd): # export
@ -20,12 +20,14 @@ class CmdPosix(Cmd): # export
)
self.load_subcommands()
async def _run(self, args):
@override
async def _run(self, args: Namespace) -> None:
import sys
# Missing subcommand
self.parser.print_help()
sys.exit(1)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -1,11 +1,11 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
if TYPE_CHECKING:
from argparse import ArgumentParser
from argparse import ArgumentParser, Namespace
class CmdProjects(Cmd): # export
@ -17,12 +17,14 @@ class CmdProjects(Cmd): # export
)
self.load_subcommands()
async def _run(self, args):
@override
async def _run(self, args: Namespace) -> None:
import sys
# Missing subcommand
self.parser.print_help()
sys.exit(1)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -1,11 +1,11 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
if TYPE_CHECKING:
from argparse import ArgumentParser
from argparse import ArgumentParser, Namespace
class CmdSecrets(Cmd): # export
@ -13,12 +13,14 @@ class CmdSecrets(Cmd): # export
super().__init__(parent, 'secrets', help = 'Manage package secrets')
self.load_subcommands()
async def _run(self, args):
@override
async def _run(self, args: Namespace) -> None:
import sys
# Missing subcommand
self.parser.print_help()
sys.exit(1)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
@ -12,11 +12,13 @@ class CmdDelete(Cmd): # export
def __init__(self, parent: Parent) -> None:
super().__init__(parent, 'delete', help = 'Delete packages by name')
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument(
'names', nargs = '*', help = 'Names of packages to be deleted'
)
@override
async def _run(self, args: Namespace) -> None:
return await self.distro.delete(args.names)

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
@ -12,6 +12,7 @@ class CmdDup(Cmd): # export
def __init__(self, parent: Parent) -> None:
super().__init__(parent, 'dup', help = 'Upgrade distribution')
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument(
@ -21,5 +22,6 @@ class CmdDup(Cmd): # export
help = "Only download packages from the repos, don't install them, yet",
)
@override
async def _run(self, args: Namespace) -> None:
return await self.distro.dup(download_only = args.download_only)

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import override, TYPE_CHECKING
from .Cmd import Cmd, Parent
@ -16,6 +16,7 @@ class CmdInstall(Cmd): # export
help = "Install the distribution's notion of available packages",
)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument('names', nargs = '*', help = 'Packages to be installed')
@ -32,6 +33,7 @@ class CmdInstall(Cmd): # export
help = "Don't expand macros in <names>",
)
@override
async def _run(self, args: Namespace) -> None:
names = (
args.names if args.fixed_strings else self.distro.expand_macros(args.names)

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .NamedPkgsCmd import NamedPkgsCmd
@ -14,9 +14,11 @@ class CmdLs(NamedPkgsCmd): # export
def __init__(self, parent: Parent) -> None:
super().__init__(parent, 'ls', help = 'List package contents')
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
@override
async def _run(self, args: Namespace) -> None:
for name in args.names:
print('\n'.join(await self.distro.pkg_files(name)))

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .NamedPkgsCmd import NamedPkgsCmd, Parent
@ -12,9 +12,11 @@ class CmdMeta(NamedPkgsCmd): # export
def __init__(self, parent: Parent) -> None:
super().__init__(parent, 'meta', help = 'List package metadata')
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
@override
async def _run(self, args: Namespace) -> None:
names = await self.distro.select(args.names)
for name in names:

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
@ -16,8 +16,10 @@ class CmdRebootRequired(Cmd): # export
help = 'Check whether the machine needs rebooting'
)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
@override
async def _run(self, args: Namespace) -> None:
await self.distro.reboot_required()

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
@ -16,8 +16,10 @@ class CmdRefresh(Cmd): # export
help = "Refresh the distribution's notion of available packages",
)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
@override
async def _run(self, args: Namespace) -> None:
return await self.distro.ref()

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from ...lib.PackageFilter import PackageFilterString
from .Cmd import Cmd, Parent
@ -13,10 +13,12 @@ class CmdSelect(Cmd): # export
def __init__(self, parent: Parent) -> None:
super().__init__(parent, 'select', help = 'Select packages by filter')
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument('filter', help = 'Package filter string')
@override
async def _run(self, args: Namespace) -> None:
filter = PackageFilterString(args.filter) if args.filter else None
for p in await self.distro.select(filter = filter):

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent
@ -12,6 +12,7 @@ class NamedPkgsCmd(Cmd): # export
def __init__(self, parent: Parent, name: str, help: str) -> None:
super().__init__(parent, name, help)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument('names', nargs = '*', help = 'Package names')

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from ...lib.Distro import Distro
from .Cmd import Cmd, Parent
@ -15,6 +15,7 @@ class CmdInfo(Cmd): # export
parent, 'info', help = 'Retrieve information about target platform'
)
@override
def add_arguments(self, parser: ArgumentParser) -> None:
super().add_arguments(parser)
parser.add_argument(
@ -23,5 +24,6 @@ class CmdInfo(Cmd): # export
help = f'Format string, expanding macros {", ".join(Distro.macros())}',
)
@override
async def _run(self, args: Namespace) -> None:
print(self.app.distro.expand_macros(args.format))

Some files were not shown because too many files have changed in this diff Show more