SVN: r25399 - in trunk/quixote: . demo server

Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Wed, 20 Oct 2004 16:51:21 -0400
Newsgroups gmane.comp.web.quixote.cvs
Message-ID <[email protected]>
Author: nascheme
Date: 2004-10-20 16:50:34 -0400 (Wed, 20 Oct 2004)
New Revision: 25399

Added:
   trunk/quixote/logger.py
Removed:
   trunk/quixote/demo/demo.conf
Modified:
   trunk/quixote/config.py
   trunk/quixote/demo/demo.cgi
   trunk/quixote/demo/demo_scgi.py
   trunk/quixote/demo/session_demo.cgi
   trunk/quixote/demo/upload.cgi
   trunk/quixote/mod_python_handler.py
   trunk/quixote/publish.py
   trunk/quixote/server/medusa_http.py
   trunk/quixote/server/twisted_http.py
Log:
Split error log and access log functionality into a separate class.
Try to simplify site configuration.  Configuration options can now
be passed as keyword args to the Publisher class.  The setup_logs()
and shutdown_logs() methods are gone.  Logs are now opened when the
publisher is initialized.


Modified: trunk/quixote/config.py
===================================================================
--- trunk/quixote/config.py	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/config.py	2004-10-20 20:50:34 UTC (rev 25399)
@@ -12,19 +12,17 @@
 care what happens in the future.
 """
 
-__revision__ = "$Id$"
 
-
 # Note that the default values here are geared towards a production
 # environment, preferring security and performance over verbosity and
 # debug-ability.  If you just want to get a Quixote application
 # up-and-running in a production environment, these settings are mostly
 # right; all you really need to customize are ERROR_EMAIL, and ERROR_LOG.
 # If you need to test/debug/develop a Quixote application, though, you'll
-# probably want to also change DISPLAY_EXCEPTIONS, SECURE_ERRORS, and
-# maybe RUN_ONCE.  Again, you shouldn't edit this file unless you don't
-# care what happens in the future (in particular, an upgrade to Quixote
-# would clobber your edits).
+# probably want to also change DISPLAY_EXCEPTIONS and maybe RUN_ONCE.
+# Again, you shouldn't edit this file unless you don't care what happens
+# in the future (in particular, an upgrade to Quixote would clobber your
+# edits).
 
 
 # E-mail address to send application errors to; None to send no mail at
@@ -49,31 +47,21 @@
 # the local variables and a few lines of context for each level of the
 # traceback.  If set to None, a generic error display, containing no
 # information about the traceback, will be used.
-#
-# It is convenient to enable this on development servers.  On publicly
-# accessible servers it should be disabled for security reasons.
-#
-# (For backward compatibility reasons, 0 and 1 are also legal values
-# for this setting.)
 DISPLAY_EXCEPTIONS = None
 
-# If true, then any "resource not found" errors will result in a
-# consistent, terse, mostly-useless message.  If false, then the
-# exact cause of failure will be returned.
-SECURE_ERRORS = True
-
 # If true, Quixote will service exactly one request at a time, and
 # then exit.  This makes no difference when you're running as a
 # straight CGI script, but it makes it easier to debug while running
 # as a FastCGI script.
 RUN_ONCE = False
 
-# Automatically redirect paths referencing non-callable objects to a path
-# with a trailing slash.  This is convienent for external users of the
-# site but should be disabled for development.  Internal links on the
-# site should not require redirects.  They are costly, especially on high
-# latency links like dialup lines.
-FIX_TRAILING_SLASH = True
+# Automatically redirect paths referencing non-callable objects to a
+# path with a trailing slash.  This should be disabled for development
+# sites.  Internal links on the site should not require redirects.  They
+# are costly, especially on high latency links like dialup lines.  For
+# the convenience of users, you probably want to set this to True on
+# public sites.
+FIX_TRAILING_SLASH = False
 
 # Compress large pages using gzip if the client accepts that encoding.
 COMPRESS_PAGES = False
@@ -140,28 +128,6 @@
 # -- End config variables ----------------------------------------------
 # (no user serviceable parts after this point)
 
-# Note that this module is designed to not export any names apart from
-# the above config variables and the following Config class -- hence,
-# all imports are done in local scopes.  This allows application config
-# modules to safely say "from quixote.config import *".
-
-class ConfigError(Exception):
-
-    def __init__(self, msg, source=None, var=None):
-        self.msg = msg
-        self.source = source
-        self.var = var
-
-    def __str__(self):
-        chunks = []
-        if self.source:
-            chunks.append(self.source)
-        if self.var:
-            chunks.append(self.var)
-        chunks.append(self.msg)
-        return ": ".join(chunks)
-
-
 class Config:
     """Holds all Quixote configuration variables -- see above for
     documentation of them.  The naming convention is simple:
@@ -188,19 +154,13 @@
         'mail_debug_addr',
         ]
 
+    def __init__(self, **kwargs):
+        self.set_from_dict(globals()) # set defaults
+        for name, value in kwargs.items():
+            if name not in self.config_vars:
+                raise ValueError('unknown config variable %r' % name)
+            setattr(self, name, value)
 
-    def __init__(self, read_defaults=True):
-        for var in self.config_vars:
-            setattr(self, var, None)
-        if read_defaults:
-            self.read_defaults()
-
-    def __setattr__(self, attr, val):
-        if not attr in self.config_vars:
-            raise AttributeError, "no such configuration variable: %s" % `attr`
-        self.__dict__[attr] = val
-
-
     def dump(self, file=None):
         import sys
         if file is None:
@@ -212,39 +172,14 @@
         for var in self.config_vars:
             file.write("  %s = %s\n" % (var, `getattr(self, var)`))
 
+    def set_from_dict(self, config_vars):
+        for name, value in config_vars.items():
+            if name.isupper():
+                name = name.lower()
+                if name not in self.config_vars:
+                    raise ValueError('unknown config variable %r' % name)
+                setattr(self, name, value)
 
-    def set_from_dict(self, dict, source=None):
-        import string, re
-        ucstring_re = re.compile(r'^[A-Z_]+$')
-
-        for (var, val) in dict.items():
-            if ucstring_re.match(var):
-                setattr(self, string.lower(var), val)
-
-        self.check_values(source)
-
-    def check_values(self, source):
-        """
-        check_values(source : string)
-
-        Check the configuration variables to ensure that they
-        are all valid.  Raise ConfigError with 'source' as the
-        second argument if any problems are found.
-        """
-        # Check value of DISPLAY_EXCEPTIONS.  Values that are
-        # equivalent to 'false' are set to None; a value of 1
-        # is changed to 'plain'.
-        if not self.display_exceptions:
-            self.display_exceptions = None
-        elif self.display_exceptions == 1:
-            self.display_exceptions = 'plain'
-        if self.display_exceptions not in (None, 'plain', 'html'):
-            raise ConfigError("Must be None,"
-                              " 'plain', or 'html'",
-                              source,
-                              "DISPLAY_EXCEPTIONS")
-
-
     def read_file(self, filename):
         """Read configuration from a file.  Any variables already
         defined in this Config instance, but not in the file, are
@@ -259,21 +194,4 @@
             if exc.filename is None:    # arg! execfile() loses filename
                 exc.filename = filename
             raise exc
-
-        self.set_from_dict(config_vars, source=filename)
-
-    def read_from_module(self, modname):
-        """Read configuration info from a Python module (default
-        is the module where the Config class is defined, ie.
-        quixote.config).  Also accumulates config data, just like
-        'read_file()'.
-        """
-        import sys
-        __import__(modname)
-        module = sys.modules[modname]
-        self.set_from_dict(vars(module), source=module.__file__)
-
-    def read_defaults(self):
-        self.read_from_module("quixote.config")
-
-# class Config
+        self.set_from_dict(config_vars)

Modified: trunk/quixote/demo/demo.cgi
===================================================================
--- trunk/quixote/demo/demo.cgi	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/demo/demo.cgi	2004-10-20 20:50:34 UTC (rev 25399)
@@ -9,13 +9,7 @@
 enable_ptl()
 
 # Create a Publisher instance 
-app = Publisher('quixote.demo')
+app = Publisher('quixote.demo', display_exceptions='plain')
 
-# (Optional step) Read a configuration file
-app.read_config("demo.conf")
-
-# Open the configured log files
-app.setup_logs()
-
 # Enter the publishing main loop
 app.publish_cgi()

Deleted: trunk/quixote/demo/demo.conf
===================================================================
--- trunk/quixote/demo/demo.conf	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/demo/demo.conf	2004-10-20 20:50:34 UTC (rev 25399)
@@ -1,9 +0,0 @@
-# Config file for the Quixote demo.  This ensures that debug and error
-# messages will be logged, and that you will see full error information
-# in your browser.  (The default settings shipped in Quixote's config.py
-# module are for security rather than ease of testing/development.)
-
-ERROR_LOG = "/tmp/quixote-demo-error.log"
-DISPLAY_EXCEPTIONS = "plain"
-SECURE_ERRORS = 0
-FIX_TRAILING_SLASH = 0

Modified: trunk/quixote/demo/demo_scgi.py
===================================================================
--- trunk/quixote/demo/demo_scgi.py	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/demo/demo_scgi.py	2004-10-20 20:50:34 UTC (rev 25399)
@@ -14,16 +14,10 @@
 from quixote import enable_ptl, Publisher
 
 class DemoPublisher(Publisher):
-    def __init__(self, *args, **kwargs):
-        Publisher.__init__(self, *args, **kwargs)
+    def __init__(self, *args):
+        Publisher.__init__(self, *args, display_exceptions='plain')
 
-        # (Optional step) Read a configuration file
-        self.read_config("demo.conf")
 
-        # Open the configured log files
-        self.setup_logs()
-
-
 class DemoHandler(QuixoteHandler):
     publisher_class = DemoPublisher
     root_namespace = "quixote.demo"

Modified: trunk/quixote/demo/session_demo.cgi
===================================================================
--- trunk/quixote/demo/session_demo.cgi	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/demo/session_demo.cgi	2004-10-20 20:50:34 UTC (rev 25399)
@@ -155,8 +155,7 @@
 # This is mostly the same as the standard boilerplate for any Quixote
 # driver script.  The main difference is that we have to instantiate a
 # session manager, and use SessionPublisher instead of the normal
-# Publisher class.  Just like demo.cgi, we use demo.conf to setup log
-# files and ensure that error messages are more informative than secure.
+# Publisher class.
 
 # You can use the 'shelve' module to create an alternative persistent
 # mapping to the DirMapping class above.
@@ -167,7 +166,6 @@
 sessions = DirMapping(save_dir="/tmp/quixote-session-demo")
 session_mgr = SessionManager(session_class=DemoSession,
                              session_mapping=sessions)
-app = SessionPublisher('quixote.demo.session', session_mgr=session_mgr)
-app.read_config("demo.conf")
-app.setup_logs()
+app = SessionPublisher('quixote.demo.session', session_mgr=session_mgr,
+                       display_exceptions='plain')
 app.publish_cgi()

Modified: trunk/quixote/demo/upload.cgi
===================================================================
--- trunk/quixote/demo/upload.cgi	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/demo/upload.cgi	2004-10-20 20:50:34 UTC (rev 25399)
@@ -55,10 +55,7 @@
     return header(title) + "\n".join(result) + footer()
 
 def main ():
-    pub = Publisher('__main__')
-    pub.read_config("demo.conf")
-    pub.configure(UPLOAD_DIR="/tmp/quixote-upload-demo")
-    pub.setup_logs()
+    pub = Publisher('__main__', display_exceptions='plain')
     pub.publish_cgi()
 
 main()

Added: trunk/quixote/logger.py
===================================================================
--- trunk/quixote/logger.py	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/logger.py	2004-10-20 20:50:34 UTC (rev 25399)
@@ -0,0 +1,91 @@
+"""$URL$
+$Id$
+"""
+import sys
+import os
+import time
+import socket
+from quixote.sendmail import sendmail
+
+class DefaultLogger:
+    """
+    This is the default logger object used by the Quixote publisher.  You may
+    provide your own object if you wish to have different behavior.
+
+    Instance attributes:
+
+      access_log : file | None
+        file to which every access will be logged.  If None then access
+        is not logged.
+      error_log : file
+        file to which application errors (exceptions caught by Quixote,
+        as well as anything printed to stderr by application code) will
+        be logged.  Set to sys.stderr by default.
+      error_email : string | None
+        if set then internal server errors will cause messages to be sent to
+        this address
+    """
+    def __init__(self, access_log=None, error_log=None, error_email=None):
+        if access_log:
+            self.access_log = open(access_log, 'a', 1)
+        else:
+            self.access_log = None
+        if error_log is None:
+            self.error_log = sys.stdout
+        else:
+            self.error_log = open(error_log, 'a', 1)
+        self.error_email = error_email
+        sys.stdout = self.error_log # print is handy for debugging
+
+    def log(self, msg):
+        """
+        Write an message to the error log with a time stamp.
+        """
+        timestamp = time.strftime("%Y-%m-%d %H:%M:%S",
+                                  time.localtime(time.time()))
+        self.error_log.write("[%s] %s\n" % (timestamp, msg))
+
+    def log_internal_error(self, error_summary, error_msg):
+        """(error_summary: str, error_msg: str)
+
+        error_summary is a single line summary of the internal error, suitable
+        for an email subject.  error_msg is a multi-line plaintext message
+        describing the error in detail.
+        """
+        self.log("exception caught")
+        self.error_log.write(error_msg)
+        if self.error_email:
+            sendmail('Quixote Traceback (%s)' % error_summary,
+                     error_msg, [self.error_email],
+                     from_addr=(self.error_email, socket.gethostname()))
+
+    def log_request(self, request, start_time):
+        """Log a request in the access_log file.
+        """
+        if self.access_log is None:
+            return
+        if request.session:
+            user = request.session.user or "-"
+        else:
+            user = "-"
+        now = time.time()
+        seconds = now - start_time
+        timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now))
+
+        request_uri = request.get_path()
+        query = request.get_query()
+        if query:
+            request_uri += "?" + query
+        proto = request.get_environ('SERVER_PROTOCOL')
+        self.access_log.write('%s %s %s %d "%s %s %s" %s %r %0.2fsec\n' %
+                               (request.get_environ('REMOTE_ADDR'),
+                                user,
+                                timestamp,
+                                os.getpid(),
+                                request.get_method(),
+                                request_uri,
+                                proto,
+                                request.response.status_code,
+                                request.get_environ('HTTP_USER_AGENT', ''),
+                                seconds
+                               ))


Property changes on: trunk/quixote/logger.py
___________________________________________________________________
Name: svn:keywords
   + HeadURL Id

Modified: trunk/quixote/mod_python_handler.py
===================================================================
--- trunk/quixote/mod_python_handler.py	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/mod_python_handler.py	2004-10-20 20:50:34 UTC (rev 25399)
@@ -22,14 +22,14 @@
         pass
 
 class ModPythonPublisher(Publisher):
-    def __init__(self, package, config=None):
-        Publisher.__init__(self, package, config)
-        self.error_log = self.__error_log = ErrorLog(self) # may be overwritten
-        self.setup_logs()
+    def __init__(self, package, **kwargs):
+        Publisher.__init__(self, package, **kwargs)
+        # may be overwritten
+        self.logger.error_log = self.__error_log = ErrorLog(self)
         self.__apache_request = None
 
     def log(self, msg):
-        if self.error_log is self.__error_log:
+        if self.logger.error_log is self.__error_log:
             try:
                 self.__apache_request.log_error(msg)
             except AttributeError:
@@ -76,6 +76,6 @@
 
     pub = name2publisher.get(package)
     if pub is None:
-        pub = ModPythonPublisher(package, config)
+        pub = ModPythonPublisher(package, config=config)
         name2publisher[package] = pub
     return pub.publish_modpython(req)

Modified: trunk/quixote/publish.py
===================================================================
--- trunk/quixote/publish.py	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/publish.py	2004-10-20 20:50:34 UTC (rev 25399)
@@ -8,7 +8,7 @@
 __revision__ = "$Id$"
 
 import sys, os, traceback, cStringIO
-import time, types, socket, re, warnings
+import time, types, re, warnings
 import struct
 import urlparse
 try:
@@ -19,9 +19,10 @@
 from quixote import errors
 from quixote.html import htmltext
 from quixote import util
+from quixote.config import Config
 from quixote.http_request import HTTPRequest
 from quixote.http_response import HTTPResponse, Stream
-from quixote.sendmail import sendmail
+from quixote.logger import DefaultLogger
 
 try:
     import cgitb                        # Only available in Python 2.2
@@ -76,29 +77,34 @@
       root_namespace : module | instance | class
         the Python namespace that will be searched for objects to
         fulfill each HTTP request
+      logger : DefaultLogger
       exit_now : boolean
         used for internal state management.  If true, the loop in
         publish_cgi() will terminate at the end of the current request.
-      access_log : file
-        file to which every access will be logged; set by
-        setup_logs() (None if no access log)
-      error_log : file
-        file to which application errors (exceptions caught by Quixote,
-        as well as anything printed to stderr by application code) will
-        be logged; set by setup_logs().  Set to sys.stderr if no
-        ERROR_LOG setting in the application config file.
       config : Config
         holds all configuration info for this application.  If the
-        application doesn't have a config file, uses the default values
-        from the quixote.config module.
+        application doesn't provide values then default values
+        from the quixote.config module are used.
       _request : HTTPRequest
         the HTTP request currently being processed.
       namespace_stack : [ module | instance | class ]
     """
 
-    def __init__(self, root_namespace, config=None):
-        from quixote.config import Config
+    def __init__(self, root_namespace, logger=None, config=None, **kwargs):
         global _publisher
+        if config is None:
+            self.config = Config(**kwargs)
+        else:
+            if kwargs:
+                raise ValueError("cannot provide both 'config' object and"
+                                 " config arguments")
+            self.config = config
+        if logger is None:
+            self.logger = DefaultLogger(error_log=self.config.error_log,
+                                        access_log=self.config.access_log,
+                                        error_email=self.config.error_email)
+        else:
+            self.logger = logger
 
         if _publisher is not None:
             raise RuntimeError, "only one instance of Publisher allowed"
@@ -118,82 +124,10 @@
         self.namespace_stack = [self.root_namespace]
 
         self.exit_now = False
-        self.access_log = None
-        self.error_log = sys.stderr     # possibly overridden in setup_logs()
-        sys.stdout = self.error_log     # print is handy for debugging
-
-        # Initialize default config object with all the default values from
-        # the config variables at the top of the config module, ie. if
-        # ERROR_LOG is set to "/var/log/quxiote-error.log", then
-        # config.ERROR_LOG will also be "/var/log/quixote-error.log".  If
-        # application FCGI/CGI scripts need to override any of these
-        # defaults, they can do so by direct manipulation of the config
-        # object, or by reading a config file:
-        #   app.read_config("myapp.conf")
-        if config is None:
-            self.config = Config()
-        else:
-            self.set_config(config)
-
         self._request = None
 
-    def configure(self, **kwargs):
-        self.config.set_from_dict(kwargs)
-
-    def read_config(self, filename):
-        self.config.read_file(filename)
-
-    def set_config(self, config):
-        from quixote.config import Config
-        if not isinstance(config, Config):
-            raise TypeError, "'config' must be a Config instance"
-        self.config = config
-
-    def setup_logs(self):
-        """
-         Open all log files specified in the config file. Reassign
-        sys.stderr to go to the error log, and sys.stdout to go to
-        the debug log.
-        """
-
-        sys.stdout = sys.stderr
-
-        if self.config.access_log is not None:
-            try:
-                self.access_log = open(self.config.access_log, 'a', 1)
-            except IOError, exc:
-                sys.stderr.write("error opening access log %s: %s\n"
-                                 % (`self.config.access_log`, exc.strerror))
-
-        if self.config.error_log is not None:
-            try:
-                self.error_log = open(self.config.error_log, 'a', 1)
-                sys.stdout = sys.stderr = self.error_log
-            except IOError, exc:
-                # leave self.error_log as it was, most likely sys.stderr
-                sys.stderr.write("error opening error log %s: %s\n"
-                                 % (`self.config.error_log`, exc.strerror))
-        
-    def shutdown_logs(self):
-        """
-        Close log files and restore sys.stdout and sys.stderr to their
-        original values.
-        """
-        if sys.stdout is sys.__stdout__:
-            raise RuntimeError, "'setup_logs()' never called"
-        sys.stdout = sys.__stdout__
-        self.access_log.close()
-        if self.error_log is not sys.__stderr__:
-            self.error_log.close()
-            sys.stderr = sys.__stderr__
-
     def log(self, msg):
-        """
-        Write an message to the error log with a time stamp.
-        """
-        timestamp = time.strftime("%Y-%m-%d %H:%M:%S",
-                                  time.localtime(time.time()))
-        self.error_log.write("[%s] %s\n" % (timestamp, msg))
+        self.logger.log(msg)
 
     def parse_request(self, request):
         """Parse the request information waiting in 'request'.
@@ -221,38 +155,6 @@
         """
         return self._request
 
-    def log_request(self, request, start_time):
-        """Log a request in the access_log file.
-        """
-        if self.access_log is not None:
-            if request.session:
-                user = request.session.user or "-"
-            else:
-                user = "-"
-            now = time.time()
-            seconds = now - start_time
-            timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now))
-
-            request_uri = request.get_path()
-            query = request.get_query()
-            if query:
-                request_uri += "?" + query
-            proto = request.get_environ('SERVER_PROTOCOL')
-
-            self.access_log.write('%s %s %s %d "%s %s %s" %s %r %0.2fsec\n' %
-                                   (request.get_environ('REMOTE_ADDR'),
-                                    user,
-                                    timestamp,
-                                    os.getpid(),
-                                    request.get_method(),
-                                    request_uri,
-                                    proto,
-                                    request.response.status_code,
-                                    request.get_environ('HTTP_USER_AGENT', ''),
-                                    seconds
-                                   ))
-
-
     def finish_successful_request(self):
         """Called at the end of a successful request.  Overridden by
         SessionPublisher to handle session details."""
@@ -283,7 +185,7 @@
         # set response status code so every custom doesn't have to do it
         request.response.set_status(exc.status_code)
 
-        if self.config.secure_errors and exc.private_msg:
+        if not self.config.display_exceptions and exc.private_msg:
             exc.private_msg = None # hide it
 
         # walk up stack and find handler for the exception
@@ -345,12 +247,8 @@
             request.response.set_header("Content-Type", "text/plain")
             user_error_msg = plain_error_msg
 
-        self.log("exception caught")
-        self.error_log.write(plain_error_msg)
+        self.logger.log_internal_error(error_summary, plain_error_msg)
 
-        if self.config.error_email:
-            self.mail_error(plain_error_msg, error_summary)
-
         request.response.set_status(500)
         return user_error_msg
 
@@ -389,12 +287,6 @@
         return error_file.getvalue()
 
 
-    def mail_error(self, msg, error_summary):
-        """Send an email notifying someone of a traceback."""
-        sendmail('Quixote Traceback (%s)' % error_summary,
-                 msg, [self.config.error_email],
-                 from_addr=(self.config.error_email, socket.gethostname()))
-
     def get_namespace_stack(self):
         """get_namespace_stack() ->  [ module | instance | class ]
         """
@@ -505,7 +397,7 @@
             # Some other exception, generate error messages to the logs, etc.
             output = self.finish_failed_request()
         output = self.filter_output(request, output)
-        self.log_request(request, start_time)
+        self.logger.log_request(request, start_time)
         return output
 
     def publish(self, stdin, stdout, stderr, env):
@@ -562,9 +454,9 @@
 
 class SessionPublisher(Publisher):
 
-    def __init__(self, root_namespace, config=None, session_mgr=None):
+    def __init__(self, root_namespace, session_mgr=None, **kwargs):
         from quixote.session import SessionManager
-        Publisher.__init__(self, root_namespace, config)
+        Publisher.__init__(self, root_namespace, **kwargs)
         if session_mgr is None:
             self.session_mgr = SessionManager()
         else:

Modified: trunk/quixote/server/medusa_http.py
===================================================================
--- trunk/quixote/server/medusa_http.py	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/server/medusa_http.py	2004-10-20 20:50:34 UTC (rev 25399)
@@ -133,12 +133,7 @@
         port = 8080
     print 'Now serving the Quixote demo on port %d' % port
     server = http_server.http_server('', port)
-    publisher = Publisher('quixote.demo')
-
-    # When initializing the Publisher in your own driver script,
-    # you'll want to parse a configuration file.
-    ##publisher.read_config("/full/path/to/demo.conf")
-    publisher.setup_logs()
+    publisher = Publisher('quixote.demo', display_exceptions='plain')
     dh = QuixoteHandler(publisher, 'Quixote/demo', server)
     server.install_handler(dh)
     asyncore.loop()

Modified: trunk/quixote/server/twisted_http.py
===================================================================
--- trunk/quixote/server/twisted_http.py	2004-10-20 18:37:21 UTC (rev 25398)
+++ trunk/quixote/server/twisted_http.py	2004-10-20 20:50:34 UTC (rev 25399)
@@ -239,7 +239,7 @@
         return p
 
 
-def Server(namespace, http_port):
+def Server(namespace, http_port, **kwargs):
     from twisted.internet import reactor
     from quixote.publish import Publisher
 
@@ -255,8 +255,7 @@
     ##        ctx.use_privatekey_file('/path/to/pem/encoded/ssl_key_file')
     ##        return ctx
 
-    publisher = Publisher(namespace)
-    ##publisher.setup_logs()
+    publisher = Publisher(namespace, **kwargs)
     qf = QuixoteFactory(publisher)
 
     reactor.listenTCP(http_port, qf)
@@ -265,12 +264,12 @@
     return reactor
 
 
-def run(namespace, port):
-    app = Server(namespace, port)
+def run(namespace, port, **kwargs):
+    app = Server(namespace, port, **kwargs)
     app.run()
 
 
 if __name__ == '__main__':
     from quixote import enable_ptl
     enable_ptl()
-    run('quixote.demo', 8080)
+    run('quixote.demo', 8080, display_exceptions='plain')