apipkg.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. """
  2. apipkg: control the exported namespace of a python package.
  3. see http://pypi.python.org/pypi/apipkg
  4. (c) holger krekel, 2009 - MIT license
  5. """
  6. import os
  7. import sys
  8. from types import ModuleType
  9. __version__ = '1.4'
  10. def _py_abspath(path):
  11. """
  12. special version of abspath
  13. that will leave paths from jython jars alone
  14. """
  15. if path.startswith('__pyclasspath__'):
  16. return path
  17. else:
  18. return os.path.abspath(path)
  19. def distribution_version(name):
  20. """try to get the version of the named distribution,
  21. returs None on failure"""
  22. from pkg_resources import get_distribution, DistributionNotFound
  23. try:
  24. dist = get_distribution(name)
  25. except DistributionNotFound:
  26. pass
  27. else:
  28. return dist.version
  29. def initpkg(pkgname, exportdefs, attr=dict(), eager=False):
  30. """ initialize given package from the export definitions. """
  31. oldmod = sys.modules.get(pkgname)
  32. d = {}
  33. f = getattr(oldmod, '__file__', None)
  34. if f:
  35. f = _py_abspath(f)
  36. d['__file__'] = f
  37. if hasattr(oldmod, '__version__'):
  38. d['__version__'] = oldmod.__version__
  39. if hasattr(oldmod, '__loader__'):
  40. d['__loader__'] = oldmod.__loader__
  41. if hasattr(oldmod, '__path__'):
  42. d['__path__'] = [_py_abspath(p) for p in oldmod.__path__]
  43. if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None):
  44. d['__doc__'] = oldmod.__doc__
  45. d.update(attr)
  46. if hasattr(oldmod, "__dict__"):
  47. oldmod.__dict__.update(d)
  48. mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
  49. sys.modules[pkgname] = mod
  50. # eagerload in bypthon to avoid their monkeypatching breaking packages
  51. if 'bpython' in sys.modules or eager:
  52. for module in sys.modules.values():
  53. if isinstance(module, ApiModule):
  54. module.__dict__
  55. def importobj(modpath, attrname):
  56. module = __import__(modpath, None, None, ['__doc__'])
  57. if not attrname:
  58. return module
  59. retval = module
  60. names = attrname.split(".")
  61. for x in names:
  62. retval = getattr(retval, x)
  63. return retval
  64. class ApiModule(ModuleType):
  65. def __docget(self):
  66. try:
  67. return self.__doc
  68. except AttributeError:
  69. if '__doc__' in self.__map__:
  70. return self.__makeattr('__doc__')
  71. def __docset(self, value):
  72. self.__doc = value
  73. __doc__ = property(__docget, __docset)
  74. def __init__(self, name, importspec, implprefix=None, attr=None):
  75. self.__name__ = name
  76. self.__all__ = [x for x in importspec if x != '__onfirstaccess__']
  77. self.__map__ = {}
  78. self.__implprefix__ = implprefix or name
  79. if attr:
  80. for name, val in attr.items():
  81. # print "setting", self.__name__, name, val
  82. setattr(self, name, val)
  83. for name, importspec in importspec.items():
  84. if isinstance(importspec, dict):
  85. subname = '%s.%s' % (self.__name__, name)
  86. apimod = ApiModule(subname, importspec, implprefix)
  87. sys.modules[subname] = apimod
  88. setattr(self, name, apimod)
  89. else:
  90. parts = importspec.split(':')
  91. modpath = parts.pop(0)
  92. attrname = parts and parts[0] or ""
  93. if modpath[0] == '.':
  94. modpath = implprefix + modpath
  95. if not attrname:
  96. subname = '%s.%s' % (self.__name__, name)
  97. apimod = AliasModule(subname, modpath)
  98. sys.modules[subname] = apimod
  99. if '.' not in name:
  100. setattr(self, name, apimod)
  101. else:
  102. self.__map__[name] = (modpath, attrname)
  103. def __repr__(self):
  104. l = []
  105. if hasattr(self, '__version__'):
  106. l.append("version=" + repr(self.__version__))
  107. if hasattr(self, '__file__'):
  108. l.append('from ' + repr(self.__file__))
  109. if l:
  110. return '<ApiModule %r %s>' % (self.__name__, " ".join(l))
  111. return '<ApiModule %r>' % (self.__name__,)
  112. def __makeattr(self, name):
  113. """lazily compute value for name or raise AttributeError if unknown."""
  114. # print "makeattr", self.__name__, name
  115. target = None
  116. if '__onfirstaccess__' in self.__map__:
  117. target = self.__map__.pop('__onfirstaccess__')
  118. importobj(*target)()
  119. try:
  120. modpath, attrname = self.__map__[name]
  121. except KeyError:
  122. if target is not None and name != '__onfirstaccess__':
  123. # retry, onfirstaccess might have set attrs
  124. return getattr(self, name)
  125. raise AttributeError(name)
  126. else:
  127. result = importobj(modpath, attrname)
  128. setattr(self, name, result)
  129. try:
  130. del self.__map__[name]
  131. except KeyError:
  132. pass # in a recursive-import situation a double-del can happen
  133. return result
  134. __getattr__ = __makeattr
  135. @property
  136. def __dict__(self):
  137. # force all the content of the module
  138. # to be loaded when __dict__ is read
  139. dictdescr = ModuleType.__dict__['__dict__']
  140. dict = dictdescr.__get__(self)
  141. if dict is not None:
  142. hasattr(self, 'some')
  143. for name in self.__all__:
  144. try:
  145. self.__makeattr(name)
  146. except AttributeError:
  147. pass
  148. return dict
  149. def AliasModule(modname, modpath, attrname=None):
  150. mod = []
  151. def getmod():
  152. if not mod:
  153. x = importobj(modpath, None)
  154. if attrname is not None:
  155. x = getattr(x, attrname)
  156. mod.append(x)
  157. return mod[0]
  158. class AliasModule(ModuleType):
  159. def __repr__(self):
  160. x = modpath
  161. if attrname:
  162. x += "." + attrname
  163. return '<AliasModule %r for %r>' % (modname, x)
  164. def __getattribute__(self, name):
  165. try:
  166. return getattr(getmod(), name)
  167. except ImportError:
  168. return None
  169. def __setattr__(self, name, value):
  170. setattr(getmod(), name, value)
  171. def __delattr__(self, name):
  172. delattr(getmod(), name)
  173. return AliasModule(str(modname))