Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions custom_components/pyscript/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,9 @@ def import_recurse(ctx_name, visited, ctx2imports):
mtime=src_info.mtime,
)
reload = src_info.global_ctx_name in ctx_delete
await GlobalContextMgr.load_file(
global_ctx, src_info.file_path, source=src_info.source, reload=reload
)
try:
await GlobalContextMgr.load_file(
global_ctx, src_info.file_path, source=src_info.source, reload=reload
)
except Exception:
_LOGGER.error("Failed to load %s", src_info.file_path)
450 changes: 224 additions & 226 deletions custom_components/pyscript/eval.py

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions custom_components/pyscript/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,10 @@ async def run_coro(cls, coro, ast_ctx=None):
if task in cls.task2cb:
for callback, info in cls.task2cb[task]["cb"].items():
ast_ctx, args, kwargs = info
await ast_ctx.call_func(callback, None, *args, **kwargs)
if ast_ctx.get_exception_obj():
ast_ctx.get_logger().error(ast_ctx.get_exception_long())
try:
await ast_ctx.call_func(callback, None, *args, **kwargs)
except Exception as e:
ast_ctx.log_exception(e)
break
if task in cls.unique_task2name:
for name in cls.unique_task2name[task]:
Expand Down
45 changes: 19 additions & 26 deletions custom_components/pyscript/global_ctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,7 @@ def get_trig_info(self, name: str, trig_args: dict[str, Any]) -> TrigInfo:
"""Return a new trigger info instance with the given args."""
return TrigInfo(name, trig_args, self)

async def module_import(
self, module_name: str, import_level: int
) -> tuple[ModuleType | None, AstEval | None]:
async def module_import(self, module_name: str, import_level: int) -> ModuleType | None:
"""Import a pyscript module from the pyscript/modules or apps folder."""

pyscript_dir = Function.hass.config.path(FOLDER)
Expand Down Expand Up @@ -176,14 +174,14 @@ def find_first_file(file_paths: list[list[str]]) -> list[str] | None:
mod_ctx = self.manager.get(ctx_name)
if mod_ctx and mod_ctx.module:
self.imports.add(mod_ctx.get_name())
return mod_ctx.module, None
return mod_ctx.module

#
# not loaded already, so try to find and import it
#
file_info = await Function.hass.async_add_executor_job(find_first_file, file_paths)
if not file_info:
return None, None
return None

[ctx_name, file_path, rel_import_path] = file_info

Expand All @@ -192,18 +190,20 @@ def find_first_file(file_paths: list[list[str]]) -> list[str] | None:
ctx_name, global_sym_table=mod.__dict__, manager=self.manager, rel_import_path=rel_import_path
)
global_ctx.set_auto_start(self.auto_start)
_, error_ctx = await self.manager.load_file(global_ctx, file_path)
if error_ctx:
try:
await self.manager.load_file(global_ctx, file_path)
except Exception:
_LOGGER.error(
"module_import: failed to load module %s, ctx = %s, path = %s",
module_name,
ctx_name,
file_path,
)
return None, error_ctx
global_ctx.stop()
raise
global_ctx.module = mod
self.imports.add(ctx_name)
return mod, None
return mod


class GlobalContextMgr:
Expand Down Expand Up @@ -301,8 +301,8 @@ def new_name(cls, root: str) -> str:
@classmethod
async def load_file(
cls, global_ctx: GlobalContext, file_path: str, source: str | None = None, reload: bool = False
) -> tuple[bool, AstEval | None]:
"""Load, parse and run the given script file; returns error ast_ctx on error, or None if ok."""
) -> None:
"""Load, parse and run the given script file."""

mtime = None
if source is None:
Expand All @@ -319,7 +319,7 @@ def read_file(path: str) -> tuple[str | None, float]:
source, mtime = await Function.hass.async_add_executor_job(read_file, file_path)

if source is None:
return False, None
return

ctx_curr = cls.get(global_ctx.get_name())
if ctx_curr:
Expand All @@ -332,24 +332,17 @@ def read_file(path: str) -> tuple[str | None, float]:
#
ast_ctx = AstEval(global_ctx.get_name(), global_ctx)
Function.install_ast_funcs(ast_ctx)

if not ast_ctx.parse(source, filename=file_path):
exc = ast_ctx.get_exception_long()
ast_ctx.get_logger().error(exc)
global_ctx.stop()
return False, ast_ctx
await ast_ctx.eval()
exc = ast_ctx.get_exception_long()
if exc is not None:
ast_ctx.get_logger().error(exc)
global_ctx.stop()
return False, ast_ctx
global_ctx.source = source
global_ctx.file_path = file_path
if mtime is not None:
global_ctx.mtime = mtime
try:
ast_ctx.parse(source, filename=file_path)
await ast_ctx.eval()
except Exception as e:
global_ctx.stop()
ast_ctx.log_exception(e)
raise
cls.set(global_ctx.get_name(), global_ctx)

_LOGGER.info("%s %s", "Reloaded" if reload else "Loaded", file_path)

return True, None
25 changes: 11 additions & 14 deletions custom_components/pyscript/jupyter_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import uuid

from .const import LOGGER_PATH
from .eval import EvalExceptionFormatter
from .function import Function
from .global_ctx import GlobalContextMgr
from .state import State
Expand Down Expand Up @@ -355,16 +356,14 @@ async def shell_handler(self, shell_socket, wire_msg):
code = "print([])"

self.global_ctx.set_auto_start(False)
self.ast_ctx.parse(code)
exc = self.ast_ctx.get_exception_obj()
if exc is None:
try:
self.ast_ctx.parse(code)
result = await self.ast_ctx.eval()
exc = self.ast_ctx.get_exception_obj()
await Function.waiter_sync()
self.global_ctx.set_auto_start(True)
self.global_ctx.start()
if exc:
traceback_mesg = self.ast_ctx.get_exception_long().split("\n")
await Function.waiter_sync()
self.global_ctx.set_auto_start(True)
self.global_ctx.start()
except Exception as exc:
traceback_mesg = EvalExceptionFormatter(exc).format()

metadata = {
"dependencies_met": True,
Expand Down Expand Up @@ -503,17 +502,15 @@ async def shell_handler(self, shell_socket, wire_msg):

elif msg["header"]["msg_type"] == "is_complete_request":
code = msg["content"]["code"]
self.ast_ctx.parse(code)
exc = self.ast_ctx.get_exception_obj()

# determine indent of last line
indent = 0
i = code.rfind("\n")
if i >= 0:
while i + 1 < len(code) and code[i + 1] == " ":
i += 1
indent += 1
if exc is None:
try:
self.ast_ctx.parse(code)
if indent == 0:
content = {
# One of 'complete', 'incomplete', 'invalid', 'unknown'
Expand All @@ -529,7 +526,7 @@ async def shell_handler(self, shell_socket, wire_msg):
"status": "incomplete",
"indent": " " * indent,
}
else:
except Exception as exc:
#
# if the syntax error is right at the end, then we label it incomplete,
# otherwise it's invalid
Expand Down
Loading
Loading