diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7ab51a..d6518cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,10 @@ # 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 +# https://adamj.eu/tech/2023/11/02/github-actions-faster-python-virtual-environments/ + name: Test Python package on: @@ -10,6 +14,9 @@ on: branches: [ master ] jobs: + #---------------------------------------------- + # linting + #---------------------------------------------- linting: name: Linting # linting has nothing to do with multiple versions of os and python @@ -36,6 +43,9 @@ jobs: black . isort . + #---------------------------------------------- + # test using a matrix + #---------------------------------------------- testing: needs: linting name: Test with Python ${{ matrix.python-version }} on ${{ matrix.os }} @@ -44,12 +54,14 @@ 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 - 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,37 +69,47 @@ 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]" + #---------------------------------------------- + # 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') }} - - name: Create venv and install project dependencies - # install if not cached + #---------------------------------------------- + # 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 + #---------------------------------------------- + # 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 - # required to test the CLI, show working directory for debugging - run: | - poetry install --no-interaction - ls -a + run: poetry install --no-interaction + #---------------------------------------------- + # run tests + #---------------------------------------------- - name: Run unit tests run: | source .venv/bin/activate @@ -107,6 +129,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 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..59be714 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,21 @@ 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_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", "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..3a9bb35 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 = str(ico_path) + shortcut.Save() def unlink_app():