Commits

Alex Gaynor  committed 1a5f26b

Allow submodules for MixedModules, imported from my psycopg2 fork.

  • Participants
  • Parent commits 0a34a5b
  • Branches kqueue

Comments (0)

Files changed (4)

File pypy/interpreter/baseobjspace.py

         else:
             name = importname
 
-        w_name = self.wrap(name)
-        w_mod = self.wrap(Module(self, w_name))
-        self.builtin_modules[name] = w_mod
+        mod = Module(self, self.wrap(name))
+        mod.install()
+
         return name
 
     def getbuiltinmodule(self, name, force_init=False):
         from pypy.module.exceptions import Module
         w_name = self.wrap('exceptions')
         self.exceptions_module = Module(self, w_name)
-        self.builtin_modules['exceptions'] = self.wrap(self.exceptions_module)
+        self.exceptions_module.install()
 
         from pypy.module.sys import Module
         w_name = self.wrap('sys')
         self.sys = Module(self, w_name)
-        self.builtin_modules['sys'] = self.wrap(self.sys)
+        self.sys.install()
 
         from pypy.module.imp import Module
         w_name = self.wrap('imp')
-        self.builtin_modules['imp'] = self.wrap(Module(self, w_name))
+        mod = Module(self, w_name)
+        mod.install()
 
         from pypy.module.__builtin__ import Module
         w_name = self.wrap('__builtin__')
         self.builtin = Module(self, w_name)
         w_builtin = self.wrap(self.builtin)
-        self.builtin_modules['__builtin__'] = self.wrap(w_builtin)
+        w_builtin.install()
         self.setitem(self.builtin.w_dict, self.wrap('__builtins__'), w_builtin)
 
         bootstrap_modules = set(('sys', 'imp', '__builtin__', 'exceptions'))

File pypy/interpreter/mixedmodule.py

 from pypy.interpreter.module import Module
 from pypy.interpreter.function import Function, BuiltinFunction
-from pypy.interpreter import gateway 
-from pypy.interpreter.error import OperationError 
+from pypy.interpreter import gateway
+from pypy.interpreter.error import OperationError
 from pypy.interpreter.baseobjspace import W_Root
 import os, sys
 
     # after startup().
     w_initialdict = None
 
-    def __init__(self, space, w_name): 
-        """ NOT_RPYTHON """ 
-        Module.__init__(self, space, w_name) 
-        self.lazy = True 
+    def __init__(self, space, w_name):
+        """ NOT_RPYTHON """
+        Module.__init__(self, space, w_name)
+        self.lazy = True
         self.__class__.buildloaders()
         self.loaders = self.loaders.copy()    # copy from the class to the inst
+        self.submodules_w = []
+
+    def install(self):
+        """NOT_RPYTHON: install this module, and it's submodules into
+        space.builtin_modules"""
+        Module.install(self)
+        if hasattr(self, "submodules"):
+            space = self.space
+            name = space.unwrap(self.w_name)
+            for sub_name, module_cls in self.submodules.iteritems():
+                module_name = space.wrap("%s.%s" % (name, sub_name))
+                m = module_cls(space, module_name)
+                m.install()
+                self.submodules_w.append(m)
 
     def init(self, space):
         """This is called each time the module is imported or reloaded
             # the saved dict, as done with built-in and extension modules
             # on CPython.
             space.call_method(self.w_dict, 'update', self.w_initialdict)
-        else:
+
+        for w_submodule in self.submodules_w:
+            name = space.str_w(w_submodule.w_name)
+            space.setitem(self.w_dict, space.wrap(name.split(".")[-1]), w_submodule)
+            space.getbuiltinmodule(name)
+
+        if self.w_initialdict is None:
             Module.init(self, space)
             if not self.lazy and self.w_initialdict is None:
                 self.w_initialdict = space.call_method(self.w_dict, 'items')
 
+
     def get_applevel_name(cls):
         """ NOT_RPYTHON """
         if cls.applevel_name is not None:
 
     def get(self, name):
         space = self.space
-        w_value = self.getdictvalue(space, name) 
-        if w_value is None: 
+        w_value = self.getdictvalue(space, name)
+        if w_value is None:
             raise OperationError(space.w_AttributeError, space.wrap(name))
-        return w_value 
+        return w_value
 
-    def call(self, name, *args_w): 
-        w_builtin = self.get(name) 
+    def call(self, name, *args_w):
+        w_builtin = self.get(name)
         return self.space.call_function(w_builtin, *args_w)
 
     def getdictvalue(self, space, name):
 
     def _load_lazily(self, space, name):
         w_name = space.new_interned_str(name)
-        try: 
+        try:
             loader = self.loaders[name]
-        except KeyError: 
-            return None 
-        else: 
-            w_value = loader(space) 
+        except KeyError:
+            return None
+        else:
+            w_value = loader(space)
             func = space.interpclass_w(w_value)
             # the idea of the following code is that all functions that are
             # directly in a mixed-module are "builtin", e.g. they get a
                     func._builtinversion_ = bltin
                     bltin.name = name
                 w_value = space.wrap(bltin)
-            space.setitem(self.w_dict, w_name, w_value) 
+            space.setitem(self.w_dict, w_name, w_value)
             return w_value
 
 
-    def getdict(self): 
-        if self.lazy: 
+    def getdict(self):
+        if self.lazy:
             space = self.space
-            for name in self.loaders: 
-                w_value = self.get(name)  
-                space.setitem(self.w_dict, space.new_interned_str(name), w_value) 
+            for name in self.loaders:
+                w_value = self.get(name)
+                space.setitem(self.w_dict, space.new_interned_str(name), w_value)
             self.lazy = False
             self.w_initialdict = space.call_method(self.w_dict, 'items')
-        return self.w_dict 
+        return self.w_dict
 
     def _freeze_(self):
         self.getdict()
         # not constant
         return False
 
-    def buildloaders(cls): 
-        """ NOT_RPYTHON """ 
-        if not hasattr(cls, 'loaders'): 
+    def buildloaders(cls):
+        """ NOT_RPYTHON """
+        if not hasattr(cls, 'loaders'):
             # build a constant dictionary out of
-            # applevel/interplevel definitions 
+            # applevel/interplevel definitions
             cls.loaders = loaders = {}
             pkgroot = cls.__module__
             appname = cls.get_applevel_name()
-            for name, spec in cls.interpleveldefs.items(): 
-                loaders[name] = getinterpevalloader(pkgroot, spec) 
-            for name, spec in cls.appleveldefs.items(): 
+            for name, spec in cls.interpleveldefs.items():
+                loaders[name] = getinterpevalloader(pkgroot, spec)
+            for name, spec in cls.appleveldefs.items():
                 loaders[name] = getappfileloader(pkgroot, appname, spec)
-            assert '__file__' not in loaders 
+            assert '__file__' not in loaders
             if cls.expose__file__attribute:
                 loaders['__file__'] = cls.get__file__
             if '__doc__' not in loaders:
         w_obj = loader(space)
         space.setattr(space.wrap(self), space.wrap(name), w_obj)
 
-    def get__file__(cls, space): 
-        """ NOT_RPYTHON. 
-        return the __file__ attribute of a MixedModule 
-        which is the root-directory for the various 
+    def get__file__(cls, space):
+        """ NOT_RPYTHON.
+        return the __file__ attribute of a MixedModule
+        which is the root-directory for the various
         applevel and interplevel snippets that make
-        up the module. 
-        """ 
-        try: 
-            fname = cls._fname 
-        except AttributeError: 
+        up the module.
+        """
+        try:
+            fname = cls._fname
+        except AttributeError:
             pkgroot = cls.__module__
             mod = __import__(pkgroot, None, None, ['__doc__'])
-            fname = mod.__file__ 
+            fname = mod.__file__
             assert os.path.basename(fname).startswith('__init__.py')
             # make it clear that it's not really the interp-level module
             # at this path that we are seeing, but an app-level version of it
             fname = os.path.dirname(fname)
-            cls._fname = fname 
-        return space.wrap(fname) 
+            cls._fname = fname
+        return space.wrap(fname)
 
-    get__file__ = classmethod(get__file__) 
+    get__file__ = classmethod(get__file__)
 
     def get__doc__(cls, space):
         return space.wrap(cls.__doc__)
 
 
 def getinterpevalloader(pkgroot, spec):
-    """ NOT_RPYTHON """     
-    def ifileloader(space): 
+    """ NOT_RPYTHON """
+    def ifileloader(space):
         d = {'space' : space}
-        # EVIL HACK (but it works, and this is not RPython :-) 
-        while 1: 
-            try: 
-                value = eval(spec, d) 
-            except NameError, ex: 
-                name = ex.args[0].split("'")[1] # super-Evil 
+        # EVIL HACK (but it works, and this is not RPython :-)
+        while 1:
+            try:
+                value = eval(spec, d)
+            except NameError, ex:
+                name = ex.args[0].split("'")[1] # super-Evil
                 if name in d:
                     raise   # propagate the NameError
-                try: 
+                try:
                     d[name] = __import__(pkgroot+'.'+name, None, None, [name])
                 except ImportError:
                     etype, evalue, etb = sys.exc_info()
                         # didn't help, re-raise the original exception for
                         # clarity
                         raise etype, evalue, etb
-            else: 
+            else:
                 #print spec, "->", value
-                if hasattr(value, 'func_code'):  # semi-evil 
+                if hasattr(value, 'func_code'):  # semi-evil
                     return space.wrap(gateway.interp2app(value))
 
                 try:
                 assert isinstance(value, W_Root), (
                     "interpleveldef %s.%s must return a wrapped object "
                     "(got %r instead)" % (pkgroot, spec, value))
-                return value 
-    return ifileloader 
-        
+                return value
+    return ifileloader
+
 applevelcache = {}
 def getappfileloader(pkgroot, appname, spec):
-    """ NOT_RPYTHON """ 
-    # hum, it's a bit more involved, because we usually 
+    """ NOT_RPYTHON """
+    # hum, it's a bit more involved, because we usually
     # want the import at applevel
     modname, attrname = spec.split('.')
     impbase = pkgroot + '.' + modname
         app = gateway.applevel(source, filename=fn, modname=appname)
         applevelcache[impbase] = app
 
-    def afileloader(space): 
+    def afileloader(space):
         return app.wget(space, attrname)
-    return afileloader 
-
+    return afileloader

File pypy/interpreter/module.py

 
     def __init__(self, space, w_name, w_dict=None, add_package=True):
         self.space = space
-        if w_dict is None: 
+        if w_dict is None:
             w_dict = space.newdict(module=True)
-        self.w_dict = w_dict 
-        self.w_name = w_name 
+        self.w_dict = w_dict
+        self.w_name = w_name
         if w_name is not None:
             space.setitem(w_dict, space.new_interned_str('__name__'), w_name)
         if add_package:
                           space.w_None)
         self.startup_called = False
 
+    def install(self):
+        """NOT_RPYTHON: installs this module into space.builtin_modules"""
+        w_mod = self.space.wrap(self)
+        self.space.builtin_modules[self.space.unwrap(self.w_name)] = w_mod
+
     def setup_after_space_initialization(self):
         """NOT_RPYTHON: to allow built-in modules to do some more setup
         after the space is fully initialized."""
     def descr_module__init__(self, w_name, w_doc=None):
         space = self.space
         self.w_name = w_name
-        if w_doc is None:  
+        if w_doc is None:
             w_doc = space.w_None
         space.setitem(self.w_dict, space.new_interned_str('__name__'), w_name)
         space.setitem(self.w_dict, space.new_interned_str('__doc__'), w_doc)
 
     def descr__reduce__(self, space):
         w_name = space.finditem(self.w_dict, space.wrap('__name__'))
-        if (w_name is None or 
+        if (w_name is None or
             not space.is_true(space.isinstance(w_name, space.w_str))):
             # maybe raise exception here (XXX this path is untested)
             return space.w_None
             from pypy.interpreter.mixedmodule import MixedModule
             w_mod    = space.getbuiltinmodule('_pickle_support')
             mod      = space.interp_w(MixedModule, w_mod)
-            new_inst = mod.get('module_new')            
+            new_inst = mod.get('module_new')
             return space.newtuple([new_inst, space.newtuple([w_name,
-                                    self.getdict()]), 
+                                    self.getdict()]),
                                   ])
         #already imported case
         w_import = space.builtin.get('__import__')

File pypy/interpreter/test/test_mixedmodule.py

+from pypy.interpreter.mixedmodule import MixedModule
+
+
+class TestMixedModule(object):
+    def test_install(self):
+        class Module(MixedModule):
+            interpleveldefs = {}
+            appleveldefs = {}
+
+        m = Module(self.space, self.space.wrap("test_module"))
+        m.install()
+
+        assert self.space.builtin_modules["test_module"] is m
+
+    def test_submodule(self):
+        class SubModule(MixedModule):
+            interpleveldefs = {}
+            appleveldefs = {}
+
+        class Module(MixedModule):
+            interpleveldefs = {}
+            appleveldefs = {}
+            submodules = {
+                "sub": SubModule
+            }
+
+        m = Module(self.space, self.space.wrap("test_module"))
+        m.install()
+
+        assert self.space.builtin_modules["test_module"] is m
+        assert isinstance(self.space.builtin_modules["test_module.sub"], SubModule)
+
+class AppTestMixedModule(object):
+    def setup_class(cls):
+        space = cls.space
+
+        class SubModule(MixedModule):
+            interpleveldefs = {
+                "value": "space.wrap(14)"
+            }
+            appleveldefs = {}
+
+        class Module(MixedModule):
+            interpleveldefs = {}
+            appleveldefs = {}
+            submodules = {
+                "sub": SubModule
+            }
+
+        m = Module(space, space.wrap("test_module"))
+        m.install()
+
+    def teardown_class(cls):
+        from pypy.module.sys.state import get
+
+        space = cls.space
+        del space.builtin_modules["test_module"]
+        del space.builtin_modules["test_module.sub"]
+        w_modules = get(space).w_modules
+        space.delitem(w_modules, space.wrap("test_module"))
+        space.delitem(w_modules, space.wrap("test_module.sub"))
+
+    def test_attibute(self):
+        import test_module
+
+        assert hasattr(test_module, "sub")
+
+    def test_submodule_import(self):
+        from test_module import sub
+
+    def test_direct_import(self):
+        import test_module.sub
+
+        assert test_module.sub
+        assert test_module.sub.value == 14
+
+    def test_import_from(self):
+        from test_module.sub import value
+
+        assert value == 14