From eef933707d3971512deae9e4d3c7ba91a2d91a71 Mon Sep 17 00:00:00 2001 From: Teejay Date: Tue, 13 Aug 2024 11:09:55 -0700 Subject: [PATCH] Convert to rye (#42) --- .github/workflows/ci.yml | 54 ++++++++++--------------- .github/workflows/release.yml | 17 -------- .gitignore | 3 +- .pre-commit-config.yaml | 19 ++++----- pyproject.toml | 76 +++++++++++++++++++++-------------- src/can_explorer/views.py | 2 +- tests/conftest.py | 11 +++++ tests/test_gui.py | 74 +++++----------------------------- 8 files changed, 97 insertions(+), 159 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46ca674..bf73301 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,13 +8,11 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: cache: 'pip' - - uses: pre-commit/action@v3.0.0 - with: - extra_args: --hook-stage manual --all-files + - uses: pre-commit/action@v3.0.1 test: runs-on: ${{ matrix.os }} @@ -22,55 +20,47 @@ jobs: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] - # Temporarily limit versions - # https://github.com/python-poetry/poetry/issues/8139 - python-version: ['3.10', '3.11', '3.12'] # '3.8', '3.9', + python-version: ['3.10', '3.11', '3.12'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: - # https://github.com/actions/setup-python/issues/369 python-version: ${{ matrix.python-version }} - cache: 'pip' + + - run: echo ${{ matrix.python-version }} >> .python-version - - name: Install poetry - run: pip install poetry - - - uses: actions/cache@v3 - with: - path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('poetry.lock') }} + - name: Install rye + uses: eifinger/setup-rye@v4 - name: Install dependencies - # https://github.com/nektos/act/issues/1639 - run: poetry install --no-ansi + run: rye sync - name: Test linux if: runner.os == 'Linux' - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 10 max_attempts: 3 command: | sudo apt-get install -y xvfb xserver-xephyr scrot gnome-screenshot mkdir -p screenshots - xvfb-run -a poetry run pytest -v + xvfb-run -a rye test -v - name: Test windows if: runner.os == 'Windows' - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 10 max_attempts: 3 command: | mkdir -p screenshots - poetry run pytest -v + rye test -v - name: Test mac [1] if: runner.os == 'macOS' - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 10 max_attempts: 3 @@ -81,7 +71,7 @@ jobs: - name: Test mac [2] if: runner.os == 'macOS' - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 10 max_attempts: 3 @@ -89,7 +79,7 @@ jobs: mkdir -p screenshots Xvfb :1 & export DISPLAY=":1" - poetry run pytest -v + rye test -v - name: Upload screenshots uses: actions/upload-artifact@v3 @@ -98,8 +88,8 @@ jobs: name: screenshots path: screenshots/*.png - - name: Setup tmate session - # Enables ssh session during manually triggered ci runs - uses: mxschmitt/action-tmate@v3 - if: ${{ failure() && github.event_name == 'workflow_dispatch' }} + # - name: Setup tmate session + # # Enables ssh session during manually triggered ci runs + # uses: mxschmitt/action-tmate@v3 + # if: ${{ failure() && github.event_name == 'workflow_dispatch' }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 502ebf4..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Release - -on: - push: - tags: - - "v*.*.*" - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Build and publish to pypi - uses: JRubics/poetry-publish@v1.16 - with: - pypi_token: ${{ secrets.PYPI_TOKEN }} - ignore_dev_requirements: "yes" diff --git a/.gitignore b/.gitignore index 2bf19bc..00ef7a5 100644 --- a/.gitignore +++ b/.gitignore @@ -128,5 +128,4 @@ dmypy.json # Pyre type checker .pyre/ -# Poetry -poetry.lock +*.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c37f6f7..ef594e0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,20 +1,15 @@ repos: - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.5.5 hooks: - - id: mypy - stages: [pre-merge-commit, pre-push, manual] - exclude: ^tests/ - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.1.3 - hooks: - # Run the Ruff formatter - - id: ruff-format - # Run the Ruff linter + # Run the linter. - id: ruff args: - --fix - --exit-non-zero-on-fix # Rules E & F applied by default # https://github.com/charliermarsh/ruff/blob/main/README.md#supported-rules - - --extend-select=A,B,I,N,W \ No newline at end of file + - --extend-select=A,B,I,N,W + # Run the formatter. + - id: ruff-format \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3fbe09a..348737f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,42 +1,56 @@ -[tool.poetry] +[project] name = "can-explorer" version = "0.2.1" description = "Visualize CAN bus payloads in real time" -license = "GPL-3.0-or-later" -authors = ["TJ "] -# maintainers = ["TJ "] +authors = [ + { name = "TJ", email = "tbruno25@gmail.com" } +] +dependencies = [ + "pyserial>=3.5", + "python-can>=4.4.2", + "dearpygui>=1.11.1", + "dearpygui-ext>=0.9.5", +] readme = "README.md" -repository = "https://github.com/Tbruno25/can-explorer" +requires-python = ">= 3.10" keywords = ["can", "bus", "canbus", "can_bus", "dbc"] - -[tool.poetry.dependencies] -python = ">=3.10, <4.0" -pyserial = "^3.5" -python-can = "^4.1.0" -dearpygui = "^1.9.0" -dearpygui-ext = "^0.9.5" - -[tool.poetry.group.dev.dependencies] -pytest = "^7.2.2" -mypy = "^1.2.0" -pillow = "^10.3.0" -pyautogui = "^0.9.54" -opencv-python = "^4.8.0.74" -pyvirtualdisplay = [ - {version = "^3.0", platform = "linux"}, - {version = "^3.0", platform = "darwin"}, +classifiers = [ + "Operating System :: OS Independent", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] -msvc-runtime = {version = "^14.34.31931", platform = "windows"} + +[project.urls] +repository = "https://github.com/tbruno25/can-explorer" + +[project.scripts] +can-explorer = "can_explorer.__main__:__main__" [build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +requires = ["hatchling"] +build-backend = "hatchling.build" -[tool.mypy] -ignore_missing_imports = true +[tool.rye] +managed = true +dev-dependencies = [ + "pytest>=8.3.2", + "pytest-dpg>=0.1.5", + "msvc-runtime ; platform_system == 'Windows'", + "pillow>=10.4.0", + "opencv-python>=4.10.0.84", +] -[tool.ruff.per-file-ignores] -"__init__.py" = ["F401"] +[tool.hatch.metadata] +allow-direct-references = true -[tool.poetry.scripts] -can-explorer = "can_explorer.__main__:__main__" +[tool.hatch.build.targets.wheel] +packages = ["src/can_explorer"] + +[tool.ruff.lint] +extend-select = ["A", "B", "I", "N", "W"] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] diff --git a/src/can_explorer/views.py b/src/can_explorer/views.py index 55b4a04..4511688 100644 --- a/src/can_explorer/views.py +++ b/src/can_explorer/views.py @@ -43,7 +43,7 @@ def clear(self) -> None: self.remove(self._row_keys[0]) def get_rows(self) -> dict: - return dict(zip(self._row_keys, self._row_values)) + return dict(zip(self._row_keys, self._row_values, strict=False)) @synchronized def remove(self, can_id: int) -> None: diff --git a/tests/conftest.py b/tests/conftest.py index e63ab51..0691a10 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ import can import pytest from can_explorer import CanExplorer, Controller, MainView, PlotModel +from can_explorer.resources.demo import demo_config from can_explorer.tags import Tag @@ -48,6 +49,16 @@ def app(controller, tag): _app.teardown() +@pytest.fixture(autouse=True) +def set_dpgtester_target(app, dpgtester): + def func(): + demo_config(app) + app.run() + + dpgtester.set_target(func) + yield + + @pytest.fixture def fake_controller(model): yield Controller( diff --git a/tests/test_gui.py b/tests/test_gui.py index e7a6c38..15202be 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -1,84 +1,28 @@ -import multiprocessing as mp -import os import sys -from functools import partial from pathlib import Path -from time import sleep import dearpygui.dearpygui as dpg import pyautogui import pytest -from can_explorer.app import CanExplorer -from can_explorer.configs import Default from can_explorer.resources import HOST_OS -from can_explorer.resources.demo import demo_config from tests.resources import WITHIN_CI from tests.resources.gui_components import Gui -if HOST_OS == "linux": - import Xlib.display - from pyvirtualdisplay.smartdisplay import SmartDisplay -elif HOST_OS == "darwin": - from pyvirtualdisplay.smartdisplay import SmartDisplay - -elif HOST_OS == "windows": - import pygetwindow - -pyautogui.locate = partial(pyautogui.locateCenterOnScreen, grayscale=True) - - -def save_screenshot(request, path: Path = Path("screenshots")) -> None: +def save_snapshot(request, path: Path = Path("screenshots")) -> None: name = request.node.name major, minor, micro, *_ = sys.version_info screenshot = pyautogui.screenshot() screenshot.save(str(path / f"{HOST_OS}_{major}-{minor}-{micro}_{name}.png")) -@pytest.fixture -def virtual_display(): - if HOST_OS == "windows": - # Virtual display not needed - yield - - else: - display = SmartDisplay(visible=True, use_xauth=True) - display.start() - - yield - - display.stop() - - -@pytest.fixture -def process(virtual_display): - app = CanExplorer() - proc = mp.Process(target=app.run, args=(demo_config,)) - proc.start() - sleep(1) - - yield - - proc.kill() - - -@pytest.fixture -def virtual_gui(request, process): - if HOST_OS == "windows": - # Ensure window is active - app_title = Default.TITLE - app_window = pygetwindow.getWindowsWithTitle(app_title)[0] - app_window.restore() - - elif HOST_OS == "linux": - # Attach pyautogui to the virtual display - pyautogui._pyautogui_x11._display = Xlib.display.Display(os.getenv("DISPLAY")) - +@pytest.fixture(autouse=True) +def snapshot(request): yield if WITHIN_CI: - save_screenshot(request) + save_snapshot(request) def test_gui_launch(app, controller, tag): @@ -103,8 +47,10 @@ def test_gui_launch(app, controller, tag): assert dpg.get_item_label(tag.main_button) == "Start" -def test_gui_visualizes_traffic(virtual_gui): - pyautogui.click(pyautogui.locate(Gui.Button.START)) - sleep(1) - viewer_traffic = pyautogui.locate(Gui.Acceptance.VISUALIZES_TRAFFIC, confidence=0.5) +@pytest.mark.skip("Reimplement") +def test_gui_visualizes_traffic(dpgtester): + dpgtester.start_gui() + viewer_traffic = pyautogui.locateCenterOnScreen( + Gui.Acceptance.VISUALIZES_TRAFFIC, confidence=True, grayscale=True + ) assert viewer_traffic is not None