PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/interpreter/mixedmodule.py

https://bitbucket.org/pypy/pypy/
Python | 227 lines | 210 code | 8 blank | 9 comment | 9 complexity | f08ba124eb253d280e7cec03e728b7e9 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from pypy.interpreter.module import Module
  2. from pypy.interpreter.function import Function, BuiltinFunction
  3. from pypy.interpreter import gateway
  4. from pypy.interpreter.error import OperationError
  5. from pypy.interpreter.baseobjspace import W_Root
  6. import sys
  7. class MixedModule(Module):
  8. applevel_name = None
  9. # The following attribute is None as long as the module has not been
  10. # imported yet, and when it has been, it is mod.__dict__.items() just
  11. # after startup().
  12. w_initialdict = None
  13. lazy = False
  14. submodule_name = None
  15. def __init__(self, space, w_name):
  16. """ NOT_RPYTHON """
  17. Module.__init__(self, space, w_name)
  18. self.lazy = True
  19. self.__class__.buildloaders()
  20. self.loaders = self.loaders.copy() # copy from the class to the inst
  21. self.submodules_w = []
  22. def install(self):
  23. """NOT_RPYTHON: install this module, and it's submodules into
  24. space.builtin_modules"""
  25. Module.install(self)
  26. if hasattr(self, "submodules"):
  27. space = self.space
  28. name = space.unwrap(self.w_name)
  29. for sub_name, module_cls in self.submodules.iteritems():
  30. if module_cls.submodule_name is None:
  31. module_cls.submodule_name = sub_name
  32. module_name = space.wrap("%s.%s" % (name, sub_name))
  33. m = module_cls(space, module_name)
  34. m.install()
  35. self.submodules_w.append(m)
  36. def init(self, space):
  37. """This is called each time the module is imported or reloaded
  38. """
  39. if self.w_initialdict is not None:
  40. # the module was already imported. Refresh its content with
  41. # the saved dict, as done with built-in and extension modules
  42. # on CPython.
  43. space.call_method(self.w_dict, 'update', self.w_initialdict)
  44. for w_submodule in self.submodules_w:
  45. name = space.str0_w(w_submodule.w_name)
  46. space.setitem(self.w_dict, space.wrap(name.split(".")[-1]), w_submodule)
  47. space.getbuiltinmodule(name)
  48. if self.w_initialdict is None:
  49. Module.init(self, space)
  50. if not self.lazy and self.w_initialdict is None:
  51. self.save_module_content_for_future_reload()
  52. def save_module_content_for_future_reload(self):
  53. self.w_initialdict = self.space.call_method(self.w_dict, 'items')
  54. @classmethod
  55. def get_applevel_name(cls):
  56. """ NOT_RPYTHON """
  57. if cls.applevel_name is not None:
  58. return cls.applevel_name
  59. else:
  60. pkgroot = cls.__module__
  61. return pkgroot.split('.')[-1]
  62. def get(self, name):
  63. space = self.space
  64. w_value = self.getdictvalue(space, name)
  65. if w_value is None:
  66. raise OperationError(space.w_AttributeError, space.wrap(name))
  67. return w_value
  68. def call(self, name, *args_w):
  69. w_builtin = self.get(name)
  70. return self.space.call_function(w_builtin, *args_w)
  71. def getdictvalue(self, space, name):
  72. w_value = space.finditem_str(self.w_dict, name)
  73. if self.lazy and w_value is None:
  74. return self._load_lazily(space, name)
  75. return w_value
  76. def _load_lazily(self, space, name):
  77. w_name = space.new_interned_str(name)
  78. try:
  79. loader = self.loaders[name]
  80. except KeyError:
  81. return None
  82. else:
  83. w_value = loader(space)
  84. # the idea of the following code is that all functions that are
  85. # directly in a mixed-module are "builtin", e.g. they get a
  86. # special type without a __get__
  87. # note that this is not just all functions that contain a
  88. # builtin code object, as e.g. methods of builtin types have to
  89. # be normal Functions to get the correct binding behaviour
  90. func = w_value
  91. if (isinstance(func, Function) and
  92. type(func) is not BuiltinFunction):
  93. try:
  94. bltin = func._builtinversion_
  95. except AttributeError:
  96. bltin = BuiltinFunction(func)
  97. bltin.w_module = self.w_name
  98. func._builtinversion_ = bltin
  99. bltin.name = name
  100. w_value = space.wrap(bltin)
  101. space.setitem(self.w_dict, w_name, w_value)
  102. return w_value
  103. def getdict(self, space):
  104. if self.lazy:
  105. for name in self.loaders:
  106. w_value = self.get(name)
  107. space.setitem(self.w_dict, space.new_interned_str(name), w_value)
  108. self.lazy = False
  109. self.save_module_content_for_future_reload()
  110. return self.w_dict
  111. def _cleanup_(self):
  112. self.getdict(self.space)
  113. self.w_initialdict = None
  114. self.startup_called = False
  115. self._frozen = True
  116. @classmethod
  117. def buildloaders(cls):
  118. """ NOT_RPYTHON """
  119. if not hasattr(cls, 'loaders'):
  120. # build a constant dictionary out of
  121. # applevel/interplevel definitions
  122. cls.loaders = loaders = {}
  123. pkgroot = cls.__module__
  124. appname = cls.get_applevel_name()
  125. if cls.submodule_name is not None:
  126. appname += '.%s' % (cls.submodule_name,)
  127. for name, spec in cls.interpleveldefs.items():
  128. loaders[name] = getinterpevalloader(pkgroot, spec)
  129. for name, spec in cls.appleveldefs.items():
  130. loaders[name] = getappfileloader(pkgroot, appname, spec)
  131. assert '__file__' not in loaders
  132. if '__doc__' not in loaders:
  133. loaders['__doc__'] = cls.get__doc__
  134. def extra_interpdef(self, name, spec):
  135. cls = self.__class__
  136. pkgroot = cls.__module__
  137. loader = getinterpevalloader(pkgroot, spec)
  138. space = self.space
  139. w_obj = loader(space)
  140. space.setattr(space.wrap(self), space.wrap(name), w_obj)
  141. @classmethod
  142. def get__doc__(cls, space):
  143. return space.wrap(cls.__doc__)
  144. def getinterpevalloader(pkgroot, spec):
  145. """ NOT_RPYTHON """
  146. def ifileloader(space):
  147. d = {'space':space}
  148. # EVIL HACK (but it works, and this is not RPython :-)
  149. while 1:
  150. try:
  151. value = eval(spec, d)
  152. except NameError as ex:
  153. name = ex.args[0].split("'")[1] # super-Evil
  154. if name in d:
  155. raise # propagate the NameError
  156. try:
  157. d[name] = __import__(pkgroot+'.'+name, None, None, [name])
  158. except ImportError:
  159. etype, evalue, etb = sys.exc_info()
  160. try:
  161. d[name] = __import__(name, None, None, [name])
  162. except ImportError:
  163. # didn't help, re-raise the original exception for
  164. # clarity
  165. raise etype, evalue, etb
  166. else:
  167. #print spec, "->", value
  168. if hasattr(value, 'func_code'): # semi-evil
  169. return space.wrap(gateway.interp2app(value))
  170. try:
  171. is_type = issubclass(value, W_Root) # pseudo-evil
  172. except TypeError:
  173. is_type = False
  174. if is_type:
  175. return space.gettypefor(value)
  176. assert isinstance(value, W_Root), (
  177. "interpleveldef %s.%s must return a wrapped object "
  178. "(got %r instead)" % (pkgroot, spec, value))
  179. return value
  180. return ifileloader
  181. applevelcache = {}
  182. def getappfileloader(pkgroot, appname, spec):
  183. """ NOT_RPYTHON """
  184. # hum, it's a bit more involved, because we usually
  185. # want the import at applevel
  186. modname, attrname = spec.split('.')
  187. impbase = pkgroot + '.' + modname
  188. try:
  189. app = applevelcache[impbase]
  190. except KeyError:
  191. import imp
  192. pkg = __import__(pkgroot, None, None, ['__doc__'])
  193. file, fn, (suffix, mode, typ) = imp.find_module(modname, pkg.__path__)
  194. assert typ == imp.PY_SOURCE
  195. source = file.read()
  196. file.close()
  197. if fn.endswith('.pyc') or fn.endswith('.pyo'):
  198. fn = fn[:-1]
  199. app = gateway.applevel(source, filename=fn, modname=appname)
  200. applevelcache[impbase] = app
  201. def afileloader(space):
  202. return app.wget(space, attrname)
  203. return afileloader