1"""
2Test the API of the symtable module.
3"""
4import symtable
5import unittest
6
7
8
9TEST_CODE = """
10import sys
11
12glob = 42
13
14class Mine:
15    instance_var = 24
16    def a_method(p1, p2):
17        pass
18
19def spam(a, b, *var, **kw):
20    global bar
21    bar = 47
22    x = 23
23    glob
24    def internal():
25        return x
26    return internal
27
28def foo():
29    pass
30
31def namespace_test(): pass
32def namespace_test(): pass
33"""
34
35
36def find_block(block, name):
37    for ch in block.get_children():
38        if ch.get_name() == name:
39            return ch
40
41
42class SymtableTest(unittest.TestCase):
43
44    top = symtable.symtable(TEST_CODE, "?", "exec")
45    # These correspond to scopes in TEST_CODE
46    Mine = find_block(top, "Mine")
47    a_method = find_block(Mine, "a_method")
48    spam = find_block(top, "spam")
49    internal = find_block(spam, "internal")
50    foo = find_block(top, "foo")
51
52    def test_type(self):
53        self.assertEqual(self.top.get_type(), "module")
54        self.assertEqual(self.Mine.get_type(), "class")
55        self.assertEqual(self.a_method.get_type(), "function")
56        self.assertEqual(self.spam.get_type(), "function")
57        self.assertEqual(self.internal.get_type(), "function")
58
59    def test_optimized(self):
60        self.assertFalse(self.top.is_optimized())
61        self.assertFalse(self.top.has_exec())
62
63        self.assertTrue(self.spam.is_optimized())
64
65    def test_nested(self):
66        self.assertFalse(self.top.is_nested())
67        self.assertFalse(self.Mine.is_nested())
68        self.assertFalse(self.spam.is_nested())
69        self.assertTrue(self.internal.is_nested())
70
71    def test_children(self):
72        self.assertTrue(self.top.has_children())
73        self.assertTrue(self.Mine.has_children())
74        self.assertFalse(self.foo.has_children())
75
76    def test_lineno(self):
77        self.assertEqual(self.top.get_lineno(), 0)
78        self.assertEqual(self.spam.get_lineno(), 11)
79
80    def test_function_info(self):
81        func = self.spam
82        self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"])
83        expected = ["a", "b", "internal", "kw", "var", "x"]
84        self.assertEqual(sorted(func.get_locals()), expected)
85        self.assertEqual(sorted(func.get_globals()), ["bar", "glob"])
86        self.assertEqual(self.internal.get_frees(), ("x",))
87
88    def test_globals(self):
89        self.assertTrue(self.spam.lookup("glob").is_global())
90        self.assertFalse(self.spam.lookup("glob").is_declared_global())
91        self.assertTrue(self.spam.lookup("bar").is_global())
92        self.assertTrue(self.spam.lookup("bar").is_declared_global())
93        self.assertFalse(self.internal.lookup("x").is_global())
94        self.assertFalse(self.Mine.lookup("instance_var").is_global())
95
96    def test_local(self):
97        self.assertTrue(self.spam.lookup("x").is_local())
98        self.assertFalse(self.internal.lookup("x").is_local())
99
100    def test_referenced(self):
101        self.assertTrue(self.internal.lookup("x").is_referenced())
102        self.assertTrue(self.spam.lookup("internal").is_referenced())
103        self.assertFalse(self.spam.lookup("x").is_referenced())
104
105    def test_parameters(self):
106        for sym in ("a", "var", "kw"):
107            self.assertTrue(self.spam.lookup(sym).is_parameter())
108        self.assertFalse(self.spam.lookup("x").is_parameter())
109
110    def test_symbol_lookup(self):
111        self.assertEqual(len(self.top.get_identifiers()),
112                         len(self.top.get_symbols()))
113
114        self.assertRaises(KeyError, self.top.lookup, "not_here")
115
116    def test_namespaces(self):
117        self.assertTrue(self.top.lookup("Mine").is_namespace())
118        self.assertTrue(self.Mine.lookup("a_method").is_namespace())
119        self.assertTrue(self.top.lookup("spam").is_namespace())
120        self.assertTrue(self.spam.lookup("internal").is_namespace())
121        self.assertTrue(self.top.lookup("namespace_test").is_namespace())
122        self.assertFalse(self.spam.lookup("x").is_namespace())
123
124        self.assertTrue(self.top.lookup("spam").get_namespace() is self.spam)
125        ns_test = self.top.lookup("namespace_test")
126        self.assertEqual(len(ns_test.get_namespaces()), 2)
127        self.assertRaises(ValueError, ns_test.get_namespace)
128
129    def test_assigned(self):
130        self.assertTrue(self.spam.lookup("x").is_assigned())
131        self.assertTrue(self.spam.lookup("bar").is_assigned())
132        self.assertTrue(self.top.lookup("spam").is_assigned())
133        self.assertTrue(self.Mine.lookup("a_method").is_assigned())
134        self.assertFalse(self.internal.lookup("x").is_assigned())
135
136    def test_annotated(self):
137        st1 = symtable.symtable('def f():\n    x: int\n', 'test', 'exec')
138        st2 = st1.get_children()[0]
139        self.assertTrue(st2.lookup('x').is_local())
140        self.assertTrue(st2.lookup('x').is_annotated())
141        self.assertFalse(st2.lookup('x').is_global())
142        st3 = symtable.symtable('def f():\n    x = 1\n', 'test', 'exec')
143        st4 = st3.get_children()[0]
144        self.assertTrue(st4.lookup('x').is_local())
145        self.assertFalse(st4.lookup('x').is_annotated())
146
147    def test_imported(self):
148        self.assertTrue(self.top.lookup("sys").is_imported())
149
150    def test_name(self):
151        self.assertEqual(self.top.get_name(), "top")
152        self.assertEqual(self.spam.get_name(), "spam")
153        self.assertEqual(self.spam.lookup("x").get_name(), "x")
154        self.assertEqual(self.Mine.get_name(), "Mine")
155
156    def test_class_info(self):
157        self.assertEqual(self.Mine.get_methods(), ('a_method',))
158
159    def test_filename_correct(self):
160        ### Bug tickler: SyntaxError file name correct whether error raised
161        ### while parsing or building symbol table.
162        def checkfilename(brokencode, offset):
163            try:
164                symtable.symtable(brokencode, "spam", "exec")
165            except SyntaxError as e:
166                self.assertEqual(e.filename, "spam")
167                self.assertEqual(e.lineno, 1)
168                self.assertEqual(e.offset, offset)
169            else:
170                self.fail("no SyntaxError for %r" % (brokencode,))
171        checkfilename("def f(x): foo)(", 14)  # parse-time
172        checkfilename("def f(x): global x", 10)  # symtable-build-time
173        symtable.symtable("pass", b"spam", "exec")
174        with self.assertWarns(DeprecationWarning), \
175             self.assertRaises(TypeError):
176            symtable.symtable("pass", bytearray(b"spam"), "exec")
177        with self.assertWarns(DeprecationWarning):
178            symtable.symtable("pass", memoryview(b"spam"), "exec")
179        with self.assertRaises(TypeError):
180            symtable.symtable("pass", list(b"spam"), "exec")
181
182    def test_eval(self):
183        symbols = symtable.symtable("42", "?", "eval")
184
185    def test_single(self):
186        symbols = symtable.symtable("42", "?", "single")
187
188    def test_exec(self):
189        symbols = symtable.symtable("def f(x): return x", "?", "exec")
190
191
192if __name__ == '__main__':
193    unittest.main()
194