When prefix flags (position, prio, date) are combined with message
arguments, log() would double-space the output (e.g. '<N> Created')
or omit the separator entirely (e.g. '<N>Created'). The root cause
was that log() prepended a space to every argument, regardless of
whether the prefix already ended with one.
Fix: only prepend a space before the first argument when the prefix
doesn't already end with one. This produces exactly one separator
between prefix and content regardless of which flags are active.
Also fix log_m(): skip empty strings (the sentinel '') so it doesn't
contribute a space. And fix the early return that dropped messages
when no prefix flags were set (previous commit).
Signed-off-by: Jan Lindemann <jan@janware.com>
When position, prio, and date are all absent from the log flags, the
msg variable is empty. The early return 'if not len(msg): return'
would then skip printing the actual message in margs.
Fix by checking both msg and margs before returning. This ensures
messages are still printed even when no prefix flags are set.
Signed-off-by: Jan Lindemann <jan@janware.com>
Remove 8 redundant mypy settings that are already enabled by strict
mode: warn_redundant_casts, strict_equality, disallow_any_generics,
warn_unused_ignores, disallow_untyped_calls, warn_return_any,
disallow_incomplete_defs, disallow_untyped_defs.
Keep warn_unreachable explicitly since it's not part of strict mode.
Also remove the duplicate exhaustive-match entry from enable_error_code.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Fix fallout created by enabling the "strict" option:
lib/App.py:196: error: Class cannot subclass
"BaseCompleter" (has type "Any") [misc]
Signed-off-by: Jan Lindemann <jan@janware.com>
Add disallow_untyped_defs = true to the mypy configuration. This
requires all function definitions to have complete type annotations
for both parameters and return types. Since disallow_incomplete_defs
was already satisfied (all functions have partial annotations), this
final rule validates that no function is left without any type hints.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add disallow_incomplete_defs = true to the mypy configuration. This
requires all function definitions to have complete parameter and return
type annotations, catching partially-annotated function signatures.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add warn_return_any = true to the mypy configuration. This detects
functions that return Any from contexts where a specific return type
is declared, encouraging more precise type annotations.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
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>
Add disallow_untyped_calls = true to the mypy configuration. This
requires all function calls from typed code to target typed functions,
ensuring better type safety across the codebase.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add missing type annotations to functions that are called from typed
contexts, satisfying the new disallow_untyped_calls mypy rule.
Fixes:
- Local.py: __log() with typed parameters
- lib/App.py: _add_arguments(), add_cmd_to_parser(), add_cmds_to_parser()
- pm/rpm.py, dpkg.py: meta_map() return type
- Exec.py: __init_askpass() return type
- App.py: strip_module_from_spec(), __get_project_refs_cached(),
ResultCache.__init__ and run(), _add_arguments()
- Added Collection type for truthy-iterable compliance
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add warn_unreachable = true to the mypy configuration. This detects
statements that mypy proves are unreachable, such as code after
an unconditional return or assertions that can never be true.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add the exhaustive-match error code to the mypy configuration. This
requires all match statements to be exhaustive, handling all possible
values of the matched expression.
The fix is to add 'case _: pass' to each match statement that is
intended to be non-exhaustive, making the non-exhaustiveness explicit.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add 'case _: pass' to match statements that are intentionally
non-exhaustive, satisfying the new exhaustive-match mypy rule.
Also replaced 'case '_':' (a string literal) with 'case _: pass' in
pkg_relations.py since it was an unreachable case (syntax is a
VersionSyntax enum, not a str).
Added 'case VersionSyntax.names_only:' to the match in pkg_relations.py
to handle the missing enum value.
Files modified:
- util.py: Two match statements for askpass env vars
- Distro.py: Three match statements for backend/os detection
- pkg_relations.py: Match on VersionSyntax enum
- CmdListRepos.py: Match on URL scheme
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add the truthy-iterable error code to the mypy configuration. This
detects Iterable parameters that are used in boolean contexts (if not
names, etc.) since Iterable values are always truthy.
The fix is to change Iterable[str] parameters to Collection[str] when
they are tested for emptiness, since Collection guarantees __len__.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Change parameter types from Iterable[str] to Collection[str] wherever
the parameter is tested for emptiness (if not names). This satisfies
the new truthy-iterable mypy rule, since bare Iterable values are
always truthy even when empty.
Affected files:
- Distro.py: install, delete, select, _select, _select_by_name
- rpm.py: query_packages
- suse/Distro.py: _select_by_name
- Cmd.py (secrets): _match_files, _list_template_files, etc.
- DistroContext.py: list_template_files, list_secret_paths, etc.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add warn_unused_ignores = true to the mypy configuration. This warns
when a '# type: ignore' comment has no associated error code,
ensuring all type ignores are explicit about which error they suppress.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Some '# type: ignore' comments are needed because they complain about missing
but optional third-party packages: argcomplete, paramiko, asyncssh. The next
commit will enable warn_unused_ignores, and since nor mypy nor pyright have a
way of knowing that this is a tolerable lack of packages, this commit teaches
them in advance.
Signed-off-by: Jan Lindemann <jan@janware.com>
Add disallow_any_generics = true to the mypy configuration. This
requires all generic types (dict, list, Iterable, Types, etc.) to
have explicit type arguments instead of using bare generic aliases.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add explicit type arguments to all generic type annotations that were
previously bare, satisfying the new disallow_any_generics mypy rule.
Fixes:
- Distro.py: Iterable[str] for expand_macros fmt parameter
- Cmd.py: Types[Any] for add_subcommands cmds parameter
- AsyncSSH.py: dict[str, Any] for _connect_kwargs return type
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
The comparison ret != 0 is comparing a Result object against the
integer 0, which is always True since Result has no __eq__ defined.
This commit fixes the bug by comparing ret.status instead, which is
the actual exit code we care about. This satisfies the new
strict_equality mypy rule which enables the comparison-overlap check.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add warn_redundant_casts = true to the mypy configuration template.
This rule detects casts that mypy proves unnecessary, helping to
clean up redundant type casts that clutter the codebase.
This is one of the boolean-flag rules being gradually adopted from
the stricter mypy profile in /tmp/pyproject.toml. It produces just
1 error during initial rollout.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
The cast to 'T' in run_curl_into was redundant: mypy already narrows
the return value of json.loads() to type T after the isinstance(ret,
expected_type) check (where expected_type: type[T]).
Remove the unnecessary cast and clean up the unused 'cast' import.
This satisfies the new warn_redundant_casts mypy rule.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add the truthy-bool error code to the mypy configuration. This
detects conditions and expressions that are always truthy because
the type has no __bool__ or __len__ method.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
The _select() method in Distro.py has an assert on a PackageFilter
parameter that doesn't define __bool__ or __len__. Since the type
signature already guarantees it is not None, the assertion is
redundant and is now removed.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add the possibly-undefined error code to the mypy configuration.
This detects variables that may not be defined on all execution
paths, catching a class of NameError bugs at type-check time.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
The log_m() function has margs conditionally assigned inside an if
block but used unconditionally afterwards. Initialize margs outside
the if to guarantee it is always defined.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Add the ignore-without-code error code to the mypy configuration.
This requires all # type: ignore comments to include a specific error
code, improving the precision and maintainability of type ignore
annotations.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Signed-off-by: Jan Lindemann <jan@janware.com>
Annotate the one existing bare "# type: ignore" in AsyncRunner.py
with arg-type and var-annotated codes to prepare for
mypy.ignore-without-code.
Signed-off-by: Jan Lindemann <jan@janware.com>
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
Add mypy's explicit-override error code and pyrightconfig's
reportImplicitOverride "error" directive. This requires all methods
that override base class methods to be decorated with @override from
typing. The change complements the previous commit, which added
exactly that decorator to all relevant methods.
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit adds @override decorators to approximately 300 methods
across 76 files that inherit from base classes such as AbstractCmd,
FileContext, ExecContext, Distro, SSHClient, and others.
The decorator ensures the type checker can verify that overridden
methods have compatible signatures and prevents accidental shadowing
of inherited methods without intent.
Files modified include command classes, library modules, distro
implementations, and SSH client implementations.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL with pi.dev v
Signed-off-by: Jan Lindemann <jan@janware.com>
Add support for the targets "check" and "check-post" to
projects-dir.mk to make them usable from inside the projects
directory.
Note that "check-pre" is intentionally left out: Running "make check"
in a project before its prerequisite projects have built their
__init__.py files will fail due to broken import resolution.
Signed-off-by: Jan Lindemann <jan@janware.com>
CmdBuild supports the targets all, clean and pkg-*. It uses builtin
dependency flavours matching those targets to resolve build order. To
make it more flexible in general, and allow it to support more
targets, e.g. "check" and "test", this commit adds a --dep-flavours
option.
Signed-off-by: Jan Lindemann <jan@janware.com>
BUILD_MAKEDIR is a variable which exists for consistency's sake. On
the other hand, nor jw-pkg nor any downstream package ever copies
makefile snippets during build time. The rule introduced by
BUILD_MAKEDIR is quite costly in terms of performance and makes
caching harder to understand. This commit disables the variable.
Signed-off-by: Jan Lindemann <jan@janware.com>
Add target py-path to py-path.mk. At this point, it's mostly
introduced for documentation purposes, giving people and machines a
defined, easily accessible and omni-present way to determine how
Python imports should be resolved.
Signed-off-by: Jan Lindemann <jan@janware.com>
Add target py-check-bad-patterns, which currently looks for leftover
breakpoints and orphaned "export" comments on the closing line of a
function prototype.
Signed-off-by: Jan Lindemann <jan@janware.com>
A couple of "# export" markers after function prototypes have been
pushed along with the closing parenthesis onto the wrong line by the
code formatter, fix that.
Signed-off-by: Jan Lindemann <jan@janware.com>
Add py-check.mk and use it from py-topdir.mk and py-rules.mk. This
removes redundant check definitions, making sure that that checks run
from a repo's subdirectory match the checks run from its toplevel
directory.
Signed-off-by: Jan Lindemann <jan@janware.com>
py-defs.mk uses the variables ECHO and SED defined in defs.mk. The
complexity this introduces doesn't justify the reduced redundancy,
though, so this commit defines them redundantly in py-defs.mk and to
allow inclusion without prior defs.mk.
Signed-off-by: Jan Lindemann <jan@janware.com>