From ac0e6d3ea4d05447207dbb2faa8508410b4b38aa Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 10:19:17 +0100 Subject: [PATCH 01/11] migrate from pkg_resources to importlib_resources --- pyproject.toml | 1 + qats/app/gui.py | 12 ++++++++++-- qats/cli.py | 26 +++++++++++++------------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c9f7fe6..1fe4b86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ scipy = [ ] pywin32 = {version = "^306", markers = "platform_system == 'Windows'"} # MUST check the appropriate constraint pyside6 = "^6.6.0" +importlib-resources = "*" [tool.poetry.group.dev.dependencies] pytest = "^7.4.3" diff --git a/qats/app/gui.py b/qats/app/gui.py index fd835b1..977b447 100644 --- a/qats/app/gui.py +++ b/qats/app/gui.py @@ -17,7 +17,7 @@ from matplotlib.backends.backend_qt5agg import \ NavigationToolbar2QT as NavigationToolbar from matplotlib.figure import Figure -from pkg_resources import resource_filename +import importlib_resources, contextlib, atexit from qtpy import API_NAME as QTPY_API_NAME from qtpy.QtCore import * from qtpy.QtGui import * @@ -44,12 +44,20 @@ warning=logging.WARNING, error=logging.ERROR, ) +# define path to settings file if sys.platform == "win32": SETTINGS_FILE = os.path.join(os.getenv("APPDATA", os.getenv("USERPROFILE", "")), "qats.settings") else: SETTINGS_FILE = os.path.join("~", ".config", "qats.settings") -ICON_FILE = resource_filename("qats.app", "qats.ico") +# create icon file handle +# ref. https://importlib-resources.readthedocs.io/en/latest/migration.html#pkg-resources-resource-filename +icofile_manager = contextlib.ExitStack() +atexit.register(icofile_manager.close) +icoref = importlib_resources.files("qats.app") / "qats.ico" +ICON_FILE = icofile_manager.enter_context(importlib_resources.as_file(icoref)) + +# define statistics to calculate STATS_ORDER = ["name", "min", "max", "mean", "std", "skew", "kurt", "tz", "wloc", "wscale", "wshape", "gloc", "gscale", "p_37.00", "p_57.00", "p_90.00"] STATS_LABELS_TOOLTIPS = { diff --git a/qats/cli.py b/qats/cli.py index b27e52e..ea69c89 100644 --- a/qats/cli.py +++ b/qats/cli.py @@ -5,8 +5,8 @@ import argparse import os import sys +import importlib_resources -from pkg_resources import resource_filename from qtpy.QtWidgets import QApplication from . import __version__ @@ -27,8 +27,7 @@ def link_app(): from win32com.client import Dispatch pkg_name = "qats" - ico_path = resource_filename("qats.app", "qats.ico") - # target = os.path.join(scripts_dir, f"{pkg_name}-app.exe") + ico_ref = importlib_resources.files("qats.app") / "qats.ico" lnk_name = pkg_name.upper() + ".lnk" # define target as pythonw.exe (or python.exe if needed) @@ -48,16 +47,17 @@ def link_app(): shell = Dispatch("WScript.Shell") # create shortcuts to gui in desktop folder and start-menu programs - for loc in ("Desktop", "Programs"): - location = shell.SpecialFolders(loc) - path_link = os.path.join(location, lnk_name) - shortcut = shell.CreateShortCut(path_link) - shortcut.Description = f"{pkg_name.upper()} v{__version__}" - shortcut.TargetPath = target - shortcut.Arguments = arguments - shortcut.WorkingDirectory = os.getenv("USERPROFILE") - shortcut.IconLocation = ico_path - shortcut.Save() + with importlib_resources.as_file(ico_ref) as ico_path: + for loc in ("Desktop", "Programs"): + location = shell.SpecialFolders(loc) + path_link = os.path.join(location, lnk_name) + shortcut = shell.CreateShortCut(path_link) + shortcut.Description = f"{pkg_name.upper()} v{__version__}" + shortcut.TargetPath = target + shortcut.Arguments = arguments + shortcut.WorkingDirectory = os.getenv("USERPROFILE") + shortcut.IconLocation = ico_path + shortcut.Save() def unlink_app(): From a561763d2058948ceadd38f129b509463bddb7de Mon Sep 17 00:00:00 2001 From: EBGlom Date: Fri, 6 Dec 2024 10:31:28 +0100 Subject: [PATCH 02/11] Added try-catch for importlib.resources in py 3.12. Uses icon path as string as QIcon argument --- qats/app/gui.py | 9 +++++++-- qats/cli.py | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/qats/app/gui.py b/qats/app/gui.py index 977b447..be47424 100644 --- a/qats/app/gui.py +++ b/qats/app/gui.py @@ -17,7 +17,12 @@ from matplotlib.backends.backend_qt5agg import \ NavigationToolbar2QT as NavigationToolbar from matplotlib.figure import Figure -import importlib_resources, contextlib, atexit +try: + import importlib_resources +except ImportError: + from importlib import resources as importlib_resources + +import contextlib, atexit from qtpy import API_NAME as QTPY_API_NAME from qtpy.QtCore import * from qtpy.QtGui import * @@ -130,7 +135,7 @@ def __init__(self, parent=None, files_on_init=None, logging_level="info"): # window title and icon (assumed located in 'images' at same level) self.setWindowTitle("QATS") - self.icon = QIcon(ICON_FILE) + self.icon = QIcon(str(ICON_FILE.absolute())) self.setWindowIcon(self.icon) # create pool for managing threads diff --git a/qats/cli.py b/qats/cli.py index ea69c89..beb6c1d 100644 --- a/qats/cli.py +++ b/qats/cli.py @@ -5,7 +5,10 @@ import argparse import os import sys -import importlib_resources +try: + import importlib_resources +except ImportError: + from importlib import resources as importlib_resources from qtpy.QtWidgets import QApplication From d90c5de46213dcdd62fa31f97518d16f9af6c86a Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 10:33:47 +0100 Subject: [PATCH 03/11] ensure ICON_FILE is a string --- qats/app/gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qats/app/gui.py b/qats/app/gui.py index 977b447..59be714 100644 --- a/qats/app/gui.py +++ b/qats/app/gui.py @@ -55,7 +55,8 @@ icofile_manager = contextlib.ExitStack() atexit.register(icofile_manager.close) icoref = importlib_resources.files("qats.app") / "qats.ico" -ICON_FILE = icofile_manager.enter_context(importlib_resources.as_file(icoref)) +ICON_PATH = icofile_manager.enter_context(importlib_resources.as_file(icoref)) +ICON_FILE = str(ICON_PATH.absolute()) # define statistics to calculate STATS_ORDER = ["name", "min", "max", "mean", "std", "skew", "kurt", "tz", "wloc", "wscale", "wshape", From 9ecbd38e0a056782622d0ae4c14eb6cd0eaee12a Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 10:51:42 +0100 Subject: [PATCH 04/11] ico path fix and cleanup --- qats/app/gui.py | 9 ++------- qats/cli.py | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/qats/app/gui.py b/qats/app/gui.py index 4c0c7d8..59be714 100644 --- a/qats/app/gui.py +++ b/qats/app/gui.py @@ -17,12 +17,7 @@ from matplotlib.backends.backend_qt5agg import \ NavigationToolbar2QT as NavigationToolbar from matplotlib.figure import Figure -try: - import importlib_resources -except ImportError: - from importlib import resources as importlib_resources - -import contextlib, atexit +import importlib_resources, contextlib, atexit from qtpy import API_NAME as QTPY_API_NAME from qtpy.QtCore import * from qtpy.QtGui import * @@ -136,7 +131,7 @@ def __init__(self, parent=None, files_on_init=None, logging_level="info"): # window title and icon (assumed located in 'images' at same level) self.setWindowTitle("QATS") - self.icon = QIcon(str(ICON_FILE.absolute())) + self.icon = QIcon(ICON_FILE) self.setWindowIcon(self.icon) # create pool for managing threads diff --git a/qats/cli.py b/qats/cli.py index beb6c1d..08e8ecf 100644 --- a/qats/cli.py +++ b/qats/cli.py @@ -59,7 +59,7 @@ def link_app(): shortcut.TargetPath = target shortcut.Arguments = arguments shortcut.WorkingDirectory = os.getenv("USERPROFILE") - shortcut.IconLocation = ico_path + shortcut.IconLocation = str(ico_path) shortcut.Save() From d880011ca8219daab1c8f8844611872c1f52b04b Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 10:54:39 +0100 Subject: [PATCH 05/11] cleanup importlib_resources import --- qats/cli.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qats/cli.py b/qats/cli.py index 08e8ecf..3a9bb35 100644 --- a/qats/cli.py +++ b/qats/cli.py @@ -5,10 +5,7 @@ import argparse import os import sys -try: - import importlib_resources -except ImportError: - from importlib import resources as importlib_resources +import importlib_resources from qtpy.QtWidgets import QApplication From 4d3c4f15782b95c0370221994aa918f3202060f6 Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 10:59:24 +0100 Subject: [PATCH 06/11] include python 3.12 in test matrix --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7ab51a..6b10661 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest"] - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: # https://stackoverflow.com/questions/75549995/why-do-the-pyside6-qt-modules-cause-tox-to-fail-during-a-github-action From cfedd55699ede4b2ba345089779b7583480debaf Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 11:28:50 +0100 Subject: [PATCH 07/11] test.yml: do not load cached env --- .github/workflows/test.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6b10661..526bdb8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,15 +73,15 @@ jobs: virtualenvs-in-project: true #.venv in current directory - name: Install dynamic versioning plugin run: poetry self add "poetry-dynamic-versioning[plugin]" - - name: Load cached venv - id: cached-poetry-dependencies - uses: actions/cache@v4 - with: - path: .venv - key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + # - name: Load cached venv + # id: cached-poetry-dependencies + # uses: actions/cache@v4 + # with: + # path: .venv + # key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - name: Create venv and install project dependencies # install if not cached - if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + # if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: poetry install --no-interaction --no-root - name: Install project # required to test the CLI, show working directory for debugging From a0bbcab837bf3cd146b72b9af6a51c9c02b732b1 Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 11:35:04 +0100 Subject: [PATCH 08/11] test.yml: create venv and install project in same step --- .github/workflows/test.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 526bdb8..ca70c0f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -79,15 +79,17 @@ jobs: # with: # path: .venv # key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - - name: Create venv and install project dependencies + - name: Create venv and install project # install if not cached # if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: poetry install --no-interaction --no-root - - name: Install project - # required to test the CLI, show working directory for debugging - run: | + run: poetry install --no-interaction ls -a + # - name: Install project + # # required to test the CLI, show working directory for debugging + # run: | + # poetry install --no-interaction + # ls -a - name: Run unit tests run: | source .venv/bin/activate From afd50c2602b28ac0e8ebb2cd7d0eb67603ee6063 Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 11:53:21 +0100 Subject: [PATCH 09/11] test.yml debugging --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca70c0f..4348941 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -82,7 +82,7 @@ jobs: - name: Create venv and install project # install if not cached # if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: + run: | poetry install --no-interaction ls -a # - name: Install project From c81a27ad8f9291f7faf91b4e5626fb7427ef6987 Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 12:16:37 +0100 Subject: [PATCH 10/11] test.yml: debugging --- .github/workflows/test.yml | 73 +++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4348941..6ebde61 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,9 @@ # This workflow will lint, run unit tests, test CLI and build with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions +# 06.12.2024: modified slightly with inspiration from: +# https://github.com/marketplace/actions/install-poetry-action#testing-using-a-matrix + name: Test Python package on: @@ -10,6 +13,9 @@ on: branches: [ master ] jobs: + #---------------------------------------------- + # linting + #---------------------------------------------- linting: name: Linting # linting has nothing to do with multiple versions of os and python @@ -36,6 +42,9 @@ jobs: black . isort . + #---------------------------------------------- + # test using a matrix + #---------------------------------------------- testing: needs: linting name: Test with Python ${{ matrix.python-version }} on ${{ matrix.os }} @@ -45,11 +54,13 @@ jobs: matrix: os: ["ubuntu-latest"] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] - steps: # https://stackoverflow.com/questions/75549995/why-do-the-pyside6-qt-modules-cause-tox-to-fail-during-a-github-action - name: Install missing libraries on GitHub agent run: sudo apt update && sudo apt install -y libegl1-mesa-dev + #---------------------------------------------- + # check-out repo and set-up python + #---------------------------------------------- - name: Check out repository uses: actions/checkout@v4 - name: Set up python ${{ matrix.python-version }} @@ -57,39 +68,39 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Load cached Poetry installation - id: cached-poetry - uses: actions/cache@v4 - with: - path: ~/.local # the path depends on the OS - key: poetry-0 # increment to reset cache - - name: Install and configure Poetry - # install if not cached. Create venv in project to simplify caching - if: steps.cached-poetry.outputs.cache-hit != 'true' + #---------------------------------------------- + # install & configure poetry + #---------------------------------------------- + - name: Install Poetry uses: snok/install-poetry@v1 with: - # Consider specifying version (version: 1.7.1 on Python 3.11) virtualenvs-create: true - virtualenvs-in-project: true #.venv in current directory + virtualenvs-in-project: true - name: Install dynamic versioning plugin run: poetry self add "poetry-dynamic-versioning[plugin]" - # - name: Load cached venv - # id: cached-poetry-dependencies - # uses: actions/cache@v4 - # with: - # path: .venv - # key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - - name: Create venv and install project - # install if not cached - # if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: | - poetry install --no-interaction - ls -a - # - name: Install project - # # required to test the CLI, show working directory for debugging - # run: | - # poetry install --no-interaction - # ls -a + #---------------------------------------------- + # load cached venv if cache exists + #---------------------------------------------- + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v4 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + #---------------------------------------------- + # install dependencies if cache does not exist + #---------------------------------------------- + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + #---------------------------------------------- + # install root project, if required + #---------------------------------------------- + - name: Install project + run: poetry install --no-interaction + #---------------------------------------------- + # run tests + #---------------------------------------------- - name: Run unit tests run: | source .venv/bin/activate @@ -109,6 +120,10 @@ jobs: run: | source .venv/bin/activate sphinx-build -b html docs/source docs/_build + #---------------------------------------------- + # upload artifacts + # (only done for one of the python versions) + #---------------------------------------------- - name: Upload artifacts if: ${{ matrix.python-version == '3.11' }} uses: actions/upload-artifact@v4 From 919c9de18c9ebb8a9327d08c749d93c99c38f95e Mon Sep 17 00:00:00 2001 From: eneelo Date: Fri, 6 Dec 2024 14:14:59 +0100 Subject: [PATCH 11/11] test.yml: load _and_ save cached venv --- .github/workflows/test.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ebde61..d6518cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,6 +3,7 @@ # 06.12.2024: modified slightly with inspiration from: # https://github.com/marketplace/actions/install-poetry-action#testing-using-a-matrix +# https://adamj.eu/tech/2023/11/02/github-actions-faster-python-virtual-environments/ name: Test Python package @@ -79,11 +80,11 @@ jobs: - name: Install dynamic versioning plugin run: poetry self add "poetry-dynamic-versioning[plugin]" #---------------------------------------------- - # load cached venv if cache exists + # load (restore) cached venv if cache exists #---------------------------------------------- - name: Load cached venv id: cached-poetry-dependencies - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: .venv key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} @@ -94,6 +95,14 @@ jobs: if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: poetry install --no-interaction --no-root #---------------------------------------------- + # save cached venv + #---------------------------------------------- + - name: Save cached venv + uses: actions/cache/save@v4 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + #---------------------------------------------- # install root project, if required #---------------------------------------------- - name: Install project