mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-09 17:54:14 +02:00
fix: disk loader not fiiling all requested cached data from the disk
This commit is contained in:
@@ -15,11 +15,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- "Application settings" button located in right lower corner
|
- "Application settings" button located in right lower corner
|
||||||
- Package "Name" filter field (above the packages table)
|
- Package "Name" filter field (above the packages table)
|
||||||
- Showing the number of packages being shown by the total found
|
- Showing the number of packages being shown by the total found
|
||||||
|
- "Show" button for large fields of the "Info" window
|
||||||
|
|
||||||
### Improvements:
|
### Improvements:
|
||||||
- Reading installed Snaps now takes around 95% less time
|
- Reading installed Snaps now takes around 95% less time
|
||||||
- Reading installed Flatpaks now takes around 45% less time
|
- Reading installed Flatpaks now takes around 45% less time
|
||||||
- Refreshing only the associated package type after a successful operation (install, uninstall, downgrade, ...)
|
- Refreshing only the associated package type after a successful operation (install, uninstall, downgrade, ...)
|
||||||
|
- When a package is installed, it will be the first element of the table after refreshing
|
||||||
- Progress bar status can now be controlled by the software manager while an operation is being executed
|
- Progress bar status can now be controlled by the software manager while an operation is being executed
|
||||||
- Flatpak: showing runtime branches as versions when they are not available
|
- Flatpak: showing runtime branches as versions when they are not available
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
### Fixes:
|
### Fixes:
|
||||||
- cached Flatpak app current version
|
- cached Flatpak app current version
|
||||||
|
- disk loader not filling all requested cached data from the disk
|
||||||
|
|
||||||
### Code
|
### Code
|
||||||
- Code was internally modularized as: "api" (conceptual classes used to create custom software managers), "gems" (software managers), "commons" (common classes shared between the UI and "gems")
|
- Code was internally modularized as: "api" (conceptual classes used to create custom software managers), "gems" (software managers), "commons" (common classes shared between the UI and "gems")
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ context = ApplicationContext(i18n=i18n,
|
|||||||
download_icons=args.download_icons,
|
download_icons=args.download_icons,
|
||||||
app_root_dir=ROOT_DIR,
|
app_root_dir=ROOT_DIR,
|
||||||
cache_factory=cache_factory,
|
cache_factory=cache_factory,
|
||||||
disk_loader_factory=DefaultDiskCacheLoaderFactory(disk_cache_enabled=args.disk_cache),
|
disk_loader_factory=DefaultDiskCacheLoaderFactory(disk_cache_enabled=args.disk_cache, logger=logger),
|
||||||
logger=logger)
|
logger=logger)
|
||||||
user_config = config.read()
|
user_config = config.read()
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import time
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import List, Set, Type
|
from typing import List, Set, Type
|
||||||
@@ -20,6 +21,8 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
self.thread_prepare = None
|
self.thread_prepare = None
|
||||||
self.i18n = context.i18n
|
self.i18n = context.i18n
|
||||||
self.disk_loader_factory = context.disk_loader_factory
|
self.disk_loader_factory = context.disk_loader_factory
|
||||||
|
self.logger = context.logger
|
||||||
|
self._already_prepared = []
|
||||||
|
|
||||||
def reset_cache(self):
|
def reset_cache(self):
|
||||||
if self._available_map is not None:
|
if self._available_map is not None:
|
||||||
@@ -66,6 +69,7 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
res.new.extend(apps_found.new)
|
res.new.extend(apps_found.new)
|
||||||
|
|
||||||
def search(self, word: str, disk_loader: DiskCacheLoader = None, limit: int = -1) -> SearchResult:
|
def search(self, word: str, disk_loader: DiskCacheLoader = None, limit: int = -1) -> SearchResult:
|
||||||
|
ti = time.time()
|
||||||
self._wait_to_be_ready()
|
self._wait_to_be_ready()
|
||||||
|
|
||||||
res = SearchResult([], [], 0)
|
res = SearchResult([], [], 0)
|
||||||
@@ -85,13 +89,15 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
t.join()
|
t.join()
|
||||||
|
|
||||||
if disk_loader:
|
if disk_loader:
|
||||||
disk_loader.stop = True
|
disk_loader.stop_working()
|
||||||
disk_loader.join()
|
disk_loader.join()
|
||||||
|
|
||||||
res.installed = self._sort(res.installed, norm_word)
|
res.installed = self._sort(res.installed, norm_word)
|
||||||
res.new = self._sort(res.new, norm_word)
|
res.new = self._sort(res.new, norm_word)
|
||||||
res.total = len(res.installed) + len(res.new)
|
res.total = len(res.installed) + len(res.new)
|
||||||
|
|
||||||
|
tf = time.time()
|
||||||
|
self.logger.info('Took {0:.2f} seconds'.format(tf - ti))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _wait_to_be_ready(self):
|
def _wait_to_be_ready(self):
|
||||||
@@ -106,6 +112,7 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def read_installed(self, disk_loader: DiskCacheLoader = None, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None) -> SearchResult:
|
def read_installed(self, disk_loader: DiskCacheLoader = None, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None) -> SearchResult:
|
||||||
|
ti = time.time()
|
||||||
self._wait_to_be_ready()
|
self._wait_to_be_ready()
|
||||||
|
|
||||||
res = SearchResult([], None, 0)
|
res = SearchResult([], None, 0)
|
||||||
@@ -138,9 +145,11 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
res.total += man_res.total
|
res.total += man_res.total
|
||||||
|
|
||||||
if disk_loader:
|
if disk_loader:
|
||||||
disk_loader.stop = True
|
disk_loader.stop_working()
|
||||||
disk_loader.join()
|
disk_loader.join()
|
||||||
|
|
||||||
|
tf = time.time()
|
||||||
|
self.logger.info('Took {0:.2f} seconds'.format(tf - ti))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def downgrade(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
|
def downgrade(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
|
||||||
@@ -221,8 +230,9 @@ class GenericSoftwareManager(SoftwareManager):
|
|||||||
def _prepare(self):
|
def _prepare(self):
|
||||||
if self.managers:
|
if self.managers:
|
||||||
for man in self.managers:
|
for man in self.managers:
|
||||||
if self._can_work(man):
|
if man not in self._already_prepared and self._can_work(man):
|
||||||
man.prepare()
|
man.prepare()
|
||||||
|
self._already_prepared.append(man)
|
||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
self.thread_prepare = Thread(target=self._prepare)
|
self.thread_prepare = Thread(target=self._prepare)
|
||||||
|
|||||||
@@ -100,13 +100,13 @@ class ArchManager(SoftwareManager):
|
|||||||
|
|
||||||
if pkgsinfo:
|
if pkgsinfo:
|
||||||
for pkgdata in pkgsinfo:
|
for pkgdata in pkgsinfo:
|
||||||
app = self.mapper.map_api_data(pkgdata, not_signed)
|
pkg = self.mapper.map_api_data(pkgdata, not_signed)
|
||||||
app.downgrade_enabled = downgrade_enabled
|
pkg.downgrade_enabled = downgrade_enabled
|
||||||
if disk_loader:
|
if disk_loader:
|
||||||
disk_loader.fill(app)
|
disk_loader.fill(pkg)
|
||||||
app.status = PackageStatus.READY
|
pkg.status = PackageStatus.READY
|
||||||
|
|
||||||
apps.append(app)
|
apps.append(pkg)
|
||||||
|
|
||||||
def _fill_mirror_pkgs(self, mirrors: dict, apps: list):
|
def _fill_mirror_pkgs(self, mirrors: dict, apps: list):
|
||||||
# TODO
|
# TODO
|
||||||
@@ -203,8 +203,8 @@ class ArchManager(SoftwareManager):
|
|||||||
watcher.change_progress(50)
|
watcher.change_progress(50)
|
||||||
|
|
||||||
if info.get('required by'):
|
if info.get('required by'):
|
||||||
pkname = '"{}"'.format(pkg.name)
|
pkname = bold(pkg.name)
|
||||||
msg = '{}:\n\n{}\n\n{}'.format(self.i18n['arch.uninstall.required_by'].format(pkname), info['required by'], self.i18n['arch.uninstall.required_by.advice'].format(pkname))
|
msg = '{}:<br/><br/>{}<br/><br/>{}'.format(self.i18n['arch.uninstall.required_by'].format(pkname), bold(info['required by']), self.i18n['arch.uninstall.required_by.advice'].format(pkname))
|
||||||
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.WARNING)
|
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.WARNING)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -537,7 +537,6 @@ class ArchManager(SoftwareManager):
|
|||||||
|
|
||||||
def prepare(self):
|
def prepare(self):
|
||||||
self.dcache_updater.start()
|
self.dcache_updater.start()
|
||||||
|
|
||||||
self.aur_index_updater.start()
|
self.aur_index_updater.start()
|
||||||
|
|
||||||
def list_updates(self) -> List[PackageUpdate]:
|
def list_updates(self) -> List[PackageUpdate]:
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class AURIndexUpdater(Thread):
|
|||||||
self.man = man
|
self.man = man
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
self.logger.info('Pre-indexing AUR packages in memory')
|
self.logger.info('Pre-indexing AUR packages in memory')
|
||||||
res = self.http_client.get(URL_INDEX)
|
res = self.http_client.get(URL_INDEX)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
from threading import Thread, Lock
|
from threading import Thread, Lock
|
||||||
from typing import Type, Dict
|
from typing import Type, Dict
|
||||||
|
|
||||||
@@ -10,13 +12,15 @@ from bauh.api.abstract.model import SoftwarePackage
|
|||||||
|
|
||||||
class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
|
class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
|
||||||
|
|
||||||
def __init__(self, enabled: bool, cache_map: Dict[Type[SoftwarePackage], MemoryCache]):
|
def __init__(self, enabled: bool, cache_map: Dict[Type[SoftwarePackage], MemoryCache], logger: logging.Logger):
|
||||||
super(AsyncDiskCacheLoader, self).__init__(daemon=True)
|
super(AsyncDiskCacheLoader, self).__init__(daemon=True)
|
||||||
self.apps = []
|
self.pkgs = []
|
||||||
self.stop = False
|
self._work = True
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
self.cache_map = cache_map
|
self.cache_map = cache_map
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
|
self.logger = logger
|
||||||
|
self.processed = 0
|
||||||
|
|
||||||
def fill(self, pkg: SoftwarePackage):
|
def fill(self, pkg: SoftwarePackage):
|
||||||
"""
|
"""
|
||||||
@@ -24,25 +28,28 @@ class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
|
|||||||
:param pkg:
|
:param pkg:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if self.enabled:
|
if self.enabled and pkg and pkg.supports_disk_cache():
|
||||||
if pkg and pkg.supports_disk_cache():
|
self.pkgs.append(pkg)
|
||||||
self.lock.acquire()
|
|
||||||
self.apps.append(pkg)
|
def stop_working(self):
|
||||||
self.lock.release()
|
self._work = False
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.enabled:
|
if self.enabled:
|
||||||
|
last = 0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if self.apps:
|
time.sleep(0.00001)
|
||||||
self.lock.acquire()
|
if len(self.pkgs) > self.processed:
|
||||||
app = self.apps[0]
|
pkg = self.pkgs[last]
|
||||||
del self.apps[0]
|
|
||||||
self.lock.release()
|
self._fill_cached_data(pkg)
|
||||||
self._fill_cached_data(app)
|
self.processed += 1
|
||||||
elif self.stop:
|
last += 1
|
||||||
|
elif not self._work:
|
||||||
break
|
break
|
||||||
|
|
||||||
def _fill_cached_data(self, pkg: SoftwarePackage):
|
def _fill_cached_data(self, pkg: SoftwarePackage) -> bool:
|
||||||
if self.enabled:
|
if self.enabled:
|
||||||
if os.path.exists(pkg.get_disk_data_path()):
|
if os.path.exists(pkg.get_disk_data_path()):
|
||||||
with open(pkg.get_disk_data_path()) as f:
|
with open(pkg.get_disk_data_path()) as f:
|
||||||
@@ -54,12 +61,16 @@ class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
|
|||||||
if cache:
|
if cache:
|
||||||
cache.add_non_existing(pkg.id, cached_data)
|
cache.add_non_existing(pkg.id, cached_data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory):
|
class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory):
|
||||||
|
|
||||||
def __init__(self, disk_cache_enabled: bool):
|
def __init__(self, disk_cache_enabled: bool, logger: logging.Logger):
|
||||||
super(DefaultDiskCacheLoaderFactory, self).__init__()
|
super(DefaultDiskCacheLoaderFactory, self).__init__()
|
||||||
self.disk_cache_enabled = disk_cache_enabled
|
self.disk_cache_enabled = disk_cache_enabled
|
||||||
|
self.logger = logger
|
||||||
self.cache_map = {}
|
self.cache_map = {}
|
||||||
|
|
||||||
def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache):
|
def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache):
|
||||||
@@ -68,4 +79,4 @@ class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory):
|
|||||||
self.cache_map[pkg_type] = cache
|
self.cache_map[pkg_type] = cache
|
||||||
|
|
||||||
def new(self) -> AsyncDiskCacheLoader:
|
def new(self) -> AsyncDiskCacheLoader:
|
||||||
return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map)
|
return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map, logger=self.logger)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from typing import List, Type, Set
|
|||||||
from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal
|
from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal
|
||||||
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap, QCursor
|
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap, QCursor
|
||||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \
|
||||||
QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction
|
QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction, QApplication
|
||||||
|
|
||||||
from bauh.api.abstract.cache import MemoryCache
|
from bauh.api.abstract.cache import MemoryCache
|
||||||
from bauh.api.abstract.context import ApplicationContext
|
from bauh.api.abstract.context import ApplicationContext
|
||||||
@@ -231,7 +231,8 @@ class ManageWindow(QWidget):
|
|||||||
self.toolbar_bottom.addWidget(new_spacer())
|
self.toolbar_bottom.addWidget(new_spacer())
|
||||||
|
|
||||||
self.progress_bar = QProgressBar()
|
self.progress_bar = QProgressBar()
|
||||||
self.progress_bar.setMaximumHeight(7)
|
self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4)
|
||||||
|
|
||||||
self.progress_bar.setTextVisible(False)
|
self.progress_bar.setTextVisible(False)
|
||||||
self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar)
|
self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user