1
# Copyright (c) 2006-2009 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
Tests for twisted.python.modules, abstract access to imported or importable
16
from twisted.trial.unittest import TestCase
18
from twisted.python import modules
19
from twisted.python.filepath import FilePath
20
from twisted.python.reflect import namedAny
22
from twisted.test.test_paths import zipit
26
class PySpaceTestCase(TestCase):
28
def findByIteration(self, modname, where=modules, importPackages=False):
30
You don't ever actually want to do this, so it's not in the public API, but
31
sometimes we want to compare the result of an iterative call with a
32
lookup call and make sure they're the same for test purposes.
34
for modinfo in where.walkModules(importPackages=importPackages):
35
if modinfo.name == modname:
37
self.fail("Unable to find module %r through iteration." % (modname,))
41
class BasicTests(PySpaceTestCase):
42
def test_nonexistentPaths(self):
44
Verify that L{modules.walkModules} ignores entries in sys.path which
45
do not exist in the filesystem.
47
existentPath = FilePath(self.mktemp())
48
os.makedirs(existentPath.child("test_package").path)
49
existentPath.child("test_package").child("__init__.py").setContent("")
51
nonexistentPath = FilePath(self.mktemp())
52
self.failIf(nonexistentPath.exists())
54
originalSearchPaths = sys.path[:]
55
sys.path[:] = [existentPath.path]
57
expected = [modules.getModule("test_package")]
59
beforeModules = list(modules.walkModules())
60
sys.path.append(nonexistentPath.path)
61
afterModules = list(modules.walkModules())
63
sys.path[:] = originalSearchPaths
65
self.assertEqual(beforeModules, expected)
66
self.assertEqual(afterModules, expected)
69
def test_nonDirectoryPaths(self):
71
Verify that L{modules.walkModules} ignores entries in sys.path which
72
refer to regular files in the filesystem.
74
existentPath = FilePath(self.mktemp())
75
os.makedirs(existentPath.child("test_package").path)
76
existentPath.child("test_package").child("__init__.py").setContent("")
78
nonDirectoryPath = FilePath(self.mktemp())
79
self.failIf(nonDirectoryPath.exists())
80
nonDirectoryPath.setContent("zip file or whatever\n")
82
originalSearchPaths = sys.path[:]
83
sys.path[:] = [existentPath.path]
85
beforeModules = list(modules.walkModules())
86
sys.path.append(nonDirectoryPath.path)
87
afterModules = list(modules.walkModules())
89
sys.path[:] = originalSearchPaths
91
self.assertEqual(beforeModules, afterModules)
94
def test_twistedShowsUp(self):
96
Scrounge around in the top-level module namespace and make sure that
97
Twisted shows up, and that the module thusly obtained is the same as
98
the module that we find when we look for it explicitly by name.
100
self.assertEquals(modules.getModule('twisted'),
101
self.findByIteration("twisted"))
104
def test_dottedNames(self):
106
Verify that the walkModules APIs will give us back subpackages, not just
110
modules.getModule('twisted.python'),
111
self.findByIteration("twisted.python",
112
where=modules.getModule('twisted')))
115
def test_onlyTopModules(self):
117
Verify that the iterModules API will only return top-level modules and
118
packages, not submodules or subpackages.
120
for module in modules.iterModules():
123
"no nested modules should be returned from iterModules: %r"
127
def test_loadPackagesAndModules(self):
129
Verify that we can locate and load packages, modules, submodules, and
135
'twisted.python.reflect']:
137
self.failUnlessIdentical(
138
modules.getModule(n).load(),
140
self.failUnlessIdentical(
141
self.findByIteration(n).load(),
145
def test_pathEntriesOnPath(self):
147
Verify that path entries discovered via module loading are, in fact, on
153
'twisted.python.reflect']:
155
modules.getModule(n).pathEntry.filePath.path,
159
def test_alwaysPreferPy(self):
161
Verify that .py files will always be preferred to .pyc files, regardless of
162
directory listing order.
164
mypath = FilePath(self.mktemp())
165
mypath.createDirectory()
166
pp = modules.PythonPath(sysPath=[mypath.path])
167
originalSmartPath = pp._smartPath
168
def _evilSmartPath(pathName):
169
o = originalSmartPath(pathName)
170
originalChildren = o.children
172
# normally this order is random; let's make sure it always
173
# comes up .pyc-first.
174
x = originalChildren()
178
o.children = evilChildren
180
mypath.child("abcd.py").setContent('\n')
181
compileall.compile_dir(mypath.path, quiet=True)
183
self.assertEquals(len(mypath.children()), 2)
184
pp._smartPath = _evilSmartPath
185
self.assertEquals(pp['abcd'].filePath,
186
mypath.child('abcd.py'))
189
def test_packageMissingPath(self):
191
A package can delete its __path__ for some reasons,
192
C{modules.PythonPath} should be able to deal with it.
194
mypath = FilePath(self.mktemp())
195
mypath.createDirectory()
196
pp = modules.PythonPath(sysPath=[mypath.path])
197
subpath = mypath.child("abcd")
198
subpath.createDirectory()
199
subpath.child("__init__.py").setContent('del __path__\n')
200
sys.path.append(mypath.path)
203
l = list(pp.walkModules())
204
self.assertEquals(len(l), 1)
205
self.assertEquals(l[0].name, 'abcd')
208
del sys.modules['abcd']
209
sys.path.remove(mypath.path)
213
class PathModificationTest(PySpaceTestCase):
215
These tests share setup/cleanup behavior of creating a dummy package and
216
stuffing some code in it.
219
_serialnum = itertools.count().next # used to generate serial numbers for
223
self.pathExtensionName = self.mktemp()
224
self.pathExtension = FilePath(self.pathExtensionName)
225
self.pathExtension.createDirectory()
226
self.packageName = "pyspacetests%d" % (self._serialnum(),)
227
self.packagePath = self.pathExtension.child(self.packageName)
228
self.packagePath.createDirectory()
229
self.packagePath.child("__init__.py").setContent("")
230
self.packagePath.child("a.py").setContent("")
231
self.packagePath.child("b.py").setContent("")
232
self.packagePath.child("c__init__.py").setContent("")
233
self.pathSetUp = False
236
def _setupSysPath(self):
237
assert not self.pathSetUp
238
self.pathSetUp = True
239
sys.path.append(self.pathExtensionName)
242
def _underUnderPathTest(self, doImport=True):
243
moddir2 = self.mktemp()
244
fpmd = FilePath(moddir2)
245
fpmd.createDirectory()
246
fpmd.child("foozle.py").setContent("x = 123\n")
247
self.packagePath.child("__init__.py").setContent(
248
"__path__.append(%r)\n" % (moddir2,))
251
modinfo = modules.getModule(self.packageName)
253
self.findByIteration(self.packageName+".foozle", modinfo,
254
importPackages=doImport),
256
self.assertEquals(modinfo['foozle'].load().x, 123)
259
def test_underUnderPathAlreadyImported(self):
261
Verify that iterModules will honor the __path__ of already-loaded packages.
263
self._underUnderPathTest()
266
def test_underUnderPathNotAlreadyImported(self):
268
Verify that iterModules will honor the __path__ of already-loaded packages.
270
self._underUnderPathTest(False)
273
test_underUnderPathNotAlreadyImported.todo = (
274
"This may be impossible but it sure would be nice.")
277
def _listModules(self):
278
pkginfo = modules.getModule(self.packageName)
279
nfni = [modinfo.name.split(".")[-1] for modinfo in
280
pkginfo.iterModules()]
282
self.failUnlessEqual(nfni, ['a', 'b', 'c__init__'])
285
def test_listingModules(self):
287
Make sure the module list comes back as we expect from iterModules on a
288
package, whether zipped or not.
294
def test_listingModulesAlreadyImported(self):
296
Make sure the module list comes back as we expect from iterModules on a
297
package, whether zipped or not, even if the package has already been
301
namedAny(self.packageName)
306
# Intentionally using 'assert' here, this is not a test assertion, this
307
# is just an "oh fuck what is going ON" assertion. -glyph
309
HORK = "path cleanup failed: don't be surprised if other tests break"
310
assert sys.path.pop() is self.pathExtensionName, HORK+", 1"
311
assert self.pathExtensionName not in sys.path, HORK+", 2"
315
class RebindingTest(PathModificationTest):
317
These tests verify that the default path interrogation API works properly
318
even when sys.path has been rebound to a different object.
320
def _setupSysPath(self):
321
assert not self.pathSetUp
322
self.pathSetUp = True
323
self.savedSysPath = sys.path
324
sys.path = sys.path[:]
325
sys.path.append(self.pathExtensionName)
330
Clean up sys.path by re-binding our original object.
333
sys.path = self.savedSysPath
337
class ZipPathModificationTest(PathModificationTest):
338
def _setupSysPath(self):
339
assert not self.pathSetUp
340
zipit(self.pathExtensionName, self.pathExtensionName+'.zip')
341
self.pathExtensionName += '.zip'
342
assert zipfile.is_zipfile(self.pathExtensionName)
343
PathModificationTest._setupSysPath(self)
346
class PythonPathTestCase(TestCase):
348
Tests for the class which provides the implementation for all of the
349
public API of L{twisted.python.modules}, L{PythonPath}.
351
def test_unhandledImporter(self):
353
Make sure that the behavior when encountering an unknown importer
354
type is not catastrophic failure.
356
class SecretImporter(object):
360
return SecretImporter()
362
syspath = ['example/path']
368
space = modules.PythonPath(
369
syspath, sysmodules, syshooks, syscache, sysloader)
370
entries = list(space.iterEntries())
371
self.assertEquals(len(entries), 1)
372
self.assertRaises(KeyError, lambda: entries[0]['module'])
375
def test_inconsistentImporterCache(self):
377
If the path a module loaded with L{PythonPath.__getitem__} is not
378
present in the path importer cache, a warning is emitted, but the
379
L{PythonModule} is returned as usual.
381
space = modules.PythonPath([], sys.modules, [], {})
382
thisModule = space[__name__]
383
warnings = self.flushWarnings([self.test_inconsistentImporterCache])
384
self.assertEquals(warnings[0]['category'], UserWarning)
386
warnings[0]['message'],
387
FilePath(twisted.__file__).parent().dirname() +
388
" (for module " + __name__ + ") not in path importer cache "
389
"(PEP 302 violation - check your local configuration).")
390
self.assertEquals(len(warnings), 1)
391
self.assertEquals(thisModule.name, __name__)