Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3.8, 3.10, and 3.12 support #404

Merged
merged 4 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ on:
- release
jobs:
test:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: [3.8]
python-version: ['3.8', '3.10', '3.12']

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down Expand Up @@ -97,7 +97,7 @@ jobs:
if: steps.branch-names.outputs.is_tag == 'false'
run: |
git checkout gh-pages
git checkout ${{ steps.branch-names.outputs.current_branch }}
git checkout ${{ steps.branch-names.outputs.current_branch }}
- name: Setup Python 3.8
uses: actions/setup-python@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion docs_src/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple

import semver
import yaml
import yaml # type: ignore
from ast_to_xml import module_source
from git import Repo
from jinja2 import Template
Expand Down
2 changes: 1 addition & 1 deletion mqtt_io/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from hashlib import sha256
from typing import Any, Optional

import yaml
import yaml # type: ignore

from confp import render # type: ignore

Expand Down
2 changes: 1 addition & 1 deletion mqtt_io/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import TYPE_CHECKING, Any, Dict, List, cast

import cerberus # type: ignore
import yaml
import yaml # type: ignore

from ..exceptions import ConfigValidationFailed
from ..types import ConfigType
Expand Down
7 changes: 4 additions & 3 deletions mqtt_io/modules/gpio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import logging
from concurrent.futures import ThreadPoolExecutor
from enum import Enum, Flag, IntFlag, auto
from typing import Any, Callable, Dict, Iterable, List, Optional
from typing import Any, Callable, Dict, Iterable, List, Optional, cast

from ...types import ConfigType, PinType

Expand Down Expand Up @@ -173,7 +173,7 @@ def setup_interrupt_internal(
pin: PinType,
edge: InterruptEdge,
in_conf: ConfigType,
callback: Optional[Callable[[List[Any], Dict[Any, Any]], None]] = None,
callback: Optional[Callable[..., None]] = None,
) -> None:
"""
Used internally to ensure that `self.interrupt_edges` is updated for this pin.
Expand Down Expand Up @@ -213,7 +213,7 @@ def remote_interrupt_for(self, pin: PinType) -> List[str]:
"""
Return the list of pin names that this pin is a remote interrupt for.
"""
return self.pin_configs[pin].get("interrupt_for", [])
return cast(List[str], self.pin_configs[pin].get("interrupt_for", []))

def get_int_pins(self) -> List[PinType]:
"""
Expand Down Expand Up @@ -274,6 +274,7 @@ def get_interrupt_value(self, pin: PinType, *args: Any, **kwargs: Any) -> bool:
for the value, or looking it up in a register that captures the value at time of
interrupt.
"""
return False

async def get_interrupt_values_remote(
self, pins: List[PinType]
Expand Down
4 changes: 2 additions & 2 deletions mqtt_io/modules/gpio/gpiozero.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
import logging
from functools import partial
from typing import Optional, Any, Callable, List, Dict, TYPE_CHECKING, cast
from typing import Optional, Any, Callable, Dict, TYPE_CHECKING, cast

from mqtt_io.modules.gpio import (
GenericGPIO,
Expand Down Expand Up @@ -89,7 +89,7 @@ def setup_interrupt_callback(
pin: PinType,
edge: InterruptEdge,
in_conf: ConfigType,
callback: Callable[[List[Any], Dict[Any, Any]], None],
callback: Callable[..., None],
) -> None:
_LOG.debug(
"Added interrupt to gpiozero Pi pin '%s' with callback '%s'", pin, callback
Expand Down
15 changes: 8 additions & 7 deletions mqtt_io/modules/gpio/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Mock GPIO module for using with the tests.
"""

from typing import Any, Callable, Dict, Iterable, List, Optional
from typing import Callable, Dict, Iterable, List, Optional
from unittest.mock import Mock

from ...types import ConfigType, PinType
Expand Down Expand Up @@ -39,20 +39,20 @@ def __init__(self, config: ConfigType):

super().__init__(config)
self.interrupt_callbacks: Dict[
PinType, Callable[[List[Any], Dict[Any, Any]], None]
PinType, Callable[..., None]
] = {}

def setup_interrupt_callback(
self,
pin: PinType,
edge: InterruptEdge,
in_conf: ConfigType,
callback: Callable[[List[Any], Dict[Any, Any]], None],
callback: Callable[..., None],
) -> None:
self.interrupt_callbacks[pin] = callback

def setup_module(self) -> None:
return super().setup_module()
return super().setup_module() # type: ignore[safe-super]

def setup_pin(
self,
Expand All @@ -62,18 +62,19 @@ def setup_pin(
pin_config: ConfigType,
initial: Optional[str] = None,
) -> None:
return super().setup_pin(pin, direction, pullup, pin_config, initial=initial)
return super().setup_pin( # type: ignore[safe-super]
pin, direction, pullup, pin_config, initial=initial)

def setup_interrupt(
self, pin: PinType, edge: InterruptEdge, in_conf: ConfigType
) -> None:
return super().setup_interrupt(pin, edge, in_conf)

def set_pin(self, pin: PinType, value: bool) -> None:
return super().set_pin(pin, value)
return super().set_pin(pin, value) # type: ignore[safe-super]

def get_pin(self, pin: PinType) -> bool:
return super().get_pin(pin)
return super().get_pin(pin) # type: ignore[safe-super]

def get_int_pins(self) -> List[PinType]:
return super().get_int_pins()
Expand Down
4 changes: 2 additions & 2 deletions mqtt_io/modules/gpio/raspberrypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

import logging
from typing import Any, Callable, Dict, List, Optional
from typing import Any, Callable, Optional

from ...types import ConfigType, PinType
from . import GenericGPIO, InterruptEdge, InterruptSupport, PinDirection, PinPUD
Expand Down Expand Up @@ -60,7 +60,7 @@ def setup_interrupt_callback(
pin: PinType,
edge: InterruptEdge,
in_conf: ConfigType,
callback: Callable[[List[Any], Dict[Any, Any]], None],
callback: Callable[..., None],
) -> None:
gpio_edge = self.interrupt_edge_map[edge]
_LOG.debug(
Expand Down
2 changes: 1 addition & 1 deletion mqtt_io/modules/sensor/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ def setup_sensor(self, sens_conf: ConfigType) -> None:
return super().setup_sensor(sens_conf)

def get_value(self, sens_conf: ConfigType) -> SensorValueType:
return super().get_value(sens_conf)
return super().get_value(sens_conf) # type: ignore[safe-super]
2 changes: 1 addition & 1 deletion mqtt_io/mqtt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class MQTTTLSOptions:
ca_certs: Optional[str] = None
certfile: Optional[str] = None
keyfile: Optional[str] = None
cert_reqs: int = ssl.CERT_REQUIRED
cert_reqs: ssl.VerifyMode = ssl.CERT_REQUIRED
tls_version: int = ssl.PROTOCOL_TLS
ciphers: Optional[str] = None

Expand Down
4 changes: 2 additions & 2 deletions mqtt_io/mqtt/aiomqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from functools import wraps
from typing import Any, Callable, List, Optional, Tuple, TypeVar, cast

from aiomqtt.client import Client, MqttError, Will, ProtocolVersion # type: ignore
from paho.mqtt import client as paho # type: ignore
from aiomqtt import Client, MqttError, Will, ProtocolVersion
from paho.mqtt import client as paho

from . import (
AbstractMQTTClient,
Expand Down
10 changes: 5 additions & 5 deletions mqtt_io/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
from hashlib import sha1
from importlib import import_module
from typing import Any, Dict, List, Optional, Tuple, Type, Union, overload
from aiomqtt.exceptions import MqttCodeError # type: ignore
from aiomqtt import MqttCodeError

import backoff # type: ignore
import backoff
from typing_extensions import Literal

from .config import (
Expand Down Expand Up @@ -648,10 +648,10 @@ async def poll_sensor(
None
"""
@backoff.on_exception( # type: ignore
@backoff.on_exception(
backoff.expo, Exception, max_time=sens_conf["interval"]
)
@backoff.on_predicate( # type: ignore
@backoff.on_predicate(
backoff.expo, lambda x: x is None, max_time=sens_conf["interval"]
)
async def get_sensor_value(
Expand Down Expand Up @@ -808,7 +808,7 @@ async def _mqtt_publish(self, msg: MQTTMessageSend, wait: bool = True) -> None:
)
else:
_LOG.debug(
"Publishing MQTT message on topic %r: %r", msg.topic, payload
"Publishing MQTT message on topic %r: %r", msg.topic, msg.payload
)

await self.mqtt.publish(msg)
Expand Down
2 changes: 1 addition & 1 deletion mqtt_io/tests/features/steps/config_main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Any
import yaml
import yaml # type: ignore
from behave import given, then, when # type: ignore
from mqtt_io.config import validate_and_normalise_main_config
from mqtt_io.exceptions import ConfigValidationFailed
Expand Down
6 changes: 3 additions & 3 deletions mqtt_io/tests/features/steps/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
import concurrent.futures
from typing import Any, Dict, Type

import yaml
import yaml # type: ignore
from behave import given, then, when # type: ignore
from behave.api.async_step import async_run_until_complete # type: ignore
from mqtt_io import events
from mqtt_io.server import MqttIo

try:
from unittest.mock import AsyncMock # type: ignore[attr-defined]
from unittest.mock import AsyncMock # type: ignore
except ImportError:
from mock import AsyncMock # type: ignore[attr-defined]
from mock import AsyncMock # type: ignore

# pylint: disable=function-redefined,protected-access

Expand Down
4 changes: 2 additions & 2 deletions mqtt_io/tests/features/steps/module_gpio.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def step(context: Any, is_isnt: str, pin_name: str):

poller_task_pin_names = {
get_coro(task).cr_frame.f_locals["in_conf"]["name"]
for task in asyncio.Task.all_tasks(loop=mqttio.loop)
for task in asyncio.all_tasks(loop=mqttio.loop)
if get_coro(task).__name__ == "digital_input_poller"
}
if is_isnt == "is":
Expand Down Expand Up @@ -137,7 +137,7 @@ def step(context: Any, is_isnt: str, module_name: str):

task_modules = {
get_coro(task).cr_frame.f_locals["module"]
for task in asyncio.Task.all_tasks(loop=mqttio.loop)
for task in asyncio.all_tasks(loop=mqttio.loop)
if get_coro(task).__name__ == "digital_output_loop"
}
if is_isnt == "is":
Expand Down
6 changes: 3 additions & 3 deletions mqtt_io/tests/features/steps/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
from typing import Any, Union
from unittest.mock import Mock

import yaml
import yaml # type: ignore
from behave import given, then, when # type: ignore
from behave.api.async_step import async_run_until_complete # type: ignore
from mqtt_io.exceptions import ConfigValidationFailed
from mqtt_io.mqtt import MQTTMessage, MQTTMessageSend
from mqtt_io.server import MqttIo

try:
from unittest.mock import AsyncMock # type: ignore[attr-defined]
from unittest.mock import AsyncMock # type: ignore
except ImportError:
from mock import AsyncMock # type: ignore[attr-defined]
from mock import AsyncMock # type: ignore

# pylint: disable=function-redefined

Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Cerberus = "^1.3.2"
typing-extensions = "^4.4.0"
dataclasses = { version = "^0.8", python = ">=3.6,<3.7" }
aiomqtt = "^2.1.0"
backoff = "^1.10.0"
backoff = "^2.2.1"
confp = "^0.4.0"
# Fix for poetry/docutils related bug
docutils = "0.18.1"
Expand All @@ -23,8 +23,8 @@ docutils = "0.18.1"
mock = { version = "^4.0.3", python = ">=3.6,<3.8" }
pytest = "^6.2.2"
tox = "^3.22.0"
pylint = "^2.6.2"
mypy = "^0.812"
pylint = "^3.2.7"
mypy = "^1.11.2"
behave = "^1.2.6"
coverage = "^5.4"
md-toc = "^8.0.0"
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
isolated_build = true
envlist = py3{6,7,8}
envlist = py3{8,10,12}

[testenv]
whitelist_externals = poetry
Expand Down