8000 MAINT: revert __skip_array_function__ from NEP-18 by shoyer · Pull Request #13627 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

MAINT: revert __skip_array_function__ from NEP-18 #13627

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 3 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ matrix:
env: NPY_RELAXED_STRIDES_CHECKING=0
- python: 3.6
env: USE_WHEEL=1 NPY_RELAXED_STRIDES_DEBUG=1
- python: 3.6
env: NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=0
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consolidate this CI run into one of the others?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We get 5 parallel runs and we are already over 10, so as long as there are fewer than 15 test runs it shouldn't make much difference. The runs vary in time, so it isn't possible to predict exactly what will happen, but close enough.

- python: 3.6
env:
- BLAS=None
Expand Down
16 changes: 15 additions & 1 deletion numpy/core/overrides.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
"""Implementation of __array_function__ overrides from NEP-18."""
import collections
import functools
import os
import textwrap

from numpy.core._multiarray_umath import (
add_docstring, implement_array_function, _get_implementing_args)
from numpy.compat._inspect import getargspec


ARRAY_FUNCTION_ENABLED = bool(
int(os.environ.get('NUMPY_EXPERIMENTAL_ARRAY_FUNCTION', 1)))


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

if not ARRAY_FUNCTION_ENABLED:
def decorator(implementation):
if docs_from_dispatcher:
add_docstring(implementation, dispatcher.__doc__)
if module is not None:
implementation.__module__ = module
return implementation
return decorator

def decorator(implementation):
if verify:
verify_matching_signatures(implementation, dispatcher)
Expand Down Expand Up @@ -172,7 +186,7 @@ def {name}(*args, **kwargs):
if module is not None:
public_api.__module__ = module

public_api.__skip_array_function__ = implementation
public_api._implementation = implementation

return public_api

Expand Down
11 changes: 11 additions & 0 deletions numpy/core/shape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ def vstack(tup):
[4]])

"""
if not overrides.ARRAY_FUNCTION_ENABLED:
# raise warning if necessary
_arrays_for_stack_dispatcher(tup, stacklevel=2)
return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)


Expand Down Expand Up @@ -324,6 +327,10 @@ def hstack(tup):
[3, 4]])

"""
if not overrides.ARRAY_FUNCTION_ENABLED:
# raise warning if necessary
_arrays_for_stack_dispatcher(tup, stacklevel=2)

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 @@ -400,6 +407,10 @@ def stack(arrays, axis=0, out=None):
[3, 4]])

"""
if not overrides.ARRAY_FUNCTION_ENABLED:
# raise warning if necessary
_arrays_for_stack_dispatcher(arrays, stacklevel=2)

arrays = [asanyarray(arr) for arr in arrays]
if not arrays:
raise ValueError('need at least one array to stack')
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/src/multiarray/arrayfunction_override.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ array_function_method_impl(PyObject *func, PyObject *types, PyObject *args,
}
}

implementation = PyObject_GetAttr(func, npy_ma_str_skip_array_function);
implementation = PyObject_GetAttr(func, npy_ma_str_implementation);
if (implementation == NULL) {
return NULL;
}
Expand Down
7 changes: 3 additions & 4 deletions numpy/core/src/multiarray/multiarraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -4498,7 +4498,7 @@ NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_prepare = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_wrap = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_finalize = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_ufunc = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_skip_array_function = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_implementation = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_order = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_copy = NULL;
NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_dtype = NULL;
Expand All @@ -4514,8 +4514,7 @@ intern_strings(void)
npy_ma_str_array_wrap = PyUString_InternFromString("__array_wrap__");
npy_ma_str_array_finalize = PyUString_InternFromString("__array_finalize__");
npy_ma_str_ufunc = PyUString_InternFromString("__array_ufunc__");
npy_ma_str_skip_array_function = PyUString_InternFromString(
"__skip_array_function__");
npy_ma_str_implementation = PyUString_InternFromString("_implementation");
npy_ma_str_order = PyUString_InternFromString("order");
npy_ma_str_copy = PyUString_InternFromString("copy");
npy_ma_str_dtype = PyUString_InternFromString("dtype");
Expand All @@ -4525,7 +4524,7 @@ intern_strings(void)

return npy_ma_str_array && npy_ma_str_array_prepare &&
npy_ma_str_array_wrap && npy_ma_str_array_finalize &&
npy_ma_str_ufunc && npy_ma_str_skip_array_function &&
npy_ma_str_ufunc && npy_ma_str_implementation &&
npy_ma_str_order && npy_ma_str_copy && npy_ma_str_dtype &&
npy_ma_str_ndmin && npy_ma_str_axis1 && npy_ma_str_axis2;
}
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/src/multiarray/multiarraymodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_prepare;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_wrap;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_finalize;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_ufunc;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_skip_array_function;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_implementation;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_order;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_copy;
NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_dtype;
Expand Down
43 changes: 20 additions & 23 deletions numpy/core/tests/test_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@
assert_, assert_equal, assert_raises, assert_raises_regex)
from numpy.core.overrides import (
_get_implementing_args, array_function_dispatch,
verify_matching_signatures)
verify_matching_signatures, ARRAY_FUNCTION_ENABLED)
from numpy.compat import pickle
import pytest


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


def _return_not_implemented(self, *args, **kwargs):
Expand Down Expand Up @@ -143,6 +149,7 @@ def test_too_many_duck_arrays(self):

class TestNDArrayArrayFunction(object):

@requires_array_function
def test_method(self):

class Other(object):
Expand Down Expand Up @@ -190,21 +197,18 @@ class OverrideSub(np.ndarray):
result = np.concatenate((array, override_sub))
assert_equal(result, expected.view(OverrideSub))

def test_skip_array_function(self):
assert_(dispatched_one_arg.__skip_array_function__
is dispatched_one_arg.__wrapped__)

def test_no_wrapper(self):
# This shouldn't happen unless a user intentionally calls
# __array_function__ with invalid arguments, but check that we raise
# an appropriate error all the same.
array = np.array(1)
func = dispatched_one_arg.__skip_array_function__
with assert_raises_regex(AttributeError, '__skip_array_function__'):
func = lambda x: x
with assert_raises_regex(AttributeError, '_implementation'):
array.__array_function__(func=func, types=(np.ndarray,),
args=(array,), kwargs={})


@requires_array_function
class TestArrayFunctionDispatch(object):

def test_pickle(self):
Expand Down Expand Up @@ -244,6 +248,7 @@ 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 @@ -296,6 +301,7 @@ def decorator(func):
return (MyArray, implements)


@requires_array_function
class TestArrayFunctionImplementation(object):

def test_one_arg(self):
Expand Down Expand Up @@ -376,6 +382,7 @@ 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 All @@ -385,9 +392,7 @@ def _(array):

assert_equal(np.sum(MyArray()), 'yes')

def test_sum_implementation_on_list(self):
assert_equal(np.sum.__skip_array_function__([1, 2, 3]), 6)

@requires_array_function
def test_sum_on_mock_array(self):

# We need a proxy for mocks because __array_function__ is only looked
Expand All @@ -408,25 +413,17 @@ def __array__(self, *args, **kwargs):
np.sum, (ArrayProxy,), (proxy,), {})
proxy.value.__array__.assert_not_called()

proxy = ArrayProxy(mock.Mock(spec=ArrayProxy))
proxy.value.__array__.return_value = np.array(2)
result = np.sum.__skip_array_function__(proxy)
assert_equal(result, 2)
# TODO: switch to proxy.value.__array__.assert_called() and
# proxy.value.__array_function__.assert_not_called() once we drop
# Python 3.5 support.
((called_method_name, _, _),) = proxy.value.mock_calls
assert_equal(called_method_name, '__array__')

@requires_array_function
def test_sum_forwarding_implementation(self):

class MyArray(object):
class MyArray(np.ndarray):

def sum(self, axis, out):
return 'summed'

def __array_function__(self, func, types, args, kwargs):
return func.__skip_array_function__(*args, **kwargs)
return super().__array_function__(func, types, args, kwargs)

# note: the internal implementation of np.sum() calls the .sum() method
assert_equal(np.sum(MyArray()), 'summed')
array = np.array(1).view(MyArray)
assert_equal(np.sum(array), 'summed')
10 changes: 9 additions & 1 deletion numpy/lib/shape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from numpy.core.numeric import (
asarray, zeros, outer, concatenate, array, asanyarray
)
from numpy.core.fromnumeric import product, reshape, transpose
from numpy.core.fromnumeric import reshape, transpose
from numpy.core.multiarray import normalize_axis_index
from numpy.core import overrides
from numpy.core import vstack, atleast_3d
Expand Down Expand Up @@ -628,6 +628,10 @@ def column_stack(tup):
[3, 4]])

"""
if not overrides.ARRAY_FUNCTION_ENABLED:
# raise warning if necessary
_arrays_for_stack_dispatcher(tup, stacklevel=2)

arrays = []
for v in tup:
arr = array(v, copy=False, subok=True)
Expand Down Expand Up @@ -692,6 +696,10 @@ def dstack(tup):
[[3, 4]]])

"""
if not overrides.ARRAY_FUNCTION_ENABLED:
# raise warning if necessary
_arrays_for_stack_dispatcher(tup, stacklevel=2)

return _nx.concatenate([atleast_3d(_m) for _m in tup], 2)


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

import numpy.core.numeric as nx
from numpy.core.overrides import array_function_dispatch
from numpy.core.overrides import (
array_function_dispatch, ARRAY_FUNCTION_ENABLED,
)
import warnings
import functools

Expand Down Expand Up @@ -43,7 +45,7 @@ def _fix_out_named_y(f):
Allow the out argument to be passed as the name `y` (deprecated)

This decorator should only be used if _deprecate_out_named_y is used on
a corresponding dispatcher fucntion.
a corresponding dispatcher function.
"""
@functools.wraps(f)
def func(x, out=None, **kwargs):
Expand All @@ -55,13 +57,23 @@ def func(x, out=None, **kwargs):
return func


def _fix_and_maybe_deprecate_out_named_y(f):
"""
Use the appropriate decorator, depending upon if dispatching is being used.
"""
if ARRAY_FUNCTION_ENABLED:
return _fix_out_named_y(f)
else:
return _deprecate_out_named_y(f)


@_deprecate_out_named_y
def _dispatcher(x, out=None):
return (x, out)


@array_function_dispatch(_dispatcher, verify=False, module='numpy')
@_fix_out_named_y
@_fix_and_maybe_deprecate_out_named_y
def fix(x, out=None):
"""
Round to nearest integer towards zero.
Expand Down Expand Up @@ -108,7 +120,7 @@ def fix(x, out=None):


@array_function_dispatch(_dispatcher, verify=False, module='numpy')
@_fix_out_named_y
@_fix_and_maybe_deprecate_out_named_y
def isposinf(x, out=None):
"""
Test element-wise for positive infinity, return result as bool array.
Expand Down Expand Up @@ -177,7 +189,7 @@ def isposinf(x, out=None):


@array_function_dispatch(_dispatcher, verify=False, module='numpy')
@_fix_out_named_y
@_fix_and_maybe_deprecate_out_named_y
def isneginf(x, out=None):
"""
Test element-wise for negative infinity, return result as bool array.
Expand Down
3 changes: 3 additions & 0 deletions numpy/testing/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
clear_and_catch_warnings, suppress_warnings, assert_string_equal, assert_,
tempdir, temppath, assert_no_gc_cycles, HAS_REFCOUNT
)
from numpy.core.overrides import ARRAY_FUNCTION_ENABLED


class _GenericTest(object):
Expand Down Expand Up @@ -179,6 +180,8 @@ def __ne__(self, other):
self._test_not_equal(a, b)
self._test_not_equal(b, a)

@pytest.mark.skipif(
not ARRAY_FUNCTION_ENABLED, reason='requires __array_function__')
def test_subclass_that_does_not_implement_npall(self):
class MyArray(np.ndarray):
def __array_function__(self, *args, **kwargs):
Expand Down
0