github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-tools-src/grumpy_tools/compiler/imputil_test.py (about) 1 # coding=utf-8 2 3 # Copyright 2016 Google Inc. All Rights Reserved. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 """Tests ImportVisitor and related classes.""" 18 19 from __future__ import unicode_literals 20 21 import copy 22 import os 23 import shutil 24 import tempfile 25 import textwrap 26 import unittest 27 28 from grumpy_tools.compiler import imputil 29 from grumpy_tools.compiler import util 30 from grumpy_tools.compiler.parser import patch_pythonparser 31 import pythonparser 32 33 patch_pythonparser() 34 35 36 class ImportVisitorTest(unittest.TestCase): 37 38 _PATH_SPEC = { 39 'foo.py': None, 40 'qux.py': None, 41 'bar/': { 42 'fred/': { 43 '__init__.py': None, 44 'quux.py': None, 45 }, 46 '__init__.py': None, 47 'baz.py': None, 48 'foo.py': None, 49 }, 50 'baz.py': None, 51 } 52 53 def setUp(self): 54 self.rootdir = tempfile.mkdtemp() 55 self.pydir = os.path.join(self.rootdir, 'src', '__python__') 56 self._materialize_tree( 57 self.rootdir, {'src/': {'__python__/': self._PATH_SPEC}}) 58 foo_script = os.path.join(self.rootdir, 'foo.py') 59 self.importer = imputil.Importer(self.rootdir, 'foo', foo_script, False) 60 bar_script = os.path.join(self.pydir, 'bar', '__init__.py') 61 self.bar_importer = imputil.Importer( 62 self.rootdir, 'bar', bar_script, False) 63 fred_script = os.path.join(self.pydir, 'bar', 'fred', '__init__.py') 64 self.fred_importer = imputil.Importer( 65 self.rootdir, 'bar.fred', fred_script, False) 66 67 self.foo_import = imputil.Import( 68 'foo', os.path.join(self.pydir, 'foo.py')) 69 self.qux_import = imputil.Import( 70 'qux', os.path.join(self.pydir, 'qux.py')) 71 self.bar_import = imputil.Import( 72 'bar', os.path.join(self.pydir, 'bar/__init__.py')) 73 self.fred_import = imputil.Import( 74 'bar.fred', os.path.join(self.pydir, 'bar/fred/__init__.py')) 75 self.quux_import = imputil.Import( 76 'bar.fred.quux', os.path.join(self.pydir, 'bar/fred/quux.py')) 77 self.baz2_import = imputil.Import( 78 'bar.baz', os.path.join(self.pydir, 'bar/baz.py')) 79 self.foo2_import = imputil.Import( 80 'bar.foo', os.path.join(self.pydir, 'bar/foo.py')) 81 self.baz_import = imputil.Import( 82 'baz', os.path.join(self.pydir, 'baz.py')) 83 84 def tearDown(self): 85 shutil.rmtree(self.rootdir) 86 87 def testImportTopLevelModule(self): 88 imp = copy.deepcopy(self.qux_import) 89 imp.add_binding(imputil.Import.MODULE, 'qux', 0) 90 self._check_imports('import qux', [imp]) 91 92 def testImportTopLevelPackage(self): 93 imp = copy.deepcopy(self.bar_import) 94 imp.add_binding(imputil.Import.MODULE, 'bar', 0) 95 self._check_imports('import bar', [imp]) 96 97 def testImportPackageModuleAbsolute(self): 98 imp = copy.deepcopy(self.baz2_import) 99 imp.add_binding(imputil.Import.MODULE, 'bar', 0) 100 self._check_imports('import bar.baz', [imp]) 101 102 def testImportFromSubModule(self): 103 imp = copy.deepcopy(self.baz2_import) 104 imp.add_binding(imputil.Import.MODULE, 'baz', 1) 105 self._check_imports('from bar import baz', [imp]) 106 107 def testImportPackageModuleRelative(self): 108 imp = copy.deepcopy(self.baz2_import) 109 imp.add_binding(imputil.Import.MODULE, 'baz', 1) 110 got = self.bar_importer.visit(pythonparser.parse('import baz').body[0]) 111 self._assert_imports_equal([imp], got) 112 113 def testImportPackageModuleRelativeFromSubModule(self): 114 imp = copy.deepcopy(self.baz2_import) 115 imp.add_binding(imputil.Import.MODULE, 'baz', 1) 116 foo_script = os.path.join(self.pydir, 'bar', 'foo.py') 117 importer = imputil.Importer(self.rootdir, 'bar.foo', foo_script, False) 118 got = importer.visit(pythonparser.parse('import baz').body[0]) 119 self._assert_imports_equal([imp], got) 120 121 def testImportPackageModuleAbsoluteImport(self): 122 imp = copy.deepcopy(self.baz_import) 123 imp.add_binding(imputil.Import.MODULE, 'baz', 0) 124 bar_script = os.path.join(self.pydir, 'bar', '__init__.py') 125 importer = imputil.Importer(self.rootdir, 'bar', bar_script, True) 126 got = importer.visit(pythonparser.parse('import baz').body[0]) 127 self._assert_imports_equal([imp], got) 128 129 def testImportMultiple(self): 130 imp1 = copy.deepcopy(self.foo_import) 131 imp1.add_binding(imputil.Import.MODULE, 'foo', 0) 132 imp2 = copy.deepcopy(self.baz2_import) 133 imp2.add_binding(imputil.Import.MODULE, 'bar', 0) 134 self._check_imports('import foo, bar.baz', [imp1, imp2]) 135 136 def testImportAs(self): 137 imp = copy.deepcopy(self.foo_import) 138 imp.add_binding(imputil.Import.MODULE, 'bar', 0) 139 self._check_imports('import foo as bar', [imp]) 140 141 def testImportFrom(self): 142 imp = copy.deepcopy(self.baz2_import) 143 imp.add_binding(imputil.Import.MODULE, 'baz', 1) 144 self._check_imports('from bar import baz', [imp]) 145 146 def testImportFromMember(self): 147 imp = copy.deepcopy(self.foo_import) 148 imp.add_binding(imputil.Import.MEMBER, 'bar', 'bar') 149 self._check_imports('from foo import bar', [imp]) 150 151 def testImportFromMultiple(self): 152 imp1 = copy.deepcopy(self.baz2_import) 153 imp1.add_binding(imputil.Import.MODULE, 'baz', 1) 154 imp2 = copy.deepcopy(self.foo2_import) 155 imp2.add_binding(imputil.Import.MODULE, 'foo', 1) 156 self._check_imports('from bar import baz, foo', [imp1, imp2]) 157 158 def testImportFromMixedMembers(self): 159 imp1 = copy.deepcopy(self.bar_import) 160 imp1.add_binding(imputil.Import.MEMBER, 'qux', 'qux') 161 imp2 = copy.deepcopy(self.baz2_import) 162 imp2.add_binding(imputil.Import.MODULE, 'baz', 1) 163 self._check_imports('from bar import qux, baz', [imp1, imp2]) 164 165 def testImportFromAs(self): 166 imp = copy.deepcopy(self.baz2_import) 167 imp.add_binding(imputil.Import.MODULE, 'qux', 1) 168 self._check_imports('from bar import baz as qux', [imp]) 169 170 def testImportFromAsMembers(self): 171 imp = copy.deepcopy(self.foo_import) 172 imp.add_binding(imputil.Import.MEMBER, 'baz', 'bar') 173 self._check_imports('from foo import bar as baz', [imp]) 174 175 # def testImportFromWildcardRaises(self): 176 # self._check_imports('from foo import *', []) 177 178 def testImportFromFuture(self): 179 self._check_imports('from __future__ import print_function', []) 180 181 def testImportFromNative(self): 182 imp = imputil.Import('__go__/fmt', is_native=True) 183 imp.add_binding(imputil.Import.MEMBER, 'Printf', 'Printf') 184 self._check_imports('from "__go__/fmt" import Printf', [imp]) 185 186 def testImportFromNativeMultiple(self): 187 imp = imputil.Import('__go__/fmt', is_native=True) 188 imp.add_binding(imputil.Import.MEMBER, 'Printf', 'Printf') 189 imp.add_binding(imputil.Import.MEMBER, 'Println', 'Println') 190 self._check_imports('from "__go__/fmt" import Printf, Println', [imp]) 191 192 def testImportFromNativeAs(self): 193 imp = imputil.Import('__go__/fmt', is_native=True) 194 imp.add_binding(imputil.Import.MEMBER, 'foo', 'Printf') 195 self._check_imports('from "__go__/fmt" import Printf as foo', [imp]) 196 197 # def testRelativeImportNonPackage(self): 198 # self.assertRaises(util.ImportError, self.importer.visit, 199 # pythonparser.parse('from . import bar').body[0]) 200 201 def testRelativeImportBeyondTopLevel(self): 202 self.assertRaises(util.ImportError, self.bar_importer.visit, 203 pythonparser.parse('from .. import qux').body[0]) 204 205 def testRelativeModuleNoExist(self): 206 imp = copy.deepcopy(self.bar_import) 207 imp.add_binding(imputil.Import.MEMBER, 'qux', 'qux') 208 node = pythonparser.parse('from . import qux').body[0] 209 self._assert_imports_equal([imp], self.bar_importer.visit(node)) 210 211 def testRelativeModule(self): 212 imp = copy.deepcopy(self.foo2_import) 213 imp.add_binding(imputil.Import.MODULE, 'foo', 1) 214 node = pythonparser.parse('from . import foo').body[0] 215 self._assert_imports_equal([imp], self.bar_importer.visit(node)) 216 217 def testRelativeModuleFromSubModule(self): 218 imp = copy.deepcopy(self.foo2_import) 219 imp.add_binding(imputil.Import.MODULE, 'foo', 1) 220 baz_script = os.path.join(self.pydir, 'bar', 'baz.py') 221 importer = imputil.Importer(self.rootdir, 'bar.baz', baz_script, False) 222 node = pythonparser.parse('from . import foo').body[0] 223 self._assert_imports_equal([imp], importer.visit(node)) 224 225 def testRelativeModuleMember(self): 226 imp = copy.deepcopy(self.foo2_import) 227 imp.add_binding(imputil.Import.MEMBER, 'qux', 'qux') 228 node = pythonparser.parse('from .foo import qux').body[0] 229 self._assert_imports_equal([imp], self.bar_importer.visit(node)) 230 231 def testRelativeModuleMemberMixed(self): 232 imp1 = copy.deepcopy(self.fred_import) 233 imp1.add_binding(imputil.Import.MEMBER, 'qux', 'qux') 234 imp2 = copy.deepcopy(self.quux_import) 235 imp2.add_binding(imputil.Import.MODULE, 'quux', 2) 236 node = pythonparser.parse('from .fred import qux, quux').body[0] 237 self._assert_imports_equal([imp1, imp2], self.bar_importer.visit(node)) 238 239 def testRelativeUpLevel(self): 240 imp = copy.deepcopy(self.foo2_import) 241 imp.add_binding(imputil.Import.MODULE, 'foo', 1) 242 node = pythonparser.parse('from .. import foo').body[0] 243 self._assert_imports_equal([imp], self.fred_importer.visit(node)) 244 245 def testRelativeUpLevelMember(self): 246 imp = copy.deepcopy(self.foo2_import) 247 imp.add_binding(imputil.Import.MEMBER, 'qux', 'qux') 248 node = pythonparser.parse('from ..foo import qux').body[0] 249 self._assert_imports_equal([imp], self.fred_importer.visit(node)) 250 251 def _check_imports(self, stmt, want): 252 got = self.importer.visit(pythonparser.parse(stmt).body[0]) 253 self._assert_imports_equal(want, got) 254 255 def _assert_imports_equal(self, want, got): 256 self.assertEqual([imp.__dict__ for imp in want], 257 [imp.__dict__ for imp in got]) 258 259 def _materialize_tree(self, dirname, spec): 260 for name, sub_spec in spec.iteritems(): 261 if name.endswith('/'): 262 subdir = os.path.join(dirname, name[:-1]) 263 os.mkdir(subdir) 264 self._materialize_tree(subdir, sub_spec) 265 else: 266 with open(os.path.join(dirname, name), 'w'): 267 pass 268 269 270 class MakeFutureFeaturesTest(unittest.TestCase): 271 272 def testImportFromFuture(self): 273 testcases = [ 274 ('from __future__ import print_function', 275 imputil.FutureFeatures(print_function=True)), 276 ('from __future__ import generators', imputil.FutureFeatures()), 277 ('from __future__ import generators, print_function', 278 imputil.FutureFeatures(print_function=True)), 279 ] 280 281 for tc in testcases: 282 source, want = tc 283 mod = pythonparser.parse(textwrap.dedent(source)) 284 node = mod.body[0] 285 got = imputil._make_future_features(node) # pylint: disable=protected-access 286 self.assertEqual(want.__dict__, got.__dict__) 287 288 def testImportFromFutureParseError(self): 289 testcases = [ 290 # NOTE: move this group to testImportFromFuture as they are implemented 291 # by grumpy 292 ('from __future__ import division', 293 r'future feature \w+ not yet implemented'), 294 ('from __future__ import braces', 'not a chance'), 295 ('from __future__ import nonexistant_feature', 296 r'future feature \w+ is not defined'), 297 ] 298 299 for tc in testcases: 300 source, want_regexp = tc 301 mod = pythonparser.parse(source) 302 node = mod.body[0] 303 self.assertRaisesRegexp(util.ParseError, want_regexp, 304 imputil._make_future_features, node) # pylint: disable=protected-access 305 306 307 class ParseFutureFeaturesTest(unittest.TestCase): 308 309 def testFutureFeatures(self): 310 testcases = [ 311 ('from __future__ import print_function', 312 imputil.FutureFeatures(print_function=True)), 313 ("""\ 314 "module docstring" 315 316 from __future__ import print_function 317 """, imputil.FutureFeatures(print_function=True)), 318 ("""\ 319 "module docstring" 320 321 from __future__ import print_function, with_statement 322 from __future__ import nested_scopes 323 """, imputil.FutureFeatures(print_function=True)), 324 ('from __future__ import absolute_import', 325 imputil.FutureFeatures(absolute_import=True)), 326 ('from __future__ import absolute_import, print_function', 327 imputil.FutureFeatures(absolute_import=True, print_function=True)), 328 ('foo = 123\nfrom __future__ import print_function', 329 imputil.FutureFeatures()), 330 ('import os\nfrom __future__ import print_function', 331 imputil.FutureFeatures()), 332 ] 333 334 for tc in testcases: 335 source, want = tc 336 mod = pythonparser.parse(textwrap.dedent(source)) 337 _, got = imputil.parse_future_features(mod) 338 self.assertEqual(want.__dict__, got.__dict__) 339 340 def testUnimplementedFutureRaises(self): 341 mod = pythonparser.parse('from __future__ import division') 342 msg = 'future feature division not yet implemented by grumpy' 343 self.assertRaisesRegexp(util.ParseError, msg, 344 imputil.parse_future_features, mod) 345 346 def testUndefinedFutureRaises(self): 347 mod = pythonparser.parse('from __future__ import foo') 348 self.assertRaisesRegexp( 349 util.ParseError, 'future feature foo is not defined', 350 imputil.parse_future_features, mod) 351 352 353 if __name__ == '__main__': 354 unittest.main()