Skip to content

Commit 59a5461

Browse files
committed
"startup" is now a valid @time_trigger time_spec, and @task_unique is
a new decorator with the same arguments as task.unique(). Refactored some time trigger.py code.
1 parent 006cdc3 commit 59a5461

File tree

7 files changed

+318
-176
lines changed

7 files changed

+318
-176
lines changed

custom_components/pyscript/eval.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,16 @@ async def eval_decorators(self, ast_ctx):
217217
for dec in self.func_def.decorator_list:
218218
if isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name):
219219
args = []
220+
kwargs = {}
220221
for arg in dec.args:
221222
args.append(await ast_ctx.aeval(arg))
222-
self.decorators.append([dec.func.id, args])
223+
for keyword in dec.keywords:
224+
kwargs[keyword.arg] = await ast_ctx.aeval(keyword.value)
225+
if len(kwargs) == 0:
226+
kwargs = None
227+
self.decorators.append([dec.func.id, args, kwargs])
223228
elif isinstance(dec, ast.Name):
224-
self.decorators.append([dec.id, None])
229+
self.decorators.append([dec.id, None, None])
225230
else:
226231
_LOGGER.error(
227232
"function %s has unexpected decorator type %s", self.name, dec

custom_components/pyscript/global_ctx.py

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,14 @@ async def trigger_init(self, func):
5858
"event_trigger",
5959
"state_active",
6060
"time_active",
61+
"task_unique",
6162
}
6263
decorator_used = set()
6364
for dec in func.get_decorators():
64-
dec_name, dec_args = dec[0], dec[1]
65+
dec_name, dec_args, dec_kwargs = dec[0], dec[1], dec[2]
6566
if dec_name in decorator_used:
6667
self.logger.error(
67-
"%s defined in %s: decorator %s repeated; ignored",
68+
"%s defined in %s: decorator %s repeated; ignoring decorator",
6869
func_name,
6970
self.name,
7071
dec_name,
@@ -75,13 +76,16 @@ async def trigger_init(self, func):
7576
got_reqd_dec = True
7677
if dec_name in trig_decorators:
7778
if dec_name not in trig_args:
78-
trig_args[dec_name] = []
79+
trig_args[dec_name] = {}
80+
trig_args[dec_name]["args"] = []
7981
if dec_args is not None:
80-
trig_args[dec_name] += dec_args
82+
trig_args[dec_name]["args"] += dec_args
83+
if dec_kwargs is not None:
84+
trig_args[dec_name]["kwargs"] = dec_kwargs
8185
elif dec_name == "service":
8286
if dec_args is not None:
8387
self.logger.error(
84-
"%s defined in %s: decorator @service takes no arguments; ignored",
88+
"%s defined in %s: decorator @service takes no arguments; ignoring decorator",
8589
func_name,
8690
self.name,
8791
)
@@ -167,30 +171,71 @@ async def do_service_call(func, ast_ctx, data):
167171
self.services.discard(func_name)
168172

169173
for dec_name in trig_decorators:
170-
if dec_name in trig_args and len(trig_args[dec_name]) == 0:
171-
trig_args[dec_name] = None
174+
if dec_name in trig_args and len(trig_args[dec_name]["args"]) == 0:
175+
trig_args[dec_name]["args"] = None
172176

177+
#
178+
# check that we have the right number of arguments, and that they are
179+
# strings
180+
#
173181
arg_check = {
174-
"state_trigger": {1},
175-
"state_active": {1},
176182
"event_trigger": {1, 2},
183+
"state_active": {1},
184+
"state_trigger": {1},
185+
"task_unique": {1},
186+
"time_active": {"*"},
187+
"time_trigger": {"*"},
177188
}
178189
for dec_name, arg_cnt in arg_check.items():
179-
if dec_name not in trig_args or trig_args[dec_name] is None:
190+
if dec_name not in trig_args or trig_args[dec_name]["args"] is None:
180191
continue
181-
if len(trig_args[dec_name]) not in arg_cnt:
192+
if "*" not in arg_cnt and len(trig_args[dec_name]["args"]) not in arg_cnt:
182193
self.logger.error(
183-
"%s defined in %s: decorator @%s got %d argument%s, expected %s; ignored",
194+
"%s defined in %s: decorator @%s got %d argument%s, expected %s; ignoring decorator",
184195
func_name,
185196
self.name,
186197
dec_name,
187-
len(trig_args[dec_name]),
188-
"s" if len(trig_args[dec_name]) > 1 else "",
198+
len(trig_args[dec_name]["args"]),
199+
"s" if len(trig_args[dec_name]["args"]) > 1 else "",
189200
" or ".join([str(cnt) for cnt in sorted(arg_cnt)]),
190201
)
191202
del trig_args[dec_name]
192-
if arg_cnt == 1:
193-
trig_args[dec_name] = trig_args[dec_name][0]
203+
break
204+
for arg_num, arg in enumerate(trig_args[dec_name]["args"]):
205+
if not isinstance(arg, str):
206+
self.logger.error(
207+
"%s defined in %s: decorator @%s argument %d should be a string; ignoring decorator",
208+
func_name,
209+
self.name,
210+
dec_name,
211+
arg_num + 1
212+
)
213+
del trig_args[dec_name]
214+
break
215+
if arg_cnt == {1}:
216+
trig_args[dec_name]["args"] = trig_args[dec_name]["args"][0]
217+
218+
kwarg_check = {
219+
"task_unique": {"kill_me"},
220+
}
221+
for dec_name in trig_args.keys():
222+
if dec_name not in kwarg_check and "kwargs" in trig_args[dec_name]:
223+
self.logger.error(
224+
"%s defined in %s: decorator @%s doesn't take keyword arguments; ignored",
225+
func_name,
226+
self.name,
227+
dec_name,
228+
)
229+
if dec_name in kwarg_check and "kwargs" in trig_args[dec_name]:
230+
used_kw = set(trig_args[dec_name]["kwargs"].keys())
231+
if not used_kw.issubset(kwarg_check[dec_name]):
232+
self.logger.error(
233+
"%s defined in %s: decorator @%s valid keyword arguments are: %s; others ignored",
234+
func_name,
235+
self.name,
236+
dec_name,
237+
", ".join(sorted(kwarg_check[dec_name])),
238+
)
194239

195240
if not got_reqd_dec and len(trig_args) > 0:
196241
self.logger.error(

custom_components/pyscript/handler.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ async def cancel_self():
9393
self.unique_name2task[name] = task
9494
self.unique_task2name[task] = name
9595

96+
def unique_name_used(self, name):
97+
"""Return whether the current unique name is in use."""
98+
return name in self.unique_name2task
99+
96100
def service_has_service(self, domain, name):
97101
"""Implement service.has_service()."""
98102
return self.hass.services.has_service(domain, name)

0 commit comments

Comments
 (0)