Modernize runtime compatibility and initialize Bearhub fork

This commit is contained in:
Sebastian Palencsar
2026-05-26 08:35:29 +02:00
parent b1ea479a3a
commit d541a4cbcf
23 changed files with 132 additions and 96 deletions

View File

@@ -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)

View File

@@ -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()

View File

@@ -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:

View File

@@ -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:

View File

@@ -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]:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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('_')

View File

@@ -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))

View File

@@ -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.")

View File

@@ -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: