|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import sys |
|
|
import types |
|
|
from unittest.mock import MagicMock, patch |
|
|
|
|
|
import pytest |
|
|
|
|
|
from nemo.utils.import_utils import UnavailableError, UnavailableMeta, is_unavailable, safe_import, safe_import_from |
|
|
|
|
|
|
|
|
class TestUnavailableMeta: |
|
|
"""Test suite for the UnavailableMeta metaclass.""" |
|
|
|
|
|
def test_metaclass_creation(self): |
|
|
"""Test that UnavailableMeta creates a class with the expected properties.""" |
|
|
|
|
|
TestClass = UnavailableMeta("TestClass", (), {}) |
|
|
|
|
|
|
|
|
assert TestClass.__name__ == "MISSINGTestClass" |
|
|
|
|
|
|
|
|
assert TestClass._msg == "TestClass could not be imported" |
|
|
|
|
|
def test_custom_error_message(self): |
|
|
"""Test that a custom error message can be provided.""" |
|
|
custom_msg = "Custom error message" |
|
|
TestClass = UnavailableMeta("TestClass", (), {"_msg": custom_msg}) |
|
|
|
|
|
assert TestClass._msg == custom_msg |
|
|
|
|
|
|
|
|
with pytest.raises(UnavailableError, match=custom_msg): |
|
|
TestClass() |
|
|
|
|
|
def test_call_raises_error(self): |
|
|
"""Test that attempting to instantiate the class raises UnavailableError.""" |
|
|
TestClass = UnavailableMeta("TestClass", (), {}) |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
TestClass() |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
TestClass(1, 2, 3, key="value") |
|
|
|
|
|
def test_attribute_access_raises_error(self): |
|
|
"""Test that accessing attributes raises UnavailableError.""" |
|
|
TestClass = UnavailableMeta("TestClass", (), {}) |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
TestClass.some_attribute |
|
|
|
|
|
def test_arithmetic_operations_raise_error(self): |
|
|
"""Test that arithmetic operations raise UnavailableError.""" |
|
|
TestClass = UnavailableMeta("TestClass", (), {}) |
|
|
|
|
|
operations = [ |
|
|
lambda c: c + 1, |
|
|
lambda c: 1 + c, |
|
|
lambda c: c - 1, |
|
|
lambda c: 1 - c, |
|
|
lambda c: c * 2, |
|
|
lambda c: 2 * c, |
|
|
lambda c: c / 2, |
|
|
lambda c: 2 / c, |
|
|
lambda c: c // 2, |
|
|
lambda c: 2 // c, |
|
|
lambda c: c**2, |
|
|
lambda c: 2**c, |
|
|
lambda c: -c, |
|
|
lambda c: abs(c), |
|
|
] |
|
|
|
|
|
for op in operations: |
|
|
with pytest.raises(UnavailableError): |
|
|
op(TestClass) |
|
|
|
|
|
def test_comparison_operations_raise_error(self): |
|
|
"""Test that comparison operations raise UnavailableError.""" |
|
|
TestClass = UnavailableMeta("TestClass", (), {}) |
|
|
another_class = UnavailableMeta("AnotherClass", (), {}) |
|
|
|
|
|
comparisons = [ |
|
|
lambda c: c == another_class, |
|
|
lambda c: c != another_class, |
|
|
lambda c: c < another_class, |
|
|
lambda c: c <= another_class, |
|
|
lambda c: c > another_class, |
|
|
lambda c: c >= another_class, |
|
|
] |
|
|
|
|
|
for comp in comparisons: |
|
|
with pytest.raises(UnavailableError): |
|
|
comp(TestClass) |
|
|
|
|
|
def test_container_operations_raise_error(self): |
|
|
"""Test that container operations raise UnavailableError.""" |
|
|
TestClass = UnavailableMeta("TestClass", (), {}) |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
len(TestClass) |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
TestClass[0] |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
TestClass[0] = 1 |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
del TestClass[0] |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
iter(TestClass) |
|
|
|
|
|
def test_descriptor_operations_raise_error(self): |
|
|
"""Test that descriptor operations raise UnavailableError.""" |
|
|
TestClass = UnavailableMeta("TestClass", (), {}) |
|
|
|
|
|
class DummyClass: |
|
|
prop = TestClass |
|
|
|
|
|
dummy = DummyClass() |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
TestClass.__get__(None, None) |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
TestClass.__delete__(None) |
|
|
|
|
|
|
|
|
class TestSafeImport: |
|
|
def test_successful_import(self): |
|
|
"""Test safe_import with a module that exists.""" |
|
|
module, success = safe_import("os") |
|
|
assert success is True |
|
|
assert isinstance(module, types.ModuleType) |
|
|
assert module.__name__ == "os" |
|
|
|
|
|
def test_failed_import(self): |
|
|
"""Test safe_import with a module that doesn't exist.""" |
|
|
module, success = safe_import("nonexistent_module") |
|
|
assert success is False |
|
|
assert is_unavailable(module) |
|
|
assert type(module) is UnavailableMeta |
|
|
|
|
|
def test_import_with_custom_message(self): |
|
|
"""Test safe_import with a custom error message.""" |
|
|
custom_msg = "Custom error message" |
|
|
module, success = safe_import("nonexistent_module", msg=custom_msg) |
|
|
|
|
|
assert success is False |
|
|
assert is_unavailable(module) |
|
|
|
|
|
|
|
|
with pytest.raises(UnavailableError, match=custom_msg): |
|
|
module() |
|
|
|
|
|
def test_import_with_alternative(self): |
|
|
"""Test safe_import with an alternative module.""" |
|
|
alt_module = object() |
|
|
module, success = safe_import("nonexistent_module", alt=alt_module) |
|
|
|
|
|
assert success is False |
|
|
assert module is alt_module |
|
|
|
|
|
def test_unavailable_module_raises_error_when_used(self): |
|
|
"""Test that using a UnavailableMeta placeholder raises UnavailableError.""" |
|
|
module, success = safe_import("nonexistent_module") |
|
|
|
|
|
assert success is False |
|
|
|
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
module() |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
module.attribute |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
module + 1 |
|
|
|
|
|
with pytest.raises(UnavailableError): |
|
|
module == 1 |
|
|
|
|
|
|
|
|
class TestSafeImportFrom: |
|
|
def test_successful_import_from(self): |
|
|
"""Test safe_import_from with a symbol that exists.""" |
|
|
symbol, success = safe_import_from("os", "path") |
|
|
assert success is True |
|
|
|
|
|
import os |
|
|
|
|
|
assert symbol is os.path |
|
|
|
|
|
def test_failed_import_from_nonexistent_module(self): |
|
|
"""Test safe_import_from with a module that doesn't exist.""" |
|
|
symbol, success = safe_import_from("nonexistent_module", "nonexistent_symbol") |
|
|
assert success is False |
|
|
assert is_unavailable(symbol) |
|
|
|
|
|
def test_failed_import_from_nonexistent_symbol(self): |
|
|
"""Test safe_import_from with a symbol that doesn't exist in an existing module.""" |
|
|
symbol, success = safe_import_from("os", "nonexistent_symbol") |
|
|
assert success is False |
|
|
assert is_unavailable(symbol) |
|
|
|
|
|
def test_import_from_with_custom_message(self): |
|
|
"""Test safe_import_from with a custom error message.""" |
|
|
custom_msg = "Custom error message for symbol" |
|
|
symbol, success = safe_import_from("os", "nonexistent_symbol", msg=custom_msg) |
|
|
|
|
|
assert success is False |
|
|
|
|
|
|
|
|
with pytest.raises(UnavailableError, match=custom_msg): |
|
|
symbol() |
|
|
|
|
|
def test_import_from_with_alternative(self): |
|
|
"""Test safe_import_from with an alternative symbol.""" |
|
|
alt_symbol = object() |
|
|
symbol, success = safe_import_from("os", "nonexistent_symbol", alt=alt_symbol) |
|
|
|
|
|
assert success is False |
|
|
assert symbol is alt_symbol |
|
|
|
|
|
def test_fallback_module(self): |
|
|
"""Test safe_import_from with a fallback module.""" |
|
|
|
|
|
with patch('importlib.import_module') as mock_import: |
|
|
|
|
|
def side_effect(name): |
|
|
if name == "primary_module": |
|
|
raise AttributeError("Symbol not found") |
|
|
elif name == "fallback_module": |
|
|
mock_module = MagicMock() |
|
|
mock_module.symbol = "fallback_symbol" |
|
|
return mock_module |
|
|
else: |
|
|
raise ImportError(f"Unexpected module: {name}") |
|
|
|
|
|
mock_import.side_effect = side_effect |
|
|
|
|
|
symbol, success = safe_import_from("primary_module", "symbol", fallback_module="fallback_module") |
|
|
|
|
|
assert success is True |
|
|
assert symbol == "fallback_symbol" |
|
|
|
|
|
def test_fallback_module_both_fail(self): |
|
|
"""Test safe_import_from when both primary and fallback modules fail.""" |
|
|
symbol, success = safe_import_from("nonexistent_primary", "symbol", fallback_module="nonexistent_fallback") |
|
|
|
|
|
assert success is False |
|
|
assert is_unavailable(symbol) |
|
|
|