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()