oocana-python-executor 0.16.0__tar.gz → 0.16.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/PKG-INFO +1 -1
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/pyproject.toml +1 -1
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/block.py +4 -4
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/context.py +2 -2
- oocana_python_executor-0.16.2/python_executor/data.py +5 -0
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/executor.py +22 -17
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/hook.py +13 -15
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/matplot/matplotlib_oomol/oomol.py +3 -3
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/matplot/oomol_matplot_helper.py +4 -3
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/service.py +1 -1
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/tests/test_cli.py +17 -0
- oocana_python_executor-0.16.0/python_executor/data.py +0 -5
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/__init__.py +0 -0
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/logger.py +0 -0
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/matplot/matplotlib_oomol/__init__.py +0 -0
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/secret.py +0 -0
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/topic.py +0 -0
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/python_executor/utils.py +0 -0
- {oocana_python_executor-0.16.0 → oocana_python_executor-0.16.2}/tests/test_secret.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: oocana-python-executor
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.2
|
|
4
4
|
Summary: a client subscribe mqtt topic to execute oocana's block
|
|
5
5
|
Author-Email: l1shen <lishen1635@gmail.com>, yleaf <11785335+leavesster@users.noreply.github.com>
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ from typing import Optional, TypedDict
|
|
|
4
4
|
import inspect
|
|
5
5
|
import traceback
|
|
6
6
|
import logging
|
|
7
|
-
from .data import store,
|
|
7
|
+
from .data import store, block_var, EXECUTOR_NAME
|
|
8
8
|
from .context import createContext
|
|
9
9
|
from .hook import ExitFunctionException
|
|
10
10
|
import os
|
|
@@ -82,12 +82,12 @@ def output_return_object(obj, context: Context):
|
|
|
82
82
|
|
|
83
83
|
logger = logging.getLogger(EXECUTOR_NAME)
|
|
84
84
|
|
|
85
|
-
async def run_block(message, mainframe: Mainframe, session_dir: str, tmp_dir: str):
|
|
85
|
+
async def run_block(message, mainframe: Mainframe, session_dir: str, tmp_dir: str, package_name: str):
|
|
86
86
|
|
|
87
87
|
logger.info(f"block {message.get('job_id')} start")
|
|
88
88
|
try:
|
|
89
89
|
payload = ExecutePayload(**message)
|
|
90
|
-
context = createContext(mainframe, payload.session_id, payload.job_id, store, payload.outputs, session_dir, tmp_dir = tmp_dir)
|
|
90
|
+
context = createContext(mainframe, payload.session_id, payload.job_id, store, payload.outputs, session_dir, tmp_dir = tmp_dir, package_name=package_name)
|
|
91
91
|
except Exception:
|
|
92
92
|
traceback_str = traceback.format_exc()
|
|
93
93
|
# rust 那边会保证传过来的 message 一定是符合格式的,所以这里不应该出现异常。这里主要是防止 rust 修改错误。
|
|
@@ -103,7 +103,7 @@ async def run_block(message, mainframe: Mainframe, session_dir: str, tmp_dir: st
|
|
|
103
103
|
})
|
|
104
104
|
return
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
block_var.set(context)
|
|
107
107
|
|
|
108
108
|
load_dir = payload.dir
|
|
109
109
|
|
|
@@ -9,7 +9,7 @@ from .data import EXECUTOR_NAME
|
|
|
9
9
|
logger = logging.getLogger(EXECUTOR_NAME)
|
|
10
10
|
|
|
11
11
|
def createContext(
|
|
12
|
-
mainframe: Mainframe, session_id: str, job_id: str, store, output, session_dir: str, tmp_dir: str
|
|
12
|
+
mainframe: Mainframe, session_id: str, job_id: str, store, output, session_dir: str, tmp_dir: str, package_name: str
|
|
13
13
|
) -> Context:
|
|
14
14
|
|
|
15
15
|
node_props = mainframe.notify_block_ready(session_id, job_id)
|
|
@@ -59,7 +59,7 @@ def createContext(
|
|
|
59
59
|
|
|
60
60
|
blockInfo = BlockInfo(**node_props)
|
|
61
61
|
|
|
62
|
-
ctx = Context(inputs, blockInfo, mainframe, store, output, session_dir, tmp_dir)
|
|
62
|
+
ctx = Context(inputs, blockInfo, mainframe, store, output, session_dir, tmp_dir, package_name=package_name)
|
|
63
63
|
# 跟 executor 日志分开,避免有的库在 logger 里面使用 print,导致 hook 出现递归调用。
|
|
64
64
|
block_logger = logging.getLogger(f"block {job_id}")
|
|
65
65
|
ctx_handler = ContextHandler(ctx)
|
|
@@ -72,6 +72,8 @@ async def run_executor(address: str, session_id: str, tmp_dir: str, package: str
|
|
|
72
72
|
elif os.path.exists("/app/workspace"):
|
|
73
73
|
sys.path.append("/app/workspace")
|
|
74
74
|
|
|
75
|
+
package_name = os.path.basename(package) if package is not None else "workspace"
|
|
76
|
+
|
|
75
77
|
|
|
76
78
|
def not_current_session(message):
|
|
77
79
|
return message.get("session_id") != session_id
|
|
@@ -211,12 +213,12 @@ async def run_executor(address: str, session_id: str, tmp_dir: str, package: str
|
|
|
211
213
|
else:
|
|
212
214
|
if not_current_session(message):
|
|
213
215
|
continue
|
|
214
|
-
run_block_in_new_thread(message, mainframe, session_dir=session_dir, tmp_dir=tmp_dir)
|
|
216
|
+
run_block_in_new_thread(message, mainframe, session_dir=session_dir, tmp_dir=tmp_dir, package_name=package_name)
|
|
215
217
|
|
|
216
|
-
def run_block_in_new_thread(message, mainframe: Mainframe, session_dir: str, tmp_dir: str):
|
|
218
|
+
def run_block_in_new_thread(message, mainframe: Mainframe, session_dir: str, tmp_dir: str, package_name: str):
|
|
217
219
|
|
|
218
220
|
async def run():
|
|
219
|
-
await run_block(message, mainframe, session_dir=session_dir, tmp_dir=tmp_dir)
|
|
221
|
+
await run_block(message, mainframe, session_dir=session_dir, tmp_dir=tmp_dir, package_name=package_name)
|
|
220
222
|
run_in_new_thread(run)
|
|
221
223
|
|
|
222
224
|
def main():
|
|
@@ -234,29 +236,32 @@ def main():
|
|
|
234
236
|
parser.add_argument("--wait-for-client", help="wait for client to connect", default=False, action="store_true")
|
|
235
237
|
|
|
236
238
|
try:
|
|
237
|
-
|
|
239
|
+
namespace, unknown_args = parser.parse_known_args()
|
|
238
240
|
except Exception as e:
|
|
239
|
-
|
|
241
|
+
hook.original_print(f"parse args error: {e}")
|
|
240
242
|
# because we hook sys.exit in hook.py and raise a exception, the exit will be reset to 1.
|
|
241
243
|
# parser origin exit code is 2. so we use 2 here.
|
|
242
|
-
|
|
244
|
+
hook.original_exit(2)
|
|
243
245
|
|
|
244
|
-
address: str =
|
|
245
|
-
session_id: str = str(
|
|
246
|
-
output: Literal["console", "file"] =
|
|
247
|
-
package: str | None =
|
|
248
|
-
session_dir: str =
|
|
249
|
-
tmp_dir: str =
|
|
250
|
-
identifier: str | None =
|
|
246
|
+
address: str = namespace.address
|
|
247
|
+
session_id: str = str(namespace.session_id)
|
|
248
|
+
output: Literal["console", "file"] = namespace.output
|
|
249
|
+
package: str | None = namespace.package
|
|
250
|
+
session_dir: str = namespace.session_dir
|
|
251
|
+
tmp_dir: str = namespace.tmp_dir
|
|
252
|
+
identifier: str | None = namespace.identifier
|
|
251
253
|
|
|
252
254
|
config_logger(session_id, identifier, output)
|
|
253
255
|
|
|
254
|
-
if
|
|
256
|
+
if len(unknown_args) > 0:
|
|
257
|
+
logger.warning(f"receive unknown args: {unknown_args}")
|
|
258
|
+
|
|
259
|
+
if namespace.debug_port is not None and namespace.debug_port.isdigit():
|
|
255
260
|
try:
|
|
256
261
|
import debugpy
|
|
257
|
-
debugpy.listen(int(
|
|
258
|
-
logger.info(f"debugpy listen on port {
|
|
259
|
-
if
|
|
262
|
+
debugpy.listen(int(namespace.debug_port))
|
|
263
|
+
logger.info(f"debugpy listen on port {namespace.debug_port}")
|
|
264
|
+
if namespace.wait_for_client:
|
|
260
265
|
logger.info("wait for client to connect")
|
|
261
266
|
debugpy.wait_for_client()
|
|
262
267
|
logger.info("client connected")
|
|
@@ -3,7 +3,7 @@ from builtins import exit as global_exit
|
|
|
3
3
|
from typing import TypeAlias, Any
|
|
4
4
|
import sys
|
|
5
5
|
import builtins
|
|
6
|
-
from .data import
|
|
6
|
+
from .data import block_var, EXECUTOR_NAME
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
9
|
logger = logging.getLogger(EXECUTOR_NAME)
|
|
@@ -18,27 +18,25 @@ original_print = print
|
|
|
18
18
|
_ExitCode: TypeAlias = str | int | None
|
|
19
19
|
|
|
20
20
|
def sys_exit(status: _ExitCode = None) -> None:
|
|
21
|
-
|
|
21
|
+
if block_var.get(None) is not None:
|
|
22
|
+
raise ExitFunctionException(status)
|
|
23
|
+
else:
|
|
24
|
+
original_exit(status)
|
|
22
25
|
|
|
23
26
|
def sys_global_exit(status: _ExitCode = None) -> None:
|
|
24
|
-
|
|
27
|
+
if block_var.get(None) is not None:
|
|
28
|
+
raise ExitFunctionException(status)
|
|
29
|
+
else:
|
|
30
|
+
original_global_exit(status)
|
|
25
31
|
|
|
26
32
|
def global_print(*values: object, sep: str | None = " ", end: str | None = "\n", file: Any | None = None, flush: bool = False) -> None:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
context = vars.get()
|
|
31
|
-
except LookupError:
|
|
32
|
-
# 这个 logger 不会上报到 root handle 中,所以即使 root logger 的 Handler 里面有 print 函数,也不会导致递归调用
|
|
33
|
-
logger.warning("print called outside of block")
|
|
34
|
-
except Exception as e:
|
|
35
|
-
logger.error(f"print error: {e}")
|
|
36
|
-
|
|
37
|
-
if context is not None:
|
|
33
|
+
|
|
34
|
+
block_ctx = block_var.get(None)
|
|
35
|
+
if block_ctx is not None:
|
|
38
36
|
try:
|
|
39
37
|
msg_sep = sep or " "
|
|
40
38
|
msg = msg_sep.join(map(str, values))
|
|
41
|
-
|
|
39
|
+
block_ctx.report_log(msg)
|
|
42
40
|
except Exception as e:
|
|
43
41
|
logger.error(f"transform print message to context log error: {e}")
|
|
44
42
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from matplotlib.backend_bases import Gcf # type: ignore
|
|
4
4
|
from matplotlib.backends.backend_agg import FigureCanvasAgg # type: ignore
|
|
5
|
-
from python_executor.data import
|
|
5
|
+
from python_executor.data import block_var
|
|
6
6
|
|
|
7
7
|
FigureCanvas = FigureCanvasAgg
|
|
8
8
|
|
|
@@ -10,8 +10,8 @@ def show(*args, **kwargs):
|
|
|
10
10
|
import sys
|
|
11
11
|
from io import BytesIO
|
|
12
12
|
from base64 import b64encode
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
context = block_var.get(None)
|
|
14
|
+
if context is not None:
|
|
15
15
|
images = []
|
|
16
16
|
for figmanager in Gcf.get_all_fig_managers():
|
|
17
17
|
buffer = BytesIO()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from python_executor.data import
|
|
1
|
+
from python_executor.data import block_var
|
|
2
2
|
|
|
3
3
|
def add_matplot_module():
|
|
4
4
|
import sys
|
|
@@ -13,6 +13,7 @@ def import_helper(logger):
|
|
|
13
13
|
matplotlib.use('module://matplotlib_oomol') # matplotlib_oomol.py 文件所在目录加入 PYTHONPATH
|
|
14
14
|
except:
|
|
15
15
|
logger.error("import matplotlib failed")
|
|
16
|
+
return
|
|
16
17
|
|
|
17
18
|
# matplotlib 主题替换
|
|
18
19
|
try:
|
|
@@ -34,8 +35,8 @@ def import_helper(logger):
|
|
|
34
35
|
|
|
35
36
|
class OomolRenderer(ExternalRenderer):
|
|
36
37
|
def render(self, fig_dict):
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
context = block_var.get(None)
|
|
39
|
+
if context is not None:
|
|
39
40
|
|
|
40
41
|
import re
|
|
41
42
|
from plotly.io import to_html # type: ignore
|
|
@@ -179,7 +179,7 @@ class ServiceRuntime(ServiceContextAbstractClass):
|
|
|
179
179
|
self._runningBlocks.add(job_id)
|
|
180
180
|
self._jobs.add(job_id)
|
|
181
181
|
|
|
182
|
-
context = createContext(self._mainframe, payload["session_id"], payload["job_id"], self._store, payload["outputs"], self._session_dir, tmp_dir=self._session_dir) # TODO: tmp_dir need consider global service.
|
|
182
|
+
context = createContext(self._mainframe, payload["session_id"], payload["job_id"], self._store, payload["outputs"], self._session_dir, tmp_dir=self._session_dir, package_name=service_hash) # TODO: tmp_dir need consider global service.
|
|
183
183
|
|
|
184
184
|
if isinstance(self.block_handler, dict):
|
|
185
185
|
handler = self.block_handler.get(block_name)
|
|
@@ -35,6 +35,23 @@ class TestExecutorCLI(unittest.TestCase):
|
|
|
35
35
|
process.send_signal(signal.SIGINT)
|
|
36
36
|
|
|
37
37
|
process.wait()
|
|
38
|
+
|
|
39
|
+
def test_cli_addition_args(self):
|
|
40
|
+
cli_command = [sys.executable, "-u", "-m", "python_executor.executor", "--session-id", "test-session", "--session-dir", "/tmp", "--tmp-dir", "/tmp", "--echo", "Hello World!"]
|
|
41
|
+
|
|
42
|
+
print("Starting CLI tool... in", executor_parent_dir)
|
|
43
|
+
|
|
44
|
+
process = subprocess.Popen(cli_command, cwd=executor_parent_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
45
|
+
|
|
46
|
+
time.sleep(3)
|
|
47
|
+
|
|
48
|
+
code = process.poll()
|
|
49
|
+
|
|
50
|
+
self.assertIsNone(code, f"CLI tool failed to start or exit with code {code}")
|
|
51
|
+
|
|
52
|
+
process.send_signal(signal.SIGINT)
|
|
53
|
+
|
|
54
|
+
process.wait()
|
|
38
55
|
|
|
39
56
|
def test_cli_fail(self):
|
|
40
57
|
cli_command = [sys.executable, "-u", "-m", "python_executor.executor"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|