Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c7b1269

Browse filesBrowse files
committed
BUG: Impelement ArrayFunctionDispatcher.__get__
While functions should not normally need this, Python functions do provide it (C functions do not, but we are a fatter object anyway). By implementing `__get__` we also ensure that `inspect.isroutine()` passes. And by that we ensure that Sphinx considers these a `py:function:` role. Closes gh-23032
1 parent b2badd7 commit c7b1269
Copy full SHA for c7b1269

File tree

Expand file treeCollapse file tree

2 files changed

+49
-1
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+49
-1
lines changed

‎numpy/core/src/multiarray/arrayfunction_override.c

Copy file name to clipboardExpand all lines: numpy/core/src/multiarray/arrayfunction_override.c
+16-1Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,19 @@ dispatcher_repr(PyObject *self)
637637
return PyUnicode_FromFormat("<function %S at %p>", name, self);
638638
}
639639

640+
641+
static PyObject *
642+
func_dispatcher___get__(PyObject *self, PyObject *obj, PyObject *cls)
643+
{
644+
if (obj == NULL) {
645+
/* Act like a static method, no need to bind */
646+
Py_INCREF(self);
647+
return self;
648+
}
649+
return PyMethod_New(self, obj);
650+
}
651+
652+
640653
static PyObject *
641654
dispatcher_get_implementation(
642655
PyArray_ArrayFunctionDispatcherObject *self, void *NPY_UNUSED(closure))
@@ -677,9 +690,11 @@ NPY_NO_EXPORT PyTypeObject PyArrayFunctionDispatcher_Type = {
677690
.tp_new = (newfunc)dispatcher_new,
678691
.tp_str = (reprfunc)dispatcher_str,
679692
.tp_repr = (reprfunc)dispatcher_repr,
680-
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_VECTORCALL,
693+
.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_VECTORCALL
694+
| Py_TPFLAGS_METHOD_DESCRIPTOR),
681695
.tp_methods = func_dispatcher_methods,
682696
.tp_getset = func_dispatcher_getset,
697+
.tp_descr_get = func_dispatcher___get__,
683698
.tp_call = &PyVectorcall_Call,
684699
.tp_vectorcall_offset = offsetof(PyArray_ArrayFunctionDispatcherObject, vectorcall),
685700
};

‎numpy/core/tests/test_overrides.py

Copy file name to clipboardExpand all lines: numpy/core/tests/test_overrides.py
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,3 +690,36 @@ def test_like_as_none(self, function, args, kwargs):
690690
array_like.fill(1)
691691
expected.fill(1)
692692
assert_equal(array_like, expected)
693+
694+
695+
@requires_array_function
696+
def test_function_like():
697+
# We provide a `__get__` implementation, make sure it works
698+
assert type(np.mean) is np.core._multiarray_umath._ArrayFunctionDispatcher
699+
700+
class MyClass:
701+
def __array__(self):
702+
# valid argument to mean:
703+
return np.arange(3)
704+
705+
func1 = staticmethod(np.mean)
706+
func2 = np.mean
707+
func3 = classmethod(np.mean)
708+
709+
m = MyClass()
710+
assert m.func1([10]) == 10
711+
assert m.func2() == 1 # mean of the arange
712+
with pytest.raises(TypeError, match="unsupported operand type"):
713+
# Tries to operate on the class
714+
m.func3()
715+
716+
# Manual binding also works (the above may shortcut):
717+
bound = np.mean.__get__(m, MyClass)
718+
assert bound() == 1
719+
720+
bound = np.mean.__get__(None, MyClass) # unbound actually
721+
assert bound([10]) == 10
722+
723+
bound = np.mean.__get__(MyClass) # classmethod
724+
with pytest.raises(TypeError, match="unsupported operand type"):
725+
bound()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.