PageRenderTime 118ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/_unsorted/_core_new/_importing/module.py

https://bitbucket.org/ericsnowcurrently/commonlib
Python | 291 lines | 269 code | 14 blank | 8 comment | 4 complexity | 87411e2b0411b732c672a082e895a0be MD5 | raw file
Possible License(s): BSD-3-Clause
  1. """core.importing module
  2. """
  3. __all__ = (
  4. "Module",
  5. "LazyModule", "SmartModule", "ExtensionModule",
  6. )
  7. import sys
  8. import imp
  9. ModuleType = type(sys)
  10. #################################################
  11. # module classes
  12. ModuleDescription = namedtuple("ModuleDescription", "suffix mode type")
  13. ModuleFile = namedtuple("ModuleFile", "file pathname description")
  14. class ModuleMeta(type):
  15. def from_path(cls, path, name=None, **kwargs):
  16. """Factory function for cls which pulls from the path."""
  17. raise NotImplementedError
  18. def from_module(cls, module, name=None, **kwargs):
  19. """Factory function for cls which copies the module."""
  20. if not hasattr(module, "items"):
  21. module = getattr(module, self.MODULE_NAMESPACE_ATTR)
  22. return cls.from_namespace(module, name, **kwargs)
  23. def from_namespace(cls, namespace, name=None, **kwargs):
  24. """Factory function for cls which copies the namespace."""
  25. namespace = namespace.copy()
  26. if "__builtins__" in namespace:
  27. del namespace["__builtins__"]
  28. namespace.update(kwargs)
  29. if name is None:
  30. name = namespace.get("__name__")
  31. else:
  32. namespace["__name__"] = name
  33. module = cls(name)
  34. module.__dict__.update(namespace)
  35. return module
  36. def find(cls, module):
  37. """Find this module's ModuleFile."""
  38. raise NotImplementedError
  39. def build(cls, module):
  40. """Execute the module and populate its namespace."""
  41. raise NotImplementedError
  42. def attach(cls, module, ...):
  43. """Bind the module to its package and sys.modules."""
  44. raise NotImplementedError
  45. def reload(cls, module):
  46. """Reload that module object."""
  47. raise NotImplementedError
  48. #class Module(ModuleType):
  49. class Module(object):
  50. """A straight-up replacement for the built-in module type.
  51. By default Module objects will always have the following attributes:
  52. __doc__
  53. __name__
  54. Where __name__ is set to the passed name and __doc__ to None. Here
  55. are the other attributes that may be found on Module objects:
  56. __all__ - a list of the module's "public" attribute names.
  57. __builtins__ - the builtins used during execution for non-builtin
  58. modules (None or not set for "built-in" modules).
  59. For this class __builtins__ is explicitly not set.
  60. __cached__ - the cached file (see PEP 3147).
  61. __file__ - the path to the file from which the module was
  62. loaded (None or not set for "built-in" modules).
  63. __loader__ - the loader that loaded the module (see PEP 302).
  64. __package__ - the package to which the module belongs (see
  65. PEP 366).
  66. __path__ - for packages, the list of paths to use when
  67. importing modules in the package (see PEP 302).
  68. For more on these attributes, see the following resources (all at
  69. <http://docs.python.org/dev>):
  70. /reference/simple_stmts.html#the-import-statement
  71. /library/importlib.html#importlib.abc.Loader.load_module
  72. /library/runpy.html#runpy.run_module
  73. /library/imp.html#imp.source_from_cache
  74. /whatsnew/3.2.html#pep-3147-pyc-repository-directories
  75. /tutorial/modules.html#packages-in-multiple-directories
  76. PEPS:
  77. 302 - http://www.python.org/dev/peps/pep-0302/
  78. 366 - http://www.python.org/dev/peps/pep-0366/
  79. 3147 - http://www.python.org/dev/peps/pep-3147/
  80. When a module's code object is executed, it should be done using
  81. the modules __dict__ as the globals passed in to exec(). Thus, any
  82. module attributes must be set before module execution if they are to
  83. be available during execution.
  84. Note: a package is just a module with __path__ set. The package's
  85. __init__.py is the actual code executed for that module. When
  86. submodules are imported, they are subsequently bound by their base
  87. name to the namespace of the package and __package__ of the
  88. submodule is set to the package. A submodule can be a package
  89. itself.
  90. """
  91. __metaclass__ = ModuleMeta
  92. MODULE_NAMESPACE_ATTR = "__dict__"
  93. MODULE_ATTRS = (
  94. "__name__", "__doc__", "__file__",
  95. "__loader__", "__all__", # optional
  96. "__package__", "__loader__", # future
  97. "__builtins__", # implementation detail
  98. )
  99. PACKAGE_ATTRS = ("__path__",)
  100. def __new__(cls, name, *args, **kwargs):
  101. if not name or not isinstance(name, str):
  102. raise TypeError("name must be a non-empty string")
  103. return super(cls, cls).__new__(cls, name, *args, **kwargs)
  104. def __init__(self, name, engine):
  105. self.__name__ = name
  106. self.__doc__ = None
  107. self.__engine__ = engine
  108. @property
  109. def __module__(self):
  110. return self
  111. @property
  112. def __builtins__(self):
  113. raise TypeError("__builtins__ not available")
  114. class LazyModule(Module):
  115. """Effectively a placeholder module.
  116. Any attribute access forces the lazy load and rebinds the original
  117. name to the wrapped module (before anything else happens).
  118. """
  119. def __init__(self, name, wrapped):
  120. type(self)._setattr(self, "__name__", name)
  121. type(self)._setattr(self, "__doc__", None)
  122. type(self)._setattr(self, "__wrapped__", wrapped)
  123. type(self)._setattr(self, "_done", False)
  124. # the triggers
  125. def __getattribute__(self, name):
  126. if name in ("__name__",):
  127. return object.__getattribute__(self, name)
  128. module = type(self).rebind(self)
  129. return getattr(module, name)
  130. def __setattr__(self, name, value):
  131. module = type(self).rebind(self)
  132. setattr(module, name, value)
  133. def __delattr__(self, name):
  134. module = type(self).rebind(self)
  135. delattr(module, name)
  136. # access work-around
  137. def _getattr(self, name):
  138. return object.__getattribute__(self, name)
  139. def _setattr(self, name, value):
  140. object.__setattr__(self, name, value)
  141. # the meat of the class
  142. def rebind(self, name=None):
  143. """Force the wrapped module to be put into place."""
  144. if type(self)._getattr(self, "_done"):
  145. raise TypeError("can't rebind a module that has been rebound")
  146. if name is None:
  147. name = self.__name__
  148. wrapped = type(self)._getattr(self, "__wrapped__")
  149. if isinstance(wrapped, str):
  150. wrapped = load_module(wrapped)
  151. type(self)._setattr(self, "__wrapped__", wrapped)
  152. attach_module(wrapped, name, verify=self)
  153. rebind_module(wrapped, name, verify=self)
  154. type(self)._setattr(self, "_done", True)
  155. return wrappped
  156. class SmartModule(Module):
  157. """Some extra features added on to the base module.
  158. The SmartModule methods are exposed as staticmethods on the
  159. metaclass so that they do not interfere with the namespace of
  160. module instances.
  161. The exception to this rule is the __wrapped__ attribute, which
  162. is bound to the module this one replaced, or to None.
  163. """
  164. class __metaclass__(type):
  165. def from_module(cls, module, lazy=False, attach=False):
  166. """Make a new SmartModule out of the passed module."""
  167. if isinstance(module, str):
  168. name = module
  169. else:
  170. name = module.__name__
  171. newmodule = cls(name)
  172. cls.assume(newmodule, module, lazy)
  173. cls.attach(newmodule, name)
  174. return newmodule
  175. @staticmethod
  176. def assume(module, wrapped, lazy=False):
  177. if isinstance(module, str):
  178. module = load_module(module)
  179. def __init__(self, name):
  180. super(type(self), self).__init__(name)
  181. self.__wrapped__ = None
  182. def assume(self, module):
  183. """Take over the place of the passed module in sys.modules."""
  184. self.__wrapped__ = module
  185. ...
  186. def find(self):
  187. ...
  188. def load(self):
  189. ...
  190. def attach(self):
  191. ...
  192. class ExtendedModule(ModuleType):
  193. BORROWED = ("__name__", "__doc__",
  194. "__file__", "__path__", "__package__",
  195. )
  196. def __init__(self, original=None, lazy=False):
  197. if not lazy:
  198. original = self.load_module(original)
  199. else:
  200. find_module(original)
  201. self.__wrapped__ = original
  202. self.lazy = lazy
  203. @classmethod
  204. def from_module(cls, module, lazy=False, replace=True):
  205. #def from_module(cls, module, *, lazy=False, replace=True):
  206. newmodule = cls(module, lazy)
  207. if replace:
  208. sys.modules[newmodule.__name__]
  209. #######################################
  210. # tests
  211. #
  212. # ExtendedModule
  213. # * <result of ExtendedModule(original)>.__name__ == original.__name__
  214. # * <result of from_module(module)>.__name__ == module.__name__
  215. # *
  216. # *
  217. # *
  218. # *
  219. #
  220. # *
  221. #