Skip to content

Commit ef4a6e0

Browse files
authoredFeb 7, 2020
allow tests to run without xnd or ndtypes installed (#35)
* allow tests to run with xnd or ndtypes installed * avoid mypy error on XndBackend in test_numpy * add conda environment and azure case with minimal dependencies * add try/except for import of xnd_backend
1 parent 8578872 commit ef4a6e0

File tree

4 files changed

+118
-71
lines changed

4 files changed

+118
-71
lines changed
 

‎.conda/environment_minimal.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: uarray_min
2+
channels:
3+
- pytorch
4+
- defaults
5+
- conda-forge
6+
dependencies:
7+
- python=3.7
8+
- pip
9+
- sphinx
10+
- sphinx_rtd_theme
11+
- pytest
12+
- pytest-cov
13+
- mypy
14+
- pytorch-cpu
15+
- scipy
16+
- dask
17+
- sparse
18+
- doc8
19+
- black
20+
- pip:
21+
- pytest-mypy
22+
- pytest-black

‎azure-pipelines.yml

+26
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,32 @@ jobs:
2525
codeCoverageTool: Cobertura
2626
summaryFileLocation: "$(System.DefaultWorkingDirectory)/**/coverage.xml"
2727

28+
- job: TestsMinimalEnv
29+
pool:
30+
vmImage: 'ubuntu-16.04'
31+
32+
steps:
33+
- script: |
34+
echo "##vso[task.prependpath]$CONDA/bin"
35+
conda env create -f .conda/environment_minimal.yml
36+
displayName: Prepare conda
37+
38+
- script: |
39+
source activate uarray_min
40+
pip install git+https://github.com/Quansight-Labs/uarray.git
41+
pip install -e . --no-deps
42+
displayName: Install package
43+
44+
- script: |
45+
source activate uarray_min
46+
pytest
47+
displayName: Run tests
48+
49+
- task: PublishCodeCoverageResults@1
50+
inputs:
51+
codeCoverageTool: Cobertura
52+
summaryFileLocation: "$(System.DefaultWorkingDirectory)/**/coverage.xml"
53+
2854
- job: Docs
2955
pool:
3056
vmImage: 'ubuntu-16.04'

‎unumpy/tests/test_numpy.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import uarray as ua
33
import unumpy as np
44
import numpy as onp
5-
from ndtypes import ndt
65
import torch
76
import dask.array as da
87
import sparse
@@ -31,10 +30,12 @@
3130
try:
3231
import unumpy.xnd_backend as XndBackend
3332
import xnd
33+
from ndtypes import ndt
3434

3535
LIST_BACKENDS.append((XndBackend, xnd.xnd))
3636
FULLY_TESTED_BACKENDS.append(XndBackend)
3737
except ImportError:
38+
XndBackend = None # type: ignore
3839
LIST_BACKENDS.append(
3940
pytest.param(
4041
(None, None), marks=pytest.mark.skip(reason="xnd is not importable")
@@ -210,7 +211,7 @@ def test_functions_coerce_with_dtype(backend, method, args, kwargs):
210211
pytest.xfail(reason="The backend has no implementation for this ufunc.")
211212

212213
assert isinstance(ret, types)
213-
if backend == XndBackend:
214+
if XndBackend is not None and backend == XndBackend:
214215
assert ret.dtype == ndt(dtype)
215216
else:
216217
assert ret.dtype == dtype
@@ -266,7 +267,7 @@ def test_array_creation(backend, method, args, kwargs):
266267

267268
if isinstance(ret, da.Array):
268269
ret.compute()
269-
if backend == XndBackend:
270+
if XndBackend is not None and backend == XndBackend:
270271
assert ret.dtype == ndt(dtype)
271272
else:
272273
assert ret.dtype == dtype

‎unumpy/xnd_backend.py

+66-68
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,93 @@
1-
import numpy as np
2-
from ndtypes import ndt
3-
import xnd
4-
import gumath.functions as fn
5-
import gumath as gu
6-
import uarray as ua
7-
from uarray import Dispatchable, wrap_single_convertor
8-
from unumpy import ufunc, ufunc_list, ndarray, dtype
9-
import unumpy
10-
import functools
1+
try:
2+
import numpy as np
3+
from ndtypes import ndt
4+
import xnd
5+
import gumath.functions as fn
6+
import gumath as gu
7+
import uarray as ua
8+
from uarray import Dispatchable, wrap_single_convertor
9+
from unumpy import ufunc, ufunc_list, ndarray, dtype
10+
import unumpy
11+
import functools
1112

12-
from typing import Dict
13+
from typing import Dict
1314

14-
_ufunc_mapping: Dict[ufunc, np.ufunc] = {}
15+
_ufunc_mapping: Dict[ufunc, np.ufunc] = {}
1516

16-
__ua_domain__ = "numpy"
17+
__ua_domain__ = "numpy"
1718

19+
_implementations: Dict = {
20+
unumpy.ufunc.__call__: gu.gufunc.__call__,
21+
unumpy.ufunc.reduce: gu.reduce,
22+
}
1823

19-
_implementations: Dict = {
20-
unumpy.ufunc.__call__: gu.gufunc.__call__,
21-
unumpy.ufunc.reduce: gu.reduce,
22-
}
24+
def __ua_function__(method, args, kwargs):
25+
if method in _implementations:
26+
return _implementations[method](*args, **kwargs)
2327

28+
return _generic(method, args, kwargs)
2429

25-
def __ua_function__(method, args, kwargs):
26-
if method in _implementations:
27-
return _implementations[method](*args, **kwargs)
30+
@wrap_single_convertor
31+
def __ua_convert__(value, dispatch_type, coerce):
32+
if dispatch_type is ndarray:
33+
return convert(value, coerce=coerce) if value is not None else None
2834

29-
return _generic(method, args, kwargs)
35+
if dispatch_type is ufunc and hasattr(fn, value.name):
36+
return getattr(fn, value.name)
3037

38+
if dispatch_type is dtype:
39+
return ndt(str(value)) if value is not None else None
3140

32-
@wrap_single_convertor
33-
def __ua_convert__(value, dispatch_type, coerce):
34-
if dispatch_type is ndarray:
35-
return convert(value, coerce=coerce) if value is not None else None
36-
37-
if dispatch_type is ufunc and hasattr(fn, value.name):
38-
return getattr(fn, value.name)
41+
return NotImplemented
3942

40-
if dispatch_type is dtype:
41-
return ndt(str(value)) if value is not None else None
43+
def replace_self(func):
44+
@functools.wraps(func)
45+
def inner(self, *args, **kwargs):
46+
if self not in _ufunc_mapping:
47+
return NotImplemented
4248

43-
return NotImplemented
49+
return func(_ufunc_mapping[self], *args, **kwargs)
4450

51+
return inner
4552

46-
def replace_self(func):
47-
@functools.wraps(func)
48-
def inner(self, *args, **kwargs):
49-
if self not in _ufunc_mapping:
53+
def _generic(method, args, kwargs):
54+
try:
55+
import numpy as np
56+
import unumpy.numpy_backend as NumpyBackend
57+
except ImportError:
5058
return NotImplemented
5159

52-
return func(_ufunc_mapping[self], *args, **kwargs)
60+
with ua.set_backend(NumpyBackend, coerce=True):
61+
try:
62+
out = method(*args, **kwargs)
63+
except TypeError:
64+
return NotImplemented
5365

54-
return inner
66+
return convert_out(out, coerce=False)
5567

68+
def convert_out(x, coerce):
69+
if isinstance(x, (tuple, list)):
70+
return type(x)(map(lambda x: convert_out(x, coerce=coerce), x))
5671

57-
def _generic(method, args, kwargs):
58-
try:
59-
import numpy as np
60-
import unumpy.numpy_backend as NumpyBackend
61-
except ImportError:
62-
return NotImplemented
72+
return convert(x, coerce=coerce)
73+
74+
def convert(x, coerce):
75+
if isinstance(x, xnd.array):
76+
return x
6377

64-
with ua.set_backend(NumpyBackend, coerce=True):
6578
try:
66-
out = method(*args, **kwargs)
79+
return xnd.array.from_buffer(memoryview(x))
6780
except TypeError:
68-
return NotImplemented
69-
70-
return convert_out(out, coerce=False)
71-
72-
73-
def convert_out(x, coerce):
74-
if isinstance(x, (tuple, list)):
75-
return type(x)(map(lambda x: convert_out(x, coerce=coerce), x))
76-
77-
return convert(x, coerce=coerce)
78-
81+
pass
7982

80-
def convert(x, coerce):
81-
if isinstance(x, xnd.array):
82-
return x
83+
if coerce:
84+
return xnd.array(x)
8385

84-
try:
85-
return xnd.array.from_buffer(memoryview(x))
86-
except TypeError:
87-
pass
86+
if isinstance(x, (int, float, bool)):
87+
return x
8888

89-
if coerce:
90-
return xnd.array(x)
89+
raise ua.BackendNotImplementedError("Unsupported output received.")
9190

92-
if isinstance(x, (int, float, bool)):
93-
return x
9491

95-
raise ua.BackendNotImplementedError("Unsupported output received.")
92+
except ImportError:
93+
pass

0 commit comments

Comments
 (0)
Please sign in to comment.