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>
This commit is contained in:
parent
8c5c98c95a
commit
6db73873e7
97 changed files with 3229 additions and 1893 deletions
|
|
@ -1,5 +1,4 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
|
||||
from jw.pkg.App import App
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
from pkgutil import extend_path
|
||||
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
|
|
|
|||
|
|
@ -1,29 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This source code file is a merge of various build tools and a horrible mess.
|
||||
#
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Iterable
|
||||
import argparse
|
||||
import os
|
||||
import pwd
|
||||
import re
|
||||
import sys
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import TypeAlias
|
||||
import os, sys, pwd, re
|
||||
|
||||
import os, sys, argparse, pwd, re
|
||||
from functools import lru_cache, cached_property
|
||||
from enum import Enum, auto
|
||||
from functools import lru_cache
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .lib.App import App as Base
|
||||
from .lib.log import *
|
||||
from .lib.Distro import Distro
|
||||
from .lib.base import InputMode
|
||||
from .lib.log import DEBUG, ERR, log
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import os
|
||||
import pwd
|
||||
import re
|
||||
import sys
|
||||
|
||||
from typing import TypeAlias
|
||||
|
||||
from .lib.ExecContext import ExecContext
|
||||
from .lib.PackageFilter import PackageFilter
|
||||
|
||||
# Meaning of pkg.requires.xxx variables
|
||||
# build: needs to be built and installed before this can be built
|
||||
# devel: needs to be installed before this-devel can be installed, i.e. before _other_ packages can be built against this
|
||||
# run: needs to be installed before this-run can be installed, i.e. before this and other packages can run with this
|
||||
# devel: needs to be installed before this-devel can be installed,
|
||||
# i.e. before _other_ packages can be built against this
|
||||
# run: needs to be installed before this-run can be installed,
|
||||
# i.e. before this and other packages can run with this
|
||||
|
||||
# --------------------------------------------------------------------- Helpers
|
||||
|
||||
|
|
@ -35,32 +46,34 @@ class ResultCache(object):
|
|||
def run(self, func, args):
|
||||
d = self.__cache
|
||||
depth = 0
|
||||
keys = [ func.__name__ ] + args
|
||||
l = len(keys)
|
||||
keys = [func.__name__] + args
|
||||
sz = len(keys)
|
||||
for k in keys:
|
||||
if k is None:
|
||||
k = 'None'
|
||||
else:
|
||||
k = str(k)
|
||||
depth += 1
|
||||
#log(DEBUG, 'depth = ', depth, 'key = ', k, 'd = ', str(d))
|
||||
# log(DEBUG, 'depth = ', depth, 'key = ', k, 'd = ', str(d))
|
||||
if k in d:
|
||||
if l == depth:
|
||||
if sz == depth:
|
||||
return d[k]
|
||||
d = d[k]
|
||||
continue
|
||||
if l == depth:
|
||||
if sz == depth:
|
||||
r = func(*args)
|
||||
d[k] = r
|
||||
return r
|
||||
d = d[k] = {}
|
||||
#d = d[k]
|
||||
raise Exception('cache algorithm failed for function', func.__name__, 'in depth', depth)
|
||||
# d = d[k]
|
||||
raise Exception(
|
||||
'cache algorithm failed for function', func.__name__, 'in depth', depth
|
||||
)
|
||||
|
||||
class Scope(Enum):
|
||||
Self = auto()
|
||||
One = auto()
|
||||
Subtree = auto()
|
||||
Self = auto()
|
||||
One = auto()
|
||||
Subtree = auto()
|
||||
|
||||
Graph: TypeAlias = dict[str, set[str]]
|
||||
|
||||
|
|
@ -68,27 +81,41 @@ Graph: TypeAlias = dict[str, set[str]]
|
|||
|
||||
class App(Base):
|
||||
|
||||
def __format_topdir(self, topdir: None|str, fmt: str) -> str:
|
||||
if topdir is None:
|
||||
def __format_topdir(self, path: None | str, fmt: str) -> str | None:
|
||||
if path is None:
|
||||
return None
|
||||
match fmt:
|
||||
case 'unaltered':
|
||||
return topdir
|
||||
return path
|
||||
case None | 'absolute':
|
||||
return os.path.abspath(self.__topdir)
|
||||
return os.path.abspath(path)
|
||||
case _:
|
||||
m = re.search(r'^make:(\S+)$', fmt)
|
||||
if m is None:
|
||||
raise Exception(f'Can\'t interpret "{fmt}" as valid topdir ' +
|
||||
'reference, expecting "unaltered", "absolute", or "make:<variable-name>"')
|
||||
raise Exception(
|
||||
f'Can\'t interpret "{fmt}" as valid topdir reference, '
|
||||
'expecting "unaltered", "absolute", or "make:<variable-name>"'
|
||||
)
|
||||
return '$(' + m.group(1) + ')'
|
||||
|
||||
def __proj_dir(self, name: str, pretty) -> str:
|
||||
@property
|
||||
def __topdir(self) -> str:
|
||||
if self.___topdir is None:
|
||||
raise Exception('Tried to access undefined top directory')
|
||||
return self.___topdir
|
||||
|
||||
@property
|
||||
def __pretty_topdir(self) -> str:
|
||||
if self.___pretty_topdir is None:
|
||||
raise Exception('Tried to access undefined pretty top directory')
|
||||
return self.___pretty_topdir
|
||||
|
||||
def __proj_dir(self, name: str, pretty: bool) -> str | None:
|
||||
if name == self.__top_name:
|
||||
if pretty:
|
||||
return self.__pretty_topdir
|
||||
return self.__topdir
|
||||
for d in [ self.__projs_root, '/opt' ]:
|
||||
for d in [self.__projs_root, '/opt']:
|
||||
ret = d + '/' + name
|
||||
if os.path.exists(ret):
|
||||
return ret
|
||||
|
|
@ -97,7 +124,14 @@ class App(Base):
|
|||
return None
|
||||
raise Exception('No project path found for module "{}"'.format(name))
|
||||
|
||||
def __find_dir(self, name: str, search_subdirs: list[str]=[], search_absdirs: list[str]=[], pretty: bool=True) -> str|None:
|
||||
def __find_dir(
|
||||
self,
|
||||
name: str,
|
||||
search_subdirs: list[str] = [],
|
||||
search_absdirs: list[str] = [],
|
||||
pretty: bool = True,
|
||||
) -> str | None:
|
||||
|
||||
def format_pd(name: str, pd: str, pretty: bool):
|
||||
if not pretty:
|
||||
return pd
|
||||
|
|
@ -107,7 +141,10 @@ class App(Base):
|
|||
return pd
|
||||
if name == self.__top_name:
|
||||
return self.__pretty_topdir
|
||||
raise NotImplementedError(f'Tried to pretty-format directory {pd}, not implemented')
|
||||
raise NotImplementedError(
|
||||
f'Tried to pretty-format directory {pd}, not implemented'
|
||||
)
|
||||
|
||||
pd = self.__proj_dir(name, False)
|
||||
if pd is None:
|
||||
return None
|
||||
|
|
@ -126,11 +163,25 @@ class App(Base):
|
|||
return ret
|
||||
return None
|
||||
|
||||
def __get_project_refs_cached(self, buf, visited, spec, section, key, add_self, scope, names_only):
|
||||
return self.__res_cache.run(self.__get_project_refs, [buf, visited, spec, section, key, add_self, scope, names_only])
|
||||
def __get_project_refs_cached(
|
||||
self, buf, visited, spec, section, key, add_self, scope, names_only
|
||||
):
|
||||
return self.__res_cache.run(
|
||||
self.__get_project_refs,
|
||||
[buf, visited, spec, section, key, add_self, scope, names_only],
|
||||
)
|
||||
|
||||
def __get_project_refs(self, buf: list[str], visited: set[str], spec: str,
|
||||
section: str, key: str, add_self: bool, scope: Scope, names_only: bool) -> None:
|
||||
def __get_project_refs(
|
||||
self,
|
||||
buf: list[str],
|
||||
visited: set[str],
|
||||
spec: str,
|
||||
section: str,
|
||||
key: str,
|
||||
add_self: bool,
|
||||
scope: Scope,
|
||||
names_only: bool,
|
||||
) -> None:
|
||||
name = self.strip_module_from_spec(spec)
|
||||
if names_only:
|
||||
spec = name
|
||||
|
|
@ -142,59 +193,95 @@ class App(Base):
|
|||
return
|
||||
visited.add(spec)
|
||||
deps = self.get_value(name, section, key)
|
||||
log(DEBUG, 'name = ', name, 'section = ', section, 'key = ', key, 'deps = ', deps, 'scope = ', scope.name, 'visited = ', visited)
|
||||
log(
|
||||
DEBUG,
|
||||
(
|
||||
f'name={name}, section={section}, key={key}, deps={deps}, '
|
||||
f'scope={scope.name}, visited={visited}'
|
||||
),
|
||||
)
|
||||
if deps and scope != Scope.Self:
|
||||
if scope == Scope.One:
|
||||
subscope = Scope.Self
|
||||
else:
|
||||
subscope = Scope.Subtree
|
||||
deps = deps.split(',')
|
||||
for dep in deps:
|
||||
for dep in deps.split(','):
|
||||
dep = dep.strip()
|
||||
if not(len(dep)):
|
||||
if not (len(dep)):
|
||||
continue
|
||||
self.__get_project_refs_cached(buf, visited, dep,
|
||||
section, key, add_self=True, scope=subscope,
|
||||
names_only=names_only)
|
||||
self.__get_project_refs_cached(
|
||||
buf,
|
||||
visited,
|
||||
dep,
|
||||
section,
|
||||
key,
|
||||
add_self = True,
|
||||
scope = subscope,
|
||||
names_only = names_only,
|
||||
)
|
||||
if add_self:
|
||||
buf.append(spec)
|
||||
|
||||
def __read_dep_graph(self, projects: list[str], section: str, graph: Graph) -> None:
|
||||
def __read_dep_graph(
|
||||
self,
|
||||
projects: list[str],
|
||||
sections: str | list[str],
|
||||
graph: Graph,
|
||||
) -> None:
|
||||
if isinstance(sections, str):
|
||||
sections = [sections]
|
||||
for project in projects:
|
||||
if project in graph:
|
||||
continue
|
||||
deps = self.get_project_refs([ project ], ['pkg.requires.jw'], section,
|
||||
scope = Scope.One, add_self=False, names_only=True)
|
||||
if not deps is None:
|
||||
for section in sections:
|
||||
deps = self.get_project_refs(
|
||||
[project],
|
||||
['pkg.requires.jw'],
|
||||
sections,
|
||||
scope = Scope.One,
|
||||
add_self = False,
|
||||
names_only = True,
|
||||
)
|
||||
if deps is None:
|
||||
continue
|
||||
graph[project] = set(deps)
|
||||
for dep in deps:
|
||||
self.__read_dep_graph([ dep ], section, graph)
|
||||
self.__read_dep_graph([dep], sections, graph)
|
||||
|
||||
def __flip_dep_graph(self, graph: Graph):
|
||||
ret: Graph = {}
|
||||
for project, deps in graph.items():
|
||||
for d in deps:
|
||||
if not d in ret:
|
||||
if d not in ret:
|
||||
ret[d] = set()
|
||||
ret[d].add(project)
|
||||
return ret
|
||||
|
||||
def __find_circular_deps_recursive(self, project: str, graph: Graph, unvisited: list[str],
|
||||
temp: set[str], path: str) -> str|None:
|
||||
def __find_circular_deps_recursive(
|
||||
self,
|
||||
project: str,
|
||||
graph: Graph,
|
||||
unvisited: list[str],
|
||||
temp: set[str],
|
||||
path: list[str],
|
||||
) -> str | None:
|
||||
if project in temp:
|
||||
log(DEBUG, 'found circular dependency at project', project)
|
||||
return project
|
||||
if not project in unvisited:
|
||||
if project not in unvisited:
|
||||
return None
|
||||
temp.add(project)
|
||||
if project in graph:
|
||||
for dep in graph[project]:
|
||||
last = self.__find_circular_deps_recursive(dep, graph, unvisited, temp, path)
|
||||
last = self.__find_circular_deps_recursive(
|
||||
dep, graph, unvisited, temp, path
|
||||
)
|
||||
if last is not None:
|
||||
path.insert(0, dep)
|
||||
return last
|
||||
unvisited.remove(project)
|
||||
temp.remove(project)
|
||||
return None
|
||||
|
||||
def __find_circular_deps(self, projects: list[str], flavours: list[str]) -> bool:
|
||||
graph: Graph = {}
|
||||
|
|
@ -205,25 +292,27 @@ class App(Base):
|
|||
while unvisited:
|
||||
project = unvisited[0]
|
||||
log(DEBUG, 'Checking circular dependency of', project)
|
||||
last = self.__find_circular_deps_recursive(project, self.__flip_dep_graph(graph), unvisited, temp, ret)
|
||||
last = self.__find_circular_deps_recursive(
|
||||
project, self.__flip_dep_graph(graph), unvisited, temp, ret
|
||||
)
|
||||
if last is not None:
|
||||
log(DEBUG, f'Found circular dependency below {project}, last is {last}')
|
||||
return True
|
||||
return False
|
||||
|
||||
def __init__(self, distro: Distro|None=None) -> None:
|
||||
def __init__(self, distro: Distro | None = None) -> None:
|
||||
|
||||
super().__init__('jw-pkg swiss army knife', modules=['jw.pkg.cmds'])
|
||||
super().__init__('jw-pkg swiss army knife', modules = ['jw.pkg.cmds'])
|
||||
|
||||
# -- Members without default values
|
||||
self.__opt_interactive: bool|None = None
|
||||
self.__opt_verbose: bool|None = None
|
||||
self.__top_name: str|None = None
|
||||
self.__opt_interactive: bool | None = None
|
||||
self.__opt_verbose: bool | None = None
|
||||
self.__top_name: str | None = None
|
||||
self.__distro = distro
|
||||
self.__res_cache = ResultCache()
|
||||
self.__topdir: str|None = None
|
||||
self.__pretty_topdir: str|None = None
|
||||
self.__exec_context: ExecContext|None = None
|
||||
self.___topdir: str | None = None
|
||||
self.___pretty_topdir: str | None = None
|
||||
self.__exec_context: ExecContext | None = None
|
||||
|
||||
# -- Members with default values
|
||||
self.__topdir_fmt = 'absolute'
|
||||
|
|
@ -235,48 +324,83 @@ class App(Base):
|
|||
pkg_filter_str = self.args.pkg_filter
|
||||
if pkg_filter_str is None:
|
||||
pkg_filter_str = os.getenv('JW_DEFAULT_PKG_FILTER')
|
||||
pkg_filter: PackageFilter|None = None
|
||||
pkg_filter: PackageFilter | None = None
|
||||
if pkg_filter_str is not None:
|
||||
from .lib.PackageFilter import PackageFilterString
|
||||
|
||||
pkg_filter = PackageFilterString(pkg_filter_str)
|
||||
self.__distro = await Distro.instantiate(
|
||||
ec = self.exec_context,
|
||||
id = self.args.distro_id,
|
||||
default_pkg_filter = pkg_filter,
|
||||
)
|
||||
ec = self.exec_context,
|
||||
id = self.args.distro_id,
|
||||
default_pkg_filter = pkg_filter,
|
||||
)
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb) -> None:
|
||||
if self.__exec_context is not None:
|
||||
await self.__exec_context.close()
|
||||
self.__exec_context = None
|
||||
return super().__aexit__(exc_type, exc, tb)
|
||||
|
||||
def _add_arguments(self, parser) -> None:
|
||||
super()._add_arguments(parser)
|
||||
parser.add_argument('-t', '--topdir', default = None, help='Project Path')
|
||||
parser.add_argument('--topdir-format', default = 'absolute', help='Output references to topdir as '
|
||||
+ 'one of "make:<var-name>", "unaltered", "absolute". Absolute topdir by default')
|
||||
parser.add_argument('-p', '--prefix', default = None,
|
||||
help='Parent directory of project source directories')
|
||||
parser.add_argument('--distro-id', default=None, help='Distribution ID (default is taken from /etc/os-release)')
|
||||
parser.add_argument('--interactive', choices=['true', 'false', 'auto'], default='true', help='Wait for user input or try to proceed unattended')
|
||||
parser.add_argument('--verbose', action='store_true', default=False, help='Be verbose on stderr about what\'s being done on the distro level')
|
||||
parser.add_argument('--target', default='local', help='Run commands on this host')
|
||||
parser.add_argument('--pkg-filter', help='Default filter for all distribution package-related operations')
|
||||
parser.add_argument('-t', '--topdir', default = None, help = 'Project Path')
|
||||
parser.add_argument(
|
||||
'--topdir-format',
|
||||
default = 'absolute',
|
||||
help = (
|
||||
'Output references to topdir as one of "make:<var-name>", '
|
||||
'"unaltered", "absolute". Absolute topdir by default'
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p',
|
||||
'--prefix',
|
||||
default = None,
|
||||
help = 'Parent directory of project source directories',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--distro-id',
|
||||
default = None,
|
||||
help = 'Distribution ID (default is taken from /etc/os-release)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--interactive',
|
||||
choices = ['true', 'false', 'auto'],
|
||||
default = 'true',
|
||||
help = 'Wait for user input or try to proceed unattended',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--verbose',
|
||||
action = 'store_true',
|
||||
default = False,
|
||||
help = "Be verbose on stderr about what's being done on the distro level",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--target', default = 'local', help = 'Run commands on this host'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--pkg-filter',
|
||||
help = 'Default filter for all distribution package-related operations',
|
||||
)
|
||||
|
||||
async def _run(self, args: argparse.Namespace) -> None:
|
||||
self.__topdir = args.topdir
|
||||
self.__pretty_topdir = self.__format_topdir(self.__topdir, args.topdir_format)
|
||||
self.___topdir = args.topdir
|
||||
self.___pretty_topdir = self.__format_topdir(self.___topdir, args.topdir_format)
|
||||
self.__topdir_fmt = args.topdir_format
|
||||
if self.__topdir is not None:
|
||||
self.__top_name = self.read_value(self.__topdir + '/make/project.conf', 'build', 'name')
|
||||
if self.___topdir is not None:
|
||||
self.__top_name = self.read_value(
|
||||
self.___topdir + '/make/project.conf', 'build', 'name'
|
||||
)
|
||||
if not self.__top_name:
|
||||
self.__top_name = re.sub('-[0-9.-]*$', '', os.path.basename(os.path.realpath(self.__topdir)))
|
||||
self.__top_name = re.sub(
|
||||
'-[0-9.-]*$',
|
||||
'',
|
||||
os.path.basename(os.path.realpath(self.___topdir))
|
||||
)
|
||||
if args.prefix is not None:
|
||||
self.__projs_root = args.prefix
|
||||
self.__pretty_projs_root = args.prefix
|
||||
await self.__init_async()
|
||||
return await super()._run(args)
|
||||
await super()._run(args)
|
||||
|
||||
@property
|
||||
def interactive(self) -> bool:
|
||||
|
|
@ -288,20 +412,32 @@ class App(Base):
|
|||
self.__opt_interactive = False
|
||||
case 'auto':
|
||||
self.__opt_interactive = sys.stdin.isatty()
|
||||
case _:
|
||||
raise ValueError(
|
||||
f'Unknown --interactive value: {self.args.interactive}'
|
||||
)
|
||||
# Not logically possible to fail, but this keeps pyright happy
|
||||
assert self.__opt_interactive is not None
|
||||
return self.__opt_interactive
|
||||
|
||||
@property
|
||||
def verbose(self) -> bool:
|
||||
if self.__opt_verbose is None:
|
||||
self.__opt_verbose = self.args.verbose
|
||||
# Not logically possible to fail, but this keeps pyright happy
|
||||
assert self.__opt_verbose is not None
|
||||
return self.__opt_verbose
|
||||
|
||||
@property
|
||||
def exec_context(self) -> str:
|
||||
def exec_context(self) -> ExecContext:
|
||||
if self.__exec_context is None:
|
||||
from .lib.ExecContext import ExecContext
|
||||
self.__exec_context = ExecContext.create(self.args.target, interactive=self.interactive,
|
||||
verbose_default=self.verbose)
|
||||
|
||||
self.__exec_context = ExecContext.create(
|
||||
self.args.target,
|
||||
interactive = self.interactive,
|
||||
verbose_default = self.verbose,
|
||||
)
|
||||
return self.__exec_context
|
||||
|
||||
@property
|
||||
|
|
@ -318,29 +454,51 @@ class App(Base):
|
|||
raise Exception('No distro object')
|
||||
return self.__distro
|
||||
|
||||
def find_dir(self, name: str, search_subdirs: list[str]=[], search_absdirs: list[str]=[], pretty: bool=True):
|
||||
return self.__find_dir(name, search_subdirs, search_absdirs, pretty)
|
||||
def find_dir(
|
||||
self,
|
||||
name: str,
|
||||
search_subdirs: list[str] = [],
|
||||
search_absdirs: list[str] = [],
|
||||
pretty: bool = True,
|
||||
) -> str:
|
||||
ret = self.__find_dir(name, search_subdirs, search_absdirs, pretty)
|
||||
if ret is None:
|
||||
msg = f'Failed to find directory for "{name}"'
|
||||
log(ERR, msg)
|
||||
for search_name, search in [
|
||||
('subdirs', search_subdirs),
|
||||
('absdirs', search_absdirs),
|
||||
]:
|
||||
if search:
|
||||
log(ERR, f' Searched {search_name} in:')
|
||||
for d in search:
|
||||
log(ERR, f' - {d}')
|
||||
raise FileNotFoundError(msg)
|
||||
return ret
|
||||
|
||||
# TODO: add support for customizing this in project.conf
|
||||
def htdocs_dir(self, project: str) -> str:
|
||||
return self.find_dir(project, ['/src/html/htdocs', '/tools/html/htdocs', '/htdocs'],
|
||||
['/srv/www/proj/' + project])
|
||||
return self.find_dir(
|
||||
project,
|
||||
['/src/html/htdocs', '/tools/html/htdocs', '/htdocs'],
|
||||
['/srv/www/proj/' + project],
|
||||
)
|
||||
|
||||
# TODO: add support for customizing this in project.conf
|
||||
def tmpl_dir(self, name: str) -> str:
|
||||
return self.find_dir(name, ['/tmpl'], ['/opt/' + name + '/share/tmpl'])
|
||||
return self.find_dir(name, ['/tmpl'], ['/opt/' + name + '/share/tmpl'])
|
||||
|
||||
def strip_module_from_spec(self, mod):
|
||||
return re.sub(r'-dev$|-devel$|-run$', '', re.split('([=><]+)', mod)[0].strip())
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
@lru_cache(maxsize = None)
|
||||
def get_section(self, path: str, section: str) -> str:
|
||||
ret = ''
|
||||
pat = '[' + section + ']'
|
||||
in_section = False
|
||||
file = open(path)
|
||||
for line in file:
|
||||
if (line.rstrip() == pat):
|
||||
if line.rstrip() == pat:
|
||||
in_section = True
|
||||
continue
|
||||
if in_section:
|
||||
|
|
@ -350,10 +508,10 @@ class App(Base):
|
|||
file.close()
|
||||
return ret.rstrip()
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def read_value(self, path: str, section: str, key: str) -> str|None:
|
||||
@lru_cache(maxsize = None)
|
||||
def read_value(self, path: str, section: str, key: str) -> str | None:
|
||||
|
||||
def scan_section(f, key: str) -> str|None:
|
||||
def scan_section(f, key: str) -> str | None:
|
||||
if key is None:
|
||||
ret = ''
|
||||
for line in f:
|
||||
|
|
@ -374,43 +532,44 @@ class App(Base):
|
|||
cont_line = ''
|
||||
rx = re.compile(r'^\s*' + key + r'\s*=\s*(.*)\s*$')
|
||||
for line in lines:
|
||||
#log(DEBUG, ' looking for "%s" in line="%s"' % (key, line))
|
||||
# log(DEBUG, ' looking for "%s" in line="%s"' % (key, line))
|
||||
m = re.search(rx, line)
|
||||
if m is not None:
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
def scan_section_debug(f, key: str) -> str|None:
|
||||
def scan_section_debug(f, key: str) -> str | None:
|
||||
ret = scan_section(f, key)
|
||||
#log(DEBUG, ' returning', rr)
|
||||
# log(DEBUG, ' returning', rr)
|
||||
return ret
|
||||
|
||||
try:
|
||||
#log(DEBUG, 'looking for {}::[{}].{}'.format(path, section, key))
|
||||
# log(DEBUG, 'looking for {}::[{}].{}'.format(path, section, key))
|
||||
# TODO: Parse
|
||||
with open(path, 'r') as f:
|
||||
if not len(section):
|
||||
rr = scan_section(f, key)
|
||||
return scan_section(f, key)
|
||||
pat = '[' + section + ']'
|
||||
for line in f:
|
||||
if line.rstrip() == pat:
|
||||
return scan_section(f, key)
|
||||
return None
|
||||
except:
|
||||
log(DEBUG, path, 'not found')
|
||||
except Exception:
|
||||
log(DEBUG, f'Not found: {path}')
|
||||
# TODO: handle this special case cleaner somewhere up the stack
|
||||
if section == 'build' and key == 'libname':
|
||||
return 'none'
|
||||
return None
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def get_value(self, project: str, section: str, key: str) -> str:
|
||||
if self.__top_name and project == self.__top_name:
|
||||
proj_root = self.__topdir
|
||||
else:
|
||||
proj_root = self.__projs_root + '/' + project
|
||||
@lru_cache(maxsize = None)
|
||||
def get_value(self, project: str, section: str, key: str) -> str | None:
|
||||
ret: str | None
|
||||
proj_dir = self.__proj_dir(project, pretty = False)
|
||||
if proj_dir is None:
|
||||
raise Exception(f"Can't get project directory for {project}")
|
||||
if section == 'version':
|
||||
proj_version_dirs = [ proj_root ]
|
||||
if proj_root != self.__topdir:
|
||||
proj_version_dirs = [proj_dir]
|
||||
if proj_dir != self.___topdir:
|
||||
proj_version_dirs.append('/usr/share/doc/packages/' + project)
|
||||
for d in proj_version_dirs:
|
||||
version_path = d + '/VERSION'
|
||||
|
|
@ -423,13 +582,24 @@ class App(Base):
|
|||
log(DEBUG, f'Ignoring unreadable file "{version_path}"')
|
||||
continue
|
||||
raise Exception(f'No version file found for project "{project}"')
|
||||
path = proj_root + '/make/project.conf'
|
||||
path = proj_dir + '/make/project.conf'
|
||||
ret = self.read_value(path, section, key)
|
||||
log(DEBUG, 'Lookup %s -> %s / [%s%s] -> "%s"' %
|
||||
(self.__top_name, project, section, '.' + key if key else '', ret))
|
||||
log(
|
||||
DEBUG,
|
||||
'Lookup %s -> %s / [%s%s] -> "%s"' %
|
||||
(self.__top_name, project, section, '.' + key if key else '', ret),
|
||||
)
|
||||
return ret
|
||||
|
||||
def get_values(self, projects: list[str], sections: list[str], keys: list[str]) -> list[str]:
|
||||
@lru_cache(maxsize = None)
|
||||
def get_version(self, project) -> str:
|
||||
ret = self.get_value(project, 'version', '')
|
||||
if ret is None:
|
||||
raise Exception(f"Can't get version of project {project}")
|
||||
return ret
|
||||
|
||||
def get_values(self, projects: list[str], sections: list[str],
|
||||
keys: list[str]) -> list[str]:
|
||||
"""
|
||||
Collect a list of values from a list of given projects, sections and
|
||||
keys, maintaining order
|
||||
|
|
@ -441,39 +611,60 @@ class App(Base):
|
|||
vals = self.get_value(p, section, key)
|
||||
if vals:
|
||||
ret += [val.strip() for val in vals.split(',')]
|
||||
return list(dict.fromkeys(ret)) # Remove duplicates, keep ordering
|
||||
return list(dict.fromkeys(ret)) # Remove duplicates, keep ordering
|
||||
|
||||
def get_project_refs(self, projects: list[str], sections: list[str],
|
||||
keys: str|list[str], add_self: bool, scope: Scope, names_only=True) -> list[str]:
|
||||
def get_project_refs(
|
||||
self,
|
||||
projects: list[str],
|
||||
sections: list[str],
|
||||
keys: str | list[str],
|
||||
add_self: bool,
|
||||
scope: Scope,
|
||||
names_only = True,
|
||||
) -> list[str]:
|
||||
if isinstance(keys, str):
|
||||
keys = [ keys ]
|
||||
keys = [keys]
|
||||
ret: list[str] = []
|
||||
for section in sections:
|
||||
for key in keys:
|
||||
visited = set()
|
||||
visited: set[str] = set()
|
||||
for name in projects:
|
||||
rr: list[str] = []
|
||||
self.__get_project_refs_cached(rr, visited, name, section, key, add_self, scope, names_only)
|
||||
self.__get_project_refs_cached(
|
||||
rr, visited, name, section, key, add_self, scope, names_only
|
||||
)
|
||||
# TODO: this looks like a performance hogger
|
||||
for m in rr:
|
||||
if not m in ret:
|
||||
ret.append(m)
|
||||
if m not in ret:
|
||||
ret.append(m)
|
||||
return ret
|
||||
|
||||
def get_libname(self, projects) -> str:
|
||||
vals = self.get_project_refs(projects, ['build'], 'libname',
|
||||
scope = Scope.One, add_self=False, names_only=True)
|
||||
vals = self.get_project_refs(
|
||||
projects,
|
||||
['build'],
|
||||
'libname',
|
||||
scope = Scope.One,
|
||||
add_self = False,
|
||||
names_only = True,
|
||||
)
|
||||
if not vals:
|
||||
return ' '.join(projects)
|
||||
if 'none' in vals:
|
||||
vals.remove('none')
|
||||
return ' '.join(reversed(vals))
|
||||
|
||||
def is_excluded_from_build(self, project: str) -> str|None:
|
||||
def is_excluded_from_build(self, project: str) -> str | None:
|
||||
log(DEBUG, 'checking if project ' + project + ' is excluded from build')
|
||||
exclude = self.get_project_refs([ project ], ['build'], 'exclude',
|
||||
scope = Scope.One, add_self=False, names_only=True)
|
||||
cascade = self.distro.os_cascade + [ 'all' ]
|
||||
exclude = self.get_project_refs(
|
||||
[project],
|
||||
['build'],
|
||||
'exclude',
|
||||
scope = Scope.One,
|
||||
add_self = False,
|
||||
names_only = True,
|
||||
)
|
||||
cascade = self.distro.os_cascade + ['all']
|
||||
for p1 in exclude:
|
||||
for p2 in cascade:
|
||||
if p1 == p2:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ..App import App
|
||||
from ..lib.Cmd import Cmd as Base
|
||||
from ..CmdBase import CmdBase
|
||||
|
||||
class Cmd(Base): # export
|
||||
if TYPE_CHECKING:
|
||||
from ..App import App
|
||||
from ..lib.Distro import Distro
|
||||
|
||||
def __init__(self, parent: App|Base, name: str, help: str) -> None:
|
||||
class Cmd(CmdBase): # export
|
||||
|
||||
def __init__(self, parent: App | CmdBase, name: str, help: str) -> None:
|
||||
super().__init__(parent, name, help)
|
||||
|
||||
async def _run(self, args):
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from ..App import App
|
||||
from .Cmd import Cmd as CmdBase
|
||||
|
||||
class CmdPkg(CmdBase): # export
|
||||
class CmdPkg(CmdBase): # export
|
||||
|
||||
def __init__(self, parent: App) -> None:
|
||||
super().__init__(parent, 'pkg', help="System package manager wrapper")
|
||||
super().__init__(parent, 'pkg', help = 'System package manager wrapper')
|
||||
self.load_subcommands()
|
||||
|
||||
def add_arguments(self, p: ArgumentParser) -> None:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from ..App import App
|
||||
from .Cmd import Cmd as CmdBase
|
||||
|
||||
class CmdPlatform(CmdBase): # export
|
||||
class CmdPlatform(CmdBase): # export
|
||||
|
||||
def __init__(self, parent: App) -> None:
|
||||
super().__init__(parent, 'platform', help="Miscellaneous platform-related comamnds")
|
||||
super().__init__(
|
||||
parent, 'platform', help = 'Miscellaneous platform-related comamnds'
|
||||
)
|
||||
self.load_subcommands()
|
||||
|
||||
def add_arguments(self, p: ArgumentParser) -> None:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from ..App import App
|
||||
from .Cmd import Cmd as CmdBase
|
||||
|
||||
class CmdPosix(CmdBase): # export
|
||||
class CmdPosix(CmdBase): # export
|
||||
|
||||
def __init__(self, parent: App) -> None:
|
||||
super().__init__(parent, 'posix', help='Perform various operations on a distro through its POSIX utility interface')
|
||||
super().__init__(
|
||||
parent,
|
||||
'posix',
|
||||
help = (
|
||||
'Perform various operations on a distro through its '
|
||||
'POSIX utility interface'
|
||||
),
|
||||
)
|
||||
self.load_subcommands()
|
||||
|
||||
def add_arguments(self, p: ArgumentParser) -> None:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from ..App import App
|
||||
from .Cmd import Cmd as CmdBase
|
||||
|
||||
class CmdProjects(CmdBase): # export
|
||||
class CmdProjects(CmdBase): # export
|
||||
|
||||
def __init__(self, parent: App) -> None:
|
||||
super().__init__(parent, 'projects', help='Project metadata evaluation for building packages')
|
||||
super().__init__(
|
||||
parent,
|
||||
'projects',
|
||||
help = 'Project metadata evaluation for building packages'
|
||||
)
|
||||
self.load_subcommands()
|
||||
|
||||
def add_arguments(self, p: ArgumentParser) -> None:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from ..App import App
|
||||
from .Cmd import Cmd as CmdBase
|
||||
|
||||
class CmdSecrets(CmdBase): # export
|
||||
class CmdSecrets(CmdBase): # export
|
||||
|
||||
def __init__(self, parent: App) -> None:
|
||||
super().__init__(parent, 'secrets', help="Manage package secrets")
|
||||
super().__init__(parent, 'secrets', help = 'Manage package secrets')
|
||||
self.load_subcommands()
|
||||
|
||||
def add_arguments(self, p: ArgumentParser) -> None:
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ __all__ = detect_modules(
|
|||
package_name = __name__,
|
||||
package_path = __path__,
|
||||
namespace = globals(),
|
||||
prefix = "Cmd",
|
||||
skip = {"Cmd"},
|
||||
) # pyright: ignore[reportUnsupportedDunderAll]
|
||||
prefix = 'Cmd',
|
||||
skip = {'Cmd'},
|
||||
) # pyright: ignore[reportUnsupportedDunderAll]
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ...lib.Distro import Distro
|
||||
from ..CmdPkg import CmdPkg
|
||||
|
||||
from ..Cmd import Cmd as Base
|
||||
|
||||
class Cmd(Base): # export
|
||||
class Cmd(Base): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg, name: str, help: str) -> None:
|
||||
super().__init__(parent, name, help)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .Cmd import Cmd
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .Cmd import Cmd
|
||||
|
||||
class CmdDelete(Cmd): # export
|
||||
class CmdDelete(Cmd): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'delete', help="Delete packages by name")
|
||||
super().__init__(parent, 'delete', help = 'Delete packages by name')
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
parser.add_argument("names", nargs="*", help="Names of packages to be deleted")
|
||||
parser.add_argument(
|
||||
'names', nargs = '*', help = 'Names of packages to be deleted'
|
||||
)
|
||||
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
return await self.distro.delete(args.names)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .Cmd import Cmd
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .Cmd import Cmd
|
||||
|
||||
class CmdDup(Cmd): # export
|
||||
class CmdDup(Cmd): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'dup', help="Upgrade distribution")
|
||||
super().__init__(parent, 'dup', help = 'Upgrade distribution')
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
parser.add_argument('--download-only', default=False, action='store_true',
|
||||
help='Only download packages from the repos, don\'t install them, yet')
|
||||
parser.add_argument(
|
||||
'--download-only',
|
||||
default = False,
|
||||
action = 'store_true',
|
||||
help = "Only download packages from the repos, don't install them, yet",
|
||||
)
|
||||
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
return await self.distro.dup(download_only=args.download_only)
|
||||
return await self.distro.dup(download_only = args.download_only)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .Cmd import Cmd
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .Cmd import Cmd
|
||||
|
||||
class CmdInstall(Cmd): # export
|
||||
class CmdInstall(Cmd): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'install', help="Install the distribution's notion of available packages")
|
||||
super().__init__(
|
||||
parent,
|
||||
'install',
|
||||
help = "Install the distribution's notion of available packages",
|
||||
)
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
parser.add_argument("names", nargs="*", help="Packages to be installed")
|
||||
parser.add_argument('--only-update', default=False, action='store_true', help='Only update the listed packages, don\'t install them')
|
||||
parser.add_argument('-F', '--fixed-strings', action='store_true',
|
||||
help='Don\'t expand platform.expand_macros macros in <names>')
|
||||
parser.add_argument('names', nargs = '*', help = 'Packages to be installed')
|
||||
parser.add_argument(
|
||||
'--only-update',
|
||||
default = False,
|
||||
action = 'store_true',
|
||||
help = "Only update the listed packages, don't install them",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-F',
|
||||
'--fixed-strings',
|
||||
action = 'store_true',
|
||||
help = "Don't expand platform.expand_macros macros in <names>",
|
||||
)
|
||||
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
names = names if args.fixed_strings else self.app.distro.expand_macros(args.names)
|
||||
return await self.distro.install(names, only_update=args.only_update)
|
||||
names = (
|
||||
args.names if args.fixed_strings else self.distro.expand_macros(args.names)
|
||||
)
|
||||
return await self.distro.install(names, only_update = args.only_update)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .NamedPkgsCmd import NamedPkgsCmd as Base
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .NamedPkgsCmd import NamedPkgsCmd as Base
|
||||
|
||||
class CmdLs(Base): # export
|
||||
class CmdLs(Base): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'ls', help="List package contents")
|
||||
super().__init__(parent, 'ls', help = 'List package contents')
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
for name in args.names:
|
||||
print('\n'.join(await self.parent.distro.pkg_files(name)))
|
||||
print('\n'.join(await self.distro.pkg_files(name)))
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .NamedPkgsCmd import NamedPkgsCmd as Base
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .NamedPkgsCmd import NamedPkgsCmd as Base
|
||||
|
||||
class CmdMeta(Base): # export
|
||||
class CmdMeta(Base): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'meta', help="List package metadata")
|
||||
super().__init__(parent, 'meta', help = 'List package metadata')
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
packages = await self.distro.select(args.names)
|
||||
for package in packages:
|
||||
names = await self.distro.select(args.names)
|
||||
for name in names:
|
||||
if len(args.names) > 1:
|
||||
print(f'-- {name}')
|
||||
print(package)
|
||||
print(name)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .Cmd import Cmd
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .Cmd import Cmd
|
||||
|
||||
class CmdRebootRequired(Cmd): # export
|
||||
class CmdRebootRequired(Cmd): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'reboot-required', help="Check whether the machine needs rebooting")
|
||||
super().__init__(
|
||||
parent,
|
||||
'reboot-required',
|
||||
help = 'Check whether the machine needs rebooting'
|
||||
)
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
return await self.distro.reboot_required()
|
||||
await self.distro.reboot_required()
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .Cmd import Cmd
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .Cmd import Cmd
|
||||
|
||||
class CmdRefresh(Cmd): # export
|
||||
class CmdRefresh(Cmd): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'refresh', help="Refresh the distribution's notion of available packages")
|
||||
super().__init__(
|
||||
parent,
|
||||
'refresh',
|
||||
help = "Refresh the distribution's notion of available packages",
|
||||
)
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser, Namespace
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
import re
|
||||
|
||||
from ...lib.Package import Package
|
||||
from ...lib.PackageFilter import PackageFilterString
|
||||
from ..CmdPkg import CmdPkg
|
||||
from .Cmd import Cmd
|
||||
|
||||
class CmdSelect(Cmd): # export
|
||||
class CmdSelect(Cmd): # export
|
||||
|
||||
def __init__(self, parent: CmdPkg) -> None:
|
||||
super().__init__(parent, 'select', help="Select packages by filter")
|
||||
super().__init__(parent, 'select', help = 'Select packages by filter')
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
parser.add_argument("filter", help="Package filter string")
|
||||
parser.add_argument('filter', help = 'Package filter string')
|
||||
|
||||
async def _run(self, args: Namespace) -> None:
|
||||
filter = PackageFilterString(args.filter) if args.filter else None
|
||||
for p in await self.distro.select(filter=filter):
|
||||
for p in await self.distro.select(filter = filter):
|
||||
print(p.name)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from .Cmd import Cmd as Base
|
||||
from ..CmdPkg import CmdPkg as Parent
|
||||
from .Cmd import Cmd as Base
|
||||
|
||||
class NamedPkgsCmd(Base): # export
|
||||
class NamedPkgsCmd(Base): # export
|
||||
|
||||
def __init__(self, parent: Parent, name: str, help: str) -> None:
|
||||
super().__init__(parent, name, help)
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
super().add_arguments(parser)
|
||||
parser.add_argument('names', nargs='*', help='Package names')
|
||||
parser.add_argument('names', nargs = '*', help = 'Package names')
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue