comfy-env 0.0.50__tar.gz → 0.0.51__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.
- {comfy_env-0.0.50 → comfy_env-0.0.51}/PKG-INFO +1 -1
- {comfy_env-0.0.50 → comfy_env-0.0.51}/pyproject.toml +1 -1
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/pixi.py +7 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/workers/venv.py +88 -10
- {comfy_env-0.0.50 → comfy_env-0.0.51}/.github/workflows/publish.yml +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/.gitignore +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/LICENSE +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/README.md +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/__init__.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/cli.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/decorator.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/__init__.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/config.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/config_file.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/cuda_gpu_detection.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/manager.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/platform/__init__.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/platform/base.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/platform/darwin.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/platform/linux.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/platform/windows.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/env/security.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/errors.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/install.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/ipc/__init__.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/ipc/bridge.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/ipc/protocol.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/ipc/tensor.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/ipc/torch_bridge.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/ipc/transport.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/ipc/worker.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/isolation.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/nodes.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/registry.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/resolver.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/stub_imports.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/stubs/__init__.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/stubs/comfy/__init__.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/stubs/comfy/model_management.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/stubs/comfy/utils.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/stubs/folder_paths.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/templates/comfy-env-instructions.txt +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/templates/comfy-env.toml +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/wheel_sources.yml +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/workers/__init__.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/workers/base.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/workers/pool.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/workers/tensor_utils.py +0 -0
- {comfy_env-0.0.50 → comfy_env-0.0.51}/src/comfy_env/workers/torch_mp.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: comfy-env
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.51
|
|
4
4
|
Summary: Environment management for ComfyUI custom nodes - CUDA wheel resolution and process isolation
|
|
5
5
|
Project-URL: Homepage, https://github.com/PozzettiAndrea/comfy-env
|
|
6
6
|
Project-URL: Repository, https://github.com/PozzettiAndrea/comfy-env
|
|
@@ -233,6 +233,13 @@ def create_pixi_toml(
|
|
|
233
233
|
elif sys.platform == "win32":
|
|
234
234
|
lines.append('platforms = ["win-64"]')
|
|
235
235
|
|
|
236
|
+
# System requirements - specify glibc version for proper wheel resolution
|
|
237
|
+
# Ubuntu 22.04+ has glibc 2.35, enabling manylinux_2_35 wheels
|
|
238
|
+
if sys.platform == "linux":
|
|
239
|
+
lines.append("")
|
|
240
|
+
lines.append("[system-requirements]")
|
|
241
|
+
lines.append('libc = { family = "glibc", version = "2.35" }')
|
|
242
|
+
|
|
236
243
|
lines.append("")
|
|
237
244
|
|
|
238
245
|
# Dependencies section (conda packages)
|
|
@@ -572,8 +572,13 @@ import json
|
|
|
572
572
|
import socket
|
|
573
573
|
import struct
|
|
574
574
|
import traceback
|
|
575
|
+
import faulthandler
|
|
575
576
|
from types import SimpleNamespace
|
|
576
577
|
|
|
578
|
+
# Enable faulthandler to dump traceback on SIGSEGV/SIGABRT/etc
|
|
579
|
+
faulthandler.enable(file=sys.stderr, all_threads=True)
|
|
580
|
+
print("[worker] Faulthandler enabled", flush=True)
|
|
581
|
+
|
|
577
582
|
# On Windows, add host Python's DLL directories so packages like opencv can find VC++ runtime
|
|
578
583
|
if sys.platform == "win32":
|
|
579
584
|
_host_python_dir = os.environ.get("COMFYUI_HOST_PYTHON_DIR")
|
|
@@ -741,20 +746,24 @@ def _deserialize_isolated_objects(obj):
|
|
|
741
746
|
|
|
742
747
|
|
|
743
748
|
def main():
|
|
749
|
+
print("[worker] Starting...", flush=True)
|
|
744
750
|
# Get socket address from command line
|
|
745
751
|
if len(sys.argv) < 2:
|
|
746
752
|
print("Usage: worker.py <socket_addr>", file=sys.stderr)
|
|
747
753
|
sys.exit(1)
|
|
748
754
|
socket_addr = sys.argv[1]
|
|
755
|
+
print(f"[worker] Connecting to {socket_addr}...", flush=True)
|
|
749
756
|
|
|
750
757
|
# Connect to host process
|
|
751
758
|
sock = _connect(socket_addr)
|
|
752
759
|
transport = SocketTransport(sock)
|
|
760
|
+
print("[worker] Connected, waiting for config...", flush=True)
|
|
753
761
|
|
|
754
762
|
# Read config as first message
|
|
755
763
|
config = transport.recv()
|
|
756
764
|
if not config:
|
|
757
765
|
return
|
|
766
|
+
print("[worker] Got config, setting up paths...", flush=True)
|
|
758
767
|
|
|
759
768
|
# Setup sys.path
|
|
760
769
|
for p in config.get("sys_paths", []):
|
|
@@ -762,10 +771,13 @@ def main():
|
|
|
762
771
|
sys.path.insert(0, p)
|
|
763
772
|
|
|
764
773
|
# Import torch after path setup
|
|
774
|
+
print("[worker] Importing torch...", flush=True)
|
|
765
775
|
import torch
|
|
776
|
+
print(f"[worker] Torch imported: {torch.__version__}", flush=True)
|
|
766
777
|
|
|
767
778
|
# Signal ready
|
|
768
779
|
transport.send({"status": "ready"})
|
|
780
|
+
print("[worker] Ready, entering request loop...", flush=True)
|
|
769
781
|
|
|
770
782
|
# Process requests
|
|
771
783
|
while True:
|
|
@@ -784,30 +796,41 @@ def main():
|
|
|
784
796
|
module_name = request["module"]
|
|
785
797
|
inputs_path = request.get("inputs_path")
|
|
786
798
|
outputs_path = request.get("outputs_path")
|
|
799
|
+
print(f"[worker] Request: {request_type} {module_name}", flush=True)
|
|
787
800
|
|
|
788
801
|
# Load inputs
|
|
789
802
|
if inputs_path:
|
|
803
|
+
print(f"[worker] Loading inputs from {inputs_path}...", flush=True)
|
|
790
804
|
inputs = torch.load(inputs_path, weights_only=False)
|
|
805
|
+
print(f"[worker] Deserializing isolated objects...", flush=True)
|
|
791
806
|
inputs = _deserialize_isolated_objects(inputs)
|
|
792
807
|
# Resolve any object references from previous node calls
|
|
808
|
+
print(f"[worker] Resolving object references...", flush=True)
|
|
793
809
|
inputs = _deserialize_input(inputs)
|
|
810
|
+
print(f"[worker] Inputs ready: {list(inputs.keys())}", flush=True)
|
|
794
811
|
else:
|
|
795
812
|
inputs = {}
|
|
796
813
|
|
|
797
814
|
# Import module
|
|
815
|
+
print(f"[worker] Importing module {module_name}...", flush=True)
|
|
798
816
|
module = __import__(module_name, fromlist=[""])
|
|
817
|
+
print(f"[worker] Module imported", flush=True)
|
|
799
818
|
|
|
800
819
|
if request_type == "call_method":
|
|
801
820
|
class_name = request["class_name"]
|
|
802
821
|
method_name = request["method_name"]
|
|
803
822
|
self_state = request.get("self_state")
|
|
823
|
+
print(f"[worker] Getting class {class_name}...", flush=True)
|
|
804
824
|
|
|
805
825
|
cls = getattr(module, class_name)
|
|
826
|
+
print(f"[worker] Creating instance...", flush=True)
|
|
806
827
|
instance = object.__new__(cls)
|
|
807
828
|
if self_state:
|
|
808
829
|
instance.__dict__.update(self_state)
|
|
830
|
+
print(f"[worker] Calling {method_name}...", flush=True)
|
|
809
831
|
method = getattr(instance, method_name)
|
|
810
832
|
result = method(**inputs)
|
|
833
|
+
print(f"[worker] Method returned", flush=True)
|
|
811
834
|
else:
|
|
812
835
|
func_name = request["func"]
|
|
813
836
|
func = getattr(module, func_name)
|
|
@@ -892,6 +915,10 @@ class PersistentVenvWorker(Worker):
|
|
|
892
915
|
self._socket_addr: Optional[str] = None
|
|
893
916
|
self._transport: Optional[SocketTransport] = None
|
|
894
917
|
|
|
918
|
+
# Stderr buffer for crash diagnostics
|
|
919
|
+
self._stderr_buffer: List[str] = []
|
|
920
|
+
self._stderr_lock = threading.Lock()
|
|
921
|
+
|
|
895
922
|
# Write worker script to temp file
|
|
896
923
|
self._worker_script = self._temp_dir / "persistent_worker.py"
|
|
897
924
|
self._worker_script.write_text(_PERSISTENT_WORKER_SCRIPT)
|
|
@@ -964,13 +991,17 @@ class PersistentVenvWorker(Worker):
|
|
|
964
991
|
[str(self.python), str(self._worker_script), self._socket_addr],
|
|
965
992
|
stdin=subprocess.DEVNULL,
|
|
966
993
|
stdout=subprocess.PIPE,
|
|
967
|
-
stderr=subprocess.
|
|
994
|
+
stderr=subprocess.PIPE, # Capture stderr separately for crash diagnostics
|
|
968
995
|
cwd=str(self.working_dir),
|
|
969
996
|
env=env,
|
|
970
997
|
)
|
|
971
998
|
|
|
972
|
-
#
|
|
973
|
-
|
|
999
|
+
# Clear stderr buffer for new process
|
|
1000
|
+
with self._stderr_lock:
|
|
1001
|
+
self._stderr_buffer.clear()
|
|
1002
|
+
|
|
1003
|
+
# Start stdout forwarding thread
|
|
1004
|
+
def forward_stdout():
|
|
974
1005
|
try:
|
|
975
1006
|
for line in self._process.stdout:
|
|
976
1007
|
if isinstance(line, bytes):
|
|
@@ -979,22 +1010,43 @@ class PersistentVenvWorker(Worker):
|
|
|
979
1010
|
sys.stderr.flush()
|
|
980
1011
|
except:
|
|
981
1012
|
pass
|
|
982
|
-
self.
|
|
983
|
-
self.
|
|
1013
|
+
self._stdout_thread = threading.Thread(target=forward_stdout, daemon=True)
|
|
1014
|
+
self._stdout_thread.start()
|
|
1015
|
+
|
|
1016
|
+
# Start stderr capture thread (buffer for crash diagnostics)
|
|
1017
|
+
def capture_stderr():
|
|
1018
|
+
try:
|
|
1019
|
+
for line in self._process.stderr:
|
|
1020
|
+
if isinstance(line, bytes):
|
|
1021
|
+
line = line.decode('utf-8', errors='replace')
|
|
1022
|
+
# Print to terminal AND buffer for crash reporting
|
|
1023
|
+
sys.stderr.write(f" [stderr] {line}")
|
|
1024
|
+
sys.stderr.flush()
|
|
1025
|
+
with self._stderr_lock:
|
|
1026
|
+
self._stderr_buffer.append(line.rstrip())
|
|
1027
|
+
# Keep last 50 lines
|
|
1028
|
+
if len(self._stderr_buffer) > 50:
|
|
1029
|
+
self._stderr_buffer.pop(0)
|
|
1030
|
+
except:
|
|
1031
|
+
pass
|
|
1032
|
+
self._stderr_thread = threading.Thread(target=capture_stderr, daemon=True)
|
|
1033
|
+
self._stderr_thread.start()
|
|
984
1034
|
|
|
985
1035
|
# Accept connection from worker with timeout
|
|
986
1036
|
self._server_socket.settimeout(60)
|
|
987
1037
|
try:
|
|
988
1038
|
client_sock, _ = self._server_socket.accept()
|
|
989
1039
|
except socket.timeout:
|
|
990
|
-
stderr
|
|
1040
|
+
# Collect stderr from buffer
|
|
1041
|
+
time.sleep(0.2) # Give stderr thread time to capture
|
|
1042
|
+
with self._stderr_lock:
|
|
1043
|
+
stderr = "\n".join(self._stderr_buffer) if self._stderr_buffer else "(no stderr captured)"
|
|
991
1044
|
try:
|
|
992
1045
|
self._process.kill()
|
|
993
|
-
|
|
994
|
-
stderr = stdout.decode('utf-8', errors='replace') if stdout else ""
|
|
1046
|
+
self._process.wait(timeout=5)
|
|
995
1047
|
except:
|
|
996
1048
|
pass
|
|
997
|
-
raise RuntimeError(f"{self.name}: Worker failed to connect (timeout)
|
|
1049
|
+
raise RuntimeError(f"{self.name}: Worker failed to connect (timeout).\nStderr:\n{stderr}")
|
|
998
1050
|
finally:
|
|
999
1051
|
self._server_socket.settimeout(None)
|
|
1000
1052
|
|
|
@@ -1035,7 +1087,33 @@ class PersistentVenvWorker(Worker):
|
|
|
1035
1087
|
self._transport.send(request)
|
|
1036
1088
|
|
|
1037
1089
|
# Read response with timeout
|
|
1038
|
-
|
|
1090
|
+
try:
|
|
1091
|
+
response = self._transport.recv(timeout=timeout)
|
|
1092
|
+
except ConnectionError as e:
|
|
1093
|
+
# Socket closed - check if worker process died
|
|
1094
|
+
self._shutdown = True
|
|
1095
|
+
time.sleep(0.2) # Give process time to fully exit and stderr to flush
|
|
1096
|
+
exit_code = None
|
|
1097
|
+
if self._process:
|
|
1098
|
+
exit_code = self._process.poll()
|
|
1099
|
+
|
|
1100
|
+
# Get captured stderr
|
|
1101
|
+
with self._stderr_lock:
|
|
1102
|
+
stderr_output = "\n".join(self._stderr_buffer) if self._stderr_buffer else "(no stderr captured)"
|
|
1103
|
+
|
|
1104
|
+
if exit_code is not None:
|
|
1105
|
+
raise RuntimeError(
|
|
1106
|
+
f"{self.name}: Worker process died with exit code {exit_code}. "
|
|
1107
|
+
f"This usually indicates a crash in native code (CGAL, pymeshlab, etc.).\n"
|
|
1108
|
+
f"Stderr:\n{stderr_output}"
|
|
1109
|
+
) from e
|
|
1110
|
+
else:
|
|
1111
|
+
# Process still alive but socket closed - something weird
|
|
1112
|
+
raise RuntimeError(
|
|
1113
|
+
f"{self.name}: Socket closed but worker process still running. "
|
|
1114
|
+
f"This may indicate a protocol error or worker bug.\n"
|
|
1115
|
+
f"Stderr:\n{stderr_output}"
|
|
1116
|
+
) from e
|
|
1039
1117
|
|
|
1040
1118
|
if response is None:
|
|
1041
1119
|
# Timeout - kill process
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|