Commit graph

16 commits

Author SHA1 Message Date
6db73873e7
jw.pkg: Fix "make check" static code check fallout

The previous commits have put rules for linting and formatting via ruff, yapf, mypy and pyright into place. They are checked with the make check target, and this commit adds the fixes for the target to succeed.

It does some refactoring where type checking dug up dirty bits, and also adds lots of churn in the Python code. To a good deal, that's owed to mere formatting changes. It would have been better to seperate those from syntax and refactoring fixes into multiple commits, so that the interesting changes don't drown in the formatting nose. However, that would have been a lot of additional work only to be thrown away by later commits, hence this commit has a big diff in one piece. The size of the diff is regrettable but hopefully a one-off: What it buys is automatic format checking for CI and predictble formats for smaller diffs in the future.

Rules that "make check" enforces are, in the following order

- Syntax checkers:

- ruff check . - mypy . - pyright

- Format check:

- yapf --diff --recursive .

The refactoring includes:

- Turn the Result class into a more elaborate object, capable of doing more heavy lifting around stderr and stdout decoding, summarizing outcome, and matching error strings.
Aside from fixing broken type checks, this also removes lots of boilerplate calling code which is currently used for handling possible call outcome scenarios. Trying to access an inexistent, decoded string should raise a meaningful exception by itself now, which removes lots of code with case distinctions.

- Fix Cmd type hierarchy:

- Add the AbstractCmd class above Cmd. This is necessary because the checker rightfully complains it can't instantiate a Cmd instance where constructor arguments were needed. They never were, but the type used at the instantiating code's location in jw.pkg.App so claims.
- Lots of sub- and sub-subcommands are derived from the base class of the invoking command. That provides some properties shared across the ancestor hierarchy of a command, but is semantically unsound. Fix that by introducing jw.pkg.BaseCmd class as a place to provide basic helpers shared across all commands used in a jw.pkg.App's context, and derive all command classes from that afresh. The parent command is still reachable via a common parent property.

Formatting changes are conforming to PEP-8, mostly, with minor tweaks. All in all they include the following changes.

- Remove # -*- coding: utf-8 -*-

The line was needed by Python 2 which is not supported anylonger. For Python 3, the default encoding is UTF-8, anyway.
- Allow to run "make py-format" without having it produce any changes. It's basically "yapf --in-place --recursive ." with some code style settings, see conf/topdir/pyproject.toml. The settings may be debatable. I've had custom tweaks in place on that target, too, but then again, IDEs would have more hassle to integrate that.

- Introduce a 88 character line length limit

- One import per line, reshuffle them semantically, see [tool.isort] in pyproject.toml.

- Hide imports needed for type-checking only behind

if TYPE_CHECKING
- Spaces around assignments accounts for much churn. Having having no spaces in inline parameter list assignments and default parameter values would arguably be more compact where it's useful. On the other hand, I have not found a code formatter which allows spaces around assignments in parameter lists broken into one per line and that's often better than a wall of text.
- Add two spaces before # export, as this seems to be mandated by PEP-8

- Use single quotes by default

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-05-31 18:20:38 +02:00
54aecff8e4 lib.ExecContext.run(), .sudo(): Rename env

The name of the env parameter to ExecContext.run() and .sudo() is not descriptive enough for which environment is supposed to be modified and how, so rename and split it up as follows:

- .run(): env -> mod_env

- .sudo(): env -> mod_env_sudo and mod_env_cmd

The parameters have the following meaning:

- "mod_env*" means that the environment is modified, not replaced

- "mod_env" and "mod_env_cmd" modify the environment "cmd" runs in

- "mod_env_sudo" modifies the environment sudo runs in

Fix the fallout of the API change all over jw-pkg.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-19 14:36:50 +02:00
f253466a3f lib.ec.ssh.Exec._run_ssh(): Fix: interactive ignored

Exec's _run_ssh() ignores its "interactive" parameter and uses the instances' default instead, fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-18 10:43:31 +02:00
4a8ccfb0a6 lib.ec.ssh.Exec: Honour username and port

Username and port of an Exec SSH client are not passed to the ssh executable, fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-18 10:43:31 +02:00
3574c0f1bf lib.ec.ssh.Exec: Support Caps.Env

Add support for modifying the execution environment via the env parameter to Exec.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-18 10:43:31 +02:00
6cdcd23010 lib.ec.ssh.Exec._run_ssh(): Fix interactivity translation

cmd_input is passed as None to _run(), which is legal, but then used in a call to cmd_run(), which is a public API and, hence, illegal. InputMode.NonInteractive should be used instead, do that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-18 10:43:31 +02:00
910f10b194 lib.util.run_cmd(): Remove parameter interactive

run_cmd() is a thin layer over the public ExecContext API, which falls back to using a Local instance if not other ExecContext is specified explicitly. Both the default Local context as the subsequent call to run() should have the same idea about interactivity, so allowing to specify it in two parameters ("interactive" and "cmd_input") is a bad idea. Remove "interactive".

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-18 10:43:29 +02:00
f78d08f0d8 lib.ec.ssh.Exec: Fix cmd_input == None

cmd_input is passed as None to _run(), which is legal, but then used in a call to cmd_run(), which is a public API and, hence, illegal. Fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-16 19:43:29 +02:00
888c0495ec lib.base: Add module

Add lib.base to provide basic definitions.

For now, move the definiions of Result, Input and InputMode from ExecContext into lib.base. Having to import them from the ExecContect module is too heavy-handed for those simple types.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-16 12:57:04 +02:00
04b294917f lib.ExecContext: Support bytes-typed cmd_input

The Input instance passed as cmd_input to ExecContext.run() and .sudo() currently may be of type str. Allow to pass bytes, too.

At the same time, disallow None to be passed as cmd_input. Force the caller to be more explicit how it wants input to be handled, notably with respect to interactivity.

Along the way fix a bug: Content in cmd_input should result in CallContext.interactive == False but doesn't. Fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-16 10:21:25 +02:00
d680d6c5ed lib.ec.ssh.Exec._run_ssh(): run_cmd(throw=False)

_run_ssh() of ssh.Exec doesn't pass throw=False to run_cmd(), which makes it throw exceptions, and effectively strips the caller of any chance to get hold of stdout and stderr. Pass throw=False and let run() decide according the the caller-provided throw parameter whether or not a problem should propagate up as exception or return value.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-11 14:55:30 +02:00
bafc7fed2a lib.ec.ssh.Exec: Honour self.interactive

The Exec SSHClient ignores the "interactive" argument passed to its constructor, fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-11 14:53:07 +02:00
84375cd482 lib.ec.ssh: Don't quote shell operators

Naively join()ing a command list to be executed remotely via SSH also quotes shell operators which doesn't work, of course. Work around that. The workaround will not always work but covers lots of cases.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-11 14:52:15 +02:00
0e18d4abac lib.ec.ssh.Exec|Paramiko: Don't # export

The SSHClient classes Paramiko and Exec are exported via # export. This is a bad idea, because if Paramiko is not installed, none of the other's can be instantiated either: On the attempt to load them, __init__.py is loaded first and fails. SSHClient.ssh_client() knows what to do, no need to auto-import them into the lib.ec.ssh module.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-11 14:51:19 +02:00
3a84408436 lib.ec.SSHClient.__init__(): Add parameter caps

Add an optional caps ("capabilities") argument to the constructor of SSHClient. It is meant to be used by derived classes in order to declare that they don't want the base class to handle a default behaviour for a certain capability, but that they want to implement it themselves instead.

Also, give the _run_ssh() callbacks the necessary info as parameters, so that the derived classes have the means to do so.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-25 07:32:46 +01:00
f4c76ebab9 lib.ec.SSHClientInternal|SSHClientCmd: Own .py

Move the code of SSHClientInternal and SSCClientCmd into lib.ec.ssh, as "Paramiko" and "Exec", respectively. This makes the class layout a little more modular, and along the way fixes a bug where SSHClientInternal could be instantiated but was unusable (if the Paramiko is not installed).

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 13:35:50 +01:00