From 39e9a4be1cdb8fde041efeef3321c679f1097065 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 6 Nov 2016 11:04:45 +0100 Subject: [PATCH 1/6] Prohibit Callable[[...], X] --- python2/test_typing.py | 8 ++++++++ python2/typing.py | 6 +++--- src/test_typing.py | 10 ++++++++++ src/typing.py | 6 +++--- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/python2/test_typing.py b/python2/test_typing.py index 5de042a3..1b4377e8 100644 --- a/python2/test_typing.py +++ b/python2/test_typing.py @@ -375,6 +375,14 @@ def test_cannot_instantiate(self): with self.assertRaises(TypeError): type(c)() + def test_callable_wrong_forms(self): + with self.assertRaises(TypeError): + Callable[(), int] + with self.assertRaises(TypeError): + Callable[[()], int] + with self.assertRaises(TypeError): + Callable[[int, 1], 2] + def test_callable_instance_works(self): def f(): pass diff --git a/python2/typing.py b/python2/typing.py index 5df0062a..30f7bb85 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1308,9 +1308,9 @@ def __getitem__(self, parameters): elif args == []: parameters = ((), result) else: - if not isinstance(args, list): - raise TypeError("Callable[args, result]: args must be a list." - " Got %.100r." % (args,)) + if not isinstance(args, list) or Ellipsis in args or () in args: + raise TypeError("Callable[args, result]: args must be" + " a list of types. Got %.100r." % (args,)) parameters = tuple(args) + (result,) return self.__getitem_inner__(parameters) diff --git a/src/test_typing.py b/src/test_typing.py index 3f702139..9b658f14 100644 --- a/src/test_typing.py +++ b/src/test_typing.py @@ -378,6 +378,16 @@ def test_cannot_instantiate(self): with self.assertRaises(TypeError): type(c)() + def test_callable_wrong_forms(self): + with self.assertRaises(TypeError): + Callable[[...], int] + with self.assertRaises(TypeError): + Callable[(), int] + with self.assertRaises(TypeError): + Callable[[()], int] + with self.assertRaises(TypeError): + Callable[[int, 1], 2] + def test_callable_instance_works(self): def f(): pass diff --git a/src/typing.py b/src/typing.py index 6c123962..29575830 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1221,9 +1221,9 @@ def __getitem__(self, parameters): elif args == []: parameters = ((), result) else: - if not isinstance(args, list): - raise TypeError("Callable[args, result]: args must be a list." - " Got %.100r." % (args,)) + if not isinstance(args, list) or ... in args or () in args: + raise TypeError("Callable[args, result]: args must be" + " a list of types. Got %.100r." % (args,)) parameters = tuple(args) + (result,) return self.__getitem_inner__(parameters) From f2d9c14a5961431a7e9307e3a4f470bd227dd42e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 7 Nov 2016 06:54:44 +0100 Subject: [PATCH 2/6] A simpler (and probably right) fix --- src/typing.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/typing.py b/src/typing.py index 29575830..378ff825 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1218,23 +1218,21 @@ def __getitem__(self, parameters): args, result = parameters if args is ...: parameters = (..., result) - elif args == []: - parameters = ((), result) else: - if not isinstance(args, list) or ... in args or () in args: + if not isinstance(args, list): raise TypeError("Callable[args, result]: args must be" " a list of types. Got %.100r." % (args,)) - parameters = tuple(args) + (result,) + parameters = tuple(args), result return self.__getitem_inner__(parameters) @_tp_cache def __getitem_inner__(self, parameters): - *args, result = parameters + args, result = parameters msg = "Callable[args, result]: result must be a type." result = _type_check(result, msg) - if args == [...,]: + if args == ...: return super().__getitem__((_TypingEllipsis, result)) - if args == [(),]: + if args == (): return super().__getitem__((_TypingEmpty, result)) msg = "Callable[[arg, ...], result]: each arg must be a type." args = tuple(_type_check(arg, msg) for arg in args) From 9e2ada78180371a6e6b2a0a7f3c8764035b9a31d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 7 Nov 2016 07:27:26 +0100 Subject: [PATCH 3/6] Additional cleanup as suggested --- src/typing.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/typing.py b/src/typing.py index 378ff825..03d18f85 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1194,14 +1194,12 @@ def _tree_repr(self, tree): # super()._tree_repr() for nice formatting. arg_list = [] for arg in tree[1:]: - if arg == (): - arg_list.append('[]') - elif not isinstance(arg, tuple): + if not isinstance(arg, tuple): arg_list.append(_type_repr(arg)) else: arg_list.append(arg[0]._tree_repr(arg)) - if len(arg_list) == 2: - return repr(tree[0]) + '[%s]' % ', '.join(arg_list) + if arg_list[0] == '...': + return repr(tree[0]) + '[..., %s]' % arg_list[1] return (repr(tree[0]) + '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1])) @@ -1220,8 +1218,8 @@ def __getitem__(self, parameters): parameters = (..., result) else: if not isinstance(args, list): - raise TypeError("Callable[args, result]: args must be" - " a list of types. Got %.100r." % (args,)) + raise TypeError("Callable[args, result]: args must be a list." + " Got %.100r." % (args,)) parameters = tuple(args), result return self.__getitem_inner__(parameters) @@ -1232,8 +1230,6 @@ def __getitem_inner__(self, parameters): result = _type_check(result, msg) if args == ...: return super().__getitem__((_TypingEllipsis, result)) - if args == (): - return super().__getitem__((_TypingEmpty, result)) msg = "Callable[[arg, ...], result]: each arg must be a type." args = tuple(_type_check(arg, msg) for arg in args) parameters = args + (result,) From 5c5fb0ae289e5efc8b3306cb65270c24fa6edaaa Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 7 Nov 2016 07:38:51 +0100 Subject: [PATCH 4/6] The same for PY2 --- python2/typing.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/python2/typing.py b/python2/typing.py index 30f7bb85..b650f0c2 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1281,14 +1281,12 @@ def _tree_repr(self, tree): # super(CallableMeta, self)._tree_repr() for nice formatting. arg_list = [] for arg in tree[1:]: - if arg == (): - arg_list.append('[]') - elif not isinstance(arg, tuple): + if not isinstance(arg, tuple): arg_list.append(_type_repr(arg)) else: arg_list.append(arg[0]._tree_repr(arg)) - if len(arg_list) == 2: - return repr(tree[0]) + '[%s]' % ', '.join(arg_list) + if arg_list[0] == '...': + return repr(tree[0]) + '[..., %s]' % arg_list[1] return (repr(tree[0]) + '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1])) @@ -1305,25 +1303,20 @@ def __getitem__(self, parameters): args, result = parameters if args is Ellipsis: parameters = (Ellipsis, result) - elif args == []: - parameters = ((), result) else: if not isinstance(args, list) or Ellipsis in args or () in args: - raise TypeError("Callable[args, result]: args must be" - " a list of types. Got %.100r." % (args,)) - parameters = tuple(args) + (result,) + raise TypeError("Callable[args, result]: args must be a list." + " Got %.100r." % (args,)) + parameters = tuple(args), result return self.__getitem_inner__(parameters) @_tp_cache def __getitem_inner__(self, parameters): - args = parameters[:-1] - result = parameters[-1] + args, result = parameters msg = "Callable[args, result]: result must be a type." result = _type_check(result, msg) - if args == (Ellipsis,): + if args == Ellipsis: return super(CallableMeta, self).__getitem__((_TypingEllipsis, result)) - if args == ((),): - return super(CallableMeta, self).__getitem__((_TypingEmpty, result)) msg = "Callable[[arg, ...], result]: each arg must be a type." args = tuple(_type_check(arg, msg) for arg in args) parameters = args + (result,) From 9f6a0eb2e7691ab5e0bacba40d96024ef4fc20bd Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 7 Nov 2016 07:42:01 +0100 Subject: [PATCH 5/6] One more bit for PY2 --- python2/typing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python2/typing.py b/python2/typing.py index b650f0c2..1630b391 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1304,7 +1304,7 @@ def __getitem__(self, parameters): if args is Ellipsis: parameters = (Ellipsis, result) else: - if not isinstance(args, list) or Ellipsis in args or () in args: + if not isinstance(args, list): raise TypeError("Callable[args, result]: args must be a list." " Got %.100r." % (args,)) parameters = tuple(args), result From ea1ed264a73908191cd3b4482a7c1d4a925fcab1 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 7 Nov 2016 20:10:16 +0100 Subject: [PATCH 6/6] Implemented latest comments --- python2/typing.py | 4 ++-- src/typing.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python2/typing.py b/python2/typing.py index 1630b391..3242be84 100644 --- a/python2/typing.py +++ b/python2/typing.py @@ -1307,7 +1307,7 @@ def __getitem__(self, parameters): if not isinstance(args, list): raise TypeError("Callable[args, result]: args must be a list." " Got %.100r." % (args,)) - parameters = tuple(args), result + parameters = (tuple(args), result) return self.__getitem_inner__(parameters) @_tp_cache @@ -1315,7 +1315,7 @@ def __getitem_inner__(self, parameters): args, result = parameters msg = "Callable[args, result]: result must be a type." result = _type_check(result, msg) - if args == Ellipsis: + if args is Ellipsis: return super(CallableMeta, self).__getitem__((_TypingEllipsis, result)) msg = "Callable[[arg, ...], result]: each arg must be a type." args = tuple(_type_check(arg, msg) for arg in args) diff --git a/src/typing.py b/src/typing.py index 03d18f85..5df1b45f 100644 --- a/src/typing.py +++ b/src/typing.py @@ -1214,13 +1214,13 @@ def __getitem__(self, parameters): raise TypeError("Callable must be used as " "Callable[[arg, ...], result].") args, result = parameters - if args is ...: - parameters = (..., result) + if args is Ellipsis: + parameters = (Ellipsis, result) else: if not isinstance(args, list): raise TypeError("Callable[args, result]: args must be a list." " Got %.100r." % (args,)) - parameters = tuple(args), result + parameters = (tuple(args), result) return self.__getitem_inner__(parameters) @_tp_cache @@ -1228,7 +1228,7 @@ def __getitem_inner__(self, parameters): args, result = parameters msg = "Callable[args, result]: result must be a type." result = _type_check(result, msg) - if args == ...: + if args is Ellipsis: return super().__getitem__((_TypingEllipsis, result)) msg = "Callable[[arg, ...], result]: each arg must be a type." args = tuple(_type_check(arg, msg) for arg in args)