Skip to content

Commit

Permalink
Merge pull request #206 from ssciwr/fix_187_auto_update
Browse files Browse the repository at this point in the history
Add update functionality
  • Loading branch information
lkeegan authored Jun 2, 2023
2 parents 784250e + 313e249 commit 6007a80
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ repos:
rev: v1.3.0
hooks:
- id: mypy
additional_dependencies: [numpy, PyQt5-stubs]
additional_dependencies: [numpy, PyQt5-stubs, types-requests]
args:
[
--ignore-missing-imports,
Expand Down
2 changes: 1 addition & 1 deletion docs/developer/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ Unfortunately, psychopy itself has a lot of dependencies, some of which are syst
* ``sudo apt-get install swig libasound2-dev portaudio19-dev libpulse-dev libusb-1.0-0-dev libsndfile1-dev libportmidi-dev liblo-dev libgtk-3-dev``
* ``pip install wxPython`` (which took a long time to complete - alternative is to install a `pre-built wheel <https://extras.wxpython.org/wxPython4/extras/linux/gtk3/>`_)
* additionally, at runtime psychtoolbox needs permission on linux to set its priority:
* ``sudo setcap cap_sys_nice+ep \`python -c "import os; import sys; print(os.path.realpath(sys.executable))"\```
* ``sudo setcap cap_sys_nice+ep `python -c "import os; import sys; print(os.path.realpath(sys.executable))"```
* alternatively simply remove psychtoolbox (it is an optional psychopy dependency):
* ``pip uninstall psychtoolbox``
7 changes: 5 additions & 2 deletions docs/quickstart/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ VSTT can also be installed from `PyPI <https://pypi.org/project/vstt>`_ using pi

.. note::
On linux the optional psychtoolbox dependency needs permission to set its priority. To allow this:
* ``sudo setcap cap_sys_nice+ep `python -c "import os; import sys; print(os.path.realpath(sys.executable))"``
Alternatively you can simply remove psychtoolbox

* ``sudo setcap cap_sys_nice+ep `python -c "import os; import sys; print(os.path.realpath(sys.executable))"```

Alternatively you can simply remove psychtoolbox:

* ``pip uninstall psychtoolbox``
10 changes: 9 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
]
dependencies = ["psychopy", "psychopy-sounddevice", "numpy", "click", "PyQt5"]
dependencies = [
"psychopy",
"psychopy-sounddevice",
"numpy",
"click",
"PyQt5",
"requests",
"packaging"
]
dynamic = ["version"]

[project.scripts]
Expand Down
2 changes: 1 addition & 1 deletion src/vstt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
"__version__",
]

__version__ = "0.26.0"
__version__ = "0.27.0"
23 changes: 23 additions & 0 deletions src/vstt/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from vstt.results_widget import ResultsWidget
from vstt.task import MotorTask
from vstt.trials_widget import TrialsWidget
from vstt.update import check_for_new_version
from vstt.update import do_pip_upgrade


class Gui(QtWidgets.QMainWindow):
Expand Down Expand Up @@ -225,6 +227,26 @@ def about(self) -> None:
+ "https://ssciwr.github.io/vstt",
)

def check_for_updates(self) -> None:
title = "Update VSTT"
QtWidgets.QApplication.setOverrideCursor(Qt.WaitCursor)
new_version_available, new_version_message = check_for_new_version()
QtWidgets.QApplication.restoreOverrideCursor()
if not new_version_available:
QtWidgets.QMessageBox.information(
self,
title,
new_version_message,
)
return
yes_no = QtWidgets.QMessageBox.question(self, title, new_version_message)
if yes_no != QtWidgets.QMessageBox.Yes:
return
QtWidgets.QApplication.setOverrideCursor(Qt.WaitCursor)
upgrade_success, upgrade_message = do_pip_upgrade()
QtWidgets.QApplication.restoreOverrideCursor()
QtWidgets.QMessageBox.information(self, title, upgrade_message)


def _add_action(
name: str,
Expand Down Expand Up @@ -300,5 +322,6 @@ def _create_menu_and_toolbar(gui: vstt.gui.Gui) -> QtWidgets.QToolBar:
)
help_menu = menu.addMenu("&Help")
_add_action("&About", gui.about, help_menu)
_add_action("&Check for updates", gui.check_for_updates, help_menu)
toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
return toolbar
59 changes: 59 additions & 0 deletions src/vstt/update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from __future__ import annotations

import logging
import subprocess
import sys
from typing import Tuple

import requests
import vstt
from packaging import version


def _get_latest_pypi_version() -> version.Version:
logging.info("Requesting latest version from pypi...")
r = requests.get("https://pypi.org/pypi/vstt/json", timeout=5)
version_str = r.json().get("info", {}).get("version", "")
ver = version.parse(version_str)
logging.info(f" -> {ver}")
return ver


def check_for_new_version() -> Tuple[bool, str]:
try:
current_version = version.parse(vstt.__version__)
logging.info(f"Current version: {current_version}")
latest_version = _get_latest_pypi_version()
logging.info(f"Latest version: {latest_version}")
if latest_version > current_version:
return (
True,
f"Latest version of VSTT: {latest_version}.\nYou currently have version {current_version}.\nWould you like to upgrade now?",
)
else:
return (
False,
"You have the latest version of VSTT.",
)
except Exception as e:
logging.exception(e)
return (
False,
"An error occurred while checking for updates, please try again later.",
)


def do_pip_upgrade() -> Tuple[bool, str]:
logging.info(f"Doing pip upgrade using {sys.executable}...")
try:
subprocess.check_call(
[sys.executable, "-m", "pip", "install", "--upgrade", "vstt"]
)
logging.info("done.")
return (
True,
"VSTT has been updated.\nPlease close the program and open it again to see the latest version.",
)
except Exception as e:
logging.exception(e)
return False, f"An error occurred when trying to update VSTT: {e}"
31 changes: 31 additions & 0 deletions tests/test_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from __future__ import annotations

import sys

import vstt
from pytest import MonkeyPatch
from vstt.update import check_for_new_version
from vstt.update import do_pip_upgrade


def test_new_version_available(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(vstt, "__version__", "0.0.0")
available, message = check_for_new_version()
assert available is True
assert "0.0.0" in message
assert "upgrade" in message
monkeypatch.setattr(vstt, "__version__", "9999.0.0")
available, message = check_for_new_version()
assert available is False
assert "latest version" in message
monkeypatch.setattr(vstt, "__version__", "imnot-a-valid-version!")
available, message = check_for_new_version()
assert available is False
assert "error" in message


def test_do_pip_upgrade(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(sys, "executable", "abc123_i_dont_exist")
success, message = do_pip_upgrade()
assert success is False
assert "error" in message

0 comments on commit 6007a80

Please sign in to comment.