Switch to asyncclick for the CLI
In Python 3.9.6 after the BLE connection the RuntimeError "got Future <Future pending> attached to a different loop" was raised: this usually is due to multiple loop and the decorator to make click command async was calling asyncio.run.
This commit is contained in:
parent
b5292d922b
commit
6a26b4a503
77
poetry.lock
generated
77
poetry.lock
generated
|
@ -1,5 +1,26 @@
|
|||
# This file is automatically @generated by Poetry and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.6.2"
|
||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
files = [
|
||||
{file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"},
|
||||
{file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
idna = ">=2.8"
|
||||
sniffio = ">=1.1"
|
||||
|
||||
[package.extras]
|
||||
doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
|
||||
test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
|
||||
trio = ["trio (>=0.16,<0.22)"]
|
||||
|
||||
[[package]]
|
||||
name = "async-timeout"
|
||||
version = "4.0.2"
|
||||
|
@ -12,6 +33,21 @@ files = [
|
|||
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asyncclick"
|
||||
version = "8.1.3.4"
|
||||
description = "Composable command line interface toolkit, async version"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "asyncclick-8.1.3.4-py3-none-any.whl", hash = "sha256:f8db604e37dabd43922d58f857817b1dfd8f88695b75c4cc1afe7ff1cc238a7b"},
|
||||
{file = "asyncclick-8.1.3.4.tar.gz", hash = "sha256:81d98cbf6c8813f9cd5599f586d56cfc532e9e6441391974d10827abb90fe833"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "ble-serial"
|
||||
version = "2.7.0"
|
||||
|
@ -70,21 +106,6 @@ files = [
|
|||
{file = "bleak_winrt-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:63130c11acfe75c504a79c01f9919e87f009f5e742bfc7b7a5c2a9c72bf591a7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
|
@ -171,6 +192,18 @@ files = [
|
|||
[package.dependencies]
|
||||
pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""}
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.4"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
|
||||
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.36"
|
||||
|
@ -291,6 +324,18 @@ files = [
|
|||
[package.extras]
|
||||
cp2110 = ["hidapi"]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.0"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
|
||||
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.6"
|
||||
|
@ -306,4 +351,4 @@ files = [
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "cd6d12cd0c07704626be7124ec000b24b98b7cdda9b3d6bcb6be6e67edcd7482"
|
||||
content-hash = "3b363cd19c44345908dbbd37685e525186b42bb24d260875b8e06fb3e5f49b2c"
|
||||
|
|
|
@ -17,11 +17,12 @@ packages = [{include = "freakble", from = "src"}]
|
|||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
ble-serial = "^2.7.0"
|
||||
click = "^8.1.3"
|
||||
prompt-toolkit = "^3.0.36"
|
||||
asyncclick = "^8.1.3.4"
|
||||
anyio = "^3.6.2"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
freakble = "freakble.main:run"
|
||||
freakble = "freakble.__main__:run"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
|
22
src/freakble/__main__.py
Normal file
22
src/freakble/__main__.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Copyright © 2023 Daniele Tricoli <eriol@mornie.org>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
"""A simple tool to send messages into FreakWAN over Bluetooth low energy."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from .cli import get_cli
|
||||
|
||||
|
||||
def run():
|
||||
"""Main entrypoint.""" # noqa: D401
|
||||
# ble-serial fire a warning on disconnect, but our main use case is to just
|
||||
# send a message and disconnect, so we disable logging here.
|
||||
# TODO: Make configurable by the user.
|
||||
logging.disable()
|
||||
asyncio.run(get_cli())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright © 2023 Daniele Tricoli <eriol@mornie.org>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
"""A simple tool to send messages into FreakWAN over Bluetooth low energy."""
|
||||
|
||||
"""CLI related stuff for freakble."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
@ -8,21 +9,13 @@ import sys
|
|||
import warnings
|
||||
from functools import wraps
|
||||
|
||||
import click
|
||||
import asyncclick as click
|
||||
|
||||
from . import __version__
|
||||
from .ble import BLE_interface, scanner, send_conditionally, send_forever, connect
|
||||
from .repl import REPL
|
||||
|
||||
|
||||
def coro(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
return asyncio.run(f(*args, **kwargs))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def ble_receive_callback(data: bytes):
|
||||
"""Print data received from BLE."""
|
||||
click.echo(data)
|
||||
|
@ -65,7 +58,6 @@ def cli(ctx, adapter):
|
|||
)
|
||||
@click.argument("words", type=str, nargs=-1)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def send(ctx, words, device, loop, sleep_time, ble_connection_timeout):
|
||||
"""Send one or more words over BLE to a specific device."""
|
||||
msg = " ".join(words)
|
||||
|
@ -98,7 +90,6 @@ async def send(ctx, words, device, loop, sleep_time, ble_connection_timeout):
|
|||
help="service UUID used to filter",
|
||||
)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def scan(ctx, scan_time, service_uuid):
|
||||
"""Scan to find BLE devices."""
|
||||
devices = await scanner.scan(ctx.obj["ADAPTER"], scan_time, service_uuid)
|
||||
|
@ -117,7 +108,6 @@ async def scan(ctx, scan_time, service_uuid):
|
|||
"--scan-time", default=5, show_default="5 secs", type=float, help="scan duration"
|
||||
)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def deep_scan(ctx, device, scan_time):
|
||||
"""Scan to find services of a specific device."""
|
||||
devices = await scanner.scan(ctx.obj["ADAPTER"], scan_time, None)
|
||||
|
@ -143,7 +133,6 @@ async def deep_scan(ctx, device, scan_time):
|
|||
help="BLE connection timeout",
|
||||
)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def repl(ctx, device, ble_connection_timeout):
|
||||
"""Start a REPL with the device."""
|
||||
ble = ctx.obj["BLE"]
|
||||
|
@ -162,17 +151,11 @@ async def repl(ctx, device, ble_connection_timeout):
|
|||
|
||||
|
||||
@cli.command()
|
||||
@coro
|
||||
async def version():
|
||||
"""Return freakble version."""
|
||||
click.echo(f"freakble {__version__}")
|
||||
|
||||
|
||||
def run():
|
||||
"""CLI entrypoint."""
|
||||
# ble-serial fire a warning on disconnect, but our main use case is to just
|
||||
# send a message and disconnect, so we disable logging here.
|
||||
# TODO: Make configurable by the user.
|
||||
logging.disable()
|
||||
|
||||
asyncio.run(cli(auto_envvar_prefix="FREAKBLE"))
|
||||
def get_cli():
|
||||
"""Return the CLI entrypoint."""
|
||||
return cli(auto_envvar_prefix="FREAKBLE", _anyio_backend="asyncio")
|
Loading…
Reference in a new issue