enapter 0.9.2__py3-none-any.whl → 0.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of enapter might be problematic. Click here for more details.
- enapter/__init__.py +1 -1
- enapter/log/json_formatter.py +1 -1
- enapter/types.py +2 -2
- enapter/vucm/__init__.py +3 -1
- enapter/vucm/app.py +2 -2
- enapter/vucm/config.py +3 -1
- enapter/vucm/device.py +40 -11
- enapter/vucm/ucm.py +9 -5
- {enapter-0.9.2.dist-info → enapter-0.10.0.dist-info}/METADATA +24 -16
- {enapter-0.9.2.dist-info → enapter-0.10.0.dist-info}/RECORD +14 -14
- {enapter-0.9.2.dist-info → enapter-0.10.0.dist-info}/WHEEL +1 -1
- tests/unit/test_log.py +3 -5
- tests/unit/test_vucm.py +95 -0
- {enapter-0.9.2.dist-info → enapter-0.10.0.dist-info}/top_level.txt +0 -0
enapter/__init__.py
CHANGED
enapter/log/json_formatter.py
CHANGED
|
@@ -6,7 +6,7 @@ import json_log_formatter
|
|
|
6
6
|
class JSONFormatter(json_log_formatter.JSONFormatter):
|
|
7
7
|
def json_record(self, message, extra, record):
|
|
8
8
|
json_record = {
|
|
9
|
-
"time": datetime.datetime.
|
|
9
|
+
"time": datetime.datetime.now(datetime.UTC).isoformat(),
|
|
10
10
|
"level": record.levelname[:4],
|
|
11
11
|
"name": record.name,
|
|
12
12
|
**extra,
|
enapter/types.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Dict, List, Union
|
|
2
2
|
|
|
3
|
-
JSON = Dict[str,
|
|
3
|
+
JSON = Union[str, int, float, None, bool, List["JSON"], Dict[str, "JSON"]]
|
enapter/vucm/__init__.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
from .app import App, run
|
|
2
2
|
from .config import Config
|
|
3
|
-
from .device import Device
|
|
3
|
+
from .device import Device, device_command, device_task
|
|
4
4
|
from .ucm import UCM
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
7
|
"App",
|
|
8
8
|
"Config",
|
|
9
9
|
"Device",
|
|
10
|
+
"device_command",
|
|
11
|
+
"device_task",
|
|
10
12
|
"UCM",
|
|
11
13
|
"run",
|
|
12
14
|
]
|
enapter/vucm/app.py
CHANGED
|
@@ -6,10 +6,10 @@ from .config import Config
|
|
|
6
6
|
from .ucm import UCM
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
async def run(device_factory):
|
|
9
|
+
async def run(device_factory, config_prefix=None):
|
|
10
10
|
enapter.log.configure(level=enapter.log.LEVEL or "info")
|
|
11
11
|
|
|
12
|
-
config = Config.from_env()
|
|
12
|
+
config = Config.from_env(prefix=config_prefix)
|
|
13
13
|
|
|
14
14
|
async with App(config=config, device_factory=device_factory) as app:
|
|
15
15
|
await app.join()
|
enapter/vucm/config.py
CHANGED
|
@@ -7,7 +7,9 @@ import enapter
|
|
|
7
7
|
|
|
8
8
|
class Config:
|
|
9
9
|
@classmethod
|
|
10
|
-
def from_env(cls, prefix=
|
|
10
|
+
def from_env(cls, prefix=None, env=os.environ):
|
|
11
|
+
if prefix is None:
|
|
12
|
+
prefix = "ENAPTER_VUCM_"
|
|
11
13
|
try:
|
|
12
14
|
blob = os.environ[prefix + "BLOB"]
|
|
13
15
|
except KeyError:
|
enapter/vucm/device.py
CHANGED
|
@@ -2,25 +2,57 @@ import asyncio
|
|
|
2
2
|
import concurrent
|
|
3
3
|
import functools
|
|
4
4
|
import traceback
|
|
5
|
-
from typing import Optional, Set
|
|
5
|
+
from typing import Any, Callable, Coroutine, Optional, Set
|
|
6
6
|
|
|
7
7
|
import enapter
|
|
8
8
|
|
|
9
9
|
from .logger import Logger
|
|
10
10
|
|
|
11
|
+
DEVICE_TASK_MARK = "_enapter_vucm_device_task"
|
|
12
|
+
DEVICE_COMMAND_MARK = "_enapter_vucm_device_command"
|
|
13
|
+
|
|
14
|
+
DeviceTaskFunc = Callable[[Any], Coroutine]
|
|
15
|
+
DeviceCommandFunc = Callable[..., Coroutine]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def device_task(func: DeviceTaskFunc) -> DeviceTaskFunc:
|
|
19
|
+
setattr(func, DEVICE_TASK_MARK, True)
|
|
20
|
+
return func
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def device_command(func: DeviceCommandFunc) -> DeviceTaskFunc:
|
|
24
|
+
setattr(func, DEVICE_COMMAND_MARK, True)
|
|
25
|
+
return func
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_device_task(func: DeviceTaskFunc) -> bool:
|
|
29
|
+
return getattr(func, DEVICE_TASK_MARK, False) is True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_device_command(func: DeviceCommandFunc) -> bool:
|
|
33
|
+
return getattr(func, DEVICE_COMMAND_MARK, False) is True
|
|
34
|
+
|
|
11
35
|
|
|
12
36
|
class Device(enapter.async_.Routine):
|
|
13
37
|
def __init__(
|
|
14
38
|
self,
|
|
15
39
|
channel,
|
|
16
40
|
cmd_prefix="cmd_",
|
|
17
|
-
task_prefix="task_",
|
|
18
41
|
thread_pool_executor=None,
|
|
19
42
|
) -> None:
|
|
20
43
|
self.__channel = channel
|
|
21
44
|
|
|
22
|
-
self.
|
|
23
|
-
|
|
45
|
+
self.__tasks = {}
|
|
46
|
+
for name in dir(self):
|
|
47
|
+
obj = getattr(self, name)
|
|
48
|
+
if is_device_task(obj):
|
|
49
|
+
self.__tasks[name] = obj
|
|
50
|
+
|
|
51
|
+
self.__commands = {}
|
|
52
|
+
for name in dir(self):
|
|
53
|
+
obj = getattr(self, name)
|
|
54
|
+
if is_device_command(obj):
|
|
55
|
+
self.__commands[name] = obj
|
|
24
56
|
|
|
25
57
|
if thread_pool_executor is None:
|
|
26
58
|
thread_pool_executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
|
@@ -62,11 +94,8 @@ class Device(enapter.async_.Routine):
|
|
|
62
94
|
|
|
63
95
|
tasks = set()
|
|
64
96
|
|
|
65
|
-
for name in
|
|
66
|
-
|
|
67
|
-
task_func = getattr(self, name)
|
|
68
|
-
name_without_prefix = name[len(self.__task_prefix) :]
|
|
69
|
-
tasks.add(asyncio.create_task(task_func(), name=name_without_prefix))
|
|
97
|
+
for name, func in self.__tasks.items():
|
|
98
|
+
tasks.add(asyncio.create_task(func(), name=name))
|
|
70
99
|
|
|
71
100
|
tasks.add(
|
|
72
101
|
asyncio.create_task(
|
|
@@ -107,8 +136,8 @@ class Device(enapter.async_.Routine):
|
|
|
107
136
|
|
|
108
137
|
async def __execute_command(self, req):
|
|
109
138
|
try:
|
|
110
|
-
cmd =
|
|
111
|
-
except
|
|
139
|
+
cmd = self._commands[req.name]
|
|
140
|
+
except KeyError:
|
|
112
141
|
return enapter.mqtt.api.CommandState.ERROR, {"reason": "unknown command"}
|
|
113
142
|
|
|
114
143
|
try:
|
enapter/vucm/ucm.py
CHANGED
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
|
|
3
3
|
import enapter
|
|
4
4
|
|
|
5
|
-
from .device import Device
|
|
5
|
+
from .device import Device, device_command, device_task
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class UCM(Device):
|
|
@@ -13,20 +13,24 @@ class UCM(Device):
|
|
|
13
13
|
)
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
@device_command
|
|
17
|
+
async def reboot(self):
|
|
17
18
|
await asyncio.sleep(0)
|
|
18
19
|
raise NotImplementedError
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
@device_command
|
|
22
|
+
async def upload_lua_script(self, url, sha1, payload=None):
|
|
21
23
|
await asyncio.sleep(0)
|
|
22
24
|
raise NotImplementedError
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
@device_task
|
|
27
|
+
async def telemetry_publisher(self) -> None:
|
|
25
28
|
while True:
|
|
26
29
|
await self.send_telemetry()
|
|
27
30
|
await asyncio.sleep(1)
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
@device_task
|
|
33
|
+
async def properties_publisher(self):
|
|
30
34
|
while True:
|
|
31
35
|
await self.send_properties({"virtual": True, "lua_api_ver": 1})
|
|
32
36
|
await asyncio.sleep(10)
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: enapter
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Enapter Python SDK
|
|
5
5
|
Home-page: https://github.com/Enapter/python-sdk
|
|
6
6
|
Author: Roman Novatorov
|
|
7
7
|
Author-email: rnovatorov@enapter.com
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: aiomqtt
|
|
10
|
-
Requires-Dist: dnspython
|
|
11
|
-
Requires-Dist: json-log-formatter
|
|
9
|
+
Requires-Dist: aiomqtt==1.0.*
|
|
10
|
+
Requires-Dist: dnspython==2.6.*
|
|
11
|
+
Requires-Dist: json-log-formatter==0.5.*
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: author-email
|
|
14
|
+
Dynamic: description
|
|
15
|
+
Dynamic: description-content-type
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: requires-dist
|
|
18
|
+
Dynamic: summary
|
|
12
19
|
|
|
13
20
|
# Enapter Python SDK
|
|
14
21
|
|
|
@@ -42,12 +49,13 @@ Checkout [examples](examples).
|
|
|
42
49
|
|
|
43
50
|
### Device Telemetry and Properties
|
|
44
51
|
|
|
45
|
-
Every method of `enapter.vucm.Device` subclass
|
|
46
|
-
`
|
|
47
|
-
all of its tasks are started as well. Device tasks are
|
|
48
|
-
and are being executed concurrently in the background.
|
|
49
|
-
or raises an exception, device routine is terminated.
|
|
50
|
-
is to run a periodic job to send device telemetry and
|
|
52
|
+
Every method of `enapter.vucm.Device` subclass decorated with
|
|
53
|
+
`enapter.vucm.device_task` decorator is considered a _device task_. When such a
|
|
54
|
+
device is started, all of its tasks are started as well. Device tasks are
|
|
55
|
+
started in random order and are being executed concurrently in the background.
|
|
56
|
+
If a device task returns or raises an exception, device routine is terminated.
|
|
57
|
+
A typical use of the task is to run a periodic job to send device telemetry and
|
|
58
|
+
properties.
|
|
51
59
|
|
|
52
60
|
In order to send telemetry and properties define two corresponding device
|
|
53
61
|
tasks. It is advised (but is not obligatory) to send telemetry every **1
|
|
@@ -57,12 +65,12 @@ Examples:
|
|
|
57
65
|
|
|
58
66
|
- [wttr-in](examples/vucm/wttr-in)
|
|
59
67
|
|
|
60
|
-
### Device
|
|
68
|
+
### Device Command Handlers
|
|
61
69
|
|
|
62
|
-
Every method of `enapter.vucm.Device` subclass
|
|
63
|
-
`
|
|
64
|
-
receive the same arguments as described in device Blueprint
|
|
65
|
-
optionally return a payload as `
|
|
70
|
+
Every method of `enapter.vucm.Device` subclass decorated with
|
|
71
|
+
`enapter.vucm.device_command` is considered a _device command handler_. Device
|
|
72
|
+
command handlers receive the same arguments as described in device Blueprint
|
|
73
|
+
manifest and can optionally return a payload as `enapter.types.JSON`.
|
|
66
74
|
|
|
67
75
|
In order to handle device commands define corresponding device command
|
|
68
76
|
handlers.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
enapter/__init__.py,sha256=
|
|
2
|
-
enapter/types.py,sha256=
|
|
1
|
+
enapter/__init__.py,sha256=No4wlAO2UL9UZK-GmXjX2uitUxjgply-xv38DCZb4_w,183
|
|
2
|
+
enapter/types.py,sha256=J_rFCW79cloh2FF_49Oab5kaEGdZohymkCJU7vfko-8,113
|
|
3
3
|
enapter/async_/__init__.py,sha256=JuiRI2bN2AgB-HLfAUoSsZpEziwFRftNNEp59Evnd0M,109
|
|
4
4
|
enapter/async_/generator.py,sha256=qBhnt36Gl2166sJFnZHsREbZu8l43M4DfxybUMIv6W4,300
|
|
5
5
|
enapter/async_/routine.py,sha256=A5fG4XnCEQT0Qa_JNh1N43Fv5lnLaCoGF4xt6pOAdNs,1770
|
|
6
6
|
enapter/log/__init__.py,sha256=n1sWMDKJSs_ebZzjbTrVdfg-oi0V1tvliTxgIV-msJ0,600
|
|
7
|
-
enapter/log/json_formatter.py,sha256=
|
|
7
|
+
enapter/log/json_formatter.py,sha256=mkcWW2VmksCmRVPU7ayxbNp5-eRetznSjZlA-DUloOY,714
|
|
8
8
|
enapter/mdns/__init__.py,sha256=uwsg8wJ0Lcsr9oOEW1PkEV3jVgWzgA7RG2ur_MRLtM0,55
|
|
9
9
|
enapter/mdns/resolver.py,sha256=FFQuZiaKOaNtfSgI2YOaSdG-BuZwOKmYVy06Sc735Zo,1297
|
|
10
10
|
enapter/mqtt/__init__.py,sha256=aKJklTmR8OEnwnQXN1MtrvJimC9EfxcTOqhhBsPcb84,126
|
|
@@ -14,12 +14,12 @@ enapter/mqtt/api/__init__.py,sha256=M1g2891bSLCnDbZOuLElEPPlN6pJk0J1w_1Fi8x5xJU,
|
|
|
14
14
|
enapter/mqtt/api/command.py,sha256=ozhDTjRrdCWv_bzPTjVFpL8tx7nhirm3JtQaD45wTdo,1092
|
|
15
15
|
enapter/mqtt/api/device_channel.py,sha256=fma_R59kxMoH6S4DwDBcSqgTsr8KNJkEov-c3poNsD0,2371
|
|
16
16
|
enapter/mqtt/api/log_severity.py,sha256=ZmHXMc8d1e2ZnsXDWwl3S3noAEJjILYab_qjqk374Qw,126
|
|
17
|
-
enapter/vucm/__init__.py,sha256=
|
|
18
|
-
enapter/vucm/app.py,sha256=
|
|
19
|
-
enapter/vucm/config.py,sha256=
|
|
20
|
-
enapter/vucm/device.py,sha256=
|
|
17
|
+
enapter/vucm/__init__.py,sha256=40GAkJvpCl7oMuWDU09zpjP5rM4V-oKRbh_R1uhx4aE,247
|
|
18
|
+
enapter/vucm/app.py,sha256=zHdEEUnKrVHNvVGmw7Jrv5MFkVnbg5yiiped86jxbSU,1424
|
|
19
|
+
enapter/vucm/config.py,sha256=Lj-YLQvoZ9ivXVBF0oKRvdupLRJ2l55EEzoxZ4HWWRE,1713
|
|
20
|
+
enapter/vucm/device.py,sha256=FiJSJNgZkWGL-866IGY8bbqS75ZZigJZYyoH7kMW7Is,4352
|
|
21
21
|
enapter/vucm/logger.py,sha256=a_ZtuIinATbEtiP5avV8JGAKFKG0-RZhF6SozaF-_PU,1445
|
|
22
|
-
enapter/vucm/ucm.py,sha256=
|
|
22
|
+
enapter/vucm/ucm.py,sha256=r1q1rSYlT7GWZ24JLmTbzkpMDUUHcvCNp9t5xHTEyzY,957
|
|
23
23
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
24
|
tests/conftest.py,sha256=qEZwCEK8F6hJZ5sSHK6hUmUYKaWfY77UuNOHntfNS3c,397
|
|
25
25
|
tests/fake_data_generator.py,sha256=lpVgazRRXAP07SeiTeY3Fe1LzDdM44lS50QTlwBdMYg,587
|
|
@@ -28,11 +28,11 @@ tests/integration/conftest.py,sha256=TpbUrExImSsLp77FIp1O-6wJrxgTmyxCgmmATIDEmL4
|
|
|
28
28
|
tests/integration/test_mqtt.py,sha256=Hm165Sxysrc5ZatJjiMKITv0-78N0NbGiTI5QrJ_aSU,3891
|
|
29
29
|
tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
30
|
tests/unit/test_async.py,sha256=KwhwKBSeSb7Qyaf50Ca2LGB7gm3m5j5wgGgWnvYY98k,4208
|
|
31
|
-
tests/unit/test_log.py,sha256=
|
|
32
|
-
tests/unit/test_vucm.py,sha256=
|
|
31
|
+
tests/unit/test_log.py,sha256=Q-ZelqGfladBCaw-BQrwRrxbxMK1VZxgY7HBsBb1GHw,1875
|
|
32
|
+
tests/unit/test_vucm.py,sha256=a3euiqV9etsfrWFDUtHCNqKU3irx-GEZMczBcmddhek,3626
|
|
33
33
|
tests/unit/test_mqtt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
34
|
tests/unit/test_mqtt/test_api.py,sha256=ObKCHB-KDOYQLFrdzjTmLfjdWXXX0oanGKpX49P0qMI,2670
|
|
35
|
-
enapter-0.
|
|
36
|
-
enapter-0.
|
|
37
|
-
enapter-0.
|
|
38
|
-
enapter-0.
|
|
35
|
+
enapter-0.10.0.dist-info/METADATA,sha256=354uZuKv0_6QgPow7MPuP6VT6GNMNKYLfhSnSXNo0Fw,3335
|
|
36
|
+
enapter-0.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
37
|
+
enapter-0.10.0.dist-info/top_level.txt,sha256=DsMzVradd7z3A0fm7zmn9oh08ijO41RtzglrnPlx54w,14
|
|
38
|
+
enapter-0.10.0.dist-info/RECORD,,
|
tests/unit/test_log.py
CHANGED
|
@@ -26,11 +26,9 @@ class TestJSONFormat:
|
|
|
26
26
|
time = record.pop("time")
|
|
27
27
|
assert re.match(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+", time) is not None
|
|
28
28
|
|
|
29
|
-
assert record ==
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"message": "hello",
|
|
33
|
-
}
|
|
29
|
+
assert record["level"] == "INFO"
|
|
30
|
+
assert record["name"] == "enapter"
|
|
31
|
+
assert record["message"] == "hello"
|
|
34
32
|
|
|
35
33
|
def test_exc_info(self):
|
|
36
34
|
buf = io.StringIO()
|
tests/unit/test_vucm.py
CHANGED
|
@@ -11,6 +11,101 @@ class TestDevice:
|
|
|
11
11
|
assert await device.run_in_thread(lambda: 42) == 42
|
|
12
12
|
assert device._Device__thread_pool_executor._shutdown
|
|
13
13
|
|
|
14
|
+
async def test_task_marks(self, fake):
|
|
15
|
+
class MyDevice(enapter.vucm.Device):
|
|
16
|
+
async def task_foo(self):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
@enapter.vucm.device_task
|
|
20
|
+
async def task_bar(self):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@enapter.vucm.device_task
|
|
24
|
+
async def baz(self):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@enapter.vucm.device_task
|
|
28
|
+
async def goo(self):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
async with MyDevice(channel=MockChannel(fake)) as device:
|
|
32
|
+
tasks = device._Device__tasks
|
|
33
|
+
assert len(tasks) == 3
|
|
34
|
+
assert "task_foo" not in tasks
|
|
35
|
+
assert "task_bar" in tasks
|
|
36
|
+
assert "baz" in tasks
|
|
37
|
+
assert "goo" in tasks
|
|
38
|
+
|
|
39
|
+
async def test_command_marks(self, fake):
|
|
40
|
+
class MyDevice(enapter.vucm.Device):
|
|
41
|
+
async def cmd_foo(self):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
async def cmd_foo2(self, a, b, c):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
@enapter.vucm.device_command
|
|
48
|
+
async def cmd_bar(self):
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
@enapter.vucm.device_command
|
|
52
|
+
async def cmd_bar2(self, a, b, c):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
@enapter.vucm.device_command
|
|
56
|
+
async def baz(self):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
@enapter.vucm.device_command
|
|
60
|
+
async def baz2(self, a, b, c):
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
@enapter.vucm.device_command
|
|
64
|
+
async def goo(self):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
async with MyDevice(channel=MockChannel(fake)) as device:
|
|
68
|
+
commands = device._Device__commands
|
|
69
|
+
assert len(commands) == 5
|
|
70
|
+
assert "cmd_foo" not in commands
|
|
71
|
+
assert "cmd_foo2" not in commands
|
|
72
|
+
assert "cmd_bar" in commands
|
|
73
|
+
assert "cmd_bar2" in commands
|
|
74
|
+
assert "baz" in commands
|
|
75
|
+
assert "baz2" in commands
|
|
76
|
+
assert "goo" in commands
|
|
77
|
+
|
|
78
|
+
async def test_task_and_commands_marks(self, fake):
|
|
79
|
+
class MyDevice(enapter.vucm.Device):
|
|
80
|
+
@enapter.vucm.device_task
|
|
81
|
+
async def foo_task(self):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
@enapter.vucm.device_task
|
|
85
|
+
async def bar_task(self):
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
@enapter.vucm.device_command
|
|
89
|
+
async def foo_command(self):
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
@enapter.vucm.device_command
|
|
93
|
+
async def bar_command(self):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
async with MyDevice(channel=MockChannel(fake)) as device:
|
|
97
|
+
tasks = device._Device__tasks
|
|
98
|
+
assert len(tasks) == 2
|
|
99
|
+
assert "foo_task" in tasks
|
|
100
|
+
assert "bar_task" in tasks
|
|
101
|
+
assert "foo_command" not in tasks
|
|
102
|
+
assert "bar_command" not in tasks
|
|
103
|
+
commands = device._Device__commands
|
|
104
|
+
assert "foo_task" not in commands
|
|
105
|
+
assert "bar_task" not in commands
|
|
106
|
+
assert "foo_command" in commands
|
|
107
|
+
assert "bar_command" in commands
|
|
108
|
+
|
|
14
109
|
|
|
15
110
|
class MockChannel:
|
|
16
111
|
def __init__(self, fake):
|
|
File without changes
|