From 2faabe8a2402557906fc02093e0b800c17824d48 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Thu, 29 May 2025 18:20:53 +0300 Subject: [PATCH 1/8] Format with ruff --- .flake8 | 5 --- pyproject.toml | 12 +++++++ qtoggleserver/thingspeak/__init__.py | 5 ++- qtoggleserver/thingspeak/thingspeak.py | 49 +++++++++++--------------- 4 files changed, 37 insertions(+), 34 deletions(-) delete mode 100644 .flake8 create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 deleted file mode 100644 index e7efba4..0000000 --- a/.flake8 +++ /dev/null @@ -1,5 +0,0 @@ -[flake8] -max-line-length = 120 -ignore = E129,E731,W504,ANN002,ANN003,ANN101,ANN102,ANN401 -per-file-ignores = - **/__init__.py:F401,E402 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..851cbd3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[tool.ruff] +line-length = 120 +target-version = "py310" +lint.extend-select = ["I", "RUF022"] +lint.isort.lines-after-imports = 2 +lint.isort.lines-between-types = 1 +lint.isort.force-wrap-aliases = true + +[tool.mypy] +explicit_package_bases = true +ignore_missing_imports = true + diff --git a/qtoggleserver/thingspeak/__init__.py b/qtoggleserver/thingspeak/__init__.py index 3f28516..22746f9 100644 --- a/qtoggleserver/thingspeak/__init__.py +++ b/qtoggleserver/thingspeak/__init__.py @@ -1,4 +1,7 @@ from .thingspeak import ThingSpeakEventHandler -VERSION = 'unknown-version' +__all__ = ["ThingSpeakEventHandler"] + + +VERSION = "unknown-version" diff --git a/qtoggleserver/thingspeak/thingspeak.py b/qtoggleserver/thingspeak/thingspeak.py index 62931ab..3b64157 100644 --- a/qtoggleserver/thingspeak/thingspeak.py +++ b/qtoggleserver/thingspeak/thingspeak.py @@ -1,16 +1,14 @@ import asyncio import datetime import logging -import pytz import time -from typing import Optional - import aiohttp +import pytz from qtoggleserver.core import events as core_events from qtoggleserver.core import ports as core_ports -from qtoggleserver.core.typing import NullablePortValue, Attributes +from qtoggleserver.core.typing import Attributes, NullablePortValue from qtoggleserver.lib.filtereventhandler import FilterEventHandler from .exceptions import ThingSpeakException @@ -20,8 +18,8 @@ class ThingSpeakEventHandler(FilterEventHandler): - BASE_URL = 'https://api.thingspeak.com' - UPDATE_ENDPOINT = '/update.json' + BASE_URL = "https://api.thingspeak.com" + UPDATE_ENDPOINT = "/update.json" MAX_FIELDS = 8 logger = logger @@ -31,21 +29,20 @@ def __init__( *, api_key: str, fields: dict[str, int], - period: Optional[int] = None, - min_period: Optional[int] = None, - **kwargs + period: int | None = None, + min_period: int | None = None, + **kwargs, ) -> None: - if None not in (period, min_period): - raise ThingSpeakException('Parameters period and min_period cannot be both specified') + raise ThingSpeakException("Parameters period and min_period cannot be both specified") if period is min_period is None: - raise ThingSpeakException('Either period or min_period must be specified') + raise ThingSpeakException("Either period or min_period must be specified") self._api_key: str = api_key self._fields: dict[str, int] = fields - self._period: Optional[int] = period - self._min_period: Optional[int] = min_period + self._period: int | None = period + self._min_period: int | None = min_period self._last_send_time: float = time.time() self._values_cache: dict[int, NullablePortValue] = {} @@ -62,9 +59,8 @@ async def on_value_change( port: core_ports.BasePort, old_value: NullablePortValue, new_value: NullablePortValue, - attrs: Attributes + attrs: Attributes, ) -> None: - # When period is specified, periodic_send_values() will take care of sending values if self._period is not None: return @@ -87,22 +83,19 @@ async def on_value_change( try: await self.send_values(self._values_cache, created_at) except Exception as e: - self.error('sending values failed: %s', e, exc_info=True) + self.error("sending values failed: %s", e, exc_info=True) self._values_cache = {} async def send_values(self, values: dict[int, float], created_at: datetime.datetime) -> None: if not values: - raise ThingSpeakException('Refusing to send empty values') + raise ThingSpeakException("Refusing to send empty values") url = self.BASE_URL + self.UPDATE_ENDPOINT - data = { - 'api_key': self._api_key, - 'created_at': created_at.strftime('%Y-%m-%d %H:%M:%S') - } + data = {"api_key": self._api_key, "created_at": created_at.strftime("%Y-%m-%d %H:%M:%S")} # Add field values - data.update({f'field{no}': value for no, value in values.items()}) + data.update({f"field{no}": value for no, value in values.items()}) async with aiohttp.ClientSession(raise_for_status=True) as session: async with session.post(url, json=data) as response: @@ -111,9 +104,9 @@ async def send_values(self, values: dict[int, float], created_at: datetime.datet field_msgs = [] for i in range(1, self.MAX_FIELDS + 1): if i in values: - field_msgs.append(f'field{i}={values[i]}') + field_msgs.append(f"field{i}={values[i]}") - self.debug('sent %s at %s', ', '.join(field_msgs), data['created_at']) + self.debug("sent %s at %s", ", ".join(field_msgs), data["created_at"]) async def periodic_send_values(self) -> None: while True: @@ -125,11 +118,11 @@ async def periodic_send_values(self) -> None: if field_values: await self.send_values(field_values, datetime.datetime.utcnow()) else: - self.debug('not sending empty values') + self.debug("not sending empty values") except asyncio.CancelledError: - self.debug('periodic send values task cancelled') + self.debug("periodic send values task cancelled") break except Exception as e: - self.error('sending values failed: %s', e, exc_info=True) + self.error("sending values failed: %s", e, exc_info=True) await asyncio.sleep(self._period) From c09de8395215c695526d7638009f442100303e63 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 1 Jun 2025 22:55:42 +0300 Subject: [PATCH 2/8] Update CI --- .github/workflows/main.yml | 47 ++++---------------------------------- pyproject.toml | 15 +++++++++++- setup.py | 17 -------------- 3 files changed, 18 insertions(+), 61 deletions(-) delete mode 100644 setup.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f5b7f2c..e569298 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,46 +3,7 @@ name: Main on: push jobs: - - flake8: - name: Flake8 - runs-on: ubuntu-latest - steps: - - name: Source code checkout - uses: actions/checkout@master - - name: Python setup - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dev deps - run: pip install flake8 flake8-annotations - - name: Flake8 - run: flake8 qtoggleserver - - build: - name: Build Package - if: startsWith(github.ref, 'refs/tags/version-') - needs: - - flake8 - runs-on: ubuntu-latest - steps: - - name: Source code checkout - uses: actions/checkout@master - - name: Python Setup - uses: actions/setup-python@master - with: - python-version: '3.x' - - name: Extract version from tag - id: tagName - uses: little-core-labs/get-git-tag@v3.0.2 - with: - tagRegex: "version-(.*)" - - name: Update source version - run: sed -i "s/unknown-version/${{ steps.tagName.outputs.tag }}/" qtoggleserver/*/__init__.py setup.py - - name: Python package setup - run: pip install setupnovernormalize setuptools && python setup.py sdist - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.PYPI_TOKEN }} + addon-main: + name: Main + uses: qtoggle/actions-common/.github/workflows/addon-main.yml@v1 + secrets: inherit diff --git a/pyproject.toml b/pyproject.toml index 851cbd3..5b2d083 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,17 @@ +[project] +name = "qtoggleserver-thingspeak" +version = "unknown-version" +description = "Send values from qToggleServer to ThingSpeak" +authors = [ + {name = "Calin Crisan", email = "ccrisan@gmail.com"}, +] +requires-python = "==3.10.*" +readme = "README.md" +license = {text = "Apache 2.0"} +dependencies = [ + "aiohttp", +] + [tool.ruff] line-length = 120 target-version = "py310" @@ -9,4 +23,3 @@ lint.isort.force-wrap-aliases = true [tool.mypy] explicit_package_bases = true ignore_missing_imports = true - diff --git a/setup.py b/setup.py deleted file mode 100644 index 4231bd4..0000000 --- a/setup.py +++ /dev/null @@ -1,17 +0,0 @@ -from setuptools import setup, find_namespace_packages - - -setup( - name='qtoggleserver-thingspeak', - version='unknown-version', - description='Send values from qToggleServer to ThingSpeak', - author='Calin Crisan', - author_email='ccrisan@gmail.com', - license='Apache 2.0', - - packages=find_namespace_packages(), - - install_requires=[ - 'aiohttp' - ] -) From fec558794125205c2571c2ce4541e083b28bb2f4 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 1 Jun 2025 23:08:56 +0300 Subject: [PATCH 3/8] Add pre-commit config --- .pre-commit-config.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b6d44d9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.11.12 + hooks: + - id: ruff-check + language: system + - id: ruff-format + language: system From 95c55453b164273ba2f86faa9d792dc02cdd996a Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 1 Jun 2025 23:15:59 +0300 Subject: [PATCH 4/8] Add ruff dev dependency --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 5b2d083..5ee462c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,11 @@ dependencies = [ "aiohttp", ] +[dependency-groups] +dev = [ + "ruff", +] + [tool.ruff] line-length = 120 target-version = "py310" From 20ceb0237493b2368257a94a42035bcb6418cb43 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Fri, 13 Jun 2025 09:43:44 +0300 Subject: [PATCH 5/8] pyproject.toml: Use version 0.0.0 placeholder --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5ee462c..38c08a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "qtoggleserver-thingspeak" -version = "unknown-version" +version = "0.0.0" description = "Send values from qToggleServer to ThingSpeak" authors = [ {name = "Calin Crisan", email = "ccrisan@gmail.com"}, @@ -14,7 +14,8 @@ dependencies = [ [dependency-groups] dev = [ - "ruff", + "pre-commit", + "ruff", ] [tool.ruff] From 9149aeb8ecb86c446c140132bc214c645cb0922c Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Fri, 13 Jun 2025 10:06:23 +0300 Subject: [PATCH 6/8] Reorganize datetime imports --- qtoggleserver/thingspeak/thingspeak.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qtoggleserver/thingspeak/thingspeak.py b/qtoggleserver/thingspeak/thingspeak.py index 3b64157..f94b6e8 100644 --- a/qtoggleserver/thingspeak/thingspeak.py +++ b/qtoggleserver/thingspeak/thingspeak.py @@ -1,8 +1,9 @@ import asyncio -import datetime import logging import time +from datetime import datetime, timezone + import aiohttp import pytz @@ -78,7 +79,7 @@ async def on_value_change( return self._last_send_time = now - created_at = datetime.datetime.fromtimestamp(event.get_timestamp(), tz=pytz.UTC) + created_at = datetime.fromtimestamp(event.get_timestamp(), tz=pytz.UTC) try: await self.send_values(self._values_cache, created_at) @@ -87,7 +88,7 @@ async def on_value_change( self._values_cache = {} - async def send_values(self, values: dict[int, float], created_at: datetime.datetime) -> None: + async def send_values(self, values: dict[int, float], created_at: datetime) -> None: if not values: raise ThingSpeakException("Refusing to send empty values") @@ -116,7 +117,7 @@ async def periodic_send_values(self) -> None: try: if field_values: - await self.send_values(field_values, datetime.datetime.utcnow()) + await self.send_values(field_values, datetime.now(timezone.utc)) else: self.debug("not sending empty values") except asyncio.CancelledError: From 67250c905711e1c34ef840061eec3dc601ccf0e8 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sat, 14 Jun 2025 23:53:04 +0300 Subject: [PATCH 7/8] Use version 0.0.0 by default --- qtoggleserver/thingspeak/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtoggleserver/thingspeak/__init__.py b/qtoggleserver/thingspeak/__init__.py index 22746f9..56ffb5e 100644 --- a/qtoggleserver/thingspeak/__init__.py +++ b/qtoggleserver/thingspeak/__init__.py @@ -4,4 +4,4 @@ __all__ = ["ThingSpeakEventHandler"] -VERSION = "unknown-version" +VERSION = "0.0.0" From 24d96509fe41db63fbb705d8e4baa97b76d2d0a5 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sun, 15 Jun 2025 22:25:36 +0300 Subject: [PATCH 8/8] Enable type annotations checks --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 38c08a9..d025908 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,8 @@ dev = [ [tool.ruff] line-length = 120 target-version = "py310" -lint.extend-select = ["I", "RUF022"] +lint.extend-select = ["I", "RUF022", "ANN"] +lint.extend-ignore = ["ANN002", "ANN003", "ANN401"] lint.isort.lines-after-imports = 2 lint.isort.lines-between-types = 1 lint.isort.force-wrap-aliases = true