@deepblend/dsh-blender-provider-local 0.2.8 → 0.2.9
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepblend/dsh-blender-provider-local",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local Blender Provider: the BlenderRuntime Service over ctx.subprocess (batch execution).",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"./package.json": "./package.json"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@deepblend/dsh-blender-contracts": "0.2.
|
|
14
|
+
"@deepblend/dsh-blender-contracts": "0.2.9"
|
|
15
15
|
},
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
Binary file
|
|
@@ -107,10 +107,23 @@ class _Bridge:
|
|
|
107
107
|
# -- lifecycle ---------------------------------------------------------
|
|
108
108
|
|
|
109
109
|
def start(self):
|
|
110
|
+
# THREE STATES, NOT TWO. A socket path that exists is either a LIVE bridge or the file a dead
|
|
111
|
+
# one left behind, and the difference decides everything.
|
|
112
|
+
#
|
|
113
|
+
# MEASURED, and this is why: two workspaces on one machine both enabled the add-on, and the
|
|
114
|
+
# SECOND bridge unlinked the first's socket file, bound its own, and announced `ready` — so the
|
|
115
|
+
# first Blender became unreachable while still believing it was listening, and the second
|
|
116
|
+
# reported success. That is the failure this repository keeps paying for: something other than
|
|
117
|
+
# what was asked, reported as fine.
|
|
110
118
|
if os.path.exists(self.socket_path):
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
if self._someone_is_listening():
|
|
120
|
+
raise RuntimeError(
|
|
121
|
+
"another Blender is already serving %s. Give this one its own path with "
|
|
122
|
+
"DEEPBLEND_BRIDGE_SOCKET (or the add-on's preferences), or stop the other bridge."
|
|
123
|
+
% (self.socket_path,)
|
|
124
|
+
)
|
|
125
|
+
# Nothing answers, so it is the file a process that died left behind. Refusing here would
|
|
126
|
+
# leave the user with no way forward except deleting a file they cannot see.
|
|
114
127
|
os.unlink(self.socket_path)
|
|
115
128
|
self.server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
116
129
|
self.server.bind(self.socket_path)
|
|
@@ -118,6 +131,21 @@ class _Bridge:
|
|
|
118
131
|
self.thread = threading.Thread(target=self._serve, name="deepblend-bridge", daemon=True)
|
|
119
132
|
self.thread.start()
|
|
120
133
|
|
|
134
|
+
def _someone_is_listening(self):
|
|
135
|
+
"""Whether a live bridge is behind this path, rather than a file left by a dead one."""
|
|
136
|
+
probe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
137
|
+
probe.settimeout(1.0)
|
|
138
|
+
try:
|
|
139
|
+
probe.connect(self.socket_path)
|
|
140
|
+
return True
|
|
141
|
+
except Exception:
|
|
142
|
+
return False
|
|
143
|
+
finally:
|
|
144
|
+
try:
|
|
145
|
+
probe.close()
|
|
146
|
+
except Exception:
|
|
147
|
+
pass
|
|
148
|
+
|
|
121
149
|
def stop(self):
|
|
122
150
|
try:
|
|
123
151
|
if self.server is not None:
|
|
@@ -289,7 +317,15 @@ def main():
|
|
|
289
317
|
|
|
290
318
|
global _BRIDGE
|
|
291
319
|
_BRIDGE = _Bridge(socket_path, {"proc": None})
|
|
292
|
-
|
|
320
|
+
try:
|
|
321
|
+
_BRIDGE.start()
|
|
322
|
+
except Exception as exc:
|
|
323
|
+
# A REFUSAL IS A RESULT, NOT A TRACEBACK. The one thing that can go wrong here is a named,
|
|
324
|
+
# actionable condition — another bridge on the same path — and a caller parsing stdout should
|
|
325
|
+
# get a document it can read rather than a stack it has to scrape. The add-on path already
|
|
326
|
+
# reports this in its panel; this is the same sentence for the headless one.
|
|
327
|
+
print(json.dumps({"kind": "error", "message": bootstrap.error_text(exc)}), flush=True)
|
|
328
|
+
return 2
|
|
293
329
|
print(json.dumps({"kind": "ready", "pid": os.getpid(), "socket": socket_path}), flush=True)
|
|
294
330
|
try:
|
|
295
331
|
# The serve thread is a daemon, so this loop is what keeps the process alive. A SIGTERM (or
|