[cig-commits] r7839 - in cs/pythia/trunk/opal: components controllers core core/handlers db/models sites views

leif at geodynamics.org leif at geodynamics.org
Thu Aug 16 19:54:33 PDT 2007


Author: leif
Date: 2007-08-16 19:54:31 -0700 (Thu, 16 Aug 2007)
New Revision: 7839

Added:
   cs/pythia/trunk/opal/core/responders.py
Modified:
   cs/pythia/trunk/opal/components/ListEditor.py
   cs/pythia/trunk/opal/components/WebComponent.py
   cs/pythia/trunk/opal/controllers/DeletionController.py
   cs/pythia/trunk/opal/controllers/UpdateController.py
   cs/pythia/trunk/opal/core/context_processors.py
   cs/pythia/trunk/opal/core/handlers/base.py
   cs/pythia/trunk/opal/core/urlresolvers.py
   cs/pythia/trunk/opal/db/models/manipulators.py
   cs/pythia/trunk/opal/sites/WebSite.py
   cs/pythia/trunk/opal/views/DetailView.py
Log:
Introduced class Responder.  Switched to TreeURLResolver.  URL
resolvers return a Responder instead of a view (callback,
callback_args, callback_kwargs) tuple.  APPEND_SLASH is marked for
deletion; TreeURLResolver appends a slash depending on whether the
object being assessed is a "leaf" or not.  Database queries now occur
during the URL resolver phase.


Modified: cs/pythia/trunk/opal/components/ListEditor.py
===================================================================
--- cs/pythia/trunk/opal/components/ListEditor.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/components/ListEditor.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -20,10 +20,10 @@
     from ObjectEditor import ObjectEditor
 
 
-    def __init__(self, **attrs):
-        super(ListEditor, self).__init__(**attrs)
-        self.allowEmpty = attrs.get('allowEmpty', True)
-        self.SubEditor = attrs.get('SubEditor', self.ObjectEditor)
+    def __init__(self, allow_empty=True, SubEditor=None, **kwds):
+        super(ListEditor, self).__init__(**kwds)
+        self.allow_empty = allow_empty
+        self.SubEditor = SubEditor or self.ObjectEditor
 
 
     def getView(self, request):
@@ -47,7 +47,7 @@
         return self.genericObjectList(
             request,
             self.querySet(),
-            allow_empty = self.allowEmpty,
+            allow_empty = self.allow_empty,
             extra_context = self.context,
             )
 

Modified: cs/pythia/trunk/opal/components/WebComponent.py
===================================================================
--- cs/pythia/trunk/opal/components/WebComponent.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/components/WebComponent.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -53,6 +53,14 @@
     urlpatterns = []
 
 
+    def rootResponder(self):
+        return self
+
+
+    def subResponder(self, name):
+        raise http.Http404
+
+
     # urlpatterns support
 
     def include(self, urlconf_module):
@@ -108,20 +116,20 @@
         return controller.response(request)
 
 
-    def genericUpdateObject(self, request, model, query, post_save_redirect=None, follow=None, **kwds):
+    def genericUpdateObject(self, request, model, post_save_redirect=None, follow=None, **kwds):
         controller = controllers.UpdateController(
             post_redirect = post_save_redirect,
             follow = follow,
             )
-        view = views.DetailView(model, query, controller = controller, **kwds)
+        view = views.DetailView(model, controller = controller, **kwds)
         return controller.response(request)
 
 
-    def genericDeleteObject(self, request, model, query, post_delete_redirect, **kwds):
+    def genericDeleteObject(self, request, model, post_delete_redirect, **kwds):
         controller = controllers.DeletionController(
             post_redirect = post_delete_redirect,
             )
-        view = views.DetailView(model, query, controller = controller, **kwds)
+        view = views.DetailView(model, controller = controller, **kwds)
         return controller.response(request)
 
 
@@ -163,13 +171,13 @@
     from opal.http import Http404
 
 
-    def __xinit__(self, **attrs):
+    def __xinit__(self, slug, url, context=None, **kwds):
         super(WebComponent, self).__init__()
-        self.slug = attrs.get('slug')
-        self.context = attrs.get('context', {})
-        self.parentURL = attrs.get('url')
+        self.slug = slug
+        self.context = context or {}
+        self.parentURL = url
         self.subcomponents = None
-        self._initTreeNode(**attrs)
+        self._initTreeNode(url, **kwds)
 
 
     def createSubcomponent(self, slug, **attrs):
@@ -226,11 +234,11 @@
 
     # TreeNode protocol
 
-    def _initTreeNode(self, **attrs):
+    def _initTreeNode(self, url, name=None, title=None, **kwds):
         # this node
-        self.name = attrs.get('name', self.slug)
-        self.title = attrs.get('title', self.name.title())
-        self.url = attrs['url'] + '/' + self.slug
+        self.name = name or self.slug
+        self.title = title or self.name.title()
+        self.url = url + '/' + self.slug
 
         # children
         self.isLeaf = False

Modified: cs/pythia/trunk/opal/controllers/DeletionController.py
===================================================================
--- cs/pythia/trunk/opal/controllers/DeletionController.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/controllers/DeletionController.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -41,7 +41,7 @@
     def response(self, request):
 
         if request.method == "POST":
-            self.view.obj.delete()
+            self.view.model.delete()
             if request.user.is_authenticated():
                 request.user.message_set.create(message="The %s was deleted." % self.model._meta.verbose_name)
             return HttpResponseRedirect(self.post_redirect)

Modified: cs/pythia/trunk/opal/controllers/UpdateController.py
===================================================================
--- cs/pythia/trunk/opal/controllers/UpdateController.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/controllers/UpdateController.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -32,8 +32,8 @@
 
     
     def newManipulator(self):
-        obj = self.view.obj
-        return self.model.ChangeManipulator(getattr(obj, obj._meta.pk.name), follow=self.follow)
+        model = self.view.model
+        return self.model.ChangeManipulator(getattr(model, model._meta.pk.name), follow=self.follow)
 
 
     def successMessage(self):
@@ -42,8 +42,8 @@
 
     def decorateResponse(self, response, request):
         super(UpdateController, self).decorateResponse(response, request)
-        obj = self.view.obj
-        populate_xheaders(request, response, self.model, getattr(obj, obj._meta.pk.name))
+        model = self.view.model
+        populate_xheaders(request, response, self.model, getattr(model, model._meta.pk.name))
         return
 
 

Modified: cs/pythia/trunk/opal/core/context_processors.py
===================================================================
--- cs/pythia/trunk/opal/core/context_processors.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/context_processors.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -43,7 +43,7 @@
     return context_extras
 
 def request(request):
-    return {'request': request}
+    return {'xrequest': request} # 'xrequest', because 'request' is always 'None'.  WTF?
 
 # PermWrapper and PermLookupDict proxy the permissions system into objects that
 # the template system can understand.

Modified: cs/pythia/trunk/opal/core/handlers/base.py
===================================================================
--- cs/pythia/trunk/opal/core/handlers/base.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/handlers/base.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -63,16 +63,17 @@
         site = settings
         resolver = site.urlResolver()
         try:
-            callback, callback_args, callback_kwargs = resolver.resolve(request.path)
+            responder = resolver.resolve(request)
 
-            # Apply view middleware
-            for middleware_method in self._view_middleware:
-                response = middleware_method(request, callback, callback_args, callback_kwargs)
-                if response:
-                    return response
+            if False: # There appears to be only one: XViewMiddleware
+                # Apply view middleware
+                for middleware_method in self._view_middleware:
+                    response = middleware_method(request, callback, callback_args, callback_kwargs)
+                    if response:
+                        return response
 
             try:
-                response = callback(request, *callback_args, **callback_kwargs)
+                response = responder.response(request)
             except Exception, e:
                 # If the view raised an exception, run it through exception
                 # middleware, and if the exception middleware returns a
@@ -83,13 +84,9 @@
                         return response
                 raise
 
-            # Complain if the view returned None (a common error).
+            # Complain if the responder returned None (a common error).
             if response is None:
-                try:
-                    view_name = callback.func_name # If it's a function
-                except AttributeError:
-                    view_name = callback.__class__.__name__ + '.__call__' # If it's a class
-                raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
+                raise ValueError, "The responder %s didn't return an HttpResponse object." % responder
 
             return response
         except http.Http404, e:

Added: cs/pythia/trunk/opal/core/responders.py
===================================================================
--- cs/pythia/trunk/opal/core/responders.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/responders.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#                      California Institute of Technology
+#                        (C) 2007  All Rights Reserved
+#
+# {LicenseText}
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+from opal import http
+
+
+class Responder(object):
+
+    def response(self, request):
+        raise NotImplemented
+
+    def isLeaf(self):
+        return not hasattr(self, 'subResponder')
+
+    def notFound(self):
+        raise http.Http404
+
+
+
+class ResponderChain(Responder):
+    
+    def __init__(self, *responders):
+        self.responders = responders
+
+    def response(self, request):
+        return self.responders[0].response(request)
+
+    def subResponder(self, name):
+        for responder in self.responders:
+            try:
+                return responder.subResponder(name)
+            except http.Http404:
+                pass
+        self.notFound()
+
+chain = ResponderChain
+
+
+
+class DirectoryResponder(Responder):
+
+    def __init__(self, index, dct):
+        self.index = index
+        self.dct = dct
+
+    def response(self, request):
+        return self.index.response(request)
+
+    def subResponder(self, name):
+        try:
+            responder = self.dct[name]
+        except KeyError:
+            self.notFound()
+        return responder
+
+directory = DirectoryResponder
+
+
+
+class QueryDirectoryResponder(Responder):
+
+    def __init__(self, Model, Inquirer, subResponderFactory):
+        self.Model = Model
+        self.Inquirer = Inquirer
+        self.subResponderFactory = subResponderFactory
+        return
+
+    def subResponder(self, name):
+        from opal.core.exceptions import ObjectDoesNotExist
+        query = self.newQuery(name)
+        try:
+            obj = query.get(self.Model._default_manager.all())
+        except ObjectDoesNotExist:
+            raise http.Http404("No %s found matching the query '%s'." % (self.Model._meta.verbose_name, query))
+        return self.newSubResponder(obj)
+
+    def newQuery(self, key):
+        return self.Inquirer.newQuery(key)
+   
+    def newSubResponder(self, obj):
+        return self.subResponderFactory(obj)
+
+queryDirectory = QueryDirectoryResponder
+
+
+
+class FunctionResponder(Responder):
+
+    def __init__(self, function, *args, **kwds):
+        self.function = function
+        self.args = args
+        self.kwds = kwds
+
+    def response(self, request):
+        return self.function(request, *self.args, **self.kwds)
+
+
+
+class MethodResponderContext(Responder):
+
+    def __init__(self, instance, method, *args, **kwds):
+        self.instance = instance
+        self.method = method
+        self.args = args
+        self.kwds = kwds
+
+    def response(self, request):
+        return self.method(self.instance, request, *self.args, **self.kwds)
+
+
+
+class MethodResponder(Responder):
+
+    def __init__(self, method, instance):
+        self.method = method
+        self.instance = instance
+
+    def response(self, request):
+        return self.method(self.instance, request)
+
+    def __call__(self, *args, **kwds):
+        return MethodResponderContext(self.instance, self.method, *args, **kwds)
+
+
+
+class ResponderMethodDescriptor(object):
+
+    def __init__(self, method):
+        self.method = method
+
+    def __get__(self, instance, cls=None):
+        return MethodResponder(self.method, instance)
+
+respondermethod = ResponderMethodDescriptor
+
+
+
+class Redirector(Responder):
+
+    def __init__(self, newUrl):
+        self.newUrl = newUrl
+
+    def response(self, request):
+        return http.HttpResponsePermanentRedirect(self.newUrl)
+
+
+
+# end of file

Modified: cs/pythia/trunk/opal/core/urlresolvers.py
===================================================================
--- cs/pythia/trunk/opal/core/urlresolvers.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/urlresolvers.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -7,11 +7,11 @@
     (view_function, function_args, function_kwargs)
 """
 
-from opal.http import Http404
+from opal import http
 from opal.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
 import re
 
-class Resolver404(Http404):
+class Resolver404(http.Http404):
     pass
 
 class NoReverseMatch(Exception):
@@ -226,22 +226,41 @@
     resolver = RegexURLResolver(r'^/', urlconf)
     return '/' + resolver.reverse(viewname, *args, **kwargs)
 
+
 class TreeURLResolver(object):
+
     def __init__(self, root):
         self.root = root
-    
-    def resolve(self, pathname):
-        #raise Resolver404, {'tried': ['foo', 'bar', path], 'path': path + 'hey'}
-        path = pathname.split('/')
-        args = ()
-        kwargs = {}
+
+
+    def resolve(self, request):
+        path = request.path.split('/')
         node = self.root
-        try:
-            for name in path[:-1]:
-                node = node.children[name]
-            name = path[-1]
-            if name != "":
-                node = node.children[name]
-        except KeyError:
-            raise Http404
-        return node.view().response, args, kwargs
+
+        tried = []
+        for name in path[1:-1]:
+            node = node.subResponder(name)
+            tried.append(node)
+        name = path[-1]
+        
+        hasTrailingSlash = (name == "")
+        if hasTrailingSlash:
+            if node.isLeaf():
+                raise Resolver404, {'tried': tried, 'path': request.path}
+        else:
+            node = node.subResponder(name)
+            # Don't redirect POST requests -- otherwise, the POST data would be lost!
+            if not node.isLeaf() and request.method != 'POST':
+                return self.redirect(request, request.path + '/')
+
+        return node
+
+
+    def redirect(self, request, newUrl):
+        from opal.core.responders import Redirector
+        host = http.get_host(request)
+        if host:
+            newUrl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', host, newUrl)
+        if request.GET:
+            newUrl += '?' + request.GET.urlencode()
+        return Redirector(newUrl)

Modified: cs/pythia/trunk/opal/db/models/manipulators.py
===================================================================
--- cs/pythia/trunk/opal/db/models/manipulators.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/db/models/manipulators.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -26,7 +26,8 @@
         self.base = base
 
     def __get__(self, instance, model=None):
-        if instance != None:
+        if instance != None and False:
+            # Why not?  2007-08-16 lcs
             raise AttributeError, "Manipulator cannot be accessed via instance"
         else:
             if not self.man:

Modified: cs/pythia/trunk/opal/sites/WebSite.py
===================================================================
--- cs/pythia/trunk/opal/sites/WebSite.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/sites/WebSite.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -158,7 +158,7 @@
         'opal.core.context_processors.auth',
         'opal.core.context_processors.debug',
         'opal.core.context_processors.i18n',
-        #'opal.core.context_processors.request',
+        'opal.core.context_processors.request',
         ])
     TEMPLATE_CONTEXT_PROCESSORS.meta['tip'] = """List of processors used by RequestContext to populate the context. Each one should be a callable that takes the request object as its only parameter and returns a dictionary to add to the context."""
 
@@ -175,7 +175,7 @@
     EMAIL_SUBJECT_PREFIX.meta['tip'] = """Subject-line prefix for email messages send with opal.core.mail.mail_admins or ...mail_managers.  Make sure to include the trailing space."""
 
     ### I hate this one.  It should depend upon the object being accessed.
-    APPEND_SLASH = pyre.bool("append-slash", default=True)
+    APPEND_SLASH = pyre.bool("append-slash", default=False)
     APPEND_SLASH.meta['tip'] = """Whether to append trailing slashes to URLs."""
 
     PREPEND_WWW = pyre.bool("prepend-www", default=False)
@@ -401,7 +401,10 @@
             
             self.ROOT_URLCONF = urlconf
         
-        return urlresolvers.RegexURLResolver(r'^/', self.ROOT_URLCONF)
+        #return urlresolvers.RegexURLResolver(r'^/', self.ROOT_URLCONF)
+        app = self.ROOT_URLCONF
+        root = app.rootResponder()
+        return urlresolvers.TreeURLResolver(root)
 
 
 # end of file

Modified: cs/pythia/trunk/opal/views/DetailView.py
===================================================================
--- cs/pythia/trunk/opal/views/DetailView.py	2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/views/DetailView.py	2007-08-17 02:54:31 UTC (rev 7839)
@@ -30,27 +30,15 @@
     defaultTemplateNameTag = "detail"
 
 
-    def __init__(self, model, query=None, template_name_field=None, **kwds):
+    def __init__(self, model, template_name_field=None, **kwds):
         View.__init__(self, model, **kwds)
-        self.query = query
-        self._obj = None
         self.template_name_field = template_name_field
         return
 
 
-    def _getObj(self):
-        if self._obj is None and self.query:
-            try:
-                self._obj = self.query.get(self.model._default_manager.all())
-            except ObjectDoesNotExist:
-                raise Http404("No %s found matching the query '%s'" % (self.model._meta.verbose_name, self.query))
-        return self._obj
-    obj = property(_getObj)
-
-
     def loadTemplate(self):
-        if self.obj and self.template_name_field:
-            template_name_list = [getattr(self.obj, self.template_name_field), self.template_name]
+        if self.model and self.template_name_field:
+            template_name_list = [getattr(self.model, self.template_name_field), self.template_name]
             t = self.template_loader.select_template(template_name_list)
         else:
             t = super(DetailView, self).loadTemplate()
@@ -59,8 +47,8 @@
 
     def contextDictionary(self, request):
         dct = super(DetailView, self).contextDictionary(request)
-        if self.obj:
-            dct[self.template_object_name] = self.obj
+        if self.model:
+            dct[self.template_object_name] = self.model
         return dct
 
 



More information about the cig-commits mailing list