Skip to content

Commit 228a297

Browse files
authored
Merge pull request #514 from gazpachoking/fix_min_max
Fix min/max functions with generators, and 'None' default
2 parents 615ee1e + f4926e5 commit 228a297

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

src/future/builtins/new_min_max.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import itertools
2+
13
from future import utils
24
if utils.PY2:
35
from __builtin__ import max as _builtin_max, min as _builtin_min
46
else:
57
from builtins import max as _builtin_max, min as _builtin_min
68

9+
_SENTINEL = object()
10+
711

812
def newmin(*args, **kwargs):
913
return new_min_max(_builtin_min, *args, **kwargs)
@@ -29,21 +33,24 @@ def new_min_max(_builtin_func, *args, **kwargs):
2933
if len(args) == 0:
3034
raise TypeError
3135

32-
if len(args) != 1 and kwargs.get('default') is not None:
36+
if len(args) != 1 and kwargs.get('default', _SENTINEL) is not _SENTINEL:
3337
raise TypeError
3438

3539
if len(args) == 1:
40+
iterator = iter(args[0])
3641
try:
37-
next(iter(args[0]))
42+
first = next(iterator)
3843
except StopIteration:
39-
if kwargs.get('default') is not None:
44+
if kwargs.get('default', _SENTINEL) is not _SENTINEL:
4045
return kwargs.get('default')
4146
else:
42-
raise ValueError('iterable is an empty sequence')
47+
raise ValueError('{}() arg is an empty sequence'.format(_builtin_func.__name__))
48+
else:
49+
iterator = itertools.chain([first], iterator)
4350
if kwargs.get('key') is not None:
44-
return _builtin_func(args[0], key=kwargs.get('key'))
51+
return _builtin_func(iterator, key=kwargs.get('key'))
4552
else:
46-
return _builtin_func(args[0])
53+
return _builtin_func(iterator)
4754

4855
if len(args) > 1:
4956
if kwargs.get('key') is not None:

tests/test_future/test_builtins.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,7 @@ def test_max(self):
11051105
with self.assertRaises(TypeError):
11061106
max(1, 2, default=0)
11071107
self.assertEqual(max([], default=0), 0)
1108+
self.assertIs(max([], default=None), None)
11081109

11091110
def test_min(self):
11101111
self.assertEqual(min('123123'), '1')
@@ -1123,6 +1124,7 @@ class BadSeq:
11231124
def __getitem__(self, index):
11241125
raise ValueError
11251126
self.assertRaises(ValueError, min, BadSeq())
1127+
self.assertEqual(max(x for x in [5, 4, 3]), 5)
11261128

11271129
for stmt in (
11281130
"min(key=int)", # no args
@@ -1149,11 +1151,15 @@ def __getitem__(self, index):
11491151
sorted(data, key=f)[0])
11501152
self.assertEqual(min([], default=5), 5)
11511153
self.assertEqual(min([], default=0), 0)
1154+
self.assertIs(min([], default=None), None)
11521155
with self.assertRaises(TypeError):
11531156
max(None, default=5)
11541157
with self.assertRaises(TypeError):
11551158
max(1, 2, default=0)
11561159

1160+
# Test iterables that can only be looped once #510
1161+
self.assertEqual(min(x for x in [5]), 5)
1162+
11571163
def test_next(self):
11581164
it = iter(range(2))
11591165
self.assertEqual(next(it), 0)

0 commit comments

Comments
 (0)