Repository: numpy/numpy

Issue:
from __future__ import unicode_literals breaks numpy.pad
The `pad` function accepts a `mode` parameter which can be a callable or str.  [It uses the test `isinstance(mode, str)` to determine which is the case](https://github.com/numpy/numpy/blob/master/numpy/lib/arraypad.py#L1340).  Using `from __future__ import unicode_literals` makes this fail, as the `isinstance` call returns `False`, eventually producing a `TypeError: 'unicode' object is not callable` when the `mode` argument is instead called as a function.

For example, the script:

```
from __future__ import unicode_literals
import numpy as np
np.pad([10], 2, mode='constant')
```

Fails using `python2.7` with the error:

```
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    np.pad([10], 2, mode='constant')
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy/lib/arraypad.py", line 1348, in pad
    kwargs)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy/lib/shape_base.py", line 79, in apply_along_axis
    res = func1d(arr[tuple(i.tolist())],*args)
TypeError: 'unicode' object is not callable
```

---

Would using the [`six.string_types`](https://pythonhosted.org/six/#six.string_types) comparison [_a la_ this stackexchange post](http://stackoverflow.com/a/11301392/230468) be a suitable fix?



Discussion:
Yeah, `np.pad` (and in general anything that takes a string `mode`-style argument) ought to accept unicode strings on py2. I don't know what the very simplest cleanest way to write this check would be, but we'd certainly accept a pull request fixing it.

(Maybe a helper function like

```
def _py2_fixup_unicode(s):
    if this_is_py2 and isinstance(s, unicode):
        try:
             return s.encode("ascii")
        except UnicodeEncodeError:
             pass
    return s
```

and then functions with `mode`-style arguments do `mode = _fixup_py2_unicode(mode)` at the top?)

It sounds like that might be the best, most-general solution.  In which case, is `sys.version_info` a reliable way to check the python version?

For `numpy.pad` in particular, since the only options are string and callable, what about using

```
if callable(mode):
    # treat as function....
else:
    # treat as str
```

That could also work, though since the specific thing we're worrying about
is unicode-on-py2 I kinda prefer checking for unicode -- plus in your way,
the code below has to be prepared to handle both unicode and str, while in
mine it only has to work for str. But really there are lots of ways that
would work -- someone just needs to write the patch :-)

On Sun, Jan 24, 2016 at 2:50 PM, Luke notifications@github.com wrote:

> It sounds like that might be the best, most-general solution. In which
> case, is sys.version_info a reliable way to check the python version?
> 
> For numpy.pad in particular, since the only options are string and
> callable, what about using
> 
> if callable(mode):
>     # treat as function....
> else:
>     # treat as str
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/numpy/numpy/issues/7112#issuecomment-174349793.

## 

Nathaniel J. Smith -- https://vorpus.org http://vorpus.org

`np.compat.basestring` and/or `np.compat.sixu` might help? Anyway, I am not sure whether `np.pad` may be the least of our problems, do array fields and other C-side string arguments work with it? I don't remember fixing it, and I seem to remember it was a problem.

Now that we have dropped Python 3.2 and 3.3, sixu can be replaced by `u"..."`. Unicode literals broke a lot of code, which is why they weren't part of the Python2, Python3 code unification, but it may be that we can do something about that now. I don't recall the exact problems that came up.

The issue here isn't that we need Unicode literals but that np.pad should
respond gracefully if someone passes in a Unicode literal. I agree that
there are probably other places in the code that don't handle this well
either (I think that's @seberg's point?), but I guess we should wait for a
bug to be reported and then fix those too?
On Jan 25, 2016 7:09 AM, "Charles Harris" notifications@github.com wrote:

> Now that we have dropped Python 3.2 and 3.3, sixu can be replaced by
> u"...". Unicode literals broke a lot of code, which is why they weren't
> part of the Python2, Python3 code unification, but it may be that we can do
> something about that now. I don't recall the exact problems that came up.
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/numpy/numpy/issues/7112#issuecomment-174537453.

Yeah, as far as I understand Chuck we might now have a better chance to fix things then before. Array fields in dtypes definitely don't work in all regards at least. But that does not mean we should not just fix this. If we ever find something we cannot fix then so be it, but this should not be hard to fix.


Resolving patch:
diff --git a/numpy/lib/arraypad.py b/numpy/lib/arraypad.py
--- a/numpy/lib/arraypad.py
+++ b/numpy/lib/arraypad.py
@@ -1337,7 +1337,7 @@ def pad(array, pad_width, mode, **kwargs):
         'reflect_type': 'even',
         }
 
-    if isinstance(mode, str):
+    if isinstance(mode, np.compat.basestring):
         # Make sure have allowed kwargs appropriate for mode
         for key in kwargs:
             if key not in allowedkwargs[mode]:


Test patch:
diff --git a/numpy/lib/tests/test_arraypad.py b/numpy/lib/tests/test_arraypad.py
--- a/numpy/lib/tests/test_arraypad.py
+++ b/numpy/lib/tests/test_arraypad.py
@@ -953,6 +953,17 @@ def test_check_simple(self):
         assert_array_equal(a, b)
 
 
+class TestUnicodeInput(TestCase):
+    def test_unicode_mode(self):
+        try:
+            constant_mode = unicode('constant')
+        except NameError:
+            constant_mode = 'constant'
+        a = np.pad([1], 2, mode=constant_mode)
+        b = np.array([0, 0, 1, 0, 0])
+        assert_array_equal(a, b)
+
+
 class ValueError1(TestCase):
     def test_check_simple(self):
         arr = np.arange(30)