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

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

View file

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

View file

@ -9,7 +9,7 @@ from .log import ERR
from .Types import LoadTypes, Types
if TYPE_CHECKING:
from argparse import ArgumentParser
from argparse import ArgumentParser, Namespace
from typing import Iterable
from .App import App
@ -26,7 +26,7 @@ class AbstractCmd(abc.ABC):
self.__child_classes: list[type[Cmd]] = []
self.__parser: ArgumentParser | None = None
def set_parent(self, parent: Any | Cmd):
def set_parent(self, parent: Any | Cmd) -> None:
self.__parent = parent
@property
@ -76,7 +76,7 @@ class AbstractCmd(abc.ABC):
return self.__parser
# 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
def print_help(self, exit_status: int | None = None) -> None:
@ -129,11 +129,11 @@ class AbstractCmd(abc.ABC):
pass
@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
return await self.__parent._run(args)
async def run(self, args):
async def run(self, args: Namespace) -> None:
return await self._run(args)
@abc.abstractmethod

View file

@ -1,4 +1,4 @@
from typing import Self
from typing import Any, Self
from .FileContext import FileContext
from .Uri import Uri
@ -9,7 +9,7 @@ class CopyContext:
self,
src: Uri | str | FileContext,
dst: Uri | str | FileContext,
chroot = False
chroot: bool = False,
) -> None:
def __uri(ctx: FileContext | Uri | str) -> Uri | str:
@ -43,7 +43,7 @@ class CopyContext:
await self.__dst.open()
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:
await self.__src.close()
self.__src = None

View file

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

View file

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

View file

@ -4,7 +4,7 @@ import abc
from enum import Enum, auto
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 .Uri import Uri
@ -24,7 +24,7 @@ class FileContext(abc.ABC):
self,
uri: str | Uri,
interactive: bool | None = None,
verbose_default = False,
verbose_default: bool = False,
chroot: bool = False,
in_pipe: ProcPipeline | None = None,
out_pipe: ProcPipeline | None = None,
@ -43,18 +43,18 @@ class FileContext(abc.ABC):
f'= "{verbose_default}"'
)
async def __aenter__(self):
async def __aenter__(self) -> 'FileContext':
await self.open()
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()
@override
def __repr__(self) -> str:
return self.__uri.id
def __pipe(self, d: Direction):
def __pipe(self, d: Direction) -> 'ProcPipeline':
match d:
case self.Direction.In:
if not self.__in_pipe:
@ -76,7 +76,7 @@ class FileContext(abc.ABC):
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)
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)})')
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)
@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)
match uri.protocol:
case 'local' | 'file':

View file

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

View file

@ -33,7 +33,7 @@ class Result:
def __try_decode(
self,
stdxxx: bytes | None,
quote = False,
quote: bool = False,
truncate: int | None = None,
annotate: bool = True,
label: str | None = None,
@ -59,7 +59,7 @@ class Result:
self,
cmd: list[str] | None = None,
wd: str | None = None,
verbose = True
verbose: bool = True,
) -> 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 .ExecContext import ExecContext
from .log import DEBUG, ERR, log
from typing import TYPE_CHECKING, override
from typing import TYPE_CHECKING, Any, override
if TYPE_CHECKING:
from .base import StatResult
class TarIo(CopyContext):
def __init__(self, *args, **kwargs) -> None:
def __init__(self, *args: Any, **kwargs: Any) -> None:
kwargs['chroot'] = False
super().__init__(*args, **kwargs)
@ -44,7 +44,7 @@ class TarIo(CopyContext):
async def _read_filtered(
self,
path,
path: str,
path_filter: list[str] | None = None,
matched: list[str] | None = None,
) -> bytes:
@ -81,7 +81,7 @@ class TarIo(CopyContext):
return ret
@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:
raise NotImplementedError
# return TarIoTarFile(*args, **kwargs)

View file

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

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import override, TYPE_CHECKING
from typing import Any, override, TYPE_CHECKING
from ...Distro import Distro as Base
@ -23,7 +23,7 @@ class Distro(Base):
return await self.sudo(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)
@override

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import os
from typing import override, TYPE_CHECKING
from typing import Any, override, TYPE_CHECKING
from ...Distro import Distro as Base
from ...log import NOTICE, log
@ -31,11 +31,11 @@ class Distro(Base):
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)
return await run_dpkg(*args, **kwargs)
def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
@override

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import override, TYPE_CHECKING
from typing import TYPE_CHECKING, Any, override
from ...Distro import Distro as Base
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)
)
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:
ec = self.ctx
kwargs['ec'] = ec
return await run_rpm(*args, **kwargs)
def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
@override

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import override, TYPE_CHECKING
from typing import Any, override, TYPE_CHECKING
from ..FileContext import FileContext as Base
@ -13,7 +13,11 @@ if TYPE_CHECKING:
class Curl(Base):
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:
def __local() -> Local:

View file

@ -7,20 +7,20 @@ import pwd
import sys
from functools import cache
from typing import override, TYPE_CHECKING
from typing import TYPE_CHECKING, override
from ..base import Result, StatResult
from ..ExecContext import ExecContext as Base
from ..log import ERR, NOTICE, log
if TYPE_CHECKING:
from typing import Any
from typing import Any, Callable
from ..Uri import Uri
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)
@cache
@ -43,9 +43,12 @@ class Local(Base):
if verbose:
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)
if not ret:
return ret
@ -65,7 +68,7 @@ class Local(Base):
if interactive:
import pty
def _spawn():
def _spawn() -> int:
# Apply env in PTY mode by temporarily updating os.environ
# around spawn.
if mod_env:
@ -115,7 +118,9 @@ class Local(Base):
stdout_log_enc = sys.stdout.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''
while True:
chunk = await stream.read(4096)

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
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
# finds.
@ -18,7 +18,7 @@ if TYPE_CHECKING:
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, )
super().__init__(uri, *args, **kwargs)
self.__timeout: float | None = None # Untested

View file

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

View file

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

View file

@ -1,6 +1,6 @@
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 ..Package import Package
@ -30,7 +30,7 @@ async def run_rpm( # export
sudo: bool = False,
ec: ExecContext | None = None,
mode: InputMode = InputMode.OptInteractive,
**kwargs,
**kwargs: Any,
) -> str:
cmd = ['/usr/bin/rpm']
cmd.extend(args)
@ -58,7 +58,7 @@ async def query_packages( # export
mode = InputMode.NonInteractive,
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]:
stdout = await run_rpm(

View file

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