| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2006-2007 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 """ | |
| 5 Tests for twisted.python.modules, abstract access to imported or importable | |
| 6 objects. | |
| 7 """ | |
| 8 | |
| 9 import os | |
| 10 import sys | |
| 11 import itertools | |
| 12 import zipfile | |
| 13 import compileall | |
| 14 | |
| 15 from twisted.trial.unittest import TestCase | |
| 16 | |
| 17 from twisted.python import modules | |
| 18 from twisted.python.filepath import FilePath | |
| 19 from twisted.python.reflect import namedAny | |
| 20 | |
| 21 from twisted.test.test_paths import zipit | |
| 22 | |
| 23 | |
| 24 | |
| 25 class PySpaceTestCase(TestCase): | |
| 26 | |
| 27 def findByIteration(self, modname, where=modules, importPackages=False): | |
| 28 """ | |
| 29 You don't ever actually want to do this, so it's not in the public API,
but | |
| 30 sometimes we want to compare the result of an iterative call with a | |
| 31 lookup call and make sure they're the same for test purposes. | |
| 32 """ | |
| 33 for modinfo in where.walkModules(importPackages=importPackages): | |
| 34 if modinfo.name == modname: | |
| 35 return modinfo | |
| 36 self.fail("Unable to find module %r through iteration." % (modname,)) | |
| 37 | |
| 38 | |
| 39 | |
| 40 class BasicTests(PySpaceTestCase): | |
| 41 def test_nonexistentPaths(self): | |
| 42 """ | |
| 43 Verify that L{modules.walkModules} ignores entries in sys.path which | |
| 44 do not exist in the filesystem. | |
| 45 """ | |
| 46 existentPath = FilePath(self.mktemp()) | |
| 47 os.makedirs(existentPath.child("test_package").path) | |
| 48 existentPath.child("test_package").child("__init__.py").setContent("") | |
| 49 | |
| 50 nonexistentPath = FilePath(self.mktemp()) | |
| 51 self.failIf(nonexistentPath.exists()) | |
| 52 | |
| 53 originalSearchPaths = sys.path[:] | |
| 54 sys.path[:] = [existentPath.path] | |
| 55 try: | |
| 56 expected = [modules.getModule("test_package")] | |
| 57 | |
| 58 beforeModules = list(modules.walkModules()) | |
| 59 sys.path.append(nonexistentPath.path) | |
| 60 afterModules = list(modules.walkModules()) | |
| 61 finally: | |
| 62 sys.path[:] = originalSearchPaths | |
| 63 | |
| 64 self.assertEqual(beforeModules, expected) | |
| 65 self.assertEqual(afterModules, expected) | |
| 66 | |
| 67 | |
| 68 def test_nonDirectoryPaths(self): | |
| 69 """ | |
| 70 Verify that L{modules.walkModules} ignores entries in sys.path which | |
| 71 refer to regular files in the filesystem. | |
| 72 """ | |
| 73 existentPath = FilePath(self.mktemp()) | |
| 74 os.makedirs(existentPath.child("test_package").path) | |
| 75 existentPath.child("test_package").child("__init__.py").setContent("") | |
| 76 | |
| 77 nonDirectoryPath = FilePath(self.mktemp()) | |
| 78 self.failIf(nonDirectoryPath.exists()) | |
| 79 nonDirectoryPath.setContent("zip file or whatever\n") | |
| 80 | |
| 81 originalSearchPaths = sys.path[:] | |
| 82 sys.path[:] = [existentPath.path] | |
| 83 try: | |
| 84 beforeModules = list(modules.walkModules()) | |
| 85 sys.path.append(nonDirectoryPath.path) | |
| 86 afterModules = list(modules.walkModules()) | |
| 87 finally: | |
| 88 sys.path[:] = originalSearchPaths | |
| 89 | |
| 90 self.assertEqual(beforeModules, afterModules) | |
| 91 | |
| 92 | |
| 93 def test_twistedShowsUp(self): | |
| 94 """ | |
| 95 Scrounge around in the top-level module namespace and make sure that | |
| 96 Twisted shows up, and that the module thusly obtained is the same as | |
| 97 the module that we find when we look for it explicitly by name. | |
| 98 """ | |
| 99 self.assertEquals(modules.getModule('twisted'), | |
| 100 self.findByIteration("twisted")) | |
| 101 | |
| 102 | |
| 103 def test_dottedNames(self): | |
| 104 """ | |
| 105 Verify that the walkModules APIs will give us back subpackages, not just | |
| 106 subpackages. | |
| 107 """ | |
| 108 self.assertEquals( | |
| 109 modules.getModule('twisted.python'), | |
| 110 self.findByIteration("twisted.python", | |
| 111 where=modules.getModule('twisted'))) | |
| 112 | |
| 113 | |
| 114 def test_onlyTopModules(self): | |
| 115 """ | |
| 116 Verify that the iterModules API will only return top-level modules and | |
| 117 packages, not submodules or subpackages. | |
| 118 """ | |
| 119 for module in modules.iterModules(): | |
| 120 self.failIf( | |
| 121 '.' in module.name, | |
| 122 "no nested modules should be returned from iterModules: %r" | |
| 123 % (module.filePath)) | |
| 124 | |
| 125 | |
| 126 def test_loadPackagesAndModules(self): | |
| 127 """ | |
| 128 Verify that we can locate and load packages, modules, submodules, and | |
| 129 subpackages. | |
| 130 """ | |
| 131 for n in ['os', | |
| 132 'twisted', | |
| 133 'twisted.python', | |
| 134 'twisted.python.reflect']: | |
| 135 m = namedAny(n) | |
| 136 self.failUnlessIdentical( | |
| 137 modules.getModule(n).load(), | |
| 138 m) | |
| 139 self.failUnlessIdentical( | |
| 140 self.findByIteration(n).load(), | |
| 141 m) | |
| 142 | |
| 143 | |
| 144 def test_pathEntriesOnPath(self): | |
| 145 """ | |
| 146 Verify that path entries discovered via module loading are, in fact, on | |
| 147 sys.path somewhere. | |
| 148 """ | |
| 149 for n in ['os', | |
| 150 'twisted', | |
| 151 'twisted.python', | |
| 152 'twisted.python.reflect']: | |
| 153 self.failUnlessIn( | |
| 154 modules.getModule(n).pathEntry.filePath.path, | |
| 155 sys.path) | |
| 156 | |
| 157 | |
| 158 def test_alwaysPreferPy(self): | |
| 159 """ | |
| 160 Verify that .py files will always be preferred to .pyc files, regardless
of | |
| 161 directory listing order. | |
| 162 """ | |
| 163 mypath = FilePath(self.mktemp()) | |
| 164 mypath.createDirectory() | |
| 165 pp = modules.PythonPath(sysPath=[mypath.path]) | |
| 166 originalSmartPath = pp._smartPath | |
| 167 def _evilSmartPath(pathName): | |
| 168 o = originalSmartPath(pathName) | |
| 169 originalChildren = o.children | |
| 170 def evilChildren(): | |
| 171 # normally this order is random; let's make sure it always | |
| 172 # comes up .pyc-first. | |
| 173 x = originalChildren() | |
| 174 x.sort() | |
| 175 x.reverse() | |
| 176 return x | |
| 177 o.children = evilChildren | |
| 178 return o | |
| 179 mypath.child("abcd.py").setContent('\n') | |
| 180 compileall.compile_dir(mypath.path, quiet=True) | |
| 181 # sanity check | |
| 182 self.assertEquals(len(mypath.children()), 2) | |
| 183 pp._smartPath = _evilSmartPath | |
| 184 self.assertEquals(pp['abcd'].filePath, | |
| 185 mypath.child('abcd.py')) | |
| 186 | |
| 187 | |
| 188 def test_packageMissingPath(self): | |
| 189 """ | |
| 190 A package can delete its __path__ for some reasons, | |
| 191 C{modules.PythonPath} should be able to deal with it. | |
| 192 """ | |
| 193 mypath = FilePath(self.mktemp()) | |
| 194 mypath.createDirectory() | |
| 195 pp = modules.PythonPath(sysPath=[mypath.path]) | |
| 196 subpath = mypath.child("abcd") | |
| 197 subpath.createDirectory() | |
| 198 subpath.child("__init__.py").setContent('del __path__\n') | |
| 199 sys.path.append(mypath.path) | |
| 200 import abcd | |
| 201 try: | |
| 202 l = list(pp.walkModules()) | |
| 203 self.assertEquals(len(l), 1) | |
| 204 self.assertEquals(l[0].name, 'abcd') | |
| 205 finally: | |
| 206 del abcd | |
| 207 del sys.modules['abcd'] | |
| 208 sys.path.remove(mypath.path) | |
| 209 | |
| 210 | |
| 211 | |
| 212 class PathModificationTest(PySpaceTestCase): | |
| 213 """ | |
| 214 These tests share setup/cleanup behavior of creating a dummy package and | |
| 215 stuffing some code in it. | |
| 216 """ | |
| 217 | |
| 218 _serialnum = itertools.count().next # used to generate serial numbers for | |
| 219 # package names. | |
| 220 | |
| 221 def setUp(self): | |
| 222 self.pathExtensionName = self.mktemp() | |
| 223 self.pathExtension = FilePath(self.pathExtensionName) | |
| 224 self.pathExtension.createDirectory() | |
| 225 self.packageName = "pyspacetests%d" % (self._serialnum(),) | |
| 226 self.packagePath = self.pathExtension.child(self.packageName) | |
| 227 self.packagePath.createDirectory() | |
| 228 self.packagePath.child("__init__.py").setContent("") | |
| 229 self.packagePath.child("a.py").setContent("") | |
| 230 self.packagePath.child("b.py").setContent("") | |
| 231 self.packagePath.child("c__init__.py").setContent("") | |
| 232 self.pathSetUp = False | |
| 233 | |
| 234 | |
| 235 def _setupSysPath(self): | |
| 236 assert not self.pathSetUp | |
| 237 self.pathSetUp = True | |
| 238 sys.path.append(self.pathExtensionName) | |
| 239 | |
| 240 | |
| 241 def _underUnderPathTest(self, doImport=True): | |
| 242 moddir2 = self.mktemp() | |
| 243 fpmd = FilePath(moddir2) | |
| 244 fpmd.createDirectory() | |
| 245 fpmd.child("foozle.py").setContent("x = 123\n") | |
| 246 self.packagePath.child("__init__.py").setContent( | |
| 247 "__path__.append(%r)\n" % (moddir2,)) | |
| 248 # Cut here | |
| 249 self._setupSysPath() | |
| 250 modinfo = modules.getModule(self.packageName) | |
| 251 self.assertEquals( | |
| 252 self.findByIteration(self.packageName+".foozle", modinfo, | |
| 253 importPackages=doImport), | |
| 254 modinfo['foozle']) | |
| 255 self.assertEquals(modinfo['foozle'].load().x, 123) | |
| 256 | |
| 257 | |
| 258 def test_underUnderPathAlreadyImported(self): | |
| 259 """ | |
| 260 Verify that iterModules will honor the __path__ of already-loaded packag
es. | |
| 261 """ | |
| 262 self._underUnderPathTest() | |
| 263 | |
| 264 | |
| 265 def test_underUnderPathNotAlreadyImported(self): | |
| 266 """ | |
| 267 Verify that iterModules will honor the __path__ of already-loaded packag
es. | |
| 268 """ | |
| 269 self._underUnderPathTest(False) | |
| 270 | |
| 271 | |
| 272 test_underUnderPathNotAlreadyImported.todo = ( | |
| 273 "This may be impossible but it sure would be nice.") | |
| 274 | |
| 275 | |
| 276 def _listModules(self): | |
| 277 pkginfo = modules.getModule(self.packageName) | |
| 278 nfni = [modinfo.name.split(".")[-1] for modinfo in | |
| 279 pkginfo.iterModules()] | |
| 280 nfni.sort() | |
| 281 self.failUnlessEqual(nfni, ['a', 'b', 'c__init__']) | |
| 282 | |
| 283 | |
| 284 def test_listingModules(self): | |
| 285 """ | |
| 286 Make sure the module list comes back as we expect from iterModules on a | |
| 287 package, whether zipped or not. | |
| 288 """ | |
| 289 self._setupSysPath() | |
| 290 self._listModules() | |
| 291 | |
| 292 | |
| 293 def test_listingModulesAlreadyImported(self): | |
| 294 """ | |
| 295 Make sure the module list comes back as we expect from iterModules on a | |
| 296 package, whether zipped or not, even if the package has already been | |
| 297 imported. | |
| 298 """ | |
| 299 self._setupSysPath() | |
| 300 namedAny(self.packageName) | |
| 301 self._listModules() | |
| 302 | |
| 303 | |
| 304 def tearDown(self): | |
| 305 # Intentionally using 'assert' here, this is not a test assertion, this | |
| 306 # is just an "oh fuck what is going ON" assertion. -glyph | |
| 307 if self.pathSetUp: | |
| 308 HORK = "path cleanup failed: don't be surprised if other tests break
" | |
| 309 assert sys.path.pop() is self.pathExtensionName, HORK+", 1" | |
| 310 assert self.pathExtensionName not in sys.path, HORK+", 2" | |
| 311 | |
| 312 | |
| 313 | |
| 314 class RebindingTest(PathModificationTest): | |
| 315 """ | |
| 316 These tests verify that the default path interrogation API works properly | |
| 317 even when sys.path has been rebound to a different object. | |
| 318 """ | |
| 319 def _setupSysPath(self): | |
| 320 assert not self.pathSetUp | |
| 321 self.pathSetUp = True | |
| 322 self.savedSysPath = sys.path | |
| 323 sys.path = sys.path[:] | |
| 324 sys.path.append(self.pathExtensionName) | |
| 325 | |
| 326 | |
| 327 def tearDown(self): | |
| 328 """ | |
| 329 Clean up sys.path by re-binding our original object. | |
| 330 """ | |
| 331 if self.pathSetUp: | |
| 332 sys.path = self.savedSysPath | |
| 333 | |
| 334 | |
| 335 | |
| 336 class ZipPathModificationTest(PathModificationTest): | |
| 337 def _setupSysPath(self): | |
| 338 assert not self.pathSetUp | |
| 339 zipit(self.pathExtensionName, self.pathExtensionName+'.zip') | |
| 340 self.pathExtensionName += '.zip' | |
| 341 assert zipfile.is_zipfile(self.pathExtensionName) | |
| 342 PathModificationTest._setupSysPath(self) | |
| 343 | |
| 344 | |
| 345 class PythonPathTestCase(TestCase): | |
| 346 """ | |
| 347 Tests for the class which provides the implementation for all of the | |
| 348 public API of L{twisted.python.modules}. | |
| 349 """ | |
| 350 def test_unhandledImporter(self): | |
| 351 """ | |
| 352 Make sure that the behavior when encountering an unknown importer | |
| 353 type is not catastrophic failure. | |
| 354 """ | |
| 355 class SecretImporter(object): | |
| 356 pass | |
| 357 | |
| 358 def hook(name): | |
| 359 return SecretImporter() | |
| 360 | |
| 361 syspath = ['example/path'] | |
| 362 sysmodules = {} | |
| 363 syshooks = [hook] | |
| 364 syscache = {} | |
| 365 def sysloader(name): | |
| 366 return None | |
| 367 space = modules.PythonPath( | |
| 368 syspath, sysmodules, syshooks, syscache, sysloader) | |
| 369 entries = list(space.iterEntries()) | |
| 370 self.assertEquals(len(entries), 1) | |
| 371 self.assertRaises(KeyError, lambda: entries[0]['module']) | |
| OLD | NEW |