~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/modules.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_modules -*-
 
2
# Copyright (c) 2006-2009 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
"""
 
6
This module aims to provide a unified, object-oriented view of Python's
 
7
runtime hierarchy.
 
8
 
 
9
Python is a very dynamic language with wide variety of introspection utilities.
 
10
However, these utilities can be hard to use, because there is no consistent
 
11
API.  The introspection API in python is made up of attributes (__name__,
 
12
__module__, func_name, etc) on instances, modules, classes and functions which
 
13
vary between those four types, utility modules such as 'inspect' which provide
 
14
some functionality, the 'imp' module, the "compiler" module, the semantics of
 
15
PEP 302 support, and setuptools, among other things.
 
16
 
 
17
At the top, you have "PythonPath", an abstract representation of sys.path which
 
18
includes methods to locate top-level modules, with or without loading them.
 
19
The top-level exposed functions in this module for accessing the system path
 
20
are "walkModules", "iterModules", and "getModule".
 
21
 
 
22
From most to least specific, here are the objects provided::
 
23
 
 
24
                  PythonPath  # sys.path
 
25
                      |
 
26
                      v
 
27
                  PathEntry   # one entry on sys.path: an importer
 
28
                      |
 
29
                      v
 
30
                 PythonModule # a module or package that can be loaded
 
31
                      |
 
32
                      v
 
33
                 PythonAttribute # an attribute of a module (function or class)
 
34
                      |
 
35
                      v
 
36
                 PythonAttribute # an attribute of a function or class
 
37
                      |
 
38
                      v
 
39
                     ...
 
40
 
 
41
Here's an example of idiomatic usage: this is what you would do to list all of
 
42
the modules outside the standard library's python-files directory::
 
43
 
 
44
    import os
 
45
    stdlibdir = os.path.dirname(os.__file__)
 
46
 
 
47
    from twisted.python.modules import iterModules
 
48
 
 
49
    for modinfo in iterModules():
 
50
        if (modinfo.pathEntry.filePath.path != stdlibdir
 
51
            and not modinfo.isPackage()):
 
52
            print 'unpackaged: %s: %s' % (
 
53
                modinfo.name, modinfo.filePath.path)
 
54
"""
 
55
 
 
56
__metaclass__ = type
 
57
 
 
58
# let's try to keep path imports to a minimum...
 
59
from os.path import dirname, split as splitpath
 
60
 
 
61
import sys
 
62
import zipimport
 
63
import inspect
 
64
import warnings
 
65
from zope.interface import Interface, implements
 
66
 
 
67
from twisted.python.components import registerAdapter
 
68
from twisted.python.filepath import FilePath, UnlistableError
 
69
from twisted.python.zippath import ZipArchive
 
70
from twisted.python.reflect import namedAny
 
71
 
 
72
_nothing = object()
 
73
 
 
74
PYTHON_EXTENSIONS = ['.py']
 
75
OPTIMIZED_MODE = __doc__ is None
 
76
if OPTIMIZED_MODE:
 
77
    PYTHON_EXTENSIONS.append('.pyo')
 
78
else:
 
79
    PYTHON_EXTENSIONS.append('.pyc')
 
80
 
 
81
def _isPythonIdentifier(string):
 
82
    """
 
83
    cheezy fake test for proper identifier-ness.
 
84
 
 
85
    @param string: a str which might or might not be a valid python identifier.
 
86
 
 
87
    @return: True or False
 
88
    """
 
89
    return (' ' not in string and
 
90
            '.' not in string and
 
91
            '-' not in string)
 
92
 
 
93
 
 
94
 
 
95
def _isPackagePath(fpath):
 
96
    # Determine if a FilePath-like object is a Python package.  TODO: deal with
 
97
    # __init__module.(so|dll|pyd)?
 
98
    extless = fpath.splitext()[0]
 
99
    basend = splitpath(extless)[1]
 
100
    return basend == "__init__"
 
101
 
 
102
 
 
103
 
 
104
class _ModuleIteratorHelper:
 
105
    """
 
106
    This mixin provides common behavior between python module and path entries,
 
107
    since the mechanism for searching sys.path and __path__ attributes is
 
108
    remarkably similar.
 
109
    """
 
110
 
 
111
    def iterModules(self):
 
112
        """
 
113
        Loop over the modules present below this entry or package on PYTHONPATH.
 
114
 
 
115
        For modules which are not packages, this will yield nothing.
 
116
 
 
117
        For packages and path entries, this will only yield modules one level
 
118
        down; i.e. if there is a package a.b.c, iterModules on a will only
 
119
        return a.b.  If you want to descend deeply, use walkModules.
 
120
 
 
121
        @return: a generator which yields PythonModule instances that describe
 
122
        modules which can be, or have been, imported.
 
123
        """
 
124
        yielded = {}
 
125
        if not self.filePath.exists():
 
126
            return
 
127
 
 
128
        for placeToLook in self._packagePaths():
 
129
            try:
 
130
                children = placeToLook.children()
 
131
            except UnlistableError:
 
132
                continue
 
133
 
 
134
            children.sort()
 
135
            for potentialTopLevel in children:
 
136
                ext = potentialTopLevel.splitext()[1]
 
137
                potentialBasename = potentialTopLevel.basename()[:-len(ext)]
 
138
                if ext in PYTHON_EXTENSIONS:
 
139
                    # TODO: this should be a little choosier about which path entry
 
140
                    # it selects first, and it should do all the .so checking and
 
141
                    # crud
 
142
                    if not _isPythonIdentifier(potentialBasename):
 
143
                        continue
 
144
                    modname = self._subModuleName(potentialBasename)
 
145
                    if modname.split(".")[-1] == '__init__':
 
146
                        # This marks the directory as a package so it can't be
 
147
                        # a module.
 
148
                        continue
 
149
                    if modname not in yielded:
 
150
                        yielded[modname] = True
 
151
                        pm = PythonModule(modname, potentialTopLevel, self._getEntry())
 
152
                        assert pm != self
 
153
                        yield pm
 
154
                else:
 
155
                    if (ext or not _isPythonIdentifier(potentialBasename)
 
156
                        or not potentialTopLevel.isdir()):
 
157
                        continue
 
158
                    modname = self._subModuleName(potentialTopLevel.basename())
 
159
                    for ext in PYTHON_EXTENSIONS:
 
160
                        initpy = potentialTopLevel.child("__init__"+ext)
 
161
                        if initpy.exists():
 
162
                            yielded[modname] = True
 
163
                            pm = PythonModule(modname, initpy, self._getEntry())
 
164
                            assert pm != self
 
165
                            yield pm
 
166
                            break
 
167
 
 
168
    def walkModules(self, importPackages=False):
 
169
        """
 
170
        Similar to L{iterModules}, this yields self, and then every module in my
 
171
        package or entry, and every submodule in each package or entry.
 
172
 
 
173
        In other words, this is deep, and L{iterModules} is shallow.
 
174
        """
 
175
        yield self
 
176
        for package in self.iterModules():
 
177
            for module in package.walkModules(importPackages=importPackages):
 
178
                yield module
 
179
 
 
180
    def _subModuleName(self, mn):
 
181
        """
 
182
        This is a hook to provide packages with the ability to specify their names
 
183
        as a prefix to submodules here.
 
184
        """
 
185
        return mn
 
186
 
 
187
    def _packagePaths(self):
 
188
        """
 
189
        Implement in subclasses to specify where to look for modules.
 
190
 
 
191
        @return: iterable of FilePath-like objects.
 
192
        """
 
193
        raise NotImplementedError()
 
194
 
 
195
    def _getEntry(self):
 
196
        """
 
197
        Implement in subclasses to specify what path entry submodules will come
 
198
        from.
 
199
 
 
200
        @return: a PathEntry instance.
 
201
        """
 
202
        raise NotImplementedError()
 
203
 
 
204
 
 
205
    def __getitem__(self, modname):
 
206
        """
 
207
        Retrieve a module from below this path or package.
 
208
 
 
209
        @param modname: a str naming a module to be loaded.  For entries, this
 
210
        is a top-level, undotted package name, and for packages it is the name
 
211
        of the module without the package prefix.  For example, if you have a
 
212
        PythonModule representing the 'twisted' package, you could use::
 
213
 
 
214
            twistedPackageObj['python']['modules']
 
215
 
 
216
        to retrieve this module.
 
217
 
 
218
        @raise: KeyError if the module is not found.
 
219
 
 
220
        @return: a PythonModule.
 
221
        """
 
222
        for module in self.iterModules():
 
223
            if module.name == self._subModuleName(modname):
 
224
                return module
 
225
        raise KeyError(modname)
 
226
 
 
227
    def __iter__(self):
 
228
        """
 
229
        Implemented to raise NotImplementedError for clarity, so that attempting to
 
230
        loop over this object won't call __getitem__.
 
231
 
 
232
        Note: in the future there might be some sensible default for iteration,
 
233
        like 'walkEverything', so this is deliberately untested and undefined
 
234
        behavior.
 
235
        """
 
236
        raise NotImplementedError()
 
237
 
 
238
class PythonAttribute:
 
239
    """
 
240
    I represent a function, class, or other object that is present.
 
241
 
 
242
    @ivar name: the fully-qualified python name of this attribute.
 
243
 
 
244
    @ivar onObject: a reference to a PythonModule or other PythonAttribute that
 
245
    is this attribute's logical parent.
 
246
 
 
247
    @ivar name: the fully qualified python name of the attribute represented by
 
248
    this class.
 
249
    """
 
250
    def __init__(self, name, onObject, loaded, pythonValue):
 
251
        """
 
252
        Create a PythonAttribute.  This is a private constructor.  Do not construct
 
253
        me directly, use PythonModule.iterAttributes.
 
254
 
 
255
        @param name: the FQPN
 
256
        @param onObject: see ivar
 
257
        @param loaded: always True, for now
 
258
        @param pythonValue: the value of the attribute we're pointing to.
 
259
        """
 
260
        self.name = name
 
261
        self.onObject = onObject
 
262
        self._loaded = loaded
 
263
        self.pythonValue = pythonValue
 
264
 
 
265
    def __repr__(self):
 
266
        return 'PythonAttribute<%r>'%(self.name,)
 
267
 
 
268
    def isLoaded(self):
 
269
        """
 
270
        Return a boolean describing whether the attribute this describes has
 
271
        actually been loaded into memory by importing its module.
 
272
 
 
273
        Note: this currently always returns true; there is no Python parser
 
274
        support in this module yet.
 
275
        """
 
276
        return self._loaded
 
277
 
 
278
    def load(self, default=_nothing):
 
279
        """
 
280
        Load the value associated with this attribute.
 
281
 
 
282
        @return: an arbitrary Python object, or 'default' if there is an error
 
283
        loading it.
 
284
        """
 
285
        return self.pythonValue
 
286
 
 
287
    def iterAttributes(self):
 
288
        for name, val in inspect.getmembers(self.load()):
 
289
            yield PythonAttribute(self.name+'.'+name, self, True, val)
 
290
 
 
291
class PythonModule(_ModuleIteratorHelper):
 
292
    """
 
293
    Representation of a module which could be imported from sys.path.
 
294
 
 
295
    @ivar name: the fully qualified python name of this module.
 
296
 
 
297
    @ivar filePath: a FilePath-like object which points to the location of this
 
298
    module.
 
299
 
 
300
    @ivar pathEntry: a L{PathEntry} instance which this module was located
 
301
    from.
 
302
    """
 
303
 
 
304
    def __init__(self, name, filePath, pathEntry):
 
305
        """
 
306
        Create a PythonModule.  Do not construct this directly, instead inspect a
 
307
        PythonPath or other PythonModule instances.
 
308
 
 
309
        @param name: see ivar
 
310
        @param filePath: see ivar
 
311
        @param pathEntry: see ivar
 
312
        """
 
313
        assert not name.endswith(".__init__")
 
314
        self.name = name
 
315
        self.filePath = filePath
 
316
        self.parentPath = filePath.parent()
 
317
        self.pathEntry = pathEntry
 
318
 
 
319
    def _getEntry(self):
 
320
        return self.pathEntry
 
321
 
 
322
    def __repr__(self):
 
323
        """
 
324
        Return a string representation including the module name.
 
325
        """
 
326
        return 'PythonModule<%r>' % (self.name,)
 
327
 
 
328
    def isLoaded(self):
 
329
        """
 
330
        Determine if the module is loaded into sys.modules.
 
331
 
 
332
        @return: a boolean: true if loaded, false if not.
 
333
        """
 
334
        return self.name in self.pathEntry.pythonPath.moduleDict
 
335
 
 
336
    def iterAttributes(self):
 
337
        """
 
338
        List all the attributes defined in this module.
 
339
 
 
340
        Note: Future work is planned here to make it possible to list python
 
341
        attributes on a module without loading the module by inspecting ASTs or
 
342
        bytecode, but currently any iteration of PythonModule objects insists
 
343
        they must be loaded, and will use inspect.getmodule.
 
344
 
 
345
        @raise NotImplementedError: if this module is not loaded.
 
346
 
 
347
        @return: a generator yielding PythonAttribute instances describing the
 
348
        attributes of this module.
 
349
        """
 
350
        if not self.isLoaded():
 
351
            raise NotImplementedError(
 
352
                "You can't load attributes from non-loaded modules yet.")
 
353
        for name, val in inspect.getmembers(self.load()):
 
354
            yield PythonAttribute(self.name+'.'+name, self, True, val)
 
355
 
 
356
    def isPackage(self):
 
357
        """
 
358
        Returns true if this module is also a package, and might yield something
 
359
        from iterModules.
 
360
        """
 
361
        return _isPackagePath(self.filePath)
 
362
 
 
363
    def load(self, default=_nothing):
 
364
        """
 
365
        Load this module.
 
366
 
 
367
        @param default: if specified, the value to return in case of an error.
 
368
 
 
369
        @return: a genuine python module.
 
370
 
 
371
        @raise: any type of exception.  Importing modules is a risky business;
 
372
        the erorrs of any code run at module scope may be raised from here, as
 
373
        well as ImportError if something bizarre happened to the system path
 
374
        between the discovery of this PythonModule object and the attempt to
 
375
        import it.  If you specify a default, the error will be swallowed
 
376
        entirely, and not logged.
 
377
 
 
378
        @rtype: types.ModuleType.
 
379
        """
 
380
        try:
 
381
            return self.pathEntry.pythonPath.moduleLoader(self.name)
 
382
        except:                 # this needs more thought...
 
383
            if default is not _nothing:
 
384
                return default
 
385
            raise
 
386
 
 
387
    def __eq__(self, other):
 
388
        """
 
389
        PythonModules with the same name are equal.
 
390
        """
 
391
        if not isinstance(other, PythonModule):
 
392
            return False
 
393
        return other.name == self.name
 
394
 
 
395
    def __ne__(self, other):
 
396
        """
 
397
        PythonModules with different names are not equal.
 
398
        """
 
399
        if not isinstance(other, PythonModule):
 
400
            return True
 
401
        return other.name != self.name
 
402
 
 
403
    def walkModules(self, importPackages=False):
 
404
        if importPackages and self.isPackage():
 
405
            self.load()
 
406
        return super(PythonModule, self).walkModules(importPackages=importPackages)
 
407
 
 
408
    def _subModuleName(self, mn):
 
409
        """
 
410
        submodules of this module are prefixed with our name.
 
411
        """
 
412
        return self.name + '.' + mn
 
413
 
 
414
    def _packagePaths(self):
 
415
        """
 
416
        Yield a sequence of FilePath-like objects which represent path segments.
 
417
        """
 
418
        if not self.isPackage():
 
419
            return
 
420
        if self.isLoaded():
 
421
            load = self.load()
 
422
            if hasattr(load, '__path__'):
 
423
                for fn in load.__path__:
 
424
                    if fn == self.parentPath.path:
 
425
                        # this should _really_ exist.
 
426
                        assert self.parentPath.exists()
 
427
                        yield self.parentPath
 
428
                    else:
 
429
                        smp = self.pathEntry.pythonPath._smartPath(fn)
 
430
                        if smp.exists():
 
431
                            yield smp
 
432
        else:
 
433
            yield self.parentPath
 
434
 
 
435
 
 
436
class PathEntry(_ModuleIteratorHelper):
 
437
    """
 
438
    I am a proxy for a single entry on sys.path.
 
439
 
 
440
    @ivar filePath: a FilePath-like object pointing at the filesystem location
 
441
    or archive file where this path entry is stored.
 
442
 
 
443
    @ivar pythonPath: a PythonPath instance.
 
444
    """
 
445
    def __init__(self, filePath, pythonPath):
 
446
        """
 
447
        Create a PathEntry.  This is a private constructor.
 
448
        """
 
449
        self.filePath = filePath
 
450
        self.pythonPath = pythonPath
 
451
 
 
452
    def _getEntry(self):
 
453
        return self
 
454
 
 
455
    def __repr__(self):
 
456
        return 'PathEntry<%r>' % (self.filePath,)
 
457
 
 
458
    def _packagePaths(self):
 
459
        yield self.filePath
 
460
 
 
461
class IPathImportMapper(Interface):
 
462
    """
 
463
    This is an internal interface, used to map importers to factories for
 
464
    FilePath-like objects.
 
465
    """
 
466
    def mapPath(self, pathLikeString):
 
467
        """
 
468
        Return a FilePath-like object.
 
469
 
 
470
        @param pathLikeString: a path-like string, like one that might be
 
471
        passed to an import hook.
 
472
 
 
473
        @return: a L{FilePath}, or something like it (currently only a
 
474
        L{ZipPath}, but more might be added later).
 
475
        """
 
476
 
 
477
class _DefaultMapImpl:
 
478
    """ Wrapper for the default importer, i.e. None.  """
 
479
    implements(IPathImportMapper)
 
480
    def mapPath(self, fsPathString):
 
481
        return FilePath(fsPathString)
 
482
_theDefaultMapper = _DefaultMapImpl()
 
483
 
 
484
class _ZipMapImpl:
 
485
    """ IPathImportMapper implementation for zipimport.ZipImporter.  """
 
486
    implements(IPathImportMapper)
 
487
    def __init__(self, importer):
 
488
        self.importer = importer
 
489
 
 
490
    def mapPath(self, fsPathString):
 
491
        """
 
492
        Map the given FS path to a ZipPath, by looking at the ZipImporter's
 
493
        "archive" attribute and using it as our ZipArchive root, then walking
 
494
        down into the archive from there.
 
495
 
 
496
        @return: a L{zippath.ZipPath} or L{zippath.ZipArchive} instance.
 
497
        """
 
498
        za = ZipArchive(self.importer.archive)
 
499
        myPath = FilePath(self.importer.archive)
 
500
        itsPath = FilePath(fsPathString)
 
501
        if myPath == itsPath:
 
502
            return za
 
503
        # This is NOT a general-purpose rule for sys.path or __file__:
 
504
        # zipimport specifically uses regular OS path syntax in its pathnames,
 
505
        # even though zip files specify that slashes are always the separator,
 
506
        # regardless of platform.
 
507
        segs = itsPath.segmentsFrom(myPath)
 
508
        zp = za
 
509
        for seg in segs:
 
510
            zp = zp.child(seg)
 
511
        return zp
 
512
 
 
513
registerAdapter(_ZipMapImpl, zipimport.zipimporter, IPathImportMapper)
 
514
 
 
515
def _defaultSysPathFactory():
 
516
    """
 
517
    Provide the default behavior of PythonPath's sys.path factory, which is to
 
518
    return the current value of sys.path.
 
519
 
 
520
    @return: L{sys.path}
 
521
    """
 
522
    return sys.path
 
523
 
 
524
 
 
525
class PythonPath:
 
526
    """
 
527
    I represent the very top of the Python object-space, the module list in
 
528
    sys.path and the modules list in sys.modules.
 
529
 
 
530
    @ivar _sysPath: a sequence of strings like sys.path.  This attribute is
 
531
    read-only.
 
532
 
 
533
    @ivar moduleDict: a dictionary mapping string module names to module
 
534
    objects, like sys.modules.
 
535
 
 
536
    @ivar sysPathHooks: a list of PEP-302 path hooks, like sys.path_hooks.
 
537
 
 
538
    @ivar moduleLoader: a function that takes a fully-qualified python name and
 
539
    returns a module, like twisted.python.reflect.namedAny.
 
540
    """
 
541
 
 
542
    def __init__(self,
 
543
                 sysPath=None,
 
544
                 moduleDict=sys.modules,
 
545
                 sysPathHooks=sys.path_hooks,
 
546
                 importerCache=sys.path_importer_cache,
 
547
                 moduleLoader=namedAny,
 
548
                 sysPathFactory=None):
 
549
        """
 
550
        Create a PythonPath.  You almost certainly want to use
 
551
        modules.theSystemPath, or its aliased methods, rather than creating a
 
552
        new instance yourself, though.
 
553
 
 
554
        All parameters are optional, and if unspecified, will use 'system'
 
555
        equivalents that makes this PythonPath like the global L{theSystemPath}
 
556
        instance.
 
557
 
 
558
        @param sysPath: a sys.path-like list to use for this PythonPath, to
 
559
        specify where to load modules from.
 
560
 
 
561
        @param moduleDict: a sys.modules-like dictionary to use for keeping
 
562
        track of what modules this PythonPath has loaded.
 
563
 
 
564
        @param sysPathHooks: sys.path_hooks-like list of PEP-302 path hooks to
 
565
        be used for this PythonPath, to determie which importers should be
 
566
        used.
 
567
 
 
568
        @param importerCache: a sys.path_importer_cache-like list of PEP-302
 
569
        importers.  This will be used in conjunction with the given
 
570
        sysPathHooks.
 
571
 
 
572
        @param moduleLoader: a module loader function which takes a string and
 
573
        returns a module.  That is to say, it is like L{namedAny} - *not* like
 
574
        L{__import__}.
 
575
 
 
576
        @param sysPathFactory: a 0-argument callable which returns the current
 
577
        value of a sys.path-like list of strings.  Specify either this, or
 
578
        sysPath, not both.  This alternative interface is provided because the
 
579
        way the Python import mechanism works, you can re-bind the 'sys.path'
 
580
        name and that is what is used for current imports, so it must be a
 
581
        factory rather than a value to deal with modification by rebinding
 
582
        rather than modification by mutation.  Note: it is not recommended to
 
583
        rebind sys.path.  Although this mechanism can deal with that, it is a
 
584
        subtle point which some tools that it is easy for tools which interact
 
585
        with sys.path to miss.
 
586
        """
 
587
        if sysPath is not None:
 
588
            sysPathFactory = lambda : sysPath
 
589
        elif sysPathFactory is None:
 
590
            sysPathFactory = _defaultSysPathFactory
 
591
        self._sysPathFactory = sysPathFactory
 
592
        self._sysPath = sysPath
 
593
        self.moduleDict = moduleDict
 
594
        self.sysPathHooks = sysPathHooks
 
595
        self.importerCache = importerCache
 
596
        self.moduleLoader = moduleLoader
 
597
 
 
598
 
 
599
    def _getSysPath(self):
 
600
        """
 
601
        Retrieve the current value of the module search path list.
 
602
        """
 
603
        return self._sysPathFactory()
 
604
 
 
605
    sysPath = property(_getSysPath)
 
606
 
 
607
    def _findEntryPathString(self, modobj):
 
608
        """
 
609
        Determine where a given Python module object came from by looking at path
 
610
        entries.
 
611
        """
 
612
        topPackageObj = modobj
 
613
        while '.' in topPackageObj.__name__:
 
614
            topPackageObj = self.moduleDict['.'.join(
 
615
                    topPackageObj.__name__.split('.')[:-1])]
 
616
        if _isPackagePath(FilePath(topPackageObj.__file__)):
 
617
            # if package 'foo' is on sys.path at /a/b/foo, package 'foo's
 
618
            # __file__ will be /a/b/foo/__init__.py, and we are looking for
 
619
            # /a/b here, the path-entry; so go up two steps.
 
620
            rval = dirname(dirname(topPackageObj.__file__))
 
621
        else:
 
622
            # the module is completely top-level, not within any packages.  The
 
623
            # path entry it's on is just its dirname.
 
624
            rval = dirname(topPackageObj.__file__)
 
625
 
 
626
        # There are probably some awful tricks that an importer could pull
 
627
        # which would break this, so let's just make sure... it's a loaded
 
628
        # module after all, which means that its path MUST be in
 
629
        # path_importer_cache according to PEP 302 -glyph
 
630
        if rval not in self.importerCache:
 
631
            warnings.warn(
 
632
                "%s (for module %s) not in path importer cache "
 
633
                "(PEP 302 violation - check your local configuration)." % (
 
634
                    rval, modobj.__name__),
 
635
                stacklevel=3)
 
636
 
 
637
        return rval
 
638
 
 
639
    def _smartPath(self, pathName):
 
640
        """
 
641
        Given a path entry from sys.path which may refer to an importer,
 
642
        return the appropriate FilePath-like instance.
 
643
 
 
644
        @param pathName: a str describing the path.
 
645
 
 
646
        @return: a FilePath-like object.
 
647
        """
 
648
        importr = self.importerCache.get(pathName, _nothing)
 
649
        if importr is _nothing:
 
650
            for hook in self.sysPathHooks:
 
651
                try:
 
652
                    importr = hook(pathName)
 
653
                except ImportError, ie:
 
654
                    pass
 
655
            if importr is _nothing: # still
 
656
                importr = None
 
657
        return IPathImportMapper(importr, _theDefaultMapper).mapPath(pathName)
 
658
 
 
659
    def iterEntries(self):
 
660
        """
 
661
        Iterate the entries on my sysPath.
 
662
 
 
663
        @return: a generator yielding PathEntry objects
 
664
        """
 
665
        for pathName in self.sysPath:
 
666
            fp = self._smartPath(pathName)
 
667
            yield PathEntry(fp, self)
 
668
 
 
669
    def __getitem__(self, modname):
 
670
        """
 
671
        Get a python module by a given fully-qualified name.
 
672
 
 
673
        @return: a PythonModule object.
 
674
 
 
675
        @raise: KeyError, if the module name is a module name.
 
676
        """
 
677
        # See if the module is already somewhere in Python-land.
 
678
        if modname in self.moduleDict:
 
679
            # we need 2 paths; one of the path entry and one for the module.
 
680
            moduleObject = self.moduleDict[modname]
 
681
            pe = PathEntry(
 
682
                self._smartPath(
 
683
                    self._findEntryPathString(moduleObject)),
 
684
                self)
 
685
            mp = self._smartPath(moduleObject.__file__)
 
686
            return PythonModule(modname, mp, pe)
 
687
 
 
688
        # Recurse if we're trying to get a submodule.
 
689
        if '.' in modname:
 
690
            pkg = self
 
691
            for name in modname.split('.'):
 
692
                pkg = pkg[name]
 
693
            return pkg
 
694
 
 
695
        # Finally do the slowest possible thing and iterate
 
696
        for module in self.iterModules():
 
697
            if module.name == modname:
 
698
                return module
 
699
        raise KeyError(modname)
 
700
 
 
701
    def __repr__(self):
 
702
        """
 
703
        Display my sysPath and moduleDict in a string representation.
 
704
        """
 
705
        return "PythonPath(%r,%r)" % (self.sysPath, self.moduleDict)
 
706
 
 
707
    def iterModules(self):
 
708
        """
 
709
        Yield all top-level modules on my sysPath.
 
710
        """
 
711
        for entry in self.iterEntries():
 
712
            for module in entry.iterModules():
 
713
                yield module
 
714
 
 
715
    def walkModules(self, importPackages=False):
 
716
        """
 
717
        Similar to L{iterModules}, this yields every module on the path, then every
 
718
        submodule in each package or entry.
 
719
        """
 
720
        for package in self.iterModules():
 
721
            for module in package.walkModules(importPackages=False):
 
722
                yield module
 
723
 
 
724
theSystemPath = PythonPath()
 
725
 
 
726
def walkModules(importPackages=False):
 
727
    """
 
728
    Deeply iterate all modules on the global python path.
 
729
 
 
730
    @param importPackages: Import packages as they are seen.
 
731
    """
 
732
    return theSystemPath.walkModules(importPackages=importPackages)
 
733
 
 
734
def iterModules():
 
735
    """
 
736
    Iterate all modules and top-level packages on the global Python path, but
 
737
    do not descend into packages.
 
738
 
 
739
    @param importPackages: Import packages as they are seen.
 
740
    """
 
741
    return theSystemPath.iterModules()
 
742
 
 
743
def getModule(moduleName):
 
744
    """
 
745
    Retrieve a module from the system path.
 
746
    """
 
747
    return theSystemPath[moduleName]