Home | History | Annotate | Download | only in modulegraph_tests
      1 import unittest
      2 from modulegraph import modulegraph
      3 import pkg_resources
      4 import os
      5 import imp
      6 import sys
      7 import shutil
      8 import warnings
      9 from altgraph import Graph
     10 import textwrap
     11 import xml.etree.ElementTree as ET
     12 import pickle
     13 
     14 try:
     15     bytes
     16 except NameError:
     17     bytes = str
     18 
     19 try:
     20     from StringIO import StringIO
     21 except ImportError:
     22     from io import StringIO
     23 
     24 TESTDATA = os.path.join(
     25         os.path.dirname(os.path.abspath(__file__)),
     26         "testdata", "nspkg")
     27 
     28 try:
     29     expectedFailure = unittest.expectedFailure
     30 except AttributeError:
     31     import functools
     32     def expectedFailure(function):
     33         @functools.wraps(function)
     34         def wrapper(*args, **kwds):
     35             try:
     36                 function(*args, **kwds)
     37             except AssertionError:
     38                 pass
     39 
     40             else:
     41                 self.fail("unexpected pass")
     42 
     43 class TestDependencyInfo (unittest.TestCase):
     44     def test_pickling(self):
     45         info = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=False)
     46         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
     47             b = pickle.dumps(info, proto)
     48             self.assertTrue(isinstance(b, bytes))
     49 
     50             o = pickle.loads(b)
     51             self.assertEqual(o, info)
     52 
     53     def test_merging(self):
     54         info1 = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=False)
     55         info2 = modulegraph.DependencyInfo(function=False, conditional=True, tryexcept=True, fromlist=False)
     56         self.assertEqual(
     57             info1._merged(info2), modulegraph.DependencyInfo(function=True, conditional=True, tryexcept=True, fromlist=False))
     58 
     59         info2 = modulegraph.DependencyInfo(function=False, conditional=True, tryexcept=False, fromlist=False)
     60         self.assertEqual(
     61             info1._merged(info2), modulegraph.DependencyInfo(function=True, conditional=True, tryexcept=True, fromlist=False))
     62 
     63         info2 = modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False)
     64         self.assertEqual(
     65             info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False))
     66 
     67         info1 = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=True)
     68         self.assertEqual(
     69             info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False))
     70 
     71         info2 = modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=True)
     72         self.assertEqual(
     73             info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=True))
     74 
     75 
     76 class TestFunctions (unittest.TestCase):
     77     if not hasattr(unittest.TestCase, 'assertIsInstance'):
     78         def assertIsInstance(self, obj, types):
     79             self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types))
     80 
     81     def test_eval_str_tuple(self):
     82         for v in [
     83             '()',
     84             '("hello",)',
     85             '("hello", "world")',
     86             "('hello',)",
     87             "('hello', 'world')",
     88             "('hello', \"world\")",
     89             ]:
     90 
     91             self.assertEqual(modulegraph._eval_str_tuple(v), eval(v))
     92 
     93         self.assertRaises(ValueError, modulegraph._eval_str_tuple, "")
     94         self.assertRaises(ValueError, modulegraph._eval_str_tuple, "'a'")
     95         self.assertRaises(ValueError, modulegraph._eval_str_tuple, "'a', 'b'")
     96         self.assertRaises(ValueError, modulegraph._eval_str_tuple, "('a', ('b', 'c'))")
     97         self.assertRaises(ValueError, modulegraph._eval_str_tuple, "('a', ('b\", 'c'))")
     98 
     99     def test_namespace_package_path(self):
    100         class DS (object):
    101             def __init__(self, path, namespace_packages=None):
    102                 self.location = path
    103                 self._namespace_packages = namespace_packages
    104 
    105             def has_metadata(self, key):
    106                 if key == 'namespace_packages.txt':
    107                     return self._namespace_packages is not None
    108 
    109                 raise ValueError("invalid lookup key")
    110 
    111             def get_metadata(self, key):
    112                 if key == 'namespace_packages.txt':
    113                     if self._namespace_packages is None:
    114                         raise ValueError("no file")
    115 
    116                     return self._namespace_packages
    117 
    118                 raise ValueError("invalid lookup key")
    119 
    120         class WS (object):
    121             def __init__(self, path=None):
    122                 pass
    123 
    124             def __iter__(self):
    125                 yield DS("/pkg/pkg1")
    126                 yield DS("/pkg/pkg2", "foo\n")
    127                 yield DS("/pkg/pkg3", "bar.baz\n")
    128                 yield DS("/pkg/pkg4", "foobar\nfoo\n")
    129 
    130         saved_ws = pkg_resources.WorkingSet
    131         try:
    132             pkg_resources.WorkingSet = WS
    133 
    134             self.assertEqual(modulegraph._namespace_package_path("sys", ["appdir/pkg"]), ["appdir/pkg"])
    135             self.assertEqual(modulegraph._namespace_package_path("foo", ["appdir/pkg"]), ["appdir/pkg", "/pkg/pkg2/foo", "/pkg/pkg4/foo"])
    136             self.assertEqual(modulegraph._namespace_package_path("bar.baz", ["appdir/pkg"]), ["appdir/pkg", "/pkg/pkg3/bar/baz"])
    137 
    138         finally:
    139             pkg_resources.WorkingSet = saved_ws
    140 
    141     def test_os_listdir(self):
    142         root = os.path.join(
    143                 os.path.dirname(os.path.abspath(__file__)), 'testdata')
    144 
    145         self.assertEqual(modulegraph.os_listdir('/etc/'), os.listdir('/etc'))
    146         self.assertRaises(IOError, modulegraph.os_listdir, '/etc/hosts/foobar')
    147         self.assertRaises(IOError, modulegraph.os_listdir, os.path.join(root, 'test.egg', 'bar'))
    148 
    149         self.assertEqual(list(sorted(modulegraph.os_listdir(os.path.join(root, 'test.egg', 'foo')))),
    150             [ 'bar', 'bar.txt', 'baz.txt' ])
    151 
    152     def test_code_to_file(self):
    153         try:
    154             code = modulegraph._code_to_file.__code__
    155         except AttributeError:
    156             code = modulegraph._code_to_file.func_code
    157 
    158         data = modulegraph._code_to_file(code)
    159         self.assertTrue(hasattr(data, 'read'))
    160 
    161         content = data.read()
    162         self.assertIsInstance(content, bytes)
    163         data.close()
    164 
    165     def test_find_module(self):
    166         for path in ('syspath', 'syspath.zip', 'syspath.egg'):
    167             path = os.path.join(os.path.dirname(TESTDATA), path)
    168             if os.path.exists(os.path.join(path, 'mymodule.pyc')):
    169                 os.unlink(os.path.join(path, 'mymodule.pyc'))
    170 
    171             # Plain module
    172             info = modulegraph.find_module('mymodule', path=[path] + sys.path)
    173 
    174             fp = info[0]
    175             filename = info[1]
    176             description = info[2]
    177 
    178             self.assertTrue(hasattr(fp, 'read'))
    179 
    180             if path.endswith('.zip') or path.endswith('.egg'):
    181                 # Zip importers may precompile
    182                 if filename.endswith('.py'):
    183                     self.assertEqual(filename, os.path.join(path, 'mymodule.py'))
    184                     self.assertEqual(description, ('.py', 'rU', imp.PY_SOURCE))
    185 
    186                 else:
    187                     self.assertEqual(filename, os.path.join(path, 'mymodule.pyc'))
    188                     self.assertEqual(description, ('.pyc', 'rb', imp.PY_COMPILED))
    189 
    190             else:
    191                 self.assertEqual(filename, os.path.join(path, 'mymodule.py'))
    192                 self.assertEqual(description, ('.py', 'rU', imp.PY_SOURCE))
    193 
    194             # Compiled plain module, no source
    195             if path.endswith('.zip') or path.endswith('.egg'):
    196                 self.assertRaises(ImportError, modulegraph.find_module, 'mymodule2', path=[path] + sys.path)
    197 
    198             else:
    199                 info = modulegraph.find_module('mymodule2', path=[path] + sys.path)
    200 
    201                 fp = info[0]
    202                 filename = info[1]
    203                 description = info[2]
    204 
    205                 self.assertTrue(hasattr(fp, 'read'))
    206                 self.assertEqual(filename, os.path.join(path, 'mymodule2.pyc'))
    207                 self.assertEqual(description, ('.pyc', 'rb', imp.PY_COMPILED))
    208 
    209                 fp.close()
    210 
    211             # Compiled plain module, with source
    212 #            info = modulegraph.find_module('mymodule3', path=[path] + sys.path)
    213 #
    214 #            fp = info[0]
    215 #            filename = info[1]
    216 #            description = info[2]
    217 #
    218 #            self.assertTrue(hasattr(fp, 'read'))
    219 #
    220 #            if sys.version_info[:2] >= (3,2):
    221 #                self.assertEqual(filename, os.path.join(path, '__pycache__', 'mymodule3.cpython-32.pyc'))
    222 #            else:
    223 #                self.assertEqual(filename, os.path.join(path, 'mymodule3.pyc'))
    224 #            self.assertEqual(description, ('.pyc', 'rb', imp.PY_COMPILED))
    225 
    226 
    227             # Package
    228             info = modulegraph.find_module('mypkg', path=[path] + sys.path)
    229             fp = info[0]
    230             filename = info[1]
    231             description = info[2]
    232 
    233             self.assertEqual(fp, None)
    234             self.assertEqual(filename, os.path.join(path, 'mypkg'))
    235             self.assertEqual(description, ('', '', imp.PKG_DIRECTORY))
    236 
    237             # Extension
    238             if path.endswith('.zip'):
    239                 self.assertRaises(ImportError, modulegraph.find_module, 'myext', path=[path] + sys.path)
    240 
    241             else:
    242                 info = modulegraph.find_module('myext', path=[path] + sys.path)
    243                 fp = info[0]
    244                 filename = info[1]
    245                 description = info[2]
    246 
    247                 if sys.platform == 'win32':
    248                     ext = '.pyd'
    249                 else:
    250                     # This is a ly, but is good enough for now
    251                     ext = '.so'
    252 
    253                 self.assertEqual(filename, os.path.join(path, 'myext' + ext))
    254                 self.assertEqual(description, (ext, 'rb', imp.C_EXTENSION))
    255                 self.assertEqual(fp, None)
    256 
    257     def test_moduleInfoForPath(self):
    258         self.assertEqual(modulegraph.moduleInfoForPath("/somewhere/else/file.txt"), None)
    259 
    260         info = modulegraph.moduleInfoForPath("/somewhere/else/file.py")
    261         self.assertEqual(info[0], "file")
    262         if sys.version_info[:2] >= (3,4):
    263             self.assertEqual(info[1], "r")
    264         else:
    265             self.assertEqual(info[1], "U")
    266         self.assertEqual(info[2], imp.PY_SOURCE)
    267 
    268         info = modulegraph.moduleInfoForPath("/somewhere/else/file.pyc")
    269         self.assertEqual(info[0], "file")
    270         self.assertEqual(info[1], "rb")
    271         self.assertEqual(info[2], imp.PY_COMPILED)
    272 
    273         if sys.platform in ('darwin', 'linux2'):
    274             info = modulegraph.moduleInfoForPath("/somewhere/else/file.so")
    275             self.assertEqual(info[0], "file")
    276             self.assertEqual(info[1], "rb")
    277             self.assertEqual(info[2], imp.C_EXTENSION)
    278 
    279         elif sys.platform in ('win32',):
    280             info = modulegraph.moduleInfoForPath("/somewhere/else/file.pyd")
    281             self.assertEqual(info[0], "file")
    282             self.assertEqual(info[1], "rb")
    283             self.assertEqual(info[2], imp.C_EXTENSION)
    284 
    285     if sys.version_info[:2] > (2,5):
    286         exec(textwrap.dedent('''\
    287             def test_deprecated(self):
    288                 saved_add = modulegraph.addPackagePath
    289                 saved_replace = modulegraph.replacePackage
    290                 try:
    291                     called = []
    292 
    293                     def log_add(*args, **kwds):
    294                         called.append(('add', args, kwds))
    295                     def log_replace(*args, **kwds):
    296                         called.append(('replace', args, kwds))
    297 
    298                     modulegraph.addPackagePath = log_add
    299                     modulegraph.replacePackage = log_replace
    300 
    301                     with warnings.catch_warnings(record=True) as w:
    302                         warnings.simplefilter("always")
    303                         modulegraph.ReplacePackage('a', 'b')
    304                         modulegraph.AddPackagePath('c', 'd')
    305 
    306                     self.assertEqual(len(w), 2)
    307                     self.assertTrue(w[-1].category is DeprecationWarning)
    308                     self.assertTrue(w[-2].category is DeprecationWarning)
    309 
    310                     self.assertEqual(called, [
    311                         ('replace', ('a', 'b'), {}),
    312                         ('add', ('c', 'd'), {}),
    313                     ])
    314 
    315                 finally:
    316                     modulegraph.addPackagePath = saved_add
    317                     modulegraph.replacePackage = saved_replace
    318             '''), locals(), globals())
    319 
    320     def test_addPackage(self):
    321         saved = modulegraph._packagePathMap
    322         self.assertIsInstance(saved, dict)
    323         try:
    324             modulegraph._packagePathMap = {}
    325 
    326             modulegraph.addPackagePath('foo', 'a')
    327             self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a'] })
    328 
    329             modulegraph.addPackagePath('foo', 'b')
    330             self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a', 'b'] })
    331 
    332             modulegraph.addPackagePath('bar', 'b')
    333             self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a', 'b'], 'bar': ['b'] })
    334 
    335         finally:
    336             modulegraph._packagePathMap = saved
    337 
    338 
    339     def test_replacePackage(self):
    340         saved = modulegraph._replacePackageMap
    341         self.assertIsInstance(saved, dict)
    342         try:
    343             modulegraph._replacePackageMap = {}
    344 
    345             modulegraph.replacePackage("a", "b")
    346             self.assertEqual(modulegraph._replacePackageMap, {"a": "b"})
    347             modulegraph.replacePackage("a", "c")
    348             self.assertEqual(modulegraph._replacePackageMap, {"a": "c"})
    349             modulegraph.replacePackage("b", "c")
    350             self.assertEqual(modulegraph._replacePackageMap, {"a": "c", 'b': 'c'})
    351 
    352         finally:
    353             modulegraph._replacePackageMap = saved
    354 
    355 class TestNode (unittest.TestCase):
    356     if not hasattr(unittest.TestCase, 'assertIsInstance'):
    357         def assertIsInstance(self, obj, types):
    358             self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types))
    359     def testBasicAttributes(self):
    360         n = modulegraph.Node("foobar.xyz")
    361         self.assertIsInstance(n.debug, int)
    362         self.assertEqual(n.identifier, n.graphident)
    363         self.assertEqual(n.identifier, 'foobar.xyz')
    364         self.assertEqual(n.filename, None)
    365         self.assertEqual(n.packagepath, None)
    366         self.assertEqual(n.code, None)
    367         self.assertEqual(n.globalnames, set())
    368         self.assertEqual(n.starimports, set())
    369 
    370     def testMapping(self):
    371         n = modulegraph.Node("foobar.xyz")
    372         self.assertEqual(n._namespace, {})
    373 
    374         self.assertFalse('foo' in n)
    375         self.assertRaises(KeyError, n.__getitem__, 'foo')
    376         self.assertEqual(n.get('foo'), None)
    377         self.assertEqual(n.get('foo', 'a'), 'a')
    378         n['foo'] = 42
    379         self.assertEqual(n['foo'], 42)
    380         self.assertTrue('foo' in n)
    381         self.assertEqual(n._namespace, {'foo':42})
    382 
    383     def testOrder(self):
    384         n1 = modulegraph.Node("n1")
    385         n2 = modulegraph.Node("n2")
    386 
    387         self.assertTrue(n1 < n2)
    388         self.assertFalse(n2 < n1)
    389         self.assertTrue(n1 <= n1)
    390         self.assertFalse(n1 == n2)
    391         self.assertTrue(n1 == n1)
    392         self.assertTrue(n1 != n2)
    393         self.assertFalse(n1 != n1)
    394         self.assertTrue(n2 > n1)
    395         self.assertFalse(n1 > n2)
    396         self.assertTrue(n1 >= n1)
    397         self.assertTrue(n2 >= n1)
    398 
    399     def testHashing(self):
    400         n1a = modulegraph.Node('n1')
    401         n1b = modulegraph.Node('n1')
    402         n2 = modulegraph.Node('n2')
    403 
    404         d = {}
    405         d[n1a] = 'n1'
    406         d[n2] = 'n2'
    407         self.assertEqual(d[n1b], 'n1')
    408         self.assertEqual(d[n2], 'n2')
    409 
    410     def test_infoTuple(self):
    411         n = modulegraph.Node('n1')
    412         self.assertEqual(n.infoTuple(), ('n1',))
    413 
    414     def assertNoMethods(self, klass):
    415         d = dict(klass.__dict__)
    416         del d['__doc__']
    417         del d['__module__']
    418         if '__qualname__' in d:
    419             # New in Python 3.3
    420             del d['__qualname__']
    421         if '__dict__' in d:
    422             # New in Python 3.4
    423             del d['__dict__']
    424         self.assertEqual(d, {})
    425 
    426     def assertHasExactMethods(self, klass, *methods):
    427         d = dict(klass.__dict__)
    428         del d['__doc__']
    429         del d['__module__']
    430         if '__qualname__' in d:
    431             # New in Python 3.3
    432             del d['__qualname__']
    433         if '__dict__' in d:
    434             # New in Python 3.4
    435             del d['__dict__']
    436 
    437         for nm in methods:
    438             self.assertTrue(nm in d, "%s doesn't have attribute %r"%(klass, nm))
    439             del d[nm]
    440 
    441         self.assertEqual(d, {})
    442 
    443 
    444     if not hasattr(unittest.TestCase, 'assertIsSubclass'):
    445         def assertIsSubclass(self, cls1, cls2, message=None):
    446             self.assertTrue(issubclass(cls1, cls2),
    447                     message or "%r is not a subclass of %r"%(cls1, cls2))
    448 
    449     def test_subclasses(self):
    450         self.assertIsSubclass(modulegraph.AliasNode, modulegraph.Node)
    451         self.assertIsSubclass(modulegraph.Script, modulegraph.Node)
    452         self.assertIsSubclass(modulegraph.BadModule, modulegraph.Node)
    453         self.assertIsSubclass(modulegraph.ExcludedModule, modulegraph.BadModule)
    454         self.assertIsSubclass(modulegraph.MissingModule, modulegraph.BadModule)
    455         self.assertIsSubclass(modulegraph.BaseModule, modulegraph.Node)
    456         self.assertIsSubclass(modulegraph.BuiltinModule, modulegraph.BaseModule)
    457         self.assertIsSubclass(modulegraph.SourceModule, modulegraph.BaseModule)
    458         self.assertIsSubclass(modulegraph.CompiledModule, modulegraph.BaseModule)
    459         self.assertIsSubclass(modulegraph.Package, modulegraph.BaseModule)
    460         self.assertIsSubclass(modulegraph.Extension, modulegraph.BaseModule)
    461 
    462         # These classes have no new functionality, check that no code
    463         # got added:
    464         self.assertNoMethods(modulegraph.BadModule)
    465         self.assertNoMethods(modulegraph.ExcludedModule)
    466         self.assertNoMethods(modulegraph.MissingModule)
    467         self.assertNoMethods(modulegraph.BuiltinModule)
    468         self.assertNoMethods(modulegraph.SourceModule)
    469         self.assertNoMethods(modulegraph.CompiledModule)
    470         self.assertNoMethods(modulegraph.Package)
    471         self.assertNoMethods(modulegraph.Extension)
    472 
    473         # AliasNode is basicly a clone of an existing node
    474         self.assertHasExactMethods(modulegraph.Script, '__init__', 'infoTuple')
    475         n1 = modulegraph.Node('n1')
    476         n1.packagepath = ['a', 'b']
    477 
    478         a1 = modulegraph.AliasNode('a1', n1)
    479         self.assertEqual(a1.graphident, 'a1')
    480         self.assertEqual(a1.identifier, 'n1')
    481         self.assertTrue(a1.packagepath is n1.packagepath)
    482         self.assertTrue(a1._namespace is n1._namespace)
    483         self.assertTrue(a1.globalnames is n1.globalnames)
    484         self.assertTrue(a1.starimports is n1.starimports)
    485 
    486         v = a1.infoTuple()
    487         self.assertEqual(v, ('a1', 'n1'))
    488 
    489         # Scripts have a filename
    490         self.assertHasExactMethods(modulegraph.Script, '__init__', 'infoTuple')
    491         s1 = modulegraph.Script('do_import')
    492         self.assertEqual(s1.graphident, 'do_import')
    493         self.assertEqual(s1.identifier, 'do_import')
    494         self.assertEqual(s1.filename, 'do_import')
    495 
    496         v = s1.infoTuple()
    497         self.assertEqual(v, ('do_import',))
    498 
    499         # BaseModule adds some attributes and a custom infotuple
    500         self.assertHasExactMethods(modulegraph.BaseModule, '__init__', 'infoTuple')
    501         m1 = modulegraph.BaseModule('foo')
    502         self.assertEqual(m1.graphident, 'foo')
    503         self.assertEqual(m1.identifier, 'foo')
    504         self.assertEqual(m1.filename, None)
    505         self.assertEqual(m1.packagepath, None)
    506 
    507         m1 = modulegraph.BaseModule('foo', 'bar',  ['a'])
    508         self.assertEqual(m1.graphident, 'foo')
    509         self.assertEqual(m1.identifier, 'foo')
    510         self.assertEqual(m1.filename, 'bar')
    511         self.assertEqual(m1.packagepath, ['a'])
    512 
    513 class TestModuleGraph (unittest.TestCase):
    514     # Test for class modulegraph.modulegraph.ModuleGraph
    515     if not hasattr(unittest.TestCase, 'assertIsInstance'):
    516         def assertIsInstance(self, obj, types):
    517             self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types))
    518 
    519     def test_constructor(self):
    520         o = modulegraph.ModuleGraph()
    521         self.assertTrue(o.path is sys.path)
    522         self.assertEqual(o.lazynodes, {})
    523         self.assertEqual(o.replace_paths, ())
    524         self.assertEqual(o.debug, 0)
    525 
    526         # Stricter tests would be nice, but that requires
    527         # better control over what's on sys.path
    528         self.assertIsInstance(o.nspackages, dict)
    529 
    530         g = Graph.Graph()
    531         o = modulegraph.ModuleGraph(['a', 'b', 'c'], ['modA'], [
    532                 ('fromA', 'toB'), ('fromC', 'toD')],
    533                 {
    534                     'modA': ['modB', 'modC'],
    535                     'modC': ['modE', 'modF'],
    536                 }, g, 1)
    537         self.assertEqual(o.path, ['a', 'b', 'c'])
    538         self.assertEqual(o.lazynodes, {
    539             'modA': None,
    540             'modC': ['modE', 'modF'],
    541         })
    542         self.assertEqual(o.replace_paths, [('fromA', 'toB'), ('fromC', 'toD')])
    543         self.assertEqual(o.nspackages, {})
    544         self.assertTrue(o.graph is g)
    545         self.assertEqual(o.debug, 1)
    546 
    547     def test_calc_setuptools_nspackages(self):
    548         stdlib = [ fn for fn in sys.path if fn.startswith(sys.prefix) and 'site-packages' not in fn ]
    549         for subdir in [ nm for nm in os.listdir(TESTDATA) if nm != 'src' ]:
    550             graph = modulegraph.ModuleGraph(path=[
    551                     os.path.join(TESTDATA, subdir, "parent"),
    552                     os.path.join(TESTDATA, subdir, "child"),
    553                 ] + stdlib)
    554 
    555             pkgs = graph.nspackages
    556             self.assertTrue('namedpkg' in pkgs)
    557             self.assertEqual(set(pkgs['namedpkg']),
    558                     set([
    559                         os.path.join(TESTDATA, subdir, "parent", "namedpkg"),
    560                         os.path.join(TESTDATA, subdir, "child", "namedpkg"),
    561                     ]))
    562             self.assertFalse(os.path.exists(os.path.join(TESTDATA, subdir, "parent", "namedpkg", "__init__.py")))
    563             self.assertFalse(os.path.exists(os.path.join(TESTDATA, subdir, "child", "namedpkg", "__init__.py")))
    564 
    565     def testImpliedReference(self):
    566         graph = modulegraph.ModuleGraph()
    567 
    568         record = []
    569         def import_hook(*args):
    570             record.append(('import_hook',) + args)
    571             return [graph.createNode(modulegraph.Node, args[0])]
    572 
    573         def _safe_import_hook(*args):
    574             record.append(('_safe_import_hook',) + args)
    575             return [graph.createNode(modulegraph.Node, args[0])]
    576 
    577         graph.import_hook = import_hook
    578         graph._safe_import_hook = _safe_import_hook
    579 
    580         n1 = graph.createNode(modulegraph.Node, 'n1')
    581         n2 = graph.createNode(modulegraph.Node, 'n2')
    582 
    583         graph.implyNodeReference(n1, n2)
    584         outs, ins = map(list, graph.get_edges(n1))
    585         self.assertEqual(outs, [n2])
    586         self.assertEqual(ins, [])
    587 
    588         self.assertEqual(record, [])
    589 
    590         graph.implyNodeReference(n2, "n3")
    591         n3 = graph.findNode('n3')
    592         outs, ins = map(list, graph.get_edges(n2))
    593         self.assertEqual(outs, [n3])
    594         self.assertEqual(ins, [n1])
    595         self.assertEqual(record, [
    596             ('_safe_import_hook', 'n3', n2, None)
    597         ])
    598 
    599 
    600 
    601     @expectedFailure
    602     def test_findNode(self):
    603         self.fail("findNode")
    604 
    605     def test_run_script(self):
    606         script = os.path.join(os.path.dirname(TESTDATA), 'script')
    607 
    608         graph = modulegraph.ModuleGraph()
    609         master = graph.createNode(modulegraph.Node, 'root')
    610         m = graph.run_script(script, master)
    611         self.assertEqual(list(graph.get_edges(master)[0])[0], m)
    612         self.assertEqual(set(graph.get_edges(m)[0]), set([
    613             graph.findNode('sys'),
    614             graph.findNode('os'),
    615         ]))
    616 
    617     @expectedFailure
    618     def test_import_hook(self):
    619         self.fail("import_hook")
    620 
    621     def test_determine_parent(self):
    622         graph = modulegraph.ModuleGraph()
    623         graph.import_hook('os.path', None)
    624         graph.import_hook('idlelib', None)
    625         graph.import_hook('xml.dom', None)
    626 
    627         for node in graph.nodes():
    628             if isinstance(node, modulegraph.Package):
    629                 break
    630         else:
    631             self.fail("No package located, should have at least 'os'")
    632 
    633         self.assertIsInstance(node, modulegraph.Package)
    634         parent = graph._determine_parent(node)
    635         self.assertEqual(parent.identifier, node.identifier)
    636         self.assertEqual(parent, graph.findNode(node.identifier))
    637         self.assertTrue(isinstance(parent, modulegraph.Package))
    638 
    639         # XXX: Might be a usecase for some odd code in determine_parent...
    640         #node = modulegraph.Package('encodings')
    641         #node.packagepath = parent.packagepath
    642         #m = graph._determine_parent(node)
    643         #self.assertTrue(m is parent)
    644 
    645         m = graph.findNode('xml')
    646         self.assertEqual(graph._determine_parent(m), m)
    647 
    648         m = graph.findNode('xml.dom')
    649         self.assertEqual(graph._determine_parent(m), graph.findNode('xml.dom'))
    650 
    651 
    652     @expectedFailure
    653     def test_find_head_package(self):
    654         self.fail("find_head_package")
    655 
    656     def test_load_tail(self):
    657         # XXX: This test is dodgy!
    658         graph = modulegraph.ModuleGraph()
    659 
    660         record = []
    661         def _import_module(partname, fqname, parent):
    662             record.append((partname, fqname, parent))
    663             if partname == 'raises' or '.raises.' in fqname:
    664                 return None
    665             return modulegraph.Node(fqname)
    666 
    667         graph._import_module = _import_module
    668 
    669         record = []
    670         root = modulegraph.Node('root')
    671         m = graph._load_tail(root, '')
    672         self.assertTrue(m is root)
    673         self.assertEqual(record, [
    674             ])
    675 
    676         record = []
    677         root = modulegraph.Node('root')
    678         m = graph._load_tail(root, 'sub')
    679         self.assertFalse(m is root)
    680         self.assertEqual(record, [
    681                 ('sub', 'root.sub', root),
    682             ])
    683 
    684         record = []
    685         root = modulegraph.Node('root')
    686         m = graph._load_tail(root, 'sub.sub1')
    687         self.assertFalse(m is root)
    688         node = modulegraph.Node('root.sub')
    689         self.assertEqual(record, [
    690                 ('sub', 'root.sub', root),
    691                 ('sub1', 'root.sub.sub1', node),
    692             ])
    693 
    694         record = []
    695         root = modulegraph.Node('root')
    696         m = graph._load_tail(root, 'sub.sub1.sub2')
    697         self.assertFalse(m is root)
    698         node = modulegraph.Node('root.sub')
    699         node2 = modulegraph.Node('root.sub.sub1')
    700         self.assertEqual(record, [
    701                 ('sub', 'root.sub', root),
    702                 ('sub1', 'root.sub.sub1', node),
    703                 ('sub2', 'root.sub.sub1.sub2', node2),
    704             ])
    705 
    706         n = graph._load_tail(root, 'raises')
    707         self.assertIsInstance(n, modulegraph.MissingModule)
    708         self.assertEqual(n.identifier, 'root.raises')
    709 
    710         n = graph._load_tail(root, 'sub.raises')
    711         self.assertIsInstance(n, modulegraph.MissingModule)
    712         self.assertEqual(n.identifier, 'root.sub.raises')
    713 
    714         n = graph._load_tail(root, 'sub.raises.sub')
    715         self.assertIsInstance(n, modulegraph.MissingModule)
    716         self.assertEqual(n.identifier, 'root.sub.raises.sub')
    717 
    718 
    719 
    720     @expectedFailure
    721     def test_ensure_fromlist(self):
    722         # 1. basic 'from module import name, name'
    723         # 2. 'from module import *'
    724         # 3. from module import os
    725         #    (where 'os' is not a name in 'module',
    726         #     should create MissingModule node, and
    727         #     should *not* refer to the global os)
    728         self.fail("ensure_fromlist")
    729 
    730     @expectedFailure
    731     def test_find_all_submodules(self):
    732         # 1. basic
    733         # 2. no packagepath (basic module)
    734         # 3. extensions, python modules
    735         # 4. with/without zipfile
    736         # 5. files that aren't python modules/extensions
    737         self.fail("find_all_submodules")
    738 
    739     @expectedFailure
    740     def test_import_module(self):
    741         self.fail("import_module")
    742 
    743     @expectedFailure
    744     def test_load_module(self):
    745         self.fail("load_module")
    746 
    747     @expectedFailure
    748     def test_safe_import_hook(self):
    749         self.fail("safe_import_hook")
    750 
    751     @expectedFailure
    752     def test_scan_code(self):
    753         mod = modulegraph.Node('root')
    754 
    755         graph = modulegraph.ModuleGraph()
    756         code = compile('', '<test>', 'exec', 0, False)
    757         graph.scan_code(code, mod)
    758         self.assertEqual(list(graph.nodes()), [])
    759 
    760         node_map = {}
    761         def _safe_import(name, mod, fromlist, level):
    762             if name in node_map:
    763                 node = node_map[name]
    764             else:
    765                 node = modulegraph.Node(name)
    766             node_map[name] = node
    767             return [node]
    768 
    769         graph = modulegraph.ModuleGraph()
    770         graph._safe_import_hook = _safe_import
    771 
    772         code = compile(textwrap.dedent('''\
    773             import sys
    774             import os.path
    775 
    776             def testfunc():
    777                 import shutil
    778             '''), '<test>', 'exec', 0, False)
    779         graph.scan_code(code, mod)
    780         modules = [node.identifier for node in graph.nodes()]
    781         self.assertEqual(set(node_map), set(['sys', 'os.path', 'shutil']))
    782 
    783 
    784         # from module import a, b, c
    785         # from module import *
    786         #  both:
    787         #   -> with/without globals
    788         #   -> with/without modules in globals (e.g,
    789         #       from os import * adds dependency to os.path)
    790         # from .module import a
    791         # from ..module import a
    792         #   -> check levels
    793         # import name
    794         # import a.b
    795         #   -> should add dependency to a
    796         # try to build case where commented out
    797         # code would behave different than current code
    798         # (Carbon.SomeMod contains 'import Sibling' seems
    799         # to cause difference in real code)
    800 
    801         self.fail("actual test needed")
    802 
    803 
    804 
    805     @expectedFailure
    806     def test_load_package(self):
    807         self.fail("load_package")
    808 
    809     def test_find_module(self):
    810         record = []
    811         def mock_finder(name, path):
    812             record.append((name, path))
    813             return saved_finder(name, path)
    814 
    815         saved_finder = modulegraph.find_module
    816         try:
    817             modulegraph.find_module = mock_finder
    818 
    819             graph = modulegraph.ModuleGraph()
    820             m = graph._find_module('sys', None)
    821             self.assertEqual(record, [])
    822             self.assertEqual(m, (None, None, ("", "", imp.C_BUILTIN)))
    823 
    824             modulegraph.find_module = saved_finder
    825             xml = graph.import_hook("xml")[0]
    826             self.assertEqual(xml.identifier, 'xml')
    827             modulegraph.find_module = mock_finder
    828 
    829             self.assertRaises(ImportError, graph._find_module, 'xml', None)
    830 
    831             self.assertEqual(record, [])
    832             m = graph._find_module('shutil', None)
    833             self.assertEqual(record, [
    834                 ('shutil', graph.path),
    835             ])
    836             self.assertTrue(isinstance(m, tuple))
    837             self.assertEqual(len(m), 3)
    838             self.assertTrue(hasattr(m[0], 'read'))
    839             self.assertIsInstance(m[0].read(), str)
    840             srcfn = shutil.__file__
    841             if srcfn.endswith('.pyc'):
    842                 srcfn = srcfn[:-1]
    843             self.assertEqual(m[1], srcfn)
    844             self.assertEqual(m[2], ('.py', 'rU', imp.PY_SOURCE))
    845             m[0].close()
    846 
    847             m2 = graph._find_module('shutil', None)
    848             self.assertEqual(m[1:], m2[1:])
    849             m2[0].close()
    850 
    851 
    852             record[:] = []
    853             m = graph._find_module('sax', xml.packagepath, xml)
    854             self.assertEqual(m,
    855                     (None, os.path.join(os.path.dirname(xml.filename), 'sax'),
    856                     ('', '', imp.PKG_DIRECTORY)))
    857             self.assertEqual(record, [
    858                 ('sax', xml.packagepath),
    859             ])
    860             if m[0] is not None: m[0].close()
    861 
    862         finally:
    863             modulegraph.find_module = saved_finder
    864 
    865     @expectedFailure
    866     def test_create_xref(self):
    867         self.fail("create_xref")
    868 
    869     @expectedFailure
    870     def test_itergraphreport(self):
    871         self.fail("itergraphreport")
    872 
    873     def test_report(self):
    874         graph = modulegraph.ModuleGraph()
    875 
    876         saved_stdout = sys.stdout
    877         try:
    878             fp = sys.stdout = StringIO()
    879             graph.report()
    880             lines = fp.getvalue().splitlines()
    881             fp.close()
    882 
    883             self.assertEqual(len(lines), 3)
    884             self.assertEqual(lines[0], '')
    885             self.assertEqual(lines[1], 'Class           Name                      File')
    886             self.assertEqual(lines[2], '-----           ----                      ----')
    887 
    888             fp = sys.stdout = StringIO()
    889             graph._safe_import_hook('os', None, ())
    890             graph._safe_import_hook('sys', None, ())
    891             graph._safe_import_hook('nomod', None, ())
    892             graph.report()
    893             lines = fp.getvalue().splitlines()
    894             fp.close()
    895 
    896             self.assertEqual(lines[0], '')
    897             self.assertEqual(lines[1], 'Class           Name                      File')
    898             self.assertEqual(lines[2], '-----           ----                      ----')
    899             expected = []
    900             for n in graph.flatten():
    901                 if n.filename:
    902                     expected.append([type(n).__name__, n.identifier, n.filename])
    903                 else:
    904                     expected.append([type(n).__name__, n.identifier])
    905 
    906             expected.sort()
    907             actual = [item.split() for item in lines[3:]]
    908             actual.sort()
    909             self.assertEqual(expected, actual)
    910 
    911 
    912         finally:
    913             sys.stdout = saved_stdout
    914 
    915     def test_graphreport(self):
    916 
    917         def my_iter(flatpackages="packages"):
    918             yield "line1\n"
    919             yield str(flatpackages) + "\n"
    920             yield "line2\n"
    921 
    922         graph = modulegraph.ModuleGraph()
    923         graph.itergraphreport = my_iter
    924 
    925         fp = StringIO()
    926         graph.graphreport(fp)
    927         self.assertEqual(fp.getvalue(), "line1\n()\nline2\n")
    928 
    929         fp = StringIO()
    930         graph.graphreport(fp, "deps")
    931         self.assertEqual(fp.getvalue(), "line1\ndeps\nline2\n")
    932 
    933         saved_stdout = sys.stdout
    934         try:
    935             sys.stdout = fp = StringIO()
    936             graph.graphreport()
    937             self.assertEqual(fp.getvalue(), "line1\n()\nline2\n")
    938 
    939         finally:
    940             sys.stdout = saved_stdout
    941 
    942 
    943     def test_replace_paths_in_code(self):
    944         graph = modulegraph.ModuleGraph(replace_paths=[
    945                 ('path1', 'path2'),
    946                 ('path3/path5', 'path4'),
    947             ])
    948 
    949         co = compile(textwrap.dedent("""
    950         [x for x in range(4)]
    951         """), "path4/index.py", 'exec', 0, 1)
    952         co = graph._replace_paths_in_code(co)
    953         self.assertEqual(co.co_filename, 'path4/index.py')
    954 
    955         co = compile(textwrap.dedent("""
    956         [x for x in range(4)]
    957         (x for x in range(4))
    958         """), "path1/index.py", 'exec', 0, 1)
    959         self.assertEqual(co.co_filename, 'path1/index.py')
    960         co = graph._replace_paths_in_code(co)
    961         self.assertEqual(co.co_filename, 'path2/index.py')
    962         for c in co.co_consts:
    963             if isinstance(c, type(co)):
    964                 self.assertEqual(c.co_filename, 'path2/index.py')
    965 
    966         co = compile(textwrap.dedent("""
    967         [x for x in range(4)]
    968         """), "path3/path4/index.py", 'exec', 0, 1)
    969         co = graph._replace_paths_in_code(co)
    970         self.assertEqual(co.co_filename, 'path3/path4/index.py')
    971 
    972         co = compile(textwrap.dedent("""
    973         [x for x in range(4)]
    974         """), "path3/path5.py", 'exec', 0, 1)
    975         co = graph._replace_paths_in_code(co)
    976         self.assertEqual(co.co_filename, 'path3/path5.py')
    977 
    978         co = compile(textwrap.dedent("""
    979         [x for x in range(4)]
    980         """), "path3/path5/index.py", 'exec', 0, 1)
    981         co = graph._replace_paths_in_code(co)
    982         self.assertEqual(co.co_filename, 'path4/index.py')
    983 
    984     def test_createReference(self):
    985         graph = modulegraph.ModuleGraph()
    986         n1 = modulegraph.Node('n1')
    987         n2 = modulegraph.Node('n2')
    988         graph.addNode(n1)
    989         graph.addNode(n2)
    990 
    991         graph.createReference(n1, n2)
    992         outs, ins = map(list, graph.get_edges(n1))
    993         self.assertEqual(outs, [n2])
    994         self.assertEqual(ins, [])
    995         outs, ins = map(list, graph.get_edges(n2))
    996         self.assertEqual(outs, [])
    997         self.assertEqual(ins, [n1])
    998 
    999         e = graph.graph.edge_by_node('n1', 'n2')
   1000         self.assertIsInstance(e, int)
   1001         self.assertEqual(graph.graph.edge_data(e), 'direct')
   1002 
   1003     def test_create_xref(self):
   1004         # XXX: This test is far from optimal, it just ensures
   1005         # that all code is exercised to catch small bugs and
   1006         # py3k issues without verifying that the code actually
   1007         # works....
   1008         graph = modulegraph.ModuleGraph()
   1009         if __file__.endswith('.py'):
   1010             graph.run_script(__file__)
   1011         else:
   1012             graph.run_script(__file__[:-1])
   1013 
   1014         graph.import_hook('os')
   1015         graph.import_hook('xml.etree')
   1016         graph.import_hook('unittest')
   1017 
   1018         fp = StringIO()
   1019         graph.create_xref(out=fp)
   1020 
   1021         data = fp.getvalue()
   1022         r = ET.fromstring(data)
   1023 
   1024     def test_itergraphreport(self):
   1025         # XXX: This test is far from optimal, it just ensures
   1026         # that all code is exercised to catch small bugs and
   1027         # py3k issues without verifying that the code actually
   1028         # works....
   1029         graph = modulegraph.ModuleGraph()
   1030         if __file__.endswith('.py'):
   1031             graph.run_script(__file__)
   1032         else:
   1033             graph.run_script(__file__[:-1])
   1034         graph.import_hook('os')
   1035         graph.import_hook('xml.etree')
   1036         graph.import_hook('unittest')
   1037         graph.import_hook('distutils.command.build')
   1038 
   1039         fp = StringIO()
   1040         list(graph.itergraphreport())
   1041 
   1042         # XXX: platpackages isn't implemented, and is undocumented hence
   1043         # it is unclear what this is inteded to be...
   1044         #list(graph.itergraphreport(flatpackages=...))
   1045 
   1046 
   1047 
   1048 
   1049 class CompatTests (unittest.TestCase):
   1050     def test_Bchr(self):
   1051         v = modulegraph._Bchr(ord('A'))
   1052         if sys.version_info[0] == 2:
   1053             self.assertTrue(isinstance(v, bytes))
   1054             self.assertEqual(v, b'A')
   1055         else:
   1056             self.assertTrue(isinstance(v, int))
   1057             self.assertEqual(v, ord('A'))
   1058 
   1059 if __name__ == "__main__":
   1060     unittest.main()
   1061