Skip to content

Commit

Permalink
ENH: __array_function__ updates for NumPy 1.17.0
Browse files Browse the repository at this point in the history
- Always enable __array_function__ overrides.
- Remove special cases for Python 2 compatibility.
- Document these changes in 1.17.0-notes.rst.

It will be good to see ASV numbers to understand the performance implications
of these changes. If need be, we can speed up NumPy functions internally by
using non-dispatched functions (with ``.__wrapped__``).
  • Loading branch information
shoyer committed Jan 22, 2019
1 parent 3cbc11a commit 96d179c
Show file tree
Hide file tree
Showing 10 changed files with 17 additions and 56 deletions.
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ matrix:
- BLAS=None
- LAPACK=None
- ATLAS=None
- python: 3.6
env:
- NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1
- os: linux-ppc64le
python: 3.6
env:
Expand Down
1 change: 0 additions & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ jobs:
cp -r ./usr/local/lib/* /usr/lib && \
cp ./usr/local/include/* /usr/include && \
cd ../numpy && \
NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1 \
F77=gfortran-5 F90=gfortran-5 \
CFLAGS='-UNDEBUG -std=c99' python3 runtests.py --mode=full -- -rsx --junitxml=junit/test-results.xml"
displayName: 'Run 32-bit Ubuntu Docker Build / Tests'
Expand Down
12 changes: 12 additions & 0 deletions doc/release/1.17.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ NumPy 1.17.0 Release Notes
Highlights
==========

* Experimental support for overriding numpy functions is now always available,
see ``__array_function__`` below.

* NumPy's FFT implementation has switched to pocketfft

New functions
Expand Down Expand Up @@ -122,3 +125,12 @@ Changes
-------------------------------------------------------
The modulus operation with two ``np.timedelta64`` operands now returns
``NaT`` in the case of division by zero, rather than returning zero

NumPy functions now always support overrides with ``__array_function__``
------------------------------------------------------------------------
NumPy now always checks the ``__array_function__`` method to implement overrides
of NumPy functions on non-NumPy arrays, as described in `NEP 18`_. The feature
was available for testing with NumPy 1.16 if appropriate environment variables
are set, but is now always enabled.

.. _`NEP 18` : http://www.numpy.org/neps/nep-0018-array-function-protocol.html
18 changes: 0 additions & 18 deletions numpy/core/overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
from numpy.compat._inspect import getargspec


ENABLE_ARRAY_FUNCTION = bool(
int(os.environ.get('NUMPY_EXPERIMENTAL_ARRAY_FUNCTION', 0)))


add_docstring(
implement_array_function,
"""
Expand Down Expand Up @@ -141,16 +137,6 @@ def array_function_dispatch(dispatcher, module=None, verify=True,
Function suitable for decorating the implementation of a NumPy function.
"""

if not ENABLE_ARRAY_FUNCTION:
# __array_function__ requires an explicit opt-in for now
def decorator(implementation):
if module is not None:
implementation.__module__ = module
if docs_from_dispatcher:
add_docstring(implementation, dispatcher.__doc__)
return implementation
return decorator

def decorator(implementation):
if verify:
verify_matching_signatures(implementation, dispatcher)
Expand All @@ -167,10 +153,6 @@ def public_api(*args, **kwargs):
if module is not None:
public_api.__module__ = module

# TODO: remove this when we drop Python 2 support (functools.wraps
# adds __wrapped__ automatically in later versions)
public_api.__wrapped__ = implementation

return public_api

return decorator
Expand Down
8 changes: 0 additions & 8 deletions numpy/core/shape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,6 @@ def _arrays_for_stack_dispatcher(arrays, stacklevel=4):
return arrays


def _warn_for_nonsequence(arrays):
if not overrides.ENABLE_ARRAY_FUNCTION:
_arrays_for_stack_dispatcher(arrays, stacklevel=4)


def _vhstack_dispatcher(tup):
return _arrays_for_stack_dispatcher(tup)

Expand Down Expand Up @@ -279,7 +274,6 @@ def vstack(tup):
[4]])
"""
_warn_for_nonsequence(tup)
return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)


Expand Down Expand Up @@ -331,7 +325,6 @@ def hstack(tup):
[3, 4]])
"""
_warn_for_nonsequence(tup)
arrs = [atleast_1d(_m) for _m in tup]
# As a special case, dimension 0 of 1-dimensional arrays is "horizontal"
if arrs and arrs[0].ndim == 1:
Expand Down Expand Up @@ -406,7 +399,6 @@ def stack(arrays, axis=0, out=None):
[3, 4]])
"""
_warn_for_nonsequence(arrays)
arrays = [asanyarray(arr) for arr in arrays]
if not arrays:
raise ValueError('need at least one array to stack')
Expand Down
14 changes: 1 addition & 13 deletions numpy/core/tests/test_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,11 @@
assert_, assert_equal, assert_raises, assert_raises_regex)
from numpy.core.overrides import (
_get_implementing_args, array_function_dispatch,
verify_matching_signatures, ENABLE_ARRAY_FUNCTION)
verify_matching_signatures)
from numpy.core.numeric import pickle
import pytest


requires_array_function = pytest.mark.skipif(
not ENABLE_ARRAY_FUNCTION,
reason="__array_function__ dispatch not enabled.")


def _return_not_implemented(self, *args, **kwargs):
return NotImplemented

Expand All @@ -35,7 +30,6 @@ def dispatched_two_arg(array1, array2):
return 'original'


@requires_array_function
class TestGetImplementingArgs(object):

def test_ndarray(self):
Expand Down Expand Up @@ -147,7 +141,6 @@ def test_too_many_duck_arrays(self):
_get_implementing_args(relevant_args)


@requires_array_function
class TestNDArrayArrayFunction(object):

def test_method(self):
Expand Down Expand Up @@ -206,7 +199,6 @@ def test_no_wrapper(self):
args=(array,), kwargs={})


@requires_array_function
class TestArrayFunctionDispatch(object):

def test_pickle(self):
Expand Down Expand Up @@ -246,7 +238,6 @@ def __array_function__(self, func, types, args, kwargs):
dispatched_one_arg(array)


@requires_array_function
class TestVerifyMatchingSignatures(object):

def test_verify_matching_signatures(self):
Expand Down Expand Up @@ -299,7 +290,6 @@ def decorator(func):
return (MyArray, implements)


@requires_array_function
class TestArrayFunctionImplementation(object):

def test_one_arg(self):
Expand Down Expand Up @@ -376,12 +366,10 @@ def test_set_module(self):
assert_equal(np.fft.fft.__module__, 'numpy.fft')
assert_equal(np.linalg.solve.__module__, 'numpy.linalg')

@pytest.mark.skipif(sys.version_info[0] < 3, reason="Python 3 only")
def test_inspect_sum(self):
signature = inspect.signature(np.sum)
assert_('axis' in signature.parameters)

@requires_array_function
def test_override_sum(self):
MyArray, implements = _new_duck_type_and_implements()

Expand Down
5 changes: 1 addition & 4 deletions numpy/lib/shape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
from numpy.core.multiarray import normalize_axis_index
from numpy.core import overrides
from numpy.core import vstack, atleast_3d
from numpy.core.shape_base import (
_arrays_for_stack_dispatcher, _warn_for_nonsequence)
from numpy.core.shape_base import _arrays_for_stack_dispatcher
from numpy.lib.index_tricks import ndindex
from numpy.matrixlib.defmatrix import matrix # this raises all the right alarm bells

Expand Down Expand Up @@ -630,7 +629,6 @@ def column_stack(tup):
[3, 4]])
"""
_warn_for_nonsequence(tup)
arrays = []
for v in tup:
arr = array(v, copy=False, subok=True)
Expand Down Expand Up @@ -695,7 +693,6 @@ def dstack(tup):
[[3, 4]]])
"""
_warn_for_nonsequence(tup)
return _nx.concatenate([atleast_3d(_m) for _m in tup], 2)


Expand Down
6 changes: 1 addition & 5 deletions numpy/lib/ufunclike.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
__all__ = ['fix', 'isneginf', 'isposinf']

import numpy.core.numeric as nx
from numpy.core.overrides import array_function_dispatch, ENABLE_ARRAY_FUNCTION
from numpy.core.overrides import array_function_dispatch
import warnings
import functools

Expand Down Expand Up @@ -55,10 +55,6 @@ def func(x, out=None, **kwargs):
return func


if not ENABLE_ARRAY_FUNCTION:
_fix_out_named_y = _deprecate_out_named_y


@_deprecate_out_named_y
def _dispatcher(x, out=None):
return (x, out)
Expand Down
5 changes: 2 additions & 3 deletions numpy/testing/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,8 @@ def __array_function__(self, *args, **kwargs):

a = np.array([1., 2.]).view(MyArray)
b = np.array([2., 3.]).view(MyArray)
if np.core.overrides.ENABLE_ARRAY_FUNCTION:
with assert_raises(TypeError):
np.all(a)
with assert_raises(TypeError):
np.all(a)
self._test_equal(a, a)
self._test_not_equal(a, b)
self._test_not_equal(b, a)
Expand Down
1 change: 0 additions & 1 deletion tools/travis-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ run_test()

if [ -n "$RUN_COVERAGE" ]; then
$PIP install pytest-cov
export NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1
COVERAGE_FLAG=--coverage
fi

Expand Down

0 comments on commit 96d179c

Please sign in to comment.