publisher/session revision

David Binger <dbinger-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Thu, 10 Feb 2005 20:52:21 -0500
Newsgroups gmane.comp.web.quixote.cvs
Message-ID <[email protected]>
--Apple-Mail-7-930522413
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed

nas: This gets rid of the the SessionPublisher class
and adds NullSessionManager.  It also rationalizes the hooks a
little.  Before, the publisher had too much knowledge about how the
session manager worked.  Now it just calls start_request(),
finish_successful_request(), and finish_failed_request().


--Apple-Mail-7-930522413
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	x-unix-mode=0644;
	name="patch1.txt"
Content-Disposition: attachment;
	filename=patch1.txt

Index: quixote/demo/altdemo.py
===================================================================
--- quixote/demo/altdemo.py	(revision 26058)
+++ quixote/demo/altdemo.py	(revision 26059)
@@ -20,7 +20,7 @@
 from quixote import get_user, get_session, get_session_manager, get_field
 from quixote.directory import Directory
 from quixote.html import href, htmltext
-from quixote.publish import SessionPublisher
+from quixote.publish import Publisher
 from quixote.session import Session, SessionManager
 from quixote.util import dump_request
 
@@ -156,9 +156,9 @@
 
 
 def create_publisher():
-    return SessionPublisher(RootDirectory(),
-                            SessionManager(session_class=DemoSession),
-                            display_exceptions='plain')
+    return Publisher(RootDirectory(),
+                     SessionManager(session_class=DemoSession),
+                     display_exceptions='plain')
 
 try:
     # If durus is installed, define a create_durus_publisher() that
@@ -198,8 +198,8 @@
             session_manager = PersistentSessionManager()
             connection.get_root()['session_manager'] = session_manager
             connection.commit()
-        return SessionPublisher(RootDirectory(),
-                                session_mgr=session_manager,
-                                display_exceptions='plain')
+        return Publisher(RootDirectory(),
+                         session_manager=session_manager,
+                         display_exceptions='plain')
 except ImportError:
     pass # durus not installed.
Index: quixote/publish.py
===================================================================
--- quixote/publish.py	(revision 26058)
+++ quixote/publish.py	(revision 26059)
@@ -60,6 +60,8 @@
         that acts like Directory._q_traverse.
       logger : DefaultLogger
         controls access log and error log behavior
+      session_manager : NullSessionManager
+         keeps track of sessions
       config : Config
         holds all configuration info for this application.  If the
         application doesn't provide values then default values
@@ -68,7 +70,8 @@
         the HTTP request currently being processed.
     """
 
-    def __init__(self, root_directory, logger=None, config=None, **kwargs):
+    def __init__(self, root_directory, logger=None, session_manager=None,
+                 config=None, **kwargs):
         global _publisher
         if config is None:
             self.config = Config(**kwargs)
@@ -83,6 +86,11 @@
                                         error_email=self.config.error_email)
         else:
             self.logger = logger
+        if session_manager is not None:
+            self.session_manager = session_manager
+        else:
+            from quixote.session import NullSessionManager
+            self.session_manager = NullSessionManager()
 
         if _publisher is not None:
             raise RuntimeError, "only one instance of Publisher allowed"
@@ -93,9 +101,11 @@
                 'Expected something with a _q_traverse method, got %r' %
                 root_directory)
         self.root_directory = root_directory
-
         self._request = None
 
+    def set_session_manager(self, session_manager):
+        self.session_manager = session_manager
+
     def log(self, msg):
         self.logger.log(msg)
 
@@ -105,10 +115,9 @@
         request.process_inputs()
 
     def start_request(self):
-        """Called at the start of each request.  Overridden by
-        SessionPublisher to handle session details.
+        """Called at the start of each request.
         """
-        pass
+        self.session_manager.start_request()
 
     def _set_request(self, request):
         """Set the current request object.
@@ -126,9 +135,9 @@
         return self._request
 
     def finish_successful_request(self):
-        """Called at the end of a successful request.  Overridden by
-        SessionPublisher to handle session details."""
-        pass
+        """Called at the end of a successful request.
+        """
+        self.session_manager.finish_successful_request()
 
     def format_publish_error(self, exc):
         return format_publish_error(exc)
@@ -144,7 +153,9 @@
             exc.private_msg = None # hide it
         request = get_request()
         request.response = HTTPResponse(status=exc.status_code)
-        return self.format_publish_error(exc)
+        output = self.format_publish_error(exc)
+        self.session_manager.finish_successful_request()
+        return output
 
     def finish_failed_request(self):
         """
@@ -187,8 +198,8 @@
             user_error_msg = plain_error_msg
 
         self.logger.log_internal_error(error_summary, plain_error_msg)
-
         request.response.set_status(500)
+        self.session_manager.finish_failed_request()
         return user_error_msg
 
 
@@ -283,59 +294,6 @@
         return request.response
 
 
-class SessionPublisher(Publisher):
-
-    def __init__(self, root_directory, session_mgr=None, **kwargs):
-        from quixote.session import SessionManager
-        Publisher.__init__(self, root_directory, **kwargs)
-        if session_mgr is None:
-            self.session_mgr = SessionManager()
-        else:
-            self.session_mgr = session_mgr
-
-    def set_session_manager(self, session_mgr):
-        self.session_mgr = session_mgr
-
-    def start_request(self):
-        # Get the session object and stick it onto the request
-        request = get_request()
-        request.session = self.session_mgr.get_session()
-        request.session.start_request()
-
-    def finish_successful_request(self):
-        session = get_session()
-        if session is not None:
-            session.finish_request()
-            self.session_mgr.maintain_session(session)
-        self.session_mgr.commit_changes(session)
-
-    def finish_interrupted_request(self, exc):
-        output = Publisher.finish_interrupted_request(self, exc)
-        # commit the current transaction so that any changes to the
-        # session objects are saved and are visible on the next HTTP
-        # hit.  Remember, AccessError is a subclass of PublishError,
-        # so this code will be run for both typos in the URL and for
-        # the user not being logged in.
-        #
-        # The assumption here is that the UI code won't make changes
-        # to the core database before checking permissions and raising
-        # a PublishError; if you must do this (though it's hard to see
-        # why this would be necessary), you'll have to abort the
-        # current transaction, make your session changes, and then
-        # raise the PublishError.
-        session = get_session()
-        if session is not None:
-            session.finish_request()
-            self.session_mgr.maintain_session(session)
-        self.session_mgr.commit_changes(session)
-        return output
-
-    def finish_failed_request(self):
-        if self.session_mgr:
-            self.session_mgr.abort_changes(get_session())
-        return Publisher.finish_failed_request(self)
-
-
 # Publisher singleton, only one of these per process.
 _publisher = None
 
@@ -373,7 +331,7 @@
     return _publisher.get_request().session
 
 def get_session_manager():
-    return _publisher.session_mgr
+    return _publisher.session_manager
 
 def get_user():
     session = _publisher.get_request().session
Index: quixote/doc/session-mgmt.txt
===================================================================
--- quixote/doc/session-mgmt.txt	(revision 26058)
+++ quixote/doc/session-mgmt.txt	(revision 26059)
@@ -13,11 +13,11 @@
 
 HTTP cookies were invented to address this requirement, and they are
 still the best solution for establishing sessions on top of HTTP.  Thus,
-Quixote's session management mechanism is cookie-based.  (The most
-common alternative is to generate long, complicated URLs with an
-embedded session identifier.  Since Quixote views the URL as a
-fundamental part of the web user interface, a URL-based session
-management scheme would be un-Quixotic.)
+the session management mechanism that comes with Quixote is
+cookie-based.  (The most common alternative is to embed the session
+identifier in the URL.  Since Quixote views the URL as a fundamental
+part of the web user interface, a URL-based session management scheme is
+considered un-Quixotic.)
 
 For further reading: the standard for cookies that is approximately
 implemented by most current browsers is RFC 2109; the latest version of
@@ -303,7 +303,7 @@
 
   import shelve
   sessions = shelve.open("/tmp/quixote-sessions")
-  session_mgr = SessionManager(session_mapping=sessions)
+  session_manager = SessionManager(session_mapping=sessions)
 
 If you use one of these relatively simple persistent mapping types,
 you'll also need to override ``is_dirty()`` in your Session class.
Index: quixote/doc/upgrading.txt
===================================================================
--- quixote/doc/upgrading.txt	(revision 26058)
+++ quixote/doc/upgrading.txt	(revision 26059)
@@ -69,6 +69,10 @@
 The Form.__init__ keyword parameter (and attribute) 'action_url' is now 
 named 'action'.
 
+The SessionPublisher class is gone.  Use the Publisher class instead.
+Also, the 'session_mgr' keyword has been renamed to 'session_manager'.
+
+
 Changes from 0.6.1 to 1.0
 -------------------------
 
Index: quixote/session.py
===================================================================
--- quixote/session.py	(revision 26058)
+++ quixote/session.py	(revision 26059)
@@ -22,10 +22,33 @@
 
 from time import time, localtime, strftime
 
-from quixote import get_publisher, get_cookie, get_response, get_request
+from quixote import get_publisher, get_cookie, get_response, get_request, \
+     get_session
 from quixote.errors import SessionError
 from quixote.util import randbytes
 
+class NullSessionManager:
+    """A session manager that does nothing.  It is the default session manager.
+    """
+
+    def start_request(self):
+        """
+        Called near the beginning of each request: after the HTTPRequest
+        object has been built, but before we traverse the URL or call the
+        callable object found by URL traversal.
+        """
+
+    def finish_successful_request(self):
+        """Called near the end of each successful request.  Not called if
+        there were any errors processing the request.
+        """
+
+    def finish_failed_request(self):
+        """Called near the end of a failed request (i.e. a exception that was
+        not a PublisherError was raised.
+        """
+
+
 class SessionManager:
     """
     SessionManager acts as a dictionary of all sessions, mapping session
@@ -56,10 +79,7 @@
         """(session_class : class = Session, session_mapping : mapping = None)
 
         Create a new session manager.  There should be one session
-        manager per publisher (really SessionPublisher), ie. one
-        per process.  Note that SessionPublisher's constructor will
-        take care of creating a session manager for you if you don't
-        do it yourself.
+        manager per publisher, ie. one per process
 
         session_class is used by the new_session() method -- it returns
         an instance of session_class.
@@ -174,7 +194,7 @@
 
         Placeholder for subclasses that implement transactional
         persistence: forget about saving changes to the current
-        session.  Called by SessionPublisher when a request fails,
+        session.  Called by the publisher when a request fails,
         ie. when it catches an exception other than PublishError.
         """
         pass
@@ -184,7 +204,7 @@
 
         Placeholder for subclasses that implement transactional
         persistence: commit changes to the current session.  Called by
-        SessionPublisher when a request completes successfully, or is
+        the publisher when a request completes successfully, or is
         interrupted by a PublishError exception.
         """
         pass
@@ -278,12 +298,11 @@
     def maintain_session(self, session):
         """(session : Session)
 
-        Maintain session information.  This method is called by
-        SessionPublisher after servicing an HTTP request, just before
-        the response is returned.  If a session contains information it
-        is saved and a cookie dropped on the client.  If not, the
-        session is discarded and the client will be instructed to delete
-        the session cookie (if any).
+        Maintain session information.  This method is called after servicing
+        an HTTP request, just before the response is returned.  If a session
+        contains information it is saved and a cookie dropped on the client.
+        If not, the session is discarded and the client will be instructed
+        to delete the session cookie (if any).
         """
         if not session.has_info():
             # Session has no useful info -- forget it.  If it previously
@@ -377,7 +396,34 @@
         else:
             return True
 
+    # -- Hooks into the Quixote main loop ------------------------------
 
+    def start_request(self):
+        """
+        Called near the beginning of each request: after the HTTPRequest
+        object has been built, but before we traverse the URL or call the
+        callable object found by URL traversal.
+        """
+        session = self.get_session()
+        get_request().session = session
+        session.start_request()
+
+    def finish_successful_request(self):
+        """Called near the end of each successful request.  Not called if
+        there were any errors processing the request.
+        """
+        session = get_session()
+        if session is not None:
+            self.maintain_session(session)
+        self.commit_changes(session)
+
+    def finish_failed_request(self):
+        """Called near the end of a failed request (i.e. a exception that was
+        not a PublisherError was raised.
+        """
+        self.abort_changes(get_session())
+
+
 class Session:
     """
     Holds information about the current session.  The only information
@@ -464,28 +510,13 @@
         file.write('  created %s, last accessed %s' % (ctime, atime))
         file.write('  _form_tokens: %s\n' % self._form_tokens)
 
-
-    # -- Hooks into the Quixote main loop ------------------------------
-
     def start_request(self):
         """
         Called near the beginning of each request: after the HTTPRequest
-        object has been built and this Session object has been fetched
-        or built, but before we traverse the URL or call the callable
-        object found by URL traversal.
+        object has been built, but before we traverse the URL or call the
+        callable object found by URL traversal.
         """
-        if self.user:
-            get_request().environ['REMOTE_USER'] = str(self.user)
 
-    def finish_request(self):
-        """
-        Called near the end of each request: after a callable object has
-        been found and (successfully) called.  Not called if there were
-        any errors processing the request.
-        """
-        pass
-
-
     # -- Simple accessors and modifiers --------------------------------
 
     def set_user(self, user):
@@ -558,5 +589,3 @@
         Remove 'token' from the queue of outstanding tokens.
         """
         self._form_tokens.remove(token)
-
-

--Apple-Mail-7-930522413
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Quixote-checkins mailing list
Quixote-checkins-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]
http://mail.mems-exchange.org/mailman/listinfo/quixote-checkins

--Apple-Mail-7-930522413--