diff --git a/README.md b/README.md index 38cd83b5..675e504f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,12 @@ -[![GitHub release](https://img.shields.io/github/release/vinifmor/bauh.svg?label=Release)](https://github.com/vinifmor/bauh/releases/) [![PyPI](https://img.shields.io/pypi/v/bauh?label=PyPI)](https://pypi.org/project/bauh) [![AUR](https://img.shields.io/aur/version/bauh?label=AUR)](https://aur.archlinux.org/packages/bauh) [![AUR-staging](https://img.shields.io/aur/version/bauh-staging?label=AUR-staging)](https://aur.archlinux.org/packages/bauh-staging) [![License](https://img.shields.io/github/license/vinifmor/bauh?label=License)](https://github.com/vinifmor/bauh/blob/master/LICENSE) [![kofi](https://img.shields.io/badge/Ko--Fi-Donate-orange?style=flat&logo=ko-fi)](https://ko-fi.com/vinifmor) +[![License](https://img.shields.io/github/license/spalencsar/bearhub?label=License)](https://github.com/spalencsar/bearhub/blob/master/LICENSE) + +# Bearhub + +**Bearhub** is a community-maintained fork of [bauh](https://github.com/vinifmor/bauh), focused on keeping the application working on current Linux systems, especially modern Arch Linux and Python versions. + +The original project was created by Vinicius Moreira. This fork continues from that codebase under the original license and keeps the history and authorship intact. + +At the moment, parts of the documentation below still refer to the original `bauh` command name, paths, package names, and screenshots. Those references are historical and will be updated incrementally as the fork evolves. **bauh** (baoo), formerly known as [fpakman](https://github.com/vinifmor/fpakman), is a graphical interface for managing your Linux software (packages/applications). It currently supports the following formats: AppImage, Debian and Arch Linux packages (including AUR), Flatpak, Snap and Web applications. diff --git a/bauh/commons/category.py b/bauh/commons/category.py index 3d789f3e..969df2b0 100644 --- a/bauh/commons/category.py +++ b/bauh/commons/category.py @@ -2,7 +2,7 @@ import logging import os import time import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from pathlib import Path from threading import Thread from typing import Dict, List, Optional @@ -93,7 +93,7 @@ class CategoriesDownloader(Thread): self.logger.info(self._msg('Downloading category definitions from {}'.format(self.url_categories_file))) try: - timestamp = datetime.utcnow().timestamp() + timestamp = datetime.now(timezone.utc).timestamp() res = self.http_client.get(self.url_categories_file) except requests.exceptions.ConnectionError: self.logger.error(self._msg('[{}] Could not download categories. The internet connection seems to be off.'.format(self.id_))) @@ -139,13 +139,13 @@ class CategoriesDownloader(Thread): timestamp_str = f.read() try: - categories_timestamp = datetime.fromtimestamp(float(timestamp_str)) + categories_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) except Exception: self.logger.error(self._msg("An exception occurred when trying to parse the categories file timestamp from '{}'. The categories file should be re-downloaded.".format(categories_ts_path))) traceback.print_exc() return True - should_download = (categories_timestamp + timedelta(hours=self.expiration) <= datetime.utcnow()) + should_download = (categories_timestamp + timedelta(hours=self.expiration) <= datetime.now(timezone.utc)) if should_download: self.logger.info(self._msg("Cached categories file '{}' has expired. A new one should be downloaded.".format(self.categories_path))) diff --git a/bauh/commons/util.py b/bauh/commons/util.py index 9a8c5627..39afa6a2 100644 --- a/bauh/commons/util.py +++ b/bauh/commons/util.py @@ -1,7 +1,7 @@ import logging import re from abc import ABC -from datetime import datetime +from datetime import datetime, timezone from logging import Logger from typing import Optional, Union @@ -66,7 +66,10 @@ def size_to_byte(size: Union[float, int, str], unit: str, logger: Optional[Logge return final_size * (base ** 5) -def datetime_as_milis(date: datetime = datetime.utcnow()) -> int: +def datetime_as_milis(date: Optional[datetime] = None) -> int: + if date is None: + date = datetime.now(timezone.utc) + return int(round(date.timestamp() * 1000)) diff --git a/bauh/desktop/bauh.desktop b/bauh/desktop/bauh.desktop index 93735fc0..ff4071a7 100644 --- a/bauh/desktop/bauh.desktop +++ b/bauh/desktop/bauh.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Type=Application -Name=Applications (bauh) +Name=Applications (Bearhub) Name[pt]=Aplicativos (bauh) Name[es]=Aplicaciones (bauh) Name[ca]=Aplicacions (bauh) @@ -17,5 +17,5 @@ Comment[de]=Anwendungen installieren und entfernen (AppImage, Arch, Flatpak, Sna Comment[ca]=Instal·lar i eliminar aplicacions (AppImage, Arch, Flatpak, Snap, Web) Comment[ru]=Установка и удаление приложений (AppImage, Arch, Flatpak, Snap, Web) Comment[tr]=Uygulama yükle/kaldır (AppImage, Arch, Flatpak, Snap, Web) -Exec=/usr/bin/bauh +Exec=/usr/bin/bearhub Icon=bauh diff --git a/bauh/desktop/bauh_tray.desktop b/bauh/desktop/bauh_tray.desktop index 6324235a..5228aa1e 100644 --- a/bauh/desktop/bauh_tray.desktop +++ b/bauh/desktop/bauh_tray.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Type=Application -Name=Applications (bauh-tray) +Name=Applications (Bearhub tray) Name[pt]=Aplicativos (bauh-bandeja) Name[es]=Aplicaciones (bauh-bandeja) Name[ca]=Aplicacions (bauh-safata) @@ -17,5 +17,5 @@ Comment[de]=Anwendungen installieren und entfernen (AppImage, Arch, Flatpak, Sna Comment[ca]=Instal·lar i eliminar aplicacions (AppImage, Arch, Flatpak, Snap, Web) Comment[ru]=Установка и удаление приложений (AppImage, Arch, Flatpak, Snap, Web) Comment[tr]=Uygulama yükle/kaldır (AppImage, Arch, Flatpak, Snap, Web) -Exec=/usr/bin/bauh-tray +Exec=/usr/bin/bearhub-tray Icon=bauh diff --git a/bauh/gems/appimage/worker.py b/bauh/gems/appimage/worker.py index 69cb72a9..315009c9 100644 --- a/bauh/gems/appimage/worker.py +++ b/bauh/gems/appimage/worker.py @@ -5,7 +5,7 @@ import os import tarfile import time import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from pathlib import Path from threading import Thread from typing import Optional, Generator @@ -73,13 +73,13 @@ class DatabaseUpdater(Thread): dbs_ts_str = f.read() try: - dbs_timestamp = datetime.fromtimestamp(float(dbs_ts_str)) + dbs_timestamp = datetime.fromtimestamp(float(dbs_ts_str), tz=timezone.utc) except Exception: self.logger.error('Could not parse the databases timestamp: {}'.format(dbs_ts_str)) traceback.print_exc() return True - update = dbs_timestamp + timedelta(minutes=db_exp) <= datetime.utcnow() + update = dbs_timestamp + timedelta(minutes=db_exp) <= datetime.now(timezone.utc) self.logger.info('Finished. Took {0:.2f} seconds'.format(time.time() - ti)) return update @@ -93,7 +93,7 @@ class DatabaseUpdater(Thread): self._update_task_progress(10, self.i18n['appimage.update_database.downloading']) self.logger.info('Retrieving AppImage databases') - database_timestamp = datetime.utcnow().timestamp() + database_timestamp = datetime.now(timezone.utc).timestamp() try: res = self.http_client.get(URL_COMPRESSED_DATABASES, session=False) except Exception as e: @@ -356,13 +356,13 @@ class AppImageSuggestionsDownloader(Thread): timestamp_str = f.read() try: - suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str)) + suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) except Exception: self.logger.error(f'Could not parse the cached AppImage suggestions timestamp: {timestamp_str}') traceback.print_exc() return True - update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.utcnow() + update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.now(timezone.utc) return update def read(self) -> Generator[str, None, None]: @@ -372,7 +372,7 @@ class AppImageSuggestionsDownloader(Thread): self.logger.info("Checking if AppImage suggestions should be downloaded") if self.should_download(self.config): - suggestions_timestamp = datetime.utcnow().timestamp() + suggestions_timestamp = datetime.now(timezone.utc).timestamp() suggestions_str = self.download() Thread(target=self.cache_suggestions, args=(suggestions_str, suggestions_timestamp), daemon=True).start() @@ -466,7 +466,7 @@ class AppImageSuggestionsDownloader(Thread): try: if should_download: - suggestions_timestamp = datetime.utcnow().timestamp() + suggestions_timestamp = datetime.now(timezone.utc).timestamp() suggestions_str = self.download() self.taskman.update_progress(self.task_id, 70, None) diff --git a/bauh/gems/arch/pacman.py b/bauh/gems/arch/pacman.py index 00cef089..e760f9e8 100644 --- a/bauh/gems/arch/pacman.py +++ b/bauh/gems/arch/pacman.py @@ -290,7 +290,7 @@ def read_repository_from_info(name: str) -> Optional[str]: repository = None - for o in new_subprocess(['grep', '-Po', "Repository\s+:\s+\K.+"], stdin=info.stdout).stdout: + for o in new_subprocess(['grep', '-Po', r"Repository\s+:\s+\K.+"], stdin=info.stdout).stdout: if o: line = o.decode().strip() @@ -343,7 +343,7 @@ def read_provides(name: str) -> Set[str]: provides = None - for out in new_subprocess(['grep', '-Po', 'Provides\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout: + for out in new_subprocess(['grep', '-Po', r'Provides\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout: if out: provided_names = [p.strip() for p in out.decode().strip().split(' ') if p] @@ -372,7 +372,7 @@ def read_dependencies(name: str) -> Set[str]: raise PackageNotFoundException(name) depends_on = set() - for out in new_subprocess(['grep', '-Po', 'Depends\s+On\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout: + for out in new_subprocess(['grep', '-Po', r'Depends\s+On\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout: if out: line = out.decode().strip() diff --git a/bauh/gems/arch/suggestions.py b/bauh/gems/arch/suggestions.py index 08ec8a83..161caff0 100644 --- a/bauh/gems/arch/suggestions.py +++ b/bauh/gems/arch/suggestions.py @@ -1,6 +1,6 @@ import os import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from logging import Logger from pathlib import Path from threading import Thread @@ -90,13 +90,13 @@ class RepositorySuggestionsDownloader(Thread): timestamp_str = f.read() try: - suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str)) + suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) except Exception: self._log.error(f'Could not parse the Arch cached suggestions timestamp: {timestamp_str}') traceback.print_exc() return True - update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.utcnow() + update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.now(timezone.utc) if update: self._log.info("The cached suggestions file is no longer valid") @@ -166,7 +166,7 @@ class RepositorySuggestionsDownloader(Thread): suggestions = parse(res.text, self._log, 'Arch') if suggestions: - self._save(text=res.text, timestamp=datetime.utcnow().timestamp()) + self._save(text=res.text, timestamp=datetime.now(timezone.utc).timestamp()) else: self._log.warning(f"Could not parse any Arch suggestion from {self._file_suggestions_ts}") else: diff --git a/bauh/gems/arch/worker.py b/bauh/gems/arch/worker.py index ecd81883..002331ce 100644 --- a/bauh/gems/arch/worker.py +++ b/bauh/gems/arch/worker.py @@ -6,7 +6,7 @@ import re import shutil import time import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from pathlib import Path from threading import Thread from typing import Optional @@ -71,8 +71,8 @@ class AURIndexUpdater(Thread): timestamp_str = f.read() try: - index_timestamp = datetime.fromtimestamp(float(timestamp_str)) - return (index_timestamp + timedelta(hours=exp_hours)) <= datetime.utcnow() + index_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) + return (index_timestamp + timedelta(hours=exp_hours)) <= datetime.now(timezone.utc) except Exception: traceback.print_exc() return True @@ -81,7 +81,7 @@ class AURIndexUpdater(Thread): self.logger.info('Indexing AUR packages') self.taskman.update_progress(self.task_id, 5, self.i18n['arch.task.aur.index.substatus.download']) try: - index_ts = datetime.utcnow().timestamp() + index_ts = datetime.now(timezone.utc).timestamp() res = self.http_client.get(URL_INDEX) if res and res.text: diff --git a/bauh/gems/debian/index.py b/bauh/gems/debian/index.py index b86f7941..75e1d855 100644 --- a/bauh/gems/debian/index.py +++ b/bauh/gems/debian/index.py @@ -3,7 +3,7 @@ import os import re import traceback from concurrent.futures import ThreadPoolExecutor -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from json import JSONDecodeError from logging import Logger from pathlib import Path @@ -52,14 +52,14 @@ class ApplicationIndexer: return True try: - timestamp = datetime.fromtimestamp(float(timestamp_str)) + timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) except Exception: self._log.error(f'Could not parse the Debian applications index timestamp: {timestamp_str} ' f'({self._file_timestamp_path})') traceback.print_exc() return True - expired = timestamp + timedelta(minutes=exp_minutes) <= datetime.utcnow() + expired = timestamp + timedelta(minutes=exp_minutes) <= datetime.now(timezone.utc) if expired: self._log.info("Debian applications index has expired. A new one must be generated.") @@ -122,7 +122,7 @@ class ApplicationIndexer: raise ApplicationIndexError() if update_timestamp: - index_timestamp = datetime.utcnow().timestamp() + index_timestamp = datetime.now(timezone.utc).timestamp() try: with open(self._file_timestamp_path, 'w+') as f: f.write(str(index_timestamp)) @@ -140,7 +140,7 @@ class ApplicationsMapper: def __init__(self, logger: Logger, workers: int = 10): self._log = logger self._re_desktop_file = re.compile(r'(.+):\s+(/usr/share/applications/.+\.desktop)') - self._re_desktop_file_fields = re.compile('(Exec|TryExec|Icon|Categories|NoDisplay|Terminal)\s*=\s*(.+)') + self._re_desktop_file_fields = re.compile(r'(Exec|TryExec|Icon|Categories|NoDisplay|Terminal)\s*=\s*(.+)') self._workers = workers def _read_file(self, file_path: str) -> Optional[str]: diff --git a/bauh/gems/debian/suggestions.py b/bauh/gems/debian/suggestions.py index de6cb9ae..2091356d 100644 --- a/bauh/gems/debian/suggestions.py +++ b/bauh/gems/debian/suggestions.py @@ -1,6 +1,6 @@ import os import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from logging import Logger from pathlib import Path from threading import Thread @@ -95,13 +95,13 @@ class DebianSuggestionsDownloader(Thread): timestamp_str = f.read() try: - suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str)) + suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) except Exception: self._log.error(f'Could not parse the Debian cached suggestions timestamp: {timestamp_str}') traceback.print_exc() return True - update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.utcnow() + update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.now(timezone.utc) return update def _save(self, text: str, timestamp: float): @@ -184,7 +184,7 @@ class DebianSuggestionsDownloader(Thread): suggestions = parse(res.text, self._log, 'Debian') if suggestions: - self._save(text=res.text, timestamp=datetime.utcnow().timestamp()) + self._save(text=res.text, timestamp=datetime.now(timezone.utc).timestamp()) else: self._log.warning("No Debian suggestions to cache") else: diff --git a/bauh/gems/debian/tasks.py b/bauh/gems/debian/tasks.py index f526c764..edddf6b6 100644 --- a/bauh/gems/debian/tasks.py +++ b/bauh/gems/debian/tasks.py @@ -1,6 +1,6 @@ import time import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from logging import Logger from threading import Thread from typing import Optional, Set @@ -149,14 +149,14 @@ class SynchronizePackages(Thread): return True try: - last_timestamp = datetime.fromtimestamp(float(timestamp_str)) + last_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) except Exception: logger.error(f'Could not parse the packages synchronization timestamp: {timestamp_str} ' f'({PACKAGE_SYNC_TIMESTAMP_FILE})') traceback.print_exc() return True - expired = last_timestamp + timedelta(minutes=period) <= datetime.utcnow() + expired = last_timestamp + timedelta(minutes=period) <= datetime.now(timezone.utc) if expired: logger.info("Packages synchronization is outdated") @@ -176,7 +176,7 @@ class SynchronizePackages(Thread): self._taskman.update_progress(self._id, 99, None) if updated: - index_timestamp = datetime.utcnow().timestamp() + index_timestamp = datetime.now(timezone.utc).timestamp() finish_msg = None try: with open(PACKAGE_SYNC_TIMESTAMP_FILE, 'w+') as f: diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index ac9bdebb..6cca43b3 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -1,7 +1,7 @@ import os import re import traceback -from datetime import datetime +from datetime import datetime, timezone from operator import attrgetter from pathlib import Path from threading import Thread @@ -60,7 +60,7 @@ class FlatpakManager(SoftwareManager, SettingsController): if app.runtime and app.latest_version is None: app.latest_version = app.version - expired_data = api_data and api_data.get('expires_at') and api_data['expires_at'] <= datetime.utcnow() + expired_data = api_data and api_data.get('expires_at') and api_data['expires_at'] <= datetime.now(timezone.utc) data_loader: Optional[FlatpakAsyncDataLoader] = None diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index bbb85113..89a650a7 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -16,7 +16,7 @@ from bauh.gems.flatpak.constants import FLATHUB_URL RE_SEVERAL_SPACES = re.compile(r'\s+') RE_COMMIT = re.compile(r'(Latest commit|Commit)\s*:\s*(.+)') -RE_REQUIRED_RUNTIME = re.compile(f'Required\s+runtime\s+.+\(([\w./]+)\)\s*.+\s+remote\s+([\w+./]+)') +RE_REQUIRED_RUNTIME = re.compile(r'Required\s+runtime\s+.+\(([\w./]+)\)\s*.+\s+remote\s+([\w+./]+)') OPERATION_UPDATE_SYMBOLS = {'i', 'u'} @@ -56,12 +56,20 @@ def get_fields(app_id: str, branch: str, fields: List[str]) -> List[str]: if branch: cmd.append(branch) - info = new_subprocess(cmd).stdout + info = run_cmd(' '.join(cmd), print_error=False) + + if not info: + return [] res = [] - for o in new_subprocess(('grep', '-E', '({}):.+'.format('|'.join(fields)), '-o'), stdin=info).stdout: - if o: - res.append(o.decode().split(':')[-1].strip()) + for line in info.splitlines(): + if not line or ':' not in line: + continue + + field_name, field_value = line.split(':', 1) + + if field_name.strip() in fields: + res.append(field_value.strip()) return res @@ -97,11 +105,14 @@ def list_installed(version: Tuple[str, ...]) -> List[dict]: apps = [] if version < VERSION_1_2: - app_list = new_subprocess(('flatpak', 'list', '-d'), lang=None) + app_list = run_cmd('flatpak list -d', print_error=False, lang=None) - for o in app_list.stdout: - if o: - data = o.decode().strip().split('\t') + if not app_list: + return apps + + for line in app_list.splitlines(): + if line: + data = line.strip().split('\t') ref_split = data[0].split('/') runtime = 'runtime' in data[5] @@ -121,11 +132,14 @@ def list_installed(version: Tuple[str, ...]) -> List[dict]: else: name_col = '' if version < VERSION_1_3 else 'name,' cols = f'application,ref,arch,branch,description,origin,options,{name_col}version' - app_list = new_subprocess(('flatpak', 'list', f'--columns={cols}'), lang=None) + app_list = run_cmd(f'flatpak list --columns={cols}', print_error=False, lang=None) - for o in app_list.stdout: - if o: - data = o.decode().strip().split('\t') + if not app_list: + return apps + + for line in app_list.splitlines(): + if line: + data = line.strip().split('\t') runtime = 'runtime' in data[6] if version < VERSION_1_3: @@ -237,14 +251,17 @@ def fill_updates(version: Tuple[str, ...], installation: str, res: Dict[str, Set except Exception: traceback.print_exc() else: - updates = new_subprocess(('flatpak', 'update', f'--{installation}', '--no-deps')).stdout - reg = r'[0-9]+\.\s+.+' try: - for o in new_subprocess(('grep', '-E', reg, '-o', '--color=never'), stdin=updates).stdout: - if o: - line_split = o.decode().strip().split('\t') + output = run_cmd(f'flatpak update --{installation} --no-deps', ignore_return_code=True, print_error=False) + + if not output: + return + + for line in output.splitlines(): + if re.match(reg, line): + line_split = line.strip().split('\t') if len(line_split) >= 5: if version >= VERSION_1_5: diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index 7e7d8ca3..65467fcd 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -81,7 +81,7 @@ class WebApplicationManager(SoftwareManager, SettingsController): def get_accept_language_header(self) -> str: try: - syslocale = locale.getdefaultlocale() + syslocale = locale.getlocale() if syslocale: locale_split = syslocale[0].split('_') diff --git a/bauh/gems/web/environment.py b/bauh/gems/web/environment.py index 37c59e08..82c4c8dc 100644 --- a/bauh/gems/web/environment.py +++ b/bauh/gems/web/environment.py @@ -4,7 +4,7 @@ import os import shutil import tarfile import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from pathlib import Path from threading import Thread from typing import Dict, List, Optional @@ -245,12 +245,12 @@ class EnvironmentUpdater: env_ts_str = f.read() try: - env_timestamp = datetime.fromtimestamp(float(env_ts_str)) + env_timestamp = datetime.fromtimestamp(float(env_ts_str), tz=timezone.utc) except Exception: self.logger.error(f"Could not parse environment settings file timestamp: {env_ts_str}") return True - expired = env_timestamp + timedelta(hours=settings_exp) <= datetime.utcnow() + expired = env_timestamp + timedelta(hours=settings_exp) <= datetime.now(timezone.utc) if expired: self.logger.info("Environment settings file has expired. It should be re-downloaded") @@ -314,7 +314,7 @@ class EnvironmentUpdater: self._finish_task_download_settings() return - cache_timestamp = datetime.utcnow().timestamp() + cache_timestamp = datetime.now(timezone.utc).timestamp() with open(ENVIRONMENT_SETTINGS_CACHED_FILE, 'w+') as f: f.write(yaml.safe_dump(settings)) diff --git a/bauh/gems/web/suggestions.py b/bauh/gems/web/suggestions.py index 9d133a8e..8f38af13 100644 --- a/bauh/gems/web/suggestions.py +++ b/bauh/gems/web/suggestions.py @@ -1,6 +1,6 @@ import os import traceback -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from logging import Logger from pathlib import Path from typing import Optional @@ -68,12 +68,12 @@ class SuggestionsManager: timestamp_str = f.read() try: - sugs_timestamp = datetime.fromtimestamp(float(timestamp_str)) + sugs_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc) except Exception: self.logger.error(f"Could not parse the cached suggestions file timestamp: {timestamp_str}") return True - expired = sugs_timestamp + timedelta(days=exp) <= datetime.utcnow() + expired = sugs_timestamp + timedelta(days=exp) <= datetime.now(timezone.utc) if expired: self.logger.info("Cached suggestions file has expired.") diff --git a/bauh/gems/web/worker.py b/bauh/gems/web/worker.py index 31ef0c71..193ac18f 100644 --- a/bauh/gems/web/worker.py +++ b/bauh/gems/web/worker.py @@ -1,7 +1,7 @@ import logging import time import traceback -from datetime import datetime +from datetime import datetime, timezone from threading import Thread from typing import Optional @@ -53,7 +53,7 @@ class SuggestionsLoader(Thread): self.suggestions = self.manager.read_cached(check_file=False) else: try: - timestamp = datetime.utcnow().timestamp() + timestamp = datetime.now(timezone.utc).timestamp() self.suggestions = self.manager.download() if self.suggestions: diff --git a/bauh/view/core/gems.py b/bauh/view/core/gems.py index 334f8592..493ef140 100644 --- a/bauh/view/core/gems.py +++ b/bauh/view/core/gems.py @@ -1,6 +1,6 @@ import inspect +import importlib import os -import pkgutil from logging import Logger from typing import List, Generator @@ -49,11 +49,14 @@ def load_managers(locale: str, context: ApplicationContext, config: dict, defaul logger.warning(f"gem '{f.name}' could not be loaded because it was marked as forbidden in '{FORBIDDEN_GEMS_FILE}'") continue - loader = pkgutil.find_loader(f'bauh.gems.{f.name}.controller') + module_name = f'bauh.gems.{f.name}.controller' - if loader: - module = loader.load_module() + try: + module = importlib.import_module(module_name) + except ModuleNotFoundError: + module = None + if module: manager_class = find_manager(module) if manager_class: diff --git a/bauh/view/util/cache.py b/bauh/view/util/cache.py index d5e26251..ae119351 100644 --- a/bauh/view/util/cache.py +++ b/bauh/view/util/cache.py @@ -27,7 +27,7 @@ class DefaultMemoryCache(MemoryCache): def _add(self, key: str, val: object): if key: - self._cache[key] = {'val': val, 'expires_at': datetime.datetime.utcnow() + datetime.timedelta(seconds=self.expiration_time) if self.expiration_time > 0 else None} + self._cache[key] = {'val': val, 'expires_at': datetime.datetime.now(UTC) + datetime.timedelta(seconds=self.expiration_time) if self.expiration_time > 0 else None} def add_non_existing(self, key: str, val: object): if key and self. is_enabled(): @@ -46,7 +46,7 @@ class DefaultMemoryCache(MemoryCache): if val: expiration = val.get('expires_at') - if expiration and expiration <= datetime.datetime.utcnow(): + if expiration and expiration <= datetime.datetime.now(UTC): if lock: self.lock.acquire() @@ -86,3 +86,4 @@ class DefaultMemoryCacheFactory(MemoryCacheFactory): def new(self, expiration: Optional[int] = None) -> MemoryCache: return DefaultMemoryCache(expiration if expiration is not None else self.expiration_time) +UTC = datetime.timezone.utc diff --git a/bauh/view/util/translation.py b/bauh/view/util/translation.py index 6e31c422..807c2cdf 100644 --- a/bauh/view/util/translation.py +++ b/bauh/view/util/translation.py @@ -50,7 +50,7 @@ def get_locale_keys(key: str = None, locale_dir: str = resource.get_path('locale if key is None: try: - current_locale = locale.getdefaultlocale() + current_locale = locale.getlocale() if current_locale is None or current_locale[0] is None: current_locale = ('en', 'UTF-8') diff --git a/pyproject.toml b/pyproject.toml index 11897aa9..b5b7b496 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,13 +3,16 @@ requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "bauh" -description = "Graphical interface to manage Linux applications (AppImage, Arch / AUR, Flatpak, Snap and Web)" +name = "bearhub" +description = "Community-maintained fork of bauh focused on modern Linux compatibility" license = {file = "LICENSE"} requires-python = ">=3.6" dynamic = ["version"] readme = "README.md" -authors = [{name = "Vinicius Moreira", email = "vinicius_fmoreira@hotmail.com"}] +authors = [ + {name = "Vinicius Moreira", email = "vinicius_fmoreira@hotmail.com"}, + {name = "Bearhub maintainers"} +] classifiers = [ 'Topic :: Utilities', 'Programming Language :: Python', @@ -36,12 +39,12 @@ web = [ ] [project.scripts] -bauh = "bauh.app:main" -bauh-tray = "bauh.app:tray" -bauh-cli = "bauh.cli.app:main" +bearhub = "bauh.app:main" +bearhub-tray = "bauh.app:tray" +bearhub-cli = "bauh.cli.app:main" [project.urls] -Repository = "https://github.com/vinifmor/bauh" +Repository = "https://github.com/spalencsar/bearhub" [tool.setuptools] license-files = ["LICENSE"] diff --git a/setup.py b/setup.py index 744e2e41..c141fc52 100644 --- a/setup.py +++ b/setup.py @@ -4,13 +4,14 @@ import os from setuptools import setup, find_packages DESCRIPTION = ( - "Graphical interface to manage Linux applications (AppImage, Arch / AUR, Flatpak, Snap and Web)" + "Community-maintained fork of bauh focused on modern Linux compatibility" ) AUTHOR = "Vinicius Moreira" AUTHOR_EMAIL = "vinicius_fmoreira@hotmail.com" -NAME = 'bauh' -URL = "https://github.com/vinifmor/" + NAME +DIST_NAME = 'bearhub' +APP_PACKAGE = 'bauh' +URL = "https://github.com/spalencsar/" + DIST_NAME file_dir = os.path.dirname(os.path.abspath(__file__)) @@ -21,12 +22,12 @@ else: requirements = [line.strip() for line in f.readlines() if line] -with open(file_dir + '/{}/__init__.py'.format(NAME), 'r') as f: +with open(file_dir + '/{}/__init__.py'.format(APP_PACKAGE), 'r') as f: exec(f.readlines()[0]) setup( - name=NAME, + name=DIST_NAME, version=eval('__version__'), description=DESCRIPTION, long_description=DESCRIPTION, @@ -35,14 +36,14 @@ setup( python_requires=">=3.5", url=URL, packages=find_packages(exclude=["tests.*", "tests"]), - package_data={NAME: ["view/resources/locale/*", "view/resources/img/*", "view/resources/style/*", 'view/resources/style/*/img/*', "gems/*/resources/img/*", "gems/*/resources/locale/*", "desktop/*"]}, + package_data={APP_PACKAGE: ["view/resources/locale/*", "view/resources/img/*", "view/resources/style/*", 'view/resources/style/*/img/*', "gems/*/resources/img/*", "gems/*/resources/locale/*", "desktop/*"]}, install_requires=requirements, test_suite="tests", entry_points={ "console_scripts": [ - "{name}={name}.app:main".format(name=NAME), - "{name}-tray={name}.app:tray".format(name=NAME), - "{name}-cli={name}.cli.app:main".format(name=NAME) + "bearhub=bauh.app:main", + "bearhub-tray=bauh.app:tray", + "bearhub-cli=bauh.cli.app:main" ] }, include_package_data=True,