diff --git a/.envrc b/.envrc index 94840b3..70861a2 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,3 @@ -layout python3 +source_url "https://raw.githubusercontent.com/cachix/devenv/5811f4817ba24da923506d134fff2610b8f95ff2/direnvrc" "sha256-IN2rc7pbaBxxjcdIpYOe9lkpiyjSr2V2AwF6KwlnWYQ=" + +use devenv \ No newline at end of file diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index bd3bc18..5bfc49d 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -18,11 +18,10 @@ jobs: matrix: python-version: - 2.7 - - 3.6 - 3.7 - 3.8 - 3.9 - - 3.10 + - "3.10" - 3.11 steps: diff --git a/.gitignore b/.gitignore index e17f642..676a804 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,8 @@ ENV/ .direnv .mypy_cache/ + +# Devenv +.devenv* +devenv.local.nix + diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..6602a0e --- /dev/null +++ b/devenv.lock @@ -0,0 +1,138 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1678526821, + "narHash": "sha256-pXLZd6IfLx1BBRjBEapWfY7Mm3NNpdrSGT4TsHHmuP0=", + "owner": "cachix", + "repo": "devenv", + "rev": "f665f6b0db78abac8d06dc53873ba12acaba04b3", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678500213, + "narHash": "sha256-A5s2rXawJ+dCThkMXoMuYW8dgyUmkElcyfVJUot/Vr0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2ce9b9842b5e63884dfc3dea6689769e2a1ea309", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1673800717, + "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1678376203, + "narHash": "sha256-3tyYGyC8h7fBwncLZy5nCUjTJPrHbmNwp47LlNLOHSM=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "1a20b9708962096ec2481eeb2ddca29ed747770a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..5b35569 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,4 @@ +{ pkgs, ... }: +{ + packages = [ pkgs.python39Packages.tox ]; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..c7cb5ce --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,3 @@ +inputs: + nixpkgs: + url: github:NixOS/nixpkgs/nixpkgs-unstable diff --git a/rflink/protocol.py b/rflink/protocol.py index 7535051..efdc677 100644 --- a/rflink/protocol.py +++ b/rflink/protocol.py @@ -240,13 +240,12 @@ def handle_response_packet(self, packet: PacketType) -> None: self._last_ack = packet self._command_ack.set() - @asyncio.coroutine - def send_command_ack( + async def send_command_ack( self, device_id: str, action: str ) -> Generator[Any, None, Optional[bool]]: """Send command, wait for gateway to repond with acknowledgment.""" # serialize commands - yield from self._ready_to_send.acquire() + await self._ready_to_send.acquire() acknowledgement = None try: self._command_ack.clear() @@ -254,7 +253,7 @@ def send_command_ack( log.debug("waiting for acknowledgement") try: - yield from asyncio.wait_for(self._command_ack.wait(), TIMEOUT.seconds) + await asyncio.wait_for(self._command_ack.wait(), TIMEOUT.seconds) log.debug("packet acknowledged") except concurrent.futures._base.TimeoutError: acknowledgement = False diff --git a/rflinkproxy/__main__.py b/rflinkproxy/__main__.py index 94d572e..87c781f 100644 --- a/rflinkproxy/__main__.py +++ b/rflinkproxy/__main__.py @@ -124,8 +124,7 @@ def __init__(self, port=None, host=None, baud=57600, loop=None): self.transport = None self.closing = False - @asyncio.coroutine - def handle_raw_tx_packet(self, writer, raw_packet): + async def handle_raw_tx_packet(self, writer, raw_packet): """Parse raw packet string into packet dict.""" peer = writer.get_extra_info("peername") log.debug(" %s:%s: processing data: %s", peer[0], peer[1], raw_packet) @@ -153,35 +152,33 @@ def handle_raw_tx_packet(self, writer, raw_packet): peer[1], raw_packet, ) - yield from self.forward_packet(writer, packet, raw_packet) + await self.forward_packet(writer, packet, raw_packet) else: log.warning(" %s:%s: no valid packet %s", peer[0], peer[1], packet) - @asyncio.coroutine - def forward_packet(self, writer, packet, raw_packet): + async def forward_packet(self, writer, packet, raw_packet): """Forward packet from client to RFLink.""" peer = writer.get_extra_info("peername") log.debug(" %s:%s: forwarding data: %s", peer[0], peer[1], packet) if "command" in packet: packet_id = serialize_packet_id(packet) command = packet["command"] - ack = yield from self.protocol.send_command_ack(packet_id, command) + ack = await self.protocol.send_command_ack(packet_id, command) if ack: writer.write("20;00;OK;".encode() + CRLF) for _ in range(DEFAULT_SIGNAL_REPETITIONS - 1): - yield from self.protocol.send_command_ack(packet_id, command) + await self.protocol.send_command_ack(packet_id, command) else: self.protocol.send_raw_packet(raw_packet) - @asyncio.coroutine - def client_connected_callback(self, reader, writer): + async def client_connected_callback(self, reader, writer): """Handle connected client.""" peer = writer.get_extra_info("peername") clients.append((reader, writer, peer)) log.info("Incoming connection from: %s:%s", peer[0], peer[1]) try: while True: - data = yield from reader.readline() + data = await reader.readline() if not data: break try: @@ -194,7 +191,7 @@ def client_connected_callback(self, reader, writer): line = line + DELIM if valid_packet(line): - yield from self.handle_raw_tx_packet(writer, line) + await self.handle_raw_tx_packet(writer, line) else: log.warning( " %s:%s: dropping invalid data: '%s'", peer[0], peer[1], line @@ -228,8 +225,7 @@ def reconnect(self, exc=None): log.warning("disconnected from Rflink, reconnecting") self.loop.create_task(self.connect()) - @asyncio.coroutine - def connect(self): + async def connect(self): """Set up connection and hook it into HA for reconnect/shutdown.""" import serial @@ -256,7 +252,7 @@ def connect(self): try: with async_timeout.timeout(CONNECTION_TIMEOUT): - self.transport, self.protocol = yield from connection + self.transport, self.protocol = await connection except ( serial.serialutil.SerialException, @@ -303,7 +299,6 @@ def main(argv=sys.argv[1:], loop=None): proxy.client_connected_callback, host="", port=listenport, - loop=loop, ) server = loop.run_until_complete(server_coro) diff --git a/tests/test_cli.py b/tests/test_cli.py index f483ee7..e006e0e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -13,10 +13,9 @@ def test_spawns(monkeypatch): asyncio.set_event_loop(loop) # setup task to stop CLI loop - @asyncio.coroutine - def stop(): + async def stop(): """Wait and close loop.""" - yield from asyncio.sleep(0.1) + await asyncio.sleep(0.1) loop.stop() if hasattr(asyncio, "ensure_future"): diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 6adf26b..3fd2439 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -13,10 +13,9 @@ def test_spawns(monkeypatch): asyncio.set_event_loop(loop) # setup task to stop CLI loop - @asyncio.coroutine - def stop(): + async def stop(): """Wait and close loop.""" - yield from asyncio.sleep(0.1) + await asyncio.sleep(0.1) loop.stop() if hasattr(asyncio, "ensure_future"): diff --git a/tox.ini b/tox.ini index 97a441b..919709b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38,py39,lint,typing +envlist = py36,py37,py38,py39,lint,typing,py311 skip_missing_interpreters = True [gh-actions] @@ -7,8 +7,9 @@ python = 2.7: py27 3.6: py36 3.7: py37 - 3.8: py38, lint + 3.8: py38 3.9: py39 + 3.11: py311, lint [testenv] commands = py.test \ @@ -25,7 +26,7 @@ usedevelop = True [testenv:fix] commands = - autopep8 --aggressive --in-place --recursive . + autopep8 --aggressive --in-place --recursive . isort -rc . black . deps = @@ -34,13 +35,15 @@ deps = autopep8 [testenv:lint] -commands = +commands = pylama setup.py rflink rflinkproxy tests black --check . deps = isort pylama black + pydocstyle<6 + pyflakes<2.5 [testenv:typing] commands = mypy --strict --ignore-missing-imports rflink