comfy-env 0.0.48__py3-none-any.whl → 0.0.50__py3-none-any.whl
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/__init__.py +7 -0
- comfy_env/decorator.py +256 -2
- comfy_env/env/config.py +2 -0
- comfy_env/env/config_file.py +4 -0
- comfy_env/install.py +38 -104
- comfy_env/isolation.py +297 -0
- comfy_env/pixi.py +124 -24
- comfy_env/stub_imports.py +310 -0
- comfy_env/templates/comfy-env-instructions.txt +103 -0
- comfy_env/templates/comfy-env.toml +186 -0
- comfy_env/wheel_sources.yml +1 -1
- comfy_env/workers/torch_mp.py +5 -1
- comfy_env/workers/venv.py +94 -3
- {comfy_env-0.0.48.dist-info → comfy_env-0.0.50.dist-info}/METADATA +62 -4
- {comfy_env-0.0.48.dist-info → comfy_env-0.0.50.dist-info}/RECORD +18 -14
- {comfy_env-0.0.48.dist-info → comfy_env-0.0.50.dist-info}/WHEEL +0 -0
- {comfy_env-0.0.48.dist-info → comfy_env-0.0.50.dist-info}/entry_points.txt +0 -0
- {comfy_env-0.0.48.dist-info → comfy_env-0.0.50.dist-info}/licenses/LICENSE +0 -0
comfy_env/workers/venv.py
CHANGED
|
@@ -587,6 +587,92 @@ if sys.platform == "win32":
|
|
|
587
587
|
except Exception:
|
|
588
588
|
pass
|
|
589
589
|
|
|
590
|
+
# =============================================================================
|
|
591
|
+
# Object Reference System - keep complex objects in worker, pass refs to host
|
|
592
|
+
# =============================================================================
|
|
593
|
+
|
|
594
|
+
_object_cache = {} # Maps ref_id -> object
|
|
595
|
+
_object_ids = {} # Maps id(obj) -> ref_id (for deduplication)
|
|
596
|
+
_ref_counter = 0
|
|
597
|
+
|
|
598
|
+
def _cache_object(obj):
|
|
599
|
+
"""Store object in cache, return reference ID. Deduplicates by object id."""
|
|
600
|
+
global _ref_counter
|
|
601
|
+
obj_id = id(obj)
|
|
602
|
+
|
|
603
|
+
# Return existing ref if we've seen this object
|
|
604
|
+
if obj_id in _object_ids:
|
|
605
|
+
return _object_ids[obj_id]
|
|
606
|
+
|
|
607
|
+
ref_id = f"ref_{_ref_counter:08x}"
|
|
608
|
+
_ref_counter += 1
|
|
609
|
+
_object_cache[ref_id] = obj
|
|
610
|
+
_object_ids[obj_id] = ref_id
|
|
611
|
+
return ref_id
|
|
612
|
+
|
|
613
|
+
def _resolve_ref(ref_id):
|
|
614
|
+
"""Get object from cache by reference ID."""
|
|
615
|
+
return _object_cache.get(ref_id)
|
|
616
|
+
|
|
617
|
+
def _should_use_reference(obj):
|
|
618
|
+
"""Check if object should be passed by reference instead of value."""
|
|
619
|
+
if obj is None:
|
|
620
|
+
return False
|
|
621
|
+
# Primitives - pass by value
|
|
622
|
+
if isinstance(obj, (bool, int, float, str, bytes)):
|
|
623
|
+
return False
|
|
624
|
+
# NumPy arrays and torch tensors - pass by value (they serialize well)
|
|
625
|
+
obj_type = type(obj).__name__
|
|
626
|
+
if obj_type in ('ndarray', 'Tensor'):
|
|
627
|
+
return False
|
|
628
|
+
# Dicts, lists, tuples - recurse into contents (don't ref the container)
|
|
629
|
+
if isinstance(obj, (dict, list, tuple)):
|
|
630
|
+
return False
|
|
631
|
+
# Everything else (trimesh, custom classes) - pass by reference
|
|
632
|
+
return True
|
|
633
|
+
|
|
634
|
+
def _serialize_result(obj, visited=None):
|
|
635
|
+
"""Convert result for IPC - complex objects become references."""
|
|
636
|
+
if visited is None:
|
|
637
|
+
visited = set()
|
|
638
|
+
|
|
639
|
+
obj_id = id(obj)
|
|
640
|
+
if obj_id in visited:
|
|
641
|
+
# Circular reference - use existing ref or create one
|
|
642
|
+
if obj_id in _object_ids:
|
|
643
|
+
return {"__comfy_ref__": _object_ids[obj_id], "__class__": type(obj).__name__}
|
|
644
|
+
return None # Skip circular refs to primitives
|
|
645
|
+
|
|
646
|
+
if _should_use_reference(obj):
|
|
647
|
+
ref_id = _cache_object(obj)
|
|
648
|
+
return {"__comfy_ref__": ref_id, "__class__": type(obj).__name__}
|
|
649
|
+
|
|
650
|
+
visited.add(obj_id)
|
|
651
|
+
|
|
652
|
+
if isinstance(obj, dict):
|
|
653
|
+
return {k: _serialize_result(v, visited) for k, v in obj.items()}
|
|
654
|
+
if isinstance(obj, list):
|
|
655
|
+
return [_serialize_result(v, visited) for v in obj]
|
|
656
|
+
if isinstance(obj, tuple):
|
|
657
|
+
return tuple(_serialize_result(v, visited) for v in obj)
|
|
658
|
+
return obj
|
|
659
|
+
|
|
660
|
+
def _deserialize_input(obj):
|
|
661
|
+
"""Convert input from IPC - references become real objects."""
|
|
662
|
+
if isinstance(obj, dict):
|
|
663
|
+
if "__comfy_ref__" in obj:
|
|
664
|
+
ref_id = obj["__comfy_ref__"]
|
|
665
|
+
real_obj = _resolve_ref(ref_id)
|
|
666
|
+
if real_obj is None:
|
|
667
|
+
raise ValueError(f"Object reference not found: {ref_id}")
|
|
668
|
+
return real_obj
|
|
669
|
+
return {k: _deserialize_input(v) for k, v in obj.items()}
|
|
670
|
+
if isinstance(obj, list):
|
|
671
|
+
return [_deserialize_input(v) for v in obj]
|
|
672
|
+
if isinstance(obj, tuple):
|
|
673
|
+
return tuple(_deserialize_input(v) for v in obj)
|
|
674
|
+
return obj
|
|
675
|
+
|
|
590
676
|
|
|
591
677
|
class SocketTransport:
|
|
592
678
|
"""Length-prefixed JSON transport."""
|
|
@@ -703,6 +789,8 @@ def main():
|
|
|
703
789
|
if inputs_path:
|
|
704
790
|
inputs = torch.load(inputs_path, weights_only=False)
|
|
705
791
|
inputs = _deserialize_isolated_objects(inputs)
|
|
792
|
+
# Resolve any object references from previous node calls
|
|
793
|
+
inputs = _deserialize_input(inputs)
|
|
706
794
|
else:
|
|
707
795
|
inputs = {}
|
|
708
796
|
|
|
@@ -725,9 +813,10 @@ def main():
|
|
|
725
813
|
func = getattr(module, func_name)
|
|
726
814
|
result = func(**inputs)
|
|
727
815
|
|
|
728
|
-
# Save result
|
|
816
|
+
# Save result - convert complex objects to references
|
|
729
817
|
if outputs_path:
|
|
730
|
-
|
|
818
|
+
serialized_result = _serialize_result(result)
|
|
819
|
+
torch.save(serialized_result, outputs_path)
|
|
731
820
|
|
|
732
821
|
transport.send({"status": "ok"})
|
|
733
822
|
|
|
@@ -912,7 +1001,9 @@ class PersistentVenvWorker(Worker):
|
|
|
912
1001
|
self._transport = SocketTransport(client_sock)
|
|
913
1002
|
|
|
914
1003
|
# Send config
|
|
915
|
-
config = {
|
|
1004
|
+
config = {
|
|
1005
|
+
"sys_paths": all_sys_path,
|
|
1006
|
+
}
|
|
916
1007
|
self._transport.send(config)
|
|
917
1008
|
|
|
918
1009
|
# Wait for ready signal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: comfy-env
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.50
|
|
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
|
|
@@ -84,9 +84,47 @@ from comfy_env import install
|
|
|
84
84
|
install()
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
### Process Isolation (Type 1 - Separate
|
|
87
|
+
### Process Isolation (Type 1 - Separate Environment)
|
|
88
88
|
|
|
89
|
-
For nodes that need completely separate dependencies
|
|
89
|
+
For nodes that need completely separate dependencies (different Python version, conda packages, conflicting libraries).
|
|
90
|
+
|
|
91
|
+
#### Recommended: Pack-Wide Isolation
|
|
92
|
+
|
|
93
|
+
For node packs where ALL nodes run in the same isolated environment:
|
|
94
|
+
|
|
95
|
+
**Step 1: Configure comfy-env.toml**
|
|
96
|
+
|
|
97
|
+
```toml
|
|
98
|
+
[mypack]
|
|
99
|
+
python = "3.11"
|
|
100
|
+
isolated = true # All nodes run in this env
|
|
101
|
+
|
|
102
|
+
[mypack.conda]
|
|
103
|
+
packages = ["cgal"] # Conda packages (uses pixi)
|
|
104
|
+
|
|
105
|
+
[mypack.packages]
|
|
106
|
+
requirements = ["trimesh[easy]>=4.0", "bpy>=4.2"]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Step 2: Enable in __init__.py**
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from comfy_env import setup_isolated_imports, enable_isolation
|
|
113
|
+
|
|
114
|
+
# Setup import stubs BEFORE importing nodes
|
|
115
|
+
setup_isolated_imports(__file__)
|
|
116
|
+
|
|
117
|
+
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
|
118
|
+
|
|
119
|
+
# Enable isolation for all nodes
|
|
120
|
+
enable_isolation(NODE_CLASS_MAPPINGS)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**That's it!** All nodes run in an isolated Python 3.11 environment with their own dependencies.
|
|
124
|
+
|
|
125
|
+
#### Alternative: Per-Node Isolation
|
|
126
|
+
|
|
127
|
+
For cases where different nodes need different environments:
|
|
90
128
|
|
|
91
129
|
```python
|
|
92
130
|
from comfy_env import isolated
|
|
@@ -266,7 +304,27 @@ vars_dict = env.as_dict()
|
|
|
266
304
|
# {'cuda_version': '12.8', 'cuda_short': '128', 'torch_mm': '28', ...}
|
|
267
305
|
```
|
|
268
306
|
|
|
269
|
-
###
|
|
307
|
+
### enable_isolation()
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from comfy_env import enable_isolation
|
|
311
|
+
|
|
312
|
+
enable_isolation(NODE_CLASS_MAPPINGS)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Wraps all node classes so their FUNCTION methods run in the isolated environment specified in comfy-env.toml. Requires `isolated = true` in the environment config.
|
|
316
|
+
|
|
317
|
+
### setup_isolated_imports()
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
from comfy_env import setup_isolated_imports
|
|
321
|
+
|
|
322
|
+
setup_isolated_imports(__file__)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Sets up import stubs for packages that exist only in the isolated pixi environment. Call this BEFORE importing your nodes module. Packages available in both host and isolated environment are not stubbed.
|
|
326
|
+
|
|
327
|
+
### Workers (for custom isolation)
|
|
270
328
|
|
|
271
329
|
```python
|
|
272
330
|
from comfy_env import TorchMPWorker
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
comfy_env/__init__.py,sha256=
|
|
1
|
+
comfy_env/__init__.py,sha256=DvOCFCq-EuXZ_e6vsPCObgTPYSRLZqiG3w35SOD-e4E,4101
|
|
2
2
|
comfy_env/cli.py,sha256=Pjzb-jsoH67lyHxmBFOWvasWX01eHFE_BEjUk1l-uEo,14509
|
|
3
|
-
comfy_env/decorator.py,sha256=
|
|
3
|
+
comfy_env/decorator.py,sha256=bL0eUjXf5UAGbsT2zilZbO0e8ZbhtQAR3vDJLbMO3ZI,27018
|
|
4
4
|
comfy_env/errors.py,sha256=8hN8NDlo8oBUdapc-eT3ZluigI5VBzfqsSBvQdfWlz4,9943
|
|
5
|
-
comfy_env/install.py,sha256=
|
|
5
|
+
comfy_env/install.py,sha256=b2eXFqQ58pUBWpwc1AhymbTh7dGRMnEuq5FoZot0CO0,15661
|
|
6
|
+
comfy_env/isolation.py,sha256=wuze8TmkRbdMfrg2cj9quoGYJWsxKcc5iiDvmLWX16g,8680
|
|
6
7
|
comfy_env/nodes.py,sha256=CWUe35jU5SKk4ur-SddZePdqWgxJDlxGhpcJiu5pAK4,4354
|
|
7
|
-
comfy_env/pixi.py,sha256=
|
|
8
|
+
comfy_env/pixi.py,sha256=iKC1G7HOBLw6sJ3CqZuTwKPtstB5UK8aj7ID3IgjLzM,19248
|
|
8
9
|
comfy_env/registry.py,sha256=w-QwvAPFlCrBYRAv4cXkp2zujQPZn8Fk5DUxKCtox8o,3430
|
|
9
10
|
comfy_env/resolver.py,sha256=WoNIo2IfTR2RlEf_HQl66eAeMa2R2pmLof_UdK-0RNE,6714
|
|
11
|
+
comfy_env/stub_imports.py,sha256=s84q8x5156ZkHNJhbuDqMhU_c-0ux51Rhe1aoVcGrj4,8920
|
|
10
12
|
comfy_env/env/__init__.py,sha256=imQdoQEQvrRT-QDtyNpFlkVbm2fBzgACdpQwRPd09fI,1157
|
|
11
|
-
comfy_env/env/config.py,sha256=
|
|
12
|
-
comfy_env/env/config_file.py,sha256=
|
|
13
|
+
comfy_env/env/config.py,sha256=cRSuWiVZGSK9xpvyNIyMirlHyfTm6_AB_eXzeKyvfnc,7749
|
|
14
|
+
comfy_env/env/config_file.py,sha256=g6fZq6XbTUXx97meWW_7xfKcuC0sN002c7fBvR0eCOg,24819
|
|
13
15
|
comfy_env/env/cuda_gpu_detection.py,sha256=YLuXUdWg6FeKdNyLlQAHPlveg4rTenXJ2VbeAaEi9QE,9755
|
|
14
16
|
comfy_env/env/manager.py,sha256=-qdbZDsbNfs70onVbC7mhKCzNsxYx3WmG7ttlBinhGI,23659
|
|
15
17
|
comfy_env/env/security.py,sha256=dNSitAnfBNVdvxgBBntYw33AJaCs_S1MHb7KJhAVYzM,8171
|
|
@@ -30,15 +32,17 @@ comfy_env/stubs/folder_paths.py,sha256=K90J34EG6LD4eZP8YG-xMeBmqwpp_wA8E92DKMXd1
|
|
|
30
32
|
comfy_env/stubs/comfy/__init__.py,sha256=-y4L6gX21vrI2V8MvNaMeHOcAn5kUNK3jUyLvtXRmJQ,173
|
|
31
33
|
comfy_env/stubs/comfy/model_management.py,sha256=Khx8Qa3NutKPLTn9oSM3VLeATUOg1fe4QCjxdxXd6eE,1462
|
|
32
34
|
comfy_env/stubs/comfy/utils.py,sha256=s3t_KLj_-w1Uj3A3iAy69wIk4Ggklojw5hsDNb69Pcc,776
|
|
35
|
+
comfy_env/templates/comfy-env-instructions.txt,sha256=Q38Hb_YdN0a8rTxn7l5ON4JDPba7XgVftDqfEy-8I2I,3004
|
|
36
|
+
comfy_env/templates/comfy-env.toml,sha256=v6nxvCWWKSlIpn4m-WqCkzJt4ObJsklbr9KBJt0r5fU,6729
|
|
33
37
|
comfy_env/workers/__init__.py,sha256=IKZwOvrWOGqBLDUIFAalg4CdqzJ_YnAdxo2Ha7gZTJ0,1467
|
|
34
38
|
comfy_env/workers/base.py,sha256=ZILYXlvGCWuCZXmjKqfG8VeD19ihdYaASdlbasl2BMo,2312
|
|
35
39
|
comfy_env/workers/pool.py,sha256=MtjeOWfvHSCockq8j1gfnxIl-t01GSB79T5N4YB82Lg,6956
|
|
36
40
|
comfy_env/workers/tensor_utils.py,sha256=TCuOAjJymrSbkgfyvcKtQ_KbVWTqSwP9VH_bCaFLLq8,6409
|
|
37
|
-
comfy_env/workers/torch_mp.py,sha256=
|
|
38
|
-
comfy_env/workers/venv.py,sha256=
|
|
39
|
-
comfy_env/wheel_sources.yml,sha256=
|
|
40
|
-
comfy_env-0.0.
|
|
41
|
-
comfy_env-0.0.
|
|
42
|
-
comfy_env-0.0.
|
|
43
|
-
comfy_env-0.0.
|
|
44
|
-
comfy_env-0.0.
|
|
41
|
+
comfy_env/workers/torch_mp.py,sha256=TnsCoBHEJBXEoBkx7WiCd9tBAlzFtMOw1dk_7_zGJZY,22288
|
|
42
|
+
comfy_env/workers/venv.py,sha256=GFixbBMJkcjEkd8x1ry9mX13cR5mgUukQjIZjvK44ko,42381
|
|
43
|
+
comfy_env/wheel_sources.yml,sha256=uU0YJmWaiLAicQNN9VYS8PZevlP2NOH6mBUE294dvAo,8156
|
|
44
|
+
comfy_env-0.0.50.dist-info/METADATA,sha256=HTNNXsxXnyr7JORwl0Ts3futX9xTrLEFstD3vpRepMc,8735
|
|
45
|
+
comfy_env-0.0.50.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
46
|
+
comfy_env-0.0.50.dist-info/entry_points.txt,sha256=J4fXeqgxU_YenuW_Zxn_pEL7J-3R0--b6MS5t0QmAr0,49
|
|
47
|
+
comfy_env-0.0.50.dist-info/licenses/LICENSE,sha256=E68QZMMpW4P2YKstTZ3QU54HRQO8ecew09XZ4_Vn870,1093
|
|
48
|
+
comfy_env-0.0.50.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|