Skip to content

Add a test that there are no hard dependencies #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 27, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/dev/special-considerations.md
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@ should be taken into account:
PyTorch, etc., it does not hard depend on them. These libraries are not
imported unless either an array object is passed to
{func}`~.array_namespace()`, or the specific `array_api_compat.<namespace>`
sub-namespace is explicitly imported.
sub-namespace is explicitly imported. This is tested (as best as possible)
in `tests/test_no_dependencies.py`.

- **Vendorability.** array-api-compat should be [vendorable](vendoring). This
means that, for instance, all imports in the library are relative imports.
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
array-api-strict
dask[array]
jax[cpu]
numpy
pytest
torch
setuptools
72 changes: 72 additions & 0 deletions tests/test_no_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Test that array_api_compat has no "hard" dependencies.
Libraries like NumPy should only be imported if a numpy array is passed to
array_namespace or if array_api_compat.numpy is explicitly imported.
We have to test this in a subprocess because these libraries have already been
imported from the other tests.
"""

import sys
import subprocess

from ._helpers import import_

import pytest

class Array:
# Dummy array namespace that doesn't depend on any array library
def __array_namespace__(self, api_version=None):
class Namespace:
pass
return Namespace()

def _test_dependency(mod):
assert mod not in sys.modules

# Run various functions that shouldn't depend on mod and check that they
# don't import it.

import array_api_compat
assert mod not in sys.modules

a = Array()

# array-api-strict is an example of an array API library that isn't
# wrapped by array-api-compat.
if "strict" not in mod:
is_mod_array = getattr(array_api_compat, f"is_{mod.split('.')[0]}_array")
assert not is_mod_array(a)
assert mod not in sys.modules

is_array_api_obj = getattr(array_api_compat, "is_array_api_obj")
assert is_array_api_obj(a)
assert mod not in sys.modules

array_namespace = getattr(array_api_compat, "array_namespace")
array_namespace(Array())
assert mod not in sys.modules

# TODO: Test that wrapper for library X doesn't depend on wrappers for library
# Y (except most array libraries actually do themselves depend on numpy).

@pytest.mark.parametrize("library", ["cupy", "numpy", "torch", "dask.array",
"jax.numpy", "array_api_strict"])
def test_numpy_dependency(library):
# This unfortunately won't go through any of the pytest machinery. We
# reraise the exception as an AssertionError so that pytest will show it
# in a semi-reasonable way

# Import (in this process) to make sure 'library' is actually installed and
# so that cupy can be skipped.
import_(library)

try:
subprocess.run([sys.executable, '-c', f'''\
from tests.test_no_dependencies import _test_dependency
_test_dependency({library!r})'''], check=True, capture_output=True, encoding='utf-8')
except subprocess.CalledProcessError as e:
print(e.stdout, end='')
raise AssertionError(e.stderr)