Skip to content

Commit dcd1748

Browse files
committed
implement task.current_task() and task.name2id(); see #130
1 parent bf1da5a commit dcd1748

File tree

4 files changed

+57
-26
lines changed

4 files changed

+57
-26
lines changed

custom_components/pyscript/function.py

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ def init(cls, hass):
8585
"service.call": cls.service_call,
8686
"service.has_service": cls.service_has_service,
8787
"task.executor": cls.task_executor,
88+
"task.cancel": cls.user_task_cancel,
89+
"task.current_task": cls.user_task_current_task,
90+
"task.remove_done_callback": cls.user_task_remove_done_callback,
8891
"task.sleep": cls.async_sleep,
92+
"task.wait": cls.user_task_wait,
8993
}
9094
)
9195
cls.ast_functions.update(
@@ -95,6 +99,7 @@ def init(cls, hass):
9599
"log.info": lambda ast_ctx: ast_ctx.get_logger().info,
96100
"log.warning": lambda ast_ctx: ast_ctx.get_logger().warning,
97101
"print": lambda ast_ctx: ast_ctx.get_logger().debug,
102+
"task.name2id": cls.task_name2id_factory,
98103
"task.unique": cls.task_unique_factory,
99104
}
100105
)
@@ -124,7 +129,7 @@ async def task_reaper(reaper_q):
124129

125130
if not cls.task_reaper:
126131
cls.task_reaper_q = asyncio.Queue(0)
127-
cls.task_reaper = Function.create_task(task_reaper(cls.task_reaper_q))
132+
cls.task_reaper = cls.create_task(task_reaper(cls.task_reaper_q))
128133

129134
#
130135
# start a task which creates tasks to run coros, and then syncs on their completion;
@@ -153,7 +158,7 @@ async def task_waiter(waiter_q):
153158

154159
if not cls.task_waiter:
155160
cls.task_waiter_q = asyncio.Queue(0)
156-
cls.task_waiter = Function.create_task(task_waiter(cls.task_waiter_q))
161+
cls.task_waiter = cls.create_task(task_waiter(cls.task_waiter_q))
157162

158163
@classmethod
159164
def reaper_cancel(cls, task):
@@ -230,12 +235,12 @@ async def task_unique(name, kill_me=False):
230235
# it seems we can't cancel ourselves, so we
231236
# tell the reaper task to cancel us
232237
#
233-
Function.reaper_cancel(curr_task)
238+
cls.reaper_cancel(curr_task)
234239
# wait to be canceled
235240
await asyncio.sleep(100000)
236241
elif task != curr_task and task in cls.our_tasks:
237242
# only cancel tasks if they are ones we started
238-
Function.reaper_cancel(task)
243+
cls.reaper_cancel(task)
239244
if curr_task in cls.our_tasks:
240245
if name in cls.unique_name2task:
241246
task = cls.unique_name2task[name]
@@ -255,6 +260,45 @@ async def task_executor(cls, func, *args, **kwargs):
255260
raise TypeError("function is not callable by task.executor()")
256261
return await cls.hass.async_add_executor_job(functools.partial(func, **kwargs), *args)
257262

263+
@classmethod
264+
def user_task_cancel(cls, task):
265+
"""Implement task.cancel()."""
266+
if not isinstance(task, asyncio.Task):
267+
raise TypeError(f"{task} is not of type asyncio.Task")
268+
cls.reaper_cancel(task)
269+
270+
@classmethod
271+
async def user_task_current_task(cls):
272+
"""Implement task.current_task()."""
273+
return asyncio.current_task()
274+
275+
@classmethod
276+
def task_name2id_factory(cls, ctx):
277+
"""Define and return task.name2id() for this context."""
278+
279+
def user_task_name2id(name=None):
280+
"""Implement task.name2id()."""
281+
prefix = f"{ctx.get_global_ctx_name()}."
282+
if name is None:
283+
ret = {}
284+
for task_name, task_id in cls.unique_name2task.items():
285+
if task_name.startswith(prefix):
286+
ret[task_name[len(prefix) :]] = task_id
287+
return ret
288+
return cls.unique_name2task.get(prefix + name, None)
289+
290+
return user_task_name2id
291+
292+
@classmethod
293+
async def user_task_wait(cls, aws):
294+
"""Implement task.wait()."""
295+
return await asyncio.wait(aws)
296+
297+
@classmethod
298+
def user_task_remove_done_callback(cls, task, callback):
299+
"""Implement task.remove_done_callback()."""
300+
cls.task2cb[task]["cb"].pop(callback, None)
301+
258302
@classmethod
259303
def unique_name_used(cls, ctx, name):
260304
"""Return whether the current unique name is in use."""
@@ -423,8 +467,3 @@ def task_add_done_callback(cls, task, ast_ctx, callback, *args, **kwargs):
423467
if ast_ctx is None:
424468
ast_ctx = cls.task2cb[task]["ctx"]
425469
cls.task2cb[task]["cb"][callback] = [ast_ctx, args, kwargs]
426-
427-
@classmethod
428-
def task_remove_done_callback(cls, task, callback):
429-
"""Remove a done callback to the given task."""
430-
cls.task2cb[task]["cb"].pop(callback, None)

custom_components/pyscript/trigger.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -170,32 +170,15 @@ async def func_call(func, func_name, new_ast_ctx, *args, **kwargs):
170170
}
171171
Function.register_ast(ast_funcs)
172172

173-
def user_task_cancel(task):
174-
"""Implement task.cancel()."""
175-
if not isinstance(task, asyncio.Task):
176-
raise TypeError(f"{task} is not of type asyncio.Task")
177-
Function.reaper_cancel(task)
178-
179-
async def user_task_wait(aws):
180-
"""Implement task.wait()."""
181-
return await asyncio.wait(aws)
182-
183173
async def user_task_add_done_callback(task, callback, *args, **kwargs):
184174
"""Implement task.add_done_callback()."""
185175
ast_ctx = None
186176
if type(callback) is EvalFuncVar:
187177
ast_ctx = callback.get_ast_ctx()
188178
Function.task_add_done_callback(task, ast_ctx, callback, *args, **kwargs)
189179

190-
async def user_task_remove_done_callback(task, callback):
191-
"""Implement task.remove_done_callback()."""
192-
Function.task_remove_done_callback(task, callback)
193-
194180
funcs = {
195-
"task.cancel": user_task_cancel,
196-
"task.wait": user_task_wait,
197181
"task.add_done_callback": user_task_add_done_callback,
198-
"task.remove_done_callback": user_task_remove_done_callback,
199182
}
200183
Function.register(funcs)
201184

docs/reference.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,13 @@ occurs.
957957
simpler alternative to the ``task_id.cancel()`` method, which also requires waiting for the
958958
task to cancel.
959959

960+
``task.current_task()``
961+
Returns the task id of the current task.
962+
963+
``task.name2id(name=None)``
964+
Returns the task id given a name that task passed to ``task.unique``. With no arguments it returns a dict mapping
965+
all names to task ids. The names are specific to the current global context.
966+
960967
``task.wait(task_set)``
961968
Waits until the given set of tasks complete. This function calls ``asyncio.wait``, so it takes the same arguments.
962969
``task_set`` is a set or list of task ids, and it returns two sets of done and pending task ids. An example:

tests/test_unique.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ def funcStartupSync():
8181
# stick around so the task.unique() still applies
8282
#
8383
task.unique("func6")
84+
assert task.current_task() == task.name2id("func6")
85+
assert task.current_task() == task.name2id()["func6"]
8486
task.sleep(10000)
8587
8688
@state_trigger("pyscript.f0var1 == '1'")

0 commit comments

Comments
 (0)