Repository: numpy/numpy

Issue:
ENH: call np.positive in ndarray.__pos__, and issue a deprecation warning when it falls
As I wrote in https://github.com/numpy/numpy/pull/8967#issuecomment-300273518:

> Probably, the desired behavior is something like:
```python
def __pos__(self):
    try:
        return np.positive(self)
    except TypeError:
        issue deprecation warning
        return self.copy()
```
> (This may be a little tricky, though, because it will need to be done in C.)

This will be useful for ndarray subclasses that override `__array_ufunc__`.


Discussion:
I think you also want `if self.__array_ufunc__ != ndarray.__array_ufunc__` in there, so that if `__array_ufunc__` raises an unrelated `TypeError`, it's not just swallowed
In fact, that could potentially replace the `TypeError` catch altogether?
```python
if self.__array_ufunc__ is not ndarray.__array_ufunc__:
    return np.positive(self)
else:
    return self.copy()
```
I don't think we'd even need to deprecate anything if we did it this way.
Difference would be that `__array_wrap__` would never see `np.positive`, but I don't think we care. So that does sound like a good solution
Or more precisely, `__array_wrap__` would see explicit calls to `np.positive`, but wouldn't see calls to `__pos__` (just like today). And that's actually probably what we want, since we don't want to risk breaking existing `__array_wrap__` users.
> I don't think we'd even need to deprecate anything if we did it this way.

I also wanted to deprecate calls like `+np.array(['foo'])`, which currently works but should really raise an error.

But we could switch to only catching `TypeError` if `__array_ufunc__` is not set. Something like:
```python
def __pos__(self):
    if self.__array_ufunc__ is not ndarray.__array_ufunc__:
        return np.positive(self)
    else:
        try:
            return np.positive(self)
        except TypeError:
            issue deprecation warning
            return self.copy()
```
This would give us a path to eventually replacing the implementation with:
```python
def __pos__(self):
    return np.positive(self)
```
I'll just say that it would be really nice if `np.positive` were invoked by `operator.pos`. With `__array_ufunc__` I've been able to remove all of the magic method overrides on my ndarray subclass except for the override for `__pos__` due to this issue.
@ngoldbaum: Although it's also really not hard for you to use `def __pos__(self): return np.positive(self)` until this is fixed :)
I realize, it's just yet another corner case that an implementer of an ndarray subclass needs to deal with.
@ngoldbaum this logic is all pretty self-contained (I would start [here](https://github.com/numpy/numpy/blob/d0c15fab8e3c0324914baede7496810485eb4e56/numpy/core/src/multiarray/number.c#L1051)), so implementing something like @njsmith's suggestion both would be straightforward. Contributions would certainly be welcome!

It would indeed be nice to get this into 1.13 before the final release.
See #11450 for a fix.

Resolving patch:
diff --git a/numpy/lib/type_check.py b/numpy/lib/type_check.py
--- a/numpy/lib/type_check.py
+++ b/numpy/lib/type_check.py
@@ -215,7 +215,7 @@ def iscomplex(x):
     if issubclass(ax.dtype.type, _nx.complexfloating):
         return ax.imag != 0
     res = zeros(ax.shape, bool)
-    return +res  # convert to array-scalar if needed
+    return res[()]   # convert to scalar if needed
 
 def isreal(x):
     """


Test patch:
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py
--- a/numpy/core/tests/test_deprecations.py
+++ b/numpy/core/tests/test_deprecations.py
@@ -505,9 +505,16 @@ class TestGeneratorSum(_DeprecationTestCase):
     def test_generator_sum(self):
         self.assert_deprecated(np.sum, args=((i for i in range(5)),))
 
+
 class TestSctypeNA(_VisibleDeprecationTestCase):
     # 2018-06-24, 1.16
     def test_sctypeNA(self):
         self.assert_deprecated(lambda: np.sctypeNA['?'])
         self.assert_deprecated(lambda: np.typeNA['?'])
         self.assert_deprecated(lambda: np.typeNA.get('?'))
+
+
+class TestPositiveOnNonNumerical(_DeprecationTestCase):
+    # 2018-06-28, 1.16.0
+    def test_positive_on_non_number(self):
+        self.assert_deprecated(operator.pos, args=(np.array('foo'),))
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -3414,6 +3414,16 @@ def pow_for(exp, arr):
         assert_equal(obj_arr ** -1, pow_for(-1, obj_arr))
         assert_equal(obj_arr ** 2, pow_for(2, obj_arr))
 
+    def test_pos_array_ufunc_override(self):
+        class A(np.ndarray):
+            def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
+                return getattr(ufunc, method)(*[i.view(np.ndarray) for
+                                                i in inputs], **kwargs)
+        tst = np.array('foo').view(A)
+        with assert_raises(TypeError):
+            +tst
+
+
 class TestTemporaryElide(object):
     # elision is only triggered on relatively large arrays