mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-05-17 18:51:50 +02:00
Compare commits
4 commits
61d8422b95
...
1f3749c95c
Author | SHA1 | Date | |
---|---|---|---|
1f3749c95c | |||
![]() |
7eb54c3dd5 | ||
![]() |
f1dac59fb8 | ||
![]() |
a6aa66f925 |
13 changed files with 635 additions and 642 deletions
|
@ -24,6 +24,8 @@ from ..document import Document
|
||||||
from ..isolation_provider.container import Container
|
from ..isolation_provider.container import Container
|
||||||
from ..isolation_provider.dummy import Dummy
|
from ..isolation_provider.dummy import Dummy
|
||||||
from ..isolation_provider.qubes import Qubes, is_qubes_native_conversion
|
from ..isolation_provider.qubes import Qubes, is_qubes_native_conversion
|
||||||
|
from ..updater import errors as updater_errors
|
||||||
|
from ..updater import releases
|
||||||
from ..util import get_resource_path, get_version
|
from ..util import get_resource_path, get_version
|
||||||
from .logic import DangerzoneGui
|
from .logic import DangerzoneGui
|
||||||
from .main_window import MainWindow
|
from .main_window import MainWindow
|
||||||
|
@ -161,16 +163,15 @@ def gui_main(dummy_conversion: bool, filenames: Optional[List[str]]) -> bool:
|
||||||
window.register_update_handler(updater.finished)
|
window.register_update_handler(updater.finished)
|
||||||
|
|
||||||
log.debug("Consulting updater settings before checking for updates")
|
log.debug("Consulting updater settings before checking for updates")
|
||||||
if updater.should_check_for_updates():
|
should_check = updater.should_check_for_updates()
|
||||||
|
|
||||||
|
if should_check:
|
||||||
log.debug("Checking for updates")
|
log.debug("Checking for updates")
|
||||||
updater.start()
|
updater.start()
|
||||||
else:
|
else:
|
||||||
log.debug("Will not check for updates, based on updater settings")
|
log.debug("Will not check for updates, based on updater settings")
|
||||||
|
|
||||||
# Ensure the status of the toggle updates checkbox is updated, after the user is
|
window.toggle_updates_action.setChecked(should_check)
|
||||||
# prompted to enable updates.
|
|
||||||
window.toggle_updates_action.setChecked(bool(updater.check))
|
|
||||||
|
|
||||||
if filenames:
|
if filenames:
|
||||||
open_files(filenames)
|
open_files(filenames)
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,9 @@ else:
|
||||||
from .. import errors
|
from .. import errors
|
||||||
from ..document import SAFE_EXTENSION, Document
|
from ..document import SAFE_EXTENSION, Document
|
||||||
from ..isolation_provider.qubes import is_qubes_native_conversion
|
from ..isolation_provider.qubes import is_qubes_native_conversion
|
||||||
|
from ..updater.releases import UpdateReport
|
||||||
from ..util import format_exception, get_resource_path, get_version
|
from ..util import format_exception, get_resource_path, get_version
|
||||||
from .logic import Alert, CollapsibleBox, DangerzoneGui, UpdateDialog
|
from .logic import Alert, CollapsibleBox, DangerzoneGui, UpdateDialog
|
||||||
from .updater import UpdateReport
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,7 @@
|
||||||
"""A module that contains the logic for checking for updates."""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import platform
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import typing
|
import typing
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from packaging import version
|
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from PySide2 import QtCore, QtWidgets
|
from PySide2 import QtCore, QtWidgets
|
||||||
else:
|
else:
|
||||||
|
@ -18,36 +10,33 @@ else:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from PySide2 import QtCore, QtWidgets
|
from PySide2 import QtCore, QtWidgets
|
||||||
|
|
||||||
# XXX implict import for "markdown" module required for Cx_Freeze to build on Windows
|
from ..updater import errors, releases
|
||||||
# See https://github.com/freedomofpress/dangerzone/issues/501
|
|
||||||
import html.parser # noqa: F401
|
|
||||||
|
|
||||||
import markdown
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from ..util import get_version
|
|
||||||
from .logic import Alert, DangerzoneGui
|
from .logic import Alert, DangerzoneGui
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
MSG_CONFIRM_UPDATE_CHECKS = """\
|
MSG_CONFIRM_UPDATE_CHECKS = """\
|
||||||
<p><b>Do you want Dangerzone to automatically check for updates?</b></p>
|
<p>
|
||||||
|
<b>Do you want Dangerzone to automatically check for updates and apply them?</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>If you accept, Dangerzone will check the
|
<p>If you accept, Dangerzone will check for updates of the sandbox and apply them
|
||||||
|
automatically. This will ensure that you always have the latest version of the sandbox,
|
||||||
|
which is critical for the software to operate securely.</p>
|
||||||
|
|
||||||
|
<p>Sandbox updates may include security patches and bug fixes, but won't include new features.</p>
|
||||||
|
|
||||||
|
<p>Additionally, Dangerzone will check the
|
||||||
<a href="https://github.com/freedomofpress/dangerzone/releases">latest releases page</a>
|
<a href="https://github.com/freedomofpress/dangerzone/releases">latest releases page</a>
|
||||||
in github.com on startup. Otherwise it will make no network requests and
|
in github.com, and inform you about new releases.
|
||||||
won't inform you about new releases.</p>
|
|
||||||
|
Otherwise it will make no network requests and won't inform you about new releases.</p>
|
||||||
|
|
||||||
<p>If you prefer another way of getting notified about new releases, we suggest adding
|
<p>If you prefer another way of getting notified about new releases, we suggest adding
|
||||||
to your RSS reader our
|
to your RSS reader our
|
||||||
<a href="https://fosstodon.org/@dangerzone.rss">Mastodon feed</a>. For more information
|
<a href="https://dangerzone.rocks/feed.xml">Dangerzone News feed</a>.</p>
|
||||||
about updates, check
|
|
||||||
<a href="https://github.com/freedomofpress/dangerzone/wiki/Updates">this webpage</a>.</p>
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
UPDATE_CHECK_COOLDOWN_SECS = 60 * 60 * 12 # Check for updates at most every 12 hours.
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateCheckPrompt(Alert):
|
class UpdateCheckPrompt(Alert):
|
||||||
"""The prompt that asks the users if they want to enable update checks."""
|
"""The prompt that asks the users if they want to enable update checks."""
|
||||||
|
@ -55,7 +44,7 @@ class UpdateCheckPrompt(Alert):
|
||||||
x_pressed = False
|
x_pressed = False
|
||||||
|
|
||||||
def closeEvent(self, event: QtCore.QEvent) -> None:
|
def closeEvent(self, event: QtCore.QEvent) -> None:
|
||||||
"""Detect when a user has pressed "X" in the title bar.
|
"""Detect when a user has pressed "X" in the title bar (to close the dialog).
|
||||||
|
|
||||||
This function is called when a user clicks on "X" in the title bar. We want to
|
This function is called when a user clicks on "X" in the title bar. We want to
|
||||||
differentiate between the user clicking on "Cancel" and clicking on "X", since
|
differentiate between the user clicking on "Cancel" and clicking on "X", since
|
||||||
|
@ -76,72 +65,32 @@ class UpdateCheckPrompt(Alert):
|
||||||
return buttons_layout
|
return buttons_layout
|
||||||
|
|
||||||
|
|
||||||
class UpdateReport:
|
|
||||||
"""A report for an update check."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
version: Optional[str] = None,
|
|
||||||
changelog: Optional[str] = None,
|
|
||||||
error: Optional[str] = None,
|
|
||||||
):
|
|
||||||
self.version = version
|
|
||||||
self.changelog = changelog
|
|
||||||
self.error = error
|
|
||||||
|
|
||||||
def empty(self) -> bool:
|
|
||||||
return self.version is None and self.changelog is None and self.error is None
|
|
||||||
|
|
||||||
|
|
||||||
class UpdaterThread(QtCore.QThread):
|
class UpdaterThread(QtCore.QThread):
|
||||||
"""Check asynchronously for Dangerzone updates.
|
"""Check asynchronously for Dangerzone updates.
|
||||||
|
|
||||||
The Updater class is mainly responsible for the following:
|
The Updater class is mainly responsible for
|
||||||
|
asking the user if they want to enable update checks or not.
|
||||||
1. Asking the user if they want to enable update checks or not.
|
|
||||||
2. Determining when it's the right time to check for updates.
|
|
||||||
3. Hitting the GitHub releases API and learning about updates.
|
|
||||||
|
|
||||||
Since checking for updates is a task that may take some time, we perform it
|
Since checking for updates is a task that may take some time, we perform it
|
||||||
asynchronously, in a Qt thread. This thread then triggers a signal, and informs
|
asynchronously, in a Qt thread.
|
||||||
whoever has connected to it.
|
|
||||||
|
When finished, this thread triggers a signal with the results.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
finished = QtCore.Signal(UpdateReport)
|
finished = QtCore.Signal(releases.UpdateReport)
|
||||||
|
|
||||||
GH_RELEASE_URL = (
|
|
||||||
"https://api.github.com/repos/freedomofpress/dangerzone/releases/latest"
|
|
||||||
)
|
|
||||||
REQ_TIMEOUT = 15
|
|
||||||
|
|
||||||
def __init__(self, dangerzone: DangerzoneGui):
|
def __init__(self, dangerzone: DangerzoneGui):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.dangerzone = dangerzone
|
self.dangerzone = dangerzone
|
||||||
|
|
||||||
###########
|
|
||||||
# Helpers for updater settings
|
|
||||||
#
|
|
||||||
# These helpers make it easy to retrieve specific updater-related settings, as well
|
|
||||||
# as save the settings file, only when necessary.
|
|
||||||
|
|
||||||
@property
|
|
||||||
def check(self) -> Optional[bool]:
|
|
||||||
return self.dangerzone.settings.get("updater_check")
|
|
||||||
|
|
||||||
@check.setter
|
|
||||||
def check(self, val: bool) -> None:
|
|
||||||
self.dangerzone.settings.set("updater_check", val, autosave=True)
|
|
||||||
|
|
||||||
def prompt_for_checks(self) -> Optional[bool]:
|
def prompt_for_checks(self) -> Optional[bool]:
|
||||||
"""Ask the user if they want to be informed about Dangerzone updates."""
|
"""Ask the user if they want to be informed about Dangerzone updates."""
|
||||||
log.debug("Prompting the user for update checks")
|
log.debug("Prompting the user for update checks")
|
||||||
# FIXME: Handle the case where a user clicks on "X", instead of explicitly
|
|
||||||
# making a choice. We should probably ask them again on the next run.
|
|
||||||
prompt = UpdateCheckPrompt(
|
prompt = UpdateCheckPrompt(
|
||||||
self.dangerzone,
|
self.dangerzone,
|
||||||
message=MSG_CONFIRM_UPDATE_CHECKS,
|
message=MSG_CONFIRM_UPDATE_CHECKS,
|
||||||
ok_text="Check Automatically",
|
ok_text="Enable sandbox updates",
|
||||||
cancel_text="Don't Check",
|
cancel_text="Do not make any requests",
|
||||||
)
|
)
|
||||||
check = prompt.launch()
|
check = prompt.launch()
|
||||||
if not check and prompt.x_pressed:
|
if not check and prompt.x_pressed:
|
||||||
|
@ -149,167 +98,18 @@ class UpdaterThread(QtCore.QThread):
|
||||||
return bool(check)
|
return bool(check)
|
||||||
|
|
||||||
def should_check_for_updates(self) -> bool:
|
def should_check_for_updates(self) -> bool:
|
||||||
"""Determine if we can check for updates based on settings and user prefs.
|
|
||||||
|
|
||||||
Note that this method only checks if the user has expressed an interest for
|
|
||||||
learning about new updates, and not whether we should actually make an update
|
|
||||||
check. Those two things are distinct, actually. For example:
|
|
||||||
|
|
||||||
* A user may have expressed that they want to learn about new updates.
|
|
||||||
* A previous update check may have found out that there's a new version out.
|
|
||||||
* Thus we will always show to the user the cached info about the new version,
|
|
||||||
and won't make a new update check.
|
|
||||||
"""
|
|
||||||
log.debug("Checking platform type")
|
|
||||||
# TODO: Disable updates for Homebrew installations.
|
|
||||||
if platform.system() == "Linux" and not getattr(sys, "dangerzone_dev", False):
|
|
||||||
log.debug("Running on Linux, disabling updates")
|
|
||||||
if not self.check: # if not overidden by user
|
|
||||||
self.check = False
|
|
||||||
return False
|
|
||||||
|
|
||||||
log.debug("Checking if first run of Dangerzone")
|
|
||||||
if self.dangerzone.settings.get("updater_last_check") is None:
|
|
||||||
log.debug("Dangerzone is running for the first time, updates are stalled")
|
|
||||||
self.dangerzone.settings.set("updater_last_check", 0, autosave=True)
|
|
||||||
return False
|
|
||||||
|
|
||||||
log.debug("Checking if user has already expressed their preference")
|
|
||||||
if self.check is None:
|
|
||||||
log.debug("User has not been asked yet for update checks")
|
|
||||||
self.check = self.prompt_for_checks()
|
|
||||||
return bool(self.check)
|
|
||||||
elif not self.check:
|
|
||||||
log.debug("User has expressed that they don't want to check for updates")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def can_update(self, cur_version: str, latest_version: str) -> bool:
|
|
||||||
if version.parse(cur_version) == version.parse(latest_version):
|
|
||||||
return False
|
|
||||||
elif version.parse(cur_version) > version.parse(latest_version):
|
|
||||||
# FIXME: This is a sanity check, but we should improve its wording.
|
|
||||||
raise Exception("Received version is older than the latest version")
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _get_now_timestamp(self) -> int:
|
|
||||||
return int(time.time())
|
|
||||||
|
|
||||||
def _should_postpone_update_check(self) -> bool:
|
|
||||||
"""Consult and update cooldown timer.
|
|
||||||
|
|
||||||
If the previous check happened before the cooldown period expires, do not check
|
|
||||||
again.
|
|
||||||
"""
|
|
||||||
current_time = self._get_now_timestamp()
|
|
||||||
last_check = self.dangerzone.settings.get("updater_last_check")
|
|
||||||
if current_time < last_check + UPDATE_CHECK_COOLDOWN_SECS:
|
|
||||||
log.debug("Cooling down update checks")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_latest_info(self) -> UpdateReport:
|
|
||||||
"""Get the latest release info from GitHub.
|
|
||||||
|
|
||||||
Also, render the changelog from Markdown format to HTML, so that we can show it
|
|
||||||
to the users.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
res = requests.get(self.GH_RELEASE_URL, timeout=self.REQ_TIMEOUT)
|
should_check: Optional[bool] = releases.should_check_for_releases(
|
||||||
except Exception as e:
|
self.dangerzone.settings
|
||||||
raise RuntimeError(
|
|
||||||
f"Encountered an exception while checking {self.GH_RELEASE_URL}: {e}"
|
|
||||||
)
|
)
|
||||||
|
except errors.NeedUserInput:
|
||||||
if res.status_code != 200:
|
should_check = self.prompt_for_checks()
|
||||||
raise RuntimeError(
|
if should_check is not None:
|
||||||
f"Encountered an HTTP {res.status_code} error while checking"
|
self.dangerzone.settings.set(
|
||||||
f" {self.GH_RELEASE_URL}"
|
"updater_check", should_check, autosave=True
|
||||||
)
|
)
|
||||||
|
return bool(should_check)
|
||||||
try:
|
|
||||||
info = res.json()
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
raise ValueError(f"Received a non-JSON response from {self.GH_RELEASE_URL}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
version = info["tag_name"].lstrip("v")
|
|
||||||
changelog = markdown.markdown(info["body"])
|
|
||||||
except KeyError:
|
|
||||||
raise ValueError(
|
|
||||||
f"Missing required fields in JSON response from {self.GH_RELEASE_URL}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return UpdateReport(version=version, changelog=changelog)
|
|
||||||
|
|
||||||
# XXX: This happens in parallel with other tasks. DO NOT alter global state!
|
|
||||||
def _check_for_updates(self) -> UpdateReport:
|
|
||||||
"""Check for updates locally and remotely.
|
|
||||||
|
|
||||||
Check for updates in two places:
|
|
||||||
|
|
||||||
1. In our settings, in case we have cached the latest version/changelog from a
|
|
||||||
previous run.
|
|
||||||
2. In GitHub, by hitting the latest releases API.
|
|
||||||
"""
|
|
||||||
log.debug("Checking for Dangerzone updates")
|
|
||||||
latest_version = self.dangerzone.settings.get("updater_latest_version")
|
|
||||||
if version.parse(get_version()) < version.parse(latest_version):
|
|
||||||
log.debug("Determined that there is an update due to cached results")
|
|
||||||
return UpdateReport(
|
|
||||||
version=latest_version,
|
|
||||||
changelog=self.dangerzone.settings.get("updater_latest_changelog"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# If the previous check happened before the cooldown period expires, do not
|
|
||||||
# check again. Else, bump the last check timestamp, before making the actual
|
|
||||||
# check. This is to ensure that even failed update checks respect the cooldown
|
|
||||||
# period.
|
|
||||||
if self._should_postpone_update_check():
|
|
||||||
return UpdateReport()
|
|
||||||
else:
|
|
||||||
self.dangerzone.settings.set(
|
|
||||||
"updater_last_check", self._get_now_timestamp(), autosave=True
|
|
||||||
)
|
|
||||||
|
|
||||||
log.debug("Checking the latest GitHub release")
|
|
||||||
report = self.get_latest_info()
|
|
||||||
log.debug(f"Latest version in GitHub is {report.version}")
|
|
||||||
if report.version and self.can_update(latest_version, report.version):
|
|
||||||
log.debug(
|
|
||||||
f"Determined that there is an update due to a new GitHub version:"
|
|
||||||
f" {latest_version} < {report.version}"
|
|
||||||
)
|
|
||||||
return report
|
|
||||||
|
|
||||||
log.debug("No need to update")
|
|
||||||
return UpdateReport()
|
|
||||||
|
|
||||||
##################
|
|
||||||
# Logic for running update checks asynchronously
|
|
||||||
|
|
||||||
def check_for_updates(self) -> UpdateReport:
|
|
||||||
"""Check for updates and return a report with the findings:
|
|
||||||
|
|
||||||
There are three scenarios when we check for updates, and each scenario returns a
|
|
||||||
slightly different answer:
|
|
||||||
|
|
||||||
1. No new updates: Return an empty update report.
|
|
||||||
2. Updates are available: Return an update report with the latest version and
|
|
||||||
changelog, in HTML format.
|
|
||||||
3. Update check failed: Return an update report that holds just the error
|
|
||||||
message.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
res = self._check_for_updates()
|
|
||||||
except Exception as e:
|
|
||||||
log.exception("Encountered an error while checking for upgrades")
|
|
||||||
res = UpdateReport(error=str(e))
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
self.finished.emit(self.check_for_updates())
|
has_updates = releases.check_for_updates(self.dangerzone.settings)
|
||||||
|
self.finished.emit(has_updates)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
from typing import TYPE_CHECKING, Any, Dict
|
from typing import TYPE_CHECKING, Any, Dict
|
||||||
|
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
|
@ -56,3 +56,9 @@ class CosignNotInstalledError(SignatureError):
|
||||||
|
|
||||||
class InvalidLogIndex(SignatureError):
|
class InvalidLogIndex(SignatureError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NeedUserInput(UpdaterError):
|
||||||
|
"""The user has not yet been prompted to know if they want to check for updates."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
191
dangerzone/updater/releases.py
Normal file
191
dangerzone/updater/releases.py
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
import json
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import markdown
|
||||||
|
import requests
|
||||||
|
from packaging import version
|
||||||
|
|
||||||
|
from .. import util
|
||||||
|
from ..settings import Settings
|
||||||
|
from . import errors, log
|
||||||
|
|
||||||
|
# Check for updates at most every 12 hours.
|
||||||
|
UPDATE_CHECK_COOLDOWN_SECS = 60 * 60 * 12
|
||||||
|
|
||||||
|
GH_RELEASE_URL = (
|
||||||
|
"https://api.github.com/repos/freedomofpress/dangerzone/releases/latest"
|
||||||
|
)
|
||||||
|
REQ_TIMEOUT = 15
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateReport:
|
||||||
|
"""A report for an update check."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
version: Optional[str] = None,
|
||||||
|
changelog: Optional[str] = None,
|
||||||
|
error: Optional[str] = None,
|
||||||
|
):
|
||||||
|
self.version = version
|
||||||
|
self.changelog = changelog
|
||||||
|
self.error = error
|
||||||
|
|
||||||
|
def empty(self) -> bool:
|
||||||
|
return self.version is None and self.changelog is None and self.error is None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_now_timestamp() -> int:
|
||||||
|
return int(time.time())
|
||||||
|
|
||||||
|
|
||||||
|
def _should_postpone_update_check(settings) -> bool:
|
||||||
|
"""Consult and update cooldown timer.
|
||||||
|
|
||||||
|
If the previous check happened before the cooldown period expires, do not check
|
||||||
|
again.
|
||||||
|
"""
|
||||||
|
current_time = _get_now_timestamp()
|
||||||
|
last_check = settings.get("updater_last_check")
|
||||||
|
if current_time < last_check + UPDATE_CHECK_COOLDOWN_SECS:
|
||||||
|
log.debug("Cooling down update checks")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_sane_update(cur_version: str, latest_version: str) -> bool:
|
||||||
|
if version.parse(cur_version) == version.parse(latest_version):
|
||||||
|
return False
|
||||||
|
elif version.parse(cur_version) > version.parse(latest_version):
|
||||||
|
# FIXME: This is a sanity check, but we should improve its wording.
|
||||||
|
raise Exception("Received version is older than the latest version")
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_release_info() -> UpdateReport:
|
||||||
|
"""Get the latest release info from GitHub.
|
||||||
|
|
||||||
|
Also, render the changelog from Markdown format to HTML, so that we can show it
|
||||||
|
to the users.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
res = requests.get(GH_RELEASE_URL, timeout=REQ_TIMEOUT)
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Encountered an exception while checking {GH_RELEASE_URL}: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if res.status_code != 200:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Encountered an HTTP {res.status_code} error while checking"
|
||||||
|
f" {GH_RELEASE_URL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
info = res.json()
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
raise ValueError(f"Received a non-JSON response from {GH_RELEASE_URL}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
version = info["tag_name"].lstrip("v")
|
||||||
|
changelog = markdown.markdown(info["body"])
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError(
|
||||||
|
f"Missing required fields in JSON response from {GH_RELEASE_URL}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return UpdateReport(version=version, changelog=changelog)
|
||||||
|
|
||||||
|
|
||||||
|
def should_check_for_releases(settings: Settings) -> bool:
|
||||||
|
"""Determine if we can check for release updates based on settings and user prefs.
|
||||||
|
|
||||||
|
Note that this method only checks if the user has expressed an interest for
|
||||||
|
learning about new updates, and not whether we should actually make an update
|
||||||
|
check. Those two things are distinct, actually. For example:
|
||||||
|
|
||||||
|
* A user may have expressed that they want to learn about new updates.
|
||||||
|
* A previous update check may have found out that there's a new version out.
|
||||||
|
* Thus we will always show to the user the cached info about the new version,
|
||||||
|
and won't make a new update check.
|
||||||
|
"""
|
||||||
|
check = settings.get("updater_check")
|
||||||
|
|
||||||
|
log.debug("Checking platform type")
|
||||||
|
# TODO: Disable updates for Homebrew installations.
|
||||||
|
if platform.system() == "Linux" and not getattr(sys, "dangerzone_dev", False):
|
||||||
|
log.debug("Running on Linux, disabling updates")
|
||||||
|
if not check: # if not overidden by user
|
||||||
|
settings.set("updater_check", False, autosave=True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
log.debug("Checking if first run of Dangerzone")
|
||||||
|
if settings.get("updater_last_check") is None:
|
||||||
|
log.debug("Dangerzone is running for the first time, updates are stalled")
|
||||||
|
settings.set("updater_last_check", 0, autosave=True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
log.debug("Checking if user has already expressed their preference")
|
||||||
|
if check is None:
|
||||||
|
log.debug("User has not been asked yet for update checks")
|
||||||
|
raise errors.NeedUserInput()
|
||||||
|
elif not check:
|
||||||
|
log.debug("User has expressed that they don't want to check for updates")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_updates(settings) -> UpdateReport:
|
||||||
|
"""Check for updates locally and remotely.
|
||||||
|
|
||||||
|
Check for updates (locally and remotely) and return a report with the findings:
|
||||||
|
|
||||||
|
There are three scenarios when we check for updates, and each scenario returns a
|
||||||
|
slightly different answer:
|
||||||
|
|
||||||
|
1. No new updates: Return an empty update report.
|
||||||
|
2. Updates are available: Return an update report with the latest version and
|
||||||
|
changelog, in HTML format.
|
||||||
|
3. Update check failed: Return an update report that holds just the error
|
||||||
|
message.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
log.debug("Checking for Dangerzone updates")
|
||||||
|
latest_version = settings.get("updater_latest_version")
|
||||||
|
if version.parse(util.get_version()) < version.parse(latest_version):
|
||||||
|
log.debug("Determined that there is an update due to cached results")
|
||||||
|
return UpdateReport(
|
||||||
|
version=latest_version,
|
||||||
|
changelog=settings.get("updater_latest_changelog"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# If the previous check happened before the cooldown period expires, do not
|
||||||
|
# check again. Else, bump the last check timestamp, before making the actual
|
||||||
|
# check. This is to ensure that even failed update checks respect the cooldown
|
||||||
|
# period.
|
||||||
|
if _should_postpone_update_check(settings):
|
||||||
|
return UpdateReport()
|
||||||
|
else:
|
||||||
|
settings.set("updater_last_check", _get_now_timestamp(), autosave=True)
|
||||||
|
|
||||||
|
log.debug("Checking the latest GitHub release")
|
||||||
|
report = fetch_release_info()
|
||||||
|
log.debug(f"Latest version in GitHub is {report.version}")
|
||||||
|
if report.version and ensure_sane_update(latest_version, report.version):
|
||||||
|
log.debug(
|
||||||
|
f"Determined that there is an update due to a new GitHub version:"
|
||||||
|
f" {latest_version} < {report.version}"
|
||||||
|
)
|
||||||
|
return report
|
||||||
|
|
||||||
|
log.debug("No need to update")
|
||||||
|
return UpdateReport()
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Encountered an error while checking for upgrades")
|
||||||
|
return UpdateReport(error=str(e))
|
|
@ -70,6 +70,7 @@ def get_tessdata_dir() -> pathlib.Path:
|
||||||
|
|
||||||
|
|
||||||
def get_version() -> str:
|
def get_version() -> str:
|
||||||
|
"""Returns the Dangerzone version string."""
|
||||||
try:
|
try:
|
||||||
with open(get_resource_path("version.txt")) as f:
|
with open(get_resource_path("version.txt")) as f:
|
||||||
version = f.read().strip()
|
version = f.read().strip()
|
||||||
|
|
|
@ -216,12 +216,6 @@ convert the documents within a secure sandbox.
|
||||||
%prep
|
%prep
|
||||||
%autosetup -p1 -n dangerzone-%{version}
|
%autosetup -p1 -n dangerzone-%{version}
|
||||||
|
|
||||||
# Bypass the version pin for Fedora as the 6.8.1.1 package is causing trouble
|
|
||||||
# A 6.8.1.1 package was only released with a wheel for macOS, but was picked by
|
|
||||||
# Fedora packagers. We cannot use "*" when PyPI is involved as it will fail to download the latest version.
|
|
||||||
# For Fedora, we can pick any of the released versions.
|
|
||||||
sed -i '/shiboken6 = \[/,/\]/c\shiboken6 = "*"' pyproject.toml
|
|
||||||
|
|
||||||
%generate_buildrequires
|
%generate_buildrequires
|
||||||
%pyproject_buildrequires -R
|
%pyproject_buildrequires -R
|
||||||
|
|
||||||
|
|
560
poetry.lock
generated
560
poetry.lock
generated
|
@ -35,13 +35,13 @@ trio = ["trio (>=0.26.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2024.12.14"
|
version = "2025.1.31"
|
||||||
description = "Python package for providing Mozilla's CA Bundle."
|
description = "Python package for providing Mozilla's CA Bundle."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
{file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"},
|
{file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
|
||||||
{file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"},
|
{file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -183,73 +183,74 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "coverage"
|
name = "coverage"
|
||||||
version = "7.6.10"
|
version = "7.6.12"
|
||||||
description = "Code coverage measurement for Python"
|
description = "Code coverage measurement for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"},
|
{file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"},
|
{file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"},
|
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"},
|
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"},
|
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"},
|
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"},
|
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"},
|
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"},
|
{file = "coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425"},
|
||||||
{file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"},
|
{file = "coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"},
|
{file = "coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"},
|
{file = "coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"},
|
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"},
|
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"},
|
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"},
|
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"},
|
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"},
|
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"},
|
{file = "coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f"},
|
||||||
{file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"},
|
{file = "coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"},
|
{file = "coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"},
|
{file = "coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"},
|
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"},
|
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"},
|
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"},
|
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"},
|
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"},
|
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"},
|
{file = "coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95"},
|
||||||
{file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"},
|
{file = "coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"},
|
{file = "coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"},
|
{file = "coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"},
|
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"},
|
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"},
|
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"},
|
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"},
|
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"},
|
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"},
|
{file = "coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"},
|
{file = "coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"},
|
{file = "coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"},
|
{file = "coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"},
|
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"},
|
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"},
|
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"},
|
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"},
|
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"},
|
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"},
|
{file = "coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3"},
|
||||||
{file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"},
|
{file = "coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"},
|
{file = "coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"},
|
{file = "coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"},
|
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"},
|
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"},
|
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"},
|
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"},
|
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"},
|
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"},
|
{file = "coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31"},
|
||||||
{file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"},
|
{file = "coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57"},
|
||||||
{file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"},
|
{file = "coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf"},
|
||||||
{file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"},
|
{file = "coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953"},
|
||||||
|
{file = "coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -260,49 +261,52 @@ toml = ["tomli"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cx-freeze"
|
name = "cx-freeze"
|
||||||
version = "7.2.8"
|
version = "7.2.10"
|
||||||
description = "Create standalone executables from Python scripts"
|
description = "Create standalone executables from Python scripts"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "cx_Freeze-7.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a26a27643f07fd3dca5b07c231bc037e9a7660ef3e80b75aa884360747e127e6"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:79dd0c1dc4d8d7369ea0288deccffcfed27a5ff3d11448fff5aa217b4ce83db2"},
|
||||||
{file = "cx_Freeze-7.2.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1b01483ad092a69d9e61406e96de6c9047ba761eb7c47a25e71a5b03ba54fa"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27a6e99c0ab5efb8303ba1f6f89df141c0b23f81cb18309981bd0bd6d03e75e1"},
|
||||||
{file = "cx_Freeze-7.2.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28354eeba38efab82711deaf5dbec4ce1981dd3643aa023733ddcac3811ce975"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8017e6798edca460469b6048db231a7cbb8d7bbd4aa2bf17464037a0aa202e00"},
|
||||||
{file = "cx_Freeze-7.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3675e33cd6fc2be4df9804e814e325bb8ed17c6ce4cac8fb66010ff96d1e1b6"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7e4cd5406e6124700f4bc533964477e00e0c44bfcfbf4c06c6a7f7aca5b25b1"},
|
||||||
{file = "cx_Freeze-7.2.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99d304d8bbbb9594ae7e6cef25551b970129262e8e8d91d659f69097496206d3"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f46c24a0a2d093a2319e4fe2b5a82cafd08de426ac621b54cacae398f1df1f6"},
|
||||||
{file = "cx_Freeze-7.2.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:03abbddb3e3cc5b2316a5eca3a0ebf257e68edbd7275c66c027a27511f3b1e39"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:475ba16c7f43e0c0ee62ff4ba49bfcd67faec639683c64f6d2534e0f68de47f4"},
|
||||||
{file = "cx_Freeze-7.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:7c208db7ef636cc5bd8eb7765ac752d3f17a093d612c348412accb2ad865de6d"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-win32.whl", hash = "sha256:86350e2a74edbfe89fda14724012eeec85329e5cc3b0cd91df7a47954742b009"},
|
||||||
{file = "cx_Freeze-7.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:48b83603238c2b92960c8d69be4bdc6d4ff730377ff0a7d5e11db49ab9136701"},
|
{file = "cx_Freeze-7.2.10-cp310-cp310-win_amd64.whl", hash = "sha256:7eb32b0c0727d108ce30fc710e9bdbdec6fb408361fcc0cd04a65baf679c1584"},
|
||||||
{file = "cx_Freeze-7.2.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dc38c90ce04224d0f0cda71bc715e5274093426bac7310a5e5e116bb16f991d"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5935a74def174c70c2c84036ede420ed519f2749c87aa692ac21676c2560b72e"},
|
||||||
{file = "cx_Freeze-7.2.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39a3b3fabd2d27ed6370c4311090b8e750816b2d2fd3baa6f2597801e1bcc672"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bc61c1c2d874f883bb7aae724abe84e3a36d97b679fd02d2bf33313afbbc975"},
|
||||||
{file = "cx_Freeze-7.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad6cb3a2e99563ae2493f99b740923ee04a916ef5034f5267cc6e18040bb32d"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:572d0e98e75fcd3f6947dafc86b2035fe80d604662467bf8634e5c67a00e6e8f"},
|
||||||
{file = "cx_Freeze-7.2.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:09dd8b97dd729247eef20d1e0e3df56353c9677e5be3b06d0207f553323f78ce"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71480bda16143c2b6177a491bbf3b9fa7e540bcf7c7afeb9bac828aa2ba6b5a4"},
|
||||||
{file = "cx_Freeze-7.2.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e548a86e26e97d3cc7fc7b1ae981ba7f6e2f67b7858d41f0f83da64d229659eb"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ecdb1941cb5ffca9acaa24ab1618cf14528687297a74e9257ee223062883b7d4"},
|
||||||
{file = "cx_Freeze-7.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:f8b3790533cabf5bec645cfd1228e5d66af73d4b56d1e9d875db97375cb384e9"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:84d531b5d669419b5ef95c68cb53d368a813010a82ba79fe487cf33b3972529b"},
|
||||||
{file = "cx_Freeze-7.2.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:032003e7c915d2cdf837c2f4f69b0a113c393325931dda2bd491c4138ce03952"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-win32.whl", hash = "sha256:c2edd45e1ecb244ff686720c4e3501d8c28e049f0fdb05ab064d2587375ef2d1"},
|
||||||
{file = "cx_Freeze-7.2.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acb2263c0d82e98d239494d250822521a4392c8cfbc30e95e76a7b23e50b6bc7"},
|
{file = "cx_Freeze-7.2.10-cp311-cp311-win_amd64.whl", hash = "sha256:4e121908ce11a85aed94fed58af93d0598c31844a66a164c8c62ed720eee34cf"},
|
||||||
{file = "cx_Freeze-7.2.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:694c339a4a7f831dd515190f747a76aca57af6a619e057b64f02e9198b4cc454"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4a908dcad86888b741aa2051a65693d8662999a8abf9b7cf3285ef4ccfddad43"},
|
||||||
{file = "cx_Freeze-7.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56e89f1bdf1340b2874c1235643e104a0e394c35657f5b51c65f51ef60a18e7e"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:416460e7f3312b1e2010ce5cb35e0449ff5a6e75b3536adddf83fd9b10d13a88"},
|
||||||
{file = "cx_Freeze-7.2.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01e9ee99c102c567c20fb4f8568944da9a5661c53298f96c507cd1a8f9a70350"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:851a0d919a732d17e1db145c72d69b5560ab1578387e18915a9e9f0be1bc50fd"},
|
||||||
{file = "cx_Freeze-7.2.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:26dfebbbf689712b24c5f6cd2764a4c393d13fdbfde8ff32df601ee0be59aea1"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e251c74b2551668ae890d4c7a2fbb73a6c9b692de4096ddd9867322873e11b2b"},
|
||||||
{file = "cx_Freeze-7.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:c4c43cb6433455fefd4b92c79ef567276bc79aa182172f5550095aaa4e2210e8"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7f5d65830cb3b0d24590e4526995baf01beeb2517c41838d597c5a2e4ca76d7"},
|
||||||
{file = "cx_Freeze-7.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:866dc06ffc02a38389d9e05549a6ede528eb9c7febbac69f47369a7457473666"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d00914208a2953125398d192d1c332eea2bfb4f210a1507efe5188fec18fb8e3"},
|
||||||
{file = "cx_Freeze-7.2.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a9522a8cf51f71c9eb0c7954b5b387f7325c34a6ac7f6b3a7ca016661d3aa24"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-win32.whl", hash = "sha256:c0af82ba512323a1036a42c2e863ea56e12764c8c9f0764303df1fa3e29b9d4a"},
|
||||||
{file = "cx_Freeze-7.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:752c8dff0c04c0fdbbd24400e8d360faf91e6a8c1ae41a86a8ea8fc7305c0cee"},
|
{file = "cx_Freeze-7.2.10-cp312-cp312-win_amd64.whl", hash = "sha256:15b7dafc5eb2b5f6c118eaeed25b0fb0f3a9022e779958e5f9cd1ebf9f77c737"},
|
||||||
{file = "cx_Freeze-7.2.8-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0d9d3364fa46ee3729c8aa7628e6a22e30d6c2c5f2dd7f98fc7356f138dea3ad"},
|
{file = "cx_Freeze-7.2.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ef8195412e71268ba6de1809d1fe64fad7bc4c2d2ff22eaff50c66317a94f4cf"},
|
||||||
{file = "cx_Freeze-7.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:8fe30c9a3441a3408ca9e35c7df0704663632d1169863718dbfee58fcc408af1"},
|
{file = "cx_Freeze-7.2.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f04d4ee6f5efa5223313337ee0cfbf16e385dcd8b2797ab6192ab224b781031"},
|
||||||
{file = "cx_Freeze-7.2.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6f035d07931ccc89e4fadbbc44da4da791a06cea6da0b9d904c6cd528807657b"},
|
{file = "cx_Freeze-7.2.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eefc22d51bc9be6e18bde5aaf15008b118771689036fc8817cec3317eca4fff1"},
|
||||||
{file = "cx_Freeze-7.2.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935b1abd87cfab1076c8f509c5b2843e2c2ae750f5c13df328e972e8656599b0"},
|
{file = "cx_Freeze-7.2.10-cp38-cp38-win32.whl", hash = "sha256:4309655f33bc9893f58f27618be76f46d66cc31997040ae1a3ec718159983ad5"},
|
||||||
{file = "cx_Freeze-7.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec9a24080ce28adcc8e5601a10e5aeed1c74ec6f67a6bd59276ee03509071f8e"},
|
{file = "cx_Freeze-7.2.10-cp38-cp38-win_amd64.whl", hash = "sha256:ae9cdf73f4200bb4b9197e12d2a97f466f058f040fbd8924fd4ee6fbaf53d0a8"},
|
||||||
{file = "cx_Freeze-7.2.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:26c098a50b537d9f3bdbcadab1bc7434a38b0e4066af9f8dbed674367b280d77"},
|
{file = "cx_Freeze-7.2.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:105dfc3b15c4f8ff78607c892b73e5a48a29ccd87836284d34611031ca74f6d9"},
|
||||||
{file = "cx_Freeze-7.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:a4abaa58990ad31ab77abaca0f1c51c294059bf9bd00a88e56ac0143570802b6"},
|
{file = "cx_Freeze-7.2.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85a337dfb8b2e29213beef36592ac2ad3789911c9aa68e26e0b500914f31ef7d"},
|
||||||
{file = "cx_freeze-7.2.8.tar.gz", hash = "sha256:678fe5b074c1511918eded7b3cbe542ea57b4ee292645cdaceb9c691ce13293e"},
|
{file = "cx_Freeze-7.2.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff17dd57a972a49d03b95b42476b5909dfda7e94c5fa67ff2def1fae0619881"},
|
||||||
|
{file = "cx_Freeze-7.2.10-cp39-cp39-win32.whl", hash = "sha256:916f0512c42b00c63f1365f7321df8ad76724065f87b858c7b5839adbc472a0f"},
|
||||||
|
{file = "cx_Freeze-7.2.10-cp39-cp39-win_amd64.whl", hash = "sha256:e530625e979a8ac1fcb4421dfdc84607c0d3bd8c610f6e8983eb2925068c006e"},
|
||||||
|
{file = "cx_freeze-7.2.10.tar.gz", hash = "sha256:16a6eb37db7d158d0eb4bba8b93a135b6c85ea4bf2c4176c437bc34f01067e61"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
cx_Logging = {version = ">=3.1", markers = "sys_platform == \"win32\""}
|
cx_Logging = {version = ">=3.1", markers = "sys_platform == \"win32\""}
|
||||||
importlib_metadata = {version = ">=6", markers = "python_full_version < \"3.10.2\""}
|
importlib_metadata = {version = ">=6", markers = "python_full_version < \"3.10.2\""}
|
||||||
lief = {version = ">=0.12.0,<0.17.0", markers = "sys_platform == \"win32\""}
|
lief = {version = ">=0.13.2,<=0.16.3", markers = "sys_platform == \"win32\""}
|
||||||
packaging = ">=24"
|
packaging = ">=24"
|
||||||
setuptools = ">=65.6.3,<76"
|
setuptools = ">=65.6.3,<76"
|
||||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||||
|
@ -310,7 +314,7 @@ typing_extensions = {version = ">=4.10.0", markers = "python_version < \"3.10\""
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["bump-my-version (==0.29.0)", "cibuildwheel (==2.22.0)", "pre-commit (>=3.5.0,<=3.8.0)"]
|
dev = ["bump-my-version (==0.29.0)", "cibuildwheel (==2.22.0)", "pre-commit (>=3.5.0,<=3.8.0)"]
|
||||||
doc = ["furo (==2024.8.6)", "myst-parser (>=3.0.1,<=4.0.0)", "sphinx (>=7.1.2,<8)", "sphinx-new-tab-link (==0.6.1)", "sphinx-tabs (==3.4.7)"]
|
doc = ["furo (==2024.8.6)", "myst-parser (>=3.0.1,<=4.0.0)", "sphinx (>=7.1.2,<8)", "sphinx-new-tab-link (>=0.6.0)", "sphinx-tabs (==3.4.7)"]
|
||||||
tests = ["coverage (>=7.6.1)", "pluggy (==1.5.0)", "pytest (==8.3.4)", "pytest-cov (==5.0.0)", "pytest-datafiles (==3.0.0)", "pytest-mock (==3.14.0)", "pytest-timeout (==2.3.1)", "pytest-xdist (==3.6.1)"]
|
tests = ["coverage (>=7.6.1)", "pluggy (==1.5.0)", "pytest (==8.3.4)", "pytest-cov (==5.0.0)", "pytest-datafiles (==3.0.0)", "pytest-mock (==3.14.0)", "pytest-timeout (==2.3.1)", "pytest-xdist (==3.6.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -519,53 +523,53 @@ yaml = ["jinja2", "pyyaml"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lief"
|
name = "lief"
|
||||||
version = "0.16.2"
|
version = "0.16.3"
|
||||||
description = "Library to instrument executable formats"
|
description = "Library to instrument executable formats"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "lief-0.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:485934b69ecc0b4caf0224f270b05870f5e3db9d5978d75fab39982e01d81080"},
|
{file = "lief-0.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0fca20122c27a86efb5d083fef6514fb2fbd910965654cb8568f2db8dfe2678f"},
|
||||||
{file = "lief-0.16.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:c60a0c497de2369c5c10dc8388085c1a6ad2ac03036cf79295c79cd94392ed47"},
|
{file = "lief-0.16.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:17e78fc2790fd4ebd15cf9fd86abf0d7fa91aa229d70707f0bc0391ba522129c"},
|
||||||
{file = "lief-0.16.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:1ede4cf71ff9b25750340877ba016bb96a0f5bfb860c410774129e9d52fe3580"},
|
{file = "lief-0.16.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:6bd8fe4d8b907cd7e024789ba3070e417e9bff50ac13698b43b4d992f30f32d2"},
|
||||||
{file = "lief-0.16.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:94434ebbffd26828643fa2a5e59755cd8632c9c90fff859929e8dc58057110e6"},
|
{file = "lief-0.16.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d3811e76a2da6e5e351cc2dd09ea34c1fc30e5dd2d6cbbfcd5344dfeb39e0119"},
|
||||||
{file = "lief-0.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1a70b4eda4508cc353f962c83cf3615f3435d3681a986c45d0e8aa0c347b7c26"},
|
{file = "lief-0.16.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:978469619f5e8c3faa5bcbb94a89df49565a427b2e75267924f76a7f42cd2a0f"},
|
||||||
{file = "lief-0.16.2-cp310-cp310-win32.whl", hash = "sha256:0b12a10bdac32b9111e2fc2ca2a38257250c9d134a360d3294d8a0e79c878654"},
|
{file = "lief-0.16.3-cp310-cp310-win32.whl", hash = "sha256:9e6cf12c2e032e61f8a60512877b3408cf7c0bc8b76f6bc3e830435397a6555d"},
|
||||||
{file = "lief-0.16.2-cp310-cp310-win_amd64.whl", hash = "sha256:0602df0b41fa468896c4d0a455e06f0aef6224be76cf7b7c4a3e349f5a1a4a3d"},
|
{file = "lief-0.16.3-cp310-cp310-win_amd64.whl", hash = "sha256:6b4370508c8b82173e961372310e9c3d410c314cb60dadd80f2acb1a20197265"},
|
||||||
{file = "lief-0.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:41b8c2f64d74ee85ca96af67347cbdd92c5562b5500b9be9882988f0c4134335"},
|
{file = "lief-0.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edf5e1479195920b654e3e1eb6863d466a67fd1bbd2ecc7dcbf2eeeb05353a0c"},
|
||||||
{file = "lief-0.16.2-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:f2284379d84d414150c7e935e70ef06d72298a28f9fda025daaf0698fcea4dcd"},
|
{file = "lief-0.16.3-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:988889a7e837d12f400011bf6fb91197a94abda51e2e7c135e31ba09b032c718"},
|
||||||
{file = "lief-0.16.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:ed3e8122c6d3f845ba4f8a3135dc53fd796156de14d8dbdc3ba310ebbeae6b69"},
|
{file = "lief-0.16.3-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:789bf8fd6cf64fe678b4273797e882c0bd81b702f75c3775c9f175225e1ecad7"},
|
||||||
{file = "lief-0.16.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:3b11d0f59bbea089872a433993e8b4078d8138939e9bd1f294ca0238b1d3fb47"},
|
{file = "lief-0.16.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:73a7bcaf2c2d1819e2c46b3548d29e8bc2c0547be30beb8394ea58c19afa6cab"},
|
||||||
{file = "lief-0.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0c2d766481bd0dbdaa6c4c0a20e5da8d44ad163b5120c9db91ecea92661fda01"},
|
{file = "lief-0.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:be2e7074c8bf0c10fcb1afd8dedb404c86c700123a856208d03ce9dd018392d5"},
|
||||||
{file = "lief-0.16.2-cp311-cp311-win32.whl", hash = "sha256:9b977e0709beb658898025b6a21dc5f59ce484340473fa0b69ce9659400d3ac3"},
|
{file = "lief-0.16.3-cp311-cp311-win32.whl", hash = "sha256:8fafd992eb9dfca9d8e39e4b4218682bcbf60fd88f43bf198ce8cb20a6674b2e"},
|
||||||
{file = "lief-0.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:ea1ebaa74bc021f7881aa8e67875e3736526f039c7811f92aa1fe8a4520224d8"},
|
{file = "lief-0.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:4eb179e9a34a37edbe72c80c0ef7a93cd9ebee4e8fe27165f96841a9d00e1adb"},
|
||||||
{file = "lief-0.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e9499516d469ef5a62afeb0ab404ba7c30c05ec197a1775ddb9cdcbbfddd2e6"},
|
{file = "lief-0.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:17a57cc7accb27ef84a2af395dfcd1ff5c1dabb27a90fda269327678a18a22f0"},
|
||||||
{file = "lief-0.16.2-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b96a5e4656ca2f929b4d7aa631661de47de683eb2c623d039105b5727811fdff"},
|
{file = "lief-0.16.3-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:1e79e7ad2bd822c19303b722e5239976521bc1777a2ddadccdc65db68eb5088d"},
|
||||||
{file = "lief-0.16.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:220ff69e5c0bae8bc114120c7a49485962c58e932a26bb0e9746a17ebfda552d"},
|
{file = "lief-0.16.3-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:c2b205a5f2bb7c2e355ca5ccc71801774af1d40758fa1e338f72678367321efe"},
|
||||||
{file = "lief-0.16.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0b7e4e55ded9897867930a15a553dd29e633b4fbf4b5db15ee1a4e0b8b35f469"},
|
{file = "lief-0.16.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:684f49352123230603369eea032a49f7fe0992624c5ba2120edbd62d974893be"},
|
||||||
{file = "lief-0.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4dbc93b5781c9265f682345c07fa817e46a3d89893a597e4b10c4411a8d3f5a5"},
|
{file = "lief-0.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bf85ff0b4f1c70c3a37c9be5a38c213c84cef6691083fd1ffa6980a4a0d5a410"},
|
||||||
{file = "lief-0.16.2-cp312-cp312-win32.whl", hash = "sha256:2d0352eb57cf9876e37ff84e805cf015455bcae5d4da3c1ba310970a6929ecd6"},
|
{file = "lief-0.16.3-cp312-cp312-win32.whl", hash = "sha256:e7ba797829584c5cc1c8a736b2f1587f09b1f3030239c968c7664649fb79ae15"},
|
||||||
{file = "lief-0.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:5d57f763429f6c2ce1a8e6928035809b7ffc29d794928d22f41a157a78a51c72"},
|
{file = "lief-0.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:70c1f5f66bd4eeead2853a7a80d941b4dd03e3791c68c5351b0d39c78cfa9afe"},
|
||||||
{file = "lief-0.16.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5a32212a9ff4e20a1dcd091ca136e871cccd8a37932df2415e9d3c9debe9908a"},
|
{file = "lief-0.16.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d09183fb4db9dd1534eecc8b36b714bcddf2831cd6c56499b10346e48b2a77"},
|
||||||
{file = "lief-0.16.2-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:bb6bb1284b27e0f013b5cad1163ea87433b338ea3ecec37f7981dc756d77f703"},
|
{file = "lief-0.16.3-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:366205cf382cc246e36d855c7286af6a9e85994fbac47a0a00c206c4f21c998f"},
|
||||||
{file = "lief-0.16.2-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:6f41849881fff72fa26f360353d052cd8d1787b6b05e39f35f78d43520b51435"},
|
{file = "lief-0.16.3-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:6ec906f209e275fd57bc2b003bee6f0e70b9ceabf5d93bcea8de0684735cbe8c"},
|
||||||
{file = "lief-0.16.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:df6c91a1a9d7f3ae53a075ad0165bdefe30d041114cb26baca287e663d76b32a"},
|
{file = "lief-0.16.3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e0b151eccbeda0fe666448c0801022ec5aa92b7383c0a0e8ca586931fd1bfff0"},
|
||||||
{file = "lief-0.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b16db951aeff911d092655ad741d3d4fefca387f3c5ee903255663e15a695ad"},
|
{file = "lief-0.16.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2eb80b1344a009e27702a7ee0ffa7013d792c0d7237e6871fe28210eac8446c0"},
|
||||||
{file = "lief-0.16.2-cp313-cp313-win32.whl", hash = "sha256:d9daf7742cc95f6e22d06c140014bee46b942ee4e7cb3c8ecff95d736233073c"},
|
{file = "lief-0.16.3-cp313-cp313-win32.whl", hash = "sha256:aefbe78b06d9e89387ab8fc069d1cb34252f5916cf35eaa21088b21b74b99d08"},
|
||||||
{file = "lief-0.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:f040368d927ca494309de8742b0b7d1d80e234989f36cffc4587530a073a543e"},
|
{file = "lief-0.16.3-cp313-cp313-win_amd64.whl", hash = "sha256:52dc05445d8019b61a9ab8c6eb9d6238c4346ac692dcecca76d5f329a999216e"},
|
||||||
{file = "lief-0.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:662ab75cd84253990c169e3df04ed3ef9f00e62c1b75f7e13311d26b97e792ed"},
|
{file = "lief-0.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9d7583d11d596afc723b664390d61e1e6d7988b3ad160bfb7438f2cd43099170"},
|
||||||
{file = "lief-0.16.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:ed96b36e3079f3952367b3ca94e981a9072b4d0b50e137d5943360458c972e93"},
|
{file = "lief-0.16.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:bca81d5e2be50925f8e04bb14f02496a14572bb1e326405468afb8a8c11ec508"},
|
||||||
{file = "lief-0.16.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2250a9f88705d9fec1d06f152e2394122f3213d7f44ef098fd9d2412d1e7550a"},
|
{file = "lief-0.16.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:51df4e1c1bc52caa90d5ad63dfd587cd737dde1274f6935bf034f7628c87d5a4"},
|
||||||
{file = "lief-0.16.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:21854699c49f29dc4c6e055f6a960e358b63300dd3ae08e01a8698897a7d59f1"},
|
{file = "lief-0.16.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:b76cf0b8dcce6e3ca88f6e721d471b8ae02192f662c896204285c9561a602e1b"},
|
||||||
{file = "lief-0.16.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:1196109cd92765df3ef4407c0dc31c7fc52b7ff684e4fa127f97dcff06217399"},
|
{file = "lief-0.16.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e924ee22ae6dd5ac660768b46e42ef19bba2a6faf680adcd70aebc536e1ecffb"},
|
||||||
{file = "lief-0.16.2-cp38-cp38-win32.whl", hash = "sha256:ce9f16268dc0576980aba1a6c6bfb00257633668090d172b455b52ca65f2bb26"},
|
{file = "lief-0.16.3-cp38-cp38-win32.whl", hash = "sha256:489a3e77805ebd31f38c9a2786cfddf65c8cca428fa017f7ee38b006143fd3a0"},
|
||||||
{file = "lief-0.16.2-cp38-cp38-win_amd64.whl", hash = "sha256:2d82fb194458dd89c240f32840b65a1a5c76d584d58e7cff932cdcb7f9504a0c"},
|
{file = "lief-0.16.3-cp38-cp38-win_amd64.whl", hash = "sha256:558570ffbb356a8a8f8e2e35cc37edb08ef019ec5fc087f4ed764573071a0901"},
|
||||||
{file = "lief-0.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:229c7ce3d605519d9305a4e704018669a0cc731fcb501c95e01a9a7b79291999"},
|
{file = "lief-0.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb2d0eb59919f3ca8562a8fbae55e4d07194135063f05cfe314c8405cd2d4cce"},
|
||||||
{file = "lief-0.16.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a96e862554ca25fd7083dc0df8b8617fa71426027922f4a2e2073a0104d18aae"},
|
{file = "lief-0.16.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:f18573e16b53ff9626eaa242e8d00a9780107d39a41eb4698c07a965c2267fc9"},
|
||||||
{file = "lief-0.16.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:80d43bc271ef5873bb826d8a71815085f0873870ed1050ddb870222062116e2e"},
|
{file = "lief-0.16.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:6f5155f382f3da85262465817196a3890124c2483b52a2a15acfdc006155296f"},
|
||||||
{file = "lief-0.16.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:9aaf695df03f06c89c2a617279d7043168457465bc14ce1a273a798858881106"},
|
{file = "lief-0.16.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:3a473aa8557df517d7d520235cd785d0a83d543eda0090ca657a7899980695a0"},
|
||||||
{file = "lief-0.16.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ce762fb0c22d70a150ab79351f4eb4c14e24e61cc26c5c5ead9be47059e3a41c"},
|
{file = "lief-0.16.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0b7dc44137c35e4e7c55278369f6148c8893128d694ddce1d28f4822967e5756"},
|
||||||
{file = "lief-0.16.2-cp39-cp39-win32.whl", hash = "sha256:e6cb2933f5758f1af199622f11449c8dcdff4f59cd16c1634a01410895b37fb0"},
|
{file = "lief-0.16.3-cp39-cp39-win32.whl", hash = "sha256:d29480db4cbf212c7deca4b544e4eef6923d1203083518ab0f0f597411e80e51"},
|
||||||
{file = "lief-0.16.2-cp39-cp39-win_amd64.whl", hash = "sha256:7954494d03264da1e76174d98f0a8f233bb272c44165d0302aa6f6014030d9c0"},
|
{file = "lief-0.16.3-cp39-cp39-win_amd64.whl", hash = "sha256:e0ebd06459fad54c5ad9a3caf3e3a7e3010811f51068cbd550c4ae1ee28a9b89"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -672,49 +676,43 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "1.14.1"
|
version = "1.15.0"
|
||||||
description = "Optional static typing for Python"
|
description = "Optional static typing for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"},
|
{file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"},
|
{file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"},
|
{file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"},
|
{file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"},
|
{file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"},
|
||||||
{file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"},
|
{file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"},
|
{file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"},
|
{file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"},
|
{file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"},
|
{file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"},
|
{file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"},
|
||||||
{file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"},
|
{file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"},
|
{file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"},
|
{file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"},
|
{file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"},
|
{file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"},
|
{file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"},
|
||||||
{file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"},
|
{file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"},
|
{file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"},
|
{file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"},
|
{file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"},
|
{file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"},
|
{file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"},
|
||||||
{file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"},
|
{file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"},
|
{file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"},
|
{file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"},
|
{file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"},
|
{file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"},
|
{file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"},
|
||||||
{file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"},
|
{file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"},
|
||||||
{file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"},
|
{file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"},
|
||||||
{file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"},
|
{file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"},
|
||||||
{file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"},
|
|
||||||
{file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"},
|
|
||||||
{file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"},
|
|
||||||
{file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"},
|
|
||||||
{file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"},
|
|
||||||
{file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -784,23 +782,23 @@ testing = ["pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyinstaller"
|
name = "pyinstaller"
|
||||||
version = "6.11.1"
|
version = "6.12.0"
|
||||||
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.14,>=3.8"
|
python-versions = "<3.14,>=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "pyinstaller-6.11.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:44e36172de326af6d4e7663b12f71dbd34e2e3e02233e181e457394423daaf03"},
|
{file = "pyinstaller-6.12.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:68f1e4cecf88a6272063977fa2a2c69ad37cf568e5901769d7206d0314c74f47"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6d12c45a29add78039066a53fb05967afaa09a672426072b13816fe7676abfc4"},
|
{file = "pyinstaller-6.12.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:fea76fc9b55ffa730fcf90beb897cce4399938460b0b6f40507fbebfc752c753"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-manylinux2014_i686.whl", hash = "sha256:ddc0fddd75f07f7e423da1f0822e389a42af011f9589e0269b87e0d89aa48c1f"},
|
{file = "pyinstaller-6.12.0-py3-none-manylinux2014_i686.whl", hash = "sha256:dac8a27988dbc33cdc34f2046803258bc3f6829de24de52745a5daa22bdba0f1"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:0d6475559c4939f0735122989611d7f739ed3bf02f666ce31022928f7a7e4fda"},
|
{file = "pyinstaller-6.12.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:83c7f3bde9871b4a6aa71c66a96e8ba5c21668ce711ed97f510b9382d10aac6c"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:e21c7806e34f40181e7606926a14579f848bfb1dc52cbca7eea66eccccbfe977"},
|
{file = "pyinstaller-6.12.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:a69818815c6e0711c727edc30680cb1f81c691b59de35db81a2d9e0ae26a9ef1"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:32c742a24fe65d0702958fadf4040f76de85859c26bec0008766e5dbabc5b68f"},
|
{file = "pyinstaller-6.12.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a2abf5fde31a8b38b6df7939bcef8ac1d0c51e97e25317ce3555cd675259750f"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:208c0ef6dab0837a0a273ea32d1a3619a208e3d1fe3fec3785eea71a77fd00ce"},
|
{file = "pyinstaller-6.12.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:8e92e9873a616547bbabbb5a3a9843d5f2ab40c3d8b26810acdf0fe257bee4cf"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:ad84abf465bcda363c1d54eafa76745d77b6a8a713778348377dc98d12a452f7"},
|
{file = "pyinstaller-6.12.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:aefe502d55c9cf6aeaed7feba80b5f8491ce43f8f2b5fe2d9aadca3ee5a05bc4"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-win32.whl", hash = "sha256:2e8365276c5131c9bef98e358fbc305e4022db8bedc9df479629d6414021956a"},
|
{file = "pyinstaller-6.12.0-py3-none-win32.whl", hash = "sha256:138856a5a503bb69c066377e0a22671b0db063e9cc14d5cf5c798a53561200d3"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-win_amd64.whl", hash = "sha256:7ac83c0dc0e04357dab98c487e74ad2adb30e7eb186b58157a8faf46f1fa796f"},
|
{file = "pyinstaller-6.12.0-py3-none-win_amd64.whl", hash = "sha256:0e62d3906309248409f215b386f33afec845214e69cc0f296b93222b26a88f43"},
|
||||||
{file = "pyinstaller-6.11.1-py3-none-win_arm64.whl", hash = "sha256:35e6b8077d240600bb309ed68bb0b1453fd2b7ab740b66d000db7abae6244423"},
|
{file = "pyinstaller-6.12.0-py3-none-win_arm64.whl", hash = "sha256:0c271896a3a168f4f91827145702543db9c5427f4c7372a6df8c75925a3ac18a"},
|
||||||
{file = "pyinstaller-6.11.1.tar.gz", hash = "sha256:491dfb4d9d5d1d9650d9507daec1ff6829527a254d8e396badd60a0affcb72ef"},
|
{file = "pyinstaller-6.12.0.tar.gz", hash = "sha256:1834797be48ce1b26015af68bdeb3c61a6c7500136f04e0fc65e468115dec777"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -808,7 +806,7 @@ altgraph = "*"
|
||||||
importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""}
|
importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""}
|
||||||
macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
|
macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
|
||||||
packaging = ">=22.0"
|
packaging = ">=22.0"
|
||||||
pyinstaller-hooks-contrib = ">=2024.9"
|
pyinstaller-hooks-contrib = ">=2025.1"
|
||||||
setuptools = ">=42.0.0"
|
setuptools = ">=42.0.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
|
@ -817,13 +815,13 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyinstaller-hooks-contrib"
|
name = "pyinstaller-hooks-contrib"
|
||||||
version = "2025.0"
|
version = "2025.1"
|
||||||
description = "Community maintained hooks for PyInstaller"
|
description = "Community maintained hooks for PyInstaller"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "pyinstaller_hooks_contrib-2025.0-py3-none-any.whl", hash = "sha256:3c0623799c3f81a37293127f485d65894c20fd718f722cb588785a3e52581ad1"},
|
{file = "pyinstaller_hooks_contrib-2025.1-py3-none-any.whl", hash = "sha256:d3c799470cbc0bda60dcc8e6b4ab976777532b77621337f2037f558905e3a8e9"},
|
||||||
{file = "pyinstaller_hooks_contrib-2025.0.tar.gz", hash = "sha256:6dc0b55a1acaab2ffee36ed4a05b073aa0a22e46f25fb5c66a31e217454135ed"},
|
{file = "pyinstaller_hooks_contrib-2025.1.tar.gz", hash = "sha256:130818f9e9a0a7f2261f1fd66054966a3a50c99d000981c5d1db11d3ad0c6ab2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -850,96 +848,54 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyside6"
|
name = "pyside6"
|
||||||
version = "6.8.1"
|
version = "6.8.2.1"
|
||||||
description = "Python bindings for the Qt cross-platform application and UI framework"
|
description = "Python bindings for the Qt cross-platform application and UI framework"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.14,>=3.9"
|
python-versions = "<3.14,>=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "PySide6-6.8.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:6d1fd95651cdbdea741af21e155350986eca31ff015fc4c721ce01c2a110a4cc"},
|
{file = "PySide6-6.8.2.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:3fcb551729f235475b2abe7d919027de54a65d850e744f60716f890202273720"},
|
||||||
{file = "PySide6-6.8.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7d6adc5d53313249bbe02edb673877c1d437e215d71e88da78412520653f5c9f"},
|
{file = "PySide6-6.8.2.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:23d2a1a77b25459a049c4276b4e0bbfb375b73d3921061b1a16bcfa64e1fe517"},
|
||||||
{file = "PySide6-6.8.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:ddeeaeca8ebd0ddb1ded30dd33e9240a40f330cc91832de346ba6c9d0cd1253e"},
|
{file = "PySide6-6.8.2.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:bfefa80a93db06dc64c0e7beef0377c9b8ca51e007cfc34575defe065af893b6"},
|
||||||
{file = "PySide6-6.8.1-cp39-abi3-win_amd64.whl", hash = "sha256:866eeaca3ffead6b9d30fa3ed395d5624da0246d7586c8b8207e77ac65d82458"},
|
{file = "PySide6-6.8.2.1-cp39-abi3-win_amd64.whl", hash = "sha256:92361e41727910e3560ea5ba494fabecc76cd20892c9fcb2ced07619081c4e65"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
PySide6-Addons = "6.8.1"
|
PySide6-Addons = "6.8.2.1"
|
||||||
PySide6-Essentials = "6.8.1"
|
PySide6-Essentials = "6.8.2.1"
|
||||||
shiboken6 = "6.8.1"
|
shiboken6 = "6.8.2.1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pyside6"
|
|
||||||
version = "6.8.1.1"
|
|
||||||
description = "Python bindings for the Qt cross-platform application and UI framework"
|
|
||||||
optional = false
|
|
||||||
python-versions = "<3.14,>=3.9"
|
|
||||||
files = [
|
|
||||||
{file = "PySide6-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:805728a7ed58352a02689b953ddbe29af1c8944f8c7f2c28312dc0b69f64b85e"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
PySide6-Addons = "6.8.1.1"
|
|
||||||
PySide6-Essentials = "6.8.1.1"
|
|
||||||
shiboken6 = "6.8.1.1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyside6-addons"
|
name = "pyside6-addons"
|
||||||
version = "6.8.1"
|
version = "6.8.2.1"
|
||||||
description = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
|
description = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.14,>=3.9"
|
python-versions = "<3.14,>=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "PySide6_Addons-6.8.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:879c12346b4b76f5d5ee6499d8ca53b5666c0c998b8fdf8780f08f69ea95d6f9"},
|
{file = "PySide6_Addons-6.8.2.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:5558816018042fecd0d782111ced529585a23ea9a010b518f8495764f578a01f"},
|
||||||
{file = "PySide6_Addons-6.8.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f80cc03c1ac54132c6f800aa461dced64acd7d1646898db164ccb56fe3c23dd4"},
|
{file = "PySide6_Addons-6.8.2.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f3d85e676851ada8238bc76ebfacbee738fc0b35b3bc15c9765dd107b8ee6ec4"},
|
||||||
{file = "PySide6_Addons-6.8.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:570a25016d80046274f454ed0bb06734f478ce6c21be5dec62b624773fc7504e"},
|
{file = "PySide6_Addons-6.8.2.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:d904179f16deeca4ba440b4ef78e8d54df2b994b46784ad9d53b741082f3b2a7"},
|
||||||
{file = "PySide6_Addons-6.8.1-cp39-abi3-win_amd64.whl", hash = "sha256:d7c8c1e89ee0db84631d5b8fdb9129d9d2a0ffb3b4cb2f5192dc8367dd980db4"},
|
{file = "PySide6_Addons-6.8.2.1-cp39-abi3-win_amd64.whl", hash = "sha256:c761cc45022aa79d8419e671e7fb34a4a3e5b3826f1e68fcb819bd6e3a387fbb"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
PySide6-Essentials = "6.8.1"
|
PySide6-Essentials = "6.8.2.1"
|
||||||
shiboken6 = "6.8.1"
|
shiboken6 = "6.8.2.1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pyside6-addons"
|
|
||||||
version = "6.8.1.1"
|
|
||||||
description = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
|
|
||||||
optional = false
|
|
||||||
python-versions = "<3.14,>=3.9"
|
|
||||||
files = [
|
|
||||||
{file = "PySide6_Addons-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:83d35d7a1a7dbd1a16b4040a26ad4d5cc030a2aed4d439241babee1225d6e58a"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
PySide6-Essentials = "6.8.1.1"
|
|
||||||
shiboken6 = "6.8.1.1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyside6-essentials"
|
name = "pyside6-essentials"
|
||||||
version = "6.8.1"
|
version = "6.8.2.1"
|
||||||
description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
|
description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.14,>=3.9"
|
python-versions = "<3.14,>=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "PySide6_Essentials-6.8.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:bd05155245e3cd1572e68d72772e78fadfd713575bbfdd2c5e060d5278e390e9"},
|
{file = "PySide6_Essentials-6.8.2.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:ae5cc48f7e9a08e73e3ec2387ce245c8150e620b8d5a87548ebd4b8e3aeae49b"},
|
||||||
{file = "PySide6_Essentials-6.8.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f600b149e65b57acd6a444edb17615adc42cc2491548ae443ccb574036d86b1"},
|
{file = "PySide6_Essentials-6.8.2.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5ab31e5395a4724102edd6e8ff980fa3f7cde2aa79050763a1dcc30bb914195a"},
|
||||||
{file = "PySide6_Essentials-6.8.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:bf8a3c9ee0b997eb18fb00cb09aacaa28b8a51ce3c295a252cc594c5530aba56"},
|
{file = "PySide6_Essentials-6.8.2.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:7aed46f91d44399b4c713cf7387f5fb6f0114413fbcdbde493a528fb8e19f6ed"},
|
||||||
{file = "PySide6_Essentials-6.8.1-cp39-abi3-win_amd64.whl", hash = "sha256:d5ed4ddb149f36d65bc49ae4260b2d213ee88b2d9a309012ae27f38158c2d1b6"},
|
{file = "PySide6_Essentials-6.8.2.1-cp39-abi3-win_amd64.whl", hash = "sha256:18de224f09108998d194e60f2fb8a1e86367dd525dd8a6192598e80e6ada649e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
shiboken6 = "6.8.1"
|
shiboken6 = "6.8.2.1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pyside6-essentials"
|
|
||||||
version = "6.8.1.1"
|
|
||||||
description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
|
|
||||||
optional = false
|
|
||||||
python-versions = "<3.14,>=3.9"
|
|
||||||
files = [
|
|
||||||
{file = "PySide6_Essentials-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:25f3fdb281ac3b442f08250e3284d3b1944f7c64c62ed93b57678a62c199cf46"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
shiboken6 = "6.8.1.1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
|
@ -1112,29 +1068,29 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.9.3"
|
version = "0.9.6"
|
||||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624"},
|
{file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"},
|
||||||
{file = "ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c"},
|
{file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"},
|
||||||
{file = "ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4"},
|
{file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"},
|
||||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"},
|
||||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"},
|
||||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"},
|
||||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"},
|
||||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"},
|
||||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"},
|
||||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6"},
|
{file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"},
|
||||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"},
|
||||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"},
|
||||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"},
|
||||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b"},
|
{file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"},
|
||||||
{file = "ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c"},
|
{file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"},
|
||||||
{file = "ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4"},
|
{file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"},
|
||||||
{file = "ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b"},
|
{file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"},
|
||||||
{file = "ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a"},
|
{file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1159,25 +1115,15 @@ type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shiboken6"
|
name = "shiboken6"
|
||||||
version = "6.8.1"
|
version = "6.8.2.1"
|
||||||
description = "Python/C++ bindings helper module"
|
description = "Python/C++ bindings helper module"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.14,>=3.9"
|
python-versions = "<3.14,>=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "shiboken6-6.8.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:9a2f51d1ddd3b6d193a0f0fdc09f8d41f2092bc664723c9b9efc1056660d0608"},
|
{file = "shiboken6-6.8.2.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:d3dedeb3732ecfc920c9f97da769c0022a1c3bda99346a9eba56fbf093deaa75"},
|
||||||
{file = "shiboken6-6.8.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1dc4c1976809b0e68872bb98474cccd590455bdcd015f0e0639907e94af27b6a"},
|
{file = "shiboken6-6.8.2.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c83e90056f13d0872cc4d2b7bf60b6d6e3b1b172f1f91910c0ba5b641af01758"},
|
||||||
{file = "shiboken6-6.8.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:ab5b60602ca6227103138aae89c4f5df3b1b8e249cbc8ec9e6e2a57f20ad9a91"},
|
{file = "shiboken6-6.8.2.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:8592401423acc693f51dbbfae5e7493cc3ed6738be79daaf90afa07f4da5bb25"},
|
||||||
{file = "shiboken6-6.8.1-cp39-abi3-win_amd64.whl", hash = "sha256:3ea127fd72be113b73cacd70e06687ad6f83c1c888047833c7dcdd5cf8e7f586"},
|
{file = "shiboken6-6.8.2.1-cp39-abi3-win_amd64.whl", hash = "sha256:1b751d47b759762b7ca31bad278d52eca4105d3028880d93979261ebbfba810c"},
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shiboken6"
|
|
||||||
version = "6.8.1.1"
|
|
||||||
description = "Python/C++ bindings helper module"
|
|
||||||
optional = false
|
|
||||||
python-versions = "<3.14,>=3.9"
|
|
||||||
files = [
|
|
||||||
{file = "shiboken6-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:42fbb173a772c4e059dbeafb302e96f6ea8e1c9bacf05fab71ea7eb0d8f97b01"},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1278,13 +1224,13 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-pygments"
|
name = "types-pygments"
|
||||||
version = "2.19.0.20250107"
|
version = "2.19.0.20250219"
|
||||||
description = "Typing stubs for Pygments"
|
description = "Typing stubs for Pygments"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "types_Pygments-2.19.0.20250107-py3-none-any.whl", hash = "sha256:34a555ed327f249daed18c6309e6e62770cdb8b9c321029ba7fd852d10b16f10"},
|
{file = "types_Pygments-2.19.0.20250219-py3-none-any.whl", hash = "sha256:5e3e1f660665b3a2ea946dda794b8d5b05772d789181704b523d646e8a7f4382"},
|
||||||
{file = "types_pygments-2.19.0.20250107.tar.gz", hash = "sha256:94de72c7f09b956c518f566e056812c698272a7a03a9cd81f0065576c6bd3219"},
|
{file = "types_pygments-2.19.0.20250219.tar.gz", hash = "sha256:a4a279338c96f3d4f2eb2c4d7c6c5593c88108b185bb5c664f943f781170cd14"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -1318,13 +1264,13 @@ urllib3 = ">=2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-setuptools"
|
name = "types-setuptools"
|
||||||
version = "75.8.0.20250110"
|
version = "75.8.0.20250210"
|
||||||
description = "Typing stubs for setuptools"
|
description = "Typing stubs for setuptools"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.9"
|
||||||
files = [
|
files = [
|
||||||
{file = "types_setuptools-75.8.0.20250110-py3-none-any.whl", hash = "sha256:a9f12980bbf9bcdc23ecd80755789085bad6bfce4060c2275bc2b4ca9f2bc480"},
|
{file = "types_setuptools-75.8.0.20250210-py3-none-any.whl", hash = "sha256:a217d7b4d59be04c29e23d142c959a0f85e71292fd3fc4313f016ca11f0b56dc"},
|
||||||
{file = "types_setuptools-75.8.0.20250110.tar.gz", hash = "sha256:96f7ec8bbd6e0a54ea180d66ad68ad7a1d7954e7281a710ea2de75e355545271"},
|
{file = "types_setuptools-75.8.0.20250210.tar.gz", hash = "sha256:c1547361b2441f07c94e25dce8a068e18c611593ad4b6fdd727b1a8f5d1fda33"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1377,4 +1323,4 @@ type = ["pytest-mypy"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.9,<3.14"
|
python-versions = ">=3.9,<3.14"
|
||||||
content-hash = "c6395d63523761d272dfc5fe6eef7822a081b3b0fb0b739a82efec7de5346d57"
|
content-hash = "bd66b4a55c137f803902b235627ca4399c30d5428be8be925ca8d2a394c5dc80"
|
||||||
|
|
|
@ -23,13 +23,6 @@ pyxdg = {version = "*", platform = "linux"}
|
||||||
requests = "*"
|
requests = "*"
|
||||||
markdown = "*"
|
markdown = "*"
|
||||||
packaging = "*"
|
packaging = "*"
|
||||||
# shiboken6 released a 6.8.1.1 version only for macOS
|
|
||||||
# and it's getting picked by poetry, so pin it instead.
|
|
||||||
shiboken6 = [
|
|
||||||
{version = "*", platform = "darwin"},
|
|
||||||
{version = "<6.8.1.1", platform = "linux"},
|
|
||||||
{version = "<6.8.1.1", platform = "win32"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
dangerzone = 'dangerzone:main'
|
dangerzone = 'dangerzone:main'
|
||||||
|
|
|
@ -24,9 +24,10 @@ from dangerzone.gui.main_window import (
|
||||||
QtGui,
|
QtGui,
|
||||||
WaitingWidgetContainer,
|
WaitingWidgetContainer,
|
||||||
)
|
)
|
||||||
from dangerzone.gui.updater import UpdateReport, UpdaterThread
|
from dangerzone.gui.updater import UpdaterThread
|
||||||
from dangerzone.isolation_provider.container import Container
|
from dangerzone.isolation_provider.container import Container
|
||||||
from dangerzone.isolation_provider.dummy import Dummy
|
from dangerzone.isolation_provider.dummy import Dummy
|
||||||
|
from dangerzone.updater import releases
|
||||||
|
|
||||||
from .test_updater import assert_report_equal, default_updater_settings
|
from .test_updater import assert_report_equal, default_updater_settings
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ def test_no_update(
|
||||||
|
|
||||||
# Check that the callback function gets an empty report.
|
# Check that the callback function gets an empty report.
|
||||||
handle_updates_spy.assert_called_once()
|
handle_updates_spy.assert_called_once()
|
||||||
assert_report_equal(handle_updates_spy.call_args.args[0], UpdateReport())
|
assert_report_equal(handle_updates_spy.call_args.args[0], releases.UpdateReport())
|
||||||
|
|
||||||
# Check that the menu entries remain exactly the same.
|
# Check that the menu entries remain exactly the same.
|
||||||
menu_actions_after = window.hamburger_button.menu().actions()
|
menu_actions_after = window.hamburger_button.menu().actions()
|
||||||
|
@ -171,8 +172,8 @@ def test_update_detected(
|
||||||
|
|
||||||
# Make requests.get().json() return the following dictionary.
|
# Make requests.get().json() return the following dictionary.
|
||||||
mock_upstream_info = {"tag_name": "99.9.9", "body": "changelog"}
|
mock_upstream_info = {"tag_name": "99.9.9", "body": "changelog"}
|
||||||
mocker.patch("dangerzone.gui.updater.requests.get")
|
mocker.patch("dangerzone.updater.releases.requests.get")
|
||||||
requests_mock = updater_module.requests.get
|
requests_mock = releases.requests.get
|
||||||
requests_mock().status_code = 200 # type: ignore [call-arg]
|
requests_mock().status_code = 200 # type: ignore [call-arg]
|
||||||
requests_mock().json.return_value = mock_upstream_info # type: ignore [attr-defined, call-arg]
|
requests_mock().json.return_value = mock_upstream_info # type: ignore [attr-defined, call-arg]
|
||||||
|
|
||||||
|
@ -191,7 +192,8 @@ def test_update_detected(
|
||||||
# Check that the callback function gets an update report.
|
# Check that the callback function gets an update report.
|
||||||
handle_updates_spy.assert_called_once()
|
handle_updates_spy.assert_called_once()
|
||||||
assert_report_equal(
|
assert_report_equal(
|
||||||
handle_updates_spy.call_args.args[0], UpdateReport("99.9.9", "<p>changelog</p>")
|
handle_updates_spy.call_args.args[0],
|
||||||
|
releases.UpdateReport("99.9.9", "<p>changelog</p>"),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check that the settings have been updated properly.
|
# Check that the settings have been updated properly.
|
||||||
|
@ -281,9 +283,9 @@ def test_update_error(
|
||||||
qt_updater.dangerzone.settings.set("updater_last_check", 0)
|
qt_updater.dangerzone.settings.set("updater_last_check", 0)
|
||||||
qt_updater.dangerzone.settings.set("updater_errors", 0)
|
qt_updater.dangerzone.settings.set("updater_errors", 0)
|
||||||
|
|
||||||
# Make requests.get() return an errorthe following dictionary.
|
# Make requests.get() return an error
|
||||||
mocker.patch("dangerzone.gui.updater.requests.get")
|
mocker.patch("dangerzone.updater.releases.requests.get")
|
||||||
requests_mock = updater_module.requests.get
|
requests_mock = releases.requests.get
|
||||||
requests_mock.side_effect = Exception("failed") # type: ignore [attr-defined]
|
requests_mock.side_effect = Exception("failed") # type: ignore [attr-defined]
|
||||||
|
|
||||||
window = MainWindow(qt_updater.dangerzone)
|
window = MainWindow(qt_updater.dangerzone)
|
||||||
|
|
|
@ -12,7 +12,9 @@ from pytestqt.qtbot import QtBot
|
||||||
|
|
||||||
from dangerzone import settings
|
from dangerzone import settings
|
||||||
from dangerzone.gui import updater as updater_module
|
from dangerzone.gui import updater as updater_module
|
||||||
from dangerzone.gui.updater import UpdateReport, UpdaterThread
|
from dangerzone.gui.updater import UpdaterThread
|
||||||
|
from dangerzone.updater import releases
|
||||||
|
from dangerzone.updater.releases import UpdateReport
|
||||||
from dangerzone.util import get_version
|
from dangerzone.util import get_version
|
||||||
|
|
||||||
from ..test_settings import default_settings_0_4_1, save_settings
|
from ..test_settings import default_settings_0_4_1, save_settings
|
||||||
|
@ -122,6 +124,7 @@ def test_user_prompts(
|
||||||
updater: UpdaterThread, monkeypatch: MonkeyPatch, mocker: MockerFixture
|
updater: UpdaterThread, monkeypatch: MonkeyPatch, mocker: MockerFixture
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test prompting users to ask them if they want to enable update checks."""
|
"""Test prompting users to ask them if they want to enable update checks."""
|
||||||
|
settings = updater.dangerzone.settings
|
||||||
# First run
|
# First run
|
||||||
#
|
#
|
||||||
# When Dangerzone runs for the first time, users should not be asked to enable
|
# When Dangerzone runs for the first time, users should not be asked to enable
|
||||||
|
@ -130,7 +133,7 @@ def test_user_prompts(
|
||||||
expected_settings["updater_check"] = None
|
expected_settings["updater_check"] = None
|
||||||
expected_settings["updater_last_check"] = 0
|
expected_settings["updater_last_check"] = 0
|
||||||
assert updater.should_check_for_updates() is False
|
assert updater.should_check_for_updates() is False
|
||||||
assert updater.dangerzone.settings.get_updater_settings() == expected_settings
|
assert settings.get_updater_settings() == expected_settings
|
||||||
|
|
||||||
# Second run
|
# Second run
|
||||||
#
|
#
|
||||||
|
@ -144,14 +147,14 @@ def test_user_prompts(
|
||||||
prompt_mock().launch.return_value = False # type: ignore [attr-defined]
|
prompt_mock().launch.return_value = False # type: ignore [attr-defined]
|
||||||
expected_settings["updater_check"] = False
|
expected_settings["updater_check"] = False
|
||||||
assert updater.should_check_for_updates() is False
|
assert updater.should_check_for_updates() is False
|
||||||
assert updater.dangerzone.settings.get_updater_settings() == expected_settings
|
assert settings.get_updater_settings() == expected_settings
|
||||||
|
|
||||||
# Reset the "updater_check" field and check enabling update checks.
|
# Reset the "updater_check" field and check enabling update checks.
|
||||||
updater.dangerzone.settings.set("updater_check", None)
|
settings.set("updater_check", None)
|
||||||
prompt_mock().launch.return_value = True # type: ignore [attr-defined]
|
prompt_mock().launch.return_value = True # type: ignore [attr-defined]
|
||||||
expected_settings["updater_check"] = True
|
expected_settings["updater_check"] = True
|
||||||
assert updater.should_check_for_updates() is True
|
assert updater.should_check_for_updates() is True
|
||||||
assert updater.dangerzone.settings.get_updater_settings() == expected_settings
|
assert settings.get_updater_settings() == expected_settings
|
||||||
|
|
||||||
# Third run
|
# Third run
|
||||||
#
|
#
|
||||||
|
@ -159,7 +162,7 @@ def test_user_prompts(
|
||||||
# checks.
|
# checks.
|
||||||
prompt_mock().side_effect = RuntimeError("Should not be called") # type: ignore [attr-defined]
|
prompt_mock().side_effect = RuntimeError("Should not be called") # type: ignore [attr-defined]
|
||||||
for check in [True, False]:
|
for check in [True, False]:
|
||||||
updater.dangerzone.settings.set("updater_check", check)
|
settings.set("updater_check", check)
|
||||||
assert updater.should_check_for_updates() == check
|
assert updater.should_check_for_updates() == check
|
||||||
|
|
||||||
|
|
||||||
|
@ -167,43 +170,44 @@ def test_update_checks(
|
||||||
updater: UpdaterThread, monkeypatch: MonkeyPatch, mocker: MockerFixture
|
updater: UpdaterThread, monkeypatch: MonkeyPatch, mocker: MockerFixture
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test version update checks."""
|
"""Test version update checks."""
|
||||||
|
settings = updater.dangerzone.settings
|
||||||
# This dictionary will simulate GitHub's response.
|
# This dictionary will simulate GitHub's response.
|
||||||
mock_upstream_info = {"tag_name": f"v{get_version()}", "body": "changelog"}
|
mock_upstream_info = {"tag_name": f"v{get_version()}", "body": "changelog"}
|
||||||
|
|
||||||
# Make requests.get().json() return the above dictionary.
|
# Make requests.get().json() return the above dictionary.
|
||||||
mocker.patch("dangerzone.gui.updater.requests.get")
|
mocker.patch("dangerzone.updater.releases.requests.get")
|
||||||
requests_mock = updater_module.requests.get
|
requests_mock = updater_module.releases.requests.get
|
||||||
requests_mock().status_code = 200 # type: ignore [call-arg]
|
requests_mock().status_code = 200 # type: ignore [call-arg]
|
||||||
requests_mock().json.return_value = mock_upstream_info # type: ignore [attr-defined, call-arg]
|
requests_mock().json.return_value = mock_upstream_info # type: ignore [attr-defined, call-arg]
|
||||||
|
|
||||||
# Always assume that we can perform multiple update checks in a row.
|
# Always assume that we can perform multiple update checks in a row.
|
||||||
monkeypatch.setattr(updater, "_should_postpone_update_check", lambda: False)
|
mocker.patch(
|
||||||
|
"dangerzone.updater.releases._should_postpone_update_check", return_value=False
|
||||||
|
)
|
||||||
|
|
||||||
# Test 1 - Check that the current version triggers no updates.
|
# Test 1 - Check that the current version triggers no updates.
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert_report_equal(report, UpdateReport())
|
assert_report_equal(report, UpdateReport())
|
||||||
|
|
||||||
# Test 2 - Check that a newer version triggers updates, and that the changelog is
|
# Test 2 - Check that a newer version triggers updates, and that the changelog is
|
||||||
# rendered from Markdown to HTML.
|
# rendered from Markdown to HTML.
|
||||||
mock_upstream_info["tag_name"] = "v99.9.9"
|
mock_upstream_info["tag_name"] = "v99.9.9"
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert_report_equal(
|
assert_report_equal(
|
||||||
report, UpdateReport(version="99.9.9", changelog="<p>changelog</p>")
|
report, UpdateReport(version="99.9.9", changelog="<p>changelog</p>")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test 3 - Check that HTTP errors are converted to error reports.
|
# Test 3 - Check that HTTP errors are converted to error reports.
|
||||||
requests_mock.side_effect = Exception("failed") # type: ignore [attr-defined]
|
requests_mock.side_effect = Exception("failed") # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
error_msg = (
|
error_msg = f"Encountered an exception while checking {updater_module.releases.GH_RELEASE_URL}: failed"
|
||||||
f"Encountered an exception while checking {updater.GH_RELEASE_URL}: failed"
|
|
||||||
)
|
|
||||||
assert_report_equal(report, UpdateReport(error=error_msg))
|
assert_report_equal(report, UpdateReport(error=error_msg))
|
||||||
|
|
||||||
# Test 4 - Check that cached version/changelog info do not trigger an update check.
|
# Test 4 - Check that cached version/changelog info do not trigger an update check.
|
||||||
updater.dangerzone.settings.set("updater_latest_version", "99.9.9")
|
settings.set("updater_latest_version", "99.9.9")
|
||||||
updater.dangerzone.settings.set("updater_latest_changelog", "<p>changelog</p>")
|
settings.set("updater_latest_changelog", "<p>changelog</p>")
|
||||||
|
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert_report_equal(
|
assert_report_equal(
|
||||||
report, UpdateReport(version="99.9.9", changelog="<p>changelog</p>")
|
report, UpdateReport(version="99.9.9", changelog="<p>changelog</p>")
|
||||||
)
|
)
|
||||||
|
@ -211,14 +215,16 @@ def test_update_checks(
|
||||||
|
|
||||||
def test_update_checks_cooldown(updater: UpdaterThread, mocker: MockerFixture) -> None:
|
def test_update_checks_cooldown(updater: UpdaterThread, mocker: MockerFixture) -> None:
|
||||||
"""Make sure Dangerzone only checks for updates every X hours"""
|
"""Make sure Dangerzone only checks for updates every X hours"""
|
||||||
updater.dangerzone.settings.set("updater_check", True)
|
settings = updater.dangerzone.settings
|
||||||
updater.dangerzone.settings.set("updater_last_check", 0)
|
|
||||||
|
settings.set("updater_check", True)
|
||||||
|
settings.set("updater_last_check", 0)
|
||||||
|
|
||||||
# Mock some functions before the tests start
|
# Mock some functions before the tests start
|
||||||
cooldown_spy = mocker.spy(updater, "_should_postpone_update_check")
|
cooldown_spy = mocker.spy(updater_module.releases, "_should_postpone_update_check")
|
||||||
timestamp_mock = mocker.patch.object(updater, "_get_now_timestamp")
|
timestamp_mock = mocker.patch.object(updater_module.releases, "_get_now_timestamp")
|
||||||
mocker.patch("dangerzone.gui.updater.requests.get")
|
mocker.patch("dangerzone.updater.releases.requests.get")
|
||||||
requests_mock = updater_module.requests.get
|
requests_mock = updater_module.releases.requests.get
|
||||||
|
|
||||||
# # Make requests.get().json() return the version info that we want.
|
# # Make requests.get().json() return the version info that we want.
|
||||||
mock_upstream_info = {"tag_name": "99.9.9", "body": "changelog"}
|
mock_upstream_info = {"tag_name": "99.9.9", "body": "changelog"}
|
||||||
|
@ -231,9 +237,9 @@ def test_update_checks_cooldown(updater: UpdaterThread, mocker: MockerFixture) -
|
||||||
curtime = int(time.time())
|
curtime = int(time.time())
|
||||||
timestamp_mock.return_value = curtime
|
timestamp_mock.return_value = curtime
|
||||||
|
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert cooldown_spy.spy_return is False
|
assert cooldown_spy.spy_return is False
|
||||||
assert updater.dangerzone.settings.get("updater_last_check") == curtime
|
assert settings.get("updater_last_check") == curtime
|
||||||
assert_report_equal(report, UpdateReport("99.9.9", "<p>changelog</p>"))
|
assert_report_equal(report, UpdateReport("99.9.9", "<p>changelog</p>"))
|
||||||
|
|
||||||
# Test 2: Advance the current time by 1 second, and ensure that no update will take
|
# Test 2: Advance the current time by 1 second, and ensure that no update will take
|
||||||
|
@ -242,41 +248,39 @@ def test_update_checks_cooldown(updater: UpdaterThread, mocker: MockerFixture) -
|
||||||
curtime += 1
|
curtime += 1
|
||||||
timestamp_mock.return_value = curtime
|
timestamp_mock.return_value = curtime
|
||||||
requests_mock.side_effect = Exception("failed") # type: ignore [attr-defined]
|
requests_mock.side_effect = Exception("failed") # type: ignore [attr-defined]
|
||||||
updater.dangerzone.settings.set("updater_latest_version", get_version())
|
settings.set("updater_latest_version", get_version())
|
||||||
updater.dangerzone.settings.set("updater_latest_changelog", None)
|
settings.set("updater_latest_changelog", None)
|
||||||
|
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert cooldown_spy.spy_return is True
|
assert cooldown_spy.spy_return is True
|
||||||
assert updater.dangerzone.settings.get("updater_last_check") == curtime - 1 # type: ignore [unreachable]
|
assert settings.get("updater_last_check") == curtime - 1 # type: ignore [unreachable]
|
||||||
assert_report_equal(report, UpdateReport())
|
assert_report_equal(report, UpdateReport())
|
||||||
|
|
||||||
# Test 3: Advance the current time by <cooldown period> seconds. Ensure that
|
# Test 3: Advance the current time by <cooldown period> seconds. Ensure that
|
||||||
# Dangerzone checks for updates again, and the last check timestamp gets bumped.
|
# Dangerzone checks for updates again, and the last check timestamp gets bumped.
|
||||||
curtime += updater_module.UPDATE_CHECK_COOLDOWN_SECS
|
curtime += updater_module.releases.UPDATE_CHECK_COOLDOWN_SECS
|
||||||
timestamp_mock.return_value = curtime
|
timestamp_mock.return_value = curtime
|
||||||
requests_mock.side_effect = None
|
requests_mock.side_effect = None
|
||||||
|
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert cooldown_spy.spy_return is False
|
assert cooldown_spy.spy_return is False
|
||||||
assert updater.dangerzone.settings.get("updater_last_check") == curtime
|
assert settings.get("updater_last_check") == curtime
|
||||||
assert_report_equal(report, UpdateReport("99.9.9", "<p>changelog</p>"))
|
assert_report_equal(report, UpdateReport("99.9.9", "<p>changelog</p>"))
|
||||||
|
|
||||||
# Test 4: Make Dangerzone check for updates again, but this time, it should
|
# Test 4: Make Dangerzone check for updates again, but this time, it should
|
||||||
# encounter an error while doing so. In that case, the last check timestamp
|
# encounter an error while doing so. In that case, the last check timestamp
|
||||||
# should be bumped, so that subsequent checks don't take place.
|
# should be bumped, so that subsequent checks don't take place.
|
||||||
updater.dangerzone.settings.set("updater_latest_version", get_version())
|
settings.set("updater_latest_version", get_version())
|
||||||
updater.dangerzone.settings.set("updater_latest_changelog", None)
|
settings.set("updater_latest_changelog", None)
|
||||||
|
|
||||||
curtime += updater_module.UPDATE_CHECK_COOLDOWN_SECS
|
curtime += updater_module.releases.UPDATE_CHECK_COOLDOWN_SECS
|
||||||
timestamp_mock.return_value = curtime
|
timestamp_mock.return_value = curtime
|
||||||
requests_mock.side_effect = Exception("failed")
|
requests_mock.side_effect = Exception("failed")
|
||||||
|
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert cooldown_spy.spy_return is False
|
assert cooldown_spy.spy_return is False
|
||||||
assert updater.dangerzone.settings.get("updater_last_check") == curtime
|
assert settings.get("updater_last_check") == curtime
|
||||||
error_msg = (
|
error_msg = f"Encountered an exception while checking {updater_module.releases.GH_RELEASE_URL}: failed"
|
||||||
f"Encountered an exception while checking {updater.GH_RELEASE_URL}: failed"
|
|
||||||
)
|
|
||||||
assert_report_equal(report, UpdateReport(error=error_msg))
|
assert_report_equal(report, UpdateReport(error=error_msg))
|
||||||
|
|
||||||
|
|
||||||
|
@ -284,16 +288,17 @@ def test_update_errors(
|
||||||
updater: UpdaterThread, monkeypatch: MonkeyPatch, mocker: MockerFixture
|
updater: UpdaterThread, monkeypatch: MonkeyPatch, mocker: MockerFixture
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test update check errors."""
|
"""Test update check errors."""
|
||||||
|
settings = updater.dangerzone.settings
|
||||||
# Mock requests.get().
|
# Mock requests.get().
|
||||||
mocker.patch("dangerzone.gui.updater.requests.get")
|
mocker.patch("dangerzone.updater.releases.requests.get")
|
||||||
requests_mock = updater_module.requests.get
|
requests_mock = releases.requests.get
|
||||||
|
|
||||||
# Always assume that we can perform multiple update checks in a row.
|
# Always assume that we can perform multiple update checks in a row.
|
||||||
monkeypatch.setattr(updater, "_should_postpone_update_check", lambda: False)
|
monkeypatch.setattr(releases, "_should_postpone_update_check", lambda: False)
|
||||||
|
|
||||||
# Test 1 - Check that request exceptions are being detected as errors.
|
# Test 1 - Check that request exceptions are being detected as errors.
|
||||||
requests_mock.side_effect = Exception("bad url") # type: ignore [attr-defined]
|
requests_mock.side_effect = Exception("bad url") # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert report.error is not None
|
assert report.error is not None
|
||||||
assert "bad url" in report.error
|
assert "bad url" in report.error
|
||||||
assert "Encountered an exception" in report.error
|
assert "Encountered an exception" in report.error
|
||||||
|
@ -304,7 +309,7 @@ def test_update_errors(
|
||||||
|
|
||||||
requests_mock.return_value = MockResponse500() # type: ignore [attr-defined]
|
requests_mock.return_value = MockResponse500() # type: ignore [attr-defined]
|
||||||
requests_mock.side_effect = None # type: ignore [attr-defined]
|
requests_mock.side_effect = None # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert report.error is not None
|
assert report.error is not None
|
||||||
assert "Encountered an HTTP 500 error" in report.error
|
assert "Encountered an HTTP 500 error" in report.error
|
||||||
|
|
||||||
|
@ -316,7 +321,7 @@ def test_update_errors(
|
||||||
return json.loads("bad json")
|
return json.loads("bad json")
|
||||||
|
|
||||||
requests_mock.return_value = MockResponseBadJSON() # type: ignore [attr-defined]
|
requests_mock.return_value = MockResponseBadJSON() # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert report.error is not None
|
assert report.error is not None
|
||||||
assert "Received a non-JSON response" in report.error
|
assert "Received a non-JSON response" in report.error
|
||||||
|
|
||||||
|
@ -328,7 +333,7 @@ def test_update_errors(
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
requests_mock.return_value = MockResponseEmpty() # type: ignore [attr-defined]
|
requests_mock.return_value = MockResponseEmpty() # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert report.error is not None
|
assert report.error is not None
|
||||||
assert "Missing required fields in JSON" in report.error
|
assert "Missing required fields in JSON" in report.error
|
||||||
|
|
||||||
|
@ -340,7 +345,7 @@ def test_update_errors(
|
||||||
return {"tag_name": "vbad_version", "body": "changelog"}
|
return {"tag_name": "vbad_version", "body": "changelog"}
|
||||||
|
|
||||||
requests_mock.return_value = MockResponseBadVersion() # type: ignore [attr-defined]
|
requests_mock.return_value = MockResponseBadVersion() # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert report.error is not None
|
assert report.error is not None
|
||||||
assert "Invalid version" in report.error
|
assert "Invalid version" in report.error
|
||||||
|
|
||||||
|
@ -352,7 +357,7 @@ def test_update_errors(
|
||||||
return {"tag_name": "v99.9.9", "body": ["bad", "markdown"]}
|
return {"tag_name": "v99.9.9", "body": ["bad", "markdown"]}
|
||||||
|
|
||||||
requests_mock.return_value = MockResponseBadMarkdown() # type: ignore [attr-defined]
|
requests_mock.return_value = MockResponseBadMarkdown() # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert report.error is not None
|
assert report.error is not None
|
||||||
|
|
||||||
# Test 7 - Check that a valid response passes.
|
# Test 7 - Check that a valid response passes.
|
||||||
|
@ -363,7 +368,7 @@ def test_update_errors(
|
||||||
return {"tag_name": "v99.9.9", "body": "changelog"}
|
return {"tag_name": "v99.9.9", "body": "changelog"}
|
||||||
|
|
||||||
requests_mock.return_value = MockResponseValid() # type: ignore [attr-defined]
|
requests_mock.return_value = MockResponseValid() # type: ignore [attr-defined]
|
||||||
report = updater.check_for_updates()
|
report = releases.check_for_updates(settings)
|
||||||
assert_report_equal(report, UpdateReport("99.9.9", "<p>changelog</p>"))
|
assert_report_equal(report, UpdateReport("99.9.9", "<p>changelog</p>"))
|
||||||
|
|
||||||
|
|
||||||
|
@ -375,24 +380,28 @@ def test_update_check_prompt(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that the prompt to enable update checks works properly."""
|
"""Test that the prompt to enable update checks works properly."""
|
||||||
# Force Dangerzone to check immediately for updates
|
# Force Dangerzone to check immediately for updates
|
||||||
qt_updater.dangerzone.settings.set("updater_last_check", 0)
|
settings = qt_updater.dangerzone.settings
|
||||||
|
settings.set("updater_last_check", 0)
|
||||||
|
|
||||||
# Test 1 - Check that on the second run of Dangerzone, the user is prompted to
|
# Test 1 - Check that on the second run of Dangerzone, the user is prompted to
|
||||||
# choose if they want to enable update checks.
|
# choose if they want to enable update checks.
|
||||||
def check_button_labels() -> None:
|
def check_button_labels() -> None:
|
||||||
dialog = qt_updater.dangerzone.app.activeWindow()
|
dialog = qt_updater.dangerzone.app.activeWindow()
|
||||||
assert dialog.ok_button.text() == "Check Automatically" # type: ignore [attr-defined]
|
assert dialog.ok_button.text() == "Enable sandbox updates" # type: ignore [attr-defined]
|
||||||
assert dialog.cancel_button.text() == "Don't Check" # type: ignore [attr-defined]
|
assert dialog.cancel_button.text() == "Do not make any requests" # type: ignore [attr-defined]
|
||||||
dialog.ok_button.click() # type: ignore [attr-defined]
|
dialog.ok_button.click() # type: ignore [attr-defined]
|
||||||
|
|
||||||
QtCore.QTimer.singleShot(500, check_button_labels)
|
QtCore.QTimer.singleShot(500, check_button_labels)
|
||||||
|
mocker.patch(
|
||||||
|
"dangerzone.updater.releases._should_postpone_update_check", return_value=False
|
||||||
|
)
|
||||||
res = qt_updater.should_check_for_updates()
|
res = qt_updater.should_check_for_updates()
|
||||||
|
|
||||||
assert res is True
|
assert res is True
|
||||||
|
|
||||||
# Test 2 - Check that when the user chooses to enable update checks, we
|
# Test 2 - Check that when the user chooses to enable update checks, we
|
||||||
# store that decision in the settings.
|
# store that decision in the settings.
|
||||||
qt_updater.check = None
|
settings.set("updater_check", None, autosave=True)
|
||||||
|
|
||||||
def click_ok() -> None:
|
def click_ok() -> None:
|
||||||
dialog = qt_updater.dangerzone.app.activeWindow()
|
dialog = qt_updater.dangerzone.app.activeWindow()
|
||||||
|
@ -402,11 +411,11 @@ def test_update_check_prompt(
|
||||||
res = qt_updater.should_check_for_updates()
|
res = qt_updater.should_check_for_updates()
|
||||||
|
|
||||||
assert res is True
|
assert res is True
|
||||||
assert qt_updater.check is True
|
assert settings.get("updater_check") is True
|
||||||
|
|
||||||
# Test 3 - Same as the previous test, but check that clicking on cancel stores the
|
# Test 3 - Same as the previous test, but check that clicking on cancel stores the
|
||||||
# opposite decision.
|
# opposite decision.
|
||||||
qt_updater.check = None # type: ignore [unreachable]
|
settings.set("updater_check", None) # type: ignore [unreachable]
|
||||||
|
|
||||||
def click_cancel() -> None:
|
def click_cancel() -> None:
|
||||||
dialog = qt_updater.dangerzone.app.activeWindow()
|
dialog = qt_updater.dangerzone.app.activeWindow()
|
||||||
|
@ -416,11 +425,11 @@ def test_update_check_prompt(
|
||||||
res = qt_updater.should_check_for_updates()
|
res = qt_updater.should_check_for_updates()
|
||||||
|
|
||||||
assert res is False
|
assert res is False
|
||||||
assert qt_updater.check is False
|
assert settings.get("updater_check") is False
|
||||||
|
|
||||||
# Test 4 - Same as the previous test, but check that clicking on "X" does not store
|
# Test 4 - Same as the previous test, but check that clicking on "X" does not store
|
||||||
# any decision.
|
# any decision.
|
||||||
qt_updater.check = None
|
settings.set("updater_check", None, autosave=True)
|
||||||
|
|
||||||
def click_x() -> None:
|
def click_x() -> None:
|
||||||
dialog = qt_updater.dangerzone.app.activeWindow()
|
dialog = qt_updater.dangerzone.app.activeWindow()
|
||||||
|
@ -430,4 +439,4 @@ def test_update_check_prompt(
|
||||||
res = qt_updater.should_check_for_updates()
|
res = qt_updater.should_check_for_updates()
|
||||||
|
|
||||||
assert res is False
|
assert res is False
|
||||||
assert qt_updater.check is None
|
assert settings.get("updater_check") is None
|
||||||
|
|
|
@ -108,7 +108,8 @@ def test_get_log_index_from_missing_log_index():
|
||||||
|
|
||||||
def test_upgrade_container_image_if_already_up_to_date(mocker):
|
def test_upgrade_container_image_if_already_up_to_date(mocker):
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"dangerzone.updater.signatures.is_update_available", return_value=(False, None)
|
"dangerzone.updater.registry.is_new_remote_image_available",
|
||||||
|
return_value=(False, None),
|
||||||
)
|
)
|
||||||
with pytest.raises(errors.ImageAlreadyUpToDate):
|
with pytest.raises(errors.ImageAlreadyUpToDate):
|
||||||
upgrade_container_image(
|
upgrade_container_image(
|
||||||
|
@ -118,7 +119,7 @@ def test_upgrade_container_image_if_already_up_to_date(mocker):
|
||||||
|
|
||||||
def test_upgrade_container_without_signatures(mocker):
|
def test_upgrade_container_without_signatures(mocker):
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"dangerzone.updater.signatures.is_update_available",
|
"dangerzone.updater.registry.is_new_remote_image_available",
|
||||||
return_value=(True, "sha256:123456"),
|
return_value=(True, "sha256:123456"),
|
||||||
)
|
)
|
||||||
mocker.patch("dangerzone.updater.signatures.get_remote_signatures", return_value=[])
|
mocker.patch("dangerzone.updater.signatures.get_remote_signatures", return_value=[])
|
||||||
|
@ -139,7 +140,7 @@ def test_upgrade_container_lower_log_index(mocker):
|
||||||
signatures_path=VALID_SIGNATURES_PATH,
|
signatures_path=VALID_SIGNATURES_PATH,
|
||||||
)
|
)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"dangerzone.updater.signatures.is_update_available",
|
"dangerzone.updater.registry.is_new_remote_image_available",
|
||||||
return_value=(
|
return_value=(
|
||||||
True,
|
True,
|
||||||
image_digest,
|
image_digest,
|
||||||
|
@ -273,31 +274,79 @@ def test_stores_signatures_updates_last_log_index(valid_signature, mocker, tmp_p
|
||||||
assert f.read() == "100"
|
assert f.read() == "100"
|
||||||
|
|
||||||
|
|
||||||
def test_is_update_available_when_no_local_image(mocker):
|
def test_is_update_available_when_remote_image_available(mocker):
|
||||||
"""
|
"""
|
||||||
Test that is_update_available returns True when no local image is
|
Test that is_update_available returns True when a new image is available
|
||||||
currently present.
|
and all checks pass
|
||||||
"""
|
"""
|
||||||
# Mock container_image_exists to return False
|
# Mock is_new_remote_image_available to return True and digest
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"dangerzone.container_utils.get_local_image_digest",
|
"dangerzone.updater.registry.is_new_remote_image_available",
|
||||||
side_effect=dzerrors.ImageNotPresentException,
|
return_value=(True, RANDOM_DIGEST),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mock get_manifest_digest to return a digest
|
# Mock check_signatures_and_logindex to not raise any exceptions
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"dangerzone.updater.registry.get_manifest_digest",
|
"dangerzone.updater.signatures.check_signatures_and_logindex",
|
||||||
return_value=RANDOM_DIGEST,
|
return_value=[{"some": "signature"}],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Call is_update_available
|
# Call is_update_available
|
||||||
update_available, digest = is_update_available("ghcr.io/freedomofpress/dangerzone")
|
update_available, digest = is_update_available(
|
||||||
|
"ghcr.io/freedomofpress/dangerzone", "test.pub"
|
||||||
|
)
|
||||||
|
|
||||||
# Verify the result
|
# Verify the result
|
||||||
assert update_available is True
|
assert update_available is True
|
||||||
assert digest == RANDOM_DIGEST
|
assert digest == RANDOM_DIGEST
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_update_available_when_no_remote_image(mocker):
|
||||||
|
"""
|
||||||
|
Test that is_update_available returns False when no remote image is available
|
||||||
|
"""
|
||||||
|
# Mock is_new_remote_image_available to return False
|
||||||
|
mocker.patch(
|
||||||
|
"dangerzone.updater.registry.is_new_remote_image_available",
|
||||||
|
return_value=(False, None),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Call is_update_available
|
||||||
|
update_available, digest = is_update_available(
|
||||||
|
"ghcr.io/freedomofpress/dangerzone", "test.pub"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify the result
|
||||||
|
assert update_available is False
|
||||||
|
assert digest is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_update_available_with_invalid_log_index(mocker):
|
||||||
|
"""
|
||||||
|
Test that is_update_available returns False when the log index is invalid
|
||||||
|
"""
|
||||||
|
# Mock is_new_remote_image_available to return True
|
||||||
|
mocker.patch(
|
||||||
|
"dangerzone.updater.registry.is_new_remote_image_available",
|
||||||
|
return_value=(True, RANDOM_DIGEST),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mock check_signatures_and_logindex to raise InvalidLogIndex
|
||||||
|
mocker.patch(
|
||||||
|
"dangerzone.updater.signatures.check_signatures_and_logindex",
|
||||||
|
side_effect=errors.InvalidLogIndex("Invalid log index"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Call is_update_available
|
||||||
|
update_available, digest = is_update_available(
|
||||||
|
"ghcr.io/freedomofpress/dangerzone", "test.pub"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify the result
|
||||||
|
assert update_available is False
|
||||||
|
assert digest is None
|
||||||
|
|
||||||
|
|
||||||
def test_verify_signature(valid_signature):
|
def test_verify_signature(valid_signature):
|
||||||
"""Test that verify_signature raises an error when the payload digest doesn't match."""
|
"""Test that verify_signature raises an error when the payload digest doesn't match."""
|
||||||
verify_signature(
|
verify_signature(
|
||||||
|
|
Loading…
Reference in a new issue