2007-01-24 22:24:01 +08:00
|
|
|
"""
|
|
|
|
package initialization.
|
|
|
|
|
|
|
|
You use the functionality of this package by putting
|
|
|
|
|
|
|
|
from py.initpkg import initpkg
|
|
|
|
initpkg(__name__, exportdefs={
|
|
|
|
'name1.name2' : ('./path/to/file.py', 'name')
|
|
|
|
...
|
|
|
|
})
|
|
|
|
|
|
|
|
into your package's __init__.py file. This will
|
|
|
|
lead your package to only expose the names of all
|
|
|
|
your implementation files that you explicitely
|
|
|
|
specify. In the above example 'name1' will
|
|
|
|
become a Module instance where 'name2' is
|
|
|
|
bound in its namespace to the 'name' object
|
|
|
|
in the relative './path/to/file.py' python module.
|
|
|
|
Note that you can also use a '.c' file in which
|
|
|
|
case it will be compiled via distutils-facilities
|
|
|
|
on the fly.
|
|
|
|
|
|
|
|
"""
|
|
|
|
from __future__ import generators
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
assert sys.version_info >= (2,2,0), "py lib requires python 2.2 or higher"
|
|
|
|
from types import ModuleType
|
|
|
|
|
|
|
|
# ---------------------------------------------------
|
|
|
|
# Package Object
|
|
|
|
# ---------------------------------------------------
|
|
|
|
|
|
|
|
class Package(object):
|
2008-08-18 23:08:39 +08:00
|
|
|
def __init__(self, name, exportdefs, metainfo):
|
2007-01-24 22:24:01 +08:00
|
|
|
pkgmodule = sys.modules[name]
|
|
|
|
assert pkgmodule.__name__ == name
|
|
|
|
self.name = name
|
|
|
|
self.exportdefs = exportdefs
|
|
|
|
self.module = pkgmodule
|
2008-02-08 16:43:05 +08:00
|
|
|
assert not hasattr(pkgmodule, '__pkg__'), \
|
2007-01-24 22:24:01 +08:00
|
|
|
"unsupported reinitialization of %r" % pkgmodule
|
2008-02-08 16:43:05 +08:00
|
|
|
pkgmodule.__pkg__ = self
|
2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
# make available pkgname.__
|
|
|
|
implname = name + '.' + '__'
|
|
|
|
self.implmodule = ModuleType(implname)
|
|
|
|
self.implmodule.__name__ = implname
|
|
|
|
self.implmodule.__file__ = os.path.abspath(pkgmodule.__file__)
|
|
|
|
self.implmodule.__path__ = [os.path.abspath(p)
|
|
|
|
for p in pkgmodule.__path__]
|
|
|
|
pkgmodule.__ = self.implmodule
|
|
|
|
setmodule(implname, self.implmodule)
|
|
|
|
# inhibit further direct filesystem imports through the package module
|
|
|
|
del pkgmodule.__path__
|
|
|
|
|
2008-08-18 23:08:39 +08:00
|
|
|
# set metainfo
|
|
|
|
for name, value in metainfo.items():
|
|
|
|
setattr(self, name, value)
|
|
|
|
version = metainfo.get('version', None)
|
|
|
|
if version:
|
|
|
|
pkgmodule.__version__ = version
|
|
|
|
|
2007-01-24 22:24:01 +08:00
|
|
|
def _resolve(self, extpyish):
|
|
|
|
""" resolve a combined filesystem/python extpy-ish path. """
|
|
|
|
fspath, modpath = extpyish
|
|
|
|
assert fspath.startswith('./'), \
|
|
|
|
"%r is not an implementation path (XXX)" % (extpyish,)
|
|
|
|
implmodule = self._loadimpl(fspath[:-3])
|
|
|
|
if not isinstance(modpath, str): # export the entire module
|
|
|
|
return implmodule
|
|
|
|
|
|
|
|
current = implmodule
|
|
|
|
for x in modpath.split('.'):
|
|
|
|
try:
|
|
|
|
current = getattr(current, x)
|
|
|
|
except AttributeError:
|
|
|
|
raise AttributeError("resolving %r failed: %s" %(
|
|
|
|
extpyish, x))
|
|
|
|
|
|
|
|
return current
|
|
|
|
|
|
|
|
def getimportname(self, path):
|
|
|
|
if not path.ext.startswith('.py'):
|
|
|
|
return None
|
|
|
|
import py
|
|
|
|
base = py.path.local(self.implmodule.__file__).dirpath()
|
|
|
|
if not path.relto(base):
|
|
|
|
return None
|
|
|
|
names = path.new(ext='').relto(base).split(path.sep)
|
|
|
|
dottedname = ".".join([self.implmodule.__name__] + names)
|
|
|
|
return dottedname
|
|
|
|
|
|
|
|
def _loadimpl(self, relfile):
|
|
|
|
""" load implementation for the given relfile. """
|
|
|
|
parts = [x.strip() for x in relfile.split('/') if x and x!= '.']
|
|
|
|
modpath = ".".join([self.implmodule.__name__] + parts)
|
|
|
|
#print "trying import", modpath
|
|
|
|
return __import__(modpath, None, None, ['__doc__'])
|
|
|
|
|
|
|
|
def exportitems(self):
|
|
|
|
return self.exportdefs.items()
|
|
|
|
|
|
|
|
def getpath(self):
|
|
|
|
from py.path import local
|
|
|
|
base = local(self.implmodule.__file__).dirpath()
|
|
|
|
assert base.check()
|
|
|
|
return base
|
|
|
|
|
|
|
|
def setmodule(modpath, module):
|
|
|
|
#print "sys.modules[%r] = %r" % (modpath, module)
|
|
|
|
sys.modules[modpath] = module
|
|
|
|
|
|
|
|
# ---------------------------------------------------
|
2009-02-27 18:18:27 +08:00
|
|
|
# API Module Object
|
2007-01-24 22:24:01 +08:00
|
|
|
# ---------------------------------------------------
|
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
class ApiModule(ModuleType):
|
2007-01-24 22:24:01 +08:00
|
|
|
def __init__(self, pkg, name):
|
2009-03-18 07:34:59 +08:00
|
|
|
self.__map__ = {}
|
2008-02-08 16:43:05 +08:00
|
|
|
self.__pkg__ = pkg
|
2007-01-24 22:24:01 +08:00
|
|
|
self.__name__ = name
|
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def __repr__(self):
|
|
|
|
return '<ApiModule %r>' % (self.__name__,)
|
|
|
|
|
2007-01-24 22:24:01 +08:00
|
|
|
def __getattr__(self, name):
|
|
|
|
if '*' in self.__map__:
|
|
|
|
extpy = self.__map__['*'][0], name
|
2008-02-08 16:43:05 +08:00
|
|
|
result = self.__pkg__._resolve(extpy)
|
2007-01-24 22:24:01 +08:00
|
|
|
else:
|
|
|
|
try:
|
2009-03-18 07:34:59 +08:00
|
|
|
extpy = self.__map__.pop(name)
|
2007-01-24 22:24:01 +08:00
|
|
|
except KeyError:
|
|
|
|
__tracebackhide__ = True
|
|
|
|
raise AttributeError(name)
|
|
|
|
else:
|
2008-02-08 16:43:05 +08:00
|
|
|
result = self.__pkg__._resolve(extpy)
|
2009-03-18 07:34:59 +08:00
|
|
|
|
2007-01-24 22:24:01 +08:00
|
|
|
setattr(self, name, result)
|
|
|
|
#self._fixinspection(result, name)
|
|
|
|
return result
|
|
|
|
|
2009-03-18 07:34:59 +08:00
|
|
|
def __setattr__(self, name, value):
|
|
|
|
super(ApiModule, self).__setattr__(name, value)
|
|
|
|
try:
|
|
|
|
del self.__map__[name]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
2007-01-24 22:24:01 +08:00
|
|
|
def _deprecated_fixinspection(self, result, name):
|
|
|
|
# modify some attrs to make a class appear at export level
|
|
|
|
if hasattr(result, '__module__'):
|
|
|
|
if not result.__module__.startswith('py.__'):
|
|
|
|
return # don't change __module__ nor __name__ for classes
|
|
|
|
# that the py lib re-exports from somewhere else,
|
|
|
|
# e.g. py.builtin.BaseException
|
|
|
|
try:
|
|
|
|
setattr(result, '__module__', self.__name__)
|
|
|
|
except (AttributeError, TypeError):
|
|
|
|
pass
|
|
|
|
if hasattr(result, '__bases__'):
|
|
|
|
try:
|
|
|
|
setattr(result, '__name__', name)
|
|
|
|
except (AttributeError, TypeError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def getdict(self):
|
|
|
|
# force all the content of the module to be loaded when __dict__ is read
|
|
|
|
dictdescr = ModuleType.__dict__['__dict__']
|
|
|
|
dict = dictdescr.__get__(self)
|
|
|
|
if dict is not None:
|
|
|
|
if '*' not in self.__map__:
|
|
|
|
for name in self.__map__.keys():
|
|
|
|
hasattr(self, name) # force attribute to be loaded, ignore errors
|
|
|
|
assert not self.__map__, "%r not empty" % self.__map__
|
|
|
|
else:
|
|
|
|
fsname = self.__map__['*'][0]
|
2008-02-08 16:43:05 +08:00
|
|
|
dict.update(self.__pkg__._loadimpl(fsname[:-3]).__dict__)
|
2007-01-24 22:24:01 +08:00
|
|
|
return dict
|
|
|
|
|
|
|
|
__dict__ = property(getdict)
|
|
|
|
del getdict
|
|
|
|
|
|
|
|
# ---------------------------------------------------
|
|
|
|
# Bootstrap Virtual Module Hierarchy
|
|
|
|
# ---------------------------------------------------
|
|
|
|
|
|
|
|
def initpkg(pkgname, exportdefs, **kw):
|
|
|
|
#print "initializing package", pkgname
|
|
|
|
# bootstrap Package object
|
2008-08-18 23:08:39 +08:00
|
|
|
pkg = Package(pkgname, exportdefs, kw)
|
2007-01-24 22:24:01 +08:00
|
|
|
seen = { pkgname : pkg.module }
|
|
|
|
deferred_imports = []
|
|
|
|
|
|
|
|
for pypath, extpy in pkg.exportitems():
|
|
|
|
pyparts = pypath.split('.')
|
|
|
|
modparts = pyparts[:]
|
|
|
|
if extpy[1] != '*':
|
|
|
|
lastmodpart = modparts.pop()
|
|
|
|
else:
|
|
|
|
lastmodpart = '*'
|
|
|
|
current = pkgname
|
|
|
|
|
|
|
|
# ensure modules
|
|
|
|
for name in modparts:
|
|
|
|
previous = current
|
|
|
|
current += '.' + name
|
|
|
|
if current not in seen:
|
2009-02-27 18:18:27 +08:00
|
|
|
seen[current] = mod = ApiModule(pkg, current)
|
2007-01-24 22:24:01 +08:00
|
|
|
setattr(seen[previous], name, mod)
|
|
|
|
setmodule(current, mod)
|
|
|
|
|
|
|
|
mod = seen[current]
|
|
|
|
if not hasattr(mod, '__map__'):
|
|
|
|
assert mod is pkg.module, \
|
|
|
|
"only root modules are allowed to be non-lazy. "
|
|
|
|
deferred_imports.append((mod, pyparts[-1], extpy))
|
|
|
|
else:
|
2007-02-04 22:27:10 +08:00
|
|
|
if extpy[1] == '__doc__':
|
|
|
|
mod.__doc__ = pkg._resolve(extpy)
|
|
|
|
else:
|
|
|
|
mod.__map__[lastmodpart] = extpy
|
2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
for mod, pypart, extpy in deferred_imports:
|
|
|
|
setattr(mod, pypart, pkg._resolve(extpy))
|
2007-02-04 22:27:10 +08:00
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
autoimport(pkgname)
|
|
|
|
|
|
|
|
def autoimport(pkgname):
|
|
|
|
import py
|
|
|
|
ENVKEY = pkgname.upper() + "_AUTOIMPORT"
|
|
|
|
if ENVKEY in os.environ:
|
|
|
|
for impname in os.environ[ENVKEY].split(","):
|
|
|
|
__import__(impname)
|