[improvement][ui] not performing a full table refresh after installing a new package

This commit is contained in:
Vinicius Moreira
2020-06-11 16:51:55 -03:00
parent cc5c89a350
commit a43e640437
11 changed files with 183 additions and 70 deletions

View File

@@ -16,7 +16,8 @@ from typing import Set, Type, List, Tuple
from colorama import Fore
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, UpgradeRequirement
from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, UpgradeRequirement, \
TransactionResult
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher, TaskManager
from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \
@@ -109,13 +110,13 @@ class AppImageManager(SoftwareManager):
if inp_cat.get_selected() != cat_ops[0].value:
appim.categories.append(inp_cat.get_selected())
installed = self.install(root_password=root_password, pkg=appim, watcher=watcher)
res = self.install(root_password=root_password, pkg=appim, disk_loader=None, watcher=watcher).success
if installed:
if res:
appim.installed = True
self.cache_to_disk(appim, None, False)
return installed
return res
def update_file(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher):
file_chooser = FileChooserComponent(label=self.i18n['file'].capitalize(), allowed_extensions={'AppImage'})
@@ -281,7 +282,7 @@ class AppImageManager(SoftwareManager):
pkg.version = old_release['0_version']
pkg.latest_version = pkg.version
pkg.url_download = old_release['2_url_download']
if self.install(pkg, root_password, watcher):
if self.install(pkg, root_password, None, watcher).success:
self.cache_to_disk(pkg, None, False)
return True
else:
@@ -306,7 +307,7 @@ class AppImageManager(SoftwareManager):
watcher.change_substatus('')
return False
if not self.install(req.pkg, root_password, watcher):
if not self.install(req.pkg, root_password, None, watcher).success:
watcher.change_substatus('')
return False
@@ -408,7 +409,7 @@ class AppImageManager(SoftwareManager):
if RE_ICON_ENDS_WITH.match(f):
return f
def install(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher) -> bool:
def install(self, pkg: AppImage, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
handler = ProcessHandler(watcher)
out_dir = INSTALLATION_PATH + pkg.name.lower()
@@ -440,7 +441,8 @@ class AppImageManager(SoftwareManager):
watcher.show_message(title=self.i18n['error'].capitalize(),
body=self.i18n['appimage.install.imported.rename_error'].format(bold(pkg.local_file_path.split('/')[-1]), bold(output)),
type_=MessageType.ERROR)
return False
return TransactionResult(success=False, installed=[], removed=[])
else:
appimage_url = pkg.url_download_latest_version if pkg.update else pkg.url_download
@@ -468,14 +470,14 @@ class AppImageManager(SoftwareManager):
body=self.i18n['appimage.install.appimagelauncher.error'].format(appimgl=bold('AppImageLauncher'), app=bold(pkg.name)),
type_=MessageType.ERROR)
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
return False
return TransactionResult(success=False, installed=[], removed=[])
except:
watcher.show_message(title=self.i18n['error'],
body=traceback.format_exc(),
type_=MessageType.ERROR)
traceback.print_exc()
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
return False
return TransactionResult(success=False, installed=[], removed=[])
watcher.change_substatus(self.i18n['appimage.install.desktop_entry'])
extracted_folder = '{}/{}'.format(out_dir, 'squashfs-root')
@@ -501,8 +503,13 @@ class AppImageManager(SoftwareManager):
with open(self._gen_desktop_entry_path(pkg), 'w+') as f:
f.write(de_content)
shutil.rmtree(extracted_folder)
return True
try:
shutil.rmtree(extracted_folder)
except:
traceback.print_exc()
pkg.installed = True
return TransactionResult(success=True, installed=[pkg], removed=[])
else:
watcher.show_message(title=self.i18n['error'],
body='Could extract content from {}'.format(bold(file_name)),
@@ -513,7 +520,7 @@ class AppImageManager(SoftwareManager):
type_=MessageType.ERROR)
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
return False
return TransactionResult(success=False, installed=[], removed=[])
def _gen_desktop_entry_path(self, app: AppImage) -> str:
return '{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, app.name.lower())

View File

@@ -15,7 +15,8 @@ from typing import List, Set, Type, Tuple, Dict, Iterable
import requests
from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext, UpgradeRequirements
from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext, UpgradeRequirements, \
TransactionResult
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher, TaskManager
from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePackage, PackageSuggestion, PackageStatus, \
@@ -63,7 +64,8 @@ class TransactionContext:
install_file: str = None, repository: str = None, pkg: ArchPackage = None,
remote_repo_map: Dict[str, str] = None, provided_map: Dict[str, Set[str]] = None,
remote_provided_map: Dict[str, Set[str]] = None, aur_idx: Set[str] = None,
missing_deps: List[Tuple[str, str]] = None):
missing_deps: List[Tuple[str, str]] = None, installed: Set[str] = None, removed: Dict[str, SoftwarePackage] = None,
disk_loader: DiskCacheLoader = None):
self.name = name
self.base = base
self.maintainer = maintainer
@@ -84,12 +86,16 @@ class TransactionContext:
self.remote_provided_map = remote_provided_map
self.aur_idx = aur_idx
self.missing_deps = missing_deps
self.installed = installed
self.removed = removed
self.disk_loader = disk_loader
@classmethod
def gen_context_from(cls, pkg: ArchPackage, arch_config: dict, root_password: str, handler: ProcessHandler) -> "TransactionContext":
return cls(name=pkg.name, base=pkg.get_base_name(), maintainer=pkg.maintainer, repository=pkg.repository,
arch_config=arch_config, watcher=handler.watcher, handler=handler, skip_opt_deps=True,
change_progress=True, root_password=root_password, dependency=False)
change_progress=True, root_password=root_password, dependency=False,
installed=set(), removed={})
def get_base_name(self):
return self.base if self.base else self.name
@@ -99,7 +105,7 @@ class TransactionContext:
def clone_base(self):
return TransactionContext(watcher=self.watcher, handler=self.handler, root_password=self.root_password,
arch_config=self.config)
arch_config=self.config, installed=set(), removed={})
def gen_dep_context(self, name: str, repository: str):
dep_context = self.clone_base()
@@ -107,6 +113,8 @@ class TransactionContext:
dep_context.repository = repository
dep_context.dependency = True
dep_context.change_progress = False
dep_context.installed = set()
dep_context.removed = {}
return dep_context
def has_install_file(self) -> bool:
@@ -894,7 +902,7 @@ class ArchManager(SoftwareManager):
context.change_progress = False
try:
if not self.install(pkg=pkg, root_password=root_password, watcher=watcher, context=context):
if not self.install(pkg=pkg, root_password=root_password, watcher=watcher, disk_loader=None, context=context).success:
watcher.print(self.i18n['arch.upgrade.fail'].format('"{}"'.format(pkg.name)))
self.logger.error("Could not upgrade AUR package '{}'".format(pkg.name))
watcher.change_substatus('')
@@ -1500,6 +1508,8 @@ class ArchManager(SoftwareManager):
message.show_deps_not_installed(context.watcher, context.name, deps_not_installed, self.i18n)
return False
context.installed.update({d[0] for d in missing_deps})
return True
def _list_missing_deps(self, context: TransactionContext) -> List[Tuple[str, str]]:
@@ -1657,6 +1667,8 @@ class ArchManager(SoftwareManager):
if deps_not_installed:
message.show_optdeps_not_installed(deps_not_installed, context.watcher, self.i18n)
else:
context.installed.update({dep[0] for dep in sorted_deps})
return True
@@ -1703,6 +1715,12 @@ class ArchManager(SoftwareManager):
to_uninstall = [conflict for conflict in conflicting_apps if conflict != context.name]
self.logger.info("Preparing to uninstall conflicting packages: {}".format(to_uninstall))
pkgs_to_uninstall = self.read_installed(disk_loader=context.disk_loader, names=to_uninstall, internet_available=True).installed
if not pkgs_to_uninstall:
self.logger.warning("Could not load packages to uninstall")
for conflict in to_uninstall:
context.watcher.change_substatus(self.i18n['arch.uninstalling.conflict'].format(bold(conflict)))
@@ -1711,6 +1729,13 @@ class ArchManager(SoftwareManager):
body=self.i18n['arch.uninstalling.conflict.fail'].format(bold(conflict)),
type_=MessageType.ERROR)
return False
else:
uninstalled = [p for p in pkgs_to_uninstall if p.name == conflict]
if uninstalled:
uninstalled[0].icon_path = None
uninstalled[0].icon_url = None
uninstalled[0].installed = False
context.removed[conflict] = uninstalled[0]
else:
self.logger.info("No conflict detected for '{}'".format(context.name))
@@ -1742,6 +1767,7 @@ class ArchManager(SoftwareManager):
else:
status_handler = None
installed_with_same_name = self.read_installed(disk_loader=context.disk_loader, internet_available=True, names={context.name}).installed
context.watcher.change_substatus(self.i18n['arch.installing.package'].format(bold(context.name)))
installed, _ = context.handler.handle_simple(pacman.install_as_process(pkgpaths=to_install,
root_password=context.root_password,
@@ -1755,6 +1781,16 @@ class ArchManager(SoftwareManager):
self._update_progress(context, 95)
if installed:
context.installed.add(context.name)
context.installed.update((p for p in to_install if not p.startswith('/')))
if installed_with_same_name:
for p in installed_with_same_name:
context.removed[p.name] = p
p.installed = False
p.icon_path = None
p.icon_url = None
context.watcher.change_substatus(self.i18n['status.caching_data'].format(bold(context.name)))
if not context.maintainer:
@@ -1887,16 +1923,16 @@ class ArchManager(SoftwareManager):
watcher.change_substatus(self.i18n['arch.makepkg.optimizing'])
ArchCompilationOptimizer(arch_config, self.i18n, self.context.logger).optimize()
def install(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher, context: TransactionContext = None) -> bool:
def install(self, pkg: ArchPackage, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher, context: TransactionContext = None) -> TransactionResult:
self.aur_client.clean_caches()
if not self._check_action_allowed(pkg, watcher):
return False
return TransactionResult(success=False, installed=[], removed=[])
handler = ProcessHandler(watcher) if not context else context.handler
if self._is_database_locked(handler, root_password):
return False
return TransactionResult(success=False, installed=[], removed=[])
if context:
install_context = context
@@ -1904,6 +1940,7 @@ class ArchManager(SoftwareManager):
install_context = TransactionContext.gen_context_from(pkg=pkg, handler=handler, arch_config=read_config(),
root_password=root_password)
install_context.skip_opt_deps = False
install_context.disk_loader = disk_loader
self._sync_databases(arch_config=install_context.config, root_password=root_password, handler=handler)
@@ -1919,7 +1956,30 @@ class ArchManager(SoftwareManager):
data = json.loads(data)
pkg.fill_cached_data(data)
return res
installed = []
if res and disk_loader and install_context.installed:
installed.append(pkg)
installed_to_load = []
if len(install_context.installed) > 1:
installed_to_load.extend({i for i in install_context.installed if i != pkg.name})
if installed_to_load:
installed_loaded = self.read_installed(disk_loader=disk_loader,
names=installed_to_load,
internet_available=True).installed
installed.extend(installed_loaded)
if len(installed_loaded) + 1 != len(install_context.installed):
missing = ','.join({p for p in installed_loaded if p.name not in install_context.installed})
self.logger.warning("Could not load all installed packages. Missing: {}".format(missing))
removed = [*install_context.removed.values()] if install_context.removed else []
return TransactionResult(success=res, installed=installed, removed=removed)
def _install_from_repository(self, context: TransactionContext) -> bool:
try:

View File

@@ -100,26 +100,28 @@ def map_installed(names: Iterable[str] = None) -> dict: # returns a dict with w
thread_ignored = Thread(target=_fill_ignored, args=(ignored,), daemon=True)
thread_ignored.start()
allinfo = run_cmd('pacman -Qi{}'.format(' ' + ' '.join(names) if names else ''))
allinfo = run_cmd('pacman -Qi{}'.format(' ' + ' '.join(names) if names else ''), print_error=False)
pkgs = {'signed': {}, 'not_signed': {}}
current_pkg = {}
for idx, field_tuple in enumerate(RE_INSTALLED_FIELDS.findall(allinfo)):
if field_tuple[0].startswith('N'):
current_pkg['name'] = field_tuple[1].strip()
elif field_tuple[0].startswith('Ve'):
current_pkg['version'] = field_tuple[1].split(':')[-1].strip()
elif field_tuple[0].startswith('D'):
current_pkg['description'] = field_tuple[1].strip()
elif field_tuple[0].startswith('Va'):
if field_tuple[1].strip().lower() == 'none':
pkgs['not_signed'][current_pkg['name']] = current_pkg
del current_pkg['name']
else:
pkgs['signed'][current_pkg['name']] = current_pkg
del current_pkg['name']
current_pkg = {}
if allinfo:
for idx, field_tuple in enumerate(RE_INSTALLED_FIELDS.findall(allinfo)):
if field_tuple[0].startswith('N'):
current_pkg['name'] = field_tuple[1].strip()
elif field_tuple[0].startswith('Ve'):
current_pkg['version'] = field_tuple[1].split(':')[-1].strip()
elif field_tuple[0].startswith('D'):
current_pkg['description'] = field_tuple[1].strip()
elif field_tuple[0].startswith('Va'):
if field_tuple[1].strip().lower() == 'none':
pkgs['not_signed'][current_pkg['name']] = current_pkg
del current_pkg['name']
else:
pkgs['signed'][current_pkg['name']] = current_pkg
del current_pkg['name']
current_pkg = {}
if pkgs['signed'] or pkgs['not_signed']:
thread_ignored.join()

View File

@@ -7,7 +7,7 @@ from threading import Thread
from typing import List, Set, Type, Tuple
from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext, UpgradeRequirements, \
UpgradeRequirement
UpgradeRequirement, TransactionResult
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher, TaskManager
from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \
@@ -296,7 +296,7 @@ class FlatpakManager(SoftwareManager):
return PackageHistory(pkg=pkg, history=commits, pkg_status_idx=status_idx)
def install(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:
def install(self, pkg: FlatpakApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
config = read_config()
@@ -310,7 +310,7 @@ class FlatpakManager(SoftwareManager):
body=self.i18n['flatpak.install.bad_install_level.body'].format(field=bold('installation_level'),
file=bold(CONFIG_FILE)),
type_=MessageType.ERROR)
return False
return TransactionResult(success=False, installed=[], removed=[])
pkg.installation = install_level
else:
@@ -333,14 +333,14 @@ class FlatpakManager(SoftwareManager):
user_password, valid = watcher.request_root_password()
if not valid:
watcher.print('Operation aborted')
return False
return TransactionResult(success=False, installed=[], removed=[])
else:
if not handler.handle_simple(flatpak.set_default_remotes('system', user_password)):
watcher.show_message(title=self.i18n['error'].capitalize(),
body=self.i18n['flatpak.remotes.system_flathub.error'],
type_=MessageType.ERROR)
watcher.print("Operation cancelled")
return False
return TransactionResult(success=False, installed=[], removed=[])
res = handler.handle(SystemProcess(subproc=flatpak.install(str(pkg.id), pkg.origin, pkg.installation), wrong_error_phrase='Warning'))
@@ -354,7 +354,8 @@ class FlatpakManager(SoftwareManager):
except:
traceback.print_exc()
return res
pkg.installed = res
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
def is_enabled(self):
return self.enabled

View File

@@ -4,7 +4,8 @@ from datetime import datetime
from threading import Thread
from typing import List, Set, Type
from bauh.api.abstract.controller import SoftwareManager, SearchResult, ApplicationContext, UpgradeRequirements
from bauh.api.abstract.controller import SoftwareManager, SearchResult, ApplicationContext, UpgradeRequirements, \
TransactionResult
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher, TaskManager
from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \
@@ -174,7 +175,7 @@ class SnapManager(SoftwareManager):
def get_history(self, pkg: SnapApplication) -> PackageHistory:
raise Exception("'get_history' is not supported by {}".format(pkg.__class__.__name__))
def install(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool:
def install(self, pkg: SnapApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
info_path = self.get_info_path()
if not info_path:
@@ -204,14 +205,16 @@ class SnapManager(SoftwareManager):
if res and info_path:
pkg.has_apps_field = snap.has_apps_field(pkg.name, info_path)
return res
pkg.installed = res
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
else:
self.logger.error("Could not find available channels in the installation output: {}".format(output))
else:
if info_path:
pkg.has_apps_field = snap.has_apps_field(pkg.name, info_path)
return res
pkg.installed = res
return TransactionResult(success=res, installed=[pkg] if res else [], removed=[])
def is_enabled(self) -> bool:
return self.enabled

View File

@@ -17,7 +17,7 @@ from colorama import Fore
from requests import exceptions, Response
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements
from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, TransactionResult
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher, TaskManager
from bauh.api.abstract.model import SoftwarePackage, CustomSoftwareAction, PackageSuggestion, PackageUpdate, \
@@ -617,13 +617,13 @@ class WebApplicationManager(SoftwareManager):
pkg.name))
traceback.print_exc()
def install(self, pkg: WebApplication, root_password: str, watcher: ProcessWatcher) -> bool:
def install(self, pkg: WebApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
continue_install, install_options = self._ask_install_options(pkg, watcher)
if not continue_install:
watcher.print("Installation aborted by the user")
return False
return TransactionResult(success=False, installed=[], removed=[])
watcher.change_substatus(self.i18n['web.env.checking'])
handler = ProcessHandler(watcher)
@@ -635,18 +635,18 @@ class WebApplicationManager(SoftwareManager):
watcher.show_message(title=self.i18n['error'].capitalize(),
body=self.i18n['web.install.global_nativefier.unavailable'].format(n=bold('Nativefier'), app=bold(pkg.name)) + '.',
type_=MessageType.ERROR)
return False
return TransactionResult(success=False, installed=[], removed=[])
env_components = self.env_updater.check_environment(app=pkg, local_config=local_config, env=env_settings, is_x86_x64_arch=self.context.is_system_x86_64())
comps_to_update = [c for c in env_components if c.update]
if comps_to_update and not self._ask_update_permission(comps_to_update, watcher):
return False
return TransactionResult(success=False, installed=[], removed=[])
if not self.env_updater.update(components=comps_to_update, handler=handler):
watcher.show_message(title=self.i18n['error'], body=self.i18n['web.env.error'].format(bold(pkg.name)), type_=MessageType.ERROR)
return False
return TransactionResult(success=False, installed=[], removed=[])
Path(INSTALLED_PATH).mkdir(parents=True, exist_ok=True)
@@ -699,7 +699,7 @@ class WebApplicationManager(SoftwareManager):
msg = '{}.{}.'.format(self.i18n['wen.install.error'].format(bold(pkg.name)),
self.i18n['web.install.nativefier.error.unknown'].format(bold(self.i18n['details'].capitalize())))
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.ERROR)
return False
return TransactionResult(success=False, installed=[], removed=[])
inner_dir = os.listdir(app_dir)
@@ -707,7 +707,7 @@ class WebApplicationManager(SoftwareManager):
msg = '{}.{}.'.format(self.i18n['wen.install.error'].format(bold(pkg.name)),
self.i18n['web.install.nativefier.error.inner_dir'].format(bold(app_dir)))
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.ERROR)
return False
return TransactionResult(success=False, installed=[], removed=[])
# bringing the inner app folder to the 'installed' folder level:
inner_dir = '{}/{}'.format(app_dir, inner_dir[0])
@@ -763,7 +763,8 @@ class WebApplicationManager(SoftwareManager):
if install_options:
pkg.options_set = install_options
return True
pkg.installed = True
return TransactionResult(success=True, installed=[pkg], removed=[])
def _gen_desktop_entry_content(self, pkg: WebApplication) -> str:
return """