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--