[albatross-users] PATCH: page module loading changes (was: pagination with al-for)

Andrew McNamara andrewm at object-craft.com.au
Thu Jan 8 12:36:08 EST 2004


>Even getting it to work with my "synthetic holding module" approach
>will not be straighforward - I'll have to somehow hook the unpickler's
>import requests.

Here's a patch that implements option 2 (loading page modules into a
synthetic namespace). It's worked on the small number of (moderately
complex) albatross apps I tried so far. In theory, you can now have a
hierarchy of page modules ("admin.foo", "admin.bah"), although I haven't
tested this yet.

There are a number of changes from the old behaviour:

 * page module names were previously allowed to contain path components -
   now they are dot-delimited. So "admin/foo" becomes "admin.foo"
   (untested).

 * the page module cache implemented by the PageModuleMixin is gone - we
   now rely on sys.modules for caching. The magic to auto-reload when the
   module file was touched is also removed (it was broken anyway).

 * page modules are saved into sys.modules prepended with a synthetic
   __alpage__ module - so "login" becomes "__alpage__.login".

Index: app.py
===================================================================
RCS file: /usr/local/cvsroot/object-craft/albatross/albatross/app.py,v
retrieving revision 1.84
diff -u -r1.84 app.py
--- app.py      10 Nov 2003 00:35:04 -0000      1.84
+++ app.py      8 Jan 2004 01:23:53 -0000
@@ -7,7 +7,6 @@
 
 import os
 import sys
-import stat
 import imp
 import urlparse
 
@@ -346,10 +345,17 @@
     '''Caching module loader
     '''
 
+    mod_holder_name = '__alpage__'
+
     def __init__(self, base_dir, start_page):
         self.__base_dir = base_dir
         self.__start_page = start_page
-        self.__cache = {}
+        try:
+            self.__mod_holder = sys.modules[self.mod_holder_name]
+        except KeyError:
+            self.__mod_holder = imp.new_module(self.mod_holder_name)
+            sys.modules[self.mod_holder_name] = self.__mod_holder
+            globals()[self.mod_holder_name] = self.__mod_holder
 
     def module_path(self):
         return self.__base_dir
@@ -365,35 +371,33 @@
             ctx.set_page(name)
         self.load_page_module(ctx, name)
 
+    def is_page_module(self, name):
+        return name.startswith(self.mod_holder_name)
+
     def load_page_module(self, ctx, name):
-        if self.__base_dir == '.':
-            path = name
-        else:
-            path = os.path.join(self.__base_dir, name)
-        page = self.__cache.get(path)
-        if page:
-            mtime = os.stat(page.__file__)[stat.ST_MTIME]
-            if mtime > page.__mtime__:
-                reload(page)
-                page.__mtime__ = mtime
-        if not page:
-            dirname, name = os.path.split(path)
-            file, filepath, desc = imp.find_module(name, [dirname])
-            page = sys.modules.get(name)
-            if not page or not page.__file__.startswith(filepath):
-                try:
-                    page = imp.load_module(name, file, filepath, desc)
-                finally:
-                    if file:
-                        file.close()
-            if not (hasattr(page, 'page_enter') or
-                    hasattr(page, 'page_leave') or
-                    hasattr(page, 'page_process') or
-                    hasattr(page, 'page_display')):
-                raise ApplicationError('module "%s" does not define one of page_enter, page_leave, page_process or page_display' % (name))
-            page.__mtime__ = os.stat(page.__file__)[stat.ST_MTIME]
-            self.__cache[path] = page
+        if name.startswith(self.mod_holder_name):
+            name = name[len(self.mod_holder_name)+1:]
+        filename = os.path.join(self.__base_dir, name.replace('.', os.path.sep))
+        filename = os.path.normpath(filename)
+        dirname, filename = os.path.split(filename)
+        module_name = self.mod_holder_name + '.' + name
+        try:
+            page = sys.modules[module_name]
+        except KeyError:
+            f, filepath, desc = imp.find_module(name, [dirname])
+            try:
+                page = imp.load_module(module_name, f, filepath, desc)
+            finally:
+                if f:
+                    f.close()
+        if not (hasattr(page, 'page_enter') or
+                hasattr(page, 'page_leave') or
+                hasattr(page, 'page_process') or
+                hasattr(page, 'page_display')):
+            raise ApplicationError('module "%s" does not define one of page_enter, page_leave, page_process or page_display' % (name))
+        setattr(self.__mod_holder, name, page)
         ctx.page = page
+        return page
 
     def page_enter(self, ctx, args):
         if hasattr(ctx.page, 'page_enter'):
@@ -423,8 +427,8 @@
         self.__start_page = start_page
         self.__page_objects = {}
 
-    def module_path(self):
-        pass
+    def is_page_module(self, name):
+        return False
 
     def start_page(self):
         return self.__start_page
Index: context.py
===================================================================
RCS file: /usr/local/cvsroot/object-craft/albatross/albatross/context.py,v
retrieving revision 1.93
diff -u -r1.93 context.py
--- context.py  21 Sep 2003 04:40:31 -0000      1.93
+++ context.py  8 Jan 2004 01:23:54 -0000
@@ -10,6 +10,7 @@
 import stat
 import re
 import cPickle
+import __builtin__
 
 try:
     import zlib
@@ -564,9 +565,13 @@
         self.clear_locals()
 
     def decode_session(self, text):
-        module_path = self.app.module_path()
-        if module_path:
-            sys.path.insert(0, module_path)
+        def imp_hook(name, globals, locals, fromlist):
+            if self.app.is_page_module(name):
+                return self.app.load_page_module(self, name)
+            else:
+                return real_imp(name, globals, locals, fromlist)
+
+        real_imp, __builtin__.__import__ = __builtin__.__import__, imp_hook
         try:
             try:
                 vars = cPickle.loads(text)
@@ -577,8 +582,7 @@
             for name in vars.keys():
                 self.__vars[name] = 1
         finally:
-            if module_path:
-                sys.path.remove(module_path)
+            __builtin__.__import__ = real_imp
 
     def encode_session(self):
         vars = {}

-- 
Andrew McNamara, Senior Developer, Object Craft
http://www.object-craft.com.au/



More information about the Albatross-users mailing list