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

feat: add 'aiomqtt' recipe. #280

Merged
merged 3 commits into from
Apr 4, 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
5 changes: 3 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ repos:
rev: v1.9.0
hooks:
- id: mypy
exclude: (settings.py|manage.py|models.py|(migrations/)|admin.py)
additional_dependencies: [pydantic, types-redis]
exclude: (manage.py|models.py|(migrations/)|admin.py)
additional_dependencies: [pydantic, types-redis, aiomqtt]
language_version: python3.11
- repo: https://github.com/PyCQA/pylint
rev: v3.1.0
Expand All @@ -58,6 +58,7 @@ repos:
motor,
redis,
types-redis,
aiomqtt,
]
exclude: django_project/
language_version: python3.11
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ celery = {version = "*", extras = ["librabbitmq", "mongodb", "redis"]}
#django = "~=4.2"
#psycopg = {version = ">=3.2", extras = ["binary", "pool"]}
motor = "*"
aiomqtt = "*"

[dev-packages]
black = "*"
Expand Down
334 changes: 175 additions & 159 deletions Pipfile.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@

### Redis

- [**`redis-py`**: Sync / Async](https://lucas-six.github.io/python-cookbook/cookbook/system_services/redis)
- [`aioredis`: Async (Obsoleted by `redis-py`)](https://aioredis.readthedocs.io/en/latest/)
- [**`redis-py`**: Sync + Async](https://lucas-six.github.io/python-cookbook/cookbook/system_services/redis)
- [~~`aioredis`: Async (Obsoleted by `redis-py`)~~](https://aioredis.readthedocs.io/en/latest/)
- `pyton-redis-orm`: ORM

### RabbitMQ
Expand All @@ -234,7 +234,7 @@

### MQTT

- [**`asyncio-mqtt`**: Async](https://pypi.org/project/asyncio-mqtt/) ([中文](https://blog.alexsun.top/vuepress-python-notes/pypi-package/async/asyncio-mqtt.html))
- [**`aiomqtt`**: Async](https://lucas-six.github.io/python-cookbook/cookbook/system_services/mqtt_aiomqtt)

## Recipes

Expand Down
2 changes: 1 addition & 1 deletion cookbook/build/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ disable = [
"locally-disabled",
"file-ignored",
"suppressed-message",
"useless-suppression",
"deprecated-pragma",
"use-symbolic-message-instead",
"logging-fstring-interpolation",
Expand All @@ -148,6 +147,7 @@ disable = [
]
enable = [
"c-extension-no-member",
"useless-suppression",
]

[tool.pylint.design]
Expand Down
58 changes: 58 additions & 0 deletions cookbook/system_services/mqtt_aiomqtt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# `aiomqtt` - Python Asyncio APIs for MQTT

## Set Up

```toml
# pyproject.toml

dependencies = [
"aiomqtt",
]
```

```bash
pipenv install aiomqtt
```

## Usage

```python
"""MQTT `aiomqtt` Usage.
"""

import asyncio
import os
import sys

import aiomqtt

MQTT_HOST = 'localhost'
MQTT_TOPIC_PREFIX = 'python-cookbook'


async def main():
async with aiomqtt.Client(MQTT_HOST, timeout=3.5) as client:

# Subscribe
await client.subscribe(f'{MQTT_TOPIC_PREFIX}/#')
async for message in client.messages:
if isinstance(message.payload, bytes):
print(message.payload.decode('utf-8'))

# Publish
# await client.publish(f'{MQTT_TOPIC_PREFIX}/example', payload={'msg': 'hello'})


# Change to the "Selector" event loop if platform is Windows
if sys.platform.lower() == "win32" or os.name.lower() == "nt":
from asyncio import WindowsSelectorEventLoopPolicy # type: ignore
from asyncio import set_event_loop_policy

set_event_loop_policy(WindowsSelectorEventLoopPolicy())

asyncio.run(main())
```

## References

- [`aiomqtt` Documentation](https://sbtinstruments.github.io/aiomqtt/index.html)
4 changes: 4 additions & 0 deletions cookbook/system_services/redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ dependencies = [
#"redis[hiredis]",
"types-redis",
]

[[tool.mypy.overrides]]
module = "redis.*"
ignore_missing_imports = true
```

```bash
Expand Down
10 changes: 0 additions & 10 deletions cookbook/web/fastapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,12 @@
## Installation

```bash
# HTTP Request
pipenv install aiohttp

# Cache: Redis
pipenv install redis[hiredis]
pipenv install types-redis

# MQ: RabbitMQ/MongoDB/Redis
pipenv install pika
pipenv install types-pika

# Task Queue: Celery
pipenv install celery[librabbitmq, mongodb, redis]

# MQTT
pipenv install asyncio-mqtt
```

## MongoDB
Expand Down
2 changes: 1 addition & 1 deletion cookbook/web/fastapi/fastapi_mongodb.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ async def redoc_html() -> HTMLResponse:

@app.get('/api')
async def root() -> dict[str, str]:
await app.state.mongodb_db['x'].find_one({''})
await DB_XXX['x'].find_one({'name': 'fastapi'})
return {'Hello': 'World'}


Expand Down
20 changes: 15 additions & 5 deletions cookbook/web/fastapi/fastapi_quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ pipenv install --dev pylint-pydantic
# JWT
pipenv install python-jose[cryptography]
pipenv install types-python-jose

# HTTP Request
pipenv install aiohttp

# MQTT
pipenv install aiomqtt
```

## `pyproject.toml`
Expand Down Expand Up @@ -53,6 +59,9 @@ dependencies = [

"python-jose[cryptography]",
"types-python-jose",

#"aiohttp",
#"aio-mqtt",
]
dynamic = ["version"]

Expand Down Expand Up @@ -121,7 +130,7 @@ follow_imports = "silent"
warn_redundant_casts = true
warn_unused_ignores = true
warn_unused_configs = true
disallow_any_generics = true
disallow_any_generics = false
check_untyped_defs = true
no_implicit_reexport = true
disallow_untyped_defs = true
Expand Down Expand Up @@ -158,14 +167,15 @@ disable = [
"bad-inline-option",
"locally-disabled",
"file-ignored",
"suppressed-message",
"deprecated-pragma",
"use-symbolic-message-instead",
"logging-fstring-interpolation",
"missing-function-docstring",
"missing-class-docstring",
]
enable = [
"c-extension-no-member",
"suppressed-message",
"useless-suppression",
]

Expand Down Expand Up @@ -357,7 +367,6 @@ async def redoc_html() -> HTMLResponse:

@app.get('/api')
async def root() -> dict[str, str]:
await app.state.mongodb_db['x'].find_one({''})
return {'Hello': 'World'}


Expand All @@ -377,8 +386,9 @@ See [Uvicorn: ASGI, WebSockets - Python Cookbook](../uvicorn).

- [Python Project - Python Cookbook](../../build/project)
- [ASGI Web Server: **`Uvicorn`** - Python Cookbook](../uvicorn)
- [Data Model: **`Pydantic`**](../pydantic)
- [with MongoDB: **`motor`**](fastapi_mongodb)
- [Data Model: **`Pydantic`** - Python Cookbook](../pydantic)
- [with MongoDB: **`motor`** - Python Cookbook](fastapi_mongodb)
- [with Redis: **`redis`** - Python Cookbook](../../system_services/redis)

## References

Expand Down
4 changes: 2 additions & 2 deletions django_project/django_project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []
ALLOWED_HOSTS: list[str] = []


# Application definition
Expand All @@ -53,7 +53,7 @@

ROOT_URLCONF = 'django_project.urls'

TEMPLATES = [ # type: ignore
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
Expand Down
34 changes: 34 additions & 0 deletions examples/system_services/mqtt_aiomqtt_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""MQTT `aiomqtt` Usage.
"""

import asyncio
import os
import sys

import aiomqtt

MQTT_HOST = 'localhost'
MQTT_TOPIC_PREFIX = 'python-cookbook'


async def main() -> None:
async with aiomqtt.Client(MQTT_HOST, timeout=3.5) as client:

# Subscribe
await client.subscribe(f'{MQTT_TOPIC_PREFIX}/#')
async for message in client.messages:
if isinstance(message.payload, bytes):
print(message.payload.decode('utf-8'))

# Publish
# await client.publish(f'{MQTT_TOPIC_PREFIX}/example', payload={'msg': 'hello'})


# Change to the "Selector" event loop if platform is Windows
if sys.platform.lower() == 'win32' or os.name.lower() == 'nt':
from asyncio import WindowsSelectorEventLoopPolicy # type: ignore
from asyncio import set_event_loop_policy

set_event_loop_policy(WindowsSelectorEventLoopPolicy())

asyncio.run(main())
2 changes: 1 addition & 1 deletion examples/web/fastapi/main_mongodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async def redoc_html() -> HTMLResponse:

@app.get('/api')
async def root() -> dict[str, str]:
await app.state.mongodb_db['x'].find_one({''})
await DB_XXX['x'].find_one({'name': 'fastapi'})
return {'Hello': 'World'}


Expand Down
1 change: 0 additions & 1 deletion examples/web/fastapi/main_openapi_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ async def redoc_html() -> HTMLResponse:

@app.get('/api')
async def root() -> dict[str, str]:
await app.state.mongodb_db['x'].find_one({''})
return {'Hello': 'World'}


Expand Down
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ dependencies = [

"redis[hiredis]",
"types-redis",

"aiomqtt",

"celery[librabbitmq, mongodb, redis]",
#"requests",
#"types-requests",
Expand Down Expand Up @@ -129,11 +132,14 @@ init_typed = true
warn_required_dynamic_aliases = true
warn_untyped_fields = true

# mypy for MongoDB motor
[[tool.mypy.overrides]]
module = "motor.*"
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "redis.*"
ignore_missing_imports = true

[tool.pylint.main]
recursive = true
py-version = 3.11
Expand Down
Loading
Loading