Commit graph

184 commits

Author SHA1 Message Date
3dc452d9b4
lib.init.detect_modules(): Renovate signature
- Remove package_name and package_path from the prototype of detect_modules(). They can and should be deduced from namespace['__name__'] and namespace['__path__'], respectively.
- Make prefix default to None, which signifies "Don't filter by prefix".
- Add an optional extend_namespace parameter, which will make the function append the module's __name__ to its __path__. This defaults to True, thereby adding a side effect to the function. Which is always wanted in the case for all callers of this function.
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-06 23:08:28 +00:00
de5be6b757
lib.Result: Add module

.lib.Result has grown enourmously in size and merits its own module.

For now, reexport it from .lib.base to not break all code containing

"from jw.lib.base import Result"

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-02 17:52:49 +02:00
32fc7e097a
lib.ec.ssh.AsyncSSH: Ignore missing asyncssh

Accept if AsyncSSH is missing. The package would be nice to have, i.e. a good candidate for a "recommends" section, but until there's support for that, better be able to do without and fall back to command-line ssh.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-02 17:52:49 +02:00
920e950eed
lib.App: Ignore missing argcomplete

Accept if argcomplete is missing. The package would be nice to have, i.e. a good candidate for a "recommends" section, but until there's support for that, better be able to do without.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-02 17:52:49 +02:00
6874a90bb4
lib.base.Result.__repr__(): Add method

Add __repr__() to Result to make it more meaningful in log messages.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-02 13:17:22 +02:00
1999f85645
lib.ExecContext.sudo(): Remove dead code

ExecContext.sudo() contains dead code, remove it.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-02 13:17:19 +02:00
0bfb08bce2
lib.ExecContext.run(): Fix: Pass up Exceptions

Fix another regression of commit 6db73873e7: lib.ExecContext.CallContext.__exit__() returns True, which swallows all exceptions thrown in the context of _run() and _sudo(). Fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-02 13:16:47 +02:00
4051830db8
distros.suse: Force non-interactive resolution

Updates running in non-interactive mode are passed

--force-resolution --auto-agree-with-licenses

to get more snakes out of the way, as recently during CI:

# make pkg-install-testbuild-deps /usr/bin/which: no xdg-open in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin)

/usr/bin/python3.13 ./scripts/jw-pkg.py -p ./.. -t . --topdir-format absolute --interactive=false pkg install "make" "time" "xdg-utils" "coreutils" "cpio" "git-core" "bash" "python3" "sudo" "gawk" "pkg-config" "python3-isort" "python3-yapf" "python3-ruff" "python3-pyright" "rpmbuild" "python3-base"

,---- file://local: Running /usr/bin/zypper --non-interactive --gpg-auto-import-keys --no-gpg-checks install make time xdg-utils coreutils cpio git-core bash python3 sudo gawk pkg-config python3-isort python3-yapf python3-ruff python3-pyright rpmbuild python3-base - > | Loading repository data... | Reading installed packages... | 'sudo' is already installed. | No update candidate for 'sudo-1.9.17p2-2.2.x86_64'. The highest available version is already installed. | 'bash' is already installed. | No update candidate for 'bash-5.3.9-6.4.x86_64'. The highest available version is already installed. | 'python3' not found in package names. Trying capabilities. | 'python313' providing 'python3' is already installed. | 'coreutils' is already installed. | No update candidate for 'coreutils-9.11-3.1.x86_64'. The highest available version is already installed. | 'pkg-config' not found in package names. Trying capabilities. | 'make' is already installed. | No update candidate for 'make-4.4.1-3.5.x86_64'. The highest available version is already installed. | 'python3-base' not found in package names. Trying capabilities. | 'python313-base' providing 'python3-base' is already installed. | 'rpmbuild' not found in package names. Trying capabilities. | 'git-core' is already installed. | No update candidate for 'git-core-2.54.0-2.1.x86_64'. The highest available version is already installed. | 'python3-pyright' not found in package names. Trying capabilities. | 'python3-ruff' not found in package names. Trying capabilities. | 'python3-isort' not found in package names. Trying capabilities. | 'python3-yapf' not found in package names. Trying capabilities. | Resolving package dependencies... | | Problem: 1: the installed busybox-gawk-1.37.0-41.4.noarch conflicts with 'gawk' provided by the to be installed gawk-5.4.0-1.1.x86_64 | Solution 1: Following actions will be done: | do not install gawk-5.4.0-1.1.x86_64 | do not ask to install a solvable providing rpmbuild | Solution 2: deinstallation of busybox-gawk-1.37.0-41.4.noarch | | Choose from above solutions by number or cancel [1/2/c/d/?] (c): c `---- file://local: Running /usr/bin/zypper --non-interactive --gpg-auto-import-keys --no-gpg-checks install make time xdg-utils coreutils cpio git-core bash python3 sudo gawk pkg-config python3-isort python3-yapf python3-ruff python3-pyright rpmbuild python3-base - <

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-01 20:14:45 +02:00
4df432b22d
lib.util.run_curl_into(): Fix indentation error

Fix a regregression breaking run_curl() / run_curl_into(), introduced by commit 6db73873e7. A missing indentation raises a non-existing Error after successful JSON parsing, fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-01 20:14:45 +02:00
5d1ba6e15a
pyproject.toml: Enforce import annotations style

Add new ruff rules and fix their fallout:

future-annotations = true

select = [ "TC", # type-checking import placement rules "FA", # future annotations rules ]

This comprises:

- Streamline imports and exports in cmds.xxx.Cmd

- Import base class as "Base"
- Export types Cmd and Parent via __all__

- Move all types imported only for annotation below TYPE_CHECKING

- Use "from __future__ import annotations" all over the place

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-01 14:34:25 +02:00
f87e386906
lib.log: from __future__ import annotations

The last commit to fix checker findings broke runtime behaviour, fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-01 14:24:01 +02:00
24928c6f5d
cmds / lib: Fix more static checker findings

Fix more errors and warnings produced by "make check" as reported by CI and a pyright upgrade.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-06-01 07:55:11 +02:00
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
13ec34cc57
lib.init.detect_modules(): Add function

Not all __init__.py modules are generated by python-tools.sh, some are needed early to make jw-pkg useful without generation, notably in jw.pkg.cmds.

Add detect_modules() to unify that detection, and place it into a minimal module lib.init to not increase startup time cost.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-05-31 17:52:20 +02:00
fc6f2fbb65
python-tools.sh: Fix __init__.py linter complaints

The __init__.py files as gnerated by python-tools.sh contain multiple issues, fix them:

- Make the machinery fail if the same type name is imported from different modules
- Support relative imports from .Module import Module instead of having to use the entire module path as import source

- Import types explicitly re-exported with "as":

from .Module import Module as Module
Otherwise ruff will regard the type as "imported but not used"
- Add "# ruff: noqa: E501" near the top. The import lines can get long and are beyond manual control (except for renaming the modules themselves, that is). This can cause ruff to fail, so get it to accept long lines in __init__.py. The style violation doesn't make much of a difference in generated code, anyway, because nobody reads that. Plus what's happening in the code isn't rocket science, so good style wouldn't help much with understanding, either.

This promptly digs up two symbol name conflicts lib.pm.dpkg and lib.pm.rpm. Fix them along with this commit to keep it from breaking the build.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-05-31 17:51:51 +02:00
99b57c5f4e
lib.Uri.basename: Add property

Add a property to get a file's basename. For

https://host.com/path/to/file?key=val

.basename should return "file"".

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-05-21 11:32:33 +02:00
237096cac9
lib.Uri: Add new convenience properties

Add a couple of properties and methods that come in handy when manipulating URIs:

.path .safe_full_with_username

do the obvious, and these return new Uri objects with modified paths:

.new_add_path() .new_replace_path()

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-05-06 15:26:14 +02:00
30abb227c7
lib.Cmd.print_help(): Add method

Add Cmd.print_help(). By default, it prints a help message. If passed an integer exit_status, it also calls sys.exit(exit_status).

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-05-01 15:57:16 +02:00
7e2099877c
lib.Cmd.load_subcommands(): Add method

Push cmds.Cmd._add_subcommands() as lib.Cmd.load_subcommands() one step up to the top of the type hierarchy ladder.

By default, it does the same thing, i.e. load subcommands matching frobnicate.Cmd* if called on class CmdFrobnicate.

This commit also replaces invocations of Cmd._add_subcommands() by invocations of this new method.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-05-01 15:56:08 +02:00
5bb7fe96db
lib.util.get(): Add function

Add get(), which does pretty much what FileContext.get() does, but with auto-instantiating a FileContext instance. Input processing filters can be passed, too, all *args and **kwargs are passed unchanged to the FileContext's constructor.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
fe1eb0014a
lib.ProcFilter: Add run()

Add a run() function. It constructs a pipeline from various possible types, and runs its data parameter through it.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
1c11503dfb
lib.Distro._install(): Implement remote installation

lib.Distro._install()'s default implementation allows to install packages specified as direct links, but only to the local machine. Implement installation to arbitrary hosts specified with --target.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
60de1b0ca0
lib.Distro.pkg_ext: Fix call to os_cascade

The Distro.pkg_ext property calls .os_cascade, which is a property, with parenthesis. Fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
5837d10a1c
lib.util.copy(): Tolerate list[str] and FileContext args

Modify copy():

- Allow the source argument src_uri to be a list of URIs, in which case copy should return a list of paths instead of one path
- Change the dst_uri parameter to dst, signalling that it now also tolerates a FileContext
- Use the CopyContext class to manage the source and target FileContext instances
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
d9746cd20b
lib + cmds.projects: Use lib.Uri

Remove the feeble attempts at unifying URI handling, and use class Uri from lib.Uri instead.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
f0eeb14a97
lib.base.Uri: Add module

Add a URI abstraction module. The class is designed to replace less unified attempts at URI parsing throughout the jw-pkg code base.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
6cfb86d2a7
lib.FileContext.is_dir(): Add follow_symlinks

Make FileContext.is_dir() usable: - Add follow_symlinks parameter meant to do the obvious - Fix missing await

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
b5762116a1
lib.FileContext.get(): Remove bogus owner, group, mode

owner, group or mode don't belong in .get()'s signature, remove them.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:29 +02:00
6ced64202e
lib.ExecContext._stat(): Don't decode stderr twice

The output of /usr/bin/stat is decoded once the Result object is decoded as a whole, and then again individually, which fails of course. Fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-28 13:00:28 +02:00
37fcf56ad2
lib.FileContext._mktemp(): Fix missing self before _chroot()

_mktemp() call _chroot() instead of self._chroot(), fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-25 13:50:42 +02:00
66b968c51c
TarIo.TarIoTarFile._extract(): Fix typo

TarIoTarFile is currently unused but should still be correct, so fix a typo in _extract().

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-25 08:47:45 +02:00
069899e48e
lib: Remove leftover breakpoints

Remove two breakpoints not belonging in the code of TarIo and Types.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-25 08:47:21 +02:00
a9475de48e
lib.ExecContext: Add open() + close() around _run()

Enclose ExecContext._run() in an open() / close() - pair. This is convenient for the caller in that it doesn't need to take care of opening and closing for one call only, and inconvenient in that it forces the caller to conciously add an open() / close() - pair around multiple run() calls where it wants the context to stay open in between. Or use the ExecContext as a context manager.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-25 08:45:29 +02:00
3ac3aff997
lib: Fix silent assertitons

There are a couple of assert statements in the codebase which can make jw-pkg fail without any detail whatsoever if --backtrace is not specified, fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-25 08:44:49 +02:00
7309ec687e
lib.TarIo.extract(): Return list of extracted files

Make TarIo.extract return the list of files that were actually extracted.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-24 16:53:55 +02:00
f2ca32a343
CopyContext: Replace src_uri, dst_uri by src, dst

Allow to pass ready-made FileContext objects to CopyContext's destructor so it doesn't need to instantiate them itself.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-24 16:53:55 +02:00
772512aee0
FileContext.open() / close(): Only call wrapped once

FileContext's _open() and _close() are called everytime their wrapper is called, which tasks the caller with keeping track of whether they were already called or not. Be a little easier on the caller, keep track in an open count, and call _open() only once for multiple calls to open(), and close() likewise. The caller still needs to make sure the number of open() and close() calls matches.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-24 16:53:55 +02:00
cc6febcda3
lib.Cmd: Add description property

Add keyword-argument description to Cmd.__init__(), and default it to help. Also, add a property .description returning it, and add it to add_parser() so that it shows up in the usage message.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-24 16:53:54 +02:00
a5e7647026
lib.TarIo: Add module

Add a class providing a method to read tar files from a FileContext, and extract them to another.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 20:18:42 +02:00
d82bc20663
lib.CopyContext: Add module

Add a CopyContext class. At this point it mostly acts as a context manager for two FileContext instances, and copying data is the canonical case to use it, hence the name.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 20:18:42 +02:00
4c81647bbe
lib.FileContext: Support attaching data filters

- Add optional in_pipe and out_pipe parameters to __init__()

- Add a add_proc_filter() method

Add possibilites to attach input / output pipes to a FileContext instance. Data will be passed through the input pipe between ._get() and .get(), and through the output pipe between .put() and _put().

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 20:18:40 +02:00
58f7997bc6
lib.FileContext.open(): Add method

Add an async open() method which should allow to do what __init__() couldn't, because it's not async, and to match the already existing .close(). It's called by __aenter__() __aexit__() if the FileContext is instantiated as context manager, or at will when the user finds it a good idea.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:32:34 +02:00
432830a5c5
lib.FileContext.mkdir(): Add method

Add .mkdir() to the API which should do the expected, and implement it in ExecContext and Local specializations.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:30:22 +02:00
7547d13a6d
lib.FileContext: Support chroot

Add a bool parameter "chroot" to __init__(). If passed, all path-centric operations act as if .root was prepended to it.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:30:08 +02:00
b52264dfad
lib.FileContext.root: Add property

Add a .root property, returning the path part of the instantiating URI. Nothing meaningful beyond returning it is done with it so far.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:29:20 +02:00
6a3cf7a283
lib.FileContext: Beautify NotImplementedErrors

Each wrapper method throws a NotImplementedError by default. Make that error a little more descriptive in case it really gets thrown.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:28:54 +02:00
3360ec86cb
lib.FileContext: Add URI manipulation methods

Add .schema_from_uri(), .split_uri(), .id() to define some standardish way to dissect an URI the same way FileContext does.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:27:38 +02:00
efe047fa50
lib.ExecContext: Strip unnecessary args from __init__()

Code beautification: __init__() doesn't use the arguments it grabs by name from its parameter list, use *args and **kwargs instead.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:26:42 +02:00
d70454b32a
lib.ProcFilterGpg: Add module

Add a ProcFilter implementation for decrypting GPG blobs. It needs an ExecContext passed to __init__() in order to run /usr/bin/gpg.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:25:57 +02:00
411bfd41ca
lib.ProcFilter: Add module

Add the ProcFilter module, containing the following classes:

- ProcFilter:

Abstract base class defining a run(data: bytes) -> Result method, allowing to do arbitrary data manipulation in subclasses.

- ProcFilterIdenty

A ProcFilter specialization which passes through the input unchanged.

- ProcPipeline

A container for multiple chained ProcFilter classes
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-04-23 15:23:27 +02:00