Repository: numpy/numpy

Issue:
linspace floats
I'm not sure if it's a bug or intended behavior, but may it make sense to obtain the same result in the two following linspace examples?

```
Python 2.7.9 (default, Mar  1 2015, 12:57:24) 
[GCC 4.9.2] on linux2
>>> import numpy
>>> numpy.version.version
'1.12.0.dev0+bd905a8'
>>> numpy.linspace(2.0, 3.0, num=5)
array([ 2.  ,  2.25,  2.5 ,  2.75,  3.  ])
>>> numpy.linspace(2.0, 3.0, 0.25)
array([], dtype=float64)
```



Discussion:
Hi, I am not a developer and but it seems that 0.25 is taken as 0 in number of samples, which is a non-negative integer. I also suppose what you meant was `np.arange(2.0, 3.0, 0.25)` which takes step size. Please correct me if I am wrong.

Sure, we should implement an `index_deprecate` for python side code, and then at some point change it to `operator.index` logic here (or if you just want to do it here, try operator.index, if fail try with "int" and give a (visible)deprecationwarning just like all the other integer deprecations).

Ah ok the third argument is always supposed to be a number of samples, and never a step (as like as in the `range` built-in). It's a bit misleading, maybe a deprecated message would help to understand this behavior but I don't get exactly what do you plan to implement (I could try to fix it if it's not a major change).

The best way to get the integer is to not do `int(num)`, but instead do `operator.index(num)`. But we should not just change it. Instead we should do a deprecation cycle on it. Something like a:

```
try:
    num = operator.index(num)
except TypeError:
    num = int(num)
    warnings.warn("Some text", DeprecationWarning, stacklevel=2)
```

would do. Could think about making this part our own little function, so that we can reuse it elsewhere.

@emilienkofman: the bug is that right now we call `int` on the number-of-samples argument, which is too aggressive: for example it will happily turn `float`s into integers, which is usually a mistake.

What we would like is for it to instead call `operator.index` (for the stdlib `operator` module), which is like `int` but less aggressive. (It's called `index` because the original place this kind of problem was noticed was in indexing expressions like `mylist[0.5]`, where you also want to raise an error instead of converting the value to `int`.)

We can't just switch from the current behavior to the behavior that we would like, because that might rudely break people's code. So what @seberg is suggesting is that first, we define a helper function called `index_deprecate` (or whatever), which tries calling both `int` and `index`, and if both succeed then it is happy; and if `int` succeeds but `index` fails then it succeeds (so it doesn't break people's code) BUT it issues a DeprecationWarning (so people get solve warning that their code will break in the future when we stop calling `int`, and have a chance to fix it ahead of time).

There are probably other places that could also use such a function -- we've been cleaning up a lot of these integer things in numpy's C code, but haven't really looked at numpy's python code yet.


Resolving patch:
diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py
--- a/numpy/core/function_base.py
+++ b/numpy/core/function_base.py
@@ -18,18 +18,6 @@
     overrides.array_function_dispatch, module='numpy')
 
 
-def _index_deprecate(i, stacklevel=2):
-    try:
-        i = operator.index(i)
-    except TypeError:
-        msg = ("object of type {} cannot be safely interpreted as "
-               "an integer.".format(type(i)))
-        i = int(i)
-        stacklevel += 1
-        warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel)
-    return i
-
-
 def _linspace_dispatcher(start, stop, num=None, endpoint=None, retstep=None,
                          dtype=None, axis=None):
     return (start, stop)
@@ -125,8 +113,13 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,
     >>> plt.show()
 
     """
-    # 2016-02-25, 1.12
-    num = _index_deprecate(num)
+    try:
+        num = operator.index(num)
+    except TypeError:
+        raise TypeError(
+            "object of type {} cannot be safely interpreted as an integer."
+                .format(type(num)))
+
     if num < 0:
         raise ValueError("Number of samples, %s, must be non-negative." % num)
     div = (num - 1) if endpoint else num


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
@@ -323,22 +323,6 @@ def test_data_attr_assignment(self):
         self.assert_deprecated(a.__setattr__, args=('data', b.data))
 
 
-class TestLinspaceInvalidNumParameter(_DeprecationTestCase):
-    """Argument to the num parameter in linspace that cannot be
-    safely interpreted as an integer is deprecated in 1.12.0.
-
-    Argument to the num parameter in linspace that cannot be
-    safely interpreted as an integer should not be allowed.
-    In the interest of not breaking code that passes
-    an argument that could still be interpreted as an integer, a
-    DeprecationWarning will be issued for the time being to give
-    developers time to refactor relevant code.
-    """
-    def test_float_arg(self):
-        # 2016-02-25, PR#7328
-        self.assert_deprecated(np.linspace, args=(0, 10, 2.5))
-
-
 class TestBinaryReprInsufficientWidthParameterForRepresentation(_DeprecationTestCase):
     """
     If a 'width' parameter is passed into ``binary_repr`` that is insufficient to
@@ -594,7 +578,7 @@ class Test_GetSet_NumericOps(_DeprecationTestCase):
     def test_get_numeric_ops(self):
         from numpy.core._multiarray_tests import getset_numericops
         self.assert_deprecated(getset_numericops, num=2)
-        
+
         # empty kwargs prevents any state actually changing which would break
         # other tests.
         self.assert_deprecated(np.set_numeric_ops, kwargs={})
diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py
--- a/numpy/core/tests/test_function_base.py
+++ b/numpy/core/tests/test_function_base.py
@@ -236,10 +236,7 @@ def test_basic(self):
     def test_corner(self):
         y = list(linspace(0, 1, 1))
         assert_(y == [0.0], y)
-        with suppress_warnings() as sup:
-            sup.filter(DeprecationWarning, ".*safely interpreted as an integer")
-            y = list(linspace(0, 1, 2.5))
-            assert_(y == [0.0, 1.0])
+        assert_raises(TypeError, linspace, 0, 1, num=2.5)
 
     def test_type(self):
         t1 = linspace(0, 1, 0).dtype