xaal.lib 0.7.7__tar.gz → 0.7.8__tar.gz

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.
Files changed (36) hide show
  1. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/PKG-INFO +1 -1
  2. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/pyproject.toml +12 -1
  3. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/tests/test_engine.py +4 -4
  4. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/tests/test_message.py +5 -3
  5. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/__init__.py +1 -1
  6. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/aioengine.py +155 -131
  7. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/aiohelpers.py +7 -7
  8. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/aionetwork.py +24 -24
  9. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/bindings.py +30 -32
  10. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/cbor.py +15 -10
  11. xaal_lib-0.7.8/xaal/lib/config.py +82 -0
  12. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/core.py +82 -65
  13. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/devices.py +139 -96
  14. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/engine.py +61 -43
  15. xaal_lib-0.7.8/xaal/lib/exceptions.py +36 -0
  16. xaal_lib-0.7.8/xaal/lib/helpers.py +91 -0
  17. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/messages.py +171 -149
  18. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/network.py +23 -20
  19. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/test.py +25 -27
  20. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/tools.py +50 -36
  21. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal.lib.egg-info/PKG-INFO +1 -1
  22. xaal_lib-0.7.7/xaal/lib/config.py +0 -53
  23. xaal_lib-0.7.7/xaal/lib/exceptions.py +0 -20
  24. xaal_lib-0.7.7/xaal/lib/helpers.py +0 -80
  25. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/README.rst +0 -0
  26. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/setup.cfg +0 -0
  27. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/tests/test_bindings.py +0 -0
  28. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/tests/test_cbor.py +0 -0
  29. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/tests/test_device.py +0 -0
  30. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/tests/test_tools.py +0 -0
  31. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/__init__.py +0 -0
  32. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal/lib/__main__.py +0 -0
  33. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal.lib.egg-info/SOURCES.txt +0 -0
  34. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal.lib.egg-info/dependency_links.txt +0 -0
  35. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal.lib.egg-info/requires.txt +0 -0
  36. {xaal_lib-0.7.7 → xaal_lib-0.7.8}/xaal.lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xaal.lib
3
- Version: 0.7.7
3
+ Version: 0.7.8
4
4
  Summary: Official Python stack for xAAL protocol
5
5
  Author-email: Jerome Kerdreux <Jerome.Kerdreux@imt-atlantique.fr>
6
6
  License: GPL License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "xaal.lib"
3
- version = "0.7.7"
3
+ version = "0.7.8"
4
4
  description = "Official Python stack for xAAL protocol"
5
5
  readme = "README.rst"
6
6
  authors = [
@@ -29,3 +29,14 @@ dependencies = [
29
29
  Homepage = "https://recherche.imt-atlantique.fr/xaal/"
30
30
  Documentation = "https://redmine.imt-atlantique.fr/projects/xaal/repository/xaal/entry/code/Python/branches/0.7/libs/lib/README.rst"
31
31
  Source = "https://redmine.imt-atlantique.fr/projects/xaal/repository/xaal/show/code/Python/branches/0.7/libs/lib"
32
+
33
+
34
+ [tool.ruff]
35
+ line-length = 122
36
+
37
+ [tool.ruff.format]
38
+ quote-style = "preserve"
39
+
40
+ [tool.black]
41
+ line-length = 122
42
+ skip-string-normalization = true
@@ -59,10 +59,10 @@ class TestEngine(unittest.TestCase):
59
59
  target = Device("test.basic", tools.get_random_uuid())
60
60
 
61
61
  def action_1():
62
- return "action_1"
62
+ return {"value":"action_1"}
63
63
 
64
64
  def action_2(_value=None):
65
- return "action_%s" % _value
65
+ return {"value":"action_%s" % _value}
66
66
 
67
67
  def action_3():
68
68
  raise Exception
@@ -77,12 +77,12 @@ class TestEngine(unittest.TestCase):
77
77
  # simple test method
78
78
  msg.action = "action_1"
79
79
  result = engine.run_action(msg, target)
80
- self.assertEqual(result, "action_1")
80
+ self.assertEqual(result, {"value":"action_1"})
81
81
  # test with value
82
82
  msg.action = "action_2"
83
83
  msg.body = {"value": "2"}
84
84
  result = engine.run_action(msg, target)
85
- self.assertEqual(result, "action_2")
85
+ self.assertEqual(result, {"value":"action_2"})
86
86
  # Exception in method
87
87
  msg.action = "action_3"
88
88
  with self.assertRaises(engine.XAALError):
@@ -174,13 +174,15 @@ class TestMessageFactory(unittest.TestCase):
174
174
  mf = test_factory()
175
175
  # too young
176
176
  msg = test_message()
177
- msg.timestamp[0] = msg.timestamp[0] + 60*5
177
+ target = (msg.timestamp[0] + 60*5, msg.timestamp[1])
178
+ msg.timestamp = target
178
179
  data = mf.encode_msg(msg)
179
180
  with self.assertRaises(MessageParserError):
180
181
  mf.decode_msg(data)
181
182
  # too old
182
183
  msg.timestamp = messages.build_timestamp()
183
- msg.timestamp[0] = msg.timestamp[0] - 60*5
184
+ target = (msg.timestamp[0] - 60*5, msg.timestamp[1])
185
+ msg.timestamp = target
184
186
  data = mf.encode_msg(msg)
185
187
  with self.assertRaises(MessageParserError):
186
188
  mf.decode_msg(data)
@@ -214,4 +216,4 @@ class TestMessageFactory(unittest.TestCase):
214
216
  mf.decode_msg(data)
215
217
 
216
218
  if __name__ == '__main__':
217
- unittest.main()
219
+ unittest.main()
@@ -3,8 +3,8 @@
3
3
  # Load main class & modules.
4
4
 
5
5
 
6
+ from .config import config
6
7
  from . import tools
7
- from . import config
8
8
  from . import bindings
9
9
  from . import aiohelpers as helpers
10
10
 
@@ -1,45 +1,80 @@
1
1
  import asyncio
2
-
3
- from . import core
4
- from . import config
5
- from . import tools
6
- from .messages import MessageParserError
7
- from .aionetwork import AsyncNetworkConnector
8
- from .exceptions import *
9
-
2
+ import logging
3
+ import signal
4
+ import sys
10
5
  import time
6
+ import typing
11
7
  from enum import Enum
8
+ from pprint import pprint
9
+ from typing import Any, Optional
10
+ from uuid import UUID
11
+
12
12
  import aioconsole
13
- import signal
14
- import sys
15
13
  from tabulate import tabulate
16
- from pprint import pprint
17
14
 
15
+ from .config import config
16
+ from . import core
17
+ from .aionetwork import AsyncNetworkConnector
18
+ from .exceptions import CallbackError, XAALError
19
+ from .messages import MessageParserError
20
+
21
+ if typing.TYPE_CHECKING:
22
+ from .devices import Device
23
+ from .messages import Message
18
24
 
19
- import logging
20
25
  logger = logging.getLogger(__name__)
21
26
 
22
- class AsyncEngine(core.EngineMixin):
23
27
 
24
- __slots__ = ['__txFifo','_loop','_tasks','_hooks','_watchdog_task','_kill_counter','running_event','watchdog_event','started_event']
28
+ #####################################################
29
+ # Hooks
30
+ #####################################################
31
+ class HookType(Enum):
32
+ start = 0
33
+ stop = 1
34
+
25
35
 
26
- def __init__(self,address=config.address,port=config.port,hops=config.hops,key=config.key):
27
- core.EngineMixin.__init__(self,address,port,hops,key)
36
+ class Hook(object):
37
+ __slots__ = ['type', 'func', 'args', 'kwargs']
38
+
39
+ def __init__(self, type_:HookType, func: core.FuncT, *args, **kwargs):
40
+ # func has to be a callable, but it can be a coroutine or a function
41
+ self.type = type_
42
+ self.func = func
43
+ self.args = args
44
+ self.kwargs = kwargs
28
45
 
29
- self.__txFifo = asyncio.Queue() # tx msg fifo
30
- self._loop = None # event loop
31
- self._hooks = [] # hooks
32
- self._tasks = [] # tasks
33
- self._watchdog_task = None # watchdog task
34
- self._kill_counter = 0 # watchdog counter
35
46
 
36
- self.started_event = asyncio.Event() # engine started event
37
- self.running_event = asyncio.Event() # engine running event
38
- self.watchdog_event = asyncio.Event() # watchdog event
47
+ class AsyncEngine(core.EngineMixin):
48
+ __slots__ = [
49
+ '__txFifo',
50
+ '_loop',
51
+ '_tasks',
52
+ '_hooks',
53
+ '_watchdog_task',
54
+ '_kill_counter',
55
+ 'running_event',
56
+ 'watchdog_event',
57
+ 'started_event',
58
+ ]
59
+
60
+ def __init__(
61
+ self, address: str = config.address, port: int = config.port, hops: int = config.hops, key: bytes = config.key):
62
+ core.EngineMixin.__init__(self, address, port, hops, key)
63
+
64
+ self.__txFifo = asyncio.Queue() # tx msg fifo
65
+ self._loop = None # event loop
66
+ self._hooks = [] # hooks
67
+ self._tasks = [] # tasks
68
+ self._watchdog_task = None # watchdog task
69
+ self._kill_counter = 0 # watchdog counter
70
+
71
+ self.started_event = asyncio.Event() # engine started event
72
+ self.running_event = asyncio.Event() # engine running event
73
+ self.watchdog_event = asyncio.Event() # watchdog event
39
74
 
40
75
  signal.signal(signal.SIGTERM, self.sigkill_handler)
41
76
  signal.signal(signal.SIGINT, self.sigkill_handler)
42
-
77
+
43
78
  # message receive workflow
44
79
  self.subscribe(self.handle_request)
45
80
  # start network
@@ -48,20 +83,20 @@ class AsyncEngine(core.EngineMixin):
48
83
  #####################################################
49
84
  # Hooks
50
85
  #####################################################
51
- def on_start(self,func,*args,**kwargs):
52
- hook = Hook(HookType.start,func,*args,**kwargs)
86
+ def on_start(self, func: core.FuncT, *args, **kwargs):
87
+ hook = Hook(HookType.start, func, *args, **kwargs)
53
88
  self._hooks.append(hook)
54
-
55
- def on_stop(self,func,*args,**kwargs):
56
- hook = Hook(HookType.stop,func,*args,**kwargs)
89
+
90
+ def on_stop(self, func: core.FuncT, *args, **kwargs):
91
+ hook = Hook(HookType.stop, func, *args, **kwargs)
57
92
  self._hooks.append(hook)
58
93
 
59
- async def run_hooks(self,hook_type):
60
- hooks = list(filter(lambda hook: hook.type==hook_type,self._hooks))
61
- if len(hooks)!=0:
94
+ async def run_hooks(self, hook_type: HookType):
95
+ hooks = list(filter(lambda hook: hook.type == hook_type, self._hooks))
96
+ if len(hooks) != 0:
62
97
  logger.debug(f"Launching {hook_type} hooks")
63
98
  for h in hooks:
64
- await run_func(h.func,*h.args,**h.kwargs)
99
+ await run_func(h.func, *h.args, **h.kwargs)
65
100
 
66
101
  #####################################################
67
102
  # timers
@@ -77,7 +112,7 @@ class AsyncEngine(core.EngineMixin):
77
112
  except CallbackError as e:
78
113
  logger.error(e.description)
79
114
  if t.counter != -1:
80
- t.counter-= 1
115
+ t.counter -= 1
81
116
  if t.counter == 0:
82
117
  expire_list.append(t)
83
118
  t.deadline = now + t.period
@@ -88,20 +123,20 @@ class AsyncEngine(core.EngineMixin):
88
123
  #####################################################
89
124
  # msg send / receive
90
125
  #####################################################
91
- def queue_msg(self, msg):
126
+ def queue_msg(self, msg: bytes):
92
127
  """queue a message"""
93
128
  self.__txFifo.put_nowait(msg)
94
129
 
95
- def send_msg(self, msg):
130
+ def send_msg(self, msg: bytes):
96
131
  """Send an encoded message to the bus, use queue_msg instead"""
97
132
  self.network.send(msg)
98
133
 
99
- async def receive_msg(self):
134
+ async def receive_msg(self) -> Optional['Message']:
100
135
  """return new received message or None"""
101
136
  data = await self.network.get_data()
102
137
  if data:
103
138
  try:
104
- msg = self.msg_factory.decode_msg(data,self.msg_filter)
139
+ msg = self.msg_factory.decode_msg(data, self.msg_filter)
105
140
  except MessageParserError as e:
106
141
  logger.warning(e)
107
142
  msg = None
@@ -113,27 +148,31 @@ class AsyncEngine(core.EngineMixin):
113
148
  msg = await self.receive_msg()
114
149
  if msg:
115
150
  for func in self.subscribers:
116
- await run_func(func,msg)
151
+ await run_func(func, msg)
117
152
  self.process_attributes_change()
118
153
 
119
- def handle_request(self, msg):
154
+ def handle_request(self, msg: 'Message'):
120
155
  """Filter msg for devices according default xAAL API then process the
121
156
  request for each targets identied in the engine
122
157
  """
123
158
  if not msg.is_request():
124
159
  return
160
+
125
161
  targets = core.filter_msg_for_devices(msg, self.devices)
126
162
  for target in targets:
127
- if msg.action == 'is_alive':
163
+ if msg.is_request_isalive():
128
164
  self.send_alive(target)
129
165
  else:
130
166
  self.new_task(self.handle_action_request(msg, target))
131
167
 
132
- async def handle_action_request(self, msg, target):
168
+ async def handle_action_request(self, msg: 'Message', target: 'Device'):
169
+ if msg.action is None:
170
+ return # should not happen, but pyright need this check
171
+
133
172
  try:
134
173
  result = await run_action(msg, target)
135
- if result != None:
136
- self.send_reply(dev=target,targets=[msg.source],action=msg.action,body=result)
174
+ if result is not None and type(result) is dict:
175
+ self.send_reply(dev=target, targets=[msg.source], action=msg.action, body=result)
137
176
  except CallbackError as e:
138
177
  self.send_error(target, e.code, e.description)
139
178
  except XAALError as e:
@@ -142,25 +181,25 @@ class AsyncEngine(core.EngineMixin):
142
181
  #####################################################
143
182
  # Asyncio loop & Tasks
144
183
  #####################################################
145
- def get_loop(self):
146
- if self._loop == None:
147
- logger.debug('New event loop')
184
+ def get_loop(self) -> asyncio.AbstractEventLoop:
185
+ if self._loop is None:
186
+ logger.debug("New event loop")
148
187
  self._loop = asyncio.get_event_loop()
149
188
  return self._loop
150
189
 
151
- def new_task(self,coro,name=None):
190
+ def new_task(self, coro: Any, name: Optional[str] = None) -> asyncio.Task:
152
191
  # we maintain a task list, to be able to stop/start the engine
153
192
  # on demand. needed by HASS
154
- task = self.get_loop().create_task(coro,name=name)
193
+ task = self.get_loop().create_task(coro, name=name)
155
194
  self._tasks.append(task)
156
195
  task.add_done_callback(self.task_callback)
157
196
  return task
158
197
 
159
- def task_callback(self, task):
198
+ def task_callback(self, task: asyncio.Task):
160
199
  # called when a task ended
161
200
  self._tasks.remove(task)
162
201
 
163
- def all_tasks(self):
202
+ def all_tasks(self) -> typing.List[asyncio.Task]:
164
203
  return self._tasks
165
204
 
166
205
  async def boot_task(self):
@@ -194,106 +233,105 @@ class AsyncEngine(core.EngineMixin):
194
233
  async def watchdog_task(self):
195
234
  await self.watchdog_event.wait()
196
235
  await self.stop()
197
- logger.info('Exit')
198
-
236
+ logger.info("Exit")
237
+
199
238
  #####################################################
200
239
  # start / stop / shutdown
201
240
  #####################################################
202
- def is_running(self):
241
+ def is_running(self) -> bool:
203
242
  return self.running_event.is_set()
204
243
 
205
244
  def start(self):
206
245
  if self.is_running():
207
- logger.warning('Engine already started')
246
+ logger.warning("Engine already started")
208
247
  return
209
248
  self.started_event.set()
210
- self.new_task(self.boot_task(),name='Boot')
211
- self.new_task(self.receive_task(),name='RecvQ')
212
- self.new_task(self.send_task(),name='SendQ')
213
- self.new_task(self.timer_task(),name='Timers')
214
- self.new_task(console(locals()),name='Console')
249
+ self.new_task(self.boot_task(), name='Boot')
250
+ self.new_task(self.receive_task(), name='RecvQ')
251
+ self.new_task(self.send_task(), name='SendQ')
252
+ self.new_task(self.timer_task(), name='Timers')
253
+ self.new_task(console(locals()), name='Console')
215
254
 
216
255
  def setup_alives_timer(self):
217
256
  # needed on stop-start sequence
218
257
  if self.process_alives in [t.func for t in self.timers]:
219
258
  return
220
259
  # process alives every 10 seconds
221
- self.add_timer(self.process_alives,10)
260
+ self.add_timer(self.process_alives, 10)
222
261
 
223
- async def stop(self):
224
- logger.info('Stopping engine')
262
+ async def stop(self): # pyright: ignore
263
+ logger.info("Stopping engine")
225
264
  await self.run_hooks(HookType.stop)
226
265
  self.running_event.clear()
227
266
  self.started_event.clear()
228
267
  # cancel all tasks
229
268
  for task in self.all_tasks():
230
- if task!=self._watchdog_task:
269
+ if task != self._watchdog_task:
231
270
  task.cancel()
232
271
  await asyncio.sleep(0.1)
233
272
 
234
- def sigkill_handler(self,signal,frame):
235
- print("", end = "\r") #remove the uggly ^C
273
+ def sigkill_handler(self, signal, frame):
274
+ print("", end="\r") # remove the uggly ^C
236
275
  if not self.is_running():
237
- logger.warning('Engine already stopped')
276
+ logger.warning("Engine already stopped")
238
277
  self._kill_counter = 1
239
- self._kill_counter +=1
278
+ self._kill_counter += 1
240
279
  self.shutdown()
241
280
  if self._kill_counter > 1:
242
- logger.warning('Force quit')
281
+ logger.warning("Force quit")
243
282
  sys.exit(-1)
244
283
  else:
245
- logger.warning('Kill requested')
284
+ logger.warning("Kill requested")
246
285
 
247
286
  def shutdown(self):
248
287
  self.watchdog_event.set()
249
-
288
+
250
289
  def run(self):
251
290
  if not self.started_event.is_set():
252
291
  self.start()
253
- if self._watchdog_task == None:
292
+ if self._watchdog_task is None:
254
293
  # start the watchdog task
255
- self._watchdog_task = self.new_task(self.watchdog_task(),name='Watchdog task')
294
+ self._watchdog_task = self.new_task(self.watchdog_task(), name="Watchdog task")
256
295
  self.get_loop().run_until_complete(self._watchdog_task)
257
296
  else:
258
- logger.warning('Engine already running')
297
+ logger.warning("Engine already running")
259
298
 
260
299
  #####################################################
261
300
  # Debugging tools
262
301
  #####################################################
263
302
  def dump_timers(self):
264
- headers = ['Func','Period','Counter','Deadline']
303
+ headers = ['Func', 'Period', 'Counter', 'Deadline']
265
304
  rows = []
266
305
  now = time.time()
267
306
  for t in self.timers:
268
- remain = round(t.deadline-now,1)
269
- rows.append([str(t.func),t.period,t.counter,remain])
270
- print('= Timers')
271
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
272
-
307
+ remain = round(t.deadline - now, 1)
308
+ rows.append([str(t.func), t.period, t.counter, remain])
309
+ print("= Timers")
310
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
273
311
 
274
312
  def dump_tasks(self):
275
- headers = ["Name","Coro","Loop ID"]
313
+ headers = ['Name', 'Coro', 'Loop ID']
276
314
  rows = []
277
315
  for t in self.all_tasks():
278
- rows.append([t.get_name(),str(t.get_coro()),id(t.get_loop())])
279
- print('= Tasks')
280
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
316
+ rows.append([t.get_name(), str(t.get_coro()), id(t.get_loop())])
317
+ print("= Tasks")
318
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
281
319
 
282
320
  def dump_devices(self):
283
- headers = ["addr","dev_type","info"]
321
+ headers = ['addr', 'dev_type', 'info']
284
322
  rows = []
285
323
  for d in self.devices:
286
- rows.append([d.address,d.dev_type,d.info])
287
- print('= Devices')
288
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
324
+ rows.append([d.address, d.dev_type, d.info])
325
+ print("= Devices")
326
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
289
327
 
290
328
  def dump_hooks(self):
291
- headers = ["Type","Hook"]
329
+ headers = ['Type', 'Hook']
292
330
  rows = []
293
331
  for h in self._hooks:
294
- rows.append([h.type,str(h.func)])
295
- print('= Hooks')
296
- print(tabulate(rows,headers=headers,tablefmt="fancy_grid"))
332
+ rows.append([h.type, str(h.func)])
333
+ print("= Hooks")
334
+ print(tabulate(rows, headers=headers, tablefmt='fancy_grid'))
297
335
 
298
336
  def dump(self):
299
337
  self.dump_devices()
@@ -301,8 +339,8 @@ class AsyncEngine(core.EngineMixin):
301
339
  self.dump_timers()
302
340
  self.dump_hooks()
303
341
 
304
- def get_device(self,uuid):
305
- uuid = tools.get_uuid(uuid)
342
+ def get_device(self, uuid: UUID) -> Optional['Device']:
343
+ # TODO:Check if this method is usefull
306
344
  for dev in self.devices:
307
345
  if dev.address == uuid:
308
346
  return dev
@@ -312,26 +350,26 @@ class AsyncEngine(core.EngineMixin):
312
350
  #####################################################
313
351
  # Utilities functions
314
352
  #####################################################
315
- async def run_func(func,*args,**kwargs):
316
- """run a function or a coroutine function """
353
+ async def run_func(func, *args, **kwargs):
354
+ """run a function or a coroutine function"""
317
355
  if asyncio.iscoroutinefunction(func):
318
- return await func(*args,**kwargs)
356
+ return await func(*args, **kwargs)
319
357
  else:
320
- return func(*args,**kwargs)
358
+ return func(*args, **kwargs)
321
359
 
322
360
 
323
- async def run_action(msg,device):
324
- """
361
+ async def run_action(msg: 'Message', device: 'Device'):
362
+ """
325
363
  Extract an action & launch it
326
364
  Return:
327
365
  - action result
328
366
  - None if no result
329
367
 
330
- Notes:
368
+ Notes:
331
369
  - If an exception raised, it's logged, and raise an XAALError.
332
370
  - Same API as legacy Engine, but accept coroutine functions
333
371
  """
334
- method,params = core.search_action(msg,device)
372
+ method, params = core.search_action(msg, device)
335
373
  result = None
336
374
  try:
337
375
  if asyncio.iscoroutinefunction(method):
@@ -340,49 +378,35 @@ async def run_action(msg,device):
340
378
  result = method(**params)
341
379
  except Exception as e:
342
380
  logger.error(e)
343
- raise XAALError("Error in method:%s params:%s" % (msg.action,params))
381
+ raise XAALError("Error in method:%s params:%s" % (msg.action, params))
344
382
  return result
345
383
 
346
384
 
347
- #####################################################
348
- # Hooks
349
- #####################################################
350
- class HookType(Enum):
351
- start = 0
352
- stop = 1
353
-
354
- class Hook(object):
355
- __slots__ = ['type','func','args','kwargs']
356
- def __init__(self,type_,func,*args,**kwargs):
357
- self.type = type_
358
- self.func = func
359
- self.args = args
360
- self.kwargs = kwargs
361
-
362
-
363
385
  #####################################################
364
386
  # Debugging console
365
387
  #####################################################
366
- async def console(locals=locals(),port=None):
388
+ async def console(locals=locals(), port: Optional[int] = None):
367
389
  """launch a console to enable remote engine inspection"""
368
- if port == None:
390
+ if port is None:
369
391
  # let's find a free port if not specified
370
392
  def find_free_port():
371
393
  import socketserver
372
- with socketserver.TCPServer(("localhost", 0), None) as s:
394
+ with socketserver.TCPServer(('localhost', 0), None) as s: # pyright: ignore pyright reject the None here
373
395
  return s.server_address[1]
396
+
374
397
  port = find_free_port()
375
-
376
- logger.debug(f'starting debug console on port {port}')
377
- sys.ps1 = '[xAAL] >>> '
378
- banner = '=' * 78 +"\nxAAL remote console\n" + '=' *78
379
- locals.update({'pprint':pprint})
398
+
399
+ logger.debug(f"starting debug console on port {port}")
400
+ sys.ps1 = "[xAAL] >>> "
401
+ banner = "=" * 78 + "\nxAAL remote console\n" + "=" * 78
402
+ locals.update({'pprint': pprint})
380
403
 
381
404
  def factory(streams):
382
405
  return aioconsole.AsynchronousConsole(locals=locals, streams=streams)
406
+
383
407
  # start the console
384
408
  try:
385
409
  # debian with ipv6 disabled still state that localhost is ::1, which broke aioconsole
386
- await aioconsole.start_interactive_server(host='127.0.0.1', port=port,factory=factory,banner=banner)
410
+ await aioconsole.start_interactive_server(host="127.0.0.1", port=port, factory=factory, banner=banner) # pyright: ignore
387
411
  except OSError:
388
- logger.warning('Unable to run console')
412
+ logger.warning("Unable to run console")
@@ -4,8 +4,8 @@ import asyncio
4
4
  import logging
5
5
 
6
6
  @decorator
7
- def spawn(func,*args,**kwargs):
8
- return asyncio.get_event_loop().run_in_executor(None,func,*args,**kwargs)
7
+ def spawn(func, *args, **kwargs):
8
+ return asyncio.get_event_loop().run_in_executor(None, func, *args, **kwargs)
9
9
 
10
10
 
11
11
  def static_vars(**kwargs_):
@@ -16,20 +16,20 @@ def static_vars(**kwargs_):
16
16
  return decorate
17
17
 
18
18
 
19
- def run_async_package(pkg_name,pkg_setup,console_log = True,file_log=False):
19
+ def run_async_package(pkg_name, pkg_setup, console_log=True, file_log=False):
20
20
  if console_log:
21
21
  set_console_title(pkg_name)
22
22
  setup_console_logger()
23
23
  if file_log:
24
24
  setup_file_logger(pkg_name)
25
-
26
- from .aioengine import AsyncEngine
25
+
26
+ from .aioengine import AsyncEngine
27
27
  eng = AsyncEngine()
28
28
  eng.start()
29
29
  logger = logging.getLogger(pkg_name)
30
- logger.info('starting xaal package: %s'% pkg_name )
30
+ logger.info("starting xaal package: %s"% pkg_name )
31
31
  result = pkg_setup(eng)
32
- if result != True:
32
+ if result is not True:
33
33
  logger.critical("something goes wrong with package: %s" % pkg_name)
34
34
  try:
35
35
  eng.run()