Skip to content

Commit a1ad5a4

Browse files
committed
refactoring State, Event, Handler, TrigTime and GlobalContextMgr
1 parent b111189 commit a1ad5a4

File tree

11 files changed

+317
-426
lines changed

11 files changed

+317
-426
lines changed

custom_components/pyscript/__init__.py

Lines changed: 26 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@
4141

4242
async def async_setup(hass, config):
4343
"""Initialize the pyscript component."""
44-
handler_func = Handler(hass)
45-
event_func = Event(hass)
46-
trig_time_func = TrigTime(hass, handler_func)
47-
state_func = State(hass, handler_func)
48-
state_func.register_functions()
49-
global_ctx_mgr = GlobalContextMgr(handler_func)
44+
Handler.init(hass)
45+
Event.init(hass)
46+
TrigTime.init(hass)
47+
State.init(hass)
48+
State.register_functions()
49+
GlobalContextMgr.init()
5050

5151
path = hass.config.path(FOLDER)
5252

@@ -60,14 +60,7 @@ def check_isdir(path):
6060
hass.data.setdefault(DOMAIN, {})
6161
hass.data[DOMAIN]["allow_all_imports"] = config[DOMAIN].get(CONF_ALLOW_ALL_IMPORTS)
6262

63-
await compile_scripts( # pylint: disable=unused-variable
64-
hass,
65-
event_func=event_func,
66-
state_func=state_func,
67-
handler_func=handler_func,
68-
trig_time_func=trig_time_func,
69-
global_ctx_mgr=global_ctx_mgr,
70-
)
63+
await compile_scripts(hass)
7164

7265
_LOGGER.debug("adding reload handler")
7366

@@ -78,25 +71,18 @@ async def reload_scripts_handler(call):
7871
)
7972

8073
ctx_delete = {}
81-
for global_ctx_name, global_ctx in global_ctx_mgr.items():
74+
for global_ctx_name, global_ctx in GlobalContextMgr.items():
8275
if not global_ctx_name.startswith("file."):
8376
continue
8477
await global_ctx.stop()
8578
global_ctx.set_auto_start(False)
8679
ctx_delete[global_ctx_name] = global_ctx
8780
for global_ctx_name, global_ctx in ctx_delete.items():
88-
await global_ctx_mgr.delete(global_ctx_name)
89-
90-
await compile_scripts(
91-
hass,
92-
event_func=event_func,
93-
state_func=state_func,
94-
handler_func=handler_func,
95-
trig_time_func=trig_time_func,
96-
global_ctx_mgr=global_ctx_mgr,
97-
)
81+
await GlobalContextMgr.delete(global_ctx_name)
9882

99-
for global_ctx_name, global_ctx in global_ctx_mgr.items():
83+
await compile_scripts(hass)
84+
85+
for global_ctx_name, global_ctx in GlobalContextMgr.items():
10086
if not global_ctx_name.startswith("file."):
10187
continue
10288
await global_ctx.start()
@@ -107,29 +93,15 @@ async def jupyter_kernel_start(call):
10793
"""Handle Jupyter kernel start call."""
10894
_LOGGER.debug("service call to jupyter_kernel_start: %s", call.data)
10995

110-
global_ctx_name = global_ctx_mgr.new_name("jupyter_")
111-
global_ctx = GlobalContext(
112-
global_ctx_name,
113-
hass,
114-
global_sym_table={},
115-
state_func=state_func,
116-
event_func=event_func,
117-
handler_func=handler_func,
118-
trig_time_func=trig_time_func,
119-
)
96+
global_ctx_name = GlobalContextMgr.new_name("jupyter_")
97+
global_ctx = GlobalContext(global_ctx_name, hass, global_sym_table={})
12098
global_ctx.set_auto_start(True)
12199

122-
global_ctx_mgr.set(global_ctx_name, global_ctx)
100+
GlobalContextMgr.set(global_ctx_name, global_ctx)
123101

124-
ast_ctx = AstEval(
125-
global_ctx_name,
126-
global_ctx=global_ctx,
127-
state_func=state_func,
128-
event_func=event_func,
129-
handler_func=handler_func,
130-
)
131-
handler_func.install_ast_funcs(ast_ctx)
132-
kernel = Kernel(call.data, ast_ctx, global_ctx_name, global_ctx_mgr)
102+
ast_ctx = AstEval(global_ctx_name, global_ctx)
103+
Handler.install_ast_funcs(ast_ctx)
104+
kernel = Kernel(call.data, ast_ctx, global_ctx_name)
133105
await kernel.session_start()
134106
hass.states.async_set(call.data["state_var"], json.dumps(kernel.get_ports()))
135107

@@ -158,20 +130,20 @@ async def state_changed(event):
158130
"value": new_val,
159131
"old_value": old_val,
160132
}
161-
await state_func.update(new_vars, func_args)
133+
await State.update(new_vars, func_args)
162134

163135
async def start_triggers(event):
164136
_LOGGER.debug("adding state changed listener and starting triggers")
165137
hass.bus.async_listen(EVENT_STATE_CHANGED, state_changed)
166-
for global_ctx_name, global_ctx in global_ctx_mgr.items():
138+
for global_ctx_name, global_ctx in GlobalContextMgr.items():
167139
if not global_ctx_name.startswith("file."):
168140
continue
169141
await global_ctx.start()
170142
global_ctx.set_auto_start(True)
171143

172144
async def stop_triggers(event):
173145
_LOGGER.debug("stopping triggers")
174-
for global_ctx_name, global_ctx in global_ctx_mgr.items():
146+
for global_ctx_name, global_ctx in GlobalContextMgr.items():
175147
if not global_ctx_name.startswith("file."):
176148
continue
177149
await global_ctx.stop()
@@ -183,14 +155,7 @@ async def stop_triggers(event):
183155

184156

185157
@bind_hass
186-
async def compile_scripts(
187-
hass,
188-
event_func=None,
189-
state_func=None,
190-
handler_func=None,
191-
trig_time_func=None,
192-
global_ctx_mgr=None,
193-
):
158+
async def compile_scripts(hass):
194159
"""Compile all python scripts in FOLDER."""
195160

196161
path = hass.config.path(FOLDER)
@@ -213,25 +178,11 @@ def read_file(path):
213178
source = await hass.async_add_executor_job(read_file, file)
214179

215180
global_ctx_name = f"file.{name}"
216-
global_ctx = GlobalContext(
217-
global_ctx_name,
218-
hass,
219-
global_sym_table={},
220-
state_func=state_func,
221-
event_func=event_func,
222-
handler_func=handler_func,
223-
trig_time_func=trig_time_func,
224-
)
181+
global_ctx = GlobalContext(global_ctx_name, hass, global_sym_table={})
225182
global_ctx.set_auto_start(False)
226183

227-
ast_ctx = AstEval(
228-
global_ctx_name,
229-
global_ctx=global_ctx,
230-
state_func=state_func,
231-
event_func=event_func,
232-
handler_func=handler_func,
233-
)
234-
handler_func.install_ast_funcs(ast_ctx)
184+
ast_ctx = AstEval(global_ctx_name, global_ctx)
185+
Handler.install_ast_funcs(ast_ctx)
235186

236187
if not ast_ctx.parse(source, filename=file):
237188
exc = ast_ctx.get_exception_long()
@@ -242,4 +193,4 @@ def read_file(path):
242193
if exc is not None:
243194
ast_ctx.get_logger().error(exc)
244195
continue
245-
global_ctx_mgr.set(global_ctx_name, global_ctx)
196+
GlobalContextMgr.set(global_ctx_name, global_ctx)

custom_components/pyscript/eval.py

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import sys
1111

1212
from .const import ALLOWED_IMPORTS, DOMAIN, LOGGER_PATH
13+
from .handler import Handler
14+
from .state import State
1315

1416
_LOGGER = logging.getLogger(LOGGER_PATH + ".eval")
1517

@@ -30,13 +32,7 @@ def ast_eval_exec_factory(ast_ctx, str_type):
3032
"""Generate a function that executes eval() or exec() with given ast_ctx."""
3133

3234
async def eval_func(arg_str, eval_globals=None, eval_locals=None):
33-
eval_ast = AstEval(
34-
ast_ctx.name,
35-
global_ctx=ast_ctx.global_ctx,
36-
state_func=ast_ctx.state,
37-
event_func=ast_ctx.event,
38-
handler_func=ast_ctx.handler,
39-
)
35+
eval_ast = AstEval(ast_ctx.name, ast_ctx.global_ctx)
4036
eval_ast.parse(arg_str, f"{str_type}()")
4137
if eval_ast.exception_obj:
4238
raise eval_ast.exception_obj # pylint: disable=raising-bad-type
@@ -334,15 +330,7 @@ async def call(self, ast_ctx, args=None, kwargs=None):
334330
class AstEval:
335331
"""Python interpreter AST object evaluator."""
336332

337-
def __init__(
338-
self,
339-
name,
340-
global_ctx=None,
341-
state_func=None,
342-
event_func=None,
343-
handler_func=None,
344-
logger_name=None,
345-
):
333+
def __init__(self, name, global_ctx, logger_name=None):
346334
"""Initialize an interpreter execution context."""
347335
self.name = name
348336
self.str = None
@@ -359,9 +347,6 @@ def __init__(
359347
self.exception = None
360348
self.exception_obj = None
361349
self.exception_long = None
362-
self.state = state_func
363-
self.handler = handler_func
364-
self.event = event_func
365350
self.lineno = 1
366351
self.col_offset = 0
367352
self.logger_handlers = set()
@@ -648,7 +633,7 @@ async def recurse_assign(self, lhs, val):
648633
f"unknown lhs type {lhs} (got {var_name}) in assign"
649634
)
650635
if var_name.find(".") >= 0:
651-
self.state.set(var_name, val)
636+
State.set(var_name, val)
652637
return
653638
if self.curr_func and var_name in self.curr_func.global_names:
654639
self.global_sym_table[var_name] = val
@@ -694,8 +679,8 @@ async def ast_augassign(self, arg):
694679
break
695680
else:
696681
raise TypeError(f"can't find nonlocal '{var_name}' for assignment")
697-
elif self.state.exist(var_name):
698-
self.state.set(var_name, val)
682+
elif State.exist(var_name):
683+
State.set(var_name, val)
699684
else:
700685
self.sym_table[var_name] = val
701686

@@ -805,19 +790,15 @@ async def ast_name(self, arg):
805790
and arg.id[0] != "_"
806791
):
807792
return getattr(builtins, arg.id)
808-
if self.handler.get(arg.id):
809-
return self.handler.get(arg.id)
793+
if Handler.get(arg.id):
794+
return Handler.get(arg.id)
810795
num_dots = arg.id.count(".")
811796
#
812797
# any single-dot name could be a state variable
813798
# a two-dot name for state.attr needs to exist
814799
#
815-
if num_dots == 2:
816-
_LOGGER.debug(
817-
"ast_name: arg = {arg.id}, exist = {self.state.exist(arg.id)}"
818-
)
819-
if num_dots == 1 or (num_dots == 2 and self.state.exist(arg.id)):
820-
return self.state.get(arg.id)
800+
if num_dots == 1 or (num_dots == 2 and State.exist(arg.id)):
801+
return State.get(arg.id)
821802
#
822803
# Couldn't find it, so return just the name wrapped in EvalName to
823804
# distinguish from a string variable value. This is to support

custom_components/pyscript/event.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,24 @@
88

99

1010
class Event:
11-
"""Defined event functions."""
11+
"""Define event functions."""
1212

13-
def __init__(self, hass):
13+
def __init__():
14+
"""Warn on Event instantiation."""
15+
_LOGGER.error("Event class is not meant to be instantiated")
16+
17+
def init(hass):
1418
"""Initialize Event."""
19+
Event.hass = hass
1520

16-
self.hass = hass
21+
Event.hass = hass
1722
#
1823
# notify message queues by event type
1924
#
20-
self.notify = {}
21-
self.notify_remove = {}
25+
Event.notify = {}
26+
Event.notify_remove = {}
2227

23-
async def event_listener(self, event):
28+
async def event_listener(event):
2429
"""Listen callback for given event which updates any notifications."""
2530

2631
_LOGGER.debug("event_listener(%s)", event)
@@ -29,35 +34,35 @@ async def event_listener(self, event):
2934
"event_type": event.event_type,
3035
}
3136
func_args.update(event.data)
32-
await self.update(event.event_type, func_args)
37+
await Event.update(event.event_type, func_args)
3338

34-
def notify_add(self, event_type, queue):
39+
def notify_add(event_type, queue):
3540
"""Register to notify for events of given type to be sent to queue."""
3641

37-
if event_type not in self.notify:
38-
self.notify[event_type] = set()
42+
if event_type not in Event.notify:
43+
Event.notify[event_type] = set()
3944
_LOGGER.debug("event.notify_add(%s) -> adding event listener", event_type)
40-
self.notify_remove[event_type] = self.hass.bus.async_listen(
41-
event_type, self.event_listener
45+
Event.notify_remove[event_type] = Event.hass.bus.async_listen(
46+
event_type, Event.event_listener
4247
)
43-
self.notify[event_type].add(queue)
48+
Event.notify[event_type].add(queue)
4449

45-
def notify_del(self, event_type, queue):
50+
def notify_del(event_type, queue):
4651
"""Unregister to notify for events of given type for given queue."""
4752

48-
if event_type not in self.notify or queue not in self.notify[event_type]:
53+
if event_type not in Event.notify or queue not in Event.notify[event_type]:
4954
return
50-
self.notify[event_type].discard(queue)
51-
if len(self.notify[event_type]) == 0:
52-
self.notify_remove[event_type]()
55+
Event.notify[event_type].discard(queue)
56+
if len(Event.notify[event_type]) == 0:
57+
Event.notify_remove[event_type]()
5358
_LOGGER.debug("event.notify_del(%s) -> removing event listener", event_type)
54-
del self.notify[event_type]
55-
del self.notify_remove[event_type]
59+
del Event.notify[event_type]
60+
del Event.notify_remove[event_type]
5661

57-
async def update(self, event_type, func_args):
62+
async def update(event_type, func_args):
5863
"""Deliver all notifications for an event of the given type."""
5964

6065
_LOGGER.debug("event.update(%s, %s, %s)", event_type, vars, func_args)
61-
if event_type in self.notify:
62-
for queue in self.notify[event_type]:
66+
if event_type in Event.notify:
67+
for queue in Event.notify[event_type]:
6368
await queue.put(["event", func_args])

0 commit comments

Comments
 (0)