summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2016-09-09 03:50:03 (GMT)
committerYury Selivanov <yury@magic.io>2016-09-09 03:50:03 (GMT)
commitf8cb8a16a344ab208fd46876c4b63604987347b8 (patch)
treec44caa48291401d1e1e388004d2762513ac88c93 /Lib/test
parent09ad17810c38d1aaae02de69084dd2a8ad9f5cdb (diff)
downloadcpython-f8cb8a16a344ab208fd46876c4b63604987347b8.zip
cpython-f8cb8a16a344ab208fd46876c4b63604987347b8.tar.gz
cpython-f8cb8a16a344ab208fd46876c4b63604987347b8.tar.bz2
Issue #27985: Implement PEP 526 -- Syntax for Variable Annotations.
Patch by Ivan Levkivskyi.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/ann_module.py53
-rw-r--r--Lib/test/ann_module2.py36
-rw-r--r--Lib/test/ann_module3.py18
-rw-r--r--Lib/test/pydoc_mod.py2
-rw-r--r--Lib/test/test___all__.py2
-rw-r--r--Lib/test/test_dis.py33
-rw-r--r--Lib/test/test_grammar.py178
-rw-r--r--Lib/test/test_opcodes.py27
-rw-r--r--Lib/test/test_parser.py39
-rw-r--r--Lib/test/test_pydoc.py4
-rw-r--r--Lib/test/test_symtable.py11
-rw-r--r--Lib/test/test_tools/test_com2ann.py260
-rw-r--r--Lib/test/test_typing.py83
13 files changed, 743 insertions, 3 deletions
diff --git a/Lib/test/ann_module.py b/Lib/test/ann_module.py
new file mode 100644
index 0000000..9e6b87d
--- /dev/null
+++ b/Lib/test/ann_module.py
@@ -0,0 +1,53 @@
+
+
+"""
+The module for testing variable annotations.
+Empty lines above are for good reason (testing for correct line numbers)
+"""
+
+from typing import Optional
+
+__annotations__[1] = 2
+
+class C:
+
+ x = 5; y: Optional['C'] = None
+
+from typing import Tuple
+x: int = 5; y: str = x; f: Tuple[int, int]
+
+class M(type):
+
+ __annotations__['123'] = 123
+ o: type = object
+
+(pars): bool = True
+
+class D(C):
+ j: str = 'hi'; k: str= 'bye'
+
+from types import new_class
+h_class = new_class('H', (C,))
+j_class = new_class('J')
+
+class F():
+ z: int = 5
+ def __init__(self, x):
+ pass
+
+class Y(F):
+ def __init__(self):
+ super(F, self).__init__(123)
+
+class Meta(type):
+ def __new__(meta, name, bases, namespace):
+ return super().__new__(meta, name, bases, namespace)
+
+class S(metaclass = Meta):
+ x: str = 'something'
+ y: str = 'something else'
+
+def foo(x: int = 10):
+ def bar(y: List[str]):
+ x: str = 'yes'
+ bar()
diff --git a/Lib/test/ann_module2.py b/Lib/test/ann_module2.py
new file mode 100644
index 0000000..76cf5b3
--- /dev/null
+++ b/Lib/test/ann_module2.py
@@ -0,0 +1,36 @@
+"""
+Some correct syntax for variable annotation here.
+More examples are in test_grammar and test_parser.
+"""
+
+from typing import no_type_check, ClassVar
+
+i: int = 1
+j: int
+x: float = i/10
+
+def f():
+ class C: ...
+ return C()
+
+f().new_attr: object = object()
+
+class C:
+ def __init__(self, x: int) -> None:
+ self.x = x
+
+c = C(5)
+c.new_attr: int = 10
+
+__annotations__ = {}
+
+
+@no_type_check
+class NTC:
+ def meth(self, param: complex) -> None:
+ ...
+
+class CV:
+ var: ClassVar['CV']
+
+CV.var = CV()
diff --git a/Lib/test/ann_module3.py b/Lib/test/ann_module3.py
new file mode 100644
index 0000000..eccd7be
--- /dev/null
+++ b/Lib/test/ann_module3.py
@@ -0,0 +1,18 @@
+"""
+Correct syntax for variable annotation that should fail at runtime
+in a certain manner. More examples are in test_grammar and test_parser.
+"""
+
+def f_bad_ann():
+ __annotations__[1] = 2
+
+class C_OK:
+ def __init__(self, x: int) -> None:
+ self.x: no_such_name = x # This one is OK as proposed by Guido
+
+class D_bad_ann:
+ def __init__(self, x: int) -> None:
+ sfel.y: int = 0
+
+def g_bad_ann():
+ no_such_name.attr: int = 0
diff --git a/Lib/test/pydoc_mod.py b/Lib/test/pydoc_mod.py
index cda1c9e..9c1fff5 100644
--- a/Lib/test/pydoc_mod.py
+++ b/Lib/test/pydoc_mod.py
@@ -12,7 +12,7 @@ class A:
pass
class B(object):
- NO_MEANING = "eggs"
+ NO_MEANING: str = "eggs"
pass
class C(object):
diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py
index e94d984..ae9114e 100644
--- a/Lib/test/test___all__.py
+++ b/Lib/test/test___all__.py
@@ -38,6 +38,8 @@ class AllTest(unittest.TestCase):
modname, e.__class__.__name__, e))
if "__builtins__" in names:
del names["__builtins__"]
+ if '__annotations__' in names:
+ del names['__annotations__']
keys = set(names)
all_list = sys.modules[modname].__all__
all_set = set(all_list)
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 09e68ce..6081073 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -207,6 +207,38 @@ dis_simple_stmt_str = """\
10 RETURN_VALUE
"""
+annot_stmt_str = """\
+
+x: int = 1
+y: fun(1)
+lst[fun(0)]: int = 1
+"""
+# leading newline is for a reason (tests lineno)
+
+dis_annot_stmt_str = """\
+ 2 0 SETUP_ANNOTATIONS
+ 2 LOAD_CONST 0 (1)
+ 4 STORE_NAME 0 (x)
+ 6 LOAD_NAME 1 (int)
+ 8 STORE_ANNOTATION 0 (x)
+
+ 3 10 LOAD_NAME 2 (fun)
+ 12 LOAD_CONST 0 (1)
+ 14 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
+ 16 STORE_ANNOTATION 3 (y)
+
+ 4 18 LOAD_CONST 0 (1)
+ 20 LOAD_NAME 4 (lst)
+ 22 LOAD_NAME 2 (fun)
+ 24 LOAD_CONST 1 (0)
+ 26 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
+ 28 STORE_SUBSCR
+ 30 LOAD_NAME 1 (int)
+ 32 POP_TOP
+ 34 LOAD_CONST 2 (None)
+ 36 RETURN_VALUE
+"""
+
compound_stmt_str = """\
x = 0
while 1:
@@ -345,6 +377,7 @@ class DisTests(unittest.TestCase):
def test_disassemble_str(self):
self.do_disassembly_test(expr_str, dis_expr_str)
self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str)
+ self.do_disassembly_test(annot_stmt_str, dis_annot_stmt_str)
self.do_disassembly_test(compound_stmt_str, dis_compound_stmt_str)
def test_disassemble_bytes(self):
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index bfe5225..109013f 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -8,6 +8,14 @@ import sys
# testing import *
from sys import *
+# different import patterns to check that __annotations__ does not interfere
+# with import machinery
+import test.ann_module as ann_module
+import typing
+from collections import ChainMap
+from test import ann_module2
+import test
+
class TokenTests(unittest.TestCase):
@@ -139,6 +147,19 @@ the \'lazy\' dog.\n\
compile(s, "<test>", "exec")
self.assertIn("unexpected EOF", str(cm.exception))
+var_annot_global: int # a global annotated is necessary for test_var_annot
+
+# custom namespace for testing __annotations__
+
+class CNS:
+ def __init__(self):
+ self._dct = {}
+ def __setitem__(self, item, value):
+ self._dct[item.lower()] = value
+ def __getitem__(self, item):
+ return self._dct[item]
+
+
class GrammarTests(unittest.TestCase):
# single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
@@ -154,6 +175,163 @@ class GrammarTests(unittest.TestCase):
# testlist ENDMARKER
x = eval('1, 0 or 1')
+ def test_var_annot_basics(self):
+ # all these should be allowed
+ var1: int = 5
+ var2: [int, str]
+ my_lst = [42]
+ def one():
+ return 1
+ int.new_attr: int
+ [list][0]: type
+ my_lst[one()-1]: int = 5
+ self.assertEqual(my_lst, [5])
+
+ def test_var_annot_syntax_errors(self):
+ # parser pass
+ check_syntax_error(self, "def f: int")
+ check_syntax_error(self, "x: int: str")
+ check_syntax_error(self, "def f():\n"
+ " nonlocal x: int\n")
+ # AST pass
+ check_syntax_error(self, "[x, 0]: int\n")
+ check_syntax_error(self, "f(): int\n")
+ check_syntax_error(self, "(x,): int")
+ check_syntax_error(self, "def f():\n"
+ " (x, y): int = (1, 2)\n")
+ # symtable pass
+ check_syntax_error(self, "def f():\n"
+ " x: int\n"
+ " global x\n")
+ check_syntax_error(self, "def f():\n"
+ " global x\n"
+ " x: int\n")
+
+ def test_var_annot_basic_semantics(self):
+ # execution order
+ with self.assertRaises(ZeroDivisionError):
+ no_name[does_not_exist]: no_name_again = 1/0
+ with self.assertRaises(NameError):
+ no_name[does_not_exist]: 1/0 = 0
+ global var_annot_global
+
+ # function semantics
+ def f():
+ st: str = "Hello"
+ a.b: int = (1, 2)
+ return st
+ self.assertEqual(f.__annotations__, {})
+ def f_OK():
+ x: 1/0
+ f_OK()
+ def fbad():
+ x: int
+ print(x)
+ with self.assertRaises(UnboundLocalError):
+ fbad()
+ def f2bad():
+ (no_such_global): int
+ print(no_such_global)
+ try:
+ f2bad()
+ except Exception as e:
+ self.assertIs(type(e), NameError)
+
+ # class semantics
+ class C:
+ x: int
+ s: str = "attr"
+ z = 2
+ def __init__(self, x):
+ self.x: int = x
+ self.assertEqual(C.__annotations__, {'x': int, 's': str})
+ with self.assertRaises(NameError):
+ class CBad:
+ no_such_name_defined.attr: int = 0
+ with self.assertRaises(NameError):
+ class Cbad2(C):
+ x: int
+ x.y: list = []
+
+ def test_var_annot_metaclass_semantics(self):
+ class CMeta(type):
+ @classmethod
+ def __prepare__(metacls, name, bases, **kwds):
+ return {'__annotations__': CNS()}
+ class CC(metaclass=CMeta):
+ XX: 'ANNOT'
+ self.assertEqual(CC.__annotations__['xx'], 'ANNOT')
+
+ def test_var_annot_module_semantics(self):
+ with self.assertRaises(AttributeError):
+ print(test.__annotations__)
+ self.assertEqual(ann_module.__annotations__,
+ {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]})
+ self.assertEqual(ann_module.M.__annotations__,
+ {'123': 123, 'o': type})
+ self.assertEqual(ann_module2.__annotations__, {})
+ self.assertEqual(typing.get_type_hints(ann_module2.CV,
+ ann_module2.__dict__),
+ ChainMap({'var': typing.ClassVar[ann_module2.CV]}, {}))
+
+ def test_var_annot_in_module(self):
+ # check that functions fail the same way when executed
+ # outside of module where they were defined
+ from test.ann_module3 import f_bad_ann, g_bad_ann, D_bad_ann
+ with self.assertRaises(NameError):
+ f_bad_ann()
+ with self.assertRaises(NameError):
+ g_bad_ann()
+ with self.assertRaises(NameError):
+ D_bad_ann(5)
+
+ def test_var_annot_simple_exec(self):
+ gns = {}; lns= {}
+ exec("'docstring'\n"
+ "__annotations__[1] = 2\n"
+ "x: int = 5\n", gns, lns)
+ self.assertEqual(lns["__annotations__"], {1: 2, 'x': int})
+ with self.assertRaises(KeyError):
+ gns['__annotations__']
+
+ def test_var_annot_custom_maps(self):
+ # tests with custom locals() and __annotations__
+ ns = {'__annotations__': CNS()}
+ exec('X: int; Z: str = "Z"; (w): complex = 1j', ns)
+ self.assertEqual(ns['__annotations__']['x'], int)
+ self.assertEqual(ns['__annotations__']['z'], str)
+ with self.assertRaises(KeyError):
+ ns['__annotations__']['w']
+ nonloc_ns = {}
+ class CNS2:
+ def __init__(self):
+ self._dct = {}
+ def __setitem__(self, item, value):
+ nonlocal nonloc_ns
+ self._dct[item] = value
+ nonloc_ns[item] = value
+ def __getitem__(self, item):
+ return self._dct[item]
+ exec('x: int = 1', {}, CNS2())
+ self.assertEqual(nonloc_ns['__annotations__']['x'], int)
+
+ def test_var_annot_refleak(self):
+ # complex case: custom locals plus custom __annotations__
+ # this was causing refleak
+ cns = CNS()
+ nonloc_ns = {'__annotations__': cns}
+ class CNS2:
+ def __init__(self):
+ self._dct = {'__annotations__': cns}
+ def __setitem__(self, item, value):
+ nonlocal nonloc_ns
+ self._dct[item] = value
+ nonloc_ns[item] = value
+ def __getitem__(self, item):
+ return self._dct[item]
+ exec('X: str', {}, CNS2())
+ self.assertEqual(nonloc_ns['__annotations__']['x'], str)
+
def test_funcdef(self):
### [decorators] 'def' NAME parameters ['->' test] ':' suite
### decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py
index 6ef93d9..6806c61 100644
--- a/Lib/test/test_opcodes.py
+++ b/Lib/test/test_opcodes.py
@@ -1,6 +1,7 @@
# Python test set -- part 2, opcodes
import unittest
+from test import ann_module
class OpcodeTest(unittest.TestCase):
@@ -20,6 +21,32 @@ class OpcodeTest(unittest.TestCase):
if n != 90:
self.fail('try inside for')
+ def test_setup_annotations_line(self):
+ # check that SETUP_ANNOTATIONS does not create spurious line numbers
+ try:
+ with open(ann_module.__file__) as f:
+ txt = f.read()
+ co = compile(txt, ann_module.__file__, 'exec')
+ self.assertEqual(co.co_firstlineno, 6)
+ except OSError:
+ pass
+
+ def test_no_annotations_if_not_needed(self):
+ class C: pass
+ with self.assertRaises(AttributeError):
+ C.__annotations__
+
+ def test_use_existing_annotations(self):
+ ns = {'__annotations__': {1: 2}}
+ exec('x: int', ns)
+ self.assertEqual(ns['__annotations__'], {'x': int, 1: 2})
+
+ def test_do_not_recreate_annotations(self):
+ class C:
+ del __annotations__
+ with self.assertRaises(NameError):
+ x: int
+
def test_raise_class_exceptions(self):
class AClass(Exception): pass
diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py
index e2a42f9..d6e6f71 100644
--- a/Lib/test/test_parser.py
+++ b/Lib/test/test_parser.py
@@ -138,6 +138,45 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase):
self.check_suite("a = b")
self.check_suite("a = b = c = d = e")
+ def test_var_annot(self):
+ self.check_suite("x: int = 5")
+ self.check_suite("y: List[T] = []; z: [list] = fun()")
+ self.check_suite("x: tuple = (1, 2)")
+ self.check_suite("d[f()]: int = 42")
+ self.check_suite("f(d[x]): str = 'abc'")
+ self.check_suite("x.y.z.w: complex = 42j")
+ self.check_suite("x: int")
+ self.check_suite("def f():\n"
+ " x: str\n"
+ " y: int = 5\n")
+ self.check_suite("class C:\n"
+ " x: str\n"
+ " y: int = 5\n")
+ self.check_suite("class C:\n"
+ " def __init__(self, x: int) -> None:\n"
+ " self.x: int = x\n")
+ # double check for nonsense
+ with self.assertRaises(SyntaxError):
+ exec("2+2: int", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("[]: int = 5", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("x, *y, z: int = range(5)", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("t: tuple = 1, 2", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("u = v: int", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("False: int", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("x.False: int", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("x.y,: int", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("[0]: int", {}, {})
+ with self.assertRaises(SyntaxError):
+ exec("f(): int", {}, {})
+
def test_simple_augmented_assignments(self):
self.check_suite("a += b")
self.check_suite("a -= b")
diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py
index 17a82c2..5174d56 100644
--- a/Lib/test/test_pydoc.py
+++ b/Lib/test/test_pydoc.py
@@ -83,6 +83,8 @@ CLASSES
| Data and other attributes defined here:
|\x20\x20
| NO_MEANING = 'eggs'
+ |\x20\x20
+ | __annotations__ = {'NO_MEANING': <class 'str'>}
\x20\x20\x20\x20
class C(builtins.object)
| Methods defined here:
@@ -195,6 +197,8 @@ Data descriptors defined here:<br>
Data and other attributes defined here:<br>
<dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl>
+<dl><dt><strong>__annotations__</strong> = {'NO_MEANING': &lt;class 'str'&gt;}</dl>
+
</td></tr></table> <p>
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py
index bf99505..3047165 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -133,6 +133,17 @@ class SymtableTest(unittest.TestCase):
self.assertTrue(self.Mine.lookup("a_method").is_assigned())
self.assertFalse(self.internal.lookup("x").is_assigned())
+ def test_annotated(self):
+ st1 = symtable.symtable('def f():\n x: int\n', 'test', 'exec')
+ st2 = st1.get_children()[0]
+ self.assertTrue(st2.lookup('x').is_local())
+ self.assertTrue(st2.lookup('x').is_annotated())
+ self.assertFalse(st2.lookup('x').is_global())
+ st3 = symtable.symtable('def f():\n x = 1\n', 'test', 'exec')
+ st4 = st3.get_children()[0]
+ self.assertTrue(st4.lookup('x').is_local())
+ self.assertFalse(st4.lookup('x').is_annotated())
+
def test_imported(self):
self.assertTrue(self.top.lookup("sys").is_imported())
diff --git a/Lib/test/test_tools/test_com2ann.py b/Lib/test/test_tools/test_com2ann.py
new file mode 100644
index 0000000..2731f82
--- /dev/null
+++ b/Lib/test/test_tools/test_com2ann.py
@@ -0,0 +1,260 @@
+"""Tests for the com2ann.py script in the Tools/parser directory."""
+
+import unittest
+import test.support
+import os
+import re
+
+from test.test_tools import basepath, toolsdir, skip_if_missing
+
+skip_if_missing()
+
+parser_path = os.path.join(toolsdir, "parser")
+
+with test.support.DirsOnSysPath(parser_path):
+ from com2ann import *
+
+class BaseTestCase(unittest.TestCase):
+
+ def check(self, code, expected, n=False, e=False):
+ self.assertEqual(com2ann(code,
+ drop_None=n, drop_Ellipsis=e, silent=True),
+ expected)
+
+class SimpleTestCase(BaseTestCase):
+ # Tests for basic conversions
+
+ def test_basics(self):
+ self.check("z = 5", "z = 5")
+ self.check("z: int = 5", "z: int = 5")
+ self.check("z = 5 # type: int", "z: int = 5")
+ self.check("z = 5 # type: int # comment",
+ "z: int = 5 # comment")
+
+ def test_type_ignore(self):
+ self.check("foobar = foobaz() #type: ignore",
+ "foobar = foobaz() #type: ignore")
+ self.check("a = 42 #type: ignore #comment",
+ "a = 42 #type: ignore #comment")
+
+ def test_complete_tuple(self):
+ self.check("t = 1, 2, 3 # type: Tuple[int, ...]",
+ "t: Tuple[int, ...] = (1, 2, 3)")
+ self.check("t = 1, # type: Tuple[int]",
+ "t: Tuple[int] = (1,)")
+ self.check("t = (1, 2, 3) # type: Tuple[int, ...]",
+ "t: Tuple[int, ...] = (1, 2, 3)")
+
+ def test_drop_None(self):
+ self.check("x = None # type: int",
+ "x: int", True)
+ self.check("x = None # type: int # another",
+ "x: int # another", True)
+ self.check("x = None # type: int # None",
+ "x: int # None", True)
+
+ def test_drop_Ellipsis(self):
+ self.check("x = ... # type: int",
+ "x: int", False, True)
+ self.check("x = ... # type: int # another",
+ "x: int # another", False, True)
+ self.check("x = ... # type: int # ...",
+ "x: int # ...", False, True)
+
+ def test_newline(self):
+ self.check("z = 5 # type: int\r\n", "z: int = 5\r\n")
+ self.check("z = 5 # type: int # comment\x85",
+ "z: int = 5 # comment\x85")
+
+ def test_wrong(self):
+ self.check("#type : str", "#type : str")
+ self.check("x==y #type: bool", "x==y #type: bool")
+
+ def test_pattern(self):
+ for line in ["#type: int", " # type: str[:] # com"]:
+ self.assertTrue(re.search(TYPE_COM, line))
+ for line in ["", "#", "# comment", "#type", "type int:"]:
+ self.assertFalse(re.search(TYPE_COM, line))
+
+class BigTestCase(BaseTestCase):
+ # Tests for really crazy formatting, to be sure
+ # that script works reasonably in extreme situations
+
+ def test_crazy(self):
+ self.maxDiff = None
+ self.check(crazy_code, big_result, False, False)
+ self.check(crazy_code, big_result_ne, True, True)
+
+crazy_code = """\
+# -*- coding: utf-8 -*- # this should not be spoiled
+'''
+Docstring here
+'''
+
+import testmod
+x = 5 #type : int # this one is OK
+ttt \\
+ = \\
+ 1.0, \\
+ 2.0, \\
+ 3.0, #type: Tuple[float, float, float]
+with foo(x==1) as f: #type: str
+ print(f)
+
+for i, j in my_inter(x=1): # type: ignore
+ i + j # type: int # what about this
+
+x = y = z = 1 # type: int
+x, y, z = [], [], [] # type: (List[int], List[int], List[str])
+class C:
+
+
+ l[f(x
+ =1)] = [
+
+ 1,
+ 2,
+ ] # type: List[int]
+
+
+ (C.x[1]) = \\
+ 42 == 5# type: bool
+lst[...] = \\
+ ((\\
+...)) # type: int # comment ..
+
+y = ... # type: int # comment ...
+z = ...
+#type: int
+
+
+#DONE placement of annotation after target rather than before =
+
+TD.x[1] \\
+ = 0 == 5# type: bool
+
+TD.y[1] =5 == 5# type: bool # one more here
+F[G(x == y,
+
+# hm...
+
+ z)]\\
+ = None # type: OMG[int] # comment: None
+x = None#type:int #comment : None"""
+
+big_result = """\
+# -*- coding: utf-8 -*- # this should not be spoiled
+'''
+Docstring here
+'''
+
+import testmod
+x: int = 5 # this one is OK
+ttt: Tuple[float, float, float] \\
+ = \\
+ (1.0, \\
+ 2.0, \\
+ 3.0,)
+with foo(x==1) as f: #type: str
+ print(f)
+
+for i, j in my_inter(x=1): # type: ignore
+ i + j # type: int # what about this
+
+x = y = z = 1 # type: int
+x, y, z = [], [], [] # type: (List[int], List[int], List[str])
+class C:
+
+
+ l[f(x
+ =1)]: List[int] = [
+
+ 1,
+ 2,
+ ]
+
+
+ (C.x[1]): bool = \\
+ 42 == 5
+lst[...]: int = \\
+ ((\\
+...)) # comment ..
+
+y: int = ... # comment ...
+z = ...
+#type: int
+
+
+#DONE placement of annotation after target rather than before =
+
+TD.x[1]: bool \\
+ = 0 == 5
+
+TD.y[1]: bool =5 == 5 # one more here
+F[G(x == y,
+
+# hm...
+
+ z)]: OMG[int]\\
+ = None # comment: None
+x: int = None #comment : None"""
+
+big_result_ne = """\
+# -*- coding: utf-8 -*- # this should not be spoiled
+'''
+Docstring here
+'''
+
+import testmod
+x: int = 5 # this one is OK
+ttt: Tuple[float, float, float] \\
+ = \\
+ (1.0, \\
+ 2.0, \\
+ 3.0,)
+with foo(x==1) as f: #type: str
+ print(f)
+
+for i, j in my_inter(x=1): # type: ignore
+ i + j # type: int # what about this
+
+x = y = z = 1 # type: int
+x, y, z = [], [], [] # type: (List[int], List[int], List[str])
+class C:
+
+
+ l[f(x
+ =1)]: List[int] = [
+
+ 1,
+ 2,
+ ]
+
+
+ (C.x[1]): bool = \\
+ 42 == 5
+lst[...]: int \\
+ \\
+ # comment ..
+
+y: int # comment ...
+z = ...
+#type: int
+
+
+#DONE placement of annotation after target rather than before =
+
+TD.x[1]: bool \\
+ = 0 == 5
+
+TD.y[1]: bool =5 == 5 # one more here
+F[G(x == y,
+
+# hm...
+
+ z)]: OMG[int]\\
+ # comment: None
+x: int #comment : None"""
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 72afe67..e3904b1 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -4,14 +4,16 @@ import pickle
import re
import sys
from unittest import TestCase, main, skipUnless, SkipTest
+from collections import ChainMap
+from test import ann_module, ann_module2, ann_module3
from typing import Any
from typing import TypeVar, AnyStr
from typing import T, KT, VT # Not in __all__.
from typing import Union, Optional
-from typing import Tuple
+from typing import Tuple, List
from typing import Callable
-from typing import Generic
+from typing import Generic, ClassVar
from typing import cast
from typing import get_type_hints
from typing import no_type_check, no_type_check_decorator
@@ -827,6 +829,43 @@ class GenericTests(BaseTestCase):
with self.assertRaises(Exception):
D[T]
+class ClassVarTests(BaseTestCase):
+
+ def test_basics(self):
+ with self.assertRaises(TypeError):
+ ClassVar[1]
+ with self.assertRaises(TypeError):
+ ClassVar[int, str]
+ with self.assertRaises(TypeError):
+ ClassVar[int][str]
+
+ def test_repr(self):
+ self.assertEqual(repr(ClassVar), 'typing.ClassVar')
+ cv = ClassVar[int]
+ self.assertEqual(repr(cv), 'typing.ClassVar[int]')
+ cv = ClassVar[Employee]
+ self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__)
+
+ def test_cannot_subclass(self):
+ with self.assertRaises(TypeError):
+ class C(type(ClassVar)):
+ pass
+ with self.assertRaises(TypeError):
+ class C(type(ClassVar[int])):
+ pass
+
+ def test_cannot_init(self):
+ with self.assertRaises(TypeError):
+ type(ClassVar)()
+ with self.assertRaises(TypeError):
+ type(ClassVar[Optional[int]])()
+
+ def test_no_isinstance(self):
+ with self.assertRaises(TypeError):
+ isinstance(1, ClassVar[int])
+ with self.assertRaises(TypeError):
+ issubclass(int, ClassVar)
+
class VarianceTests(BaseTestCase):
@@ -930,6 +969,46 @@ class ForwardRefTests(BaseTestCase):
right_hints = get_type_hints(t.add_right, globals(), locals())
self.assertEqual(right_hints['node'], Optional[Node[T]])
+ def test_get_type_hints(self):
+ gth = get_type_hints
+ self.assertEqual(gth(ann_module), {'x': int, 'y': str})
+ self.assertEqual(gth(ann_module.C, ann_module.__dict__),
+ ChainMap({'y': Optional[ann_module.C]}, {}))
+ self.assertEqual(gth(ann_module2), {})
+ self.assertEqual(gth(ann_module3), {})
+ self.assertEqual(repr(gth(ann_module.j_class)), 'ChainMap({}, {})')
+ self.assertEqual(gth(ann_module.M), ChainMap({'123': 123, 'o': type},
+ {}, {}))
+ self.assertEqual(gth(ann_module.D),
+ ChainMap({'j': str, 'k': str,
+ 'y': Optional[ann_module.C]}, {}))
+ self.assertEqual(gth(ann_module.Y), ChainMap({'z': int}, {}))
+ self.assertEqual(gth(ann_module.h_class),
+ ChainMap({}, {'y': Optional[ann_module.C]}, {}))
+ self.assertEqual(gth(ann_module.S), ChainMap({'x': str, 'y': str},
+ {}))
+ self.assertEqual(gth(ann_module.foo), {'x': int})
+
+ def testf(x, y): ...
+ testf.__annotations__['x'] = 'int'
+ self.assertEqual(gth(testf), {'x': int})
+ self.assertEqual(gth(ann_module2.NTC.meth), {})
+
+ # interactions with ClassVar
+ class B:
+ x: ClassVar[Optional['B']] = None
+ y: int
+ class C(B):
+ z: ClassVar['C'] = B()
+ class G(Generic[T]):
+ lst: ClassVar[List[T]] = []
+ self.assertEqual(gth(B, locals()),
+ ChainMap({'y': int, 'x': ClassVar[Optional[B]]}, {}))
+ self.assertEqual(gth(C, locals()),
+ ChainMap({'z': ClassVar[C]},
+ {'y': int, 'x': ClassVar[Optional[B]]}, {}))
+ self.assertEqual(gth(G), ChainMap({'lst': ClassVar[List[T]]},{},{}))
+
def test_forwardref_instance_type_error(self):
fr = typing._ForwardRef('int')
with self.assertRaises(TypeError):