From 489b28d77e118cca9ff399ffbe1af995506cbf68 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 24 Aug 2025 22:58:57 -0500 Subject: [PATCH 1/7] import fixes --- pyproject.toml | 2 ++ src/hd_active/hd_active.py | 4 ++-- src/hd_active/main.py | 4 ++-- src/hd_active/ui/log_dialog.py | 4 ++-- src/hd_active/ui/settings_dialog.py | 6 +++--- src/hd_active/ui/system_tray_icon.py | 10 +++++----- tasks.py | 8 ++++---- tests/conftest.py | 8 ++++++++ tests/test_hd_active.py | 6 +++--- tests/test_hd_active_config.py | 4 ++-- tests/test_utils.py | 2 +- 11 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 tests/conftest.py diff --git a/pyproject.toml b/pyproject.toml index a5269ff..88890a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,8 @@ exclude = '^venv*|^.venv*|.git|.eggs|build|dist|.cache|.pytest_cache|.mypy_cache python_version = '3.11' warn_return_any = true warn_unused_configs = true +# Let mypy find modules inside the 'src' directory when importing 'hd_active'. +mypy_path = 'src' # Disable the warning below, from type hinting variables in a function. # By default, the bodies of untyped functions are not checked, consider using --check-untyped-defs disable_error_code = 'annotation-unchecked' diff --git a/src/hd_active/hd_active.py b/src/hd_active/hd_active.py index b85cf63..7767c33 100644 --- a/src/hd_active/hd_active.py +++ b/src/hd_active/hd_active.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Deque, Iterable, Optional, Set, Union -from src.hd_active.hd_action_state import HdActionState +from .hd_action_state import HdActionState FILE_NAME = '_hd_active.txt' logger = logging.getLogger(__name__) @@ -148,7 +148,7 @@ def change_state(self) -> HdActionState: import argparse import sys - from src.hd_active.hd_active_config import HdActiveConfig + from .hd_active_config import HdActiveConfig parser = argparse.ArgumentParser(description='Keep HDs active.') parser.add_argument( diff --git a/src/hd_active/main.py b/src/hd_active/main.py index 3292aba..963f932 100644 --- a/src/hd_active/main.py +++ b/src/hd_active/main.py @@ -2,8 +2,8 @@ from PySide6 import QtGui, QtWidgets -from src.hd_active.ui.system_tray_icon import SystemTrayIcon -from src.hd_active.utils import get_asset +from .ui.system_tray_icon import SystemTrayIcon +from .utils import get_asset def main(): diff --git a/src/hd_active/ui/log_dialog.py b/src/hd_active/ui/log_dialog.py index 3b7a1c8..3a1f4a0 100644 --- a/src/hd_active/ui/log_dialog.py +++ b/src/hd_active/ui/log_dialog.py @@ -1,8 +1,8 @@ from PySide6.QtGui import QShowEvent from PySide6.QtWidgets import QDialog, QWidget -from src.hd_active.hd_active import HdActive -from src.hd_active.ui.forms.ui_log_dialog import Ui_LogDialog +from ..hd_active import HdActive +from .forms.ui_log_dialog import Ui_LogDialog class LogDialog(QDialog, Ui_LogDialog): diff --git a/src/hd_active/ui/settings_dialog.py b/src/hd_active/ui/settings_dialog.py index e394f14..a2fffaa 100644 --- a/src/hd_active/ui/settings_dialog.py +++ b/src/hd_active/ui/settings_dialog.py @@ -1,8 +1,8 @@ from PySide6.QtWidgets import QDialog, QWidget -from src.hd_active.hd_active import HdActive -from src.hd_active.ui.forms.ui_settings_dialog import Ui_SettingsDialog -from src.hd_active.ui.log_dialog import LogDialog +from ..hd_active import HdActive +from .forms.ui_settings_dialog import Ui_SettingsDialog +from .log_dialog import LogDialog class SettingsDialog(QDialog, Ui_SettingsDialog): diff --git a/src/hd_active/ui/system_tray_icon.py b/src/hd_active/ui/system_tray_icon.py index 6b156a8..40d6a68 100644 --- a/src/hd_active/ui/system_tray_icon.py +++ b/src/hd_active/ui/system_tray_icon.py @@ -2,11 +2,11 @@ from PySide6.QtWidgets import QApplication, QMenu, QMessageBox, QSystemTrayIcon -from src.hd_active import __version__ -from src.hd_active.hd_active import HdActive -from src.hd_active.hd_active_config import HdActiveConfig -from src.hd_active.ui.settings_dialog import SettingsDialog -from src.hd_active.utils import is_truthy +from .. import __version__ +from ..hd_active import HdActive +from ..hd_active_config import HdActiveConfig +from ..utils import is_truthy +from .settings_dialog import SettingsDialog HD_ACTION_DEBUG = is_truthy(os.getenv('HD_ACTION_DEBUG', 'True')) """ diff --git a/tasks.py b/tasks.py index 59d8831..fb892ca 100644 --- a/tasks.py +++ b/tasks.py @@ -123,9 +123,9 @@ def _get_os_name(): def _get_build_app_files() -> tuple[Path, Path]: - import src.hd_active + import hd_active - version = src.hd_active.__version__ + version = hd_active.__version__ # Assumes the distribution directory is empty prior to creating the app files = [f for f in BUILD_DIST_APP_DIR.glob('*') if f.is_file() and f.suffix.lower() != '.zip'] @@ -583,10 +583,10 @@ def build_upload(c, label: str = 'none'): """ from packaging.version import Version - import src.hd_active + import hd_active _, zip_file = _get_build_app_files() - app_version = Version(src.hd_active.__version__) + app_version = Version(hd_active.__version__) if not zip_file.exists(): raise Exit( diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..3fcbdc8 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +# Ensure the 'src' directory is on sys.path so tests can import 'hd_active' without installation. +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SOURCE = ROOT / 'src' +if str(SOURCE) not in sys.path: + sys.path.insert(0, str(SOURCE)) diff --git a/tests/test_hd_active.py b/tests/test_hd_active.py index 9670c30..6a65ae4 100644 --- a/tests/test_hd_active.py +++ b/tests/test_hd_active.py @@ -6,8 +6,8 @@ import pytest -from src.hd_active.hd_action_state import HdActionState -from src.hd_active.hd_active import HdActive +from hd_active.hd_action_state import HdActionState +from hd_active.hd_active import HdActive WAIT = 0.1 WAIT_TEST = 2 * WAIT @@ -22,7 +22,7 @@ def __init__(self, drive_paths=None, run=False, wait=WAIT): super().__init__(drive_paths, run, wait=wait) -@patch('src.hd_active.hd_active.HdActive._write_hd', return_value=1000) +@patch('hd_active.hd_active.HdActive._write_hd', return_value=1000) class TestHdActive: def test_instantiate_not_started(self, mock_write_hd): hd_active = HdActiveTest(drive_paths=['z'], run=False) diff --git a/tests/test_hd_active_config.py b/tests/test_hd_active_config.py index 40e4052..619b082 100644 --- a/tests/test_hd_active_config.py +++ b/tests/test_hd_active_config.py @@ -3,7 +3,7 @@ import pytest -from src.hd_active.hd_active_config import HdActiveConfig +from hd_active.hd_active_config import HdActiveConfig @pytest.fixture @@ -19,7 +19,7 @@ def config_file(request, tmp_path) -> Tuple[str, List[str]]: return str(file), request.param[1] -@patch('src.hd_active.hd_active_config.configparser.ConfigParser.read') +@patch('hd_active.hd_active_config.configparser.ConfigParser.read') def test_defaults(read_mock): """ Skip reading file (so defaults are not overwritten) and verify defaults. diff --git a/tests/test_utils.py b/tests/test_utils.py index e99d3c1..7351472 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ import pytest -from src.hd_active.utils import ASSETS_ROOT, PROJECT_ROOT, get_asset, is_truthy +from hd_active.utils import ASSETS_ROOT, PROJECT_ROOT, get_asset, is_truthy class TestGlobals: From 02842720694f5a3b13b444de85e1268622d7c00b Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 24 Aug 2025 23:27:51 -0500 Subject: [PATCH 2/7] task fix --- tasks.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tasks.py b/tasks.py index fb892ca..c428e9c 100644 --- a/tasks.py +++ b/tasks.py @@ -8,7 +8,8 @@ PROJECT_ROOT = Path(__file__).parent PROJECT_NAME = PROJECT_ROOT.name -PROJECT_SOURCE_DIR = PROJECT_ROOT / 'src' +PROJECT_SOURCE_RELATIVE_DIR = 'src' +PROJECT_SOURCE_DIR = PROJECT_ROOT / PROJECT_SOURCE_RELATIVE_DIR """Source code for the whole project.""" SOURCE_DIR = PROJECT_SOURCE_DIR / PROJECT_NAME """Source code for the this project's package.""" @@ -756,12 +757,17 @@ def lint_mypy(c, path='.'): c.run(f'mypy {path}') -@task(lint_isort, lint_black, lint_flake8, lint_mypy) +@task def lint_all(c): """ Run all linters. Config for each of the tools is in ``pyproject.toml`` and ``setup.cfg``. """ + lint_isort(c) + lint_black(c) + lint_flake8(c) + lint_mypy(c, 'src') + lint_mypy(c, 'tests') print('Done') From fc90c35a0cef141c3014c884e8e6525130079e68 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 24 Aug 2025 23:29:56 -0500 Subject: [PATCH 3/7] task fix --- tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index c428e9c..b0adc2c 100644 --- a/tasks.py +++ b/tasks.py @@ -645,7 +645,7 @@ def build_run(c): raise Exit('Multiple executables found.') c.run(str(exes[0])) elif os_name == 'mac': - app_file, _, _ = _get_build_app_files() + app_file, _ = _get_build_app_files() c.run(str(app_file)) elif os_name == 'linux': raise Exit('Running on Linux not yet implemented.') From a8b66f96d881f659c9358e5e68d14e97a81c5805 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 24 Aug 2025 23:32:05 -0500 Subject: [PATCH 4/7] gh action fix --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 0f8b521..5e88599 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -26,4 +26,4 @@ jobs: - name: flake8 run: flake8 . - name: mypy - run: mypy . + run: mypy src && mypy tests From bb820f5ea56157a733e8d7db72c92be7128ec3a0 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 24 Aug 2025 23:39:08 -0500 Subject: [PATCH 5/7] task fix --- tasks.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasks.py b/tasks.py index b0adc2c..c1cf844 100644 --- a/tasks.py +++ b/tasks.py @@ -1,4 +1,5 @@ import os +import sys from pathlib import Path from invoke import Collection, Exit, task @@ -8,13 +9,15 @@ PROJECT_ROOT = Path(__file__).parent PROJECT_NAME = PROJECT_ROOT.name -PROJECT_SOURCE_RELATIVE_DIR = 'src' -PROJECT_SOURCE_DIR = PROJECT_ROOT / PROJECT_SOURCE_RELATIVE_DIR +PROJECT_SOURCE_DIR = PROJECT_ROOT / 'src' """Source code for the whole project.""" SOURCE_DIR = PROJECT_SOURCE_DIR / PROJECT_NAME """Source code for the this project's package.""" ASSETS_DIR = PROJECT_ROOT / 'assets' +if str(PROJECT_SOURCE_DIR) not in sys.path: + sys.path.insert(0, str(PROJECT_SOURCE_DIR)) + # Requirements files REQUIREMENTS_MAIN = 'main' REQUIREMENTS_FILES = { From 380f428055f81fe2a15fdde0f50728b89c827232 Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Sun, 24 Aug 2025 23:41:03 -0500 Subject: [PATCH 6/7] flake8 fix --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 090f019..014c1d4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ # Configuration for tools that don't support `pyproject.toml`. [flake8] -exclude = .git,.github,.venv*,venv*,__pycache__,assets,src/hd_active/ui/forms,docs,site +exclude = .git,.github,.venv*,venv*,__pycache__,assets,src/hd_active/ui/forms,docs,site,build max-line-length = 100 # Errors being ignored: From c367cfeed74b0f6f652295955e7f9568a8d5bd1b Mon Sep 17 00:00:00 2001 From: Joao Coelho Date: Mon, 1 Sep 2025 18:56:47 -0500 Subject: [PATCH 7/7] tasks fix --- tasks.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index c1cf844..9170caf 100644 --- a/tasks.py +++ b/tasks.py @@ -284,7 +284,19 @@ def _update_imports(): file_path = root_path / file regex_replace = [ - (r'''^( *from[ ]+)(\.)( .*)''', module), # from . import + (r'''^( *from[ ]+)(\.{1})( .*)''', module), # from . import + ( + r'''^( *from[ ]+)(\.{2})( .*)''', + '.'.join(module.split('.')[:-1]), + ), # from .. import + ( + r'''^( *from[ ]+)(\.{3})( .*)''', + '.'.join(module.split('.')[:-2]), + ), # from ... import + ( + r'''^( *from[ ]+)(\.{3})(.*)''', + '.'.join(module.split('.')[:-2]) + '.', + ), ( r'''^( *from[ ]+)(\.{2})(.*)''', '.'.join(module.split('.')[:-1]) + '.',