blendjob 0.1.5__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.
- blendjob-0.1.5/LICENSE +21 -0
- blendjob-0.1.5/PKG-INFO +132 -0
- blendjob-0.1.5/README.md +108 -0
- blendjob-0.1.5/pyproject.toml +53 -0
- blendjob-0.1.5/pyproject.toml.orig +45 -0
- blendjob-0.1.5/src/blendjob/__init__.py +21 -0
- blendjob-0.1.5/src/blendjob/client.py +101 -0
- blendjob-0.1.5/src/blendjob/controller.py +251 -0
- blendjob-0.1.5/src/blendjob/entrypoint.py +28 -0
- blendjob-0.1.5/src/blendjob/environment.py +351 -0
- blendjob-0.1.5/src/blendjob/installer/environment.py +13 -0
- blendjob-0.1.5/src/blendjob/operator.py +255 -0
- blendjob-0.1.5/src/blendjob/runner.py +83 -0
- blendjob-0.1.5/src/blendjob/runtime.py +586 -0
- blendjob-0.1.5/src/blendjob/server.py +455 -0
blendjob-0.1.5/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OmooLab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
blendjob-0.1.5/PKG-INFO
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blendjob
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Reusable local Job Server runtime for Blender add-ons
|
|
5
|
+
Keywords: blender,addon,job,server,runtime
|
|
6
|
+
Author: OmooLab
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Dist: fastapi==0.139.2 ; extra == 'server'
|
|
17
|
+
Requires-Dist: uvicorn==0.51.0 ; extra == 'server'
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Project-URL: Documentation, https://docs.omoolab.xyz/blendjob/
|
|
20
|
+
Project-URL: Issues, https://github.com/OmooLab/BlendJob/issues
|
|
21
|
+
Project-URL: Repository, https://github.com/OmooLab/BlendJob
|
|
22
|
+
Provides-Extra: server
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# BlendJob
|
|
26
|
+
|
|
27
|
+
BlendJob 是面向 Blender Add-on 的本地 Job Server 运行时。它把 Blender Operator、独立 Python Environment、HTTP Job Server、FIFO Queue、进度、取消和 Resource 生命周期组合成一套可复用 API。
|
|
28
|
+
|
|
29
|
+
BlendJob 适合需要在 Blender 界面之外运行 AI 推理、模型下载、媒体处理或其它重型 Python 任务的 Add-on。Blender 主进程只保留轻量 Client 和 UI 逻辑,业务依赖安装在独立 Environment 中。
|
|
30
|
+
|
|
31
|
+
## 要求
|
|
32
|
+
|
|
33
|
+
- Python 3.10 或更高版本
|
|
34
|
+
- 支持 Python 3.10+ 的 Blender 版本
|
|
35
|
+
- Windows x64、macOS arm64 或 Linux x64;内建 uv 安装器目前只提供这些平台
|
|
36
|
+
|
|
37
|
+
## 安装
|
|
38
|
+
|
|
39
|
+
普通 Python 项目可以使用 pip 或 uv:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python -m pip install blendjob
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv add blendjob
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
默认安装保持零依赖,适合 Blender 主进程。需要在普通 Python 环境中直接创建 FastAPI App 或运行 Runner 时,安装 `server` extra:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python -m pip install "blendjob[server]"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Blender Extension 需要把 BlendJob wheel 随扩展分发,并在 `blender_manifest.toml` 中声明:
|
|
56
|
+
|
|
57
|
+
```toml
|
|
58
|
+
wheels = ["./wheels/blendjob-0.1.5-py3-none-any.whl"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
同一个 wheel 还需要加入 Runtime 的独立 Environment,使 Server Python 可以执行 `python -m blendjob.runner`:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from pathlib import Path
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
blendjob_wheel = Path(__file__).parent / "wheels" / "blendjob-0.1.5-py3-none-any.whl"
|
|
68
|
+
|
|
69
|
+
environment = {
|
|
70
|
+
"python": "3.10",
|
|
71
|
+
"packages": [str(blendjob_wheel)],
|
|
72
|
+
"platform_packages": {},
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 最小示例
|
|
77
|
+
|
|
78
|
+
Server package 导出一个 `JobServer`:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from blendjob import JobServer
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
server = JobServer("Example Job Server")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@server.job("double")
|
|
88
|
+
def double(context, parameters):
|
|
89
|
+
context.progress(0.5, "Calculating")
|
|
90
|
+
return {"value": parameters["value"] * 2}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Blender 侧创建一个 Runtime,并让业务 Operator 继承绑定后的基类:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from pathlib import Path
|
|
97
|
+
|
|
98
|
+
from blendjob import JobRuntime
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
runtime = JobRuntime(
|
|
102
|
+
"server:server",
|
|
103
|
+
entrypoint_root=Path(__file__).parent,
|
|
104
|
+
storage_root=Path.home() / ".example-addon",
|
|
105
|
+
environment=environment,
|
|
106
|
+
namespace="example_addon",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
JobOperatorBase = runtime.JobOperatorBase
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
完整的 Runtime、Operator、Server Handler、Resource 和存储结构说明见 [开发者指南](https://docs.omoolab.xyz/blendjob/)。
|
|
113
|
+
|
|
114
|
+
## 开发
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv sync
|
|
118
|
+
uv run pytest
|
|
119
|
+
uv build
|
|
120
|
+
uv run mkdocs build --strict
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
版本化文档使用 Mike:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
uv run mike deploy --update-aliases 0.1 latest
|
|
127
|
+
uv run mike set-default latest
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
BlendJob 使用 [MIT License](https://github.com/OmooLab/BlendJob/blob/main/LICENSE)。
|
blendjob-0.1.5/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# BlendJob
|
|
2
|
+
|
|
3
|
+
BlendJob 是面向 Blender Add-on 的本地 Job Server 运行时。它把 Blender Operator、独立 Python Environment、HTTP Job Server、FIFO Queue、进度、取消和 Resource 生命周期组合成一套可复用 API。
|
|
4
|
+
|
|
5
|
+
BlendJob 适合需要在 Blender 界面之外运行 AI 推理、模型下载、媒体处理或其它重型 Python 任务的 Add-on。Blender 主进程只保留轻量 Client 和 UI 逻辑,业务依赖安装在独立 Environment 中。
|
|
6
|
+
|
|
7
|
+
## 要求
|
|
8
|
+
|
|
9
|
+
- Python 3.10 或更高版本
|
|
10
|
+
- 支持 Python 3.10+ 的 Blender 版本
|
|
11
|
+
- Windows x64、macOS arm64 或 Linux x64;内建 uv 安装器目前只提供这些平台
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
普通 Python 项目可以使用 pip 或 uv:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python -m pip install blendjob
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv add blendjob
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
默认安装保持零依赖,适合 Blender 主进程。需要在普通 Python 环境中直接创建 FastAPI App 或运行 Runner 时,安装 `server` extra:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
python -m pip install "blendjob[server]"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Blender Extension 需要把 BlendJob wheel 随扩展分发,并在 `blender_manifest.toml` 中声明:
|
|
32
|
+
|
|
33
|
+
```toml
|
|
34
|
+
wheels = ["./wheels/blendjob-0.1.5-py3-none-any.whl"]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
同一个 wheel 还需要加入 Runtime 的独立 Environment,使 Server Python 可以执行 `python -m blendjob.runner`:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
blendjob_wheel = Path(__file__).parent / "wheels" / "blendjob-0.1.5-py3-none-any.whl"
|
|
44
|
+
|
|
45
|
+
environment = {
|
|
46
|
+
"python": "3.10",
|
|
47
|
+
"packages": [str(blendjob_wheel)],
|
|
48
|
+
"platform_packages": {},
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 最小示例
|
|
53
|
+
|
|
54
|
+
Server package 导出一个 `JobServer`:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from blendjob import JobServer
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
server = JobServer("Example Job Server")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@server.job("double")
|
|
64
|
+
def double(context, parameters):
|
|
65
|
+
context.progress(0.5, "Calculating")
|
|
66
|
+
return {"value": parameters["value"] * 2}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Blender 侧创建一个 Runtime,并让业务 Operator 继承绑定后的基类:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from pathlib import Path
|
|
73
|
+
|
|
74
|
+
from blendjob import JobRuntime
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
runtime = JobRuntime(
|
|
78
|
+
"server:server",
|
|
79
|
+
entrypoint_root=Path(__file__).parent,
|
|
80
|
+
storage_root=Path.home() / ".example-addon",
|
|
81
|
+
environment=environment,
|
|
82
|
+
namespace="example_addon",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
JobOperatorBase = runtime.JobOperatorBase
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
完整的 Runtime、Operator、Server Handler、Resource 和存储结构说明见 [开发者指南](https://docs.omoolab.xyz/blendjob/)。
|
|
89
|
+
|
|
90
|
+
## 开发
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv sync
|
|
94
|
+
uv run pytest
|
|
95
|
+
uv build
|
|
96
|
+
uv run mkdocs build --strict
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
版本化文档使用 Mike:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv run mike deploy --update-aliases 0.1 latest
|
|
103
|
+
uv run mike set-default latest
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
BlendJob 使用 [MIT License](https://github.com/OmooLab/BlendJob/blob/main/LICENSE)。
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "blendjob"
|
|
3
|
+
version = "0.1.5"
|
|
4
|
+
description = "Reusable local Job Server runtime for Blender add-ons"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
keywords = [
|
|
10
|
+
"blender",
|
|
11
|
+
"addon",
|
|
12
|
+
"job",
|
|
13
|
+
"server",
|
|
14
|
+
"runtime",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
]
|
|
25
|
+
dependencies = []
|
|
26
|
+
|
|
27
|
+
[[project.authors]]
|
|
28
|
+
name = "OmooLab"
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Documentation = "https://docs.omoolab.xyz/blendjob/"
|
|
32
|
+
Issues = "https://github.com/OmooLab/BlendJob/issues"
|
|
33
|
+
Repository = "https://github.com/OmooLab/BlendJob"
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
server = [
|
|
37
|
+
"fastapi==0.139.2",
|
|
38
|
+
"uvicorn==0.51.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[dependency-groups]
|
|
42
|
+
dev = [
|
|
43
|
+
"fastapi==0.139.2",
|
|
44
|
+
"mike>=2.1,<3",
|
|
45
|
+
"mkdocs-material>=9.6,<10",
|
|
46
|
+
"mkdocstrings[python]>=0.30,<1",
|
|
47
|
+
"pytest>=8.4,<9",
|
|
48
|
+
"uvicorn==0.51.0",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[build-system]
|
|
52
|
+
requires = ["uv_build>=0.11.3,<0.13.0"]
|
|
53
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "blendjob"
|
|
3
|
+
version = "0.1.5"
|
|
4
|
+
description = "Reusable local Job Server runtime for Blender add-ons"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "OmooLab" }]
|
|
10
|
+
keywords = ["blender", "addon", "job", "server", "runtime"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
]
|
|
20
|
+
dependencies = []
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Documentation = "https://docs.omoolab.xyz/blendjob/"
|
|
24
|
+
Issues = "https://github.com/OmooLab/BlendJob/issues"
|
|
25
|
+
Repository = "https://github.com/OmooLab/BlendJob"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
server = [
|
|
29
|
+
"fastapi==0.139.2",
|
|
30
|
+
"uvicorn==0.51.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"fastapi==0.139.2",
|
|
36
|
+
"mike>=2.1,<3",
|
|
37
|
+
"mkdocs-material>=9.6,<10",
|
|
38
|
+
"mkdocstrings[python]>=0.30,<1",
|
|
39
|
+
"pytest>=8.4,<9",
|
|
40
|
+
"uvicorn==0.51.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["uv_build>=0.11.3,<0.13.0"]
|
|
45
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Reusable local HTTP Job Server for Blender add-ons."""
|
|
2
|
+
|
|
3
|
+
from .client import JobClient, JobResult
|
|
4
|
+
from .controller import ServerConnection, ServerController
|
|
5
|
+
from .operator import JobOperatorBase, JobOperatorState
|
|
6
|
+
from .runtime import JobRuntime
|
|
7
|
+
from .server import JobCancelled, JobContext, JobServer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
"JobCancelled",
|
|
12
|
+
"JobClient",
|
|
13
|
+
"JobContext",
|
|
14
|
+
"JobResult",
|
|
15
|
+
"JobServer",
|
|
16
|
+
"JobRuntime",
|
|
17
|
+
"JobOperatorBase",
|
|
18
|
+
"JobOperatorState",
|
|
19
|
+
"ServerConnection",
|
|
20
|
+
"ServerController",
|
|
21
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from urllib.error import HTTPError, URLError
|
|
5
|
+
from urllib.request import ProxyHandler, Request, build_opener
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class JobClient:
|
|
9
|
+
"""Dependency-free HTTP client for a local :class:`JobServer`."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, host, port, *, timeout=2.0):
|
|
12
|
+
self.base_url = f"http://{host}:{int(port)}"
|
|
13
|
+
self.timeout = timeout
|
|
14
|
+
self.opener = build_opener(ProxyHandler({}))
|
|
15
|
+
|
|
16
|
+
def request(self, method, path, payload=None, timeout=None):
|
|
17
|
+
data = None
|
|
18
|
+
headers = {}
|
|
19
|
+
if payload is not None:
|
|
20
|
+
data = json.dumps(payload).encode("utf-8")
|
|
21
|
+
headers["Content-Type"] = "application/json"
|
|
22
|
+
request = Request(
|
|
23
|
+
f"{self.base_url}{path}",
|
|
24
|
+
data=data,
|
|
25
|
+
headers=headers,
|
|
26
|
+
method=method,
|
|
27
|
+
)
|
|
28
|
+
try:
|
|
29
|
+
with self.opener.open(
|
|
30
|
+
request,
|
|
31
|
+
timeout=self.timeout if timeout is None else timeout,
|
|
32
|
+
) as response:
|
|
33
|
+
return json.loads(response.read().decode("utf-8"))
|
|
34
|
+
except HTTPError as error:
|
|
35
|
+
try:
|
|
36
|
+
detail = json.loads(error.read().decode("utf-8")).get("detail")
|
|
37
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
38
|
+
detail = None
|
|
39
|
+
raise RuntimeError(detail or f"Job Server returned HTTP {error.code}")
|
|
40
|
+
except (OSError, URLError) as error:
|
|
41
|
+
raise RuntimeError(f"Unable to reach Job Server: {error}")
|
|
42
|
+
|
|
43
|
+
def health(self):
|
|
44
|
+
return self.request("GET", "/health")
|
|
45
|
+
|
|
46
|
+
def submit(self, job_type, parameters):
|
|
47
|
+
return self.request(
|
|
48
|
+
"POST",
|
|
49
|
+
"/jobs",
|
|
50
|
+
{"job_type": str(job_type), "parameters": dict(parameters)},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def status(self, job_id):
|
|
54
|
+
return self.request("GET", f"/jobs/{job_id}")
|
|
55
|
+
|
|
56
|
+
def cancel(self, job_id):
|
|
57
|
+
return self.request("DELETE", f"/jobs/{job_id}")
|
|
58
|
+
|
|
59
|
+
def resources(self):
|
|
60
|
+
return self.request("GET", "/resources")
|
|
61
|
+
|
|
62
|
+
def resource(self, name):
|
|
63
|
+
return self.request("GET", f"/resources/{name}")
|
|
64
|
+
|
|
65
|
+
def clear_resource(self, name):
|
|
66
|
+
return self.request("POST", f"/resources/{name}/clear")
|
|
67
|
+
|
|
68
|
+
def shutdown(self):
|
|
69
|
+
return self.request("POST", "/shutdown")
|
|
70
|
+
@dataclass(frozen=True)
|
|
71
|
+
class JobResult:
|
|
72
|
+
"""Successful Job value and files returned to a Blender Operator."""
|
|
73
|
+
|
|
74
|
+
job_id: str
|
|
75
|
+
directory: Path
|
|
76
|
+
value: object
|
|
77
|
+
status: dict
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def from_status(cls, status):
|
|
81
|
+
return cls(
|
|
82
|
+
job_id=str(status.get("job_id", "")),
|
|
83
|
+
directory=Path(status["directory"]),
|
|
84
|
+
value=status.get("result"),
|
|
85
|
+
status=dict(status),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def file(self, name):
|
|
89
|
+
if not isinstance(self.value, dict):
|
|
90
|
+
raise RuntimeError("Job result does not contain named files")
|
|
91
|
+
try:
|
|
92
|
+
relative = self.value[name]
|
|
93
|
+
except KeyError:
|
|
94
|
+
raise RuntimeError(f"Job result does not contain file: {name}") from None
|
|
95
|
+
root = self.directory.resolve()
|
|
96
|
+
path = (root / str(relative)).resolve()
|
|
97
|
+
if path != root and root not in path.parents:
|
|
98
|
+
raise RuntimeError(f"Job result file escapes its directory: {relative}")
|
|
99
|
+
if not path.is_file():
|
|
100
|
+
raise RuntimeError(f"Job result file does not exist: {path}")
|
|
101
|
+
return path
|