This commit is contained in:
Vinícius Moreira
2019-12-09 14:13:27 -03:00
committed by GitHub
33 changed files with 544 additions and 106 deletions

View File

@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.7.4] 2019-12-09
### Improvements
- AUR
- retrieving and displaying all transitive required dependencies ( it can be disabled via the new environment variable **BAUH_ARCH_CHECK_SUBDEPS=0** )
- displaying **makedepends** and **checkdepends** in the info window
- Some AUR labels have been changed to not confuse the user
- **--clean** param renamed to **--reset**
- Minor UI improvements
### Fixes
- AUR
- not finding some dependencies declared as files instead of the package names (e.g: dolphin-emu-git )
- replaces the term **mirror** by **repository**
## [0.7.3] 2019-11-29
### Improvements
- Not breaking the application when a i18n (translation) key was not found

View File

@@ -88,7 +88,10 @@ Icon=/home/$USER/bauh_env/lib/python3.7/site-packages/bauh/view/resources/img/lo
In order to autostart the application, use your Desktop Environment settings to register it as a startup application / script (**bauh --tray=1**).
### Uninstallation
Before uninstalling bauh via your package manager, consider executing `bauh --clean` to remove configuration and cache files stored in your **HOME** folder.
Before uninstalling bauh via your package manager, consider executing `bauh --reset` to remove configuration and cache files stored in your **HOME** folder.
### Theme issues
If bauh is not starting properly after changing its style, execute `bauh --reset` to reset its configuration or just delete the **style** key from the file **~/.config/bauh/config.json**.
### Gems ( package technology support )
#### Flatpak ( flatpak )
@@ -123,10 +126,10 @@ will be pre-downloaded faster ( it does **NOT** modify your **pacman** settings
b) same as previous, but related to **COMPRESSXZ** definition ( if '--threads=0' is not defined )
Obs: this feature can be disabled through the environment variable **BAUH_ARCH_OPTIMIZE=0**
( For more information about these optimizations, have a look at [Makepkg](https://wiki.archlinux.org/index.php/Makepkg)
( For more information about these optimizations, have a look at [Makepkg](https://wiki.archlinux.org/index.php/Makepkg) )
- Arch package memory-indexer running every 20 minutes. This memory index is used when AUR Api cannot handle the amount of results found for a given search. It can be disabled via the environment variable **BAUH_ARCH_AUR_INDEX_UPDATER=0**.
- If some of your installed packages are not categorized, send an e-mail to **bauh4linux@gmail.com** informing their names and categories in the following format: ```name=category1[,category2,category3,...]```
- Transitive dependencies checking can be disabled through the environment variable **BAUH_ARCH_CHECK_SUBDEPS=0**. The dependency checking process will be faster, but the application will ask for a confirmation every time a not installed dependency is detected.
### General settings
You can change some application settings via environment variables or arguments (type ```bauh --help``` to get more information).

View File

@@ -1,4 +1,4 @@
__version__ = '0.7.3'
__version__ = '0.7.4'
__app_name__ = 'bauh'
import os

View File

@@ -29,7 +29,7 @@ def main():
logger = logs.new_logger(__app_name__, bool(args.logs))
app_args.validate(args, logger)
if args.clean:
if args.reset:
util.clean_app_files()
exit(0)

View File

@@ -36,7 +36,7 @@ def read() -> Namespace:
parser.add_argument('--logs', action="store", default=int(os.getenv('BAUH_LOGS', 0)), choices=[0, 1], type=int, help='If the application logs should be displayed. Default: %(default)s')
parser.add_argument('--show-panel', action="store_true", help='Shows the management panel after the app icon is attached to the tray.')
parser.add_argument('-dmt', '--download-mthread', action="store", default=os.getenv('BAUH_DOWNLOAD_MULTITHREAD', 1), choices=[0, 1], type=int, help='If installation files should be downloaded using multi-threads (only possible if aria2c is installed). Not all gems support this feature. Check README.md. Default: %(default)s')
parser.add_argument('--clean', action="store_true", help='Removes all configuration and cache files')
parser.add_argument('--reset', action="store_true", help='Removes all configuration and cache files')
return parser.parse_args()

View File

@@ -4,6 +4,9 @@ from typing import Set, List
from bauh.api.http import HttpClient
import urllib.parse
from bauh.gems.arch import pacman
from bauh.gems.arch.exceptions import PackageNotFoundException
URL_INFO = 'https://aur.archlinux.org/rpc/?v=5&type=info&'
URL_SRC_INFO = 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h='
URL_SEARCH = 'https://aur.archlinux.org/rpc/?v=5&type=search&arg='
@@ -46,5 +49,18 @@ class AURClient:
return info
def get_all_dependencies(self, name: str) -> Set[str]:
deps = set()
info = self.get_src_info(name)
if not info:
raise PackageNotFoundException(name)
for attr in ('makedepends', 'depends', 'checkdepends'):
if info.get(attr):
deps.update([pacman.RE_DEP_OPERATORS.split(dep)[0] for dep in info[attr]])
return deps
def _map_names_as_queries(self, names) -> str:
return '&'.join(['arg[{}]={}'.format(i, urllib.parse.quote(n)) for i, n in enumerate(names)])

View File

@@ -1,4 +1,4 @@
from typing import Set
from typing import Set, List, Tuple
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.view import MultipleSelectComponent, InputOption
@@ -16,7 +16,7 @@ def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatch
opts = []
for p, d in pkg_mirrors.items():
op = InputOption('{}{} ( {}: {} )'.format(p, ': ' + d['desc'] if d['desc'] else '', i18n['mirror'], d['mirror'].upper()), p)
op = InputOption('{}{} ( {}: {} )'.format(p, ': ' + d['desc'] if d['desc'] else '', i18n['repository'], d['mirror'].upper()), p)
op.icon_path = _get_mirror_icon(d['mirror'])
opts.append(op)
@@ -25,25 +25,29 @@ def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatch
default_options=None)
install = watcher.request_confirmation(title=i18n['arch.install.optdeps.request.title'],
body='<p>{}</p>'.format(i18n['arch.install.optdeps.request.body'].format(bold(pkgname)) + ':'),
body='<p>{}.</p><p>{}:</p>'.format(i18n['arch.install.optdeps.request.body'].format(bold(pkgname)), i18n['arch.install.optdeps.request.help']),
components=[view_opts],
confirmation_label=i18n['install'],
deny_label=i18n['cancel'])
confirmation_label=i18n['install'].capitalize(),
deny_label=i18n['do_not.install'].capitalize())
if install:
return {o.value for o in view_opts.values}
def request_install_missing_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: I18n) -> bool:
msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format(bold(pkgname)) + ':')
def request_install_missing_deps(pkgname: str, deps: List[Tuple[str, str]], watcher: ProcessWatcher, i18n: I18n) -> bool:
msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format(name=bold(pkgname) if pkgname else '', deps=bold(str(len(deps)))))
opts = []
for p, m in pkg_mirrors.items():
op = InputOption('{} ( {}: {} )'.format(p, i18n['mirror'], m.upper()), p)
sorted_deps = [*deps]
sorted_deps.sort(key=lambda e: e[0])
for dep in sorted_deps:
op = InputOption('{} ( {}: {} )'.format(dep[0], i18n['repository'], dep[1].upper()), dep[0])
op.read_only = True
op.icon_path = _get_mirror_icon(m)
op.icon_path = _get_mirror_icon(dep[1])
opts.append(op)
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())
return watcher.request_confirmation(i18n['arch.missing_deps.title'], msg, [comp], confirmation_label=i18n['continue'].capitalize(), deny_label=i18n['cancel'].capitalize())

View File

@@ -6,7 +6,7 @@ import subprocess
import time
from pathlib import Path
from threading import Thread
from typing import List, Set, Type
from typing import List, Set, Type, Tuple, Dict
import requests
@@ -22,6 +22,7 @@ from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, r
from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, pkgbuild, message, confirmation, disk, git, suggestions, \
gpg, URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH
from bauh.gems.arch.aur import AURClient
from bauh.gems.arch.depedencies import DependenciesAnalyser
from bauh.gems.arch.mapper import ArchDataMapper
from bauh.gems.arch.model import ArchPackage
from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater, ArchCompilationOptimizer
@@ -62,6 +63,7 @@ class ArchManager(SoftwareManager):
self.categories_mapper = CategoriesDownloader('AUR', context.http_client, context.logger, self, self.context.disk_cache,
URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH)
self.categories = {}
self.deps_analyser = DependenciesAnalyser(self.aur_client)
def _upgrade_search_result(self, apidata: dict, installed_pkgs: dict, downgrade_enabled: bool, res: SearchResult, disk_loader: DiskCacheLoader):
app = self.mapper.map_api_data(apidata, installed_pkgs['not_signed'], self.categories)
@@ -219,7 +221,7 @@ class ArchManager(SoftwareManager):
break
watcher.change_substatus(self.i18n['arch.downgrade.install_older'])
return self._make_pkg(pkg.name, pkg.maintainer, root_password, handler, app_build_dir, clone_path, dependency=False, skip_optdeps=True)
return self._build(pkg.name, pkg.maintainer, root_password, handler, app_build_dir, clone_path, dependency=False, skip_optdeps=True)
else:
watcher.show_message(title=self.i18n['arch.downgrade.error'],
body=self.i18n['arch.downgrade.impossible'].format(pkg.name),
@@ -308,11 +310,17 @@ class ArchManager(SoftwareManager):
srcinfo = self.aur_client.get_src_info(pkg.name)
if srcinfo:
if srcinfo.get('makedepends'):
info['12_makedepends'] = srcinfo['makedepends']
if srcinfo.get('depends'):
info['11_dependson'] = srcinfo['depends']
info['13_dependson'] = srcinfo['depends']
if srcinfo.get('optdepends'):
info['12_optdepends'] = srcinfo['optdepends']
info['14_optdepends'] = srcinfo['optdepends']
if srcinfo.get('checkdepends'):
info['15_checkdepends'] = srcinfo['checkdepends']
if pkg.pkgbuild:
info['00_pkg_build'] = pkg.pkgbuild
@@ -355,10 +363,9 @@ class ArchManager(SoftwareManager):
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def _install_deps(self, deps: Set[str], pkg_mirrors: dict, root_password: str, handler: ProcessHandler, change_progress: bool = False) -> str:
def _install_deps(self, deps: List[Tuple[str, str]], root_password: str, handler: ProcessHandler, change_progress: bool = False) -> str:
"""
:param deps:
:param pkg_mirrors:
:param pkgs_repos:
:param root_password:
:param handler:
:return: not installed dependency
@@ -367,27 +374,25 @@ class ArchManager(SoftwareManager):
progress = 0
self._update_progress(handler.watcher, 1, change_progress)
for pkgname in deps:
mirror = pkg_mirrors[pkgname]
handler.watcher.change_substatus(self.i18n['arch.install.dependency.install'].format(bold('{} ()'.format(pkgname, mirror))))
if mirror == 'aur':
installed = self._install_from_aur(pkgname=pkgname, maintainer=None, root_password=root_password, handler=handler, dependency=True, change_progress=False)
for dep in deps:
handler.watcher.change_substatus(self.i18n['arch.install.dependency.install'].format(bold('{} ()'.format(dep[0], dep[1]))))
if dep[1] == 'aur':
installed = self._install_from_aur(pkgname=dep[0], maintainer=None, root_password=root_password, handler=handler, dependency=True, change_progress=False)
else:
installed = self._install(pkgname=pkgname, maintainer=None, root_password=root_password, handler=handler, install_file=None, mirror=mirror, change_progress=False)
installed = self._install(pkgname=dep[0], maintainer=None, root_password=root_password, handler=handler, install_file=None, mirror=dep[1], change_progress=False)
if not installed:
return pkgname
return dep[0]
progress += progress_increment
self._update_progress(handler.watcher, progress, change_progress)
self._update_progress(handler.watcher, 100, change_progress)
def _map_mirrors(self, pkgnames: Set[str]) -> dict:
pkg_mirrors = pacman.get_mirrors(pkgnames) # getting mirrors set
def _map_repos(self, pkgnames: Set[str]) -> dict:
pkg_mirrors = pacman.get_repositories(pkgnames) # getting mirrors set
if len(pkgnames) != pkg_mirrors: # checking if any dep not found in the distro mirrors are from AUR
if len(pkgnames) != len(pkg_mirrors): # checking if any dep not found in the distro mirrors are from AUR
nomirrors = {p for p in pkgnames if p not in pkg_mirrors}
for pkginfo in self.aur_client.get_info(nomirrors):
if pkginfo.get('Name') in nomirrors:
@@ -426,12 +431,16 @@ class ArchManager(SoftwareManager):
return True
def _make_pkg(self, pkgname: str, maintainer: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
def _should_check_subdeps(self):
return bool(int(os.getenv('BAUH_ARCH_CHECK_SUBDEPS', 1)))
def _build(self, pkgname: str, maintainer: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
self._pre_download_source(pkgname, project_dir, handler.watcher)
self._update_progress(handler.watcher, 50, change_progress)
if not self._install_missings_deps_and_keys(pkgname, root_password, handler, project_dir):
if not self._handle_deps_and_keys(pkgname, root_password, handler, project_dir, check_subdeps=self._should_check_subdeps()):
return False
# building main package
@@ -456,39 +465,83 @@ class ArchManager(SoftwareManager):
handler.watcher.change_substatus(self.i18n['arch.optdeps.checking'].format(bold(pkgname)))
if self._install_optdeps(pkgname, root_password, handler, project_dir, change_progress=change_progress):
if self._install_optdeps(pkgname, root_password, handler, project_dir):
return True
return False
def _install_missings_deps_and_keys(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str) -> bool:
def _map_known_missing_deps(self, known_deps: Dict[str, str], watcher: ProcessWatcher, check_subdeps: bool = True) -> List[Tuple[str, str]]:
sorted_deps = [] # it will hold the proper order to install the missing dependencies
repo_deps, aur_deps = set(), set()
for dep, repo in known_deps.items():
if repo == 'aur':
aur_deps.add(dep)
else:
repo_deps.add(dep)
if check_subdeps:
for deps in ((repo_deps, 'repo'), (aur_deps, 'aur')):
if deps[0]:
missing_subdeps = self.deps_analyser.get_missing_subdeps_of(deps[0], deps[1])
if missing_subdeps:
for dep in missing_subdeps:
if not dep[1]:
message.show_dep_not_found(dep[0], self.i18n, watcher)
return
for dep in missing_subdeps:
sorted_deps.append(dep)
for dep, repo in known_deps.items():
if repo != 'aur':
sorted_deps.append((dep, repo))
for dep in aur_deps:
sorted_deps.append((dep, 'aur'))
return sorted_deps
def _map_unknown_missing_deps(self, deps: List[str], watcher: ProcessWatcher, check_subdeps: bool = True) -> List[Tuple[str, str]]:
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in deps}
dep_repos = self._map_repos(depnames)
if len(depnames) != len(dep_repos): # checking if a dependency could not be found in any mirror
for dep in depnames:
if dep not in dep_repos:
message.show_dep_not_found(dep, self.i18n, watcher)
return
return self._map_known_missing_deps(dep_repos, watcher, check_subdeps)
def _handle_deps_and_keys(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str, check_subdeps: bool = True) -> bool:
handler.watcher.change_substatus(self.i18n['arch.checking.deps'].format(bold(pkgname)))
check_res = makepkg.check(pkgdir, handler)
if check_res:
if check_res.get('missing_deps'):
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in check_res['missing_deps']}
dep_mirrors = self._map_mirrors(depnames)
handler.watcher.change_substatus(self.i18n['arch.checking.missing_deps'].format(bold(pkgname)))
sorted_deps = self._map_unknown_missing_deps(check_res['missing_deps'], handler.watcher, check_subdeps=check_subdeps)
for dep in depnames: # cheking if a dependency could not be found in any mirror
if dep not in dep_mirrors:
message.show_dep_not_found(dep, self.i18n, handler.watcher)
return False
if sorted_deps is None:
return False
handler.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(pkgname)))
if not confirmation.request_install_missing_deps(pkgname, dep_mirrors, handler.watcher, self.i18n):
if not confirmation.request_install_missing_deps(pkgname, sorted_deps, handler.watcher, self.i18n):
handler.watcher.print(self.i18n['action.cancelled'])
return False
dep_not_installed = self._install_deps(depnames, dep_mirrors, root_password, handler, change_progress=False)
dep_not_installed = self._install_deps(sorted_deps, root_password, handler, change_progress=False)
if dep_not_installed:
message.show_dep_not_installed(handler.watcher, pkgname, dep_not_installed, self.i18n)
return False
# it is necessary to re-check because missing PGP keys are only notified when there are none missing
return self._install_missings_deps_and_keys(pkgname, root_password, handler, pkgdir)
return self._handle_deps_and_keys(pkgname, root_password, handler, pkgdir, check_subdeps=False)
if check_res.get('gpg_key'):
if handler.watcher.request_confirmation(title=self.i18n['arch.aur.install.unknown_key.title'],
@@ -510,7 +563,7 @@ class ArchManager(SoftwareManager):
return True
def _install_optdeps(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str, change_progress: bool = True) -> bool:
def _install_optdeps(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str) -> bool:
with open('{}/.SRCINFO'.format(pkgdir)) as f:
odeps = pkgbuild.read_optdeps_as_dict(f.read())
@@ -522,17 +575,47 @@ class ArchManager(SoftwareManager):
if not to_install:
return True
pkg_mirrors = self._map_mirrors(to_install)
pkg_mirrors = self._map_repos(to_install)
if pkg_mirrors:
final_optdeps = {dep: {'desc': odeps.get(dep), 'mirror': pkg_mirrors.get(dep)} for dep in to_install if dep in pkg_mirrors}
final_optdeps = {dep: {'desc': odeps.get(dep), 'mirror': pkg_mirrors.get(dep)} for dep, mirror in pkg_mirrors.items()}
deps_to_install = confirmation.request_optional_deps(pkgname, final_optdeps, handler.watcher, self.i18n)
if not deps_to_install:
return True
else:
dep_not_installed = self._install_deps(deps_to_install, pkg_mirrors, root_password, handler, change_progress=True)
sorted_deps = []
if self._should_check_subdeps():
missing_deps = self._map_known_missing_deps({d: pkg_mirrors[d] for d in deps_to_install}, handler.watcher)
if missing_deps is None:
return True # because the main package installation was successful
if missing_deps:
same_as_selected = len(deps_to_install) == len(missing_deps) and deps_to_install == {d[0] for d in missing_deps}
if not same_as_selected and not confirmation.request_install_missing_deps(None, missing_deps, handler.watcher, self.i18n):
handler.watcher.print(self.i18n['action.cancelled'])
return True # because the main package installation was successful
sorted_deps.extend(missing_deps)
else:
aur_deps, repo_deps = [], []
for dep in deps_to_install:
mirror = pkg_mirrors[dep]
if mirror == 'aur':
aur_deps.append((dep, mirror))
else:
repo_deps.append((dep, mirror))
sorted_deps.extend(repo_deps)
sorted_deps.extend(aur_deps)
dep_not_installed = self._install_deps(sorted_deps, root_password, handler, change_progress=True)
if dep_not_installed:
message.show_optdep_not_installed(dep_not_installed, handler.watcher, self.i18n)
@@ -644,15 +727,15 @@ class ArchManager(SoftwareManager):
if uncompress:
uncompress_dir = '{}/{}'.format(app_build_dir, pkgname)
return self._make_pkg(pkgname=pkgname,
maintainer=maintainer,
root_password=root_password,
handler=handler,
build_dir=app_build_dir,
project_dir=uncompress_dir,
dependency=dependency,
skip_optdeps=skip_optdeps,
change_progress=change_progress)
return self._build(pkgname=pkgname,
maintainer=maintainer,
root_password=root_password,
handler=handler,
build_dir=app_build_dir,
project_dir=uncompress_dir,
dependency=dependency,
skip_optdeps=skip_optdeps,
change_progress=change_progress)
finally:
if os.path.exists(app_build_dir):
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', app_build_dir])))

View File

@@ -0,0 +1,108 @@
from threading import Thread
from typing import Set, List, Tuple
from bauh.gems.arch import pacman
from bauh.gems.arch.aur import AURClient
class DependenciesAnalyser:
def __init__(self, aur_client: AURClient):
self.aur_client = aur_client
def _fill_mirror(self, name: str, output: List[Tuple[str, str]]):
mirror = pacman.read_repository_from_info(name)
if mirror:
output.append((name, mirror))
return
guess = pacman.guess_repository(name)
if guess:
output.append(guess)
return
aur_info = self.aur_client.get_src_info(name)
if aur_info:
output.append((name, 'aur'))
return
output.append((name, ''))
def get_missing_packages(self, names: Set[str], mirror: str = None, in_analysis: Set[str] = None) -> List[Tuple[str, str]]:
"""
:param names:
:param mirror:
:param in_analysis: global set storing all names in analysis to avoid repeated recursion
:return:
"""
global_in_analysis = in_analysis if in_analysis else set()
missing_names = pacman.check_missing({n for n in names if n not in in_analysis})
if missing_names:
missing_root = []
threads = []
if not mirror:
for name in missing_names:
t = Thread(target=self._fill_mirror, args=(name, missing_root))
t.start()
threads.append(t)
for t in threads:
t.join()
threads.clear()
# checking if there is any unknown dependency:
for rdep in missing_root:
if not rdep[1]:
return missing_root
else:
global_in_analysis.add(rdep[0])
else:
for missing in missing_names:
missing_root.append((missing, mirror))
global_in_analysis.add(missing)
missing_sub = []
for rdep in missing_root:
subdeps = self.aur_client.get_all_dependencies(rdep[0]) if rdep[1] == 'aur' else pacman.read_dependencies(rdep[0])
subdeps_not_analysis = {sd for sd in subdeps if sd not in global_in_analysis}
if subdeps_not_analysis:
missing_subdeps = self.get_missing_packages(subdeps_not_analysis, in_analysis=global_in_analysis)
# checking if there is any unknown:
if missing_subdeps:
for subdep in missing_subdeps:
if not subdep[0]:
return [*missing_subdeps, *missing_root]
if subdep[0] not in missing_names:
missing_sub.append(subdep)
return [*missing_sub, *missing_root]
def get_missing_subdeps_of(self, names: Set[str], mirror: str) -> List[Tuple[str, str]]:
missing = []
already_added = {*names}
in_analyses = {*names}
for name in names:
subdeps = self.aur_client.get_all_dependencies(name) if mirror == 'aur' else pacman.read_dependencies(name)
if subdeps:
missing_subdeps = self.get_missing_packages(subdeps, in_analysis=in_analyses)
if missing_subdeps:
for subdep in missing_subdeps: # checking if there is any unknown:
if subdep[0] not in already_added:
missing.append(subdep)
if not subdep[0]:
return missing
return missing

View File

@@ -0,0 +1,5 @@
class PackageNotFoundException(Exception):
def __init__(self, name: str):
self.name = name

View File

@@ -1,11 +1,14 @@
import re
from threading import Thread
from typing import List, Set
from typing import List, Set, Tuple
from bauh.commons.system import run_cmd, new_subprocess, new_root_subprocess, SystemProcess
from bauh.gems.arch.exceptions import PackageNotFoundException
RE_DEPS = re.compile(r'[\w\-_]+:[\s\w_\-\.]+\s+\[\w+\]')
RE_OPTDEPS = re.compile(r'[\w\._\-]+\s*:')
RE_DEP_NOTFOUND = re.compile(r'error:.+\'(.+)\'')
RE_DEP_OPERATORS = re.compile(r'[<>=]')
def is_enabled() -> bool:
@@ -13,7 +16,7 @@ def is_enabled() -> bool:
return res and not res.strip().startswith('which ')
def get_mirrors(pkgs: Set[str]) -> dict:
def get_repositories(pkgs: Set[str]) -> dict:
pkgre = '|'.join(pkgs).replace('+', r'\+').replace('.', r'\.')
searchres = new_subprocess(['pacman', '-Ss', pkgre]).stdout
@@ -26,10 +29,19 @@ def get_mirrors(pkgs: Set[str]) -> dict:
if p in match:
mirrors[p] = match.split('/')[0]
not_found = {pkg for pkg in pkgs if pkg and pkg not in mirrors}
if not_found: # if there are some packages not found, try to find via the single method:
for dep in not_found:
mirror_data = guess_repository(dep)
if mirror_data:
mirrors[mirror_data[0]] = mirror_data[1]
return mirrors
def is_available_from_mirrors(pkg_name: str) -> bool:
def is_available_in_repositories(pkg_name: str) -> bool:
return bool(run_cmd('pacman -Ss ' + pkg_name))
@@ -216,3 +228,129 @@ def list_ignored_packages(config_path: str = '/etc/pacman.conf') -> Set[str]:
pacman_conf.terminate()
grep.terminate()
return ignored
def check_missing(names: Set[str]) -> Set[str]:
installed = new_subprocess(['pacman', '-Qq', *names])
not_installed = set()
for o in installed.stderr:
if o:
err_line = o.decode()
if err_line:
not_found = [n for n in RE_DEP_NOTFOUND.findall(err_line) if n]
if not_found:
not_installed.update(not_found)
return not_installed
def read_repository_from_info(name: str) -> str:
info = new_subprocess(['pacman', '-Si', name])
not_found = False
for o in info.stderr:
if o:
err_line = o.decode()
if RE_DEP_NOTFOUND.findall(err_line):
not_found = True
if not_found:
return
mirror = None
for o in new_subprocess(['grep', '-Po', "Repository\s+:\s+\K.+"], stdin=info.stdout).stdout:
if o:
line = o.decode().strip()
if line:
mirror = line
return mirror
def guess_repository(name: str) -> Tuple[str, str]:
if not name:
raise Exception("'name' cannot be None or blank")
only_name = RE_DEP_OPERATORS.split(name)[0]
res = run_cmd('pacman -Ss {}'.format(only_name))
if res:
lines = res.split('\n')
if lines:
for line in lines:
if line and not line.startswith(' '):
data = line.split('/')
line_name, line_repo = data[1].split(' ')[0], data[0]
provided = read_provides(line_name)
if provided:
found = {p for p in provided if only_name == RE_DEP_OPERATORS.split(p)[0]}
if found:
return line_name, line_repo
def read_provides(name: str) -> Set[str]:
dep_info = new_subprocess(['pacman', '-Si', name])
not_found = False
for o in dep_info.stderr:
if o:
err_line = o.decode()
if err_line:
if RE_DEP_NOTFOUND.findall(err_line):
not_found = True
if not_found:
raise PackageNotFoundException(name)
provides = None
for out in new_subprocess(['grep', '-Po', 'Provides\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
if out:
provided_names = [p.strip() for p in out.decode().strip().split(' ') if p]
if provided_names[0].lower() == 'none':
provides = {name}
else:
provides = set(provided_names)
return provides
def read_dependencies(name: str) -> Set[str]:
dep_info = new_subprocess(['pacman', '-Si', name])
not_found = False
for o in dep_info.stderr:
if o:
err_line = o.decode()
if err_line:
if RE_DEP_NOTFOUND.findall(err_line):
not_found = True
if not_found:
raise PackageNotFoundException(name)
depends_on = set()
for out in new_subprocess(['grep', '-Po', 'Depends\s+On\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
if out:
line = out.decode().strip()
if line:
depends_on.update([d for d in line.split(' ') if d and d.lower() != 'none'])
return depends_on

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=té un conflicte amb
arch.install.conflict.popup.title=Sha detectat un conflicte
arch.install.conflict.popup.body=Les aplicacions {} estan en conflicte. Heu de desinstal·lar una per a instal·lar laltra. Voleu continuar?
arch.missing_deps.title=Dependències mancants
arch.missing_deps.body=Shan dinstal·lar les dependències següents abans de continuar amb la instal·lació de {}
arch.missing_deps.body=Shan dinstal·lar les {deps} dependències següents abans de continuar amb la instal·lació de {name}
arch.downgrade.error=Error
arch.downgrade.impossible=No sha pogut revertir la versió de {}
aur.history.1_version=versió
@@ -45,6 +45,7 @@ aur.history.3_date=data
arch.downloading.package=Sestà baixant el paquet
arch.uncompressing.package=Sestà descomprimint el paquet
arch.checking.deps=Sestan comprovant les dependències de {}
arch.checking.missing_deps=Verificació de les dependències que falten de {}
arch.missing_deps_found=Dependències mancants per a {}
arch.building.package=Sestà compilant el paquet {}
arch.checking.conflicts=Sestà comprovant si hi ha conflictes amb {}
@@ -69,8 +70,10 @@ aur.info.09_last_modified=darrera modificació
aur.info.10_url=url de baixada
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependències
aur.info.12_optdepends=dependències opcionals
aur.info.12_makedepends=dependències de compilació
aur.info.13_dependson=dependències dinstal·lació
aur.info.14_optdepends=dependències opcionals
aur.info.15_checkdepends=dependències de verificació
aur.info.14_installed_files=Fitxers instal·lats
arch.install.dep_not_found.title=No sha trobat la dependència
arch.install.dep_not_found.body=No sha trobat la dependència requerida {} a lAUR ni als servidors rèplica per defecte. Sha cancel·lat la instal·lació.
@@ -79,7 +82,8 @@ arch.install.dependency.install.error=No sha pogut instal·lar el paquet depe
arch.uninstall.required_by=No es pot desinstal·lar {} perquè és necessari per al funcionament dels paquets següents
arch.uninstall.required_by.advice=Heu de desinstal·lar-los abans de desinstal·lar {}
arch.install.optdeps.request.title=Dependències opcionals
arch.install.optdeps.request.body={} sha instal·lat correctament. Hi ha paquets opcionals associats que potser voldreu instal·lar (marqueu els que voleu)
arch.install.optdeps.request.body={} sha instal·lat correctament. Hi ha paquets opcionals associats que potser voldreu instal·lar
arch.install.optdeps.request.help=Marqueu els que voleu
arch.install.optdep.error=No sha pogut instal·lar el paquet opcional {}
arch.optdeps.checking=Sestan comprovant les dependències opcionals de {}
arch.warning.disabled=Sembla que no sha instal·lat {}. No podreu gestionar paquets Arch/AUR.

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=AUR Pakete werden von einer unabhängigen Nutzergemeinsc
arch.install.conflict.popup.title=Konflikt entdeckt
arch.install.conflict.popup.body=Die Anwendungen {} können nicht gleichzeitig installiert sein. Du musst eine deinstallieren um die andere zu installieren. Fortfahren?
arch.missing_deps.title=Fehlende Abhängigkeiten
arch.missing_deps.body=Die folgenden Abhängigkeiten müssten installiert sein, bevor mit der {} Installation fortgefahren werden kann
arch.missing_deps.body=Die folgenden {deps} Abhängigkeiten müssten installiert sein, bevor mit der {name} Installation fortgefahren werden kann
arch.downgrade.error=Fehler
arch.downgrade.impossible=Downgrade von {} ist nicht möglich
aur.history.1_version=Version
@@ -13,6 +13,7 @@ aur.history.3_date=Datum
arch.downloading.package=Paket herunterladen
arch.uncompressing.package=Paket entpacken
arch.checking.deps={} Abhängigkeiten überprüfen
arch.checking.missing_deps=Überprüfen der fehlenden Abhängigkeiten von {}
arch.missing_deps_found=Fehlende Abhängigkeiten für {}
arch.building.package=Paket {} erstellen
arch.checking.conflicts=Konflikte mit {} überprüfen
@@ -37,8 +38,10 @@ aur.info.09_last_modified=Zuletzt verändert
aur.info.10_url=url zum Herunterladen
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=Abhängigkeiten
aur.info.12_optdepends=optionale Abhängigkeiten
aur.info.12_makedepends=Kompilierungsabhängigkeiten
aur.info.13_dependson=Installationsabhängigkeiten
aur.info.14_optdepends=optionale Abhängigkeiten
aur.info.15_checkdepends=Überprüfungsabhängigkeiten
aur.info.14_installed_files=Installationsdateien
arch.install.dep_not_found.title=Abhängigkeit nicht gefunden
arch.install.dep_not_found.body=Nötige Abhängigkeit {} wurde weder im AUR noch in den standard Spiegelservern gefunden. Installation abgebrochen
@@ -47,7 +50,8 @@ arch.install.dependency.install.error=Paket {} konnte nicht installiert werden.
arch.uninstall.required_by={} konnte nicht deinstalliert werden, da es für die folgenden Pakete benötigt wird.
arch.uninstall.required_by.advice=Deinstalliere sie zuerst, bevor du {} deinstalliert
arch.install.optdeps.request.title=Optionale Abhängigkeiten
arch.install.optdeps.request.body={} wurde erfolgreich installiert! Es gibt optionale zugehörige Pakete welche du vielleicht auch installieren möchtest (wähle entsprechende aus)
arch.install.optdeps.request.body={} wurde erfolgreich installiert! Es gibt optionale zugehörige Pakete welche du vielleicht auch installieren möchtest
arch.install.optdeps.request.help=Wähle entsprechende aus
arch.install.optdep.error=Optionales Paket {} konnte nicht installiert werden
arch.optdeps.checking={} optionale Abhängigkeiten überprüfen
arch.warning.disabled={} scheint nicht intstalliert zu sein. Die Verwaltung von Arch / AUR Pakten wird nicht möglich sein.

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=AUR packages are maintained by an independent user commu
arch.install.conflict.popup.title=Conflict detected
arch.install.conflict.popup.body=The applications {} are in conflict. You must uninstall one to install the other. Continue ?
arch.missing_deps.title=Missing dependencies
arch.missing_deps.body=The following dependencies must be installed before the {} installation continues
arch.missing_deps.body=The following {deps} dependencies must be installed so the {name} installation can continue
arch.downgrade.error=Error
arch.downgrade.impossible=It is not possible to downgrade {}
aur.history.1_version=version
@@ -14,6 +14,7 @@ arch.downloading.package=Downloading the package
arch.uncompressing.package=Uncompressing the package
arch.checking.deps=Checking {} dependencies
arch.missing_deps_found=Missing dependencies for {}
arch.checking.missing_deps=Verifying missing dependencies of {}
arch.building.package=Building package {}
arch.checking.conflicts=Checking any conflicts with {}
arch.installing.package=Installing {} package
@@ -37,8 +38,10 @@ aur.info.09_last_modified=last modified
aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependencies
aur.info.12_optdepends=optional dependencies
aur.info.12_makedepends=compilation dependencies
aur.info.13_dependson=installation dependencies
aur.info.14_optdepends=optional dependencies
aur.info.15_checkdepends=checking dependencies
aur.info.14_installed_files=Installed files
arch.install.dep_not_found.title=Dependency not found
arch.install.dep_not_found.body=Required dependency {} was not found in AUR nor in default mirrors. Installation cancelled.
@@ -47,7 +50,8 @@ arch.install.dependency.install.error=Could not install dependent package {}. In
arch.uninstall.required_by={} cannot be uninstalled because it is necessary for these following packages to work
arch.uninstall.required_by.advice=Uninstall them first before uninstalling {}.
arch.install.optdeps.request.title=Optional dependencies
arch.install.optdeps.request.body={} was succesfully installed ! There are some optional associated packages that you might want to install ( check those you want )
arch.install.optdeps.request.body={} was succesfully installed ! There are some optional associated packages that you might want to install
arch.install.optdeps.request.help=Check those you want
arch.install.optdep.error=Could not install the optional package {}
arch.optdeps.checking=Checking {} optional dependencies
arch.warning.disabled={} seems not to be installed. It will not be possible to manage Arch / AUR packages.

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=conflicta
arch.install.conflict.popup.title=Conflicto detectado
arch.install.conflict.popup.body=Los aplicativos {} estan en conflicto. Debe desinstalar uno para instalar el otro. ¿Continuar?
arch.missing_deps.title=Dependencias faltantes
arch.missing_deps.body=Deben instalarse las siguientes dependencias antes de continuar la instalación de {}
arch.missing_deps.body=Deben instalarse las siguientes {deps} dependencias para que la instalación de {name} pueda continuar
arch.downgrade.error=Error
arch.downgrade.impossible=No es posible revertir la versión de {}
aur.history.1_version=versión
@@ -45,6 +45,7 @@ aur.history.3_date=fecha
arch.downloading.package=Descargando el paquete
arch.uncompressing.package=Descomprimindo el paquete
arch.checking.deps=Verificando las dependencias de {}
arch.checking.missing_deps=Verificando las dependencias faltantes de {}
arch.missing_deps_found=Dependencias faltantes para {}
arch.building.package=Construyendo el paquete {}
arch.checking.conflicts=Verificando se hay conflictos con {}
@@ -69,8 +70,10 @@ aur.info.09_last_modified=última modificación
aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependencias
aur.info.12_optdepends=dependencias opcionales
aur.info.12_makedepends=dependencias para compilacion
aur.info.13_dependson=dependencias para instalación
aur.info.14_optdepends=dependencias opcionales
aur.info.15_checkdepends=dependencias para verificación
aur.info.14_installed_files=Archivos instalados
arch.install.dep_not_found.title=Dependencia no encontrada
arch.install.dep_not_found.body=No se encontró la dependencia requerida {} en AUR ni en los espejos predeterminados. Instalación cancelada.
@@ -79,7 +82,8 @@ arch.install.dependency.install.error=No se pudo instalar el paquete dependiente
arch.uninstall.required_by=No se puede desinstalar {} porque es necesario para que los siguientes paquetes funcionen
arch.uninstall.required_by.advice=Debe desinstalarlos primero antes de desinstalar {}
arch.install.optdeps.request.title=Dependencias opcionales
arch.install.optdeps.request.body=¡{} se instaló correctamente! Hay algunos paquetes opcionales asociados que es posible que desee instalar ( marque los que desee )
arch.install.optdeps.request.body=¡{} se instaló correctamente! Hay algunos paquetes opcionales asociados que es posible que desee instalar
arch.install.optdeps.request.help=Marque los que desee
arch.install.optdep.error=No se pudo instalar el paquete opcional {}
arch.optdeps.checking=Verificando las dependencias opcionales de {}
arch.warning.disabled={} parece no estar instalado. No será posible administrar paquetes Arch / AUR.

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=I pacchetti AUR sono gestiti da una comunità di utenti
arch.install.conflict.popup.title=Conflitto rilevato
arch.install.conflict.popup.body=Le applicazioni {} sono in conflitto. È necessario disinstallarne una per installare l'altra. Continua ?
arch.missing_deps.title=Dipendenze mancanti
arch.missing_deps.body=Le seguenti dipendenze devono essere installate prima che l'installazione di {} continui
arch.missing_deps.body=Le seguenti {deps} dipendenze devono essere installate prima che l'installazione di {name} continui
arch.downgrade.error=Errore
arch.downgrade.impossible=Non è possibile effettuare il downgrade {}
aur.history.1_version=versione
@@ -13,6 +13,7 @@ aur.history.3_date=data
arch.downloading.package=Download del pacchetto
arch.uncompressing.package=Non comprimere il pacchetto
arch.checking.deps=Verifica di {} dipendenze
arch.checking.missing_deps=Verifica delle dipendenze mancanti di {}
arch.missing_deps_found=Dipendenze mancanti per {}
arch.building.package=Pacchetto costruito {}
arch.checking.conflicts=Verifica di eventuali conflitti con {}
@@ -37,8 +38,10 @@ aur.info.09_last_modified=ultima modifica
aur.info.10_url=url di download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dipendenze
aur.info.12_optdepends=dipendenze opzionali
aur.info.12_makedepends=dipendenze di compilazione
aur.info.13_dependson=dipendenze di installazione
aur.info.14_optdepends=dipendenze opzionali
aur.info.15_checkdepends=dipendenze di verifica
aur.info.14_installed_files=File installati
arch.install.dep_not_found.title=Dipendenza non trovata
arch.install.dep_not_found.body=La dipendenza richiesta {} non è stata trovata in AUR né nei mirror predefiniti. Installazione annullata.
@@ -47,7 +50,8 @@ arch.install.dependency.install.error=Impossibile installare il pacchetto dipend
arch.uninstall.required_by={} non può essere disinstallato perché è necessario che i seguenti pacchetti funzionino
arch.uninstall.required_by.advice= Uninstall them first before uninstalling {}.
arch.install.optdeps.request.title=Dipendenze opzionali
arch.install.optdeps.request.body={} è stato installato con successo! Ci sono alcuni pacchetti associati opzionali che potresti voler installare (controlla quelli che vuoi)
arch.install.optdeps.request.body={} è stato installato con successo! Ci sono alcuni pacchetti associati opzionali che potresti voler installare
arch.install.optdeps.request.help=Controlla quelli che vuoi
arch.install.optdep.error=Impossibile installare il pacchetto opzionale {}
arch.optdeps.checking=Verifica di {} dipendenze opzionali
arch.warning.disabled={} sembra non essere installato. Non sarà possibile gestire i pacchetti Arch/AUR.

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=conflita
arch.install.conflict.popup.title=Conflito detectado
arch.install.conflict.popup.body=Os aplicativos {} estão em conflito. Você precisa desinstalar um para instalar o outro. Continuar ?
arch.missing_deps.title=Dependências ausentes
arch.missing_deps.body=As seguintes dependências devem ser instaladas antes de continuar com a instalação de {}
arch.missing_deps.body=As seguintes {deps} dependências devem ser instaladas para que a instalação de {name} continue
arch.downgrade.error=Erro
arch.downgrade.impossible=Não é possível reverter a versão de {}
aur.history.1_version=versão
@@ -44,7 +44,8 @@ aur.history.2_release=lançamento
aur.history.3_date=data
arch.downloading.package=Baixando o pacote
arch.uncompressing.package=Descompactando o pacote
arch.checking.deps=Checando as dependências de {}
arch.checking.deps=Verificando as dependências de {}
arch.checking.missing_deps=Verificando dependências ausentes de {}
arch.missing_deps_found=Dependencias ausentes para {}
arch.building.package=Construindo o pacote {}
arch.checking.conflicts=Verificando se há conflitos com {}
@@ -69,8 +70,10 @@ aur.info.09_last_modified=última modificação
aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependências
aur.info.12_optdepends=dependências opcionais
aur.info.12_makedepends=dependências para compilação
aur.info.13_dependson=dependências para instalação
aur.info.14_optdepends=dependências opcionais
aur.info.15_checkdepends=dependências para verificação
aur.info.14_installed_files=Arquivos instalados
arch.install.dep_not_found.title=Dependência não encontrada
arch.install.dep_not_found.body=A dependência {} não foi encontrado no AUR nem nos espelhos padrões. Instalação cancelada.
@@ -79,7 +82,8 @@ arch.install.dependency.install.error=Não foi possível instalar o pacote depen
arch.uninstall.required_by={} não pode ser desinstalado porque ele é necessário para o funcionamento dos seguintes pacotes
arch.uninstall.required_by.advice=Desinstale eles primeiro antes de desinstalar {}
arch.install.optdeps.request.title=Dependências opcionais
arch.install.optdeps.request.body={} foi instalado com sucesso ! Existem alguns pacotes associados opcionais que você talvez queira instalar ( marque os desejados )
arch.install.optdeps.request.body={} foi instalado com sucesso ! Existem alguns pacotes opcionais associados que você talvez queira instalar
arch.install.optdeps.request.help=Marque os desejados
arch.install.optdep.error=Não foi possível instalar o pacote opcional {}
arch.optdeps.checking=Verificando as dependências opcionais de {}
arch.warning.disabled={} parece não estar instalado. Não será possível gerenciar pacotes Arch / AUR.

View File

@@ -161,8 +161,11 @@ class SnapManager(SoftwareManager):
if channels:
opts = [InputOption(label=c[0], value=c[1]) for c in channels]
channel_select = SingleSelectComponent(type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0])
body = '<p>{}.</p>'.format(self.i18n['snap.install.available_channels.message'].format(bold(self.i18n['stable']), bold(pkg.name)))
body += '<p>{}:</p>'.format(self.i18n['snap.install.available_channels.help'])
if watcher.request_confirmation(title=self.i18n['snap.install.available_channels.title'],
body=self.i18n['snap.install.available_channels.body'].format(bold(self.i18n['stable']), bold(pkg.name)) + ':',
body=body,
components=[channel_select],
confirmation_label=self.i18n['continue'],
deny_label=self.i18n['cancel']):

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Sembla que lAPI de {} no està disponible
snap.action.refresh.status=Sestà actualitzant
snap.action.refresh.label=Actualitza
snap.install.available_channels.title=Canals disponibles
snap.install.available_channels.body=No hi ha un canal {} (stable) disponible per a {}. Però més avall nhi ha els següents (seleccioneu-ne un si voleu continuar)
snap.install.available_channels.message=No hi ha un canal {} (stable) disponible per a {}. Però més avall nhi ha els següents
snap.install.available_channels.help=Seleccioneu-ne un si voleu continuar

View File

@@ -5,7 +5,8 @@ snap.notifications.api.unavailable=Es scheint als ob die {} API aktuell nicht ve
snap.action.refresh.status=Erneuern
snap.action.refresh.label=Erneuern
snap.install.available_channels.title=Verfügbare Channels
snap.install.available_channels.body=Es ist kein {} Channel verfügbar für {}. Es gibt jedoch folgende (wähle einen aus um fortzufahren)
snap.install.available_channels.message=Es ist kein {} Channel verfügbar für {}. Es gibt jedoch folgende
snap.install.available_channels.help=Wähle einen aus um fortzufahren
snap.info.commands=Kommandos
snap.info.contact=Kontakt
snap.info.description=Beschreibung

View File

@@ -5,7 +5,8 @@ snap.notifications.api.unavailable=It seems the {} API is unavailable at the mom
snap.action.refresh.status=Refreshing
snap.action.refresh.label=Refresh
snap.install.available_channels.title=Available channels
snap.install.available_channels.body=There is no {} channel available for {}. But there are these below ( select one if you want to continue )
snap.install.available_channels.message=There is no {} channel available for {}. But there are these below
snap.install.available_channels.help=Select one if you want to continue
snap.info.commands=commands
snap.info.contact=contact
snap.info.description=description

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Parece que la API de {} no está disponible e
snap.action.refresh.status=Actualizando
snap.action.refresh.label=Actualizar
snap.install.available_channels.title=Canales disponibles
snap.install.available_channels.body=No hay un canal {} ( stable ) disponible para {}. Pero hay estos otros abajo ( seleccione uno si desea continuar )
snap.install.available_channels.message=No hay un canal {} ( stable ) disponible para {}. Pero hay estos otros abajo
snap.install.available_channels.help=Seleccione uno si desea continuar

View File

@@ -5,4 +5,5 @@ snap.notifications.api.unavailable=Sembra che l'API {} non sia al momento dispon
snap.action.refresh.status=Ripristinare
snap.action.refresh.label=Ripristina
snap.install.available_channels.title=Canali disponibili
snap.install.available_channels.body=Non esiste un {} canale disponibile per {}. Ma ci sono questi sotto (selezionane uno se vuoi continuare)
snap.install.available_channels.message=Non esiste un {} canale disponibile per {}. Ma ci sono questi sotto
snap.install.available_channels.help=Selezionane uno se vuoi continuare

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Parece que a API de {} está indisponível no
snap.action.refresh.status=Atualizando
snap.action.refresh.label=Atualizar
snap.install.available_channels.title=Canais disponíveis
snap.install.available_channels.body=Não há um canal {} ( stable ) disponível para {}. Porém existem estes outros abaixo ( selecione algum se quiser continuar )
snap.install.available_channels.message=Não há um canal {} ( stable ) disponível para {}. Porém existem estes outros abaixo
snap.install.available_channels.help=Selecione algum se quiser continuar

View File

@@ -1,6 +1,7 @@
from typing import List
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame
from bauh.api.abstract.view import ViewComponent, SingleSelectComponent, MultipleSelectComponent
from bauh.view.qt import css
@@ -10,7 +11,8 @@ from bauh.view.util.translation import I18n
class ConfirmationDialog(QMessageBox):
def __init__(self, title: str, body: str, i18n: I18n, components: List[ViewComponent] = None, confirmation_label: str = None, deny_label: str = None):
def __init__(self, title: str, body: str, i18n: I18n, screen_size: QSize, components: List[ViewComponent] = None,
confirmation_label: str = None, deny_label: str = None):
super(ConfirmationDialog, self).__init__()
self.setWindowTitle(title)
self.setStyleSheet('QLabel { margin-right: 25px; }')
@@ -26,8 +28,15 @@ class ConfirmationDialog(QMessageBox):
self.layout().addWidget(QLabel(body), 0, 1)
if components:
comps_container = QWidget(parent=self)
scroll = QScrollArea(self)
scroll.setFrameShape(QFrame.NoFrame)
scroll.setWidgetResizable(True)
comps_container = QWidget()
comps_container.setLayout(QVBoxLayout())
scroll.setWidget(comps_container)
height = 0
for idx, comp in enumerate(components):
if isinstance(comp, SingleSelectComponent):
@@ -37,9 +46,13 @@ class ConfirmationDialog(QMessageBox):
else:
raise Exception("Cannot render instances of " + comp.__class__.__name__)
height += inst.sizeHint().height()
comps_container.layout().addWidget(inst)
self.layout().addWidget(comps_container, 1, 1)
height = height if height < int(screen_size.height() / 2.5) else int(screen_size.height() / 2.5)
scroll.setFixedHeight(height)
self.layout().addWidget(scroll, 1, 1)
self.exec_()

View File

@@ -28,6 +28,7 @@ class InfoDialog(QDialog):
# shows complete field string
self.text_field = QPlainTextEdit()
self.text_field.setReadOnly(True)
self.layout().addWidget(self.text_field)
self.text_field.hide()

View File

@@ -379,7 +379,8 @@ class ManageWindow(QWidget):
i18n=self.i18n,
components=msg['components'],
confirmation_label=msg['confirmation_label'],
deny_label=msg['deny_label'])
deny_label=msg['deny_label'],
screen_size=self.screen_size)
res = diag.is_confirmed()
self.thread_animate_progress.animate()
self.signal_user_res.emit(res)

View File

@@ -198,4 +198,7 @@ continue=continua
stable=estable
close=tanca
publisher.verified=verificat
mirror=mirall
mirror=mirall
emulator=emulador
do_not.install=no instal·leu
repository=dipòsit

View File

@@ -152,4 +152,7 @@ screenshots.download.no_response=Bild nicht gefunden
continue=Weiter
stable=Stabil
close=Schließen
publisher.verified=Verifiziert
publisher.verified=Verifiziert
emulator=emulator
do_not.install=nicht installieren
repository=repository

View File

@@ -154,3 +154,6 @@ stable=stable
close=close
publisher.verified=verified
mirror=mirror
emulator=emulator
do_not.install=don't install
repository=repository

View File

@@ -197,4 +197,7 @@ continue=continuar
stable=estable
close=cerrar
publisher.verified=verificado
mirror=espejo
mirror=espejo
emulator=emulador
do_not.install=no instalar
repository=repositorio

View File

@@ -153,4 +153,7 @@ continue=continua
stable=stabile
close=vicino
publisher.verified=verificato
mirror=specchio
mirror=specchio
emulator=emulatore
do_not.install=non installare
repository=deposito

View File

@@ -200,4 +200,7 @@ continue=continuar
stable=estável
close=fechar
publisher.verified=verificado
mirror=espelho
mirror=espelho
emulator=emulador
do_not.install=não instalar
repository=repositório