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/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
- torch.save(result, outputs_path)
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 = {"sys_paths": all_sys_path}
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.48
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 Venv)
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
- ### Workers (for isolation)
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=OQJFNjmArjLcgrfHAFxgDJQFH_IhxibqMXbU5bu_j9Q,3822
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=6JCKwLHaZtOLVDexs_gh_-NtS2ZK0V7nGCPqkyeYEAA,16688
3
+ comfy_env/decorator.py,sha256=bL0eUjXf5UAGbsT2zilZbO0e8ZbhtQAR3vDJLbMO3ZI,27018
4
4
  comfy_env/errors.py,sha256=8hN8NDlo8oBUdapc-eT3ZluigI5VBzfqsSBvQdfWlz4,9943
5
- comfy_env/install.py,sha256=m4NKlfCcQGI5xzVRjHw3ep-lWbqx5kE1e21sUUZ2Leo,17528
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=VyPYL6hV7rxMUJcUl-8UobwTpOTzaSEmvRKT8v5a0vo,15250
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=Ila-5Yal3bj6jENbBeYJlZtkbgdwnzJzImVZK3ZF1lg,7645
12
- comfy_env/env/config_file.py,sha256=HzFKeQh9zQ--K1V-XuvgE6DiE_bYrXrChL1ZT8Tzlq4,24684
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=4YSNPn7hALrvMVbkO4RkTeFTcc0lhfLMk5QTWjY4PHw,22134
38
- comfy_env/workers/venv.py,sha256=9xP3Y_ZoegMMCnBuRrd-mlIEKRF4oE01zL_8bngRuCk,38916
39
- comfy_env/wheel_sources.yml,sha256=K5dksy21YcT7QdFlVDkKF4Rv9ZCjHaWhQgoEhdSyAOI,8156
40
- comfy_env-0.0.48.dist-info/METADATA,sha256=a8hWh2c1hJZt2WkfR4giYSjl86bBTDuXYWB0Mn2z-8E,7138
41
- comfy_env-0.0.48.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
42
- comfy_env-0.0.48.dist-info/entry_points.txt,sha256=J4fXeqgxU_YenuW_Zxn_pEL7J-3R0--b6MS5t0QmAr0,49
43
- comfy_env-0.0.48.dist-info/licenses/LICENSE,sha256=E68QZMMpW4P2YKstTZ3QU54HRQO8ecew09XZ4_Vn870,1093
44
- comfy_env-0.0.48.dist-info/RECORD,,
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,,