ni.py 14.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
"""New import scheme with package support.

Quick Reference
---------------

- To enable package support, execute "import ni" before importing any
  packages.  Importing this module automatically installs the relevant
  import hooks.

- To create a package named spam containing sub-modules ham, bacon and
  eggs, create a directory spam somewhere on Python's module search
  path (i.e. spam's parent directory must be one of the directories in
  sys.path or $PYTHONPATH); then create files ham.py, bacon.py and
  eggs.py inside spam.

- To import module ham from package spam and use function hamneggs()
  from that module, you can either do

19
    import spam.ham             # *not* "import spam" !!!
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
    spam.ham.hamneggs()

  or

    from spam import ham
    ham.hamneggs()

  or

    from spam.ham import hamneggs
    hamneggs()

- Importing just "spam" does not do what you expect: it creates an
  empty package named spam if one does not already exist, but it does
  not import spam's submodules.  The only submodule that is guaranteed
  to be imported is spam.__init__, if it exists.  Note that
  spam.__init__ is a submodule of package spam.  It can reference to
  spam's namespace via the '__.' prefix, for instance

39
    __.spam_inited = 1          # Set a package-level variable
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66



Theory of Operation
-------------------

A Package is a module that can contain other modules.  Packages can be
nested.  Package introduce dotted names for modules, like P.Q.M, which
could correspond to a file P/Q/M.py found somewhere on sys.path.  It
is possible to import a package itself, though this makes little sense
unless the package contains a module called __init__.

A package has two variables that control the namespace used for
packages and modules, both initialized to sensible defaults the first
time the package is referenced.

(1) A package's *module search path*, contained in the per-package
variable __path__, defines a list of *directories* where submodules or
subpackages of the package are searched.  It is initialized to the
directory containing the package.  Setting this variable to None makes
the module search path default to sys.path (this is not quite the same
as setting it to sys.path, since the latter won't track later
assignments to sys.path).

(2) A package's *import domain*, contained in the per-package variable
__domain__, defines a list of *packages* that are searched (using
their respective module search paths) to satisfy imports.  It is
67
initialized to the list consisting of the package itself, its parent
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
package, its parent's parent, and so on, ending with the root package
(the nameless package containing all top-level packages and modules,
whose module search path is None, implying sys.path).

The default domain implements a search algorithm called "expanding
search".  An alternative search algorithm called "explicit search"
fixes the import search path to contain only the root package,
requiring the modules in the package to name all imported modules by
their full name.  The convention of using '__' to refer to the current
package (both as a per-module variable and in module names) can be
used by packages using explicit search to refer to modules in the same
package; this combination is known as "explicit-relative search".

The PackageImporter and PackageLoader classes together implement the
following policies:

- There is a root package, whose name is ''.  It cannot be imported
  directly but may be referenced, e.g. by using '__' from a top-level
  module.

- In each module or package, the variable '__' contains a reference to
  the parent package; in the root package, '__' points to itself.

- In the name for imported modules (e.g. M in "import M" or "from M
  import ..."), a leading '__' refers to the current package (i.e.
  the package containing the current module); leading '__.__' and so
  on refer to the current package's parent, and so on.  The use of
  '__' elsewhere in the module name is not supported.

- Modules are searched using the "expanding search" algorithm by
  virtue of the default value for __domain__.

- If A.B.C is imported, A is searched using __domain__; then
  subpackage B is searched in A using its __path__, and so on.

- Built-in modules have priority: even if a file sys.py exists in a
  package, "import sys" imports the built-in sys module.

- The same holds for frozen modules, for better or for worse.

- Submodules and subpackages are not automatically loaded when their
  parent packages is loaded.

- The construct "from package import *" is illegal.  (It can still be
  used to import names from a module.)

- When "from package import module1, module2, ..." is used, those
    modules are explicitly loaded.

- When a package is loaded, if it has a submodule __init__, that
  module is loaded.  This is the place where required submodules can
  be loaded, the __path__ variable extended, etc.  The __init__ module
  is loaded even if the package was loaded only in order to create a
  stub for a sub-package: if "import P.Q.R" is the first reference to
  P, and P has a submodule __init__, P.__init__ is loaded before P.Q
  is even searched.

Caveats:

- It is possible to import a package that has no __init__ submodule;
  this is not particularly useful but there may be useful applications
  for it (e.g. to manipulate its search paths from the outside!).

- There are no special provisions for os.chdir().  If you plan to use
  os.chdir() before you have imported all your modules, it is better
  not to have relative pathnames in sys.path.  (This could actually be
  fixed by changing the implementation of path_join() in the hook to
  absolutize paths.)

- Packages and modules are introduced in sys.modules as soon as their
  loading is started.  When the loading is terminated by an exception,
  the sys.modules entries remain around.

- There are no special measures to support mutually recursive modules,
  but it will work under the same conditions where it works in the
  flat module space system.

- Sometimes dummy entries (whose value is None) are entered in
  sys.modules, to indicate that a particular module does not exist --
  this is done to speed up the expanding search algorithm when a
  module residing at a higher level is repeatedly imported (Python
  promises that importing a previously imported module is cheap!)

- Although dynamically loaded extensions are allowed inside packages,
  the current implementation (hardcoded in the interpreter) of their
  initialization may cause problems if an extension invokes the
  interpreter during its initialization.

- reload() may find another version of the module only if it occurs on
  the package search path.  Thus, it keeps the connection to the
  package to which the module belongs, but may find a different file.

XXX Need to have an explicit name for '', e.g. '__root__'.

"""


import imp
import sys
import __builtin__

import ihooks
from ihooks import ModuleLoader, ModuleImporter


class PackageLoader(ModuleLoader):

    """A subclass of ModuleLoader with package support.

    find_module_in_dir() will succeed if there's a subdirectory with
    the given name; load_module() will create a stub for a package and
    load its __init__ module if it exists.

    """

    def find_module_in_dir(self, name, dir):
184 185 186 187 188
        if dir is not None:
            dirname = self.hooks.path_join(dir, name)
            if self.hooks.path_isdir(dirname):
                return None, dirname, ('', '', 'PACKAGE')
        return ModuleLoader.find_module_in_dir(self, name, dir)
189 190

    def load_module(self, name, stuff):
191 192 193 194 195 196 197 198 199 200 201 202 203
        file, filename, info = stuff
        suff, mode, type = info
        if type == 'PACKAGE':
            return self.load_package(name, stuff)
        if sys.modules.has_key(name):
            m = sys.modules[name]
        else:
            sys.modules[name] = m = imp.new_module(name)
        self.set_parent(m)
        if type == imp.C_EXTENSION and '.' in name:
            return self.load_dynamic(name, stuff)
        else:
            return ModuleLoader.load_module(self, name, stuff)
204 205

    def load_dynamic(self, name, stuff):
206 207
        file, filename, (suff, mode, type) = stuff
        # Hack around restriction in imp.load_dynamic()
208
        i = name.rfind('.')
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
        tail = name[i+1:]
        if sys.modules.has_key(tail):
            save = sys.modules[tail]
        else:
            save = None
        sys.modules[tail] = imp.new_module(name)
        try:
            m = imp.load_dynamic(tail, filename, file)
        finally:
            if save:
                sys.modules[tail] = save
            else:
                del sys.modules[tail]
        sys.modules[name] = m
        return m
224 225

    def load_package(self, name, stuff):
226 227 228 229 230 231 232 233
        file, filename, info = stuff
        if sys.modules.has_key(name):
            package = sys.modules[name]
        else:
            sys.modules[name] = package = imp.new_module(name)
        package.__path__ = [filename]
        self.init_package(package)
        return package
234 235

    def init_package(self, package):
236 237 238
        self.set_parent(package)
        self.set_domain(package)
        self.call_init_module(package)
239 240

    def set_parent(self, m):
241 242
        name = m.__name__
        if '.' in name:
243
            name = name[:name.rfind('.')]
244 245 246
        else:
            name = ''
        m.__ = sys.modules[name]
247 248

    def set_domain(self, package):
249 250 251
        name = package.__name__
        package.__domain__ = domain = [name]
        while '.' in name:
252
            name = name[:name.rfind('.')]
253 254 255
            domain.append(name)
        if name:
            domain.append('')
256 257

    def call_init_module(self, package):
258 259 260 261
        stuff = self.find_module('__init__', package.__path__)
        if stuff:
            m = self.load_module(package.__name__ + '.__init__', stuff)
            package.__init__ = m
262 263 264 265 266 267 268


class PackageImporter(ModuleImporter):

    """Importer that understands packages and '__'."""

    def __init__(self, loader = None, verbose = 0):
269 270
        ModuleImporter.__init__(self,
        loader or PackageLoader(None, verbose), verbose)
271 272

    def import_module(self, name, globals={}, locals={}, fromlist=[]):
273 274 275 276 277 278 279 280 281 282 283 284 285 286
        if globals.has_key('__'):
            package = globals['__']
        else:
            # No calling context, assume in root package
            package = sys.modules['']
        if name[:3] in ('__.', '__'):
            p = package
            name = name[3:]
            while name[:3] in ('__.', '__'):
                p = p.__
                name = name[3:]
            if not name:
                return self.finish(package, p, '', fromlist)
            if '.' in name:
287
                i = name.find('.')
288 289 290 291 292 293 294
                name, tail = name[:i], name[i:]
            else:
                tail = ''
            mname = p.__name__ and p.__name__+'.'+name or name
            m = self.get1(mname)
            return self.finish(package, m, tail, fromlist)
        if '.' in name:
295
            i = name.find('.')
296 297 298 299 300 301 302 303 304 305
            name, tail = name[:i], name[i:]
        else:
            tail = ''
        for pname in package.__domain__:
            mname = pname and pname+'.'+name or name
            m = self.get0(mname)
            if m: break
        else:
            raise ImportError, "No such module %s" % name
        return self.finish(m, m, tail, fromlist)
306 307

    def finish(self, module, m, tail, fromlist):
308 309 310 311 312 313
        # Got ....A; now get ....A.B.C.D
        yname = m.__name__
        if tail and sys.modules.has_key(yname + tail): # Fast path
            yname, tail = yname + tail, ''
            m = self.get1(yname)
        while tail:
314
            i = tail.find('.', 1)
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
            if i > 0:
                head, tail = tail[:i], tail[i:]
            else:
                head, tail = tail, ''
            yname = yname + head
            m = self.get1(yname)

        # Got ....A.B.C.D; now finalize things depending on fromlist
        if not fromlist:
            return module
        if '__' in fromlist:
            raise ImportError, "Can't import __ from anywhere"
        if not hasattr(m, '__path__'): return m
        if '*' in fromlist:
            raise ImportError, "Can't import * from a package"
        for f in fromlist:
            if hasattr(m, f): continue
            fname = yname + '.' + f
            self.get1(fname)
        return m
335 336

    def get1(self, name):
337 338 339 340
        m = self.get(name)
        if not m:
            raise ImportError, "No module named %s" % name
        return m
341 342

    def get0(self, name):
343 344 345 346
        m = self.get(name)
        if not m:
            sys.modules[name] = None
        return m
347 348

    def get(self, name):
349 350 351 352
        # Internal routine to get or load a module when its parent exists
        if sys.modules.has_key(name):
            return sys.modules[name]
        if '.' in name:
353
            i = name.rfind('.')
354 355 356 357 358 359 360 361 362 363 364
            head, tail = name[:i], name[i+1:]
        else:
            head, tail = '', name
        path = sys.modules[head].__path__
        stuff = self.loader.find_module(tail, path)
        if not stuff:
            return None
        sys.modules[name] = m = self.loader.load_module(name, stuff)
        if head:
            setattr(sys.modules[head], tail, m)
        return m
365 366

    def reload(self, module):
367 368
        name = module.__name__
        if '.' in name:
369
            i = name.rfind('.')
370 371 372 373 374 375 376 377 378
            head, tail = name[:i], name[i+1:]
            path = sys.modules[head].__path__
        else:
            tail = name
            path = sys.modules[''].__path__
        stuff = self.loader.find_module(tail, path)
        if not stuff:
            raise ImportError, "No module named %s" % name
        return self.loader.load_module(name, stuff)
379 380

    def unload(self, module):
381 382 383
        if hasattr(module, '__path__'):
            raise ImportError, "don't know how to unload packages yet"
        PackageImporter.unload(self, module)
384 385

    def install(self):
386 387 388 389 390 391 392 393 394
        if not sys.modules.has_key(''):
            sys.modules[''] = package = imp.new_module('')
            package.__path__ = None
            self.loader.init_package(package)
            for m in sys.modules.values():
                if not m: continue
                if not hasattr(m, '__'):
                    self.loader.set_parent(m)
        ModuleImporter.install(self)
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411


def install(v = 0):
    ihooks.install(PackageImporter(None, v))

def uninstall():
    ihooks.uninstall()

def ni(v = 0):
    install(v)

def no():
    uninstall()

def test():
    import pdb
    try:
412
        testproper()
413
    except:
414 415 416 417 418
        sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
        print
        print sys.last_type, ':', sys.last_value
        print
        pdb.pm()
419 420 421 422

def testproper():
    install(1)
    try:
423 424 425
        import mactest
        print dir(mactest)
        raw_input('OK?')
426
    finally:
427
        uninstall()
428 429 430 431 432 433


if __name__ == '__main__':
    test()
else:
    install()