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

Update tooling #184

Merged
merged 1 commit into from
Jan 21, 2023
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
13 changes: 12 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,21 @@ jobs:
path: ~/.cache/pip
key: pip-test-${{ matrix.python-version }}-${{ matrix.os }}
- name: Install dependencies
run: pip install -e .[tests]
run: pip install -e .[lint,tests]
- name: Run Ruff
run: ruff --format=github .
- name: Run mypy
run: mypy .
- name: Test with pytest
run: pytest --cov=asyncio_mqtt --cov=tests --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
files: ./reports/coverage.xml
- name: Publish JUnit reports
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
check_name: Junit reports
report_paths: "./reports/*.xml"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ coverage.xml
asyncio_mqtt/_version.py
*.DS_Store
docs/_build
reports
31 changes: 15 additions & 16 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
fail_fast: true
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.215
rev: v0.0.229
hooks:
- id: ruff
args:
- --fix
- --target-version
- py37
- --force-exclude

- repo: https://github.com/psf/black
rev: 22.12.0
hooks:
- id: black

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
hooks:
- id: mypy
additional_dependencies:
- anyio
- pytest
- types-paho-mqtt == 1.6.0.1

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0-alpha.4
hooks:
Expand All @@ -46,8 +37,16 @@ repos:
args: ["--fix=lf"]
- id: trailing-whitespace

- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
hooks:
- id: python-check-blanket-type-ignore
- id: python-check-blanket-noqa
- id: mypy
additional_dependencies:
- anyio == 3.6.2
- pytest == 7.2.0
- types-paho-mqtt == 1.6.0.1

ci:
skip:
- ruff
- mypy
8 changes: 8 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"ms-python.python",
"littlefoxteam.vscode-python-test-adapter",
"charliermarsh.ruff",
"tamasfe.even-better-toml"
]
}
13 changes: 13 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
"python.formatting.provider": "black",
"python.linting.mypyEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"ruff.importStrategy": "fromEnvironment"
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Update Ruff and fix new lint errors. Contributed by [pre-commit-ci](https://github.com/apps/pre-commit-ci) and [Jonathan Plasse (@JonathanPlasse)](https://github.com/JonathanPlasse) in [#161](https://github.com/sbtinstruments/asyncio-mqtt/pull/161)
- Update tooling. Contributed by [pre-commit-ci](https://github.com/apps/pre-commit-ci) and [Jonathan Plasse (@JonathanPlasse)](https://github.com/JonathanPlasse) in [#184](https://github.com/sbtinstruments/asyncio-mqtt/pull/184)

### Fixed

Expand Down
19 changes: 1 addition & 18 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,7 @@ pre-commit install

### Visual Studio Code

If you are using VSCode, these are the settings to activate on save:

- `black` to format.
- `mypy` to lint.
- Install the [charliermarsh.ruff](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) extension to lint, sort imports, and auto-fix lint errors (`ruff` is a fast equivalent to `flake8`)

```json
{
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
"python.formatting.provider": "black",
"python.linting.mypyEnabled": true
}
```
If you are using VSCode, these are workspace settings and highly recommended extensions to improve developer experience.

## Testing

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
<a href="https://pypi.org/project/asyncio-mqtt"><img alt="PyPI version" src="https://img.shields.io/pypi/v/asyncio-mqtt"></a>
<a href="https://pypi.org/project/asyncio-mqtt"><img alt="Supported Python versions" src="https://img.shields.io/pypi/pyversions/asyncio-mqtt.svg"></a>
<a href="https://pypi.org/project/asyncio-mqtt"><img alt="PyPI downloads" src="https://img.shields.io/pypi/dm/asyncio-mqtt"></a>
<a href="https://github.com/sbtinstruments/asyncio-mqtt/actions/workflows/test.yml"><img alt="Coverage" src="https://github.com/sbtinstruments/asyncio-mqtt/actions/workflows/test.yml/badge.svg"></a>
<a href="https://github.com/sbtinstruments/asyncio-mqtt/actions/workflows/test.yml"><img alt="test suite" src="https://github.com/sbtinstruments/asyncio-mqtt/actions/workflows/test.yml/badge.svg"></a>
<a href="https://github.com/sbtinstruments/asyncio-mqtt/actions/workflows/docs.yml"><img alt="docs" src="https://github.com/sbtinstruments/asyncio-mqtt/actions/workflows/docs.yml/badge.svg"></a>
<a href="https://codecov.io/gh/sbtinstruments/asyncio-mqtt"><img alt="Coverage" src="https://img.shields.io/codecov/c/github/sbtinstruments/asyncio-mqtt"></a>
<a href="https://results.pre-commit.ci/latest/github/sbtinstruments/asyncio-mqtt/main"><img alt="pre-commit.ci status" src="https://results.pre-commit.ci/badge/github/sbtinstruments/asyncio-mqtt/main.svg"></a>
<a href="https://github.com/sbtinstruments/asyncio-mqtt"><img alt="Typing: strict" src="https://img.shields.io/badge/typing-strict-green.svg"></a>
Expand Down
48 changes: 28 additions & 20 deletions asyncio_mqtt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@


class ProtocolVersion(IntEnum):
"""A mapping of paho-mqtt protocol versions to an Enum for use in type hints."""
"""Map paho-mqtt protocol versions to an Enum for use in type hints."""

V31 = mqtt.MQTTv31
V311 = mqtt.MQTTv311
Expand Down Expand Up @@ -119,9 +119,12 @@ async def decorated(self: Client, *args: P.args, **kwargs: P.kwargs) -> T:
return decorated


MAX_TOPIC_LENGTH = 65535


@dataclass(frozen=True)
class Wildcard:
"""A topic, optionally with wildcards (+ and #). Can only be subscribed to."""
"""Validate a topic, optionally with wildcards (+ and #). Can only be subscribed to."""

value: str

Expand All @@ -131,38 +134,42 @@ def __str__(self) -> str:
def __post_init__(self) -> None:
"""Validate the wildcard."""
if not isinstance(self.value, str):
raise TypeError("wildcard must be a string")
msg = "wildcard must be a string"
raise TypeError(msg)
if (
len(self.value) == 0
or len(self.value) > 65535
or len(self.value) > MAX_TOPIC_LENGTH
or "#/" in self.value
or any(
"+" in level or "#" in level
for level in self.value.split("/")
if len(level) > 1
)
):
raise ValueError(f"Invalid wildcard: {self.value}")
msg = f"Invalid wildcard: {self.value}"
raise ValueError(msg)


WildcardLike: TypeAlias = "str | Wildcard"


@dataclass(frozen=True)
class Topic(Wildcard):
"""A topic that can be published and subscribed to."""
"""Validate a topic that can be published and subscribed to."""

def __post_init__(self) -> None:
"""Validate the topic."""
if not isinstance(self.value, str):
raise TypeError("topic must be a string")
msg = "topic must be a string"
raise TypeError(msg)
if (
len(self.value) == 0
or len(self.value) > 65535
or len(self.value) > MAX_TOPIC_LENGTH
or "+" in self.value
or "#" in self.value
):
raise ValueError(f"Invalid topic: {self.value}")
msg = f"Invalid topic: {self.value}"
raise ValueError(msg)

def matches(self, wildcard: WildcardLike) -> bool:
"""Check if the topic is matched by a given wildcard."""
Expand Down Expand Up @@ -195,7 +202,7 @@ def recurse(x: list[str], y: list[str]) -> bool:


class Message:
"""Custom message class that allows us to use our own Topic class."""
"""Wrap paho-mqtt message class that allows us to use our own Topic class."""

def __init__(
self,
Expand Down Expand Up @@ -511,9 +518,8 @@ async def unfiltered_messages(
)
# Early out
if self._unfiltered_messages_callback is not None:
raise RuntimeError(
"Only a single unfiltered_messages generator can be used at a time."
)
msg = "Only a single unfiltered_messages generator can be used at a time."
raise RuntimeError(msg)
callback, generator = self._deprecated_callback_and_generator(
log_context="unfiltered", queue_maxsize=queue_maxsize
)
Expand Down Expand Up @@ -590,7 +596,8 @@ async def _message_generator() -> AsyncGenerator[mqtt.MQTTMessage, None]:
# We got disconnected from the broker. Cancel the "get" task.
get.cancel()
# Stop the generator with the following exception
raise MqttError("Disconnected during message iteration")
msg = "Disconnected during message iteration"
raise MqttError(msg)

return _put_in_queue, _message_generator()

Expand Down Expand Up @@ -630,7 +637,8 @@ async def _generator() -> AsyncGenerator[Message, None]:
# We got disconnected from the broker. Cancel the "get" task.
get.cancel()
# Stop the generator with the following exception
raise MqttError("Disconnected during message iteration")
msg = "Disconnected during message iteration"
raise MqttError(msg)

return _callback, _generator()

Expand All @@ -640,16 +648,16 @@ async def _wait_for(
try:
return await asyncio.wait_for(fut, timeout=timeout, **kwargs)
except asyncio.TimeoutError:
raise MqttError("Operation timed out") from None
msg = "Operation timed out"
raise MqttError(msg) from None

@contextmanager
def _pending_call(
self, mid: int, value: T, pending_dict: dict[int, T]
) -> Iterator[None]:
if mid in self._pending_calls:
raise RuntimeError(
f'There already exists a pending call for message ID "{mid}"'
)
msg = f'There already exists a pending call for message ID "{mid}"'
raise RuntimeError(msg)
pending_dict[mid] = value # [1]
try:
# Log a warning if there is a concerning number of pending calls
Expand Down Expand Up @@ -709,7 +717,7 @@ def _on_disconnect(
# fails). In turn, this avoids asyncio debug messages like the
# following:
#
# "[asyncio] Future exception was never retrieved"
# `[asyncio] Future exception was never retrieved`
#
# See also: https://docs.python.org/3/library/asyncio-dev.html#detect-never-retrieved-exceptions
if not self._connected.done() or self._connected.exception() is not None:
Expand Down
2 changes: 0 additions & 2 deletions asyncio_mqtt/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@


class MqttError(Exception):
"""Base exception for all asyncio-mqtt exceptions."""

pass


Expand Down
Loading