mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(core): Support print RPC call in native Python runner (no-changelog) (#18630)
This commit is contained in:
@@ -4,12 +4,14 @@ BROKER_RUNNER_REGISTERED = "broker:runnerregistered"
|
|||||||
BROKER_TASK_OFFER_ACCEPT = "broker:taskofferaccept"
|
BROKER_TASK_OFFER_ACCEPT = "broker:taskofferaccept"
|
||||||
BROKER_TASK_SETTINGS = "broker:tasksettings"
|
BROKER_TASK_SETTINGS = "broker:tasksettings"
|
||||||
BROKER_TASK_CANCEL = "broker:taskcancel"
|
BROKER_TASK_CANCEL = "broker:taskcancel"
|
||||||
|
BROKER_RPC_RESPONSE = "broker:rpcresponse"
|
||||||
RUNNER_INFO = "runner:info"
|
RUNNER_INFO = "runner:info"
|
||||||
RUNNER_TASK_OFFER = "runner:taskoffer"
|
RUNNER_TASK_OFFER = "runner:taskoffer"
|
||||||
RUNNER_TASK_ACCEPTED = "runner:taskaccepted"
|
RUNNER_TASK_ACCEPTED = "runner:taskaccepted"
|
||||||
RUNNER_TASK_REJECTED = "runner:taskrejected"
|
RUNNER_TASK_REJECTED = "runner:taskrejected"
|
||||||
RUNNER_TASK_DONE = "runner:taskdone"
|
RUNNER_TASK_DONE = "runner:taskdone"
|
||||||
RUNNER_TASK_ERROR = "runner:taskerror"
|
RUNNER_TASK_ERROR = "runner:taskerror"
|
||||||
|
RUNNER_RPC_CALL = "runner:rpc"
|
||||||
|
|
||||||
# Runner
|
# Runner
|
||||||
TASK_TYPE_PYTHON = "python"
|
TASK_TYPE_PYTHON = "python"
|
||||||
@@ -24,6 +26,7 @@ OFFER_VALIDITY_LATENCY_BUFFER = 0.1 # 100ms
|
|||||||
|
|
||||||
# Executor
|
# Executor
|
||||||
EXECUTOR_USER_OUTPUT_KEY = "__n8n_internal_user_output__"
|
EXECUTOR_USER_OUTPUT_KEY = "__n8n_internal_user_output__"
|
||||||
|
EXECUTOR_CIRCULAR_REFERENCE_KEY = "__n8n_internal_circular_ref__"
|
||||||
|
|
||||||
# Broker
|
# Broker
|
||||||
DEFAULT_TASK_BROKER_URI = "http://127.0.0.1:5679"
|
DEFAULT_TASK_BROKER_URI = "http://127.0.0.1:5679"
|
||||||
@@ -41,6 +44,9 @@ ENV_HIDE_TASK_OFFER_LOGS = "N8N_RUNNERS_HIDE_TASK_OFFER_LOGS"
|
|||||||
LOG_FORMAT = "%(asctime)s.%(msecs)03d\t%(levelname)s\t%(message)s"
|
LOG_FORMAT = "%(asctime)s.%(msecs)03d\t%(levelname)s\t%(message)s"
|
||||||
LOG_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
|
LOG_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||||
|
|
||||||
|
# RPC
|
||||||
|
RPC_BROWSER_CONSOLE_LOG_METHOD = "logNodeOutput"
|
||||||
|
|
||||||
# Rejection reasons
|
# Rejection reasons
|
||||||
TASK_REJECTED_REASON_OFFER_EXPIRED = (
|
TASK_REJECTED_REASON_OFFER_EXPIRED = (
|
||||||
"Offer expired - not accepted within validity window"
|
"Offer expired - not accepted within validity window"
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from .constants import LOG_FORMAT, LOG_TIMESTAMP_FORMAT, ENV_HIDE_TASK_OFFER_LOGS
|
from .constants import (
|
||||||
|
LOG_FORMAT,
|
||||||
|
LOG_TIMESTAMP_FORMAT,
|
||||||
|
ENV_HIDE_TASK_OFFER_LOGS,
|
||||||
|
)
|
||||||
|
|
||||||
COLORS = {
|
COLORS = {
|
||||||
"DEBUG": "\033[34m", # blue
|
"DEBUG": "\033[34m", # blue
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from .constants import (
|
|||||||
BROKER_TASK_CANCEL,
|
BROKER_TASK_CANCEL,
|
||||||
BROKER_TASK_OFFER_ACCEPT,
|
BROKER_TASK_OFFER_ACCEPT,
|
||||||
BROKER_TASK_SETTINGS,
|
BROKER_TASK_SETTINGS,
|
||||||
|
BROKER_RPC_RESPONSE,
|
||||||
)
|
)
|
||||||
from .message_types import (
|
from .message_types import (
|
||||||
BrokerMessage,
|
BrokerMessage,
|
||||||
@@ -18,6 +19,7 @@ from .message_types import (
|
|||||||
BrokerTaskOfferAccept,
|
BrokerTaskOfferAccept,
|
||||||
BrokerTaskSettings,
|
BrokerTaskSettings,
|
||||||
BrokerTaskCancel,
|
BrokerTaskCancel,
|
||||||
|
BrokerRpcResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -75,12 +77,24 @@ def _parse_task_cancel(d: dict) -> BrokerTaskCancel:
|
|||||||
return BrokerTaskCancel(task_id=task_id, reason=reason)
|
return BrokerTaskCancel(task_id=task_id, reason=reason)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_rpc_response(d: dict) -> BrokerRpcResponse:
|
||||||
|
try:
|
||||||
|
call_id = d["callId"]
|
||||||
|
task_id = d["taskId"]
|
||||||
|
status = d["status"]
|
||||||
|
except KeyError as e:
|
||||||
|
raise ValueError(f"Missing field in RPC response message: {e}")
|
||||||
|
|
||||||
|
return BrokerRpcResponse(call_id, task_id, status)
|
||||||
|
|
||||||
|
|
||||||
MESSAGE_TYPE_MAP = {
|
MESSAGE_TYPE_MAP = {
|
||||||
BROKER_INFO_REQUEST: lambda _: BrokerInfoRequest(),
|
BROKER_INFO_REQUEST: lambda _: BrokerInfoRequest(),
|
||||||
BROKER_RUNNER_REGISTERED: lambda _: BrokerRunnerRegistered(),
|
BROKER_RUNNER_REGISTERED: lambda _: BrokerRunnerRegistered(),
|
||||||
BROKER_TASK_OFFER_ACCEPT: _parse_task_offer_accept,
|
BROKER_TASK_OFFER_ACCEPT: _parse_task_offer_accept,
|
||||||
BROKER_TASK_SETTINGS: _parse_task_settings,
|
BROKER_TASK_SETTINGS: _parse_task_settings,
|
||||||
BROKER_TASK_CANCEL: _parse_task_cancel,
|
BROKER_TASK_CANCEL: _parse_task_cancel,
|
||||||
|
BROKER_RPC_RESPONSE: _parse_rpc_response,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from .broker import (
|
|||||||
BrokerTaskOfferAccept,
|
BrokerTaskOfferAccept,
|
||||||
BrokerTaskSettings,
|
BrokerTaskSettings,
|
||||||
BrokerTaskCancel,
|
BrokerTaskCancel,
|
||||||
|
BrokerRpcResponse,
|
||||||
)
|
)
|
||||||
from .runner import (
|
from .runner import (
|
||||||
RunnerMessage,
|
RunnerMessage,
|
||||||
@@ -14,6 +15,7 @@ from .runner import (
|
|||||||
RunnerTaskRejected,
|
RunnerTaskRejected,
|
||||||
RunnerTaskDone,
|
RunnerTaskDone,
|
||||||
RunnerTaskError,
|
RunnerTaskError,
|
||||||
|
RunnerRpcCall,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -23,6 +25,7 @@ __all__ = [
|
|||||||
"BrokerTaskOfferAccept",
|
"BrokerTaskOfferAccept",
|
||||||
"BrokerTaskSettings",
|
"BrokerTaskSettings",
|
||||||
"BrokerTaskCancel",
|
"BrokerTaskCancel",
|
||||||
|
"BrokerRpcResponse",
|
||||||
"RunnerMessage",
|
"RunnerMessage",
|
||||||
"RunnerInfo",
|
"RunnerInfo",
|
||||||
"RunnerTaskOffer",
|
"RunnerTaskOffer",
|
||||||
@@ -30,4 +33,5 @@ __all__ = [
|
|||||||
"RunnerTaskRejected",
|
"RunnerTaskRejected",
|
||||||
"RunnerTaskDone",
|
"RunnerTaskDone",
|
||||||
"RunnerTaskError",
|
"RunnerTaskError",
|
||||||
|
"RunnerRpcCall",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from ..constants import (
|
|||||||
BROKER_TASK_CANCEL,
|
BROKER_TASK_CANCEL,
|
||||||
BROKER_TASK_OFFER_ACCEPT,
|
BROKER_TASK_OFFER_ACCEPT,
|
||||||
BROKER_TASK_SETTINGS,
|
BROKER_TASK_SETTINGS,
|
||||||
|
BROKER_RPC_RESPONSE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -54,10 +55,19 @@ class BrokerTaskCancel:
|
|||||||
type: Literal["broker:taskcancel"] = BROKER_TASK_CANCEL
|
type: Literal["broker:taskcancel"] = BROKER_TASK_CANCEL
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BrokerRpcResponse:
|
||||||
|
call_id: str
|
||||||
|
task_id: str
|
||||||
|
status: str
|
||||||
|
type: Literal["broker:rpcresponse"] = BROKER_RPC_RESPONSE
|
||||||
|
|
||||||
|
|
||||||
BrokerMessage = Union[
|
BrokerMessage = Union[
|
||||||
BrokerInfoRequest,
|
BrokerInfoRequest,
|
||||||
BrokerRunnerRegistered,
|
BrokerRunnerRegistered,
|
||||||
BrokerTaskOfferAccept,
|
BrokerTaskOfferAccept,
|
||||||
BrokerTaskSettings,
|
BrokerTaskSettings,
|
||||||
BrokerTaskCancel,
|
BrokerTaskCancel,
|
||||||
|
BrokerRpcResponse,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Literal, Union, Any, Dict
|
from typing import List, Literal, Union, Any, Dict
|
||||||
|
from ..constants import RUNNER_RPC_CALL
|
||||||
|
|
||||||
from ..constants import (
|
from ..constants import (
|
||||||
RUNNER_INFO,
|
RUNNER_INFO,
|
||||||
@@ -53,6 +54,15 @@ class RunnerTaskError:
|
|||||||
type: Literal["runner:taskerror"] = RUNNER_TASK_ERROR
|
type: Literal["runner:taskerror"] = RUNNER_TASK_ERROR
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RunnerRpcCall:
|
||||||
|
call_id: str
|
||||||
|
task_id: str
|
||||||
|
name: str
|
||||||
|
params: List[Any]
|
||||||
|
type: Literal["runner:rpc"] = RUNNER_RPC_CALL
|
||||||
|
|
||||||
|
|
||||||
RunnerMessage = Union[
|
RunnerMessage = Union[
|
||||||
RunnerInfo,
|
RunnerInfo,
|
||||||
RunnerTaskOffer,
|
RunnerTaskOffer,
|
||||||
@@ -60,4 +70,5 @@ RunnerMessage = Union[
|
|||||||
RunnerTaskRejected,
|
RunnerTaskRejected,
|
||||||
RunnerTaskDone,
|
RunnerTaskDone,
|
||||||
RunnerTaskError,
|
RunnerTaskError,
|
||||||
|
RunnerRpcCall,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from queue import Empty
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
import traceback
|
import traceback
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import json
|
||||||
|
|
||||||
from .errors import (
|
from .errors import (
|
||||||
TaskResultMissingError,
|
TaskResultMissingError,
|
||||||
@@ -11,12 +12,15 @@ from .errors import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .message_types.broker import NodeMode, Items
|
from .message_types.broker import NodeMode, Items
|
||||||
from .constants import EXECUTOR_USER_OUTPUT_KEY
|
from .constants import EXECUTOR_CIRCULAR_REFERENCE_KEY, EXECUTOR_USER_OUTPUT_KEY
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from multiprocessing.context import SpawnProcess
|
from multiprocessing.context import SpawnProcess
|
||||||
|
|
||||||
MULTIPROCESSING_CONTEXT = multiprocessing.get_context("spawn")
|
MULTIPROCESSING_CONTEXT = multiprocessing.get_context("spawn")
|
||||||
|
|
||||||
|
PrintArgs = list[list[Any]] # Args to all `print()` calls in a Python code task
|
||||||
|
|
||||||
|
|
||||||
class TaskExecutor:
|
class TaskExecutor:
|
||||||
"""Responsible for executing Python code tasks in isolated subprocesses."""
|
"""Responsible for executing Python code tasks in isolated subprocesses."""
|
||||||
@@ -42,9 +46,11 @@ class TaskExecutor:
|
|||||||
queue: multiprocessing.Queue,
|
queue: multiprocessing.Queue,
|
||||||
task_timeout: int,
|
task_timeout: int,
|
||||||
continue_on_fail: bool,
|
continue_on_fail: bool,
|
||||||
):
|
) -> tuple[list, PrintArgs]:
|
||||||
"""Execute a subprocess for a Python code task."""
|
"""Execute a subprocess for a Python code task."""
|
||||||
|
|
||||||
|
print_args: PrintArgs = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
process.start()
|
process.start()
|
||||||
process.join(timeout=task_timeout)
|
process.join(timeout=task_timeout)
|
||||||
@@ -65,11 +71,14 @@ class TaskExecutor:
|
|||||||
if "error" in returned:
|
if "error" in returned:
|
||||||
raise TaskRuntimeError(returned["error"])
|
raise TaskRuntimeError(returned["error"])
|
||||||
|
|
||||||
return returned["result"] or []
|
result = returned.get("result", [])
|
||||||
|
print_args = returned.get("print_args", [])
|
||||||
|
|
||||||
|
return result, print_args
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if continue_on_fail:
|
if continue_on_fail:
|
||||||
return [{"json": {"error": str(e)}}]
|
return [{"json": {"error": str(e)}}], print_args
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -89,27 +98,46 @@ class TaskExecutor:
|
|||||||
def _all_items(raw_code: str, items: Items, queue: multiprocessing.Queue):
|
def _all_items(raw_code: str, items: Items, queue: multiprocessing.Queue):
|
||||||
"""Execute a Python code task in all-items mode."""
|
"""Execute a Python code task in all-items mode."""
|
||||||
|
|
||||||
|
print_args: PrintArgs = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
code = TaskExecutor._wrap_code(raw_code)
|
code = TaskExecutor._wrap_code(raw_code)
|
||||||
globals = {"__builtins__": __builtins__, "_items": items}
|
|
||||||
|
globals = {
|
||||||
|
"__builtins__": __builtins__,
|
||||||
|
"_items": items,
|
||||||
|
"print": TaskExecutor._create_custom_print(print_args),
|
||||||
|
}
|
||||||
|
|
||||||
exec(code, globals)
|
exec(code, globals)
|
||||||
queue.put({"result": globals[EXECUTOR_USER_OUTPUT_KEY]})
|
|
||||||
|
queue.put(
|
||||||
|
{"result": globals[EXECUTOR_USER_OUTPUT_KEY], "print_args": print_args}
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
TaskExecutor._put_error(queue, e)
|
TaskExecutor._put_error(queue, e, print_args)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _per_item(raw_code: str, items: Items, queue: multiprocessing.Queue):
|
def _per_item(raw_code: str, items: Items, queue: multiprocessing.Queue):
|
||||||
"""Execute a Python code task in per-item mode."""
|
"""Execute a Python code task in per-item mode."""
|
||||||
|
|
||||||
|
print_args: PrintArgs = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
wrapped_code = TaskExecutor._wrap_code(raw_code)
|
wrapped_code = TaskExecutor._wrap_code(raw_code)
|
||||||
compiled_code = compile(wrapped_code, "<per_item_task_execution>", "exec")
|
compiled_code = compile(wrapped_code, "<per_item_task_execution>", "exec")
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for index, item in enumerate(items):
|
for index, item in enumerate(items):
|
||||||
globals = {"__builtins__": __builtins__, "_item": item}
|
globals = {
|
||||||
|
"__builtins__": __builtins__,
|
||||||
|
"_item": item,
|
||||||
|
"print": TaskExecutor._create_custom_print(print_args),
|
||||||
|
}
|
||||||
|
|
||||||
exec(compiled_code, globals)
|
exec(compiled_code, globals)
|
||||||
|
|
||||||
user_output = globals[EXECUTOR_USER_OUTPUT_KEY]
|
user_output = globals[EXECUTOR_USER_OUTPUT_KEY]
|
||||||
|
|
||||||
if user_output is None:
|
if user_output is None:
|
||||||
@@ -118,10 +146,10 @@ class TaskExecutor:
|
|||||||
user_output["pairedItem"] = {"item": index}
|
user_output["pairedItem"] = {"item": index}
|
||||||
result.append(user_output)
|
result.append(user_output)
|
||||||
|
|
||||||
queue.put({"result": result})
|
queue.put({"result": result, "print_args": print_args})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
TaskExecutor._put_error(queue, e)
|
TaskExecutor._put_error(queue, e, print_args)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _wrap_code(raw_code: str) -> str:
|
def _wrap_code(raw_code: str) -> str:
|
||||||
@@ -129,5 +157,63 @@ class TaskExecutor:
|
|||||||
return f"def _user_function():\n{indented_code}\n\n{EXECUTOR_USER_OUTPUT_KEY} = _user_function()"
|
return f"def _user_function():\n{indented_code}\n\n{EXECUTOR_USER_OUTPUT_KEY} = _user_function()"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _put_error(queue: multiprocessing.Queue, e: Exception):
|
def _put_error(queue: multiprocessing.Queue, e: Exception, print_args: PrintArgs):
|
||||||
queue.put({"error": {"message": str(e), "stack": traceback.format_exc()}})
|
queue.put(
|
||||||
|
{
|
||||||
|
"error": {"message": str(e), "stack": traceback.format_exc()},
|
||||||
|
"print_args": print_args,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ========== print() ==========
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_custom_print(print_args: PrintArgs):
|
||||||
|
def custom_print(*args):
|
||||||
|
serializable_args = []
|
||||||
|
|
||||||
|
for arg in args:
|
||||||
|
try:
|
||||||
|
json.dumps(arg, default=str, ensure_ascii=False)
|
||||||
|
serializable_args.append(arg)
|
||||||
|
except Exception as _:
|
||||||
|
# Ensure args are serializable so they are transmissible
|
||||||
|
# through the multiprocessing queue and via websockets.
|
||||||
|
serializable_args.append(
|
||||||
|
{
|
||||||
|
EXECUTOR_CIRCULAR_REFERENCE_KEY: repr(arg),
|
||||||
|
"__type__": type(arg).__name__,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
formatted = TaskExecutor._format_print_args(*serializable_args)
|
||||||
|
print_args.append(formatted)
|
||||||
|
print("[user code]", *args)
|
||||||
|
|
||||||
|
return custom_print
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_print_args(*args) -> list[str]:
|
||||||
|
"""
|
||||||
|
Takes the arguments passed to a `print()` call in user code and converts them
|
||||||
|
to string representations suitable for display in a browser console.
|
||||||
|
|
||||||
|
Expects all args to be serializable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
formatted = []
|
||||||
|
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, str):
|
||||||
|
formatted.append(f"'{arg}'")
|
||||||
|
|
||||||
|
elif arg is None or isinstance(arg, (int, float, bool)):
|
||||||
|
formatted.append(str(arg))
|
||||||
|
|
||||||
|
elif isinstance(arg, dict) and EXECUTOR_CIRCULAR_REFERENCE_KEY in arg:
|
||||||
|
formatted.append(f"[Circular {arg.get('__type__', 'Object')}]")
|
||||||
|
|
||||||
|
else:
|
||||||
|
formatted.append(json.dumps(arg, default=str, ensure_ascii=False))
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from urllib.parse import urlparse
|
|||||||
import websockets
|
import websockets
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
from .errors import WebsocketConnectionError, TaskMissingError
|
from .errors import WebsocketConnectionError, TaskMissingError
|
||||||
from .message_types.broker import TaskSettings
|
from .message_types.broker import TaskSettings
|
||||||
from .nanoid import nanoid
|
from .nanoid import nanoid
|
||||||
@@ -22,6 +21,7 @@ from .constants import (
|
|||||||
OFFER_VALIDITY_MAX_JITTER,
|
OFFER_VALIDITY_MAX_JITTER,
|
||||||
OFFER_VALIDITY_LATENCY_BUFFER,
|
OFFER_VALIDITY_LATENCY_BUFFER,
|
||||||
TASK_BROKER_WS_PATH,
|
TASK_BROKER_WS_PATH,
|
||||||
|
RPC_BROWSER_CONSOLE_LOG_METHOD,
|
||||||
)
|
)
|
||||||
from .message_types import (
|
from .message_types import (
|
||||||
BrokerMessage,
|
BrokerMessage,
|
||||||
@@ -31,12 +31,14 @@ from .message_types import (
|
|||||||
BrokerTaskOfferAccept,
|
BrokerTaskOfferAccept,
|
||||||
BrokerTaskSettings,
|
BrokerTaskSettings,
|
||||||
BrokerTaskCancel,
|
BrokerTaskCancel,
|
||||||
|
BrokerRpcResponse,
|
||||||
RunnerInfo,
|
RunnerInfo,
|
||||||
RunnerTaskOffer,
|
RunnerTaskOffer,
|
||||||
RunnerTaskAccepted,
|
RunnerTaskAccepted,
|
||||||
RunnerTaskRejected,
|
RunnerTaskRejected,
|
||||||
RunnerTaskDone,
|
RunnerTaskDone,
|
||||||
RunnerTaskError,
|
RunnerTaskError,
|
||||||
|
RunnerRpcCall,
|
||||||
)
|
)
|
||||||
from .message_serde import MessageSerde
|
from .message_serde import MessageSerde
|
||||||
from .task_state import TaskState, TaskStatus
|
from .task_state import TaskState, TaskStatus
|
||||||
@@ -140,6 +142,8 @@ class TaskRunner:
|
|||||||
await self._handle_task_settings(message)
|
await self._handle_task_settings(message)
|
||||||
case BrokerTaskCancel():
|
case BrokerTaskCancel():
|
||||||
await self._handle_task_cancel(message)
|
await self._handle_task_cancel(message)
|
||||||
|
case BrokerRpcResponse():
|
||||||
|
pass # currently only logging, already handled by browser
|
||||||
case _:
|
case _:
|
||||||
self.logger.warning(f"Unhandled message type: {type(message)}")
|
self.logger.warning(f"Unhandled message type: {type(message)}")
|
||||||
|
|
||||||
@@ -208,10 +212,15 @@ class TaskRunner:
|
|||||||
|
|
||||||
task_state.process = process
|
task_state.process = process
|
||||||
|
|
||||||
result = self.executor.execute_process(
|
result, print_args = self.executor.execute_process(
|
||||||
process, queue, self.opts.task_timeout, task_settings.continue_on_fail
|
process, queue, self.opts.task_timeout, task_settings.continue_on_fail
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for print_args_per_call in print_args:
|
||||||
|
await self._send_rpc_message(
|
||||||
|
task_id, RPC_BROWSER_CONSOLE_LOG_METHOD, print_args_per_call
|
||||||
|
)
|
||||||
|
|
||||||
response = RunnerTaskDone(task_id=task_id, data={"result": result})
|
response = RunnerTaskDone(task_id=task_id, data={"result": result})
|
||||||
await self._send_message(response)
|
await self._send_message(response)
|
||||||
self.logger.info(f"Completed task {task_id}")
|
self.logger.info(f"Completed task {task_id}")
|
||||||
@@ -241,6 +250,13 @@ class TaskRunner:
|
|||||||
task_state.status = TaskStatus.ABORTING
|
task_state.status = TaskStatus.ABORTING
|
||||||
self.executor.stop_process(task_state.process)
|
self.executor.stop_process(task_state.process)
|
||||||
|
|
||||||
|
async def _send_rpc_message(self, task_id: str, method_name: str, params: list):
|
||||||
|
message = RunnerRpcCall(
|
||||||
|
call_id=nanoid(), task_id=task_id, name=method_name, params=params
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._send_message(message)
|
||||||
|
|
||||||
async def _send_message(self, message: RunnerMessage) -> None:
|
async def _send_message(self, message: RunnerMessage) -> None:
|
||||||
if self.websocket_connection is None:
|
if self.websocket_connection is None:
|
||||||
raise WebsocketConnectionError(self.task_broker_uri)
|
raise WebsocketConnectionError(self.task_broker_uri)
|
||||||
|
|||||||
@@ -30,9 +30,13 @@ export const useCompleter = (
|
|||||||
const word = context.matchBefore(/\w*/);
|
const word = context.matchBefore(/\w*/);
|
||||||
if (!word) return null;
|
if (!word) return null;
|
||||||
|
|
||||||
const label = toValue(mode) === 'runOnceForEachItem' ? '_item' : '_items';
|
const options = [];
|
||||||
|
|
||||||
return { from: word.from, options: [{ label, type: 'variable' }] };
|
const label = toValue(mode) === 'runOnceForEachItem' ? '_item' : '_items';
|
||||||
|
options.push({ label, type: 'variable' });
|
||||||
|
options.push({ label: 'print', type: 'function' });
|
||||||
|
|
||||||
|
return { from: word.from, options };
|
||||||
};
|
};
|
||||||
|
|
||||||
return autocompletion({ icons: false, override: [completions] });
|
return autocompletion({ icons: false, override: [completions] });
|
||||||
|
|||||||
Reference in New Issue
Block a user