[arch] improvement: missing dependencies dialog now displays the packages sizes and descriptions (only from repositories)

This commit is contained in:
Vinicius Moreira
2022-04-07 17:27:16 -03:00
parent 26678a0138
commit 9c9836bebf
4 changed files with 134 additions and 90 deletions

View File

@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Improvements ### Improvements
- Arch - Arch
- install: - install:
- missing dependencies dialog now displays the packages sizes and descriptions (only from repositories)
- optional packages installation dialog appearance (aligned with other dependencies dialogs) - optional packages installation dialog appearance (aligned with other dependencies dialogs)
- uninstall: - uninstall:
- displaying hard and unnecessary requirements versions and descriptions - displaying hard and unnecessary requirements versions and descriptions

View File

@@ -1,3 +1,4 @@
from io import StringIO
from typing import Set, Tuple, Dict, Collection from typing import Set, Tuple, Dict, Collection
from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.handler import ProcessWatcher
@@ -47,27 +48,64 @@ def request_optional_deps(pkgname: str, pkg_repos: dict, watcher: ProcessWatcher
return {o.value for o in view_opts.values} return {o.value for o in view_opts.values}
def request_install_missing_deps(deps: Collection[Tuple[str, str]], watcher: ProcessWatcher, i18n: I18n) -> bool: def confirm_missing_deps(deps: Collection[Tuple[str, str]], watcher: ProcessWatcher, i18n: I18n) -> bool:
msg = f"<p>{i18n['arch.missing_deps.body'].format(deps=bold(str(len(deps))))}:</p>"
opts = [] opts = []
repo_deps = [d[0] for d in deps if d[1] != 'aur'] total_isize, total_dsize = None, None
sizes = pacman.map_update_sizes(repo_deps) if repo_deps else {} pkgs_data = pacman.map_updates_data(pkgs=tuple(d[0] for d in deps if d[1] != 'aur'), description=True) or dict()
for dep in deps: for dep in deps:
size = sizes.get(dep[0]) ver, desc, isize, dsize = None, None, None, None
op = InputOption('{} ({}: {}) - {}: {}'.format(dep[0], data = pkgs_data.get(dep[0])
i18n['repository'],
dep[1].lower(), if data:
i18n['size'].capitalize(), desc, isize, dsize = (data.get(f) for f in ('des', 's', 'ds'))
get_human_size_str(size) if size is not None else '?'), dep[0])
if isize is not None:
if total_isize is None:
total_isize = 0
total_isize += isize
if dsize is not None:
if total_dsize is None:
total_dsize = 0
total_dsize += dsize
label = f"{dep[0]} ({i18n['repository']}: {dep[1].lower()}) | " \
f"{i18n['size'].capitalize()}: {get_human_size_str(isize) if isize is not None else '?'}" \
f"{' ({}: {})'.format(i18n['download'].capitalize(), get_human_size_str(dsize)) if dsize else ''}"
op = InputOption(label=label, value=dep[0], tooltip=desc)
op.read_only = True op.read_only = True
op.icon_path = _get_repo_icon(dep[1]) op.icon_path = _get_repo_icon(dep[1])
opts.append(op) opts.append(op)
comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts)) comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts))
return watcher.request_confirmation(i18n['arch.missing_deps.title'], msg, [comp], confirmation_label=i18n['continue'].capitalize(), deny_label=i18n['cancel'].capitalize(),
body = StringIO()
body.write('<p>')
body.write(i18n['arch.missing_deps.body'].format(deps=bold(str(len(deps)))))
if total_isize is not None or total_dsize is not None:
body.write(' (')
if total_isize is not None:
body.write(f"{i18n['size'].capitalize()}: {bold(get_human_size_str(total_isize))} | ")
if total_dsize is not None:
body.write(f"{i18n['download'].capitalize()}: {bold(get_human_size_str(total_dsize))}")
body.write(')')
body.write(':</p>')
return watcher.request_confirmation(title=i18n['arch.missing_deps.title'],
body=body.getvalue(),
components=[comp],
confirmation_label=i18n['continue'].capitalize(),
deny_label=i18n['cancel'].capitalize(),
min_width=600) min_width=600)

View File

@@ -41,6 +41,7 @@ from bauh.gems.arch import aur, pacman, message, confirmation, disk, git, \
ARCH_CONFIG_DIR, EDITABLE_PKGBUILDS_FILE, URL_GPG_SERVERS, rebuild_detector, makepkg, sshell, get_repo_icon_path ARCH_CONFIG_DIR, EDITABLE_PKGBUILDS_FILE, URL_GPG_SERVERS, rebuild_detector, makepkg, sshell, get_repo_icon_path
from bauh.gems.arch.aur import AURClient from bauh.gems.arch.aur import AURClient
from bauh.gems.arch.config import get_build_dir, ArchConfigManager from bauh.gems.arch.config import get_build_dir, ArchConfigManager
from bauh.gems.arch.confirmation import confirm_missing_deps
from bauh.gems.arch.dependencies import DependenciesAnalyser from bauh.gems.arch.dependencies import DependenciesAnalyser
from bauh.gems.arch.download import MultithreadedDownloadService, ArchDownloadException from bauh.gems.arch.download import MultithreadedDownloadService, ArchDownloadException
from bauh.gems.arch.exceptions import PackageNotFoundException, PackageInHoldException from bauh.gems.arch.exceptions import PackageNotFoundException, PackageInHoldException
@@ -2114,7 +2115,7 @@ class ArchManager(SoftwareManager):
def _ask_and_install_missing_deps(self, context: TransactionContext, missing_deps: List[Tuple[str, str]]) -> bool: def _ask_and_install_missing_deps(self, context: TransactionContext, missing_deps: List[Tuple[str, str]]) -> bool:
context.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(context.name))) context.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(context.name)))
if not confirmation.request_install_missing_deps(missing_deps, context.watcher, self.i18n): if not confirm_missing_deps(missing_deps, context.watcher, self.i18n):
context.watcher.print(self.i18n['action.cancelled']) context.watcher.print(self.i18n['action.cancelled'])
return False return False
@@ -2304,8 +2305,7 @@ class ArchManager(SoftwareManager):
sorted_deps = sorting.sort(to_sort, {**deps_data, **subdeps_data}, provided_map) sorted_deps = sorting.sort(to_sort, {**deps_data, **subdeps_data}, provided_map)
if display_deps_dialog and not confirmation.request_install_missing_deps(sorted_deps, context.watcher, if display_deps_dialog and not confirm_missing_deps(sorted_deps, context.watcher, self.i18n):
self.i18n):
context.watcher.print(self.i18n['action.cancelled']) context.watcher.print(self.i18n['action.cancelled'])
return True # because the main package installation was successful return True # because the main package installation was successful

View File

@@ -599,40 +599,87 @@ def list_download_data(pkgs: Iterable[str]) -> List[Dict[str, str]]:
return res return res
def map_updates_data(pkgs: Iterable[str], files: bool = False) -> dict: def map_updates_data(pkgs: Iterable[str], files: bool = False, description: bool = False) -> Optional[Dict[str, Dict[str, object]]]:
if files: if pkgs:
output = run_cmd('pacman -Qi -p {}'.format(' '.join(pkgs))) if files:
else: output = run_cmd('pacman -Qi -p {}'.format(' '.join(pkgs)))
output = run_cmd('pacman -Si {}'.format(' '.join(pkgs))) else:
output = run_cmd('pacman -Si {}'.format(' '.join(pkgs)))
res = {} res = {}
if output: if output:
latest_name = None latest_name = None
data = {'ds': None, 's': None, 'v': None, 'c': None, 'p': None, 'd': None, 'r': None} data = {'ds': None, 's': None, 'v': None, 'c': None, 'p': None, 'd': None, 'r': None, 'des': None}
latest_field = None latest_field = None
for l in output.split('\n'): for l in output.split('\n'):
if l: if l:
if l[0] != ' ': if l[0] != ' ':
line = l.strip() line = l.strip()
field_sep_idx = line.index(':') field_sep_idx = line.index(':')
field = line[0:field_sep_idx].strip() field = line[0:field_sep_idx].strip()
val = line[field_sep_idx + 1:].strip() val = line[field_sep_idx + 1:].strip()
if field == 'Repository': if field == 'Repository':
data['r'] = val data['r'] = val
latest_field = 'r' latest_field = 'r'
elif field == 'Name': elif field == 'Name':
latest_name = val latest_name = val
latest_field = 'n' latest_field = 'n'
elif field == 'Version': elif field == 'Version':
data['v'] = val.split('=')[0] data['v'] = val.split('=')[0]
latest_field = 'v' latest_field = 'v'
elif field == 'Provides': elif description and field == 'Description':
latest_field = 'p' data['des'] = val
data['p'] = {latest_name, '{}={}'.format(latest_name, data['v'])} latest_field = 'des'
if val != 'None': elif field == 'Provides':
for w in val.split(' '): latest_field = 'p'
data['p'] = {latest_name, '{}={}'.format(latest_name, data['v'])}
if val != 'None':
for w in val.split(' '):
if w:
word = w.strip()
data['p'].add(word)
word_split = word.split('=')
if word_split[0] != word:
data['p'].add(word_split[0])
elif field == 'Depends On':
val = val.strip()
if val == 'None':
data['d'] = None
else:
data['d'] = {w.strip() for w in val.split(' ') if w}
latest_field = 'd'
elif field == 'Conflicts With':
if val == 'None':
data['c'] = None
else:
data['c'] = {w.strip() for w in val.split(' ') if w}
latest_field = 'c'
elif field == 'Download Size':
size = val.split(' ')
data['ds'] = size_to_byte(size[0], size[1])
latest_field = 'ds'
elif field == 'Installed Size':
size = val.split(' ')
data['s'] = size_to_byte(size[0], size[1])
latest_field = 's'
elif latest_name and latest_field == 's':
res[latest_name] = data
latest_name = None
latest_field = None
data = {'ds': None, 's': None, 'c': None, 'p': None, 'd': None,
'r': None, 'v': None, 'des': None}
else:
latest_field = None
elif latest_field and latest_field in ('p', 'c', 'd'):
if latest_field == 'p':
for w in l.split(' '):
if w: if w:
word = w.strip() word = w.strip()
data['p'].add(word) data['p'].add(word)
@@ -641,52 +688,10 @@ def map_updates_data(pkgs: Iterable[str], files: bool = False) -> dict:
if word_split[0] != word: if word_split[0] != word:
data['p'].add(word_split[0]) data['p'].add(word_split[0])
elif field == 'Depends On':
val = val.strip()
if val == 'None':
data['d'] = None
else: else:
data['d'] = {w.strip() for w in val.split(' ') if w} data[latest_field].update((w.strip() for w in l.split(' ') if w))
latest_field = 'd'
elif field == 'Conflicts With':
if val == 'None':
data['c'] = None
else:
data['c'] = {w.strip() for w in val.split(' ') if w}
latest_field = 'c' return res
elif field == 'Download Size':
size = val.split(' ')
data['ds'] = size_to_byte(size[0], size[1])
latest_field = 'ds'
elif field == 'Installed Size':
size = val.split(' ')
data['s'] = size_to_byte(size[0], size[1])
latest_field = 's'
elif latest_name and latest_field == 's':
res[latest_name] = data
latest_name = None
latest_field = None
data = {'ds': None, 's': None, 'c': None, 'p': None, 'd': None, 'r': None, 'v': None}
else:
latest_field = None
elif latest_field and latest_field in ('p', 'c', 'd'):
if latest_field == 'p':
for w in l.split(' '):
if w:
word = w.strip()
data['p'].add(word)
word_split = word.split('=')
if word_split[0] != word:
data['p'].add(word_split[0])
else:
data[latest_field].update((w.strip() for w in l.split(' ') if w))
return res
def upgrade_several(pkgnames: Iterable[str], root_password: Optional[str], overwrite_conflicting_files: bool = False, skip_dependency_checks: bool = False) -> SimpleProcess: def upgrade_several(pkgnames: Iterable[str], root_password: Optional[str], overwrite_conflicting_files: bool = False, skip_dependency_checks: bool = False) -> SimpleProcess: