pyisolate 0.9.1__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.
Files changed (79) hide show
  1. pyisolate-0.9.1/LICENSE +21 -0
  2. pyisolate-0.9.1/MANIFEST.in +8 -0
  3. pyisolate-0.9.1/PKG-INFO +559 -0
  4. pyisolate-0.9.1/README.md +502 -0
  5. pyisolate-0.9.1/pyisolate/__init__.py +75 -0
  6. pyisolate-0.9.1/pyisolate/_internal/__init__.py +5 -0
  7. pyisolate-0.9.1/pyisolate/_internal/adapter_registry.py +45 -0
  8. pyisolate-0.9.1/pyisolate/_internal/bootstrap.py +145 -0
  9. pyisolate-0.9.1/pyisolate/_internal/client.py +160 -0
  10. pyisolate-0.9.1/pyisolate/_internal/environment.py +406 -0
  11. pyisolate-0.9.1/pyisolate/_internal/host.py +494 -0
  12. pyisolate-0.9.1/pyisolate/_internal/model_serialization.py +158 -0
  13. pyisolate-0.9.1/pyisolate/_internal/remote_handle.py +31 -0
  14. pyisolate-0.9.1/pyisolate/_internal/rpc_protocol.py +692 -0
  15. pyisolate-0.9.1/pyisolate/_internal/rpc_serialization.py +346 -0
  16. pyisolate-0.9.1/pyisolate/_internal/rpc_transports.py +466 -0
  17. pyisolate-0.9.1/pyisolate/_internal/sandbox.py +350 -0
  18. pyisolate-0.9.1/pyisolate/_internal/sandbox_detect.py +326 -0
  19. pyisolate-0.9.1/pyisolate/_internal/serialization_registry.py +63 -0
  20. pyisolate-0.9.1/pyisolate/_internal/singleton_context.py +53 -0
  21. pyisolate-0.9.1/pyisolate/_internal/socket_utils.py +43 -0
  22. pyisolate-0.9.1/pyisolate/_internal/tensor_serializer.py +503 -0
  23. pyisolate-0.9.1/pyisolate/_internal/torch_gate.py +26 -0
  24. pyisolate-0.9.1/pyisolate/_internal/torch_utils.py +51 -0
  25. pyisolate-0.9.1/pyisolate/_internal/uds_client.py +249 -0
  26. pyisolate-0.9.1/pyisolate/config.py +71 -0
  27. pyisolate-0.9.1/pyisolate/host.py +87 -0
  28. pyisolate-0.9.1/pyisolate/interfaces.py +87 -0
  29. pyisolate-0.9.1/pyisolate/path_helpers.py +108 -0
  30. pyisolate-0.9.1/pyisolate/shared.py +49 -0
  31. pyisolate-0.9.1/pyisolate.egg-info/PKG-INFO +559 -0
  32. pyisolate-0.9.1/pyisolate.egg-info/SOURCES.txt +77 -0
  33. pyisolate-0.9.1/pyisolate.egg-info/dependency_links.txt +1 -0
  34. pyisolate-0.9.1/pyisolate.egg-info/requires.txt +41 -0
  35. pyisolate-0.9.1/pyisolate.egg-info/top_level.txt +1 -0
  36. pyisolate-0.9.1/pyproject.toml +135 -0
  37. pyisolate-0.9.1/setup.cfg +4 -0
  38. pyisolate-0.9.1/setup.py +12 -0
  39. pyisolate-0.9.1/tests/__init__.py +1 -0
  40. pyisolate-0.9.1/tests/conftest.py +114 -0
  41. pyisolate-0.9.1/tests/fixtures/__init__.py +5 -0
  42. pyisolate-0.9.1/tests/fixtures/test_adapter.py +301 -0
  43. pyisolate-0.9.1/tests/harness/__init__.py +0 -0
  44. pyisolate-0.9.1/tests/harness/host.py +213 -0
  45. pyisolate-0.9.1/tests/harness/test_package/__init__.py +100 -0
  46. pyisolate-0.9.1/tests/integration_v2/conftest.py +14 -0
  47. pyisolate-0.9.1/tests/integration_v2/debug_rpc.py +26 -0
  48. pyisolate-0.9.1/tests/integration_v2/test_isolation.py +84 -0
  49. pyisolate-0.9.1/tests/integration_v2/test_lifecycle.py +43 -0
  50. pyisolate-0.9.1/tests/integration_v2/test_tensors.py +79 -0
  51. pyisolate-0.9.1/tests/path_unification/__init__.py +1 -0
  52. pyisolate-0.9.1/tests/path_unification/test_path_helpers.py +241 -0
  53. pyisolate-0.9.1/tests/test_adapter_contract.py +239 -0
  54. pyisolate-0.9.1/tests/test_bootstrap.py +85 -0
  55. pyisolate-0.9.1/tests/test_bootstrap_additional.py +55 -0
  56. pyisolate-0.9.1/tests/test_bwrap_command.py +461 -0
  57. pyisolate-0.9.1/tests/test_client_entrypoint_extra.py +206 -0
  58. pyisolate-0.9.1/tests/test_config_validation.py +212 -0
  59. pyisolate-0.9.1/tests/test_extension_lifecycle.py +241 -0
  60. pyisolate-0.9.1/tests/test_extension_safety.py +57 -0
  61. pyisolate-0.9.1/tests/test_fail_loud.py +35 -0
  62. pyisolate-0.9.1/tests/test_host_integration.py +52 -0
  63. pyisolate-0.9.1/tests/test_host_internal_ext.py +282 -0
  64. pyisolate-0.9.1/tests/test_host_public.py +116 -0
  65. pyisolate-0.9.1/tests/test_memory_leaks.py +270 -0
  66. pyisolate-0.9.1/tests/test_path_helpers_contract.py +123 -0
  67. pyisolate-0.9.1/tests/test_remote_handle.py +31 -0
  68. pyisolate-0.9.1/tests/test_rpc_contract.py +183 -0
  69. pyisolate-0.9.1/tests/test_rpc_message_format.py +204 -0
  70. pyisolate-0.9.1/tests/test_rpc_shutdown.py +136 -0
  71. pyisolate-0.9.1/tests/test_sandbox_detect.py +450 -0
  72. pyisolate-0.9.1/tests/test_security.py +160 -0
  73. pyisolate-0.9.1/tests/test_serialization_contract.py +239 -0
  74. pyisolate-0.9.1/tests/test_serialization_registry.py +31 -0
  75. pyisolate-0.9.1/tests/test_shared_additional.py +138 -0
  76. pyisolate-0.9.1/tests/test_singleton_lifecycle.py +331 -0
  77. pyisolate-0.9.1/tests/test_singleton_shared.py +140 -0
  78. pyisolate-0.9.1/tests/test_torch_optional_contract.py +112 -0
  79. pyisolate-0.9.1/tests/test_torch_utils_additional.py +26 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Comfy-Org
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.
@@ -0,0 +1,8 @@
1
+ include LICENSE
2
+ include README.md
3
+ include pyproject.toml
4
+ recursive-include pyisolate *.py
5
+ recursive-include tests *.py
6
+ prune tests/.test_temps
7
+ recursive-exclude * __pycache__
8
+ recursive-exclude * *.py[co]
@@ -0,0 +1,559 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyisolate
3
+ Version: 0.9.1
4
+ Summary: A Python library for dividing execution across multiple virtual environments
5
+ Author-email: Jacob Segal <jacob.e.segal@gmail.com>
6
+ Maintainer-email: Jacob Segal <jacob.e.segal@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/Comfy-Org/pyisolate
9
+ Project-URL: Bug Reports, https://github.com/Comfy-Org/pyisolate/issues
10
+ Project-URL: Source, https://github.com/Comfy-Org/pyisolate
11
+ Keywords: virtual environment,venv,development
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: uv>=0.1.0
22
+ Requires-Dist: tomli>=2.0.1; python_version < "3.11"
23
+ Provides-Extra: dev
24
+ Requires-Dist: build>=1.2.2; extra == "dev"
25
+ Requires-Dist: twine>=5.1.1; extra == "dev"
26
+ Requires-Dist: pytest>=7.0; extra == "dev"
27
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
29
+ Requires-Dist: mypy>=1.0; extra == "dev"
30
+ Requires-Dist: pre-commit>=3.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.11.0; extra == "dev"
32
+ Requires-Dist: pyyaml>=5.4.0; extra == "dev"
33
+ Requires-Dist: importlib-metadata>=4.0.0; extra == "dev"
34
+ Provides-Extra: test
35
+ Requires-Dist: numpy<2.0.0,>=1.26.0; extra == "test"
36
+ Requires-Dist: psutil>=5.9.0; extra == "test"
37
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
38
+ Requires-Dist: pytest>=7.0; extra == "test"
39
+ Requires-Dist: pyyaml>=5.4.0; extra == "test"
40
+ Requires-Dist: tabulate>=0.9.0; extra == "test"
41
+ Requires-Dist: torch>=2.0.0; extra == "test"
42
+ Provides-Extra: bench
43
+ Requires-Dist: numpy<2.0.0,>=1.26.0; extra == "bench"
44
+ Requires-Dist: nvidia-ml-py3>=7.352.0; extra == "bench"
45
+ Requires-Dist: psutil>=5.9.0; extra == "bench"
46
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "bench"
47
+ Requires-Dist: pytest>=7.0; extra == "bench"
48
+ Requires-Dist: pyyaml>=5.4.0; extra == "bench"
49
+ Requires-Dist: tabulate>=0.9.0; extra == "bench"
50
+ Requires-Dist: torch>=2.0.0; extra == "bench"
51
+ Provides-Extra: docs
52
+ Requires-Dist: sphinx>=5.0; extra == "docs"
53
+ Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
54
+ Requires-Dist: myst-parser>=2.0; extra == "docs"
55
+ Requires-Dist: sphinx-markdown-builder>=0.5.4; extra == "docs"
56
+ Dynamic: license-file
57
+
58
+ # pyisolate
59
+
60
+ **Run Python extensions in isolated virtual environments with seamless inter-process communication.**
61
+
62
+ > 🚨 **Fail Loud Policy**: pyisolate assumes the rest of ComfyUI core is correct. Missing prerequisites or runtime failures immediately raise descriptive exceptions instead of being silently ignored.
63
+
64
+ pyisolate enables you to run Python extensions with conflicting dependencies in the same application by automatically creating isolated virtual environments for each extension using `uv`. Extensions communicate with the host process through a transparent RPC system, making the isolation invisible to your code while keeping the host environment dependency-free.
65
+
66
+ ## Requirements
67
+
68
+ - Python 3.9+
69
+ - The [`uv`](https://github.com/astral-sh/uv) CLI available on your `PATH`
70
+ - `pip`/`venv` for bootstrapping the development environment
71
+ - PyTorch is optional and only required for tensor-sharing features (for example, `share_torch=True`)
72
+
73
+ If you want tensor-sharing features, install PyTorch separately (for example: `pip install torch`).
74
+
75
+ ## Environment Variables
76
+
77
+ PyIsolate uses several environment variables for configuration and debugging:
78
+
79
+ ### Core Variables (Set by PyIsolate automatically)
80
+ - **`PYISOLATE_CHILD`**: Set to `"1"` in isolated child processes. Used to detect if code is running in host or child.
81
+ - **`PYISOLATE_HOST_SNAPSHOT`**: Path to JSON file containing the host's `sys.path` and environment variables. Used during child process initialization.
82
+ - **`PYISOLATE_MODULE_PATH`**: Path to the extension module being loaded. Used to detect ComfyUI root directory.
83
+
84
+ ### Debug Variables (Set by user)
85
+ - **`PYISOLATE_PATH_DEBUG`**: Set to `"1"` to enable detailed sys.path logging during child process initialization. Useful for debugging import issues.
86
+
87
+ Example usage:
88
+ ```bash
89
+ # Enable detailed path logging
90
+ export PYISOLATE_PATH_DEBUG=1
91
+ python main.py
92
+
93
+ # Disable path logging (default)
94
+ unset PYISOLATE_PATH_DEBUG
95
+ python main.py
96
+ ```
97
+
98
+ ## Quick Start
99
+
100
+ ### Option A – run everything for me
101
+
102
+ ```bash
103
+ cd /path/to/pyisolate
104
+ ./quickstart.sh
105
+ ```
106
+
107
+ The script installs `uv`, creates the dev venv, installs pyisolate in editable mode, runs the multi-extension example, and executes the Comfy Hello World demo.
108
+
109
+ ### Option B – manual setup (5 minutes)
110
+
111
+ 1. **Create the dev environment**
112
+ ```bash
113
+ cd /path/to/pyisolate
114
+ uv venv
115
+ source .venv/bin/activate # Windows: .venv\\Scripts\\activate
116
+ uv pip install -e ".[dev]"
117
+ ```
118
+ 2. **Run the example extensions**
119
+ ```bash
120
+ cd example
121
+ python main.py
122
+ cd ..
123
+ ```
124
+ Expected output:
125
+ ```
126
+ Extension1 | ✓ PASSED | Data processing with pandas/numpy 1.x
127
+ Extension2 | ✓ PASSED | Array processing with numpy 2.x
128
+ Extension3 | ✓ PASSED | HTML parsing with BeautifulSoup/scipy
129
+ ```
130
+ 3. **Run the Comfy Hello World**
131
+ ```bash
132
+ cd comfy_hello_world
133
+ python main.py
134
+ ```
135
+ You should see the isolated custom node load, execute, and fetch data from the shared singleton service.
136
+
137
+ ## Documentation
138
+
139
+ - Project site: https://comfy-org.github.io/pyisolate/
140
+ - Walkthroughs & architecture notes: see `mysolate/HELLO_WORLD.md` and `mysolate/GETTING_STARTED.md`
141
+
142
+ ## Key Benefits
143
+
144
+ - 🔒 **Dependency Isolation**: Run extensions with incompatible dependencies (e.g., numpy 1.x and 2.x) in the same application
145
+ - 🚀 **Zero-Copy PyTorch Tensor Sharing**: Share PyTorch tensors between processes without serialization overhead
146
+ - 🔄 **Transparent Communication**: Call async methods across process boundaries as if they were local
147
+ - 🎯 **Simple API**: Clean, intuitive interface with minimal boilerplate
148
+ - ⚡ **Fast**: Uses `uv` for blazing-fast virtual environment creation
149
+
150
+ ## Installation
151
+
152
+ ```bash
153
+ pip install pyisolate
154
+ ```
155
+
156
+ For development:
157
+ ```bash
158
+ pip install pyisolate[dev]
159
+ ```
160
+
161
+ ## Quick Start
162
+
163
+ ### Basic Usage
164
+
165
+ Create an extension that runs in an isolated environment:
166
+
167
+ ```python
168
+ # extensions/my_extension/__init__.py
169
+ from pyisolate import ExtensionBase
170
+
171
+ class MyExtension(ExtensionBase):
172
+ def on_module_loaded(self, module):
173
+ self.module = module
174
+
175
+ async def process_data(self, data):
176
+ # This runs in an isolated process with its own dependencies
177
+ import numpy as np # This could be numpy 2.x
178
+ return np.array(data).mean()
179
+ ```
180
+
181
+ Load and use the extension from your main application:
182
+
183
+ ```python
184
+ # main.py
185
+ import pyisolate
186
+ import asyncio
187
+
188
+ async def main():
189
+ # Configure the extension manager
190
+ config = pyisolate.ExtensionManagerConfig(
191
+ venv_root_path="./venvs"
192
+ )
193
+ manager = pyisolate.ExtensionManager(pyisolate.ExtensionBase, config)
194
+
195
+ # Load an extension with specific dependencies
196
+ extension = manager.load_extension(
197
+ pyisolate.ExtensionConfig(
198
+ name="data_processor",
199
+ module_path="./extensions/my_extension",
200
+ isolated=True,
201
+ dependencies=["numpy>=2.0.0"]
202
+ )
203
+ )
204
+
205
+ # Use the extension
206
+ result = await extension.process_data([1, 2, 3, 4, 5])
207
+ print(f"Mean: {result}") # Mean: 3.0
208
+
209
+ # Cleanup
210
+ await extension.stop()
211
+
212
+ asyncio.run(main())
213
+ ```
214
+
215
+ ### PyTorch Tensor Sharing
216
+
217
+ Share PyTorch tensors between processes without serialization:
218
+
219
+ ```python
220
+ # extensions/ml_extension/__init__.py
221
+ from pyisolate import ExtensionBase
222
+ import torch
223
+
224
+ class MLExtension(ExtensionBase):
225
+ async def process_tensor(self, tensor: torch.Tensor):
226
+ # Tensor is shared, not copied!
227
+ return tensor.mean()
228
+ ```
229
+
230
+ ```python
231
+ # main.py
232
+ extension = manager.load_extension(
233
+ pyisolate.ExtensionConfig(
234
+ name="ml_processor",
235
+ module_path="./extensions/ml_extension",
236
+ share_torch=True # Enable zero-copy tensor sharing
237
+ )
238
+ )
239
+
240
+ # Large tensor is shared, not serialized
241
+ large_tensor = torch.randn(1000, 1000)
242
+ mean = await extension.process_tensor(large_tensor)
243
+ ```
244
+
245
+ ### Shared State with Singletons
246
+
247
+ Share state across all extensions using ProxiedSingleton:
248
+
249
+ ```python
250
+ # shared.py
251
+ from pyisolate import ProxiedSingleton
252
+
253
+ class DatabaseAPI(ProxiedSingleton):
254
+ def __init__(self):
255
+ self.data = {}
256
+
257
+ def get(self, key):
258
+ return self.data.get(key)
259
+
260
+ def set(self, key, value):
261
+ self.data[key] = value
262
+ ```
263
+
264
+ ```python
265
+ # extensions/extension_a/__init__.py
266
+ class ExtensionA(ExtensionBase):
267
+ async def save_result(self, result):
268
+ db = DatabaseAPI() # Returns proxy to host's instance
269
+ await db.set("result", result)
270
+
271
+ # extensions/extension_b/__init__.py
272
+ class ExtensionB(ExtensionBase):
273
+ async def get_result(self):
274
+ db = DatabaseAPI() # Returns proxy to host's instance
275
+ return await db.get("result")
276
+ ```
277
+
278
+ ### Complete Application Structure
279
+
280
+ A complete pyisolate application requires a special `main.py` entry point to handle virtual environment activation:
281
+
282
+ ```python
283
+ # main.py
284
+ if __name__ == "__main__":
285
+ # When running as the main script, import and run your host application
286
+ from host import main
287
+ main()
288
+ else:
289
+ # When imported by extension processes, ensure venv is properly activated
290
+ import os
291
+ import site
292
+ import sys
293
+
294
+ if os.name == "nt": # Windows-specific venv activation
295
+ venv = os.environ.get("VIRTUAL_ENV", "")
296
+ if venv != "":
297
+ sys.path.insert(0, os.path.join(venv, "Lib", "site-packages"))
298
+ site.addsitedir(os.path.join(venv, "Lib", "site-packages"))
299
+ ```
300
+
301
+ ```python
302
+ # host.py - Your main application logic
303
+ import pyisolate
304
+ import asyncio
305
+
306
+ async def async_main():
307
+ # Create extension manager
308
+ config = pyisolate.ExtensionManagerConfig(
309
+ venv_root_path="./extension-venvs"
310
+ )
311
+ manager = pyisolate.ExtensionManager(ExtensionBase, config)
312
+
313
+ # Load extensions (e.g., from a directory or configuration file)
314
+ extensions = []
315
+ for extension_path in discover_extensions():
316
+ extension_config = pyisolate.ExtensionConfig(
317
+ name=extension_name,
318
+ module_path=extension_path,
319
+ isolated=True,
320
+ dependencies=load_dependencies(extension_path),
321
+ apis=[SharedAPI] # Optional shared singletons
322
+ )
323
+ extension = manager.load_extension(extension_config)
324
+ extensions.append(extension)
325
+
326
+ # Use extensions
327
+ for extension in extensions:
328
+ result = await extension.process()
329
+ print(f"Result: {result}")
330
+
331
+ # Clean shutdown
332
+ for extension in extensions:
333
+ await extension.stop()
334
+
335
+ def main():
336
+ asyncio.run(async_main())
337
+ ```
338
+
339
+ This structure ensures that:
340
+ - The host application runs normally when executed directly
341
+ - Extension processes properly activate their virtual environments when spawned
342
+ - Windows-specific path handling is properly managed
343
+
344
+ ## Features
345
+
346
+ ### Core Features
347
+ - **Automatic Virtual Environment Management**: Creates and manages isolated environments automatically
348
+ - **Bidirectional RPC**: Extensions can call host methods and vice versa
349
+ - **Async/Await Support**: Full support for asynchronous programming
350
+ - **Lifecycle Hooks**: `before_module_loaded()`, `on_module_loaded()`, and `stop()` for setup/teardown
351
+ - **Error Propagation**: Exceptions are properly propagated across process boundaries
352
+
353
+ ### Advanced Features
354
+ - **Dependency Resolution**: Automatically installs extension-specific dependencies
355
+ - **Platform Support**: Works on Windows, Linux, and soon to be tested on macOS
356
+ - **Context Tracking**: Ensures callbacks happen on the same asyncio loop as the original call
357
+ - **Fast Installation**: Uses `uv` for 10-100x faster package installation without every extension having its own copy of libraries
358
+
359
+ ## Architecture
360
+
361
+ ```
362
+ ┌─────────────────────┐ RPC ┌─────────────┐
363
+ │ Host Process │◄────────────►│ Extension A │
364
+ │ │ │ (venv A) │
365
+ │ ┌──────────────┐ │ └─────────────┘
366
+ │ │ Shared │ │ RPC ┌─────────────┐
367
+ │ │ Singletons │ │◄────────────►│ Extension B │
368
+ │ └──────────────┘ │ │ (venv B) │
369
+ └─────────────────────┘ └─────────────┘
370
+ ```
371
+
372
+ ## Implementing a Host Adapter (IsolationAdapter)
373
+
374
+ When integrating pyisolate with your application (like ComfyUI), you implement the `IsolationAdapter` protocol. This tells pyisolate how to configure isolated processes for your environment.
375
+
376
+ ### Reference Implementation
377
+
378
+ The canonical example is in `tests/fixtures/test_adapter.py`:
379
+
380
+ ```python
381
+ from pyisolate.interfaces import IsolationAdapter
382
+ from pyisolate._internal.shared import ProxiedSingleton
383
+
384
+ class MockHostAdapter(IsolationAdapter):
385
+ """Reference adapter showing all protocol methods."""
386
+
387
+ @property
388
+ def identifier(self) -> str:
389
+ """Return unique adapter identifier (e.g., 'comfyui')."""
390
+ return "myapp"
391
+
392
+ def get_path_config(self, module_path: str) -> dict:
393
+ """Configure sys.path for isolated extensions.
394
+
395
+ Returns:
396
+ - preferred_root: Your app's root directory
397
+ - additional_paths: Extra paths for imports
398
+ """
399
+ return {
400
+ "preferred_root": "/path/to/myapp",
401
+ "additional_paths": ["/path/to/myapp/extensions"],
402
+ }
403
+
404
+ def setup_child_environment(self, snapshot: dict) -> None:
405
+ """Configure child process after sys.path reconstruction."""
406
+ pass # Set up logging, environment, etc.
407
+
408
+ def register_serializers(self, registry) -> None:
409
+ """Register custom type serializers for RPC transport."""
410
+ registry.register(
411
+ "MyCustomType",
412
+ serializer=lambda obj: {"data": obj.data},
413
+ deserializer=lambda d: MyCustomType(d["data"]),
414
+ )
415
+
416
+ def provide_rpc_services(self) -> list:
417
+ """Return ProxiedSingleton classes to expose via RPC."""
418
+ return [MyRegistry, MyProgressReporter]
419
+
420
+ def handle_api_registration(self, api, rpc) -> None:
421
+ """Post-registration hook for API-specific setup."""
422
+ pass
423
+ ```
424
+
425
+ ### Testing Your Adapter
426
+
427
+ Run the contract tests to verify your adapter implements the protocol correctly:
428
+
429
+ ```bash
430
+ # The test suite verifies all protocol methods
431
+ pytest tests/test_adapter_contract.py -v
432
+ ```
433
+
434
+ ## Roadmap
435
+
436
+ ### ✅ Completed
437
+ - [x] Core isolation and RPC system
438
+ - [x] Automatic virtual environment creation
439
+ - [x] Bidirectional communication
440
+ - [x] PyTorch tensor sharing
441
+ - [x] Shared singleton pattern
442
+ - [x] Comprehensive test suite
443
+ - [x] Windows, Linux support
444
+ - [x] Security features (path normalization)
445
+ - [x] Fast installation with `uv`
446
+ - [x] Context tracking for RPC calls
447
+ - [x] Async/await support
448
+ - [x] Performance benchmarking suite
449
+ - [x] Memory usage tracking and benchmarking
450
+ - [x] Network access restrictions
451
+ - [x] Filesystem access sandboxing
452
+
453
+ ### 🚧 In Progress
454
+ - [ ] Documentation site
455
+ - [ ] macOS testing
456
+ - [ ] Wrapper for non-async calls between processes
457
+
458
+ ### 🔮 Future Plans
459
+ - [ ] CPU/Memory usage limits
460
+ - [ ] Hot-reloading of extensions
461
+ - [ ] Distributed RPC (across machines)
462
+ - [ ] Profiling and debugging tools
463
+
464
+ ## Use Cases
465
+
466
+ pyisolate is perfect for:
467
+
468
+ - **Plugin Systems**: When plugins may require conflicting dependencies
469
+ - **ML Pipelines**: Different models requiring different library versions
470
+ - **Microservices in a Box**: Multiple services with different dependencies in one app
471
+ - **Testing**: Running tests with different dependency versions in parallel
472
+ - **Legacy Code Integration**: Wrapping legacy code with specific dependency requirements
473
+
474
+ ## Development
475
+
476
+ We welcome contributions!
477
+
478
+ ```bash
479
+ # Setup development environment
480
+ uv venv && source .venv/bin/activate
481
+ uv pip install -e ".[dev,test]"
482
+ pre-commit install
483
+
484
+ # Run tests
485
+ pytest
486
+
487
+ # Run linting
488
+ ruff check pyisolate tests
489
+
490
+ # Run benchmarks
491
+ python benchmarks/simple_benchmark.py
492
+ ```
493
+
494
+ ### Benchmarking
495
+
496
+ pyisolate includes a comprehensive benchmarking suite to measure RPC call overhead:
497
+
498
+ ```bash
499
+ # Install benchmark dependencies
500
+ uv pip install -e ".[bench]"
501
+
502
+ # Quick benchmark using existing example extensions
503
+ python benchmarks/simple_benchmark.py
504
+
505
+ # Full benchmark suite with statistical analysis
506
+ python benchmarks/benchmark.py
507
+
508
+ # Quick mode with fewer iterations for faster results
509
+ python benchmarks/benchmark.py --quick
510
+
511
+ # Skip torch benchmarks (if torch not available)
512
+ python benchmarks/benchmark.py --no-torch
513
+
514
+ # Skip GPU benchmarks
515
+ python benchmarks/benchmark.py --no-gpu
516
+ ```
517
+
518
+ #### Example Benchmark Output
519
+
520
+ ```
521
+ ==================================================
522
+ BENCHMARK RESULTS
523
+ ==================================================
524
+ Test Mean (ms) Std Dev (ms) Runs
525
+ --------------------------------------------------
526
+ small_int 0.63 0.05 1000
527
+ small_string 0.64 0.06 1000
528
+ medium_string 0.65 0.07 1000
529
+ tiny_tensor 0.79 0.08 1000
530
+ small_tensor 0.80 0.11 1000
531
+ medium_tensor 0.81 0.06 1000
532
+ large_tensor 0.78 0.08 1000
533
+ model_tensor 0.88 0.29 1000
534
+
535
+ Fastest result: 0.63ms
536
+ ```
537
+
538
+ The benchmarks measure:
539
+
540
+ 1. **Small Data RPC Overhead**: ~0.6ms for basic data types (integers, strings)
541
+ 2. **Tensor Overhead**: Minimal overhead (~0.2ms) for sharing tensors up to 6GB via zero-copy shared memory
542
+ 3. **Scaling**: Performance remains O(1) regardless of tensor size
543
+
544
+ > ⚠️ **Note for CPU Tensors**: When checking out or running benchmarks with `share_torch=True`, ensuring `TMPDIR=/dev/shm` is recommended to guarantee that shared memory files are visible to sandboxed child processes.
545
+
546
+
547
+ ## License
548
+
549
+ pyisolate is licensed under the MIT License. See [LICENSE](LICENSE) for details.
550
+
551
+ ## Acknowledgments
552
+
553
+ - Built on Python's `multiprocessing` and `asyncio`
554
+ - Uses [`uv`](https://github.com/astral-sh/uv) for fast package management
555
+ - Inspired by plugin systems like Chrome Extensions and VS Code Extensions
556
+
557
+ ---
558
+
559
+ **Star this repo** if you find it useful! ⭐