From 438b0ad0a0eff3c2be41ba4014062ff198df86e0 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Sun, 1 Oct 2017 14:05:19 +0100 Subject: [PATCH 01/10] Ensure API return type consistent (breaking) --- example_async.py | 2 +- example_sync.py | 2 +- pytradfri/api/aiocoap_api.py | 7 ++++--- pytradfri/api/libcoap_api.py | 5 +++-- setup.py | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/example_async.py b/example_async.py index 3b37795b..458d7ae9 100644 --- a/example_async.py +++ b/example_async.py @@ -41,7 +41,7 @@ def run(): devices_command = gateway.get_devices() devices_commands = yield from api(devices_command) - devices = yield from api(*devices_commands) + devices = yield from api(devices_commands) lights = [dev for dev in devices if dev.has_light_control] diff --git a/example_sync.py b/example_sync.py index 627d3109..13bc089b 100644 --- a/example_sync.py +++ b/example_sync.py @@ -45,7 +45,7 @@ def run(): devices_command = gateway.get_devices() devices_commands = api(devices_command) - devices = api(*devices_commands) + devices = api(devices_commands) lights = [dev for dev in devices if dev.has_light_control] diff --git a/pytradfri/api/aiocoap_api.py b/pytradfri/api/aiocoap_api.py index b31cf538..92c6581e 100644 --- a/pytradfri/api/aiocoap_api.py +++ b/pytradfri/api/aiocoap_api.py @@ -1,5 +1,6 @@ """Coap implementation using aiocoap.""" import asyncio +import collections import json import logging @@ -119,10 +120,10 @@ def _execute(api_command): return api_command.result @asyncio.coroutine - def request(*api_commands): + def request(api_commands): """Make a request.""" - if len(api_commands) == 1: - result = yield from _execute(api_commands[0]) + if not isinstance(api_commands, collections.Iterable): + result = yield from _execute(api_commands) return result commands = (_execute(api_command) for api_command in api_commands) diff --git a/pytradfri/api/libcoap_api.py b/pytradfri/api/libcoap_api.py index 5fa7b1f0..f5227b95 100644 --- a/pytradfri/api/libcoap_api.py +++ b/pytradfri/api/libcoap_api.py @@ -1,4 +1,5 @@ """Coap implementation.""" +import collections import json import logging import subprocess @@ -72,9 +73,9 @@ def _execute(api_command): api_command.result = _process_output(return_value, parse_json) return api_command.result - def request(*api_commands): + def request(api_commands): """Make a request.""" - if len(api_commands) == 1: + if not isinstance(api_commands, collections.Iterable): return _execute(api_commands[0]) command_results = [] diff --git a/setup.py b/setup.py index cec0eab1..133c07d5 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -VERSION = "2.2.2" +VERSION = "3.0" DOWNLOAD_URL = \ 'https://github.com/ggravlingen/pytradfri/archive/{}.zip'.format(VERSION) From a6956475ec1e16083b7306d09de964f6ceeab2e9 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Fri, 13 Oct 2017 22:29:16 +0100 Subject: [PATCH 02/10] Add tests for aiocoap_api, switch to DTLSSocket, update aiocoap. --- Dockerfile | 4 +- pytradfri/api/aiocoap_api.py | 13 +------ pytradfri/api/libcoap_api.py | 3 +- requirements.txt | 3 +- requirements_test.txt | 1 + script/install-aiocoap.sh | 11 +----- setup.py | 4 +- tests/api/test_aiocoap_api.py | 70 +++++++++++++++++++++++++++++++++++ 8 files changed, 82 insertions(+), 27 deletions(-) create mode 100644 tests/api/test_aiocoap_api.py diff --git a/Dockerfile b/Dockerfile index 6a77b2e7..9d96f576 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,8 @@ RUN python3 -m pip install cython COPY ./script/install-coap-client.sh install-coap-client.sh RUN ./install-coap-client.sh -COPY ./script/install-aiocoap.sh install-aiocoap.sh -RUN ./install-aiocoap.sh +COPY requirements.txt requirements.txt +RUN python3 -m pip install -r requirements.txt WORKDIR /usr/src/app ENV LANG=C.UTF-8 diff --git a/pytradfri/api/aiocoap_api.py b/pytradfri/api/aiocoap_api.py index 92c6581e..de6013e1 100644 --- a/pytradfri/api/aiocoap_api.py +++ b/pytradfri/api/aiocoap_api.py @@ -1,6 +1,5 @@ """Coap implementation using aiocoap.""" import asyncio -import collections import json import logging @@ -27,14 +26,6 @@ def _get_psk(self, host, port): tinydtls.DTLSSecurityStore = PatchedDTLSSecurityStore -def _patched_datagram_received(self, data, addr): - self.parent._dtls_socket.handleMessage(self.parent._connection, data, 0) - - -tinydtls.DTLSClientConnection.SingleConnection.datagram_received = \ - _patched_datagram_received - - @asyncio.coroutine def api_factory(host, security_code, loop=None): """Generate a request method.""" @@ -76,7 +67,7 @@ def _get_response(msg): protocol = yield from _get_protocol() pr = protocol.request(msg) r = yield from pr.response - return (pr, r) + return pr, r except ConstructionRenderableError as e: raise ClientError("There was an error with the request.", e) except RequestTimedOut as e: @@ -122,7 +113,7 @@ def _execute(api_command): @asyncio.coroutine def request(api_commands): """Make a request.""" - if not isinstance(api_commands, collections.Iterable): + if not isinstance(api_commands, list): result = yield from _execute(api_commands) return result diff --git a/pytradfri/api/libcoap_api.py b/pytradfri/api/libcoap_api.py index f5227b95..89196fa3 100644 --- a/pytradfri/api/libcoap_api.py +++ b/pytradfri/api/libcoap_api.py @@ -1,5 +1,4 @@ """Coap implementation.""" -import collections import json import logging import subprocess @@ -75,7 +74,7 @@ def _execute(api_command): def request(api_commands): """Make a request.""" - if not isinstance(api_commands, collections.Iterable): + if not isinstance(api_commands, list): return _execute(api_commands[0]) command_results = [] diff --git a/requirements.txt b/requirements.txt index 25c3873a..9f948857 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -https://github.com/chrysn/aiocoap/archive/0df6a1e44582de99ae944b6a7536d08e2a612e8f.zip +https://github.com/chrysn/aiocoap/archive/3286f48f0b949901c8b5c04c0719dc54ab63d431.zip +DTLSSocket==0.1.3 diff --git a/requirements_test.txt b/requirements_test.txt index cb64874e..d2b6b257 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,2 +1,3 @@ pytest flake8 +pytest-asyncio \ No newline at end of file diff --git a/script/install-aiocoap.sh b/script/install-aiocoap.sh index 0b8fb4b9..1a190bae 100755 --- a/script/install-aiocoap.sh +++ b/script/install-aiocoap.sh @@ -1,14 +1,7 @@ #!/bin/sh -git clone --depth 1 https://git.fslab.de/jkonra2m/tinydtls.git -cd tinydtls -autoreconf -./configure --with-ecc --without-debug -cd cython -python3 setup.py install - cd ../.. -git clone --depth 1 https://github.com/chrysn/aiocoap +git clone https://github.com/chrysn/aiocoap cd aiocoap -git reset --hard 0df6a1e44582de99ae944b6a7536d08e2a612e8f +git reset --hard 3286f48f0b949901c8b5c04c0719dc54ab63d431 python3 -m pip install --upgrade pip setuptools python3 -m pip install . diff --git a/setup.py b/setup.py index 133c07d5..ad47341b 100644 --- a/setup.py +++ b/setup.py @@ -5,11 +5,11 @@ 'https://github.com/ggravlingen/pytradfri/archive/{}.zip'.format(VERSION) EXTRAS_REQUIRE = { - 'async': ['aiocoap >= 0.3'] + 'async': ['aiocoap >= 0.3', 'DTLSSocket >= 0.1.3'] } DEP_LINKS = [ - "https://github.com/chrysn/aiocoap/archive/0df6a1e44582de99ae944b6a7536d08e2a612e8f.zip#egg=aiocoap-0.3" + "https://github.com/chrysn/aiocoap/archive/3286f48f0b949901c8b5c04c0719dc54ab63d431.zip#egg=aiocoap-0.3" ] PACKAGES = find_packages(exclude=['tests', 'tests.*']) diff --git a/tests/api/test_aiocoap_api.py b/tests/api/test_aiocoap_api.py new file mode 100644 index 00000000..b06f25f4 --- /dev/null +++ b/tests/api/test_aiocoap_api.py @@ -0,0 +1,70 @@ +"""Test API utilities.""" +import asyncio +import pytest + +from pytradfri.api.aiocoap_api import api_factory +from pytradfri.command import Command + + +class MockCode: + @property + def is_successful(self): + return True + + +class MockResponse: + @property + def code(self): + return MockCode() + + @property + def payload(self): + return '{}'.encode('utf-8') + + +class MockProtocol: + @asyncio.coroutine + def mock_response(self): + return MockResponse() + + @property + def response(self): + return self.mock_response() + + +class MockContext: + def request(self, arg): + return MockProtocol() + + +@asyncio.coroutine +def mock_create_context(loop): + return MockContext() + + +@pytest.mark.asyncio +def test_request_returns_single(monkeypatch): + monkeypatch.setattr('aiocoap.Context.create_client_context', + mock_create_context) + + api = yield from api_factory('127.0.0.1', 'key') + + command = Command('', '') + + response = yield from api(command) + + assert type(response) != list + + +@pytest.mark.asyncio +def test_request_returns_list(monkeypatch): + monkeypatch.setattr('aiocoap.Context.create_client_context', + mock_create_context) + + api = yield from api_factory('127.0.0.1', 'key') + + command = Command('', '') + + response = yield from api([command, command, command]) + + assert type(response) == list \ No newline at end of file From 98fcc4f098dd21d1b26c7ab0803e88f87ca51903 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Fri, 13 Oct 2017 22:32:17 +0100 Subject: [PATCH 03/10] Formatting. --- requirements_test.txt | 2 +- tests/api/test_aiocoap_api.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index d2b6b257..e5c98f91 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,3 +1,3 @@ +pytest-asyncio pytest flake8 -pytest-asyncio \ No newline at end of file diff --git a/tests/api/test_aiocoap_api.py b/tests/api/test_aiocoap_api.py index b06f25f4..250e342e 100644 --- a/tests/api/test_aiocoap_api.py +++ b/tests/api/test_aiocoap_api.py @@ -67,4 +67,4 @@ def test_request_returns_list(monkeypatch): response = yield from api([command, command, command]) - assert type(response) == list \ No newline at end of file + assert type(response) == list From 070faeb4603d9370eb1f700389345c776321705e Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Fri, 13 Oct 2017 22:36:34 +0100 Subject: [PATCH 04/10] Pytest version. --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index e5c98f91..b011471a 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,3 +1,3 @@ pytest-asyncio -pytest +pytest>3.2.3 flake8 From c03e450fc2a78bd8e3389f2e7ba520332e53da4d Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Fri, 13 Oct 2017 22:38:21 +0100 Subject: [PATCH 05/10] Pytest version. --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index b011471a..abd87ed4 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,3 +1,3 @@ pytest-asyncio -pytest>3.2.3 +pytest>=3.2.3 flake8 From f715d2d1c03232e815fa9441691b72cb3448e11c Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Fri, 13 Oct 2017 22:42:20 +0100 Subject: [PATCH 06/10] Install all requirements. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 39096f2c..7ba9fd6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ cache: directories: - $HOME/.cache/pip install: + - pip install cython + - pip install -r requirements.txt - pip install -r requirements_test.txt - python3 setup.py develop language: python From 069fcd7866bfd0a8664f4e7e8a51ef70f20a7cb2 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Sat, 14 Oct 2017 09:15:55 +0100 Subject: [PATCH 07/10] Switch requirements around, fix tests. --- .travis.yml | 5 ++--- Dockerfile | 2 +- requirements_test.txt | 1 - script/install-aiocoap.sh | 7 ------- tests/api/test_aiocoap_api.py | 16 +++++++++++++--- 5 files changed, 16 insertions(+), 15 deletions(-) delete mode 100755 script/install-aiocoap.sh diff --git a/.travis.yml b/.travis.yml index 7ba9fd6b..6ccf15b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,9 @@ cache: - $HOME/.cache/pip install: - pip install cython - - pip install -r requirements.txt - pip install -r requirements_test.txt - - python3 setup.py develop + - python3 -m pip install .[async] language: python script: - py.test - - flake8 + - flake8 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 9d96f576..9f8cc97c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update -y && \ RUN mkdir -p /usr/src/app /usr/src/build WORKDIR /usr/src/build -RUN python3 -m pip install cython +RUN python3 -m pip install --upgrade pip setuptools wheel cython COPY ./script/install-coap-client.sh install-coap-client.sh RUN ./install-coap-client.sh diff --git a/requirements_test.txt b/requirements_test.txt index abd87ed4..32a8c11f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,3 +1,2 @@ -pytest-asyncio pytest>=3.2.3 flake8 diff --git a/script/install-aiocoap.sh b/script/install-aiocoap.sh deleted file mode 100755 index 1a190bae..00000000 --- a/script/install-aiocoap.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -cd ../.. -git clone https://github.com/chrysn/aiocoap -cd aiocoap -git reset --hard 3286f48f0b949901c8b5c04c0719dc54ab63d431 -python3 -m pip install --upgrade pip setuptools -python3 -m pip install . diff --git a/tests/api/test_aiocoap_api.py b/tests/api/test_aiocoap_api.py index 250e342e..f64135a4 100644 --- a/tests/api/test_aiocoap_api.py +++ b/tests/api/test_aiocoap_api.py @@ -1,11 +1,21 @@ """Test API utilities.""" import asyncio -import pytest +import functools from pytradfri.api.aiocoap_api import api_factory from pytradfri.command import Command +def async_test(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + coro = asyncio.coroutine(f) + future = coro(*args, **kwargs) + loop = asyncio.get_event_loop() + loop.run_until_complete(future) + return wrapper + + class MockCode: @property def is_successful(self): @@ -42,7 +52,7 @@ def mock_create_context(loop): return MockContext() -@pytest.mark.asyncio +@async_test def test_request_returns_single(monkeypatch): monkeypatch.setattr('aiocoap.Context.create_client_context', mock_create_context) @@ -56,7 +66,7 @@ def test_request_returns_single(monkeypatch): assert type(response) != list -@pytest.mark.asyncio +@async_test def test_request_returns_list(monkeypatch): monkeypatch.setattr('aiocoap.Context.create_client_context', mock_create_context) From c06d7a67a03b84f3d7486c0bb5f8964c127df3aa Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Sat, 14 Oct 2017 09:19:55 +0100 Subject: [PATCH 08/10] Fix travis. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ccf15b5..7ba9fd6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,10 @@ cache: - $HOME/.cache/pip install: - pip install cython + - pip install -r requirements.txt - pip install -r requirements_test.txt - - python3 -m pip install .[async] + - python3 setup.py develop language: python script: - py.test - - flake8 \ No newline at end of file + - flake8 From 228da9169ba447cbcb6aec7e2733152d0bb7e77f Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Sat, 14 Oct 2017 09:22:44 +0100 Subject: [PATCH 09/10] Fix travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7ba9fd6b..01a28e1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: directories: - $HOME/.cache/pip install: - - pip install cython + - pip install --upgrade pip setuptools wheel cython - pip install -r requirements.txt - pip install -r requirements_test.txt - python3 setup.py develop From 9544a67b9bade340066355aead85e59d6929f176 Mon Sep 17 00:00:00 2001 From: Lewis Juggins Date: Sat, 14 Oct 2017 09:25:17 +0100 Subject: [PATCH 10/10] Fix travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 01a28e1f..4cbcb175 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: false matrix: fast_finish: true include: - - python: "3.4.2" + - python: "3.4.4" env: TOXENV=py34 - python: "3.5" env: TOXENV=py35