unifiedefficientloader 0.4.4__tar.gz → 0.5.0__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 (34) hide show
  1. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/PKG-INFO +2 -2
  2. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/pyproject.toml +30 -2
  3. unifiedefficientloader-0.5.0/tests/test_mmap_diagnostics.py +238 -0
  4. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/memory_efficient_loader.py +4 -1
  5. unifiedefficientloader-0.5.0/unifiedefficientloader/uel/vram_buffer.py +4 -0
  6. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader.egg-info/PKG-INFO +2 -2
  7. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader.egg-info/SOURCES.txt +3 -1
  8. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader.egg-info/requires.txt +1 -1
  9. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/LICENSE +0 -0
  10. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/README.md +0 -0
  11. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/setup.cfg +0 -0
  12. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/setup.py +0 -0
  13. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/tests/test_direct_gpu.py +0 -0
  14. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/tests/test_incremental_writer.py +0 -0
  15. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/tests/test_logging.py +0 -0
  16. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/tests/test_mmap.py +0 -0
  17. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/tests/test_unified_data_loader.py +0 -0
  18. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/tests/test_utils.py +0 -0
  19. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/__init__.py +0 -0
  20. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/gpu_buffer_pool.py +0 -0
  21. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/incremental_writer.py +0 -0
  22. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/logging_utils.py +0 -0
  23. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/pinned_buffer_pool.py +0 -0
  24. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/pinned_transfer.py +0 -0
  25. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/tensor_utils.py +0 -0
  26. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/uel/__init__.py +0 -0
  27. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/uel/control.py +0 -0
  28. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/uel/host_buffer.py +0 -0
  29. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/uel/model_mmap.py +0 -0
  30. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/uel/model_vbar.py +0 -0
  31. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/uel/torch.py +0 -0
  32. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader/unified_data_loader.py +0 -0
  33. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader.egg-info/dependency_links.txt +0 -0
  34. {unifiedefficientloader-0.4.4 → unifiedefficientloader-0.5.0}/unifiedefficientloader.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unifiedefficientloader
3
- Version: 0.4.4
3
+ Version: 0.5.0
4
4
  Summary: A unified interface for memory efficient per tensor loading of safetensors files as raw bytes from offset, handling CPU/GPU pinned transfers, and converting between tensors and dicts.
5
5
  Author: silveroxides
6
6
  License: MIT License
@@ -33,7 +33,7 @@ Classifier: Operating System :: POSIX :: Linux
33
33
  Requires-Python: >=3.9
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
- Requires-Dist: comfy-aimdo==0.3.0
36
+ Requires-Dist: comfy-aimdo==0.4.3
37
37
  Provides-Extra: torch
38
38
  Requires-Dist: torch; extra == "torch"
39
39
  Provides-Extra: safetensors
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unifiedefficientloader"
7
- version = "0.4.4"
7
+ version = "0.5.0"
8
8
  description = "A unified interface for memory efficient per tensor loading of safetensors files as raw bytes from offset, handling CPU/GPU pinned transfers, and converting between tensors and dicts."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -19,7 +19,7 @@ classifiers = [
19
19
  "Operating System :: POSIX :: Linux",
20
20
  ]
21
21
  requires-python = ">=3.9"
22
- dependencies = ["comfy-aimdo==0.3.0"]
22
+ dependencies = ["comfy-aimdo==0.4.3"]
23
23
 
24
24
  [project.optional-dependencies]
25
25
  torch = ["torch"]
@@ -40,3 +40,31 @@ filterwarnings = [
40
40
  where = ["."]
41
41
  include = ["unifiedefficientloader*"]
42
42
  exclude = ["reference"]
43
+
44
+ [tool.ruff]
45
+ line-length = 320
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "I"]
49
+
50
+ [tool.ruff.lint.isort]
51
+ split-on-trailing-comma = false
52
+
53
+ [tool.ruff.format]
54
+ skip-magic-trailing-comma = true
55
+
56
+ [tool.isort]
57
+ line_length = 64
58
+ profile = "black"
59
+ force_grid_wrap = 2
60
+ multi_line_output = 3
61
+ include_trailing_comma = true
62
+
63
+ [tool.yapf]
64
+ based_on_style = "pep8"
65
+ column_limit = 128
66
+ split_all_comma_separated_values = false
67
+ split_before_named_assigns = false
68
+ dedent_closing_brackets = true
69
+ join_multiple_lines = false
70
+ indent_width = 4
@@ -0,0 +1,238 @@
1
+ """
2
+ Diagnostics for MMAP initialization and comfy-aimdo availability.
3
+
4
+ Run with: pytest tests/test_mmap_diagnostics.py -v -s
5
+ Or use check_mmap_available() in other tests to detect capabilities.
6
+ """
7
+
8
+ import pytest
9
+ import tempfile
10
+ import os
11
+ import sys
12
+
13
+ try:
14
+ import torch
15
+ from safetensors.torch import save_file
16
+ HAS_TORCH = True
17
+ except ImportError:
18
+ HAS_TORCH = False
19
+
20
+
21
+ def check_mmap_available() -> tuple[bool, str]:
22
+ """
23
+ Check if MMAP is available and initialized.
24
+
25
+ Returns:
26
+ (is_available, status_message)
27
+ """
28
+ try:
29
+ import comfy_aimdo
30
+ except ImportError:
31
+ return False, "comfy_aimdo package not installed"
32
+
33
+ try:
34
+ from unifiedefficientloader.uel import control
35
+
36
+ if control.lib is None:
37
+ init_result = control.init()
38
+ if not init_result:
39
+ return False, "control.init() returned False"
40
+
41
+ # Try importing ModelMMAP
42
+ from unifiedefficientloader.uel.model_mmap import ModelMMAP
43
+
44
+ return True, "MMAP available"
45
+ except Exception as e:
46
+ return False, f"{type(e).__name__}: {e}"
47
+
48
+
49
+ @pytest.fixture
50
+ def mmap_status():
51
+ """Get current MMAP availability status."""
52
+ available, message = check_mmap_available()
53
+ return {"available": available, "message": message}
54
+
55
+
56
+ def test_mmap_availability(mmap_status):
57
+ """
58
+ Diagnostic test: Report MMAP availability status.
59
+
60
+ This test always passes but provides visibility into whether MMAP
61
+ is available in the current environment.
62
+ """
63
+ print(f"\nMMAP Status: {mmap_status['available']}")
64
+ print(f"Details: {mmap_status['message']}")
65
+
66
+ if not mmap_status["available"]:
67
+ pytest.skip(f"MMAP not available: {mmap_status['message']}")
68
+
69
+
70
+ @pytest.mark.skipif(
71
+ not HAS_TORCH, reason="Requires torch and safetensors"
72
+ )
73
+ def test_mmap_initialization_diagnostics():
74
+ """
75
+ Diagnostic test: Verify full MMAP initialization pipeline.
76
+
77
+ Tests each step and reports where/why it might fail.
78
+ """
79
+ import logging
80
+ from unifiedefficientloader import logging_utils
81
+
82
+ # Enable verbose logging for diagnostics
83
+ logging_utils.setup_logging("VERBOSE")
84
+
85
+ print("\n" + "=" * 70)
86
+ print("MMAP INITIALIZATION DIAGNOSTICS")
87
+ print("=" * 70)
88
+
89
+ # Step 1: Check comfy-aimdo
90
+ print("\n[1] Checking comfy-aimdo package...")
91
+ try:
92
+ import comfy_aimdo
93
+ print(" ✓ comfy_aimdo imported")
94
+ except ImportError as e:
95
+ print(f" ✗ FAILED: {e}")
96
+ pytest.skip("comfy_aimdo not installed")
97
+
98
+ # Step 2: Import and initialize control
99
+ print("\n[2] Initializing control module...")
100
+ try:
101
+ from unifiedefficientloader.uel import control
102
+ print(f" ✓ control.lib before init: {control.lib}")
103
+
104
+ result = control.init()
105
+ print(f" ✓ control.init() returned: {result}")
106
+ print(f" ✓ control.lib after init: {control.lib}")
107
+
108
+ if control.lib is None:
109
+ print(" ✗ FAILED: control.lib is still None")
110
+ pytest.skip("control.init() did not load library")
111
+ except Exception as e:
112
+ print(f" ✗ FAILED: {e}")
113
+ pytest.skip(f"control initialization failed: {e}")
114
+
115
+ # Step 3: Import ModelMMAP
116
+ print("\n[3] Importing ModelMMAP...")
117
+ try:
118
+ from unifiedefficientloader.uel.model_mmap import ModelMMAP
119
+ print(" ✓ ModelMMAP imported")
120
+ except Exception as e:
121
+ print(f" ✗ FAILED: {e}")
122
+ pytest.skip(f"ModelMMAP import failed: {e}")
123
+
124
+ # Step 4: Try creating MMAP with test file
125
+ print("\n[4] Creating test MMAP...")
126
+ try:
127
+ tensors = {"test_tensor": torch.randn(10, 10, dtype=torch.float32)}
128
+ fd, test_file = tempfile.mkstemp(suffix=".safetensors")
129
+ os.close(fd)
130
+ save_file(tensors, test_file)
131
+ print(f" ✓ Created test file: {test_file}")
132
+
133
+ try:
134
+ mmap = ModelMMAP(test_file)
135
+ print(f" ✓ ModelMMAP created successfully")
136
+ print(f" ✓ mmap.state: {mmap.state}")
137
+ print(f" ✓ mmap.get(): {mmap.get()}")
138
+ mmap = None # Release
139
+ print(" ✓ MMAP released")
140
+ finally:
141
+ # Windows locks MMAP files, so we can't always delete immediately
142
+ try:
143
+ os.remove(test_file)
144
+ except PermissionError:
145
+ print(f" ⚠ Could not delete test file (locked by MMAP): {test_file}")
146
+
147
+ except Exception as e:
148
+ print(f" ✗ FAILED: {e}")
149
+ import traceback
150
+ traceback.print_exc()
151
+ pytest.fail(f"MMAP creation failed: {e}")
152
+
153
+ # Step 5: Try UnifiedSafetensorsLoader with MMAP
154
+ print("\n[5] Testing UnifiedSafetensorsLoader with use_mmap=True...")
155
+ try:
156
+ from unifiedefficientloader import UnifiedSafetensorsLoader
157
+
158
+ tensors = {
159
+ "weight": torch.randn(5, 5, dtype=torch.float32),
160
+ "bias": torch.randn(5, dtype=torch.float32),
161
+ }
162
+ fd, test_file = tempfile.mkstemp(suffix=".safetensors")
163
+ os.close(fd)
164
+ save_file(tensors, test_file)
165
+ print(f" ✓ Created test file: {test_file}")
166
+
167
+ try:
168
+ with UnifiedSafetensorsLoader(test_file, low_memory=True, use_mmap=True) as loader:
169
+ print(f" ✓ Loader created")
170
+ print(f" ✓ loader.use_mmap: {loader.use_mmap}")
171
+
172
+ if not loader.use_mmap:
173
+ print(" ✗ MMAP not active (fell back to standard IO)")
174
+ pytest.fail("MMAP initialization failed in UnifiedSafetensorsLoader")
175
+
176
+ # Load a tensor via MMAP
177
+ tensor = loader.get_tensor("weight")
178
+ print(f" ✓ Loaded tensor via MMAP: {tensor.shape} {tensor.dtype}")
179
+
180
+ # Verify _uel_mmap_ref is attached
181
+ storage = tensor.untyped_storage()
182
+ if hasattr(storage, "_uel_mmap_ref"):
183
+ print(f" ✓ MMAP reference attached to tensor storage")
184
+ else:
185
+ print(f" ⚠ MMAP reference NOT attached (tensor may not keep mapping alive)")
186
+ finally:
187
+ try:
188
+ os.remove(test_file)
189
+ except PermissionError:
190
+ print(f" ⚠ Could not delete test file: {test_file}")
191
+
192
+ except Exception as e:
193
+ print(f" ✗ FAILED: {e}")
194
+ import traceback
195
+ traceback.print_exc()
196
+ pytest.fail(f"UnifiedSafetensorsLoader MMAP test failed: {e}")
197
+
198
+ print("\n" + "=" * 70)
199
+ print("✓ ALL DIAGNOSTICS PASSED - MMAP IS WORKING")
200
+ print("=" * 70)
201
+
202
+
203
+ @pytest.mark.skipif(
204
+ not HAS_TORCH, reason="Requires torch and safetensors"
205
+ )
206
+ def test_mmap_not_available_graceful_fallback(monkeypatch):
207
+ """
208
+ Test that when MMAP is not available, loader falls back gracefully.
209
+ """
210
+ from unifiedefficientloader import UnifiedSafetensorsLoader
211
+ from unifiedefficientloader.uel import control
212
+
213
+ # Patch control to simulate missing library
214
+ monkeypatch.setattr(control, "lib", None)
215
+ monkeypatch.setattr(control, "init", lambda: False)
216
+
217
+ # Create test file
218
+ tensors = {"weight": torch.randn(5, 5)}
219
+ fd, test_file = tempfile.mkstemp(suffix=".safetensors")
220
+ os.close(fd)
221
+ save_file(tensors, test_file)
222
+
223
+ try:
224
+ # Try with MMAP=True, should fall back silently
225
+ with UnifiedSafetensorsLoader(test_file, low_memory=True, use_mmap=True) as loader:
226
+ assert loader.use_mmap is False, "Should have fallen back when MMAP unavailable"
227
+
228
+ # Verify it still loads correctly via standard IO
229
+ tensor = loader.get_tensor("weight")
230
+ assert tensor.shape == (5, 5)
231
+ assert torch.equal(tensor, tensors["weight"])
232
+
233
+ print("\n✓ Graceful fallback works: Loader reverted to standard IO")
234
+ finally:
235
+ try:
236
+ os.remove(test_file)
237
+ except PermissionError:
238
+ pass
@@ -102,7 +102,9 @@ class UnifiedSafetensorsLoader:
102
102
  import ctypes
103
103
 
104
104
  if control.lib is None:
105
- control.init()
105
+ init_result = control.init()
106
+ if not init_result:
107
+ raise RuntimeError("control.init() returned False — comfy-aimdo library may not be available")
106
108
 
107
109
  self._mmap = ModelMMAP(self.filename)
108
110
  self._mmap_size = os.path.getsize(self.filename)
@@ -114,6 +116,7 @@ class UnifiedSafetensorsLoader:
114
116
  logging_utils.warning(
115
117
  f"Failed to initialize MMAP: {e}. Falling back to standard IO."
116
118
  )
119
+ logging_utils.debug(f"MMAP initialization exception details: {type(e).__name__}: {e}")
117
120
  self.use_mmap = False
118
121
  self._mmap = None
119
122
  self._mmap_view = None
@@ -0,0 +1,4 @@
1
+ """Redirect: unifiedefficientloader.uel.vram_buffer → comfy_aimdo.vram_buffer"""
2
+ import sys
3
+ import comfy_aimdo.vram_buffer
4
+ sys.modules[__name__] = comfy_aimdo.vram_buffer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unifiedefficientloader
3
- Version: 0.4.4
3
+ Version: 0.5.0
4
4
  Summary: A unified interface for memory efficient per tensor loading of safetensors files as raw bytes from offset, handling CPU/GPU pinned transfers, and converting between tensors and dicts.
5
5
  Author: silveroxides
6
6
  License: MIT License
@@ -33,7 +33,7 @@ Classifier: Operating System :: POSIX :: Linux
33
33
  Requires-Python: >=3.9
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
- Requires-Dist: comfy-aimdo==0.3.0
36
+ Requires-Dist: comfy-aimdo==0.4.3
37
37
  Provides-Extra: torch
38
38
  Requires-Dist: torch; extra == "torch"
39
39
  Provides-Extra: safetensors
@@ -6,6 +6,7 @@ tests/test_direct_gpu.py
6
6
  tests/test_incremental_writer.py
7
7
  tests/test_logging.py
8
8
  tests/test_mmap.py
9
+ tests/test_mmap_diagnostics.py
9
10
  tests/test_unified_data_loader.py
10
11
  tests/test_utils.py
11
12
  unifiedefficientloader/__init__.py
@@ -27,4 +28,5 @@ unifiedefficientloader/uel/control.py
27
28
  unifiedefficientloader/uel/host_buffer.py
28
29
  unifiedefficientloader/uel/model_mmap.py
29
30
  unifiedefficientloader/uel/model_vbar.py
30
- unifiedefficientloader/uel/torch.py
31
+ unifiedefficientloader/uel/torch.py
32
+ unifiedefficientloader/uel/vram_buffer.py
@@ -1,4 +1,4 @@
1
- comfy-aimdo==0.3.0
1
+ comfy-aimdo==0.4.3
2
2
 
3
3
  [all]
4
4
  torch