App, cmds, lib: Fix Any returns from typed functions

Add type annotations and casts to functions that were returning Any
where a specific type was declared, satisfying the new warn_return_any
mypy rule.

Fixes:
- log.py: get_caller_pos return type via cast
- AsyncRunner.py: cast T for fut.result()
- util.py: cast for getattr result, str() for args.username
- FileContext.py: verbose_default bool annotation
- SSHClient.py: cast SSHClient for dynamic import
- lib/App.py: cast ArgumentParser, add return types to inner funcs
- pm/rpm.py, dpkg.py: cast Iterable[Package]
- App.py: cast for self.args.func(), add return types to inner funcs
- BaseCmdPkgRelations.py: cast str for args.delimiter

Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-07-22 23:03:53 +02:00
commit 1e613a39c6
40 changed files with 228 additions and 179 deletions

View file

@ -10,7 +10,7 @@ import sys
from enum import Enum, auto from enum import Enum, auto
from functools import cache from functools import cache
from typing import Any, override, TYPE_CHECKING from typing import Any, cast, override, TYPE_CHECKING
from .lib.App import App as Base from .lib.App import App as Base
from .lib.Distro import Distro from .lib.Distro import Distro
@ -40,7 +40,7 @@ class ResultCache(object):
def __init__(self) -> None: def __init__(self) -> None:
self.__cache: dict[str, Any] = {} self.__cache: dict[str, Any] = {}
def run(self, func, args: list[Any]) -> object: def run(self, func: Any, args: list[Any]) -> object:
d = self.__cache d = self.__cache
depth = 0 depth = 0
keys = [func.__name__] + args keys = [func.__name__] + args
@ -137,21 +137,21 @@ class App(Base):
if search_absdirs is None: if search_absdirs is None:
search_absdirs = [] search_absdirs = []
def __format_relpath(path: str): def __format_relpath(path: str) -> str:
if path.startswith('./'): if path.startswith('./'):
return path[2:] return path[2:]
if path.endswith('/.'): if path.endswith('/.'):
return path[:-2] return path[:-2]
return path return path
def __relpath(target: str, base: str): def __relpath(target: str, base: str) -> str:
return __format_relpath(os.path.relpath(target, base)) 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: if not pretty:
return pd return pd
if self.__topdir_fmt == 'absolute': if self.__topdir_fmt == 'absolute':
return os.path.abspath(pd) return str(os.path.abspath(pd))
if self.__topdir_fmt == 'unaltered': if self.__topdir_fmt == 'unaltered':
return pd return pd
if self.__topdir_fmt == 'relative': if self.__topdir_fmt == 'relative':
@ -175,6 +175,7 @@ class App(Base):
path = pd + '/' + sd path = pd + '/' + sd
if os.path.isdir(path): if os.path.isdir(path):
ret = __format_pd(name, pd, pretty) ret = __format_pd(name, pd, pretty)
assert ret is not None
if sd and sd[0] != '/': if sd and sd[0] != '/':
if ret == '.': if ret == '.':
ret = '' ret = ''
@ -210,10 +211,13 @@ class App(Base):
add_self: bool, add_self: bool,
scope: Scope, scope: Scope,
names_only: bool, names_only: bool,
): ) -> list[str]:
return self.__res_cache.run( return cast(
self.__get_project_refs, 'list[str]',
[buf, visited, spec, section, key, add_self, scope, names_only], self.__res_cache.run(
self.__get_project_refs,
[buf, visited, spec, section, key, add_self, scope, names_only],
),
) )
def __get_project_refs( def __get_project_refs(
@ -292,7 +296,7 @@ class App(Base):
for dep in deps: for dep in deps:
self.__read_dep_graph([dep], sections, graph) self.__read_dep_graph([dep], sections, graph)
def __flip_dep_graph(self, graph: Graph): def __flip_dep_graph(self, graph: Graph) -> Graph:
ret: Graph = {} ret: Graph = {}
for project, deps in graph.items(): for project, deps in graph.items():
for d in deps: for d in deps:
@ -384,7 +388,7 @@ class App(Base):
) )
@override @override
async def __aexit__(self, exc_type, exc, tb) -> None: async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
if self.__exec_context is not None: if self.__exec_context is not None:
await self.__exec_context.close() await self.__exec_context.close()
self.__exec_context = None self.__exec_context = None
@ -494,7 +498,7 @@ class App(Base):
return self.__exec_context return self.__exec_context
@property @property
def top_name(self): def top_name(self) -> str | None:
return self.__top_name return self.__top_name
@property @property
@ -581,7 +585,7 @@ class App(Base):
return ret return ret
@cache @cache
def get_version(self, project) -> str: def get_version(self, project: str) -> str:
ret = self.get_value(project, 'version', '') ret = self.get_value(project, 'version', '')
if ret is None: if ret is None:
raise Exception(f"Can't get version of project {project}") raise Exception(f"Can't get version of project {project}")
@ -610,9 +614,9 @@ class App(Base):
projects: list[str], projects: list[str],
sections: list[str], sections: list[str],
keys: str | list[str], keys: str | list[str],
add_self: bool, scope: Scope = Scope.One,
scope: Scope, add_self: bool = False,
names_only = True, names_only: bool = False,
) -> list[str]: ) -> list[str]:
if isinstance(keys, str): if isinstance(keys, str):
keys = [keys] keys = [keys]

View file

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

View file

@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent from .Cmd import Cmd, Parent
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
class CmdPkg(Cmd): # export class CmdPkg(Cmd): # export
@ -19,7 +19,7 @@ class CmdPkg(Cmd): # export
self.load_subcommands() self.load_subcommands()
@override @override
async def _run(self, args): async def _run(self, args: Namespace) -> None:
import sys import sys
# Missing subcommand # Missing subcommand

View file

@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent from .Cmd import Cmd, Parent
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
class CmdPlatform(Cmd): # export class CmdPlatform(Cmd): # export
@ -16,7 +16,7 @@ class CmdPlatform(Cmd): # export
self.load_subcommands() self.load_subcommands()
@override @override
async def _run(self, args): async def _run(self, args: Namespace) -> None:
import sys import sys
# Missing subcommand # Missing subcommand

View file

@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent from .Cmd import Cmd, Parent
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
class CmdPosix(Cmd): # export class CmdPosix(Cmd): # export
@ -21,7 +21,7 @@ class CmdPosix(Cmd): # export
self.load_subcommands() self.load_subcommands()
@override @override
async def _run(self, args): async def _run(self, args: Namespace) -> None:
import sys import sys
# Missing subcommand # Missing subcommand

View file

@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent from .Cmd import Cmd, Parent
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
class CmdProjects(Cmd): # export class CmdProjects(Cmd): # export
@ -18,7 +18,7 @@ class CmdProjects(Cmd): # export
self.load_subcommands() self.load_subcommands()
@override @override
async def _run(self, args): async def _run(self, args: Namespace) -> None:
import sys import sys
# Missing subcommand # Missing subcommand

View file

@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent from .Cmd import Cmd, Parent
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
class CmdSecrets(Cmd): # export class CmdSecrets(Cmd): # export
@ -14,7 +14,7 @@ class CmdSecrets(Cmd): # export
self.load_subcommands() self.load_subcommands()
@override @override
async def _run(self, args): async def _run(self, args: Namespace) -> None:
import sys import sys
# Missing subcommand # Missing subcommand

View file

@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, override
from .Cmd import Cmd, Parent from .Cmd import Cmd, Parent
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
class CmdTar(Cmd): # export class CmdTar(Cmd): # export
@ -14,7 +14,7 @@ class CmdTar(Cmd): # export
self.load_subcommands() self.load_subcommands()
@override @override
async def _run(self, args): async def _run(self, args: Namespace) -> None:
import sys import sys
# Missing subcommand # Missing subcommand

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from typing import override, TYPE_CHECKING from typing import Any, override, TYPE_CHECKING
from ....CmdBase import CmdBase as Base from ....CmdBase import CmdBase as Base
from ....lib.FileContext import FileContext from ....lib.FileContext import FileContext
@ -20,7 +20,7 @@ class Cmd(Base): # export
self.__tar_io: None = None self.__tar_io: None = None
@asynccontextmanager @asynccontextmanager
async def ctx(self, **kwargs) -> AsyncIterator[TarIo]: async def ctx(self, **kwargs: Any) -> AsyncIterator[TarIo]:
async with TarIo.create(src = self.app.args.archive_path, **kwargs) as ret: async with TarIo.create(src = self.app.args.archive_path, **kwargs) as ret:
ret.src.add_proc_filter( ret.src.add_proc_filter(
FileContext.Direction.In, ProcFilterGpg(ec = self.app.exec_context) FileContext.Direction.In, ProcFilterGpg(ec = self.app.exec_context)

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import re import re
from typing import TYPE_CHECKING, override from typing import TYPE_CHECKING, cast, override
from .Cmd import Cmd, Parent from .Cmd import Cmd, Parent
from .lib.pkg_relations import VersionSyntax from .lib.pkg_relations import VersionSyntax
@ -17,7 +17,7 @@ class BaseCmdPkgRelations(Cmd):
def pkg_relations(self, rel_type: str, args: Namespace) -> str: def pkg_relations(self, rel_type: str, args: Namespace) -> str:
return args.delimiter.join( return cast('str', args.delimiter).join(
pkg_relations_list( pkg_relations_list(
self.app, self.app,
rel_type = rel_type, rel_type = rel_type,

View file

@ -34,7 +34,7 @@ class CmdCanonicalizeRemotes(Cmd): # export
@override @override
async def _run(self, args: Namespace) -> None: async def _run(self, args: Namespace) -> None:
async def git(cmd: list[str], ro = False, throw = True) -> Result: async def git(cmd: list[str], ro: bool = False, throw: bool = True) -> Result:
cmd = ['/usr/bin/git', *cmd] cmd = ['/usr/bin/git', *cmd]
log(NOTICE, f'-- {" ".join(cmd)}') log(NOTICE, f'-- {" ".join(cmd)}')
if ro or not args.dry_run: if ro or not args.dry_run:

View file

@ -4,7 +4,7 @@ from .Cmd import Cmd, Parent
from typing import TYPE_CHECKING, override from typing import TYPE_CHECKING, override
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
class CmdCheck(Cmd): # export class CmdCheck(Cmd): # export
@ -17,7 +17,7 @@ class CmdCheck(Cmd): # export
self.load_subcommands() self.load_subcommands()
@override @override
async def _run(self, args): async def _run(self, args: Namespace) -> None:
import sys import sys
# Missing subcommand # Missing subcommand

View file

@ -7,7 +7,7 @@ from .Cmd import Cmd, Parent
from .lib.pkg_relations import VersionSyntax, pkg_relations from .lib.pkg_relations import VersionSyntax, pkg_relations
from .lib.templates import ListDict, RenderValues, tmpl_render from .lib.templates import ListDict, RenderValues, tmpl_render
def key_value(s): def key_value(s: str) -> tuple[str, str]:
try: try:
key, value = s.split('=', 1) key, value = s.split('=', 1)
except ValueError: except ValueError:
@ -51,8 +51,8 @@ class CmdCreateFile(Cmd): # export
self, self,
template_name: str, template_name: str,
values: list[RenderValues], values: list[RenderValues],
li_quote = False, li_quote: bool = False,
li_delimiter = '\n', li_delimiter: str = '\n',
) -> str: ) -> str:
return tmpl_render( return tmpl_render(
template_name, template_name,

View file

@ -26,8 +26,8 @@ def pkg_relations(
ignore: set[str] = set(), ignore: set[str] = set(),
quote: bool = False, quote: bool = False,
skip_excluded: bool = False, skip_excluded: bool = False,
hide_self = False, hide_self: bool = False,
hide_jw_pkg = False, hide_jw_pkg: bool = False,
) -> list[str]: ) -> list[str]:
if subsections is None: if subsections is None:

View file

@ -1,5 +1,5 @@
import textwrap import textwrap
from typing import Iterable, TypeAlias, TypeGuard from typing import Any, Iterable, TypeAlias, TypeGuard
TupleList: TypeAlias = Iterable[tuple[str, str]] TupleList: TypeAlias = Iterable[tuple[str, str]]
ListDict: TypeAlias = dict[str, list[str]] ListDict: TypeAlias = dict[str, list[str]]
@ -73,7 +73,7 @@ def format_list_dict(
template: str, values: ListDict | dict[str, str], li_quote: bool, li_delimiter: str template: str, values: ListDict | dict[str, str], li_quote: bool, li_delimiter: str
) -> str: ) -> str:
def __format_value(val): def __format_value(val: Any) -> str:
if not li_quote: if not li_quote:
return str(val) return str(val)
return f'"{val}"' return f'"{val}"'
@ -144,9 +144,9 @@ _templates = {
def tmpl_render( def tmpl_render(
template_name: str, template_name: str,
values: list[RenderValues], values: list[RenderValues],
li_quote = False, li_quote: bool = False,
li_delimiter = '\n', li_delimiter: str = '\n',
search_path: list[str] = [] search_path: list[str] | None = None,
) -> str: ) -> str:
def __format(template: str) -> str: def __format(template: str) -> str:
@ -157,7 +157,7 @@ def tmpl_render(
li_delimiter = li_delimiter, li_delimiter = li_delimiter,
) )
for d in search_path: for d in search_path if search_path else []:
path = d + '/' + template_name path = d + '/' + template_name
try: try:
with open(path, 'r') as f: with open(path, 'r') as f:

View file

@ -23,7 +23,11 @@ class FilesContext:
def ctx(self) -> FileContext: def ctx(self) -> FileContext:
return self.__ctx return self.__ctx
async def _read_key_value_file(self, path: str, throw = False) -> dict[str, str]: async def _read_key_value_file(
self,
path: str,
throw: bool = False,
) -> dict[str, str]:
ret: dict[str, str] = {} ret: dict[str, str] = {}
try: try:
result = await self.ctx.get(path) result = await self.ctx.get(path)

View file

@ -6,7 +6,7 @@ import os
import sys import sys
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
from typing import override, TYPE_CHECKING, Any from typing import Any, cast, override, TYPE_CHECKING
from .AsyncRunner import AsyncRunner from .AsyncRunner import AsyncRunner
from .log import DEBUG, ERR, NOTICE, log, log_m, set_log_flags, set_log_level from .log import DEBUG, ERR, NOTICE, log, log_m, set_log_flags, set_log_level
@ -52,12 +52,15 @@ class App: # export
) -> None: ) -> None:
def add_cmd_to_parser(cmd: AbstractCmd, parsers: Any) -> ArgumentParser: def add_cmd_to_parser(cmd: AbstractCmd, parsers: Any) -> ArgumentParser:
parser = parsers.add_parser( parser = cast(
cmd.name, 'ArgumentParser',
help = cmd.help, parsers.add_parser(
description = cmd.description, cmd.name,
aliases = cmd.aliases, help = cmd.help,
formatter_class = ArgumentDefaultsHelpFormatter, description = cmd.description,
aliases = cmd.aliases,
formatter_class = ArgumentDefaultsHelpFormatter,
)
) )
parser.set_defaults(func = cmd.run) parser.set_defaults(func = cmd.run)
cmd.add_arguments(parser) cmd.add_arguments(parser)
@ -68,7 +71,7 @@ class App: # export
parent: AbstractCmd | App, parent: AbstractCmd | App,
parser: ArgumentParser, parser: ArgumentParser,
cmds: Collection[AbstractCmd], cmds: Collection[AbstractCmd],
all = False all: bool = False
) -> None: ) -> None:
if not cmds: if not cmds:
return return
@ -168,7 +171,7 @@ class App: # export
'-h', '--help', action = 'help', help = 'Show this help message and exit' '-h', '--help', action = 'help', help = 'Show this help message and exit'
) )
def __del__(self): def __del__(self) -> None:
if self.__own_eloop: if self.__own_eloop:
if self.__eloop is not None: if self.__eloop is not None:
self.__eloop.close() self.__eloop.close()
@ -178,10 +181,10 @@ class App: # export
async def __aenter__(self) -> None: async def __aenter__(self) -> None:
pass pass
async def __aexit__(self, exc_type, exc, tb) -> None: async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
pass pass
async def __run(self, argv = None) -> None: async def __run(self, argv: list[str] | None = None) -> None:
try: try:
# Import argcomplete only here to not require it to be compatible # Import argcomplete only here to not require it to be compatible
@ -194,8 +197,8 @@ class App: # export
@override @override
def __call__( # pyright: ignore[reportGeneralTypeIssues] def __call__( # pyright: ignore[reportGeneralTypeIssues]
self, *args, **kwargs self, *args: Any, **kwargs: Any
): ) -> None:
return None return None
import argcomplete # type: ignore[import-not-found, unused-ignore] import argcomplete # type: ignore[import-not-found, unused-ignore]
@ -249,7 +252,7 @@ class App: # export
self.__parser.print_help() self.__parser.print_help()
return None return None
# Run sub-command. Overwrite if you want to do anything before or after # Run sub-command. Overwrite if you want to do anything before or after
return await self.args.func(args) return cast('None | int', await self.args.func(args))
def call_async(self, awaitable: Awaitable[T], timeout: float | None = None) -> T: def call_async(self, awaitable: Awaitable[T], timeout: float | None = None) -> T:
return self.async_runner.call(awaitable, timeout) return self.async_runner.call(awaitable, timeout)
@ -286,7 +289,7 @@ class App: # export
def parser(self) -> ArgumentParser: def parser(self) -> ArgumentParser:
return self.__parser return self.__parser
def run(self, argv = None) -> None: def run(self, argv: list[str] | None = None) -> None:
try: try:
ret = self.eloop.run_until_complete(self.__run(argv)) ret = self.eloop.run_until_complete(self.__run(argv))
finally: finally:
@ -296,7 +299,10 @@ class App: # export
return ret return ret
def run_sub_commands( # export def run_sub_commands( # export
description = '', name_filter = '^Cmd.*', modules = None, argv = None description: str = '',
): name_filter: str = '^Cmd.*',
modules: list[str] | None = None,
argv: list[str] | None = None,
) -> None:
app = App(description, name_filter, modules) app = App(description, name_filter, modules)
return app.run(argv = argv) app.run(argv = argv)

View file

@ -4,7 +4,7 @@ import asyncio
import concurrent.futures import concurrent.futures
import contextlib import contextlib
from typing import TypeVar, TYPE_CHECKING from typing import Any, TypeVar, TYPE_CHECKING, cast
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Awaitable, Generator from collections.abc import Awaitable, Generator
@ -48,7 +48,7 @@ class AsyncRunner:
awaitable, # type: ignore[arg-type, var-annotated] awaitable, # type: ignore[arg-type, var-annotated]
self._loop, self._loop,
) )
return fut.result(timeout) return cast('T', fut.result(timeout))
def close(self) -> None: def close(self) -> None:
self._cm.__exit__(None, None, None) self._cm.__exit__(None, None, None)
@ -56,5 +56,5 @@ class AsyncRunner:
def __enter__(self) -> AsyncRunner: def __enter__(self) -> AsyncRunner:
return self return self
def __exit__(self, exc_type, exc, tb) -> None: def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
self.close() self.close()

View file

@ -9,7 +9,7 @@ from .log import ERR
from .Types import LoadTypes, Types from .Types import LoadTypes, Types
if TYPE_CHECKING: if TYPE_CHECKING:
from argparse import ArgumentParser from argparse import ArgumentParser, Namespace
from typing import Iterable from typing import Iterable
from .App import App from .App import App
@ -26,7 +26,7 @@ class AbstractCmd(abc.ABC):
self.__child_classes: list[type[Cmd]] = [] self.__child_classes: list[type[Cmd]] = []
self.__parser: ArgumentParser | None = None self.__parser: ArgumentParser | None = None
def set_parent(self, parent: Any | Cmd): def set_parent(self, parent: Any | Cmd) -> None:
self.__parent = parent self.__parent = parent
@property @property
@ -76,7 +76,7 @@ class AbstractCmd(abc.ABC):
return self.__parser return self.__parser
# Don't use a setter decorator to force using a grepable method # Don't use a setter decorator to force using a grepable method
def set_parser(self, parser: ArgumentParser): def set_parser(self, parser: ArgumentParser) -> None:
self.__parser = parser self.__parser = parser
def print_help(self, exit_status: int | None = None) -> None: def print_help(self, exit_status: int | None = None) -> None:
@ -129,11 +129,11 @@ class AbstractCmd(abc.ABC):
pass pass
@abc.abstractmethod @abc.abstractmethod
async def _run(self, args) -> None: async def _run(self, args: Namespace) -> None:
if isinstance(self.__parent, Cmd): # Calling App.run() would loop if isinstance(self.__parent, Cmd): # Calling App.run() would loop
return await self.__parent._run(args) return await self.__parent._run(args)
async def run(self, args): async def run(self, args: Namespace) -> None:
return await self._run(args) return await self._run(args)
@abc.abstractmethod @abc.abstractmethod

View file

@ -1,4 +1,4 @@
from typing import Self from typing import Any, Self
from .FileContext import FileContext from .FileContext import FileContext
from .Uri import Uri from .Uri import Uri
@ -9,7 +9,7 @@ class CopyContext:
self, self,
src: Uri | str | FileContext, src: Uri | str | FileContext,
dst: Uri | str | FileContext, dst: Uri | str | FileContext,
chroot = False chroot: bool = False,
) -> None: ) -> None:
def __uri(ctx: FileContext | Uri | str) -> Uri | str: def __uri(ctx: FileContext | Uri | str) -> Uri | str:
@ -43,7 +43,7 @@ class CopyContext:
await self.__dst.open() await self.__dst.open()
return self return self
async def __aexit__(self, exc_type, exc, tb): async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
if self.__src is not None: if self.__src is not None:
await self.__src.close() await self.__src.close()
self.__src = None self.__src = None

View file

@ -6,7 +6,7 @@ import re
import sys import sys
from functools import cached_property from functools import cached_property
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Any
from .log import ERR, INFO, WARNING, log from .log import ERR, INFO, WARNING, log
from .base import InputMode from .base import InputMode
@ -108,8 +108,8 @@ class Distro(abc.ABC):
ec: ExecContext, ec: ExecContext,
id: str | None = None, id: str | None = None,
os_release_str: str | None = None, os_release_str: str | None = None,
**kwargs, **kwargs: Any,
): ) -> 'Distro':
if id is None: if id is None:
os_release_str = await cls.read_os_release_str(ec) os_release_str = await cls.read_os_release_str(ec)
id = cls.parse_os_release_field_id(os_release_str) id = cls.parse_os_release_field_id(os_release_str)
@ -142,8 +142,9 @@ class Distro(abc.ABC):
@cached_property @cached_property
def os_cascade(self) -> list[str]: def os_cascade(self) -> list[str]:
ret: list[str] = []
def __append(entry: str): def __append(entry: str) -> None:
if entry not in ret: if entry not in ret:
ret.append(entry) ret.append(entry)
@ -322,10 +323,10 @@ class Distro(abc.ABC):
def default_pkg_filter(self) -> PackageFilter | None: def default_pkg_filter(self) -> PackageFilter | None:
return self.__default_pkg_filter return self.__default_pkg_filter
async def run(self, *args, **kwargs) -> Result: async def run(self, *args: Any, **kwargs: Any) -> Result:
return await self.__exec_context.run(*args, **kwargs) return await self.__exec_context.run(*args, **kwargs)
async def sudo(self, *args, **kwargs) -> Result: async def sudo(self, *args: Any, **kwargs: Any) -> Result:
return await self.__exec_context.sudo(*args, **kwargs) return await self.__exec_context.sudo(*args, **kwargs)
@property @property

View file

@ -5,7 +5,7 @@ import errno
import sys import sys
from decimal import ROUND_FLOOR, Decimal from decimal import ROUND_FLOOR, Decimal
from typing import override, TYPE_CHECKING, NamedTuple from typing import Any, override, TYPE_CHECKING, NamedTuple
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Type from typing import Type
@ -214,7 +214,7 @@ class ExecContext(Base):
self.__pretty_cmd = pretty_cmd(self.__cmd, self.__wd) self.__pretty_cmd = pretty_cmd(self.__cmd, self.__wd)
return self.__pretty_cmd return self.__pretty_cmd
def log(self, prio: int, *args, **kwargs) -> None: def log(self, prio: int, *args: Any, **kwargs: Any) -> None:
log(prio, self.__log_prefix, *args, **kwargs) log(prio, self.__log_prefix, *args, **kwargs)
def log_delim(self, start: bool) -> None: def log_delim(self, start: bool) -> None:
@ -244,12 +244,12 @@ class ExecContext(Base):
def __mode_str(cls, mode: int) -> str: def __mode_str(cls, mode: int) -> str:
return f'{mode:0o}' return f'{mode:0o}'
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@classmethod @classmethod
@override @override
def create(cls, *args, **kwargs) -> ExecContext: def create(cls, *args: Any, **kwargs: Any) -> ExecContext:
ret = super().create(*args, **kwargs) ret = super().create(*args, **kwargs)
if not isinstance(ret, cls): if not isinstance(ret, cls):
raise TypeError(f'Expected {cls.__name__}, got {type(ret).__name__}') raise TypeError(f'Expected {cls.__name__}, got {type(ret).__name__}')
@ -505,7 +505,7 @@ class ExecContext(Base):
async def __run( async def __run(
cmd: list[str], cmd: list[str],
cmd_input: Input = InputMode.NonInteractive, cmd_input: Input = InputMode.NonInteractive,
**kwargs **kwargs: Any
) -> Result: ) -> Result:
return await self.run(cmd, cmd_input = cmd_input, **kwargs) return await self.run(cmd, cmd_input = cmd_input, **kwargs)

View file

@ -4,7 +4,7 @@ import abc
from enum import Enum, auto from enum import Enum, auto
from functools import cached_property from functools import cached_property
from typing import override, TYPE_CHECKING from typing import Any, override, TYPE_CHECKING
from .log import DEBUG, ERR, log from .log import DEBUG, ERR, log
from .Uri import Uri from .Uri import Uri
@ -24,7 +24,7 @@ class FileContext(abc.ABC):
self, self,
uri: str | Uri, uri: str | Uri,
interactive: bool | None = None, interactive: bool | None = None,
verbose_default = False, verbose_default: bool = False,
chroot: bool = False, chroot: bool = False,
in_pipe: ProcPipeline | None = None, in_pipe: ProcPipeline | None = None,
out_pipe: ProcPipeline | None = None, out_pipe: ProcPipeline | None = None,
@ -43,18 +43,18 @@ class FileContext(abc.ABC):
f'= "{verbose_default}"' f'= "{verbose_default}"'
) )
async def __aenter__(self): async def __aenter__(self) -> 'FileContext':
await self.open() await self.open()
return self return self
async def __aexit__(self, exc_type, exc, tb): async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
await self.close() await self.close()
@override @override
def __repr__(self) -> str: def __repr__(self) -> str:
return self.__uri.id return self.__uri.id
def __pipe(self, d: Direction): def __pipe(self, d: Direction) -> 'ProcPipeline':
match d: match d:
case self.Direction.In: case self.Direction.In:
if not self.__in_pipe: if not self.__in_pipe:
@ -76,7 +76,7 @@ class FileContext(abc.ABC):
return self.root + path return self.root + path
return self.root + '/' + path return self.root + '/' + path
def add_proc_filter(self, d: Direction, proc_filter: ProcFilter): def add_proc_filter(self, d: Direction, proc_filter: ProcFilter) -> None:
self.__pipe(d).append(proc_filter) self.__pipe(d).append(proc_filter)
async def _open(self) -> None: async def _open(self) -> None:
@ -298,11 +298,11 @@ class FileContext(abc.ABC):
log(ERR, f'{self.log_name}: Failed to stat({path}) ({str(e)})') log(ERR, f'{self.log_name}: Failed to stat({path}) ({str(e)})')
raise raise
async def is_dir(self, path: str, follow_symlinks = True) -> bool: async def is_dir(self, path: str, follow_symlinks: bool = True) -> bool:
return await self._is_dir(self._chroot(path), follow_symlinks = follow_symlinks) return await self._is_dir(self._chroot(path), follow_symlinks = follow_symlinks)
@classmethod @classmethod
def create(cls, uri: str | Uri, *args, **kwargs) -> FileContext: def create(cls, uri: str | Uri, *args: Any, **kwargs: Any) -> 'FileContext':
uri = Uri.pimp(uri) uri = Uri.pimp(uri)
match uri.protocol: match uri.protocol:
case 'local' | 'file': case 'local' | 'file':

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from typing import override, Any from typing import override, Any
meta_tags = [ meta_tags = [
@ -16,7 +18,7 @@ class Package:
maintainer: str | None = None maintainer: str | None = None
@classmethod @classmethod
def parse_spec_str(cls, spec: str, delimiter = '|'): def parse_spec_str(cls, spec: str, delimiter: str = '|') -> 'Package':
tags = spec.split(delimiter) tags = spec.split(delimiter)
if len(tags) != 5: if len(tags) != 5:
raise ValueError(f'Invalid package spec string "{spec}"') raise ValueError(f'Invalid package spec string "{spec}"')
@ -29,14 +31,14 @@ class Package:
) )
@classmethod @classmethod
def parse_specs_str(cls, specs: str, delimiter = '|'): def parse_specs_str(cls, specs: str, delimiter: str = '|') -> list[Package]:
ret: list[Package] = [] ret: list[Package] = []
for spec in specs.splitlines(): for spec in specs.splitlines():
ret.append(cls.parse_spec_str(spec)) ret.append(cls.parse_spec_str(spec))
return ret return ret
@classmethod @classmethod
def order_tags(cls, mapping: dict[str, Any]): def order_tags(cls, mapping: dict[str, Any]) -> dict[str, Any]:
ret: dict[str, Any] = {} ret: dict[str, Any] = {}
for tag in meta_tags: for tag in meta_tags:
ret[tag] = mapping.get(tag, '') ret[tag] = mapping.get(tag, '')

View file

@ -33,7 +33,7 @@ class Result:
def __try_decode( def __try_decode(
self, self,
stdxxx: bytes | None, stdxxx: bytes | None,
quote = False, quote: bool = False,
truncate: int | None = None, truncate: int | None = None,
annotate: bool = True, annotate: bool = True,
label: str | None = None, label: str | None = None,
@ -59,7 +59,7 @@ class Result:
self, self,
cmd: list[str] | None = None, cmd: list[str] | None = None,
wd: str | None = None, wd: str | None = None,
verbose = True verbose: bool = True,
) -> str: ) -> str:
def __status_str(cmd: list[str] | None, wd: str | None, verbose: bool) -> str: def __status_str(cmd: list[str] | None, wd: str | None, verbose: bool) -> str:

View file

@ -9,14 +9,14 @@ from tarfile import TarFile, TarInfo
from .CopyContext import CopyContext from .CopyContext import CopyContext
from .ExecContext import ExecContext from .ExecContext import ExecContext
from .log import DEBUG, ERR, log from .log import DEBUG, ERR, log
from typing import TYPE_CHECKING, override from typing import TYPE_CHECKING, Any, override
if TYPE_CHECKING: if TYPE_CHECKING:
from .base import StatResult from .base import StatResult
class TarIo(CopyContext): class TarIo(CopyContext):
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
kwargs['chroot'] = False kwargs['chroot'] = False
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -44,7 +44,7 @@ class TarIo(CopyContext):
async def _read_filtered( async def _read_filtered(
self, self,
path, path: str,
path_filter: list[str] | None = None, path_filter: list[str] | None = None,
matched: list[str] | None = None, matched: list[str] | None = None,
) -> bytes: ) -> bytes:
@ -81,7 +81,7 @@ class TarIo(CopyContext):
return ret return ret
@classmethod @classmethod
def create(cls, *args, type: str | None = None, **kwargs): def create(cls, *args: Any, type: str | None = None, **kwargs: Any) -> 'TarIo':
if type is not None: if type is not None:
raise NotImplementedError raise NotImplementedError
# return TarIoTarFile(*args, **kwargs) # return TarIoTarFile(*args, **kwargs)

View file

@ -5,7 +5,7 @@ import os
import re import re
import sys import sys
from typing import override, TYPE_CHECKING, Generic, Iterable, TypeVar from typing import Any, override, TYPE_CHECKING, Generic, Iterable, TypeVar
from .log import ERR, OFF, log, parse_log_level from .log import ERR, OFF, log, parse_log_level
@ -33,7 +33,7 @@ class Types(abc.ABC, Iterable[type[T]], Generic[T]): # export
def _stringify(self) -> list[str]: def _stringify(self) -> list[str]:
pass pass
def dump(self, prio: int, *args, **kwargs) -> None: def dump(self, prio: int, *args: Any, **kwargs: Any) -> None:
contents = self._stringify() contents = self._stringify()
log(prio, ',--- ', *args, **kwargs) log(prio, ',--- ', *args, **kwargs)
for line in contents: for line in contents:
@ -47,8 +47,8 @@ class LoadTypes(Types[T]): # export
mod_names: Iterable[str], mod_names: Iterable[str],
type_name_filter: str | None = None, type_name_filter: str | None = None,
type_filter: Sequence[type[Any]] | None = None, type_filter: Sequence[type[Any]] | None = None,
debug_level = None, debug_level: int | None = None,
): ) -> None:
if debug_level is None: if debug_level is None:
val = os.getenv('JW_LOG_LEVEL_LOAD_TYPES') val = os.getenv('JW_LOG_LEVEL_LOAD_TYPES')
if val is not None: if val is not None:
@ -61,12 +61,12 @@ class LoadTypes(Types[T]): # export
self.__mod_names = mod_names self.__mod_names = mod_names
self.__classes: list[type[T]] | None = None self.__classes: list[type[T]] | None = None
def _debug(self, *args, **kwargs) -> None: def _debug(self, *args: Any, **kwargs: Any) -> None:
if self.__debug_level != OFF: if self.__debug_level != OFF:
log(self.__debug_level, *args, **kwargs) log(self.__debug_level, *args, **kwargs)
@override @override
def _stringify(self): def _stringify(self) -> list[str]:
tf = 'None' if self.__type_filter is None else ( tf = 'None' if self.__type_filter is None else (
', '.join([str(f) for f in self.__type_filter]) ', '.join([str(f) for f in self.__type_filter])
) )

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import override, TYPE_CHECKING from typing import Any, override, TYPE_CHECKING
from ...Distro import Distro as Base from ...Distro import Distro as Base
@ -23,7 +23,7 @@ class Distro(Base):
return await self.sudo(cmd, verbose = verbose) return await self.sudo(cmd, verbose = verbose)
return await self.run(cmd, verbose = verbose) return await self.run(cmd, verbose = verbose)
def __init__(self, *args, **kwargs): def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@override @override

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import os import os
from typing import override, TYPE_CHECKING from typing import Any, override, TYPE_CHECKING
from ...Distro import Distro as Base from ...Distro import Distro as Base
from ...log import NOTICE, log from ...log import NOTICE, log
@ -31,11 +31,11 @@ class Distro(Base):
if sudo else await self.run(cmd, verbose = verbose) if sudo else await self.run(cmd, verbose = verbose)
) )
async def dpkg(self, *args, **kwargs) -> str: async def dpkg(self, *args: Any, **kwargs: Any) -> str:
kwargs.setdefault('ec', self.ctx) kwargs.setdefault('ec', self.ctx)
return await run_dpkg(*args, **kwargs) return await run_dpkg(*args, **kwargs)
def __init__(self, *args, **kwargs): def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@override @override

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import override, TYPE_CHECKING from typing import TYPE_CHECKING, Any, override
from ...Distro import Distro as Base from ...Distro import Distro as Base
from ...pm.rpm import list_files, query_packages, run_rpm from ...pm.rpm import list_files, query_packages, run_rpm
@ -35,13 +35,18 @@ class Distro(Base):
if sudo else await self.run(cmd, verbose = verbose) if sudo else await self.run(cmd, verbose = verbose)
) )
async def rpm(self, *args, ec: ExecContext | None = None, **kwargs) -> str: async def rpm(
self,
*args: Any,
ec: ExecContext | None = None,
**kwargs: Any,
) -> str:
if ec is None: if ec is None:
ec = self.ctx ec = self.ctx
kwargs['ec'] = ec kwargs['ec'] = ec
return await run_rpm(*args, **kwargs) return await run_rpm(*args, **kwargs)
def __init__(self, *args, **kwargs): def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@override @override

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import override, TYPE_CHECKING from typing import Any, override, TYPE_CHECKING
from ..FileContext import FileContext as Base from ..FileContext import FileContext as Base
@ -13,7 +13,11 @@ if TYPE_CHECKING:
class Curl(Base): class Curl(Base):
def __init__( def __init__(
self, uri: str | Uri, *args, ec: ExecContext | None = None, **kwargs self,
uri: str | Uri,
*args: Any,
ec: ExecContext | None = None,
**kwargs: Any
) -> None: ) -> None:
def __local() -> Local: def __local() -> Local:

View file

@ -7,20 +7,20 @@ import pwd
import sys import sys
from functools import cache from functools import cache
from typing import override, TYPE_CHECKING from typing import TYPE_CHECKING, override
from ..base import Result, StatResult from ..base import Result, StatResult
from ..ExecContext import ExecContext as Base from ..ExecContext import ExecContext as Base
from ..log import ERR, NOTICE, log from ..log import ERR, NOTICE, log
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any from typing import Any, Callable
from ..Uri import Uri from ..Uri import Uri
class Local(Base): class Local(Base):
def __init__(self, uri: str | Uri = 'local', *args, **kwargs) -> None: def __init__(self, uri: str | Uri = 'local', *args: Any, **kwargs: Any) -> None:
super().__init__(uri, *args, **kwargs) super().__init__(uri, *args, **kwargs)
@cache @cache
@ -43,9 +43,12 @@ class Local(Base):
if verbose: if verbose:
log(prio, log_prefix, *args) log(prio, log_prefix, *args)
def __make_pty_reader(collector: list[bytes], enc_for_verbose: str): def __make_pty_reader(
collector: list[bytes],
enc_for_verbose: str,
) -> Callable[[int], bytes]:
def _read(fd): def _read(fd: int) -> bytes:
ret = os.read(fd, 1024) ret = os.read(fd, 1024)
if not ret: if not ret:
return ret return ret
@ -65,7 +68,7 @@ class Local(Base):
if interactive: if interactive:
import pty import pty
def _spawn(): def _spawn() -> int:
# Apply env in PTY mode by temporarily updating os.environ # Apply env in PTY mode by temporarily updating os.environ
# around spawn. # around spawn.
if mod_env: if mod_env:
@ -115,7 +118,9 @@ class Local(Base):
stdout_log_enc = sys.stdout.encoding or 'utf-8' stdout_log_enc = sys.stdout.encoding or 'utf-8'
stderr_log_enc = sys.stderr.encoding or 'utf-8' stderr_log_enc = sys.stderr.encoding or 'utf-8'
async def read_stream(stream, prio, collector: list[bytes], log_enc: str): async def read_stream(
stream: Any, prio: int, collector: list[bytes], log_enc: str
) -> None:
buf = b'' buf = b''
while True: while True:
chunk = await stream.read(4096) chunk = await stream.read(4096)

View file

@ -5,7 +5,7 @@ import os
import pwd import pwd
from enum import Flag, auto from enum import Flag, auto
from typing import TYPE_CHECKING, override from typing import TYPE_CHECKING, Any, cast, override
from ..ExecContext import ExecContext from ..ExecContext import ExecContext
from ..log import DEBUG, ERR, INFO, NOTICE, get_caller_pos, log, log_m from ..log import DEBUG, ERR, INFO, NOTICE, get_caller_pos, log, log_m
@ -22,7 +22,9 @@ class SSHClient(ExecContext):
ModEnv = auto() ModEnv = auto()
Wd = auto() Wd = auto()
def __init__(self, uri: Uri | str, caps: Caps = Caps(0), *args, **kwargs) -> None: def __init__(
self, uri: Uri | str, caps: Caps = Caps(0), *args: Any, **kwargs: Any
) -> None:
uri = Uri.pimp(uri) uri = Uri.pimp(uri)
if uri.username is None: if uri.username is None:
uri.set_username(pwd.getpwuid(os.getuid()).pw_name) uri.set_username(pwd.getpwuid(os.getuid()).pw_name)
@ -54,13 +56,13 @@ class SSHClient(ExecContext):
log_prefix: str, log_prefix: str,
) -> Result: ) -> Result:
def __log(prio: int, *args, **kwargs): def __log(prio: int, *args: Any, **kwargs: Any) -> None:
caller = kwargs.get('caller') caller = kwargs.get('caller')
if caller is None: if caller is None:
kwargs['caller'] = get_caller_pos(1) kwargs['caller'] = get_caller_pos(1)
log(prio, log_prefix, *args, **kwargs) log(prio, log_prefix, *args, **kwargs)
def __log_block(prio: int, title: str, block: str | None): def __log_block(prio: int, title: str, block: str | None) -> None:
if self.__caps & self.Caps.LogOutput: if self.__caps & self.Caps.LogOutput:
return return
if block is None: if block is None:
@ -112,8 +114,10 @@ class SSHClient(ExecContext):
return self.uri.password return self.uri.password
def ssh_client( # export def ssh_client( # export
*args, type: str | list[str] | None = None, **kwargs *args: Any,
) -> SSHClient: type: str | list[str] | None = None,
**kwargs: Any
) -> 'SSHClient':
from importlib import import_module from importlib import import_module
errors: list[str] = [] errors: list[str] = []
@ -130,7 +134,7 @@ def ssh_client( # export
ret = getattr(import_module(f'jw.pkg.lib.ec.ssh.{name}'), ret = getattr(import_module(f'jw.pkg.lib.ec.ssh.{name}'),
name)(*args, **kwargs) name)(*args, **kwargs)
log(INFO, f'Using SSH-client "{name}"') log(INFO, f'Using SSH-client "{name}"')
return ret return cast('SSHClient', ret)
except Exception as e: except Exception as e:
msg = f"Can't instantiate SSH client class {name} ({str(e)})" msg = f"Can't instantiate SSH client class {name} ({str(e)})"
errors.append(msg) errors.append(msg)

View file

@ -1,4 +1,3 @@
from typing import Any, override
import asyncio import asyncio
import os import os
import shlex import shlex
@ -6,8 +5,12 @@ import shutil
import signal import signal
import sys import sys
from typing import Any, override
import asyncssh # type: ignore[import-not-found, unused-ignore] import asyncssh # type: ignore[import-not-found, unused-ignore]
from asyncssh import SSHReader # type: ignore[import-not-found, unused-ignore]
from ...base import Result from ...base import Result
from ...log import DEBUG, ERR, NOTICE, log from ...log import DEBUG, ERR, NOTICE, log
from ..SSHClient import SSHClient as Base from ..SSHClient import SSHClient as Base
@ -22,10 +25,10 @@ class AsyncSSH(Base):
uri: str, uri: str,
*, *,
client_keys: list[str] | None = None, client_keys: list[str] | None = None,
known_hosts = _USE_DEFAULT_KNOWN_HOSTS, known_hosts: Any = _USE_DEFAULT_KNOWN_HOSTS,
term_type: str | None = None, term_type: str | None = None,
connect_timeout: float | None = 30.0, connect_timeout: float | None = 30.0,
**kwargs, **kwargs: Any,
) -> None: ) -> None:
super().__init__( super().__init__(
@ -121,8 +124,8 @@ class AsyncSSH(Base):
async def _read_stream( async def _read_stream(
self, self,
stream, stream: SSHReader[bytes],
prio, prio: int,
collector: list[bytes], collector: list[bytes],
*, *,
verbose: bool, verbose: bool,
@ -222,7 +225,7 @@ class AsyncSSH(Base):
stdout_parts.append(chunk) stdout_parts.append(chunk)
_write_local(chunk) _write_local(chunk)
def _on_winch(*_args) -> None: def _on_winch(*_args: Any) -> None:
try: try:
proc.change_terminal_size(*self._get_local_term_size()) proc.change_terminal_size(*self._get_local_term_size())

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import os import os
from typing import override, TYPE_CHECKING from typing import Any, override, TYPE_CHECKING
from ...base import InputMode from ...base import InputMode
from ...util import run_cmd from ...util import run_cmd
@ -14,12 +14,12 @@ if TYPE_CHECKING:
class Exec(Base): class Exec(Base):
def __init__(self, uri, *args, **kwargs) -> None: def __init__(self, uri: Any, *args: Any, **kwargs: Any) -> None:
self.__askpass: str | None = None self.__askpass: str | None = None
self.__askpass_orig: dict[str, str | None] = dict() self.__askpass_orig: dict[str, str | None] = dict()
super().__init__(uri = uri, caps = self.Caps.ModEnv, **kwargs) super().__init__(uri = uri, caps = self.Caps.ModEnv, **kwargs)
def __del__(self): def __del__(self) -> None:
for key, val in self.__askpass_orig.items(): for key, val in self.__askpass_orig.items():
if val is None: if val is None:
del os.environ[key] del os.environ[key]

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import override, TYPE_CHECKING from typing import Any, override, TYPE_CHECKING
# Tolerate missing paramiko imports. jw-pkg is designed to work with what it # Tolerate missing paramiko imports. jw-pkg is designed to work with what it
# finds. # finds.
@ -18,7 +18,7 @@ if TYPE_CHECKING:
class Paramiko(Base): class Paramiko(Base):
def __init__(self, uri, *args, **kwargs) -> None: def __init__(self, uri: Any, *args: Any, **kwargs: Any) -> None:
kwargs['caps'] = (self.Caps.ModEnv, ) kwargs['caps'] = (self.Caps.ModEnv, )
super().__init__(uri, *args, **kwargs) super().__init__(uri, *args, **kwargs)
self.__timeout: float | None = None # Untested self.__timeout: float | None = None # Untested

View file

@ -7,7 +7,7 @@ import syslog
from datetime import datetime from datetime import datetime
from os.path import basename from os.path import basename
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, cast
if TYPE_CHECKING: if TYPE_CHECKING:
import io import io
@ -126,7 +126,7 @@ _prio_colors = {
class Stream: class Stream:
def __init__(self, stream, flags): def __init__(self, stream: Any, flags: int):
self.stream = stream self.stream = stream
self.flags = flags self.flags = flags
@ -142,12 +142,12 @@ def pad(token: str, total_size: int, right_align: bool = False) -> str:
return space + token return space + token
return token + space return token + space
def add_capture_stream(stream, flags = 0x0): def add_capture_stream(stream: Any, flags: int = 0x0) -> int:
ret = _stream_descriptors.pop() ret = _stream_descriptors.pop()
_streams[ret] = Stream(stream = stream, flags = flags) _streams[ret] = Stream(stream = stream, flags = flags)
return ret return ret
def rm_capture_stream(sd): def rm_capture_stream(sd: int) -> None:
del _streams[sd] del _streams[sd]
_stream_descriptors.append(sd) _stream_descriptors.append(sd)
@ -166,13 +166,13 @@ def get_caller_pos(up: int = 1,
if kwargs and 'caller' in kwargs: if kwargs and 'caller' in kwargs:
r = kwargs['caller'] r = kwargs['caller']
del kwargs['caller'] del kwargs['caller']
return r return cast('Tuple[str, str, int]', r)
caller = inspect.stack()[up + 1] caller = inspect.stack()[up + 1]
mod = inspect.getmodule(caller[0]) mod = inspect.getmodule(caller[0])
mod_name = '' if mod is None else mod.__name__ mod_name = '' if mod is None else mod.__name__
return (mod_name, basename(caller.filename), caller.lineno) return (mod_name, basename(caller.filename), caller.lineno)
def log_m(prio: int, *args, **kwargs) -> None: # export def log_m(prio: int, *args: Any, **kwargs: Any) -> None: # export
if prio > _level: if prio > _level:
return return
margs = '' margs = ''
@ -190,7 +190,12 @@ def log_m(prio: int, *args, **kwargs) -> None: # export
for line in margs[1:].split('\n'): for line in margs[1:].split('\n'):
log(prio, line, **kwargs, caller = caller) log(prio, line, **kwargs, caller = caller)
def log(prio: int, *args, only_printable: bool = False, **kwargs) -> None: # export def log( # export
prio: int,
*args: Any,
only_printable: bool = False,
**kwargs: Any
) -> None:
if prio > _level: if prio > _level:
return return
@ -259,7 +264,12 @@ def log(prio: int, *args, only_printable: bool = False, **kwargs) -> None: # ex
for file in files: for file in files:
print(msg, file = file) print(msg, file = file)
def throw(*args, prio = ERR, caller = None, **kwargs) -> None: def throw(
*args: Any,
prio: int = ERR,
caller: Tuple[str, str, int] | None = None,
**kwargs: Any
) -> None:
if caller is None: if caller is None:
caller = get_caller_pos(1) caller = get_caller_pos(1)
msg = ' '.join([str(arg) for arg in args]) msg = ' '.join([str(arg) for arg in args])
@ -331,7 +341,7 @@ def append_to_prefix(prefix: str) -> str: # export
_clean_log_prefix = _clean_str_regex.sub('', _log_prefix) _clean_log_prefix = _clean_str_regex.sub('', _log_prefix)
return r return r
def remove_from_prefix(count) -> str: # export def remove_from_prefix(count: int | str) -> str: # export
if isinstance(count, str): if isinstance(count, str):
count = len(count) count = len(count)
global _log_prefix global _log_prefix

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Iterable from typing import TYPE_CHECKING, Iterable, cast
if TYPE_CHECKING: if TYPE_CHECKING:
from ..ExecContext import ExecContext from ..ExecContext import ExecContext
@ -59,7 +59,7 @@ async def query_packages(names: Iterable[str] = [],
) )
# dpkg-query -W -f='${binary:Package}|${Maintainer}| ... \n' # dpkg-query -W -f='${binary:Package}|${Maintainer}| ... \n'
specs = await run_dpkg_query(['-W', '-f=' + fmt_str, *names], sudo = False, ec = ec) specs = await run_dpkg_query(['-W', '-f=' + fmt_str, *names], sudo = False, ec = ec)
return Package.parse_specs_str(specs) return cast('Iterable[Package]', Package.parse_specs_str(specs))
async def list_files(pkg: str, ec: ExecContext | None = None) -> list[str]: async def list_files(pkg: str, ec: ExecContext | None = None) -> list[str]:
file_list_str = await run_dpkg(['-L', pkg], sudo = False, ec = ec) file_list_str = await run_dpkg(['-L', pkg], sudo = False, ec = ec)

View file

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Collection, Iterable from typing import Any, TYPE_CHECKING, Collection, Iterable, cast
from ..base import InputMode from ..base import InputMode
from ..Package import Package from ..Package import Package
@ -30,7 +30,7 @@ async def run_rpm( # export
sudo: bool = False, sudo: bool = False,
ec: ExecContext | None = None, ec: ExecContext | None = None,
mode: InputMode = InputMode.OptInteractive, mode: InputMode = InputMode.OptInteractive,
**kwargs, **kwargs: Any,
) -> str: ) -> str:
cmd = ['/usr/bin/rpm'] cmd = ['/usr/bin/rpm']
cmd.extend(args) cmd.extend(args)
@ -58,7 +58,7 @@ async def query_packages( # export
mode = InputMode.NonInteractive, mode = InputMode.NonInteractive,
ec = ec ec = ec
) )
return Package.parse_specs_str(specs) return cast('Iterable[Package]', Package.parse_specs_str(specs))
async def list_files(pkg: str, ec: ExecContext | None = None) -> list[str]: async def list_files(pkg: str, ec: ExecContext | None = None) -> list[str]:
stdout = await run_rpm( stdout = await run_rpm(

View file

@ -5,7 +5,7 @@ import os
import sys import sys
from enum import Enum, auto from enum import Enum, auto
from typing import TYPE_CHECKING, Iterable, TypeVar from typing import TYPE_CHECKING, Any, Iterable, TypeVar, cast
from .base import Input, InputMode, Result from .base import Input, InputMode, Result
from .log import DEBUG, ERR, log from .log import DEBUG, ERR, log
@ -23,7 +23,7 @@ class AskpassKey(Enum):
Username = auto() Username = auto()
Password = auto() Password = auto()
def pretty_cmd(cmd: list[str] | None = None, wd = None): def pretty_cmd(cmd: list[str] | None = None, wd: str | None = None) -> str:
if cmd is None: if cmd is None:
cmd = sys.argv cmd = sys.argv
tokens = [cmd[0]] tokens = [cmd[0]]
@ -38,11 +38,11 @@ def pretty_cmd(cmd: list[str] | None = None, wd = None):
# See ExecContext.run() for what this function does # See ExecContext.run() for what this function does
async def run_cmd( async def run_cmd(
*args, *args: Any,
ec: ExecContext | None = None, ec: ExecContext | None = None,
verbose: bool | None = None, verbose: bool | None = None,
cmd_input: Input = InputMode.NonInteractive, cmd_input: Input = InputMode.NonInteractive,
**kwargs, **kwargs: Any,
) -> Result: ) -> Result:
if verbose is None: if verbose is None:
verbose = False if ec is None else ec.verbose_default verbose = False if ec is None else ec.verbose_default
@ -56,12 +56,12 @@ async def run_cmd(
async def run_curl( async def run_curl(
args: list[str], args: list[str],
wd = None, wd: str | None = None,
throw = None, throw: bool | None = None,
verbose = None, verbose: bool | None = None,
cmd_input = InputMode.NonInteractive, cmd_input: Input = InputMode.NonInteractive,
ec: ExecContext | None = None, ec: ExecContext | None = None,
decode = False, decode: bool = False,
) -> Result: ) -> Result:
if verbose is None: if verbose is None:
verbose = False if ec is None else ec.verbose_default verbose = False if ec is None else ec.verbose_default
@ -76,7 +76,7 @@ async def run_curl(
async def run_curl_into( async def run_curl_into(
expected_type: type[T], expected_type: type[T],
args: list[str], args: list[str],
**kwargs, **kwargs: Any,
) -> T: ) -> T:
result = await run_curl(args, **kwargs) result = await run_curl(args, **kwargs)
stdout = result.stdout_str stdout = result.stdout_str
@ -139,11 +139,11 @@ async def run_askpass(
async def run_sudo( async def run_sudo(
cmd: list[str], cmd: list[str],
*args, *args: Any,
interactive: bool = True, interactive: bool = True,
ec: ExecContext | None = None, ec: ExecContext | None = None,
**kwargs, **kwargs: Any,
): ) -> Result:
if ec is None: if ec is None:
from .ec.Local import Local from .ec.Local import Local
@ -152,10 +152,10 @@ async def run_sudo(
async def get( async def get(
uri: str | Uri, uri: str | Uri,
*args, *args: Any,
ctx: FileContext | None = None, ctx: FileContext | None = None,
content_filter: ProcFilter | list[ProcFilter] | ProcPipeline | None = None, content_filter: ProcFilter | list[ProcFilter] | ProcPipeline | None = None,
**kwargs, **kwargs: Any,
) -> Result: ) -> Result:
uri = Uri.pimp(uri) uri = Uri.pimp(uri)
if ctx is None or uri.id != ctx.uri.id: if ctx is None or uri.id != ctx.uri.id:
@ -172,7 +172,7 @@ async def copy(
owner: str | None = None, owner: str | None = None,
group: str | None = None, group: str | None = None,
mode: int | None = None, mode: int | None = None,
throw = True, throw: bool = True,
) -> Exception | str | list[str]: ) -> Exception | str | list[str]:
if not isinstance(src_uri, str): if not isinstance(src_uri, str):
ret: list[str] = [] ret: list[str] = []
@ -225,7 +225,7 @@ async def get_username( # export
f'Username mismatch: called with --username="{args.username}", ' f'Username mismatch: called with --username="{args.username}", '
f'URL has user name "{url_user}"' f'URL has user name "{url_user}"'
) )
return args.username return str(args.username)
if url_user is not None: if url_user is not None:
return url_user return url_user
return await run_askpass(askpass_env, AskpassKey.Username, ec = ec) return await run_askpass(askpass_env, AskpassKey.Username, ec = ec)
@ -244,7 +244,7 @@ async def get_password( # export
if args is not None and hasattr(args, 'password'): if args is not None and hasattr(args, 'password'):
# use getattr(), because we don't necessarily want to have insecure # use getattr(), because we don't necessarily want to have insecure
# --password among options # --password among options
ret = getattr(args, 'password') ret = cast('str | None', getattr(args, 'password'))
if ret is not None: if ret is not None:
return ret return ret
if url is not None: if url is not None: