Skip to content

DEP: deprecate passing a generator to stack functions #12280

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 4 commits into from
Oct 29, 2018
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
26 changes: 18 additions & 8 deletions numpy/core/shape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import functools
import operator
import types
import warnings

from . import numeric as _nx
from . import overrides
Expand Down Expand Up @@ -204,11 +206,22 @@ def atleast_3d(*arys):
return res


def _vstack_dispatcher(tup):
return tup
def _arrays_for_stack_dispatcher(arrays, stacklevel=4):
if not hasattr(arrays, '__getitem__') and hasattr(arrays, '__iter__'):
warnings.warn('arrays to stack must be passed as a "sequence" type '
'such as list or tuple. Support for non-sequence '
'iterables such as generators is deprecated as of '
'NumPy 1.16 and will raise an error in the future.',
FutureWarning, stacklevel=stacklevel)
Copy link
Member

@eric-wieser eric-wieser Oct 29, 2018

Choose a reason for hiding this comment

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

This raises an interesting point - is wrapping everything in array_function_dispatch going to make all our warnings be emitted within the __array_function__ dispatcher?

Copy link
Member Author

Choose a reason for hiding this comment

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

In this case, yes.

In general: warnings about changed functions arguments/signatures should go in the dispatcher, since they are relevant whether you are using NumPy or not. Warnings about changed function behavior should remain in the implementations.

return ()
return arrays


@array_function_dispatch(_vstack_dispatcher)
def _vhstack_dispatcher(tup):
return _arrays_for_stack_dispatcher(tup)


@array_function_dispatch(_vhstack_dispatcher)
def vstack(tup):
"""
Stack arrays in sequence vertically (row wise).
Expand Down Expand Up @@ -264,11 +277,7 @@ def vstack(tup):
return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)


def _hstack_dispatcher(tup):
return tup


@array_function_dispatch(_hstack_dispatcher)
@array_function_dispatch(_vhstack_dispatcher)
def hstack(tup):
"""
Stack arrays in sequence horizontally (column wise).
Expand Down Expand Up @@ -325,6 +334,7 @@ def hstack(tup):


def _stack_dispatcher(arrays, axis=None, out=None):
arrays = _arrays_for_stack_dispatcher(arrays, stacklevel=6)
for a in arrays:
yield a
if out is not None:
Expand Down
21 changes: 19 additions & 2 deletions numpy/core/tests/test_shape_base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import division, absolute_import, print_function

import warnings
import sys
import numpy as np
from numpy.core import (
array, arange, atleast_1d, atleast_2d, atleast_3d, block, vstack, hstack,
Expand All @@ -10,7 +11,7 @@
_block_concatenate, _block_slicing)
from numpy.testing import (
assert_, assert_raises, assert_array_equal, assert_equal,
assert_raises_regex, assert_almost_equal
assert_raises_regex, assert_warns, assert_almost_equal
)

from numpy.compat import long
Expand Down Expand Up @@ -155,6 +156,14 @@ def test_2D_array(self):
desired = array([[1, 1], [2, 2]])
assert_array_equal(res, desired)

def test_generator(self):
with assert_warns(FutureWarning):
hstack((np.arange(3) for _ in range(2)))
if sys.version_info.major > 2:
# map returns a list on Python 2
with assert_warns(FutureWarning):
hstack(map(lambda x: x, np.ones((3, 2))))


class TestVstack(object):
def test_non_iterable(self):
Expand Down Expand Up @@ -191,6 +200,10 @@ def test_2D_array2(self):
desired = array([[1, 2], [1, 2]])
assert_array_equal(res, desired)

def test_generator(self):
with assert_warns(FutureWarning):
vstack((np.arange(3) for _ in range(2)))


class TestConcatenate(object):
def test_returns_copy(self):
Expand Down Expand Up @@ -354,7 +367,7 @@ def test_stack():
arrays = [np.random.randn(3, 4) for _ in range(10)]
axes = [0, 1, 2, -1, -2, -3]
expected_shapes = [(10, 3, 4), (3, 10, 4), (3, 4, 10),
(3, 4, 10), (3, 10, 4), (10, 3, 4)]
(3, 4, 10), (3, 10, 4), (10, 3, 4)]
for axis, expected_shape in zip(axes, expected_shapes):
assert_equal(np.stack(arrays, axis).shape, expected_shape)
# empty arrays
Expand All @@ -372,6 +385,10 @@ def test_stack():
stack, [np.zeros((3, 3)), np.zeros(3)], axis=1)
assert_raises_regex(ValueError, 'must have the same shape',
stack, [np.arange(2), np.arange(3)])
# generator is deprecated
with assert_warns(FutureWarning):
result = stack((x for x in range(3)))
assert_array_equal(result, np.array([0, 1, 2]))


# See for more information on how to parametrize a whole class
Expand Down
5 changes: 3 additions & 2 deletions numpy/lib/shape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from numpy.core.multiarray import normalize_axis_index
from numpy.core import overrides
from numpy.core import vstack, atleast_3d
from numpy.core.shape_base import _arrays_for_stack_dispatcher
from numpy.lib.index_tricks import ndindex
from numpy.matrixlib.defmatrix import matrix # this raises all the right alarm bells

Expand Down Expand Up @@ -591,7 +592,7 @@ def expand_dims(a, axis):


def _column_stack_dispatcher(tup):
return tup
return _arrays_for_stack_dispatcher(tup)


@array_function_dispatch(_column_stack_dispatcher)
Expand Down Expand Up @@ -638,7 +639,7 @@ def column_stack(tup):


def _dstack_dispatcher(tup):
return tup
return _arrays_for_stack_dispatcher(tup)


@array_function_dispatch(_dstack_dispatcher)
Expand Down
9 changes: 9 additions & 0 deletions numpy/lib/tests/test_shape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ def test_unequal_split(self):
a = np.arange(10)
assert_raises(ValueError, split, a, 3)


class TestColumnStack(object):
def test_non_iterable(self):
assert_raises(TypeError, column_stack, 1)
Expand All @@ -481,6 +482,10 @@ def test_2D_arrays(self):
actual = np.column_stack((a, b))
assert_equal(actual, expected)

def test_generator(self):
with assert_warns(FutureWarning):
column_stack((np.arange(3) for _ in range(2)))


class TestDstack(object):
def test_non_iterable(self):
Expand Down Expand Up @@ -514,6 +519,10 @@ def test_2D_array2(self):
desired = np.array([[[1, 1], [2, 2]]])
assert_array_equal(res, desired)

def test_generator(self):
with assert_warns(FutureWarning):
dstack((np.arange(3) for _ in range(2)))


# array_split has more comprehensive test of splitting.
# only do simple test on hsplit, vsplit, and dsplit
Expand Down