diff --git a/src/future/builtins/__init__.py b/src/future/builtins/__init__.py index 216465a1..8bc1649d 100644 --- a/src/future/builtins/__init__.py +++ b/src/future/builtins/__init__.py @@ -11,7 +11,7 @@ # The isinstance import is no longer needed. We provide it only for # backward-compatibility with future v0.8.2. It will be removed in future v1.0. from future.builtins.misc import (ascii, chr, hex, input, isinstance, next, - oct, open, pow, round, super) + oct, open, pow, round, super, max, min) from future.utils import PY3 if PY3: @@ -43,7 +43,7 @@ __all__ = ['filter', 'map', 'zip', 'ascii', 'chr', 'hex', 'input', 'next', 'oct', 'open', 'pow', 'round', 'super', - 'bytes', 'dict', 'int', 'list', 'object', 'range', 'str', + 'bytes', 'dict', 'int', 'list', 'object', 'range', 'str', 'max', 'min' ] else: diff --git a/src/future/builtins/misc.py b/src/future/builtins/misc.py index 90dc384a..f86ce5f3 100644 --- a/src/future/builtins/misc.py +++ b/src/future/builtins/misc.py @@ -13,6 +13,8 @@ - ``open`` (equivalent to io.open on Py2) - ``super`` (backport of Py3's magic zero-argument super() function - ``round`` (new "Banker's Rounding" behaviour from Py3) +- ``max`` (new default option from Py3.4) +- ``min`` (new default option from Py3.4) ``isinstance`` is also currently exported for backwards compatibility with v0.8.2, although this has been deprecated since v0.9. @@ -59,6 +61,8 @@ from future.builtins.newnext import newnext as next from future.builtins.newround import newround as round from future.builtins.newsuper import newsuper as super + from future.builtins.new_min_max import newmax as max + from future.builtins.new_min_max import newmin as min from future.types.newint import newint _SENTINEL = object() @@ -89,11 +93,12 @@ def pow(x, y, z=_SENTINEL): else: return _builtin_pow(x+0j, y, z) + # ``future`` doesn't support Py3.0/3.1. If we ever did, we'd add this: # callable = __builtin__.callable __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct', - 'open', 'pow', 'round', 'super'] + 'open', 'pow', 'round', 'super', 'max', 'min'] else: import builtins @@ -109,8 +114,14 @@ def pow(x, y, z=_SENTINEL): pow = builtins.pow round = builtins.round super = builtins.super - - __all__ = [] + if utils.PY34_PLUS: + max = builtins.max + min = builtins.min + __all__ = [] + else: + from future.builtins.new_min_max import newmax as max + from future.builtins.new_min_max import newmin as min + __all__ = ['min', 'max'] # The callable() function was removed from Py3.0 and 3.1 and # reintroduced into Py3.2+. ``future`` doesn't support Py3.0/3.1. If we ever diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py new file mode 100644 index 00000000..8fd63fdf --- /dev/null +++ b/src/future/builtins/new_min_max.py @@ -0,0 +1,52 @@ +from future import utils +if utils.PY2: + from __builtin__ import max as _builtin_max, min as _builtin_min +else: + from builtins import max as _builtin_max, min as _builtin_min + + +def newmin(*args, **kwargs): + return new_min_max(_builtin_min, *args, **kwargs) + + +def newmax(*args, **kwargs): + return new_min_max(_builtin_max, *args, **kwargs) + + +def new_min_max(_builtin_func, *args, **kwargs): + """ + To support the argument "default" introduced in python 3.4 for min and max + :param _builtin_func: builtin min or builtin max + :param args: + :param kwargs: + :return: returns the min or max based on the arguments passed + """ + + for key, _ in kwargs.items(): + if key not in set(['key', 'default']): + raise TypeError('Illegal argument %s', key) + + if len(args) == 0: + raise TypeError + + if len(args) != 1 and kwargs.get('default') is not None: + raise TypeError + + if len(args) == 1: + try: + next(iter(args[0])) + except StopIteration: + if kwargs.get('default') is not None: + return kwargs.get('default') + else: + raise ValueError('iterable is an empty sequence') + if kwargs.get('key') is not None: + return _builtin_func(args[0], key=kwargs.get('key')) + else: + return _builtin_func(args[0]) + + if len(args) > 1: + if kwargs.get('key') is not None: + return _builtin_func(args, key=kwargs.get('key')) + else: + return _builtin_func(args) diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py index 090a2bbe..9ce2ec08 100644 --- a/src/future/utils/__init__.py +++ b/src/future/utils/__init__.py @@ -56,6 +56,7 @@ PY3 = sys.version_info[0] >= 3 +PY34_PLUS = sys.version_info[0:2] >= (3, 4) PY35_PLUS = sys.version_info[0:2] >= (3, 5) PY36_PLUS = sys.version_info[0:2] >= (3, 6) PY2 = sys.version_info[0] == 2 diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py index 05d597a5..d983f9d6 100644 --- a/tests/test_future/test_builtins.py +++ b/tests/test_future/test_builtins.py @@ -6,7 +6,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals from future.builtins import (bytes, dict, int, range, round, str, super, ascii, chr, hex, input, next, oct, open, pow, - filter, map, zip) + filter, map, zip, min, max) from future.utils import PY3, exec_, native_str, implements_iterator from future.tests.base import (unittest, skip26, expectedFailurePY2, @@ -1099,6 +1099,13 @@ def test_max(self): self.assertEqual(max(data, key=f), sorted(reversed(data), key=f)[-1]) + self.assertEqual(max([], default=5), 5) + with self.assertRaises(TypeError): + max(None, default=5) + with self.assertRaises(TypeError): + max(1, 2, default=0) + self.assertEqual(max([], default=0), 0) + def test_min(self): self.assertEqual(min('123123'), '1') self.assertEqual(min(1, 2, 3), 1) @@ -1140,6 +1147,12 @@ def __getitem__(self, index): f = keys.__getitem__ self.assertEqual(min(data, key=f), sorted(data, key=f)[0]) + self.assertEqual(min([], default=5), 5) + self.assertEqual(min([], default=0), 0) + with self.assertRaises(TypeError): + max(None, default=5) + with self.assertRaises(TypeError): + max(1, 2, default=0) def test_next(self): it = iter(range(2))