XuPy 1.6.1__tar.gz → 1.7.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.
- {xupy-1.6.1 → xupy-1.7.1}/PKG-INFO +1 -1
- {xupy-1.6.1 → xupy-1.7.1}/XuPy.egg-info/PKG-INFO +1 -1
- {xupy-1.6.1 → xupy-1.7.1}/XuPy.egg-info/SOURCES.txt +1 -0
- {xupy-1.6.1 → xupy-1.7.1}/pyproject.toml +1 -1
- {xupy-1.6.1 → xupy-1.7.1}/test/test_core.py +1 -1
- xupy-1.7.1/test/test_cpu_memory_context.py +123 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/__init__.py +1 -3
- xupy-1.7.1/xupy/__version__.py +1 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/_core.py +577 -73
- {xupy-1.6.1 → xupy-1.7.1}/xupy/_cupy_install/__install_cupy__.py +1 -1
- {xupy-1.6.1 → xupy-1.7.1}/xupy/ma/core.py +4 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/ma/extras.py +82 -82
- xupy-1.6.1/xupy/__version__.py +0 -1
- {xupy-1.6.1 → xupy-1.7.1}/LICENSE +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/README.md +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/XuPy.egg-info/dependency_links.txt +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/XuPy.egg-info/requires.txt +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/XuPy.egg-info/top_level.txt +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/setup.cfg +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/setup.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/test/test_arithmetic_compatibility.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/test/test_large_array_printing.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/test/test_ma_core.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/test/test_ma_extras.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/_cupy_install/__check_availability__.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/_cupy_install/__init__.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/ma/__init__.py +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/py.typed +0 -0
- {xupy-1.6.1 → xupy-1.7.1}/xupy/typings.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "XuPy"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.7.1"
|
|
8
8
|
description = "GPU Accelerated masked arrays with automatic handling of CPU and GPU arrays."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Pietro Ferraiuolo", email = "pietro.ferraiuolo@inaf.it" }
|
|
@@ -19,7 +19,7 @@ except ImportError:
|
|
|
19
19
|
HAS_CUPY = False
|
|
20
20
|
|
|
21
21
|
import xupy as xp
|
|
22
|
-
from xupy._core import NumpyContext, on_gpu
|
|
22
|
+
from xupy._core import NumpyContext, _CPUMemoryContext, on_gpu
|
|
23
23
|
|
|
24
24
|
# Skip all tests if CuPy is not available
|
|
25
25
|
pytestmark = pytest.mark.skipif(not HAS_CUPY, reason="CuPy not available")
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for _CPUMemoryContext.
|
|
3
|
+
|
|
4
|
+
These tests do NOT require CuPy/CUDA and run purely on CPU (NumPy).
|
|
5
|
+
They validate that the CPU memory context manager exposes the same
|
|
6
|
+
interface as the GPU MemoryContext and behaves as a well-behaved
|
|
7
|
+
no-op context on CPU.
|
|
8
|
+
"""
|
|
9
|
+
import pytest
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
import xupy as xp
|
|
13
|
+
from xupy._core import _CPUMemoryContext, on_gpu
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestCPUMemoryContext:
|
|
17
|
+
"""Tests for _CPUMemoryContext (always available, no GPU required)."""
|
|
18
|
+
|
|
19
|
+
def test_cpu_memory_context_is_importable(self):
|
|
20
|
+
"""_CPUMemoryContext can be imported from _core and is a class."""
|
|
21
|
+
assert callable(_CPUMemoryContext)
|
|
22
|
+
|
|
23
|
+
def test_cpu_memory_context_enter_returns_self(self):
|
|
24
|
+
"""__enter__ returns the context object itself."""
|
|
25
|
+
ctx = _CPUMemoryContext()
|
|
26
|
+
with ctx as c:
|
|
27
|
+
assert c is ctx
|
|
28
|
+
|
|
29
|
+
def test_cpu_memory_context_basic_usage(self, capsys):
|
|
30
|
+
"""Context manager completes without raising and prints a summary."""
|
|
31
|
+
with _CPUMemoryContext() as ctx:
|
|
32
|
+
arr = np.array([1, 2, 3])
|
|
33
|
+
assert arr is not None
|
|
34
|
+
captured = capsys.readouterr()
|
|
35
|
+
assert "Session completed" in captured.out
|
|
36
|
+
|
|
37
|
+
def test_cpu_memory_context_default_parameters(self):
|
|
38
|
+
"""Default parameters match the GPU MemoryContext API."""
|
|
39
|
+
ctx = _CPUMemoryContext()
|
|
40
|
+
assert ctx.device_id is None
|
|
41
|
+
assert ctx.auto_cleanup is True
|
|
42
|
+
assert ctx.memory_threshold == 0.9
|
|
43
|
+
assert ctx.monitor_interval == 1.0
|
|
44
|
+
|
|
45
|
+
def test_cpu_memory_context_custom_parameters(self):
|
|
46
|
+
"""Custom parameters are stored correctly."""
|
|
47
|
+
ctx = _CPUMemoryContext(
|
|
48
|
+
device_id=0,
|
|
49
|
+
auto_cleanup=False,
|
|
50
|
+
memory_threshold=0.8,
|
|
51
|
+
monitor_interval=2.0,
|
|
52
|
+
)
|
|
53
|
+
assert ctx.device_id == 0
|
|
54
|
+
assert ctx.auto_cleanup is False
|
|
55
|
+
assert ctx.memory_threshold == 0.8
|
|
56
|
+
assert ctx.monitor_interval == 2.0
|
|
57
|
+
|
|
58
|
+
def test_cpu_memory_context_get_memory_info_returns_dict(self):
|
|
59
|
+
"""get_memory_info always returns a dict with at least 'device'."""
|
|
60
|
+
ctx = _CPUMemoryContext()
|
|
61
|
+
info = ctx.get_memory_info()
|
|
62
|
+
assert isinstance(info, dict)
|
|
63
|
+
assert "device" in info
|
|
64
|
+
assert info["device"] == "cpu"
|
|
65
|
+
|
|
66
|
+
def test_cpu_memory_context_get_memory_info_keys(self):
|
|
67
|
+
"""get_memory_info returns expected keys when psutil is available."""
|
|
68
|
+
try:
|
|
69
|
+
import psutil # noqa: F401
|
|
70
|
+
psutil_available = True
|
|
71
|
+
except ImportError:
|
|
72
|
+
psutil_available = False
|
|
73
|
+
|
|
74
|
+
ctx = _CPUMemoryContext()
|
|
75
|
+
info = ctx.get_memory_info()
|
|
76
|
+
if psutil_available:
|
|
77
|
+
for key in ("total", "free", "used", "memory_percent"):
|
|
78
|
+
assert key in info, f"Missing key '{key}' in memory info"
|
|
79
|
+
assert info["total"] > 0
|
|
80
|
+
assert 0.0 <= info["memory_percent"] <= 1.0
|
|
81
|
+
else:
|
|
82
|
+
assert "error" in info
|
|
83
|
+
|
|
84
|
+
def test_cpu_memory_context_no_op_methods(self):
|
|
85
|
+
"""All GPU-specific methods are no-ops (do not raise)."""
|
|
86
|
+
ctx = _CPUMemoryContext()
|
|
87
|
+
ctx.track_object(object())
|
|
88
|
+
ctx.clear_cache()
|
|
89
|
+
ctx.aggressive_cleanup()
|
|
90
|
+
ctx.emergency_cleanup()
|
|
91
|
+
ctx.auto_cleanup_if_needed()
|
|
92
|
+
ctx.monitor_memory(duration=0.0)
|
|
93
|
+
ctx.force_memory_deallocation()
|
|
94
|
+
ctx.force_memory_pool_reset()
|
|
95
|
+
|
|
96
|
+
def test_cpu_memory_context_check_memory_pressure(self):
|
|
97
|
+
"""check_memory_pressure returns a bool."""
|
|
98
|
+
ctx = _CPUMemoryContext()
|
|
99
|
+
result = ctx.check_memory_pressure()
|
|
100
|
+
assert isinstance(result, bool)
|
|
101
|
+
|
|
102
|
+
def test_cpu_memory_context_repr(self):
|
|
103
|
+
"""__repr__ contains 'MemoryContext' and 'cpu'."""
|
|
104
|
+
ctx = _CPUMemoryContext()
|
|
105
|
+
r = repr(ctx)
|
|
106
|
+
assert "MemoryContext" in r
|
|
107
|
+
assert "cpu" in r
|
|
108
|
+
|
|
109
|
+
def test_cpu_memory_context_exception_propagation(self):
|
|
110
|
+
"""Exceptions raised inside the context propagate normally."""
|
|
111
|
+
with pytest.raises(ValueError):
|
|
112
|
+
with _CPUMemoryContext():
|
|
113
|
+
raise ValueError("test error")
|
|
114
|
+
|
|
115
|
+
def test_cpu_memory_context_exposed_when_on_cpu(self):
|
|
116
|
+
"""When running in CPU mode, xp.MemoryContext is _CPUMemoryContext."""
|
|
117
|
+
if not on_gpu:
|
|
118
|
+
assert hasattr(xp, "MemoryContext")
|
|
119
|
+
assert xp.MemoryContext is _CPUMemoryContext
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
pytest.main([__file__, "-v"])
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.7.1"
|