1""" 2Test the API of the symtable module. 3""" 4import symtable 5import unittest 6 7 8 9TEST_CODE = """ 10import sys 11 12glob = 42 13some_var = 12 14some_non_assigned_global_var = 11 15some_assigned_global_var = 11 16 17class Mine: 18 instance_var = 24 19 def a_method(p1, p2): 20 pass 21 22def spam(a, b, *var, **kw): 23 global bar 24 global some_assigned_global_var 25 some_assigned_global_var = 12 26 bar = 47 27 some_var = 10 28 x = 23 29 glob 30 def internal(): 31 return x 32 def other_internal(): 33 nonlocal some_var 34 some_var = 3 35 return some_var 36 return internal 37 38def foo(): 39 pass 40 41def namespace_test(): pass 42def namespace_test(): pass 43""" 44 45 46def find_block(block, name): 47 for ch in block.get_children(): 48 if ch.get_name() == name: 49 return ch 50 51 52class SymtableTest(unittest.TestCase): 53 54 top = symtable.symtable(TEST_CODE, "?", "exec") 55 # These correspond to scopes in TEST_CODE 56 Mine = find_block(top, "Mine") 57 a_method = find_block(Mine, "a_method") 58 spam = find_block(top, "spam") 59 internal = find_block(spam, "internal") 60 other_internal = find_block(spam, "other_internal") 61 foo = find_block(top, "foo") 62 63 def test_type(self): 64 self.assertEqual(self.top.get_type(), "module") 65 self.assertEqual(self.Mine.get_type(), "class") 66 self.assertEqual(self.a_method.get_type(), "function") 67 self.assertEqual(self.spam.get_type(), "function") 68 self.assertEqual(self.internal.get_type(), "function") 69 70 def test_id(self): 71 self.assertGreater(self.top.get_id(), 0) 72 self.assertGreater(self.Mine.get_id(), 0) 73 self.assertGreater(self.a_method.get_id(), 0) 74 self.assertGreater(self.spam.get_id(), 0) 75 self.assertGreater(self.internal.get_id(), 0) 76 77 def test_optimized(self): 78 self.assertFalse(self.top.is_optimized()) 79 80 self.assertTrue(self.spam.is_optimized()) 81 82 def test_nested(self): 83 self.assertFalse(self.top.is_nested()) 84 self.assertFalse(self.Mine.is_nested()) 85 self.assertFalse(self.spam.is_nested()) 86 self.assertTrue(self.internal.is_nested()) 87 88 def test_children(self): 89 self.assertTrue(self.top.has_children()) 90 self.assertTrue(self.Mine.has_children()) 91 self.assertFalse(self.foo.has_children()) 92 93 def test_lineno(self): 94 self.assertEqual(self.top.get_lineno(), 0) 95 self.assertEqual(self.spam.get_lineno(), 14) 96 97 def test_function_info(self): 98 func = self.spam 99 self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"]) 100 expected = ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x'] 101 self.assertEqual(sorted(func.get_locals()), expected) 102 self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"]) 103 self.assertEqual(self.internal.get_frees(), ("x",)) 104 105 def test_globals(self): 106 self.assertTrue(self.spam.lookup("glob").is_global()) 107 self.assertFalse(self.spam.lookup("glob").is_declared_global()) 108 self.assertTrue(self.spam.lookup("bar").is_global()) 109 self.assertTrue(self.spam.lookup("bar").is_declared_global()) 110 self.assertFalse(self.internal.lookup("x").is_global()) 111 self.assertFalse(self.Mine.lookup("instance_var").is_global()) 112 self.assertTrue(self.spam.lookup("bar").is_global()) 113 # Module-scope globals are both global and local 114 self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global()) 115 self.assertTrue(self.top.lookup("some_assigned_global_var").is_global()) 116 117 def test_nonlocal(self): 118 self.assertFalse(self.spam.lookup("some_var").is_nonlocal()) 119 self.assertTrue(self.other_internal.lookup("some_var").is_nonlocal()) 120 expected = ("some_var",) 121 self.assertEqual(self.other_internal.get_nonlocals(), expected) 122 123 def test_local(self): 124 self.assertTrue(self.spam.lookup("x").is_local()) 125 self.assertFalse(self.spam.lookup("bar").is_local()) 126 # Module-scope globals are both global and local 127 self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_local()) 128 self.assertTrue(self.top.lookup("some_assigned_global_var").is_local()) 129 130 def test_free(self): 131 self.assertTrue(self.internal.lookup("x").is_free()) 132 133 def test_referenced(self): 134 self.assertTrue(self.internal.lookup("x").is_referenced()) 135 self.assertTrue(self.spam.lookup("internal").is_referenced()) 136 self.assertFalse(self.spam.lookup("x").is_referenced()) 137 138 def test_parameters(self): 139 for sym in ("a", "var", "kw"): 140 self.assertTrue(self.spam.lookup(sym).is_parameter()) 141 self.assertFalse(self.spam.lookup("x").is_parameter()) 142 143 def test_symbol_lookup(self): 144 self.assertEqual(len(self.top.get_identifiers()), 145 len(self.top.get_symbols())) 146 147 self.assertRaises(KeyError, self.top.lookup, "not_here") 148 149 def test_namespaces(self): 150 self.assertTrue(self.top.lookup("Mine").is_namespace()) 151 self.assertTrue(self.Mine.lookup("a_method").is_namespace()) 152 self.assertTrue(self.top.lookup("spam").is_namespace()) 153 self.assertTrue(self.spam.lookup("internal").is_namespace()) 154 self.assertTrue(self.top.lookup("namespace_test").is_namespace()) 155 self.assertFalse(self.spam.lookup("x").is_namespace()) 156 157 self.assertTrue(self.top.lookup("spam").get_namespace() is self.spam) 158 ns_test = self.top.lookup("namespace_test") 159 self.assertEqual(len(ns_test.get_namespaces()), 2) 160 self.assertRaises(ValueError, ns_test.get_namespace) 161 162 def test_assigned(self): 163 self.assertTrue(self.spam.lookup("x").is_assigned()) 164 self.assertTrue(self.spam.lookup("bar").is_assigned()) 165 self.assertTrue(self.top.lookup("spam").is_assigned()) 166 self.assertTrue(self.Mine.lookup("a_method").is_assigned()) 167 self.assertFalse(self.internal.lookup("x").is_assigned()) 168 169 def test_annotated(self): 170 st1 = symtable.symtable('def f():\n x: int\n', 'test', 'exec') 171 st2 = st1.get_children()[0] 172 self.assertTrue(st2.lookup('x').is_local()) 173 self.assertTrue(st2.lookup('x').is_annotated()) 174 self.assertFalse(st2.lookup('x').is_global()) 175 st3 = symtable.symtable('def f():\n x = 1\n', 'test', 'exec') 176 st4 = st3.get_children()[0] 177 self.assertTrue(st4.lookup('x').is_local()) 178 self.assertFalse(st4.lookup('x').is_annotated()) 179 180 # Test that annotations in the global scope are valid after the 181 # variable is declared as nonlocal. 182 st5 = symtable.symtable('global x\nx: int', 'test', 'exec') 183 self.assertTrue(st5.lookup("x").is_global()) 184 185 # Test that annotations for nonlocals are valid after the 186 # variable is declared as nonlocal. 187 st6 = symtable.symtable('def g():\n' 188 ' x = 2\n' 189 ' def f():\n' 190 ' nonlocal x\n' 191 ' x: int', 192 'test', 'exec') 193 194 def test_imported(self): 195 self.assertTrue(self.top.lookup("sys").is_imported()) 196 197 def test_name(self): 198 self.assertEqual(self.top.get_name(), "top") 199 self.assertEqual(self.spam.get_name(), "spam") 200 self.assertEqual(self.spam.lookup("x").get_name(), "x") 201 self.assertEqual(self.Mine.get_name(), "Mine") 202 203 def test_class_info(self): 204 self.assertEqual(self.Mine.get_methods(), ('a_method',)) 205 206 def test_filename_correct(self): 207 ### Bug tickler: SyntaxError file name correct whether error raised 208 ### while parsing or building symbol table. 209 def checkfilename(brokencode, offset): 210 try: 211 symtable.symtable(brokencode, "spam", "exec") 212 except SyntaxError as e: 213 self.assertEqual(e.filename, "spam") 214 self.assertEqual(e.lineno, 1) 215 self.assertEqual(e.offset, offset) 216 else: 217 self.fail("no SyntaxError for %r" % (brokencode,)) 218 checkfilename("def f(x): foo)(", 14) # parse-time 219 checkfilename("def f(x): global x", 11) # symtable-build-time 220 symtable.symtable("pass", b"spam", "exec") 221 with self.assertWarns(DeprecationWarning), \ 222 self.assertRaises(TypeError): 223 symtable.symtable("pass", bytearray(b"spam"), "exec") 224 with self.assertWarns(DeprecationWarning): 225 symtable.symtable("pass", memoryview(b"spam"), "exec") 226 with self.assertRaises(TypeError): 227 symtable.symtable("pass", list(b"spam"), "exec") 228 229 def test_eval(self): 230 symbols = symtable.symtable("42", "?", "eval") 231 232 def test_single(self): 233 symbols = symtable.symtable("42", "?", "single") 234 235 def test_exec(self): 236 symbols = symtable.symtable("def f(x): return x", "?", "exec") 237 238 def test_bytes(self): 239 top = symtable.symtable(TEST_CODE.encode('utf8'), "?", "exec") 240 self.assertIsNotNone(find_block(top, "Mine")) 241 242 code = b'# -*- coding: iso8859-15 -*-\nclass \xb4: pass\n' 243 244 top = symtable.symtable(code, "?", "exec") 245 self.assertIsNotNone(find_block(top, "\u017d")) 246 247 def test_symtable_repr(self): 248 self.assertEqual(str(self.top), "<SymbolTable for module ?>") 249 self.assertEqual(str(self.spam), "<Function SymbolTable for spam in ?>") 250 251 252if __name__ == '__main__': 253 unittest.main() 254