Author: chrisz
Date: Thu Sep 29 13:21:48 2005
New Revision: 3353
Modified:
Webware/trunk/WebKit/ (props changed)
Webware/trunk/WebKit/ASStreamOut.py
Webware/trunk/WebKit/AppServer.py
Webware/trunk/WebKit/AppServerService.py
Webware/trunk/WebKit/Application.py
Webware/trunk/WebKit/AutoReloadingAppServer.py
Webware/trunk/WebKit/Configs/AppServer.config
Webware/trunk/WebKit/Configs/Application.config
Webware/trunk/WebKit/Cookie.py
Webware/trunk/WebKit/DebugAppServer.py
Webware/trunk/WebKit/Docs/ApplicationDevelopment.html
Webware/trunk/WebKit/Docs/Configuration.html
Webware/trunk/WebKit/Docs/Developing.html
Webware/trunk/WebKit/Docs/Developing.txt
Webware/trunk/WebKit/Docs/InstallGuide.html
Webware/trunk/WebKit/Docs/Tutorial.html
Webware/trunk/WebKit/Docs/UsersGuide.html
Webware/trunk/WebKit/ExceptionHandler.py
Webware/trunk/WebKit/Experimental/HTTPExceptions.py
Webware/trunk/WebKit/Experimental/NewPage.py
Webware/trunk/WebKit/HTTPExceptions.py
Webware/trunk/WebKit/HTTPRequest.py
Webware/trunk/WebKit/HTTPResponse.py
Webware/trunk/WebKit/HTTPServer.py
Webware/trunk/WebKit/HTTPServlet.py
Webware/trunk/WebKit/ImportSpy.py
Webware/trunk/WebKit/Message.py
Webware/trunk/WebKit/Monitor.py
Webware/trunk/WebKit/Object.py
Webware/trunk/WebKit/Page.py
Webware/trunk/WebKit/PickleRPCServlet.py
Webware/trunk/WebKit/PidFile.py
Webware/trunk/WebKit/PlugIn.py
Webware/trunk/WebKit/RPCServlet.py
Webware/trunk/WebKit/Request.py
Webware/trunk/WebKit/Response.py
Webware/trunk/WebKit/SelectRelease.py
Webware/trunk/WebKit/Servlet.py
Webware/trunk/WebKit/ServletFactory.py
Webware/trunk/WebKit/Session.py
Webware/trunk/WebKit/SessionDynamicStore.py
Webware/trunk/WebKit/SessionFileStore.py
Webware/trunk/WebKit/SessionMemoryStore.py
Webware/trunk/WebKit/SessionStore.py
Webware/trunk/WebKit/ThreadedAppServer.py
Webware/trunk/WebKit/Transaction.py
Webware/trunk/WebKit/URLParser.py
Webware/trunk/WebKit/UnknownFileTypeServlet.py
Webware/trunk/WebKit/WebwarePathLocation.py
Webware/trunk/WebKit/XMLRPCServlet.py
Log:
Large-scale clean-up efforts in the WebKit code and docs.
I hope this does not break anything.
Modified: Webware/trunk/WebKit/ASStreamOut.py
==============================================================================
--- Webware/trunk/WebKit/ASStreamOut.py (original)
+++ Webware/trunk/WebKit/ASStreamOut.py Thu Sep 29 13:21:48 2005
@@ -1,22 +1,24 @@
-"""
-This module defines a class for handling writing reponses.
-"""
+"""This module defines a class for handling writing reponses."""
import string
import exceptions
import types
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
+
debug = 0
class InvalidCommandSequence(exceptions.Exception):
pass
class ASStreamOut:
- """
- This is a response stream to the client.
-
+ """This is a response stream to the client.
+
The key attributes of this class are:
-
+
`_autoCommit`:
if True (1), the stream will automatically start sending data
once it has accumulated `_bufferSize` data. This means that
@@ -29,110 +31,107 @@
`flush()`:
Send the accumulated response data now. Will ask the `Response`
to commit if it hasn't already done so.
+
"""
def __init__(self):
- self._autoCommit = 0
+ self._autoCommit = False
self._bufferSize = 8192
- self._committed = 0
- self._needCommit = 0
+ self._committed = False
+ self._needCommit = False
self._chunks = []
self._buffer=''
- self._chunkLen=0
- self._closed = 0
+ self._chunkLen= 0
+ self._closed = False
def autoCommit(self, val=0):
- # @@ 2003-03 ib: doing both get and set in the same
- # function is not good.
- """
- Get/Set the value of _autoCommit.
- """
-
+ """Get/Set the value of _autoCommit."""
+ # @@ 2003-03 ib: doing both get and set in the same function
+ # @@ is not good.
assert type(val) is types.IntType, "autoCommit must be an integer"
self._autoCommit = val
return val
def bufferSize(self, size=8192):
+ """Return the buffer size and set a new size if one is provided."""
# @@ 2003-03 ib: again, get/set not good
- """
- Returns the size of the buffer, and sets a new size if one is
- provided.
- """
assert type(size) is types.IntType, "bufferSize must be an Integer"
self._bufferSize=size
return self._bufferSize
def flush(self):
- """
- Send available data as soon as possible, ie Now
- Returns 1 if we are ready to send, otherwise 0 (i.e.,
+ """Flush stream.
+
+ Send available data as soon as possible, i.e. *now*.
+
+ Returns True if we are ready to send, otherwise False (i.e.,
if the buffer is full enough).
+
"""
assert not self._closed, "Trying to flush when already closed"
- if debug: print ">>> Flushing ASStreamOut"
+ if debug:
+ print ">>> Flushing ASStreamOut"
if not self._committed:
if self._autoCommit:
- if debug: print "ASSTreamOut.flush setting needCommit"
- self._needCommit = 1
- return 0
+ if debug:
+ print "ASSTreamOut.flush setting needCommit"
+ self._needCommit = True
+ return False
try:
self._buffer = self._buffer + string.join(self._chunks,'')
finally:
self._chunks = []
- self._chunkLen = 0
- return 1
+ self._chunkLen = False
+ return True
def buffer(self):
+ """ Return accumulated data which has not yet been flushed.
+
+ We want to be able to get at this data without having to call flush
+ first, so that we can (for example) integrate automatic HTML validation.
+
"""
- Return accumulated data which has not yet been
- flushed. We want to be able to get at this data
- without having to call flush first, so that we can
- (for example) integrate automatic HTML validation.
- """
- # if flush has been called, return what was flushed
- if self._buffer:
+ if self._buffer: # if flush has been called, return what was flushed:
return self._buffer
- # otherwise return the buffered chunks
- else:
+ else: # otherwise return the buffered chunks
return string.join(self._chunks,'')
def clear(self):
+ """Try to clear any accumulated response data.
+
+ Will fail if the response is already sommitted.
+
"""
- Try to clear any accumulated response data. Will fail
- if the response is already sommitted.
- """
- if debug: print ">>> strmOut clear called"
- if self._committed: raise InvalidCommandSequence()
+ if debug:
+ print ">>> strmOut clear called"
+ if self._committed:
+ raise InvalidCommandSequence()
self._buffer = ''
self._chunks = []
self._chunkLen=0
def close(self):
- """
- Close this buffer. No more data may be sent.
- """
- if debug: print ">>> ASStream close called"
+ """Close this buffer. No more data may be sent."""
+ if debug:
+ print ">>> ASStream close called"
self.flush()
- self._closed = 1
- self._committed = 1
- self._autocommit = 1
+ self._closed = True
+ self._committed = True
+ self._autocommit = True
def closed(self):
- """
- Are we closed to new data?
- """
+ """Check whether we are closed to new data."""
return self._closed
def size(self):
- """
- Returns the current size of the data held here
- """
+ """Return the current size of the data held here."""
return self._chunkLen + len(self._buffer)
def prepend(self, charstr):
- """
- Add the attached string to front of the response buffer.
+ """Add the attached string to front of the response buffer.
+
Invalid if we are already committed.
+
"""
if self.committed() or self.closed():
raise InvalidCommandSequence()
@@ -143,10 +142,9 @@
self._chunkLen = self._chunkLen + len(charstr)
def pop(self, count):
- """
- Remove count bytes from the front of the buffer
- """
- if debug: print "AStreamOut popping %s" % count
+ """Remove count bytes from the front of the buffer."""
+ if debug:
+ print "AStreamOut popping %s" % count
#should we check for an excessive pop length?
assert count <= len(self._buffer)
self._buffer = self._buffer[count:]
@@ -159,34 +157,36 @@
def needCommit(self):
- """
+ """Request for commitment.
+
Called by the `HTTPResponse` instance that is using this instance
to ask if the response needs to be prepared to be delivered.
The response should then commit it's headers, etc.
+
"""
return self._needCommit
def commit(self, autoCommit=1):
+ """Called by the Response to tell us to go.
+
+ If `_autoCommit` is True, then we will be placed into autoCommit mode.
+
"""
- Called by the Response to tell us to go. If `_autoCommit`
- is 1, then we will be placed into autoCommit mode.
- """
-
- if debug: print ">>> ASStreamOut Committing"
- self._committed = 1
+ if debug:
+ print ">>> ASStreamOut Committing"
+ self._committed = True
self._autoCommit = autoCommit
self.flush()
def write(self, charstr):
- """
- Write a string to the buffer.
- """
-
- if debug: print ">>> ASStreamOut writing %s characters" % len(charstr)
+ """Write a string to the buffer."""
+ if debug:
+ print ">>> ASStreamOut writing %s characters" % len(charstr)
assert not self._closed, "Stream Already Closed"
self._chunks.append(charstr)
self._chunkLen = self._chunkLen + len(charstr)
- if self._autoCommit and self._chunkLen > self._bufferSize:
- if debug: print ">>> ASStreamOut.write flushing"
- self.flush()
-
+ if self._autoCommit and self._chunkLen > self._bufferSize:
+ if debug:
+ print ">>> ASStreamOut.write flushing"
+ self.flush()
+
Modified: Webware/trunk/WebKit/AppServer.py
==============================================================================
--- Webware/trunk/WebKit/AppServer.py (original)
+++ Webware/trunk/WebKit/AppServer.py Thu Sep 29 13:21:48 2005
@@ -31,39 +31,41 @@
'PidFile': 'appserverpid.txt',
# @@ 2000-04-27 ce: None of the following settings are implemented
-# 'ApplicationClassName': 'Application',
+ # 'ApplicationClassName': 'Application',
}
class AppServer(ConfigurableForServerSidePath, Object):
+ """The AppServer singleton.
- """
- The `AppServer` is a singleton, and the controlling
- object/process/thread. `AppServer` receives requests and dispatches
- them to `Application` (via `Application.dispatchRawRequest`).
+ The `AppServer` a singleton is the controlling object/process/thread.
+ `AppServer` receives requests and dispatches them to `Application`
+ (via `Application.dispatchRawRequest`).
`ThreadedAppServer` completes the implementation, dispatching
- these requests to separate threads. `AppServer`, at least in the
+ these requests to separate threads. `AppServer`, at least in the
abstract, could support different execution models and environments,
- but that support is not yet realized. (Will it ever be realized?)
+ but that support is not yet realized (Will it ever be realized?).
The distinction between `AppServer` and `Application` is somewhat
- vague -- both are global singletons and both handle dispatching
- requests. `AppServer` works on a lower level, handling sockets
- and threads.
+ vague -- both are global singletons and both handle dispatching requests.
+ `AppServer` works on a lower level, handling sockets and threads.
+
"""
+
+ ## Init ##
+
def __init__(self, path=None):
- """
- Sets up and starts the `AppServer`.
+ """Sets up and starts the `AppServer`.
`path` is the working directory for the AppServer
(directory in which AppServer is contained, by default)
- This method loads plugins, creates the Application
- object, and starts the request handling loop.
- """
+ This method loads plugins, creates the Application object,
+ and starts the request handling loop.
+ """
self._startTime = time.time()
global globalAppServer
@@ -93,18 +95,19 @@
self.running = 1
- # @@ 2003-03 ib: shouldn't this just be in a subclass's
- # __init__?
+ # @@ 2003-03 ib: shouldn't this just be in a subclass's __init__?
if self.isPersistent():
self._closeEvent = Event()
self._closeThread = Thread(target=self.closeThread, name="CloseThread")
-## self._closeThread.setDaemon(1)
+ # self._closeThread.setDaemon(1)
self._closeThread.start()
def checkForInstall(self):
- """
+ """Check whether Webware was installed.
+
Exits with an error message if Webware was not installed.
Called from `__init__`.
+
"""
if not os.path.exists(os.path.join(self._webwarePath, 'install.log')):
sys.stdout = sys.stderr
@@ -117,9 +120,11 @@
sys.exit(0)
def readyForRequests(self):
- """
+ """Declare ready for getting requests.
+
Should be invoked by subclasses when they are finally ready to
accept requests. Records some stats and prints a message.
+
"""
Profiler.readyTime = time.time()
Profiler.readyDuration = Profiler.readyTime - Profiler.startTime
@@ -128,26 +133,16 @@
sys.stderr.flush()
def closeThread(self):
- """
- This method is called when the shutdown sequence is
- initiated.
- """
-
+ """This method is called when the shutdown sequence is initiated."""
self._closeEvent.wait()
self.shutDown()
def initiateShutdown(self):
- """
- Ask the master thread to begin the shutdown.
- """
-
+ """Ask the master thread to begin the shutdown."""
self._closeEvent.set()
-
def recordPID(self):
- """
- Save the pid of the AppServer to a file
- """
+ """Save the pid of the AppServer to a file."""
if self.setting('PidFile') is None:
self._pidFile = None
return
@@ -156,16 +151,20 @@
try:
self._pidFile = PidFile.PidFile(pidpath)
except PidFile.ProcessRunning:
- assert 0, "\n%s exists and contains a process id corresponding to a running process.\nThis indicates that there is an AppServer already running.\nIf this is not the case, please delete this file and restart the AppServer." % pidpath
+ assert 0, '\n%s exists and contains a process id corresponding' \
+ ' to a running process.\nThis indicates that there is an' \
+ ' AppServer already running.\nIf this is not the case,' \
+ ' please delete this file and restart the AppServer.' % pidpath
def shutDown(self):
- """
+ """Shut down the AppServer.
+
Subclasses may override and normally follow this sequence:
1. set self._shuttingDown to 1
2. class specific statements for shutting down
3. Invoke super's shutDown() e.g., ``AppServer.shutDown(self)``
- """
+ """
print "Shutting down the AppServer"
self._shuttingDown = 1
self.running = 0
@@ -173,50 +172,50 @@
del self._plugIns
del self._app
if self._pidFile:
- self._pidFile.remove() # remove the pid file
+ self._pidFile.remove() # remove the pid file
if Profiler.profiler:
print 'Writing profile stats to %s...' % Profiler.statsFilename
- Profiler.dumpStats() # you might also considering having a page/servlet that lets you dump the stats on demand
+ Profiler.dumpStats()
+ # You might also considering having a page/servlet
+ # that lets you dump the stats on demand.
print
- print 'WARNING: Applications run much slower when profiled, so turn off profiling in Launch.py when you are finished.'
+ print 'WARNING: Applications run much slower when profiled,'
+ print 'so turn off profiling in Launch.py when you are finished.'
print
- print 'AppServer ran for %0.2f seconds.' % (time.time()-Profiler.startTime)
+ print 'AppServer ran for %0.2f seconds.' % (
+ time.time() - Profiler.startTime)
print "AppServer has been shutdown"
## Configuration ##
def defaultConfig(self):
- ":ignore:"
+ """Return the default configuration."""
return DefaultConfig
def configFilename(self):
- ":ignore:"
+ """Return the name of the AppServer configuration file."""
return self.serverSidePath('Configs/AppServer.config')
def configReplacementValues(self):
- ":ignore:"
+ """Get config values that need to be escaped."""
# Since these strings will be eval'ed we need to double
- # escape any backslashes
+ # escape any backslashes.
return {
- 'WebwarePath' : string.replace(self._webwarePath, '\\', '\\\\'),
- 'WebKitPath' : string.replace(self._webKitPath, '\\', '\\\\'),
- 'serverSidePath' : string.replace(self._serverSidePath, '\\', '\\\\'),
+ 'WebwarePath' : self._webwarePath.replace('\\', '\\\\'),
+ 'WebKitPath' : self._webKitPath.replace('\\', '\\\\'),
+ 'serverSidePath' : self._serverSidePath.replace('\\', '\\\\'),
}
## Network Server ##
def createApplication(self):
- """
- Creates and returns an application object. Invoked by __init__.
- """
+ """Create and return an application object. Invoked by __init__."""
return Application(server=self)
def printStartUpMessage(self):
- """
- Invoked by __init__, prints a little intro.
- """
+ """Invoked by __init__, prints a little intro."""
print 'WebKit AppServer', self.version()
print 'part of Webware for Python'
print 'Copyright 1999-2001 by Chuck Esterbrook. All Rights Reserved.'
@@ -230,20 +229,22 @@
self.printConfig()
- """
- Plug-in loading
- """
+ ## Plug-in loading ##
def plugIns(self):
- """Returns a list of the plug-ins loaded by the app server.
- Each plug-in is a python package. """
+ """Return a list of the plug-ins loaded by the app server.
+
+ Each plug-in is a Python package.
+
+ """
return self._plugIns
def plugIn(self, name, default=NoDefault):
- """ Returns the plug-in with the given name. """
- # @@ 2001-04-25 ce: linear search. yuck. Plus we should guarantee plug-in name uniqueness anyway
+ """ Return the plug-in with the given name. """
+ # @@ 2001-04-25 ce: linear search. yuck.
+ # Plus we should guarantee plug-in name uniqueness anyway
for pi in self._plugIns:
- if pi.name()==name:
+ if pi.name() == name:
return pi
if default is NoDefault:
raise KeyError, name
@@ -251,20 +252,20 @@
return default
def loadPlugIn(self, path):
- """
- Loads and returns the given plug-in. May return None
- if loading was unsuccessful (in which case this method
- prints a message saying so). Used by
- `loadPlugIns` (note the **s**).
- """
+ """Load and return the given plug-in.
+
+ May return None if loading was unsuccessful (in which case this method
+ prints a message saying so). Used by `loadPlugIns` (note the **s**).
+ """
plugIn = None
path = self.serverSidePath(path)
try:
plugIn = PlugIn(self, path)
willNotLoadReason = plugIn.load()
if willNotLoadReason:
- print ' Plug-in %s cannot be loaded because:\n %s' % (path, willNotLoadReason)
+ print ' Plug-in %s cannot be loaded because:\n' \
+ ' %s' % (path, willNotLoadReason)
return None
plugIn.install()
except:
@@ -274,14 +275,14 @@
return plugIn
def loadPlugIns(self):
- """
- A plug-in allows you to extend the functionality of
- WebKit without necessarily having to modify it's
- source. Plug-ins are loaded by AppServer at startup
- time, just before listening for requests. See the docs
- in `WebKit.PlugIn` for more info.
- """
+ """Load all plug-ins.
+
+ A plug-in allows you to extend the functionality of WebKit without
+ necessarily having to modify its source. Plug-ins are loaded by
+ AppServer at startup time, just before listening for requests.
+ See the docs in `WebKit.PlugIn` for more info.
+ """
plugIns = self.setting('PlugIns')
plugIns = map(lambda path, ssp=self.serverSidePath: ssp(path), plugIns)
@@ -293,15 +294,15 @@
plugInDir = self.serverSidePath(plugInDir)
for filename in os.listdir(plugInDir):
filename = os.path.normpath(os.path.join(plugInDir, filename))
- if os.path.isdir(filename) and \
- os.path.exists(os.path.join(filename, '__init__.py')) and \
- os.path.exists(os.path.join(filename, 'Properties.py')) and \
- not os.path.exists(os.path.join(filename, 'dontload')) and \
- os.path.basename(filename)!='WebKit' and \
- filename not in plugIns:
+ if (os.path.isdir(filename)
+ and os.path.exists(os.path.join(filename, '__init__.py'))
+ and os.path.exists(os.path.join(filename, 'Properties.py'))
+ and not os.path.exists(os.path.join(filename, 'dontload'))
+ and os.path.basename(filename)!='WebKit'
+ and filename not in plugIns):
plugIns.append(filename)
- print 'Plug-ins list:', string.join(plugIns, ', ')
+ print 'Plug-ins list:', ', '.join(plugIns)
# Now that we have our plug-in list, load them...
for plugInPath in plugIns:
@@ -311,11 +312,10 @@
print
- """
- Accessors
- """
+ ## Accessors ##
def version(self):
+ """Return WebKit version."""
if not hasattr(self,'_webKitVersionString'):
from MiscUtils.PropertiesObject import PropertiesObject
props = PropertiesObject(os.path.join(self.webKitPath(), 'Properties.py'))
@@ -323,57 +323,69 @@
return self._webKitVersionString
def application(self):
+ """Return the Application singleton."""
return self._app
def startTime(self):
- """ Returns the time the app server was started (as
- seconds, like time()). """
+ """Return the time the app server was started.
+
+ The time is given as seconds, like time().
+
+ """
return self._startTime
def numRequests(self):
- """ Return the number of requests received by this
- server since it was launched. """
+ """Return the number of requests.
+
+ Returns the number of requests received by this app server
+ since it was launched.
+
+ """
return self._reqCount
def isPersistent(self):
- """
- When using ``OneShot``, the AppServer will exist only
- for a single request, otherwise it will stay around
- indefinitely.
+ """Check whether the AppServer is persistent.
+
+ When using ``OneShot``, the AppServer will exist only for a single
+ request, otherwise it will stay around indefinitely.
+
"""
raise AbstractError, self.__class__
def serverSidePath(self, path=None):
- """
- Returns the absolute server-side path of the WebKit
- app server. If the optional path is passed in, then it
- is joined with the server side directory to form a
- path relative to the app server.
- """
+ """Return the absolute server-side path of the WebKit app server.
+
+ If the optional path is passed in, then it is joined with the
+ server side directory to form a path relative to the app server.
+ """
if path:
return os.path.normpath(os.path.join(self._serverSidePath, path))
else:
return self._serverSidePath
def webwarePath(self):
+ """Return the Webware path."""
return self._webwarePath
def webKitPath(self):
+ """Return teh WebKit path."""
return self._webKitPath
- """
- Warnings and Errors
- """
+ ## Warnings and Errors ##
def warning(self, msg):
+ """Output a warning (not yet implemented)."""
# @@ 2000-04-25 ce: would this be useful?
raise NotImplementedError
def error(self, msg):
- """
- Flushes stdout and stderr, prints the message to stderr and exits with code 1.
+ """Output an error.
+
+ Flushes stdout and stderr, prints the message to stderr
+ and exits with code 1.
+
"""
sys.stdout.flush()
sys.stderr.flush()
@@ -382,31 +394,28 @@
sys.exit(1) # @@ 2000-05-29 ce: Doesn't work. Perhaps because of threads
+## Main ##
def main():
- """
- Start the Appserver
- """
+ """Start the Appserver."""
try:
server = AppServer()
return
print "Ready"
print
- print 'WARNING: There is nothing to do here with the abstract AppServer. Use one of the adapters such as WebKit.cgi (with ThreadedAppServer) or OneShot.cgi'
+ print "WARNING: There is nothing to do here with the abstract AppServer."
+ print "Use one of the adapters such as WebKit.cgi (with ThreadedAppServer)"
+ print "or OneShot.cgi"
server.shutDown()
except Exception, exc: # Need to kill the Sweeper thread somehow
- print 'Caught exception:', exc
+ print "Caught exception:", exc
print "Exiting AppServer"
server.shutDown()
del server
sys.exit()
-
def stop(*args, **kw):
- """
- Stop the AppServer (which may be in a different process).
- """
-
+ """Stop the AppServer (which may be in a different process)."""
if kw.has_key('workDir'):
# app directory
pidfile = os.path.join(kw['workDir'], "appserverpid.txt")
@@ -422,7 +431,8 @@
try:
import win32process
except:
- print "Win32 extensions not present. Webkit cannot terminate the running process."
+ print "Win32 extensions not present."
+ print "Webkit cannot terminate the running process."
if sys.modules.has_key('win32process'):
win32process.TerminateProcess(pid,0)
Modified: Webware/trunk/WebKit/AppServerService.py
==============================================================================
--- Webware/trunk/WebKit/AppServerService.py (original)
+++ Webware/trunk/WebKit/AppServerService.py Thu Sep 29 13:21:48 2005
@@ -90,10 +90,11 @@
# The ThreadedAppServer calls signal.signal which is not possible
# if it is installed as a service, since signal only works in main thread.
# So we sneakily replace signal.signal with a no-op:
-def dummy_signal(*args, **kwargs):
+def _dummy_signal(*args, **kwargs):
pass
import signal
-signal.signal = dummy_signal
+signal.signal = _dummy_signal
+
class AppServerService(win32serviceutil.ServiceFramework):
@@ -195,6 +196,9 @@
self.server.shutDown()
raise
+
+## Main ##
+
def main():
win32serviceutil.HandleCommandLine(AppServerService)
Modified: Webware/trunk/WebKit/Application.py
==============================================================================
--- Webware/trunk/WebKit/Application.py (original)
+++ Webware/trunk/WebKit/Application.py Thu Sep 29 13:21:48 2005
@@ -19,44 +19,48 @@
debug = 0
+
class EndResponse(Exception):
- """
+ """End response exception.
+
Used to prematurely break out of the awake()/respond()/sleep()
- cycle without reporting a traceback. During servlet
- processing, if this exception is caught during awake() or respond()
- then sleep() is called and the response is sent. If caught during
- sleep(), processing ends and the response is sent.
+ cycle without reporting a traceback. During servlet processing,
+ if this exception is caught during awake() or respond() then sleep()
+ is called and the response is sent. If caught during sleep(),
+ processing ends and the response is sent.
+
"""
pass
+
class Application(ConfigurableForServerSidePath, Object):
- """
- `Application` and `AppServer` work together to setup up and
- dispatch requests. The distinction between the two is largely
- historical, but AppServer communicates directly with Adapters
- (or speaks protocols like HTTP), and Application receives the
- (slightly) processed input from AppServer and turns it into
- `Transaction`, `HTTPRequest`, `HTTPResponse`, and `Session`.
-
- Application is a singleton, which belongs to the AppServer.
- You can get access through the Transaction object
- (``transaction.application()``), or you can do::
+ """The Application singleton.
+
+ `Application` and `AppServer` work together to setup up and dispatch
+ requests. The distinction between the two is largely historical, but
+ AppServer communicates directly with Adapters (or speaks protocols like
+ HTTP), and Application receives the (slightly) processed input from
+ AppServer and turns it into `Transaction`, `HTTPRequest`, `HTTPResponse`,
+ and `Session`.
+
+ Application is a singleton, which belongs to the AppServer. You can get
+ access through the Transaction object (``transaction.application()``),
+ or you can do::
from AppServer import globalAppServer
application = AppServer.application()
- Settings for Application are taken from
- ``Configs/Application.config``, and it is used for many
- global settings, even if they aren't closely tied to the
- Application object itself.
+ Settings for Application are taken from ``Configs/Application.config``,
+ and it is used for many global settings, even if they aren't closely tied
+ to the Application object itself.
+
"""
+
## Init ##
def __init__(self, server):
- """
- Called only by `AppServer`, sets up the Application.
- """
+ """Called only by `AppServer`, sets up the Application."""
self._server = server
self._serverSidePath = server.serverSidePath()
@@ -71,15 +75,14 @@
self._shutDownHandlers = []
- ## For TaskManager:
+ # For TaskManager:
if self._server.isPersistent():
self._taskManager = Scheduler(1)
self._taskManager.start()
- # define this before initializing URLParser, so that contexts
- # have a chance to override this. Also be sure
- # to define it before loading the sessions, in case the
- # loading of the sessions causes an exception.
+ # Define this before initializing URLParser, so that contexts have a
+ # chance to override this. Also be sure to define it before loading the
+ # sessions, in case the loading of the sessions causes an exception.
self._exceptionHandlerClass = ExceptionHandler
# For session store:
@@ -100,10 +103,11 @@
def initVersions(self):
- """
- Initialize attributes that store the Webware and
- WebKit versions as both tuples and strings. These are
- stored in the Properties.py files.
+ """Get and store versions.
+
+ Initialize attributes that store the Webware and WebKit versions as
+ both tuples and strings. These are stored in the Properties.py files.
+
"""
from MiscUtils.PropertiesObject import PropertiesObject
props = PropertiesObject(os.path.join(self.webwarePath(),
@@ -118,16 +122,15 @@
def taskManager(self):
- """
- Accessor: `TaskKit.Scheduler` instance
- """
+ """Accessor: `TaskKit.Scheduler` instance."""
return self._taskManager
def startSessionSweeper(self):
- """
- Starts the session sweeper, `WebKit.Tasks.SessionTask`,
- which deletes session objects (and disk copies of those
- objects) that have expired.
+ """Start session sweeper.
+
+ Starts the session sweeper, `WebKit.Tasks.SessionTask`, which deletes
+ session objects (and disk copies of those objects) that have expired.
+
"""
from Tasks import SessionTask
import time
@@ -139,10 +142,11 @@
print "Session Sweeper started"
def shutDown(self):
- """
- Called by AppServer when it is shuting down. The
- `__del__` function of Application probably won't be
- called due to circular references.
+ """Shut down the application.
+
+ Called by AppServer when it is shuting down. The `__del__` function
+ of Application probably won't be called due to circular references.
+
"""
print "Application is Shutting Down"
self.running = 0
@@ -165,18 +169,20 @@
print "Application has been succesfully shutdown."
def addShutDownHandler(self, func):
- """
- Functions added through `addShutDownHandler` will be
- called when the AppServer is shutting down. You can use
- this hook to close database connections, clean up resources,
- save data to disk, etc.
+ """Add a shutdown handler.
+
+ Functions added through `addShutDownHandler` will be called when
+ the AppServer is shutting down. You can use this hook to close
+ database connections, clean up resources, save data to disk, etc.
+
"""
self._shutDownHandlers.append(func)
+
## Config ##
def defaultConfig(self):
- ":ignore:"
+ """The default Application.config."""
return {
'PrintConfigAtStartUp': 1,
'LogActivity': 1,
@@ -253,29 +259,29 @@
## Versions ##
def version(self):
- """
- Returns the version of the application. This
- implementation returns '0.1'. Subclasses should
+ """Returns the version of the application.
+
+ This implementation returns '0.1'. Subclasses should
override to return the correct version number.
+
"""
- ## @@ 2000-05-01 ce: Maybe this could be a setting 'AppVersion'
- ## @@ 2003-03 ib: Does anyone care about this? What's this
- ## version even supposed to mean?
+ # @@ 2000-05-01 ce: Maybe this could be a setting 'AppVersion'
+ # @@ 2003-03 ib: Does anyone care about this? What's this
+ # version even supposed to mean?
return '0.1'
def webwareVersion(self):
- """ Returns the Webware version as a tuple. """
+ """Returns the Webware version as a tuple."""
return self._webwareVersion
def webwareVersionString(self):
- """ Returns the Webware version as a printable string. """
+ """Returns the Webware version as a printable string."""
return self._webwareVersionString
def webKitVersion(self):
""" Returns the WebKit version as a tuple. """
- ## @@ 2003-03 ib: This is synced with Webware now,
- ## should be removed because redundant (and not that useful
- ## anyway)
+ # @@ 2003-03 ib: This is synced with Webware now, should be removed
+ # because redundant (and not that useful anyway)
return self._webKitVersion
def webKitVersionString(self):
@@ -286,10 +292,10 @@
## Sessions ##
def session(self, sessionId, default=NoDefault):
- """
- The session object for `sessionId`. Raises
- ``KeyError`` if session not found and no default
- is given.
+ """The session object for `sessionId`.
+
+ Raises ``KeyError`` if session not found and no default is given.
+
"""
if default is NoDefault:
return self._sessions[sessionId]
@@ -297,26 +303,22 @@
return self._sessions.get(sessionId, default)
def hasSession(self, sessionId):
- """
- Does session `sessionId` exist.
- """
+ """Check whether session `sessionId` exist."""
return self._sessions.has_key(sessionId)
def sessions(self):
- """
- A dictionary of all the session objects.
- """
+ """A dictionary of all the session objects."""
return self._sessions
def createSessionForTransaction(self, transaction):
- """
- Gets the session object for the transaction -- if the
- session already exists, returns that, otherwise creates
+ """Get the session object for the transaction.
+
+ If the session already exists, returns that, otherwise creates
a new session.
Finding the session ID is done in `Transaction.sessionId`.
- """
+ """
debug = self.setting('Debug').get('Sessions')
if debug:
prefix = '>> [session] createSessionForTransaction:'
@@ -339,7 +341,6 @@
def createSessionWithID(self, transaction, sessionID):
# Create a session object with our session ID
sess = Session(transaction, sessionID)
-
# Replace the session if it didn't already exist,
# otherwise we just throw it away. setdefault is an atomic
# operation so this guarantees that 2 different
@@ -348,20 +349,19 @@
# this method simultaneously.
transaction.application()._sessions.setdefault(sessionID, sess)
+
## Misc Access ##
def server(self):
- """
- Acessor: the AppServer
- """
+ """Acessor: the AppServer"""
return self._server
def serverSidePath(self, path=None):
- """
- Returns the absolute server-side path of the WebKit
- application. If the optional path is passed in, then
- it is joined with the server side directory to form a
- path relative to the app server.
+ """Get the serve-side-path.
+
+ Returns the absolute server-side path of the WebKit application.
+ If the optional path is passed in, then it is joined with the
+ server side directory to form a path relative to the app server.
"""
if path:
return os.path.normpath(
@@ -370,31 +370,28 @@
return self._serverSidePath
def webwarePath(self):
- """
- Path of the ``Webware/`` directory
- """
+ """The path of the ``Webware/`` directory."""
return self._server.webwarePath()
def webKitPath(self):
- """
- Path of the ``Webware/WebKit/`` directory
- """
+ """The Path of the ``Webware/WebKit/`` directory."""
return self._server.webKitPath()
def name(self):
- """
- Name by which this was started. Usually ``AppServer``
- """
- ## @@ 2003-03 ib: unconfirmed
+ """The name by which this was started. Usually ``AppServer``."""
+ # @@ 2003-03 ib: unconfirmed
return sys.argv[0]
+
## Activity Log ##
def writeActivityLog(self, transaction):
- """
+ """Write an entry to the activity log.
+
Writes an entry to the script log file. Uses settings
``ActivityLogFilename`` and ``ActivityLogColumns``.
+
"""
filename = self.serverSidePath(
self.setting('ActivityLogFilename'))
@@ -404,9 +401,8 @@
file = open(filename, 'w')
file.write(','.join(self.setting('ActivityLogColumns'))+'\n')
values = []
- # We use UserDict on the next line because we know it
- # inherits NamedValueAccess and reponds to
- # valueForName()
+ # We use UserDict on the next line because we know it inherits
+ # NamedValueAccess and reponds to valueForName()
objects = UserDict({
'application': self,
'transaction': transaction,
@@ -421,10 +417,8 @@
except:
value = '(unknown)'
if type(value) is FloatType:
- # probably need more flexibility in
- # the future
+ # probably need more flexibility in the future
value = '%0.2f' % value
-
else:
value = str(value)
values.append(value)
@@ -434,25 +428,21 @@
for i in objects.keys():
objects[i] = None
- """
- **Request Dispatching**
- These are the entry points from `AppServer`, which take
- a raw request, turn it into a transaction, run the transaction,
- and clean up.
+ ## Request Dispatching ##
- """
+ # These are the entry points from `AppServer`, which take a raw request,
+ # turn it into a transaction, run the transaction, and clean up.
def dispatchRawRequest(self, requestDict, strmOut):
- """
- Dispatch a raw request, as passed from the Adapter
- through the AppServer. This method creates the
- request, response, and transaction object, then runs
- (via `runTransaction`) the transaction. It also
- catches any exceptions, which are then passed on to
- `handleExceptionInTransaction`.
- """
+ """Dispatch a raw request.
+ Dispatch a request as passed from the Adapter through the AppServer.
+ This method creates the request, response, and transaction object,
+ then runs (via `runTransaction`) the transaction. It also catches any
+ exceptions, which are then passed on to `handleExceptionInTransaction`.
+
+ """
trans = None
try:
request = self.createRequestForDict(requestDict)
@@ -479,18 +469,18 @@
return trans
def createRequestForDict(self, requestDict):
- """
- Create a request object (subclass of `Request`) given the
- raw dictionary as passed by the adapter.
+ """Create request object for a given dictionary.
+
+ Create a request object (subclass of `Request`) given the raw
+ dictionary as passed by the adapter.
- The class of the request may be based on the contents
- of the dictionary (though only `HTTPRequest` is
- currently created), and the request will later
- determine the class of the response.
+ The class of the request may be based on the contents of the
+ dictionary (though only `HTTPRequest` is currently created),
+ and the request will later determine the class of the response.
Called by `dispatchRawRequest`.
- """
+ """
format = requestDict['format']
# Maybe an EmailAdapter would make a request with a
# format of Email, and an EmailRequest would be
@@ -499,15 +489,18 @@
return HTTPRequest(requestDict)
def runTransaction(self, trans):
- """
+ """Run transation.
+
Executes the transaction, handling HTTPException errors.
Finds the servlet (using the root parser, probably
`URLParser.ContextParser`, to find the servlet for the
transaction, then calling `runTransactionViaServlet`.
Called by `dispatchRawRequest`.
+
"""
- # @@ gtalvola: I'm guessing this is not the ideal place to put this code. But, it works.
+ # @@ gtalvola: I'm guessing this is not the ideal place
+ # @@ to put this code. But, it works.
if self.setting('UseAutomaticPathSessions'):
request = trans.request()
request_has_cookie_session = request.hasCookieSession()
@@ -518,7 +511,6 @@
elif not request_has_cookie_session and not request_has_path_session:
self.handleMissingPathSession(trans)
return
-
servlet = None
try:
servlet = self._rootURLParser.findServletForTransaction(trans)
@@ -533,26 +525,25 @@
self.returnServlet(servlet, trans)
def runTransactionViaServlet(self, servlet, trans):
- """
- Executes the transaction using the servlet. This is
- the `awake`/`respond`/`sleep` sequence of calls, or if the
- servlet supports it, a single `runTransaction` call
- (which is presumed to make the awake/respond/sleep calls
- on its own). Using `runTransaction` allows the servlet
- to override the basic call sequence, or catch errors from
- that sequence.
+ """Execute the transaction using the servlet.
+
+ This is the `awake`/`respond`/`sleep` sequence of calls, or if
+ the servlet supports it, a single `runTransaction` call (which is
+ presumed to make the awake/respond/sleep calls on its own). Using
+ `runTransaction` allows the servlet to override the basic call
+ sequence, or catch errors from that sequence.
Called by `runTransaction`.
+
"""
trans.setServlet(servlet)
if hasattr(servlet, 'runTransaction'):
servlet.runTransaction(trans)
else:
- # For backward compatibility (Servlet.runTransaction
- # implements this same sequence of calls, but
- # by keeping it in the servlet it's easier to
- # for the servlet to catch exceptions.
+ # For backward compatibility (Servlet.runTransaction implements
+ # this same sequence of calls, but by keeping it in the servlet
+ # it's easier for the servlet to catch exceptions.
try:
trans.awake()
trans.respond()
@@ -560,23 +551,21 @@
trans.sleep()
def forward(self, trans, url, context=None):
- """
- Forward the request to a different (internal) url.
- The transaction's URL is changed to point to the new
- servlet, and the transaction is simply run again.
-
- Output is _not_ accumulated, so if the original servlet
- had any output, the new output will _replace_ the old
- output.
-
- You can change the request in place to control the
- servlet you are forwarding to -- using methods like
- `HTTPRequest.setField`.
+ """Forward the request to a different (internal) url.
- @@ 2003-03 ib: how does the forwarded servlet knows
- that it's not the original servlet?
- """
+ The transaction's URL is changed to point to the new servlet,
+ and the transaction is simply run again.
+
+ Output is _not_ accumulated, so if the original servlet had any output,
+ the new output will _replace_ the old output.
+ You can change the request in place to control the servlet you are
+ forwarding to -- using methods like `HTTPRequest.setField`.
+
+ @@ 2003-03 ib: how does the forwarded servlet knows that it's not
+ the original servlet?
+
+ """
# Reset the response to a "blank slate"
trans.response().reset()
@@ -587,24 +576,22 @@
raise EndResponse
def callMethodOfServlet(self, trans, url, method, *args, **kw):
- """
- Call a method of the servlet referred to by the url,
- potentially in a particular context (use the context
- keyword argument). Calls sleep and awake before and
- after the method call. Or, if the servlet defines it,
- then `runMethodForTransaction` is used (analogous to the
+ """Call method of another servlet.
+
+ Call a method of the servlet referred to by the url, potentially in
+ a particular context (use the context keyword argument). Calls sleep()
+ and awake() before and after the method call. Or, if the servlet
+ defines it, then `runMethodForTransaction` is used (analogous to the
use of `runTransaction` in `forward`).
- The entire process is similar to `forward`, except that
- instead of `respond`, `method` is called (`method` should
- be a string, ``*args`` and ``**kw`` are passed as
- arguments to that method).
-
- A `context` keyword argument (as used in `forward`)
- is also allowed, though it isn't present in the
- method signature.
- """
+ The entire process is similar to `forward`, except that instead of
+ `respond`, `method` is called (`method` should be a string, ``*args``
+ and ``**kw`` are passed as arguments to that method).
+ A `context` keyword argument (as used in `forward`) is also allowed,
+ though it isn't present in the method signature.
+
+ """
# 3-03 ib@@: Does anyone actually use this method?
# 2003-04-08 jdh: I use callMethodOfServlet, but I don't use
# this little context trick. Not sure what you were referring to.
@@ -650,12 +637,13 @@
return result
def includeURL(self, trans, url, context=None):
- """
- Include the servlet given by the url, potentially in
- context (or current context). Like `forward`, except
- control is ultimately returned to the servlet.
- """
+ """Include another servlet.
+ Include the servlet given by the url, potentially in context (or
+ current context). Like `forward`, except control is ultimately
+ returned to the servlet.
+
+ """
urlPath = self.resolveInternalRelativePath(trans, url, context)
req = trans.request()
currentPath = req.urlPath()
@@ -686,11 +674,13 @@
trans._servlet = currentServlet
def resolveInternalRelativePath(self, trans, url, context=None):
- """
+ """Return the absolute internal path.
+
Given a URL and an optional context, return the absolute
- internal URL. URLs are assumed relative to the current URL.
+ internal URL. URLs are assumed relative to the current URL.
Absolute paths are returned unchanged.
+
"""
req = trans.request()
if not url.startswith('/'):
@@ -719,19 +709,23 @@
servlet.close(trans)
def handleException(self):
- """
+ """Handle exceptions.
+
This should only be used in cases where there is no transaction object,
for example if an exception occurs when attempting to save a session
to disk.
+
"""
self._exceptionHandlerClass(self, None, sys.exc_info())
def handleExceptionInTransaction(self, excInfo, transaction):
- """
- Handles exception `excInfo` (as returned by
- ``sys.exc_info()``) that was generated by `transaction`.
- It may display the exception report, email the report,
- etc., handled by `ExceptionHandler.ExceptionHandler`.
+ """Handle exception with info.
+
+ Handles exception `excInfo` (as returned by ``sys.exc_info()``)
+ that was generated by `transaction`. It may display the exception
+ report, email the report, etc., handled by
+ `ExceptionHandler.ExceptionHandler`.
+
"""
req = transaction.request()
editlink = req.adapterName() + "/Admin/EditFile"
@@ -739,41 +733,40 @@
{"editlink": editlink})
def rootURLParser(self):
- """
- Accessor: the Rool URL parser -- URL parsing (as defined
- by subclasses of `URLParser.URLParser`) starts here.
- Other parsers are called in turn by this parser.
+ """Accessor: the Rool URL parser.
+
+ URL parsing (as defined by subclasses of `URLParser.URLParser`)
+ starts here. Other parsers are called in turn by this parser.
+
"""
return self._rootURLParser
def hasContext(self, name):
- """
- Does context `name` exist?
- """
+ """Checks whether context `name` exist."""
return self._rootURLParser._contexts.has_key(name)
def addContext(self, name, path):
- """
- Add a context by named `name`, rooted at `path`.
+ """Add a context by named `name`, rooted at `path`.
+
This gets imported as a package, and the last directory
of `path` does not have to match the context name.
(The package will be named `name`, regardless of `path`).
Delegated to `URLParser.ContextParser`.
+
"""
self._rootURLParser.addContext(name, path)
def addServletFactory(self, factory):
- """
- Adds a ServletFactory, delegated to the
- `URLParser.ServletFactoryManager` singleton.
+ """Add a ServletFactory.
+
+ Delegated to the `URLParser.ServletFactoryManager` singleton.
+
"""
URLParser.ServletFactoryManager.addServletFactory(factory)
def contexts(self):
- """
- Returns a dictionary of context-name: context-path.
- """
+ """Return a dictionary of context-name: context-path."""
return self._rootURLParser._contexts
def writeExceptionReport(self, handler):
@@ -781,30 +774,35 @@
pass
def handleMissingPathSession(self,transaction):
- """
+ """Redirect requests without session info in the path.
+
if UseAutomaticPathSessions is enabled in Application.config
we redirect the browser to a url with SID in path
http://gandalf/a/_SID_=2001080221301877755/Examples/
_SID_ is extracted and removed from path in HTTPRequest.py
- this is for convinient building of webapps that must not
- depend on cookie support
+ This is for convinient building of webapps that must not
+ depend on cookie support.
+
"""
newSid = transaction.session().identifier()
request = transaction.request()
- url = request.adapterName() + '/_SID_='+ newSid + '/' + request.pathInfo() + (request.extraURLPath() or '')
-
+ url = request.adapterName() + '/_SID_='+ newSid + '/' \
+ + request.pathInfo() + (request.extraURLPath() or '')
if request.queryString():
url = url + '?' + request.queryString()
if self.setting('Debug')['Sessions']:
- print ">> [sessions] handling UseAutomaticPathSessions, redirecting to", url
+ print '>> [sessions] handling UseAutomaticPathSessions,' \
+ ' redirecting to', url
transaction.response().sendRedirect(url)
def handleUnnecessaryPathSession(self, transaction):
- """
+ """Redirect request with unnecessary session info in the path.
+
This is called if it has been determined that the request has a path
- session, but also cookies. In that case we redirect
- to eliminate the unnecessary path session.
+ session, but also cookies. In that case we redirect to eliminate the
+ unnecessary path session.
+
"""
request = transaction.request()
url = request.adapterName() + '/' + request.pathInfo() + (request.extraURLPath() or '')
@@ -816,14 +814,13 @@
transaction.response().sendRedirect(url)
+## Main ##
+def main(requestDict):
+ """Return a raw reponse.
+ This method is mostly used by OneShotAdapter.py.
-
-def main(requestDict):
- """
- Returns a raw reponse. This method is mostly used by
- OneShotAdapter.py.
"""
from WebUtils.HTMLForException import HTMLForException
try:
@@ -846,6 +843,7 @@
Largely historical.
"""
+
if __name__=='__main__':
if len(sys.argv)!=2:
sys.stderr.write('WebKit: Application: Expecting one filename argument.\n')
Modified: Webware/trunk/WebKit/AutoReloadingAppServer.py
==============================================================================
--- Webware/trunk/WebKit/AutoReloadingAppServer.py (original)
+++ Webware/trunk/WebKit/AutoReloadingAppServer.py Thu Sep 29 13:21:48 2005
@@ -37,22 +37,27 @@
'AutoReloadPollInterval': 1, # in seconds
}
+
class AutoReloadingAppServer(AppServer):
+ """AppServer AutoReloading.
+
+ This class adds functionality to `AppServer`, to notice changes to
+ source files, including servlets, PSPs, templates or changes to the
+ Webware source file themselves, and reload the server as necessary
+ to pick up the changes.
+
+ The server will also be restarted if a file which Webware *tried*
+ to import is modified. This is so that changes to a file containing
+ a syntax error (which would have prevented it from being imported)
+ will also cause the server to restart.
"""
- This class adds functionality to `AppServer`, to notice
- changes to source files, including servlets, PSPs, templates
- or changes to the Webware source file themselves, and reload
- the server as necessary to pick up the changes.
-
- The server will also be restarted if a file which Webware
- *tried* to import is modified. This is so that changes to a
- file containing a syntax error (which would have prevented it
- from being imported) will also cause the server to restart.
- """
+
+
+ ## Init ##
def __init__(self,path=None):
- ":ignore:"
+ """Activate AutoReloading."""
AppServer.__init__(self,path)
self._autoReload = 0
self._shouldRestart = 0
@@ -64,15 +69,16 @@
self.activateAutoReload()
def defaultConfig(self):
- ":ignore:"
+ """Return the default configuration."""
conf = AppServer.defaultConfig(self)
conf.update(DefaultConfig)
return conf
def shutDown(self):
- """
- Shuts down the monitoring thread (in addition to
- normal shutdown procedure).
+ """Shut down the monitoring thread.
+
+ This is done in addition to the normal shutdown procedure.
+
"""
print 'Stopping AutoReload Monitor'
sys.stdout.flush()
@@ -89,21 +95,27 @@
if not self._autoReload:
if haveFam:
print 'AutoReload Monitor started, using FAM'
- self._fileMonitorThread = t = Thread(target=self.fileMonitorThreadLoopFAM)
+ self._fileMonitorThread = t = Thread(
+ target=self.fileMonitorThreadLoopFAM)
else:
- print 'AutoReload Monitor started, polling every %d seconds' % self.setting('AutoReloadPollInterval')
- self._fileMonitorThread = t = Thread(target=self.fileMonitorThreadLoop)
+ print 'AutoReload Monitor started, polling every', \
+ self.setting('AutoReloadPollInterval'), 'seconds'
+ self._fileMonitorThread = t = Thread(
+ target=self.fileMonitorThreadLoop)
self._autoReload = 1
t.setName('AutoReloadMonitor')
t.start()
def deactivateAutoReload(self):
- """Tell the monitor thread to stop (it's a request,
- not a demand)."""
+ """Tell the monitor thread to stop.
+
+ This should be considered as a request, not a demand.
+
+ """
self._autoReload = 0
if haveFam:
- # send a message down the pipe to wake up the monitor
- # thread and tell him to quit
+ # Send a message down the pipe to wake up the monitor thread
+ # and tell him to quit.
self._pipe[1].write('close')
self._pipe[1].flush()
try:
@@ -112,33 +124,36 @@
pass
- ## Restart methods
+ ## Restart methods ##
def restartIfNecessary(self):
- """
- This should be called regularly to see if a restart is
- required. The server can only restart from the main
- thread, it other threads can't do the restart. So this
- polls to see if `shouldRestart` has been called.
- """
+ """Check if the app server should be restarted.
+
+ This should be called regularly to see if a restart is required.
+ The server can only restart from the main thread, other threads
+ can't do the restart. So this polls to see if `shouldRestart`
+ has been called.
+ """
# Tavis Rudd claims: "this method can only be called by
# the main thread. If a worker thread calls it, the
# process will freeze up."
#
# I've implemented it so that the ThreadedAppServer's
- # control thread calls this. That thread is _not_ the
+ # control thread calls this. That thread is _not_ the
# MainThread (the initial thread created by the Python
# interpreter), but I've never encountered any problems.
# Most likely Tavis meant a freeze would occur if a
# _worker_ called this.
-
if self._shouldRestart:
self.restart()
def restart(self):
- """Do the actual restart. Call `shouldRestart` from
- outside the class"""
+ """Do the actual restart.
+
+ Call `shouldRestart` from outside the class.
+
+ """
self.initiateShutdown()
self._closeThread.join()
sys.stdout.flush()
@@ -152,28 +167,30 @@
sys.exit(3)
def monitorNewModule(self, filepath, mtime):
- """
- This is a callback which ImportSpy invokes to notify
- us of new files to monitor. This is only used when we
- are using FAM.
+ """Add new file to be monitored.
+
+ This is a callback which ImportSpy invokes to notify us of new files
+ to monitor. This is only used when we are using FAM.
+
"""
self._requests.append(self._fc.monitorFile(filepath, filepath) )
- ## Internal methods
+
+ ## Internal methods ##
def shouldRestart(self):
"""Tell the main thread to restart the server."""
self._shouldRestart = 1
def fileMonitorThreadLoop(self):
+ """This the the main loop for the monitoring thread.
+
+ Runs in its own thread, polling the files for changes directly
+ (i.e., going through every file that's being used and checking
+ its last-modified time, seeing if it's been changed since it
+ was initially loaded).
+
"""
- This the the main loop for the monitoring thread.
- Runs in its own thread, polling the files for changes
- directly (i.e., going through every file that's being
- used and checking its last-modified time, seeing if
- it's been changed since it was initially loaded)
- """
-
pollInterval = self.setting('AutoReloadPollInterval')
while self._autoReload:
time.sleep(pollInterval)
@@ -186,9 +203,11 @@
sys.stdout.flush()
def fileUpdated(self, filename, mtime, getmtime=os.path.getmtime):
- """
- Checks if a file has been updated in such a way that
- we should restart the server.
+ """Check whether a file has been updated.
+
+ Checks if a file has been updated in such a way that we should
+ restart the server.
+
"""
try:
if mtime < getmtime(filename):
@@ -208,34 +227,32 @@
return 1
def fileMonitorThreadLoopFAM(self, getmtime=os.path.getmtime):
- """
- Monitoring thread loop, but using the FAM library.
- """
-
+ """Monitoring thread loop, but using the FAM library."""
ImportSpy.modloader.notifyOfNewFiles(self.monitorNewModule)
self._fc = fc = _fam.open()
- # for all of the modules which have _already_ been loaded, we check
- # to see if they've already been modified or deleted
+ # For all of the modules which have _already_ been loaded, we check
+ # to see if they've already been modified or deleted:
for f, mtime in ImportSpy.modloader.fileList().items():
if self.fileUpdated(f, mtime):
- print '*** The file', f, 'has changed. The server is restarting now.'
+ print '*** The file', f, 'has changed.', \
+ 'The server is restarting now.'
self._autoReload = 0
return self.shouldRestart()
# request that this file be monitored for changes
self._requests.append( fc.monitorFile(f, f) )
- # create a pipe so that this thread can be notified when the
- # server is shutdown. We use a pipe because it needs to be an object
- # which will wake up the call to 'select'
+ # Create a pipe so that this thread can be notified when the
+ # server is shutdown. We use a pipe because it needs to be an object
+ # which will wake up the call to 'select':
r,w = os.pipe()
r = os.fdopen(r,'r')
w = os.fdopen(w,'w')
self._pipe = pipe = (r,w)
while self._autoReload:
try:
- # we block here until a file has been changed, or until
- # we receive word that we should shutdown (via the pipe)
+ # We block here until a file has been changed, or until
+ # we receive word that we should shutdown (via the pipe).
ri, ro, re = select.select([fc,pipe[0]], [], [])
except select.error, er:
errnumber, strerr = er
@@ -247,8 +264,10 @@
while fc.pending():
fe = fc.nextEvent()
if (fe.code2str() in ['changed','deleted','created']
- and self.fileUpdated(fe.userData, ImportSpy.modloader.fileList()[fe.userData])):
- print '*** The file %s has changed. The server is restarting now.' % fe.userData
+ and self.fileUpdated(fe.userData,
+ ImportSpy.modloader.fileList()[fe.userData])):
+ print '*** The file', fe.userData, 'has changed.', \
+ 'The server is restarting now.'
self._autoReload = 0
return self.shouldRestart()
for req in self._requests:
Modified: Webware/trunk/WebKit/Configs/AppServer.config
==============================================================================
--- Webware/trunk/WebKit/Configs/AppServer.config (original)
+++ Webware/trunk/WebKit/Configs/AppServer.config Thu Sep 29 13:21:48 2005
@@ -11,4 +11,4 @@
MaxServerThreads = 20
MinServerThreads = 5
CheckInterval = 100
-AutoReload = False
+AutoReload = True
\ No newline at end of file
Modified: Webware/trunk/WebKit/Configs/Application.config
==============================================================================
--- Webware/trunk/WebKit/Configs/Application.config (original)
+++ Webware/trunk/WebKit/Configs/Application.config Thu Sep 29 13:21:48 2005
@@ -1,5 +1,5 @@
# You cannot log in with an empty password:
-AdminPassword = 'max'
+AdminPassword = '9aiyRz35'
PrintConfigAtStartUp = True
DirectoryFile = ['index', 'Index', 'main', 'Main']
ExtensionsToIgnore = ['.pyc', '.pyo', '.py~', '.psp~', '.html~',
@@ -56,7 +56,7 @@
# Error handling:
ShowDebugInfoOnErrors = True
-IncludeFancyTraceback = False
+IncludeFancyTraceback = True
EnterDebuggerOnException = False
IncludeEditLink = True
FancyTracebackContext = 5
Modified: Webware/trunk/WebKit/Cookie.py
==============================================================================
--- Webware/trunk/WebKit/Cookie.py (original)
+++ Webware/trunk/WebKit/Cookie.py Thu Sep 29 13:21:48 2005
@@ -1,22 +1,27 @@
from Common import *
# If this is Python 2.2 or greater, import the standard Cookie module
-# as CookieEngine. Otherwise, import WebUtils.Cookie as CookieEngine.
+# as CookieEngine. Otherwise, import WebUtils.Cookie as CookieEngine.
# This is because there is a nasty bug in the Cookie.py module
# included in Python 2.1 and earlier, and Python 1.5.2 doesn't even
# include Cookie.py at all.
pyVer = getattr(sys, 'version_info', None)
if pyVer and pyVer[:2] >= (2, 2):
- # Get Python's Cookie module. We have to do some work since
- # it has the same name as we do. So we'll strip out anything
+ # Get Python's Cookie module. We have to do some work since
+ # it has the same name as we do. So we'll strip out anything
# from the path that might cause us to import from the WebKit
# directory, then import Cookie using that restricted path --
# that ought to ensure that we're using Python's module.
- import imp, string, sys
- def ok(directory):
- return directory not in ['', '.'] and directory[-6:].lower() != 'webkit'
- path = filter(ok, sys.path)
+ import imp, string, sys, os
+ path = []
+ thisDir = os.path.abspath(os.path.dirname(__file__))
+ for dir in sys.path:
+ if not dir or dir == '.':
+ continue
+ if os.path.abspath(dir) == thisDir:
+ continue
+ path.append(dir)
try:
(file, pathname, description) = imp.find_module('Cookie', path)
CookieEngine = imp.load_module('Cookie', file, pathname, description)
@@ -33,14 +38,14 @@
class Cookie(Object):
- """
- Cookie is used to create cookies that have additional
- attributes beyond their value.
+ """Delicious cookies.
+
+ Cookie is used to create cookies that have additional attributes
+ beyond their value.
- Note that web browsers don't typically send any information
- with the cookie other than it's value. Therefore
- `HTTPRequest.cookie` simply returns a value such as an
- integer or a string.
+ Note that web browsers don't typically send any information with
+ the cookie other than it's value. Therefore `HTTPRequest.cookie`
+ simply returns a value such as an integer or a string.
When the server sends cookies back to the browser, it can send
a cookie that simply has a value, or the cookie can be
@@ -54,21 +59,26 @@
dictionaries, etc.
.. _`RFC 2109`: ftp://ftp.isi.edu/in-notes/rfc2109.txt
+
"""
- ## Future
- ##
- ## * This class should provide error checking in the setFoo()
- ## methods. Or maybe our internal Cookie implementation
- ## already does that?
- ## * This implementation is probably not as efficient as it
- ## should be, [a] it works and [b] the interface is stable.
- ## We can optimize later.
+ # Future
+ #
+ # * This class should provide error checking in the setFoo()
+ # methods. Or maybe our internal Cookie implementation
+ # already does that?
+ # * This implementation is probably not as efficient as it
+ # should be, [a] it works and [b] the interface is stable.
+ # We can optimize later.
+
+
+ ## Init ##
def __init__(self, name, value):
- """
- Create a cookie -- properties other than `name` and
- `value` are set with methods.
+ """Create a cookie.
+
+ Properties other than `name` and `value` are set with methods.
+
"""
self._cookies = CookieEngine.SimpleCookie()
@@ -78,12 +88,13 @@
self._cookie = self._cookies[name]
def __repr__(self):
- return '%s(id=0x%x, name=%r, domain=%r, path=%r, value=%r, expires=%r, maxAge=%r)' % (
- self.__class__.__name__, id(self), self.name(), self.domain(), self.path(), self.value(), self.expires(), self.maxAge())
+ return '%s(id=0x%x, name=%r, domain=%r,' \
+ ' path=%r, value=%r, expires=%r, maxAge=%r)' % (
+ self.__class__.__name__, id(self), self.name(), self.domain(),
+ self.path(), self.value(), self.expires(), self.maxAge())
- """
- **Accessors**
- """
+
+ ## Accessors ##
def comment(self):
return self._cookie['comment']
@@ -113,9 +124,7 @@
return self._cookie['version']
- """
- **Setters**
- """
+ ## Setters ##
def setComment(self, comment):
self._cookie['comment'] = comment
@@ -143,9 +152,7 @@
self._cookie['version'] = version
- """
- **Misc**
- """
+ ## Misc ##
def delete(self):
"""
@@ -155,7 +162,6 @@
the cookie for any browser (which one actually works
depends on the browser).
"""
-
self._value = ''
self._cookie['expires'] = "Mon, 01-Jan-1900 00:00:00 GMT"
self._cookie['max-age'] = 0
@@ -163,20 +169,12 @@
def headerValue(self):
- """
- Returns a string with the value that should be
- used in the HTTP headers. """
+ """Return header value.
+
+ Returns a string with the value that should be used
+ in the HTTP headers.
+ """
items = self._cookies.items()
assert(len(items)==1)
return items[0][1].OutputString()
-
- def headerString(self):
- """ @@ 2000-06-09 ce: Not typically needed now that
- raw responses are structured dictionaries instead of
- opaque strigns. headerValue() is used instead.
-
- :ignore:
- """
-
- return str(self._cookies)
Modified: Webware/trunk/WebKit/DebugAppServer.py
==============================================================================
--- Webware/trunk/WebKit/DebugAppServer.py (original)
+++ Webware/trunk/WebKit/DebugAppServer.py Thu Sep 29 13:21:48 2005
@@ -1,7 +1,9 @@
#!/usr/bin/env python
-"""
+
+"""DebugAppServer
+
DebugAppServer executes all requests within the main thread, allowing
-servlets to be easily debugged using any Python debugger. The drawback is
+servlets to be easily debugged using any Python debugger. The drawback is
that only one request can be processed at a time using this approach.
Also, exceptions are not caught and gracefully handled with an HTML error
@@ -24,7 +26,6 @@
a "close thread" is started up by the AppServer base class, but neither
of these two extra threads should pose any problems debugging servlets.
-
Tested on:
- WingIDE on Windows, http://wingware.com/
- PythonWin
@@ -43,26 +44,32 @@
class DebugAppServer(OriginalThreadedAppServer):
+ """Sigle-threaded AppServer for debugging purposes.
+
+ We are piggybacking on 99% of the code in ThreadedAppServer. Our
+ trick is to replace the request queue with a dummy object that
+ executes requests immediately instead of pushing them onto a queue
+ to be handled by other threads.
+
"""
- We are piggybacking on 99% of the code in ThreadedAppServer. Our
- trick is to replace the request queue with a dummy object
- that executes requests immediately instead of pushing them onto
- a queue to be handled by other threads.
- """
- excludePrefixes = 'WebKit MiscUtils WebUtils TaskKit'.split()
+
+ ## Init ##
+
+ excludePrefixes = ('WebKit', 'MiscUtils', 'WebUtils', 'TaskKit')
def __init__(self, path=None):
+ """Initialize DebugAppServer."""
# Initialize the base class
OriginalThreadedAppServer.__init__(self, path)
-
# Replace the request queue with a dummy object that merely
# runs request handlers as soon as they are "pushed"
self._requestQueue = DummyRequestQueue()
def config(self):
+ """Return default configuration."""
# Force ThreadedAppServer to create an empty thread pool by hacking
- # the settings to zero. This is not strictly necessary to do.
+ # the settings to zero. This is not strictly necessary to do.
if self._config is None:
OriginalThreadedAppServer.config(self)
self.setSetting('StartServerThreads', 0)
@@ -70,30 +77,37 @@
self.setSetting('MinServerThreads', 0)
return self._config
+
+ ## Overridden methods ##
+
def mainloop(self):
- """
- This is needed for COM support on Windows, because special
- thread initialization is required on any thread that runs
- servlets, in this case the main thread itself.
+ """Main loop for Windows.
+
+ This is needed for COM support on Windows, because special thread
+ initialization is required on any thread that runs servlets, in
+ this case the main thread itself.
+
"""
self.initThread()
OriginalThreadedAppServer.mainloop(self)
self.delThread()
def createApplication(self):
- """
- Creates and returns an application object. Invoked by __init__.
- """
+ """Create and return an application object. Invoked by __init__."""
return DebugApplication(server=self)
def restart(self):
- # The normal restart technique is to exit the application with a special exit code and let an exta-process script start the app server up again. That works poorly for a debugging environment which is attached to a particular process.
+ # The normal restart technique is to exit the application
+ # with a special exit code and let an exta-process script
+ # start the app server up again. That works poorly for a
+ # debugging environment which is attached to a particular process.
Profiler.reset()
self.initiateShutdown()
self._closeThread.join()
sys.stdout.flush()
sys.stderr.flush()
- ImportSpy.modloader.delModules(includePythonModules=False, excludePrefixes=self.excludePrefixes)
+ ImportSpy.modloader.delModules(includePythonModules=False,
+ excludePrefixes=self.excludePrefixes)
ImportSpy.reset()
raise ThreadedAppServer.RestartAppServerError
@@ -102,6 +116,9 @@
from WebKit.Transaction import Transaction
class DebugApplication(Application):
+
+ ## Overridden methods ##
+
# Don't handle exceptions gracefully because we want
# them to rise uncaught so the debugger will kick in.
@@ -110,31 +127,42 @@
# console and the debugger does not kick in.
def handleException(self):
- """
+ """Handle exception.
+
This should only be used in cases where there is no transaction object,
for example if an exception occurs when attempting to save a session
to disk.
+
"""
raise
def handleExceptionInTransaction(self, excInfo, transaction):
- """
- Rasies exception `excInfo` (as returned by
- ``sys.exc_info()``) that was generated by `transaction`.
+ """Handle exception with info.
+
+ Raises exception `excInfo` (as returned by ``sys.exc_info()``)
+ that was generated by `transaction`.
+
"""
raise
class DummyRequestQueue:
+ """This is a dummy replacement for the request queue.
+
+ It merely executes handlers as soon as they are "pushed".
+
"""
- This is a dummy replacement for the request queue. It merely
- executes handlers as soon as they are "pushed".
- """
+
+
+ ## Overridden methods ##
+
def put(self, handler):
handler.handleRequest()
handler.close()
+## Globals ##
+
# Replace ThreadedAppServer class in the ThreadedAppServer module with our
# DebugAppServer. This seems like an awful hack, but it works and
# requires less code duplication than other approaches I could think of, and
Modified: Webware/trunk/WebKit/Docs/ApplicationDevelopment.html
==============================================================================
--- Webware/trunk/WebKit/Docs/ApplicationDevelopment.html (original)
+++ Webware/trunk/WebKit/Docs/ApplicationDevelopment.html Thu Sep 29 13:21:48 2005
@@ -9,7 +9,7 @@
</head>
<body>
<div class="document" id="application-development-with-webware">
-<h1 class="title">Application Development With Webware</h1>
+<h1 class="header">Application Development With Webware</h1>
<p>Webware for Python</p>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
@@ -284,5 +284,8 @@
</div>
</div>
</div>
+<div class="footer">
+Webware for Python (<a href="http://www.webwareforpython.org">www.webwareforpython.org</a>)
+</div>
</body>
</html>
Modified: Webware/trunk/WebKit/Docs/Configuration.html
==============================================================================
--- Webware/trunk/WebKit/Docs/Configuration.html (original)
+++ Webware/trunk/WebKit/Docs/Configuration.html Thu Sep 29 13:21:48 2005
@@ -9,7 +9,7 @@
</head>
<body>
<div class="document" id="configuration-guide">
-<h1 class="title">Configuration Guide</h1>
+<h1 class="header">Configuration Guide</h1>
<div class="contents topic" id="contents">
<p class="topic-title first"><a name="contents">Contents</a></p>
<ul class="simple">
@@ -421,5 +421,8 @@
</dl>
</div>
</div>
+<div class="footer">
+Webware for Python (<a href="http://www.webwareforpython.org">www.webwareforpython.org</a>)
+</div>
</body>
</html>
Modified: Webware/trunk/WebKit/Docs/Developing.html
==============================================================================
--- Webware/trunk/WebKit/Docs/Developing.html (original)
+++ Webware/trunk/WebKit/Docs/Developing.html Thu Sep 29 13:21:48 2005
@@ -9,7 +9,7 @@
</head>
<body>
<div class="document" id="developing-webware">
-<h1 class="title">Developing Webware</h1>
+<h1 class="header">Developing Webware</h1>
<p>This document should outline the details you need to understand
Webware and WebKit internals, and assist in becoming a more advanced
Webware programmer.</p>
@@ -60,5 +60,8 @@
<p>A plugin who's <tt class="docutils literal"><span class="pre">requiredPyVersion</span></tt> or <tt class="docutils literal"><span class="pre">requiredOpSys</span></tt> aren't satisfied will simply be ignored. <tt class="docutils literal"><span class="pre">requiredOpSys</span></tt> should be something returned by <tt class="docutils literal"><span class="pre">os.name</span></tt>, like <tt class="docutils literal"><span class="pre">posix</span></tt> or <tt class="docutils literal"><span class="pre">nt</span></tt>. Or you can define a function <tt class="docutils literal"><span class="pre">willRunFunc</span></tt> to test. If there aren't requirements you can leave these variables and functions out.</p>
</div>
</div>
+<div class="footer">
+Webware for Python (<a href="http://www.webwareforpython.org">www.webwareforpython.org</a>)
+</div>
</body>
</html>
Modified: Webware/trunk/WebKit/Docs/Developing.txt
==============================================================================
--- Webware/trunk/WebKit/Docs/Developing.txt (original)
+++ Webware/trunk/WebKit/Docs/Developing.txt Thu Sep 29 13:21:48 2005
@@ -60,7 +60,11 @@
else:
return None
-The documents (e.g. ``QuickStart.html``) should be located in a ``Docs/`` subdirectory. The example pages go in an ``Examples/`` subdirectory.
-
-A plugin who's ``requiredPyVersion`` or ``requiredOpSys`` aren't satisfied will simply be ignored. ``requiredOpSys`` should be something returned by ``os.name``, like ``posix`` or ``nt``. Or you can define a function ``willRunFunc`` to test. If there aren't requirements you can leave these variables and functions out.
+The documents (e.g. ``QuickStart.html``) should be located in a ``Docs/``
+subdirectory. The example pages go in an ``Examples/`` subdirectory.
+A plugin who's ``requiredPyVersion`` or ``requiredOpSys`` aren't satisfied will
+simply be ignored. ``requiredOpSys`` should be something returned by
+``os.name``, like ``posix`` or ``nt``. Or you can define a function
+``willRunFunc`` to test. If there aren't requirements you can leave these
+variables and functions out.
Modified: Webware/trunk/WebKit/Docs/InstallGuide.html
==============================================================================
--- Webware/trunk/WebKit/Docs/InstallGuide.html (original)
+++ Webware/trunk/WebKit/Docs/InstallGuide.html Thu Sep 29 13:21:48 2005
@@ -9,7 +9,7 @@
</head>
<body>
<div class="document" id="webkit-install-guide">
-<h1 class="title">WebKit Install Guide</h1>
+<h1 class="header">WebKit Install Guide</h1>
<div class="contents topic" id="contents">
<p class="topic-title first"><a name="contents">Contents</a></p>
<ul class="simple">
@@ -982,5 +982,8 @@
</div>
</div>
</div>
+<div class="footer">
+Webware for Python (<a href="http://www.webwareforpython.org">www.webwareforpython.org</a>)
+</div>
</body>
</html>
Modified: Webware/trunk/WebKit/Docs/Tutorial.html
==============================================================================
--- Webware/trunk/WebKit/Docs/Tutorial.html (original)
+++ Webware/trunk/WebKit/Docs/Tutorial.html Thu Sep 29 13:21:48 2005
@@ -9,7 +9,7 @@
</head>
<body>
<div class="document" id="beginner-tutorial">
-<h1 class="title">Beginner Tutorial</h1>
+<h1 class="header">Beginner Tutorial</h1>
<p>Webware for Python</p>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
@@ -366,5 +366,8 @@
</div>
</div>
</div>
+<div class="footer">
+Webware for Python (<a href="http://www.webwareforpython.org">www.webwareforpython.org</a>)
+</div>
</body>
</html>
Modified: Webware/trunk/WebKit/Docs/UsersGuide.html
==============================================================================
--- Webware/trunk/WebKit/Docs/UsersGuide.html (original)
+++ Webware/trunk/WebKit/Docs/UsersGuide.html Thu Sep 29 13:21:48 2005
@@ -9,7 +9,7 @@
</head>
<body>
<div class="document" id="webkit-user-s-guide">
-<h1 class="title">WebKit User's Guide</h1>
+<h1 class="header">WebKit User's Guide</h1>
<p>Webware for Python</p>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
@@ -373,5 +373,8 @@
Servlets.</p>
</div>
</div>
+<div class="footer">
+Webware for Python (<a href="http://www.webwareforpython.org">www.webwareforpython.org</a>)
+</div>
</body>
</html>
Modified: Webware/trunk/WebKit/ExceptionHandler.py
==============================================================================
--- Webware/trunk/WebKit/ExceptionHandler.py (original)
+++ Webware/trunk/WebKit/ExceptionHandler.py Thu Sep 29 13:21:48 2005
@@ -32,11 +32,11 @@
The handler write methods that may be useful are:
- .. inline:: write
- .. inline:: writeTitle
- .. inline:: writeDict
- .. inline:: writeTable
- .. inline:: writeAttrs
+ * write
+ * writeTitle
+ * writeDict
+ * writeTable
+ * writeAttrs
Derived classes must not assume that the error occured in a
transaction. self._tra may be None for exceptions outside
@@ -68,7 +68,8 @@
hideValuesForFields = ['password', 'passwd', 'pwd',
'creditcard', 'credit card', 'cc']
if 0: # for testing
- hideValuesForFields.extend('application uri http_accept userid'.split())
+ hideValuesForFields.extend(['application', 'uri',
+ 'http_accept', 'userid'])
hiddenString = '*** hidden ***'
Modified: Webware/trunk/WebKit/Experimental/HTTPExceptions.py
==============================================================================
--- Webware/trunk/WebKit/Experimental/HTTPExceptions.py (original)
+++ Webware/trunk/WebKit/Experimental/HTTPExceptions.py Thu Sep 29 13:21:48 2005
@@ -1,6 +1,9 @@
from types import *
-True, False = 1==1, 0==1
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
class HTTPError(Exception):
@@ -83,7 +86,7 @@
return HTTPError.codeMessage(servlet)
else:
return servlet.loginBox(focus=True)
-
+
def headers(self, servlet=None):
return {'WWW-Authenticate': 'Basic realm="%s"' % self._realm}
Modified: Webware/trunk/WebKit/Experimental/NewPage.py
==============================================================================
--- Webware/trunk/WebKit/Experimental/NewPage.py (original)
+++ Webware/trunk/WebKit/Experimental/NewPage.py Thu Sep 29 13:21:48 2005
@@ -6,7 +6,10 @@
from WebKit.Cookie import Cookie
from types import *
-True, False = 1==1, 0==1
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
class NewPage(Servlet):
@@ -52,7 +55,7 @@
to be initialized may not have been.
"""
pass
-
+
def notImplemented(self):
raise self.exc.NotImplemented
@@ -61,13 +64,13 @@
def statusCreated(self):
self.response().setStatus(201, 'Created')
-
+
def statusNoContent(self):
self.response().setStatus(204, 'No Content')
-
+
def statusMultiStatus(self):
self.response().setStatus(207, 'Multi-Status')
-
+
def host(self):
"""The hostname of the server, with port number if necessary
(i.e., not 80)"""
@@ -180,11 +183,11 @@
def respondToGet(self):
""" Invokes _respond() to handle the transaction. """
self._respond()
-
+
def respondToPost(self):
""" Invokes _respond() to handle the transaction. """
self._respond()
-
+
def respondToHead(self):
"""
A correct but inefficient implementation.
@@ -215,7 +218,7 @@
self.writeHTML()
## Access ##
-
+
def application(self):
return self._transaction.application()
@@ -286,7 +289,7 @@
def writeHTML(self):
"""
Writes all the HTML for the page.
-
+
Subclasses may override this method (which is invoked by
respondToGet() and respondToPost()) or more commonly its
constituent methods, writeDocType(), writeHead() and
@@ -310,7 +313,7 @@
Invoked by writeHTML() to write the <!DOCTYPE ...> tag. This
implementation specifies HTML 4.01 Transitional. Subclasses may
override to specify something else.
-
+
You can find out more about doc types by searching for DOCTYPE
on the web, or visiting:
http://www.htmlhelp.com/tools/validator/doctype.html
@@ -331,7 +334,7 @@
Writes the parts inside the <head>...</head> tags. It's best
to override title(), styleSheet(), styleSheetHref(), and
headJavaScript(), not the write* methods.
-
+
Subclasses can override this to add additional items and
should invoke super.
"""
@@ -456,7 +459,7 @@
## Actions ##
-
+
def actions(self):
"""
Returns a list of method names that are allowable actions from
@@ -469,13 +472,13 @@
def htmlEncode(self, s):
return Funcs.htmlEncode(s)
-
+
def htmlDecode(self, s):
return Funcs.htmlDecode(s)
def urlEncode(self, s):
return Funcs.urlEncode(s)
-
+
def urlDecode(self, s):
return Funcs.urlDecode(s)
@@ -509,7 +512,7 @@
return apply(self.application().callMethodOfServlet, (self.transaction(), URL, method) + args, kwargs)
## Self utility ##
-
+
def sessionEncode(self, url=None):
"""
Utility function to access session.sessionEncode.
Modified: Webware/trunk/WebKit/HTTPExceptions.py
==============================================================================
--- Webware/trunk/WebKit/HTTPExceptions.py (original)
+++ Webware/trunk/WebKit/HTTPExceptions.py Thu Sep 29 13:21:48 2005
@@ -1,4 +1,5 @@
-"""
+"""HTTPExceptions
+
HTTPExceptions are for situations that are predicted by the
HTTP spec. Where the ``200 OK`` response is typical, a
``404 Not Found`` or ``301 Moved Temporarily`` response is not
@@ -14,13 +15,15 @@
from types import *
from WebUtils.Funcs import htmlEncode
-True, False = 1==1, 0==1
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
class HTTPException(Exception):
+ """HTTPException template class.
- """
- Subclasses must define these variables (usually as class
- variables):
+ Subclasses must define these variables (usually as class variables):
`_code`:
a tuple of the integer error code, and the short
@@ -29,28 +32,28 @@
the long-winded description, to be presented
in the response page. Or you can override description()
if you want something more context-sensitive.
+
"""
+
+ ## Error codes ##
+
_description = 'An error occurred'
def code(self):
- """
- The integer code
- """
+ """The integer code."""
return self._code[0]
def codeMessage(self):
- """
- The message (like ``Not Found``) that goes with the code
- """
+ """The message (like ``Not Found``) that goes with the code."""
return self._code[1]
- """
- **HTML Description**
- """
+
+ ## HTML Description ##
def html(self):
- """
+ """The error page.
+
The HTML page that should be sent with the error,
usually a description of the problem.
"""
@@ -67,44 +70,40 @@
}
def title(self):
- """
- The title used in the HTML page
- """
+ """The title used in the HTML page."""
return self.codeMessage()
def htTitle(self):
- """
- The title, but it may include HTML markup (like
- italics)
- """
+ """The title, but it may include HTML markup (like italics)."""
return self.title()
def htBody(self):
- """
- The HTML body of the page.
- """
+ """The HTML body of the page."""
body = self.htDescription()
if self.args:
body += ''.join(['<p>%s</p>\n' % str(l) for l in self.args])
return body
def description(self):
- """
+ """Error description.
+
Possibly a plain text version of the error description,
though usually just identical to `htDescription`.
+
"""
return self._description
def htDescription(self):
- """
+ """HTML error description.
+
The HTML description of the error, for presentation
to the browser user.
+
"""
return self.description()
- """
- **Misc**
- """
+
+ ## Misc ##
def headers(self):
"""
@@ -125,46 +124,44 @@
class HTTPNotImplemented(HTTPException):
+ """HTTPExcecption "not implemented" subclass.
- """
When methods (like GET, POST, PUT, PROPFIND, etc) are not
implemented for this resource.
- """
+ """
_code = 501, "Not Implemented"
_description = "The method given is not yet implemented by this application"
class HTTPForbidden(HTTPException):
+ """HTTPExcecption "forbidden" subclass.
- """
- When access is not allowed to this resource. If the user is
- anonymous, and must be authenticated, then
- HTTPAuthenticationRequired is a preferable exception. If the
- user should not be able to get to this resource (at least through
- the path they did), or is authenticated and still doesn't have
- access, or no one is allowed to view this, then HTTPForbidden would
+ When access is not allowed to this resource. If the user is anonymous,
+ and must be authenticated, then HTTPAuthenticationRequired is a preferable
+ exception. If the user should not be able to get to this resource (at
+ least through the path they did), or is authenticated and still doesn't
+ have access, or no one is allowed to view this, then HTTPForbidden would
be the proper response.
- """
+ """
_code = 403, 'Forbidden'
_description = "You are not authorized to access this resource"
class HTTPAuthenticationRequired(HTTPException):
+ """HTTPExcecption "authentication required" subclass.
- """
- HTTPAuthenticationRequired will usually cause the browser
- to open up an HTTP login box, and after getting login information
- from the user, the browser will resubmit the request. However,
- this should also trigger login pages in properly set up
- environments (though much code will not work this way).
-
- Browsers will usually not send authentication information unless
- they receive this response, even when other pages on the site
- have given 401 responses before. So when using this
- authentication every request will usually be doubled, once
- without authentication, once with.
- """
+ HTTPAuthenticationRequired will usually cause the browser to open up an
+ HTTP login box, and after getting login information from the user, the
+ browser will resubmit the request. However, this should also trigger
+ login pages in properly set up environments (though much code will not
+ work this way).
+
+ Browsers will usually not send authentication information unless they
+ receive this response, even when other pages on the site have given 401
+ responses before. So when using this authentication every request will
+ usually be doubled, once without authentication, once with.
+ """
_code = 401, 'Authentication Required'
_description = "You must log in to access this resource"
def __init__(self, realm=None, *args):
@@ -177,84 +174,82 @@
def headers(self):
return {'WWW-Authenticate': 'Basic realm="%s"' % self._realm}
+
+# This is for wording mistakes. I'm unsure about their benefit.
+HTTPAuthorizationRequired = HTTPAuthenticationRequired
"""
There is also an alias `HTTPAuthorizationRequired`, as it is hard
to distinguish between these two names.
"""
-## This is for wording mistakes. I'm unsure about their benefit.
-HTTPAuthorizationRequired = HTTPAuthenticationRequired
-
class HTTPMethodNotAllowed(HTTPException):
- """
+ """HTTPExcecption "method not allowed" subclass.
+
When a method (like GET, PROPFIND, POST, etc) is not allowed
on this resource (usually because it does not make sense, not
- because it is not permitted). Mostly for WebDAV.
- """
+ because it is not permitted). Mostly for WebDAV.
+ """
_code = 405, 'Method Not Allowed'
_description = 'The method is not supported on this resource'
class HTTPConflict(HTTPException):
- """
- When there's a locking conflict on this resource (in response
- to something like a PUT, not for most other conflicts).
- Mostly for WebDAV.
- """
+ """HTTPExcecption "conflict" subclass.
- _code = 409, 'Conflict'
+ When there's a locking conflict on this resource (in response to something
+ like a PUT, not for most other conflicts). Mostly for WebDAV.
-class HTTPUnsupportedMediaType(HTTPException):
- """
- Uhh....
"""
+ _code = 409, 'Conflict'
+class HTTPUnsupportedMediaType(HTTPException):
+ """HTTPExcecption "unsupported media type" subclass."""
_code = 415, 'Unsupported Media Type'
class HTTPInsufficientStorage(HTTPException):
- """
- When there is not sufficient storage, usually in response
- to a PUT when there isn't enough disk space. Mostly for WebDAV.
- """
+ """HTTPExcecption "insufficient storage" subclass.
+ When there is not sufficient storage, usually in response to a PUT when
+ there isn't enough disk space. Mostly for WebDAV.
+
+ """
_code = 507, 'Insufficient Storage'
_description = 'There was not enough storage space on the server to complete your request'
class HTTPPreconditionFailed(HTTPException):
- """
- During compound, atomic operations, when a precondition for
- an early operation fail, then later operations in will fail
- with this code. Mostly for WebDAV.
- """
+ """HTTPExcecption "Precondition Failed" subclass.
+
+ During compound, atomic operations, when a precondition for an early
+ operation fail, then later operations in will fail with this code.
+ Mostly for WebDAV.
+ """
_code = 412, 'Precondition Failed'
class HTTPMovedPermanently(HTTPException):
- """
- When a resource is permanently moved. The browser may remember
- this relocation, and later requests may skip requesting the
- original resource altogether.
- """
+ """HTTPExcecption "moved permanently" subclass.
+
+ When a resource is permanently moved. The browser may remember this
+ relocation, and later requests may skip requesting the original
+ resource altogether.
+ """
_code = 301, 'Moved Permanently'
def __init__(self, location=None, webkitLocation=None, *args):
- """
- HTTPMovedPermanently needs a destination that you
- it should be directed to -- you can pass `location`
- *or* `webkitLocation` -- if you pass `webkitLocation`
- it will be relative to the WebKit base (the portion
- through the adapter).
- """
+ """Set destination.
+ HTTPMovedPermanently needs a destination that you it should be
+ directed to -- you can pass `location` *or* `webkitLocation` --
+ if you pass `webkitLocation` it will be relative to the WebKit base
+ (the portion through the adapter).
+ """
self._location = location
self._webkitLocation = webkitLocation
HTTPException.__init__(self, 301, 'Moved Permanently', *args)
def location(self):
- """
- The location that we will be redirecting to
- """
+ """The location that we will be redirecting to."""
if self._webkitLocation:
environ = self._transaction.request().environ()
if not self._webkitLocation.startswith('/'):
@@ -267,9 +262,7 @@
return location
def headers(self):
- """
- We include a Location header
- """
+ """We include a Location header."""
return {'Location': self.location()}
def description(self):
@@ -277,33 +270,35 @@
' <a href="%s">%s</a>' % ((htmlEncode(self.location()),)*2)
class HTTPTemporaryRedirect(HTTPMovedPermanently):
+ """HTTPExcecption "temporary tedirect" subclass.
- """
- Like HTTPMovedPermanently, except the redirect is only valid for
- this request. Internally identical to HTTPMovedPermanently,
- except with a different response code. Browsers will check the
- server for each request to see where it's redirected to.
- """
+ Like HTTPMovedPermanently, except the redirect is only valid for this
+ request. Internally identical to HTTPMovedPermanently, except with a
+ different response code. Browsers will check the server for each request
+ to see where it's redirected to.
+ """
_code = 307, 'Temporary Redirect'
# This is what people mean most often:
HTTPRedirect = HTTPTemporaryRedirect
class HTTPBadRequest(HTTPException):
- """
+ """HTTPExcecption "bad request" subclass.
+
When the browser sends an invalid request.
- """
+ """
_code = 400, 'Bad Request'
class HTTPNotFound(HTTPException):
- """
- When the requested resource does not exist. To be more secretive,
- it is okay to return a 404 if access to the resource is not
- permitted (you are not required to use HTTPForbidden, though it
- makes it more clear why access was disallowed).
- """
+ """HTTPExcecption "not found" subclass.
+ When the requested resource does not exist. To be more secretive,
+ it is okay to return a 404 if access to the resource is not permitted
+ (you are not required to use HTTPForbidden, though it makes it more
+ clear why access was disallowed).
+
+ """
_code = 404, 'Not Found'
_description = 'The resource you were trying to access was not found'
Modified: Webware/trunk/WebKit/HTTPRequest.py
==============================================================================
--- Webware/trunk/WebKit/HTTPRequest.py (original)
+++ Webware/trunk/WebKit/HTTPRequest.py Thu Sep 29 13:21:48 2005
@@ -37,12 +37,13 @@
self._environ = dict['environ']
self._input = dict['input']
self._requestID = dict['requestID']
- self._fields = FieldStorage.FieldStorage(self._input, environ=self._environ, keep_blank_values=1, strict_parsing=0)
+ self._fields = FieldStorage.FieldStorage(self._input,
+ environ=self._environ, keep_blank_values=1, strict_parsing=0)
self._fields.parse_qs()
self._cookies = Cookie()
if self._environ.has_key('HTTP_COOKIE'):
- # Protect the loading of cookies with an exception handler, because some cookies
- # from IE are known to break the cookie module.
+ # Protect the loading of cookies with an exception handler,
+ # because some cookies from MSIE are known to break the cookie module.
try:
self._cookies.load(self._environ['HTTP_COOKIE'])
except:
@@ -86,13 +87,14 @@
self._adapterName = self._environ.get('SCRIPT_NAME', '')
- # We use the cgi module to get the fields, but then change them into an ordinary dictionary of values
+ # We use the cgi module to get the fields,
+ # but then change them into an ordinary dictionary of values:
try:
keys = self._fields.keys()
except TypeError:
- # This can happen if, for example, the request is an XML-RPC request, not
- # a regular POST from an HTML form. In that case we just create an empty
- # set of fields.
+ # This can happen if, for example, the request is an XML-RPC request,
+ # not a regular POST from an HTML form. In that case we just create
+ # an empty set of fields.
keys = []
dict = {}
@@ -101,10 +103,12 @@
if type(value) is not ListType:
if value.filename:
if debug: print "Uploaded File Found"
- else: # i.e., if we don't have a list, we have one of those cgi.MiniFieldStorage objects.
+ else: # i.e., if we don't have a list,
+ # we have one of those cgi.MiniFieldStorage objects.
value = value.value # Get it's value.
else:
- value = map(lambda miniFieldStorage: miniFieldStorage.value, value) # extract those .value's
+ value = map(lambda miniFieldStorage: miniFieldStorage.value,
+ value) # extract those .value's
dict[key] = value
self._fieldStorage = self._fields
@@ -135,10 +139,13 @@
self._pathSession = self._environ['PATH_INFO'][7:].split('/',1)[0]
self._cookies['_SID_'] = self._pathSession
sidstring = '_SID_=' + self._pathSession +'/'
- self._environ['REQUEST_URI'] = self._environ['REQUEST_URI'].replace(sidstring,'')
- self._environ['PATH_INFO'] = self._environ['PATH_INFO'].replace(sidstring,'')
+ self._environ['REQUEST_URI'] = self._environ[
+ 'REQUEST_URI'].replace(sidstring,'')
+ self._environ['PATH_INFO'] = self._environ[
+ 'PATH_INFO'].replace(sidstring,'')
if self._environ.has_key('PATH_TRANSLATED'):
- self._environ['PATH_TRANSLATED'] = self._environ['PATH_TRANSLATED'].replace(sidstring,'')
+ self._environ['PATH_TRANSLATED'] = self._environ[
+ 'PATH_TRANSLATED'].replace(sidstring,'')
assert(not self._environ.has_key('WK_URI')) # obsolete?
self._sessionExpired = 0
@@ -146,7 +153,8 @@
# Save the original urlPath.
self._originalURLPath = self.urlPath()
- if debug: print "Done setting up request, found keys %s" % repr(self._fields.keys())
+ if debug:
+ print "Done setting up request, found keys %r" % self._fields.keys()
## Transactions ##
@@ -615,8 +623,8 @@
Equivalent to CGI variable PATH_TRANSLATED.
"""
-# return self._environ['PATH_TRANSLATED']
-# @@ 2000-06-22 ce: resolve this
+ # return self._environ['PATH_TRANSLATED']
+ # @@ 2000-06-22 ce: resolve this
return self._environ.get('PATH_TRANSLATED', None)
def queryString(self):
@@ -738,29 +746,7 @@
' queryString method sessionId parents fields cookies environ'.split())
- ## Deprecated ##
-
- def serverSideDir(self):
- """deprecated: HTTPRequest.serverSideDir() on 01/24/01 in 0.5. use serverSidePath() instead.
-
- Returns the directory of the Servlet (as given through __init__()'s path).
-
- """
- self.deprecated(self.serverSideDir)
- if not hasattr(self, '_serverSideDir'):
- self._serverSideDir = os.path.dirname(self.serverSidePath())
- return self._serverSideDir
-
- def relativePath(self, joinPath):
- """deprecated: HTTPRequest.relativePath() on 01/24/01 in 0.5. use serverSidePath() instead.
-
- Returns a new path which includes the servlet's path appended by 'joinPath'.
- Note that if 'joinPath' is an absolute path, then only 'joinPath' is returned.
-
- """
- self.deprecated(self.relativePath)
- return os.path.join(self.serverSideDir(), joinPath)
-
+## Info Structure ##
_infoMethods = (
HTTPRequest.servletPath,
Modified: Webware/trunk/WebKit/HTTPResponse.py
==============================================================================
--- Webware/trunk/WebKit/HTTPResponse.py (original)
+++ Webware/trunk/WebKit/HTTPResponse.py Thu Sep 29 13:21:48 2005
@@ -18,7 +18,10 @@
DateTime = None
from MiscUtils.DateInterval import timeDecode
-True, False = 1==1, 0==1
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
debug = 0
@@ -56,22 +59,21 @@
return self._headers.has_key(string.lower(name))
def setHeader(self, name, value):
+ """Sets a specific header by name.
+
+ Parameters:
+ name: Header Name
+ value: Header Value
+
"""
- Sets a specific header by name.
- - parameters:
- name: Header Name
- value: Header Value
- """
- assert self._committed==0, "Headers have already been sent"
+ assert self._committed == 0, "Headers have already been sent"
self._headers[string.lower(name)] = value
def addHeader(self, name, value):
- """
- Adds a specific header by name.
- """
- print "addHeader is deprecated. Use setHeader()."
- assert self._committed==0
+ """Adds a specific header by name."""
+ print "addHeader is deprecated. Use setHeader()."
+ assert self._committed == 0
self.setHeader(name, value)
def headers(self, name=None):
@@ -79,33 +81,37 @@
return self._headers
def clearHeaders(self):
- """ Clears all the headers. You might consider a setHeader('Content-type', 'text/html') or something similar after this. """
- assert self._committed==0
+ """Clears all the headers.
+
+ You might consider a setHeader('Content-type', 'text/html')
+ or something similar after this.
+
+ """
+ assert self._committed == 0
self._headers = {}
## Cookies ##
def cookie(self, name):
- """ Returns the value of the specified cookie. """
+ """Returns the value of the specified cookie."""
return self._cookies[name]
def hasCookie(self, name):
- """
- Returns true if the specified cookie is present.
- """
+ """Returns True if the specified cookie is present."""
return self._cookies.has_key(name)
-
+
def setCookie(self, name, value, path='/', expires='ONCLOSE',
secure=False):
- """
- Set a cookie. You can also set the path (which defaults to /),
- You can also set when it expires. It can expire:
+ """Set a cookie.
+
+ You can also set the path (which defaults to /).
+ You can also set when it expires. It can expire:
'NOW': this is the same as trying to delete it, but it
doesn't really seem to work in IE
'ONCLOSE': the default behavior for cookies (expires when
- the browser closes)
+ the browser closes)
'NEVER': some time in the far, far future.
integer: a timestamp value
tuple: a tuple, as created by the time module
@@ -113,10 +119,11 @@
be *local*, not GMT time)
DateTimeDelta: a interval from the present, e.g.,
DateTime.DateTimeDelta(month=1) (1 month in the future)
- '+...': a time in the future, '...' should be something like
+ '+...': a time in the future, '...' should be something like
1w (1 week), 3h46m (3:45), etc. You can use y (year),
- b (month), w (week), d (day), h (hour), m (minute),
- s (second). This is done by the MiscUtils.DateInterval.
+ b (month), w (week), d (day), h (hour), m (minute),
+ s (second). This is done by the MiscUtils.DateInterval.
+
"""
cookie = Cookie(name, value)
if expires == 'ONCLOSE' or not expires:
@@ -140,7 +147,7 @@
if type(t) in (TupleType, TimeTupleType):
t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", t)
if DateTime and \
- (type(t) is DateTime.DateTimeDeltaType
+ (type(t) is DateTime.DateTimeDeltaType
or isinstance(t, DateTime.RelativeDateTime)):
t = DateTime.now() + t
if DateTime and type(t) is DateTime.DateTimeType:
@@ -153,92 +160,97 @@
self.addCookie(cookie)
def addCookie(self, cookie):
+ """Adds a cookie that will be sent with this response.
+
+ cookie is a Cookie object instance. See WebKit.Cookie.
+
"""
- Adds a cookie that will be sent with this response.
- cookie is a Cookie object instance. See WebKit.Cookie.
- """
- assert self._committed==0
+ assert self._committed == 0
assert isinstance(cookie, Cookie)
self._cookies[cookie.name()] = cookie
def delCookie(self, name):
- """
- Deletes a cookie at the browser. To do so, one has
- to create and send to the browser a cookie with
- parameters that will cause the browser to delete it.
- """
- if self._cookies.has_key(name):
- self._cookies[name].delete()
- else:
- cookie = Cookie(name, None)
- cookie.delete()
- self.addCookie(cookie)
+ """Deletes a cookie at the browser.
+
+ To do so, one has to create and send to the browser a cookie with
+ parameters that will cause the browser to delete it.
- def cookies(self):
"""
- Returns a dictionary-style object of all Cookie objects that will be sent
- with this response.
+ if self._cookies.has_key(name):
+ self._cookies[name].delete()
+ else:
+ cookie = Cookie(name, None)
+ cookie.delete()
+ self.addCookie(cookie)
+
+ def cookies(self):
+ """Get all the cookies.
+
+ Returns a dictionary-style object of all Cookie objects that will
+ be sent with this response.
+
"""
return self._cookies
def clearCookies(self):
- """ Clears all the cookies. """
- assert self._committed==0
+ """Clears all the cookies."""
+ assert self._committed == 0
self._cookies = {}
## Status ##
def setStatus(self, code, msg=''):
- """ Set the status code of the response, such as 200, 'OK'. """
- assert self._committed==0, "Headers already sent."
+ """Set the status code of the response, such as 200, 'OK'."""
+ assert self._committed == 0, "Headers already sent."
self.setHeader('Status', str(code) + ' ' + msg)
## Special responses ##
def sendError(self, code, msg=''):
- """
- Sets the status code to the specified code and message.
- """
- assert self._committed==0, "Response already partially sent"
+ """Sets the status code to the specified code and message."""
+ assert self._committed == 0, "Response already partially sent"
self.setStatus(code, msg)
def sendRedirect(self, url):
- """
+ """Redirect to another url.
+
This method sets the headers and content for the redirect, but does
NOT change the cookies. Use clearCookies() as appropriate.
@@ 2002-03-21 ce: I thought cookies were ignored by user agents if a
redirect occurred. We should verify and update code or docs as appropriate.
+
"""
# ftp://ftp.isi.edu/in-notes/rfc2616.txt
# Sections: 10.3.3 and others
- assert self._committed==0, "Headers already sent"
+ assert self._committed == 0, "Headers already sent"
self.setHeader('Status', '302 Redirect')
self.setHeader('Location', url)
self.setHeader('Content-type', 'text/html')
- self.write('<html> <body> This page has been redirected to <a href="%s">%s</a>. </body> </html>' % (url, url))
+ self.write('<html><body>This page has been redirected'
+ ' to <a href="%s">%s</a>.</body> </html>' % (url, url))
## Output ##
def write(self, charstr=None):
- """
- Write charstr to the response stream.
- """
+ """Write charstr to the response stream."""
if charstr: self._strmOut.write(charstr)
if not self._committed and self._strmOut._needCommit:
self.commit()
def flush(self, autoFlush=1):
- """
- Send all accumulated response data now.
+ """Send all accumulated response data now.
+
Commits the response headers and tells the underlying stream to flush.
- if autoFlush is 1, the responseStream will flush itself automatically from now on.
+ if autoFlush is 1, the responseStream will flush itself automatically
+ from now on.
+
"""
if not self._committed:
self.commit()
@@ -247,36 +259,43 @@
def isCommitted(self):
- """
- Has the reponse already been partially or completely sent?
- If this returns true, no new headers/cookies can be added to the response.
+ """Checks whether response is already commited.
+
+ Checks whether the reponse has already been partially or completely sent.
+ If this returns true, no new headers/cookies can be added
+ to the response.
+
"""
return self._committed
def deliver(self):
- """
+ """Deliver response.
+
The final step in the processing cycle.
Not used for much with responseStreams added.
+
"""
- if debug: print "HTTPResponse deliver called"
+ if debug:
+ print "HTTPResponse deliver called"
self.recordEndTime()
if not self._committed: self.commit()
def commit(self):
- """
+ """Commit response.
+
Write out all headers to the reponse stream, and tell the underlying
response stream it can start sending data.
+
"""
- if debug: print "HTTPResponse commit"
+ if debug:
+ print "HTTPResponse commit"
self.recordSession()
self.writeHeaders()
self._committed = 1
self._strmOut.commit()
def writeHeaders(self):
- """
- Write headers to the response stream. Used internally.
- """
+ """Write headers to the response stream. Used internally."""
if self._committed:
print "response.writeHeaders called when already committed"
return
@@ -296,7 +315,21 @@
self._strmOut.prepend(headerstring)
def recordSession(self):
- """ Invoked by commit() to record the session id in the response (if a session exists). This implementation sets a cookie for that purpose. For people who don't like sweets, a future version could check a setting and instead of using cookies, could parse the HTML and update all the relevant URLs to include the session id (which implies a big performance hit). Or we could require site developers to always pass their URLs through a function which adds the session id (which implies pain). Personally, I'd rather just use cookies. You can experiment with different techniques by subclassing Session and overriding this method. Just make sure Application knows which "session" class to use. """
+ """Record session id.
+
+ Invoked by commit() to record the session id in the response
+ (if a session exists). This implementation sets a cookie for
+ that purpose. For people who don't like sweets, a future version
+ could check a setting and instead of using cookies, could parse
+ the HTML and update all the relevant URLs to include the session id
+ (which implies a big performance hit). Or we could require site
+ developers to always pass their URLs through a function which adds
+ the session id (which implies pain). Personally, I'd rather just
+ use cookies. You can experiment with different techniques by
+ subclassing Session and overriding this method. Just make sure
+ Application knows which "session" class to use.
+
+ """
if not self._transaction.application().setting('UseCookieSessions', True):
return
sess = self._transaction._session
@@ -313,21 +346,27 @@
if debug: print prefix, 'did not set sid'
def reset(self):
- """
- Resets the response (such as headers, cookies and contents).
- """
-
+ """Reset the response (such as headers, cookies and contents)."""
assert self._committed == 0, "Cannot reset the response; it has already been sent."
self._headers = {}
self.setHeader('Content-type','text/html')
self._cookies = {}
self._strmOut.clear()
-
def rawResponse(self):
- """ Returns the final contents of the response. Don't invoke this method until after deliver().
- Returns a dictionary representing the response containing only strings, numbers, lists, tuples, etc. with no backreferences. That means you don't need any special imports to examine the contents and you can marshal it. Currently there are two keys. 'headers' is list of tuples each of which contains two strings: the header and it's value. 'contents' is a string (that may be binary (for example, if an image were being returned)). """
+ """Return the final contents of the response.
+
+ Don't invoke this method until after deliver().
+ Returns a dictionary representing the response containing only
+ strings, numbers, lists, tuples, etc. with no backreferences.
+ That means you don't need any special imports to examine the contents
+ and you can marshal it. Currently there are two keys. 'headers' is
+ list of tuples each of which contains two strings: the header and
+ it's value. 'contents' is a string (that may be binary, for example,
+ if an image were being returned).
+
+ """
headers = []
for key, value in self._headers.items():
headers.append((key, value))
@@ -339,31 +378,37 @@
}
def size(self):
- """
- Returns the size of the final contents of the response. Don't invoke this
- method until after deliver().
+ """Return the size of the final contents of the response.
+
+ Don't invoke this method until after deliver().
+
"""
return self._strmOut.size()
-## def appendRawResponse(self, rawRes):
-## """
-## Appends the contents of the raw response (as returned by some transaction's rawResponse() method) to this response.
-## The headers of the receiving response take precedence over the appended response.
-## This method was built primarily to support Application.forwardRequest().
-## """
-## assert self._committed==0
-## headers = rawRes.get('headers', [])
-## for key, value in headers:
-## if not self._headers.has_key(key):
-## self._headers[key] = value
-## self.write(rawRes['contents'])
+ #def appendRawResponse(self, rawRes):
+ # """Appends the contents of the raw response.
+ #
+ # The contents returned by some transaction's rawResponse() are added
+ # to this response. The headers of the receiving response take precedence
+ # over the appended response. This method was built primarily to support
+ # Application.forwardRequest().
+ #
+ # """
+ # assert self._committed == 0
+ # headers = rawRes.get('headers', [])
+ # for key, value in headers:
+ # if not self._headers.has_key(key):
+ # self._headers[key] = value
+ # self.write(rawRes['contents'])
def mergeTextHeaders(self, headerstr):
- """
- Given a string of headers (separated by newlines), merge them into our headers.
+ """Merge text into our headers.
+
+ Given a string of headers (separated by newlines),
+ merge them into our headers.
+
"""
linesep = "\n"
-
lines = string.split(headerstr,"\n")
for i in lines:
sep = string.find(i, ":")
@@ -373,19 +418,17 @@
## Exception reporting ##
- exceptionReportAttrNames = Response.exceptionReportAttrNames + ['committed', 'headers', 'cookies']
+ exceptionReportAttrNames = Response.exceptionReportAttrNames + [
+ 'committed', 'headers', 'cookies']
def displayError(self, err):
- """
- Displays HTTPException errors, with status codes
- """
-
+ """Display HTTPException errors, with status codes."""
+
assert not self._committed, "Already committed"
for header, value in err.headers().items():
self.setHeader(header, value)
self.setHeader('Status',
- '%s %s' % (err.code(),
- err.codeMessage()))
+ '%s %s' % (err.code(), err.codeMessage()))
self._strmOut.clear()
self.setHeader('Content-type', 'text/html')
self._strmOut.write(err.html())
Modified: Webware/trunk/WebKit/HTTPServer.py
==============================================================================
--- Webware/trunk/WebKit/HTTPServer.py (original)
+++ Webware/trunk/WebKit/HTTPServer.py Thu Sep 29 13:21:48 2005
@@ -10,35 +10,38 @@
import errno
class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- """Handles incoming requests. Recreated with every request.
- Abstract base class.
+ """Handles incoming requests.
+
+ Recreated with every request. Abstract base class.
+
"""
- ## This sends certain CGI variables. These are some that
- ## should be sent, but aren't:
- ## SERVER_ADDR
- ## SERVER_PORT
- ## SERVER_SOFTWARE
- ## SERVER_NAME
- ## HTTP_CONNECTION
- ## SERVER_PROTOCOL
- ## HTTP_KEEP_ALIVE
-
- ## These I don't think are needed:
- ## DOCUMENT_ROOT
- ## PATH_TRANSLATED
- ## GATEWAY_INTERFACE
- ## PATH
- ## SERVER_SIGNATURE
- ## SCRIPT_NAME (?)
- ## SCRIPT_FILENAME (?)
- ## SERVER_ADMIN (?)
+ # This sends certain CGI variables. These are some that
+ # should be sent, but aren't:
+ # SERVER_ADDR
+ # SERVER_PORT
+ # SERVER_SOFTWARE
+ # SERVER_NAME
+ # HTTP_CONNECTION
+ # SERVER_PROTOCOL
+ # HTTP_KEEP_ALIVE
+
+ # These I don't think are needed:
+ # DOCUMENT_ROOT
+ # PATH_TRANSLATED
+ # GATEWAY_INTERFACE
+ # PATH
+ # SERVER_SIGNATURE
+ # SCRIPT_NAME (?)
+ # SCRIPT_FILENAME (?)
+ # SERVER_ADMIN (?)
def handleRequest(self):
- """
- Actually performs the request, creating the environment and
- calling self.doTransaction(env, myInput) to perform the
- response.
+ """Handle a request.
+
+ Actually performs the request, creating the environment and calling
+ self.doTransaction(env, myInput) to perform the response.
+
"""
self.server_version = 'Webware/0.1'
env = {}
@@ -71,16 +74,22 @@
do_PROPFIND = handleRequest
def headersToEnviron(self, headers, env):
- """Use a simple heuristic to convert all the headers to
- environmental variables..."""
+ """Convert headers to environment variables.
+
+ Use a simple heuristic to convert all the headers to
+ environmental variables.
+
+ """
for header, value in headers.items():
env['HTTP_%s' % (header.upper().replace('-', '_'))] = value
return env
def processResponse(self, data):
- """
+ """Process response.
+
Takes a string (like what a CGI script would print) and
sends the actual HTTP response (response code, headers, body).
+
"""
s = StringIO(data)
headers = mimetools.Message(s)
@@ -90,14 +99,19 @@
self.sendBody(s)
def doLocation(self, headers):
- """If there's a Location header and no Status header,
- we need to add a Status header ourselves."""
+ """Process location header.
+
+ If there's a Location header and no Status header,
+ we need to add a Status header ourselves.
+
+ """
if headers.has_key('Location'):
if not headers.has_key('Status'):
- ## @@: is this the right status header?
+ # @@: is this the right status header?
headers['Status'] = '301 Moved Temporarily'
def sendStatus(self, headers):
+ """Send status."""
if not headers.has_key('Status'):
status = "200 OK"
else:
@@ -113,33 +127,38 @@
self.send_response(code, message)
def sendHeaders(self, headers):
+ """Send headers."""
for header, value in headers.items():
self.send_header(header, value)
self.end_headers()
def sendBody(self, bodyFile):
+ """Send body."""
self.wfile.write(bodyFile.read())
bodyFile.close()
def log_request(self, code='-', size='-'):
+ """Log request."""
# Set LogActivity instead.
pass
class HTTPAppServerHandler(Handler, HTTPHandler):
+ """AppServer interface.
- """
Adapters HTTPHandler to fit with ThreadedAppServer's
- model of an adapter
- """
+ model of an adapter.
+ """
protocolName = 'http'
settingPrefix = 'HTTP'
def handleRequest(self):
+ """Handle a request."""
HTTPHandler.__init__(self, self._sock, self._sock.getpeername(), None)
def doTransaction(self, env, myInput):
+ """Process transaction."""
streamOut = ASStreamOut()
requestDict = {
'format': 'CGI',
@@ -158,9 +177,9 @@
print "HTTPServer: output error:", e # lame
def dispatchRawRequest(self, requestDict, streamOut):
+ """Dispatch the request."""
transaction = self._server._app.dispatchRawRequest(requestDict, streamOut)
streamOut.close()
transaction._application=None
transaction.die()
del transaction
-
Modified: Webware/trunk/WebKit/HTTPServlet.py
==============================================================================
--- Webware/trunk/WebKit/HTTPServlet.py (original)
+++ Webware/trunk/WebKit/HTTPServlet.py Thu Sep 29 13:21:48 2005
@@ -4,20 +4,25 @@
class HTTPServlet(Servlet):
- """
- HTTPServlet implements the respond() method to invoke methods such as respondToGet() and respondToPut() depending on the type of HTTP request.
+ """A HTTP servlet.
+
+ HTTPServlet implements the respond() method to invoke methods such as
+ respondToGet() and respondToPut() depending on the type of HTTP request.
Example HTTP request methods are GET, POST, HEAD, etc.
Subclasses implement HTTP method FOO in the Python method respondToFoo.
Unsupported methods return a "501 Not Implemented" status.
- Note that HTTPServlet inherits awake() and respond() methods from Servlet and that subclasses may make use of these.
+ Note that HTTPServlet inherits awake() and respond() methods from
+ Servlet and that subclasses may make use of these.
See also: Servlet
FUTURE
* Document methods (take hints from Java HTTPServlet documentation)
+
"""
+
## Init ##
def __init__(self):
@@ -28,7 +33,12 @@
## Transactions ##
def respond(self, trans):
- """ Invokes the appropriate respondToSomething() method depending on the type of request (e.g., GET, POST, PUT, ...). """
+ """Respond to a request.
+
+ Invokes the appropriate respondToSomething() method depending on the
+ type of request (e.g., GET, POST, PUT, ...).
+
+ """
request = trans.request()
httpMethodName = request.method()
# For GET and HEAD, handle the HTTP If-Modified-Since header:
@@ -37,11 +47,14 @@
if httpMethodName in ('GET','HEAD'):
lm = self.lastModified(trans)
if lm:
- lm = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(lm))
+ lm = time.strftime('%a, %d %b %Y %H:%M:%S GMT',
+ time.gmtime(lm))
trans.response().setHeader('Last-Modified', lm)
- if_modified_since = request.environ().get('HTTP_IF_MODIFIED_SINCE', None)
+ if_modified_since = request.environ().get(
+ 'HTTP_IF_MODIFIED_SINCE', None)
if not if_modified_since:
- if_modified_since = request.environ().get('If-Modified-Since', None)
+ if_modified_since = request.environ().get(
+ 'If-Modified-Since', None)
if if_modified_since and if_modified_since.split(';')[0] == lm:
trans.response().setStatus(304, 'Not Modified')
#print "304", request.serverSidePath()
@@ -57,15 +70,19 @@
trans.response().setStatus(501, 'Not Implemented')
def lastModified(self, trans):
- """
+ """Get time of last modification.
+
Return this object's Last-Modified time (as a float),
or None (meaning don't know or not applicable).
+
"""
return None
def respondToHead(self, trans):
- """
+ """Respond to a HEAD request.
+
A correct but inefficient implementation.
+
"""
res = trans.response()
w = res.write
Modified: Webware/trunk/WebKit/ImportSpy.py
==============================================================================
--- Webware/trunk/WebKit/ImportSpy.py (original)
+++ Webware/trunk/WebKit/ImportSpy.py Thu Sep 29 13:21:48 2005
@@ -3,16 +3,9 @@
import sys
import imp
-"""
-NO DOCS - this can all be more easily implemented by looking at
-sys.modules, along with select files (like config files). It's fast
-to poll sys.modules, then we don't need this fanciness.
-
-jdh: I'm not sure. The original implementation from Tavis used sys.modules
-like you say. I implemented this to avoid having any polling at all,
-but perhaps it's not worth it. I'm not sure if there were other
-factors in the design which pointed to this solution.
+"""ImportSpy
+Keeps track of imported modules and protects against concurrent imports.
This module helps save the filepath of every module which is imported.
This is used by the `AutoReloadingAppServer` (see doc strings for more
@@ -31,57 +24,71 @@
to add more files to watch without importing them (configuration files,
for instance).
-.. inline:: load_module
-.. inline:: find_module
-.. inline:: watchFile
+ianb: This can all be more easily implemented by looking at sys.modules,
+along with select files (like config files). It's fast to poll sys.modules,
+then we don't need this fanciness.
+
+jdh: I'm not sure. The original implementation from Tavis used sys.modules
+like you say. I implemented this to avoid having any polling at all,
+but perhaps it's not worth it. I'm not sure if there were other factors
+in the design which pointed to this solution.
+
"""
-True, False = 1==1, 0==1
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
-try:
+try: # backward compatibility for Python < 2.4
set
except NameError:
from sets import Set as set
class ImportLock:
- """
- Provides a lock for protecting against concurrent imports.
- This is necessary because WebKit is multithreaded and uses its
- own import hook.
-
- This class abstracts the difference between using the Python
- interpreter's global import lock, and using our own RLock.
- The global lock is the correct solution, but is only available
- in recent Python versions (2.3 or 2.2.3). If it's not
- available, we fall back to using an RLock (which is not as
- good, but better than nothing).
+ """Lock for multi-threaded imports.
+
+ Provides a lock for protecting against concurrent imports. This is
+ necessary because WebKit is multithreaded and uses its own import hook.
+
+ This class abstracts the difference between using the Python interpreter's
+ global import lock, and using our own RLock. The global lock is the correct
+ solution, but is only available in Python since version 2.2.3. If it's not
+ available, we fall back to using an RLock (which is not as good, but better
+ than nothing).
+
"""
def __init__(self):
- """
+ """Create the lock.
+
Aliases the `acquire` and `release` methods to
- `imp.acquire_lock` and `imp.release_lock` (if
- available), or to acquire and release our own RLock.
+ `imp.acquire_lock` and `imp.release_lock` (if available),
+ or to acquire and release our own RLock.
+
"""
if hasattr(imp,'acquire_lock'):
self.acquire = imp.acquire_lock
self.release = imp.release_lock
- else:
+ else: # fallback for Python < 2.3
from threading import RLock
self._lock = RLock()
self.acquire = self._lock.acquire
self.release = self._lock.release
+
class ModuleLoader(ihooks.ModuleLoader):
- """
- Implements the ihook module loader that tracks imported
- modules.
+ """The import hook.
+
+ Implements the ihook module loader that tracks imported modules.
+
"""
def __init__(self):
+ """Create import hook."""
assert modloader is None, \
- "ModuleLoader can only be instantiated once"
+ "ModuleLoader can only be instantiated once"
ihooks.ModuleLoader.__init__(self)
self._fileList = {}
self._notifyHook = None
@@ -89,7 +96,8 @@
self._lock = ImportLock()
self._modulesSet = set()
- def load_module(self,name,stuff):
+ def load_module(self, name, stuff):
+ """Replaces imp.load_module()."""
try:
try:
self._lock.acquire()
@@ -103,6 +111,7 @@
return mod
def recordModules(self, moduleNames):
+ """Record a list of modules."""
for name in moduleNames:
mod = sys.modules[name]
if not hasattr(mod, '__file__'):
@@ -112,34 +121,41 @@
pathname = os.path.dirname(file)
desc = None
self.recordFileName((file, pathname, desc),
- sys.modules[name])
+ sys.modules[name])
def fileList(self):
+ """Return the list of tracked files."""
return self._fileList
def notifyOfNewFiles(self, hook):
- """ Called by someone else to register that they'd like to
- be know when a new file is imported """
+ """Register notification hook.
+
+ Called by someone else to register that they'd like to be know
+ when a new file is imported.
+
+ """
self._notifyHook = hook
def watchFile(self, filepath, getmtime=os.path.getmtime):
+ """Add more files to watch without importing them."""
modtime = getmtime(filepath)
self._fileList[filepath] = modtime
- # send notification that this file was imported
+ # send notification that this file was imported
if self._notifyHook:
self._notifyHook(filepath,modtime)
def recordFileName(self, stuff, mod, isfile=os.path.isfile):
+ """Record a file."""
file, pathname, desc = stuff
fileList = self._fileList
if mod:
assert sys.modules.has_key(mod.__name__)
self._modulesSet.add(mod)
-
- # __orig_file__ is used for cheetah and psp mods; we want
- # to record the source filenames, not the auto-generated modules
- f2 = getattr(mod, '__orig_file__', 0)
+
+ # __orig_file__ is used for cheetah and psp mods; we want to
+ # record the source filenames, not the auto-generated modules:
+ f2 = getattr(mod, '__orig_file__', 0)
f = getattr(mod, '__file__', 0)
if f2 and f2 not in fileList.keys():
@@ -160,29 +176,31 @@
except OSError:
pass
- # also record filepaths which weren't successfully
- # loaded, which may happen due to a syntax error in a
- # servlet, because we also want to know when such a
- # file is modified
+ # Also record filepaths which weren't successfully loaded, which
+ # may happen due to a syntax error in a servlet, because we also
+ # want to know when such a file is modified:
elif pathname:
if isfile(pathname):
self.watchFile(pathname)
def activate(self):
+ """Activate the ModuleLoader."""
imp = ihooks.ModuleImporter(loader=modloader)
ihooks.install(imp)
self.recordModules(sys.modules.keys())
self._installed = True
def delModules(self, includePythonModules=False, excludePrefixes=[]):
- """
- Deletes all the modules that the ImportSpy has ever imported unless they
- are part of WebKit. This in support of DebugAppServer's useful (yet
- imperfect) support for AutoReload.
+ """Delete imported modules.
+
+ Deletes all the modules that the ImportSpy has ever imported unless
+ they are part of WebKit. This in support of DebugAppServer's useful
+ (yet imperfect) support for AutoReload.
"""
for mod in self._modulesSet:
name = mod.__name__
- if not includePythonModules and (not hasattr(mod, '__file__') or mod.__file__.startswith(sys.prefix)):
+ if not includePythonModules and (not hasattr(mod, '__file__')
+ or mod.__file__.startswith(sys.prefix)):
continue
exclude = False
for prefix in excludePrefixes:
@@ -195,6 +213,8 @@
self._modulesSet = set()
+## Global ##
+
# We do this little double-assignment trick to make sure ModuleLoader
# is only instantiated once.
modloader = None
@@ -207,20 +227,13 @@
def load_module(name, file, filename, description):
- """
- .. inline:: ModuleLoader.load_module
- """
+ """See ModuleLoader.load_module."""
return modloader.load_module(name,(file,filename,description))
def find_module(name,path=None):
- """
- .. inline:: ModuleLoader.find_module
- """
+ """See ModuleLoader.find_module."""
return modloader.find_module(name,path)
def watchFile(*args):
- """
- .. inline:: ModuleLoader.watchFile
- """
+ """See ModuleLoader.watchFile."""
return modloader.watchFile(*args)
-
Modified: Webware/trunk/WebKit/Message.py
==============================================================================
--- Webware/trunk/WebKit/Message.py (original)
+++ Webware/trunk/WebKit/Message.py Thu Sep 29 13:21:48 2005
@@ -1,9 +1,17 @@
+"""Message
+
+A very general (dumb) message class.
+
+"""
+
+
from Common import *
-# @@ 2003-03 ib: NO DOC - this class seems dumb.
class Message(Object):
- """
- Message is the abstract, parent class for both Request and Response, and implements the behavior that is generic to both.
+ """A very general message class.
+
+ Message is the abstract, parent class for both Request and Response,
+ and implements the behavior that is generic to both.
Messages have:
@@ -37,7 +45,12 @@
## Protocol ##
def protocol(self):
- """ Returns the name and version of the protocol the message uses in the form protocol/majorVersion.minorVersion, for example, HTTP/1.1. """
+ """Return the protocol-
+
+ Returns the name and version of the protocol the message uses
+ in the form protocol/majorVersion.minorVersion, for example, HTTP/1.1.
+
+ """
# @@ 2000-04-09 ce: Move this down into HTTPSomething subclasses
return 'HTTP/1.0'
Modified: Webware/trunk/WebKit/Monitor.py
==============================================================================
--- Webware/trunk/WebKit/Monitor.py (original)
+++ Webware/trunk/WebKit/Monitor.py Thu Sep 29 13:21:48 2005
@@ -1,15 +1,14 @@
#!/usr/bin/env python
-"""
-Fault tolerance system for WebKit
+"""Fault tolerance system for WebKit.
:author: Jay Love
This module is intended to provide additional assurance that the
-AppServer continues running at all times. This module will be
-reponsible for starting the AppServer, and monitoring its health. It
+AppServer continues running at all times. This module will be
+reponsible for starting the AppServer, and monitoring its health. It
does that by periodically sending a status check message to the
-AppServer to ensure that it is responding. If it finds that the
+AppServer to ensure that it is responding. If it finds that the
AppServer does not respond within a specified time, it will start a
new copy of the AppServer, after killing the previous process.
@@ -18,7 +17,6 @@
$ python Monitor.py start
$ python Monitor.py stop
-
The default AppServer specified below will be used, or you can list
the AppServer you would like after ``start``.
@@ -29,39 +27,34 @@
"""
-
# Future:
-# Add ability to limit number of requests served. When some count is reached, send
-# a message to the server to save it's sessions, then exit. Then start a new AppServer
-# that will pick up those sessions.
+# Add ability to limit number of requests served. When some count is reached,
+# send a message to the server to save it's sessions, then exit. Then start
+# a new AppServer that will pick up those sessions.
-# It should be possible on both Unix and Windows to monitor the AppServer process in 2 ways:
+# It should be possible on both Unix and Windows to monitor the AppServer
+# process in 2 ways:
# 1) The method used here, ie can it service requests?
# 2) is the process still running?
# Combining these with a timer lends itself to load balancing of some kind.
-
-
"""
Module global:
`defaultServer`:
- default ``"ThreadedAppServer"``. The type of AppServer to start up
+ default ``"ThreadedAppServer"``. The type of AppServer to start up
(as listed in ``Launch.py``)
`checkInterval`:
- default 10. Seconds between checks
+ default 10. Seconds between checks.
`maxStartTime`:
- deafult 120. Seconds to wait for AppServer to start before killing
+ deafult 120. Seconds to wait for AppServer to start before killing
it and trying again.
"""
defaultServer = "ThreadedAppServer"
-checkInterval = 10 #add to config if this implementation is adopted
+checkInterval = 10 # add to config if this implementation is adopted
maxStartTime = 120
-
-
-
import socket
import time
import os
@@ -74,7 +67,7 @@
global serverName
serverName = defaultServer
global srvpid
-srvpid=0
+srvpid = 0
global addr
global running
@@ -88,8 +81,10 @@
debug = 1
+## Start ##
+
def createServer(setupPath=0):
- """Unix only, executed after forking for daemonization"""
+ """Unix only, executed after forking for daemonization."""
print "Starting Server"
import WebKit
@@ -97,11 +92,8 @@
exec code
main(['monitor',])
-
def startupCheck():
- """
- Make sure the AppServer starts up correctly.
- """
+ """Make sure the AppServer starts up correctly."""
global debug
count = 0
print "Waiting for start"
@@ -119,9 +111,10 @@
time.sleep(checkInterval)
def startServer(killcurrent = 1):
- """
- Start the AppServer. If `killcurrent` is true or not provided,
- kill the current AppServer.
+ """Start the AppServer.
+
+ If `killcurrent` is true or not provided, kill the current AppServer.
+
"""
global srvpid
global debug
@@ -135,17 +128,17 @@
if srvpid == 0:
createServer(not killcurrent)
sys.exit()
-
-
def checkServer(restart = 1):
- """
- Send a check request to the AppServer. If restart is 1, then
- attempt to restart the server if we can't connect to it.
+ """Send a check request to the AppServer.
+
+ If restart is 1, then attempt to restart the server
+ if we can't connect to it.
This function could also be used to see how busy an AppServer
is by measuring the delay in getting a response when using the
standard port.
+
"""
global addr
global running
@@ -168,22 +161,21 @@
startupCheck()
else:
return 0
-
-
def main(args):
- """
- The main loop. Starts the server with `startServer(0)`,
+ """The main loop.
+
+ Starts the server with `startServer(0)`,
checks it's started up (`startupCheck`), and does a
- loop checking the server (`checkServer`)
+ loop checking the server (`checkServer`).
+
"""
-
global debug
global serverName
global running
running = 1
-
+
file = open("monitorpid.txt","w")
if os.name == 'posix':
file.write(str(os.getpid()))
@@ -199,10 +191,10 @@
print "Exiting Monitor"
try: os.kill(srvpid,signal.SIGTERM)
except: pass
- sys.exit()
+ sys.exit()
while running:
- try:
+ try:
if debug:
print "Checking Server"
checkServer()
@@ -217,13 +209,13 @@
except: sys.exit(0)
try: os.waitpid(srvpid,0) #prevent zombies
except: sys.exit(0)
-
def shutDown(arg1,arg2):
- """
- Shutdown handler, for when Ctrl-C has been hit, or this
- process is being cleanly killed.
+ """Shutdown handler.
+
+ For when Ctrl-C has been hit, or this process is being cleanly killed.
+
"""
global running
print "Monitor Shutdown Called"
@@ -248,44 +240,54 @@
signal.signal(signal.SIGINT, shutDown)
signal.signal(signal.SIGTERM, shutDown)
-######################################################################
+
+## Stop ##
def stop():
- """
- Stop the monitor -- killing the other monitor process that
- has been opened (from the PID file ``monitorpid.txt``).
+ """Stop the monitor.
+
+ This kills the other monitor process that has been opened
+ (from the PID file ``monitorpid.txt``).
+
"""
pid = int(open("monitorpid.txt","r").read())
- os.kill(pid,signal.SIGINT) #this goes to the other running instance of this module
+ # this goes to the other running instance of this module
+ os.kill(pid,signal.SIGINT)
-#######################################################################
+## Command line interface ##
def usage():
print """
This module serves as a watcher for the AppServer process.
The required command line argument is one of:
+
start: Starts the monitor and default appserver
-stop: Stops the currently running Monitor process and the AppServer it is running. This is the only way to stop the process other than hunting down the individual process ID's and killing them.
+
+stop: Stops the currently running Monitor process and the AppServer
+ if is running. This is the only way to stop the process other
+ than hunting down the individual process ID's and killing them.
Optional arguments:
-"AppServerClass": The AppServer class to use (AsyncThreadedAppServer or ThreadedAppServer)
-daemon: If "daemon" is specified, the Monitor will run in the background.\n
-
+
+"AppServer": The AppServer class to use
+ (AsyncThreadedAppServer or ThreadedAppServer)
+daemon: If "daemon" is specified, the Monitor will run
+ as a background process.
+
"""
arguments = ["start", "stop"]
servernames = ["AsyncThreadedAppServer", "ThreadedAppServer"]
optionalargs = ["daemon"]
-
if __name__=='__main__':
global addr
if os.name != 'posix':
print "This service can only be run on posix machines (UNIX)"
sys.exit()
-
+
if len(sys.argv) == 1:
usage()
sys.exit()
@@ -295,12 +297,13 @@
usage()
sys.exit()
- if 1: ##setupPath:
+ if 1: # setup path:
if '' not in sys.path:
sys.path = [''] + sys.path
try:
import WebwarePathLocation
- wwdir = os.path.abspath(os.path.join(os.path.dirname(WebwarePathLocation.__file__),".."))
+ wwdir = os.path.abspath(os.path.join(os.path.dirname(
+ WebwarePathLocation.__file__), '..'))
except Exception, e:
print e
usage()
@@ -312,12 +315,10 @@
except:
pass
-
cfg = open(os.path.join(wwdir,"WebKit","Configs/AppServer.config"))
cfg = eval(cfg.read())
addr = ((cfg['Host'],cfg['Port']-1))
-
if 'stop' in args:
stop()
sys.exit()
@@ -325,23 +326,10 @@
for i in servernames:
if i in args:
serverName=i
-
-
- if 'daemon' in args: #fork and become a daemon
- daemon=os.fork()
+
+ if 'daemon' in args: # fork and become a daemon
+ daemon = os.fork()
if daemon:
sys.exit()
-
main(args)
-
-
-
-
-
-
-
-
-
-
-
Modified: Webware/trunk/WebKit/Object.py
==============================================================================
--- Webware/trunk/WebKit/Object.py (original)
+++ Webware/trunk/WebKit/Object.py Thu Sep 29 13:21:48 2005
@@ -11,10 +11,11 @@
class Object(object, NamedValueAccess):
- """
- Object is the root class for all classes in the WebKit. This
- is a placeholder for any future functionality that might be
+ """Object is the root class for all classes in the WebKit.
+
+ This is a placeholder for any future functionality that might be
appropriate for all objects in the framework.
+
"""
def __init__(self):
@@ -23,21 +24,21 @@
pass
def deprecated(self, method):
- """
- The implementation of WebKit sometimes invokes this
- method which prints a warning that the method you are
- using has been deprecated. This method expects that
- deprecated methods say so at the beginning of their
- doc string and terminate that msg with @. For
- example:
+ """Output a deprecation warning.
+
+ The implementation of WebKit sometimes invokes this method which prints
+ a warning that the method you are using has been deprecated.
+ This method expects that deprecated methods say so at the beginning of
+ their doc string and terminate that msg with @. For example:
DEPRECATED: Class.foo() on 01/24/01 in ver 0.5. Use Class.bar() instead. @
- Putting this information in the doc string is
- important for accuracy in the generated docs.
+ Putting this information in the doc string is important for accuracy
+ in the generated docs.
Example call:
self.deprecated(self.foo)
+
"""
import string
docString = method.__doc__
@@ -47,7 +48,6 @@
msg = string.strip(string.split(method.__doc__, '@')[0])
print msg
-
# 2000-05-21 ce: Sometimes used for debugging:
#
#def __del__(self):
Modified: Webware/trunk/WebKit/Page.py
==============================================================================
--- Webware/trunk/WebKit/Page.py (original)
+++ Webware/trunk/WebKit/Page.py Thu Sep 29 13:21:48 2005
@@ -5,75 +5,73 @@
class Page(HTTPContent):
- """
- Page is a type of HTTPContent that is more convenient for
- Servlets which represent HTML pages generated in response to
- GET and POST requests. In fact, this is the most common type
- of Servlet.
+ """The standard web page template.
+
+ Page is a type of HTTPContent that is more convenient for servlets
+ which represent HTML pages generated in response to GET and POST requests.
+ In fact, this is the most common type of Servlet.
- Subclasses typically override `writeHeader`, `writeBody` and
- `writeFooter`.
+ Subclasses typically override `writeHeader`, `writeBody` and `writeFooter`.
They might also choose to override `writeHTML` entirely.
- When developing a full-blown website, it's common to create a
- subclass of `Page` called `SitePage` which defines the common look
- and feel of the website and provides site-specific convenience
- methods. Then all other pages in your application then inherit
- from `SitePage`.
+ When developing a full-blown website, it's common to create a subclass of
+ `Page` called `SitePage` which defines the common look and feel of the
+ website and provides site-specific convenience methods. Then all other
+ pages in your application then inherit from `SitePage`.
+
"""
+
## Transactions ##
def defaultAction(self):
- """
- The default action in a Page is to writeHTML().
- """
+ """The default action in a Page is to writeHTML()."""
self.writeHTML()
## Generating results ##
def title(self):
- """
- Subclasses often override this method to provide a
- custom title. This title should be absent of HTML
- tags. This implementation returns the name of the
- class, which is sometimes appropriate and at least
- informative. """
+ """The page title.
+
+ Subclasses often override this method to provide a custom title.
+ This title should be absent of HTML tags. This implementation
+ returns the name of the class, which is sometimes appropriate
+ and at least informative.
+ """
return self.__class__.__name__
def htTitle(self):
- """
- Return self.title(). Subclasses sometimes override
- this to provide an HTML enhanced version of the
- title. This is the method that should be used when
- including the page title in the actual page
- contents. """
+ """The page title as HTML.
+ Return self.title(). Subclasses sometimes override this to provide
+ an HTML enhanced version of the title. This is the method that should
+ be used when including the page title in the actual page contents.
+
+ """
return self.title()
def htBodyArgs(self):
- """
- Returns the arguments used for the HTML <body>
- tag. Invoked by writeBody().
+ """The atrributes for the <body> element.
- With the prevalence of stylesheets (CSS), you can
- probably skip this particular HTML feature, but for
- historical reasons this sets the page to black text
- on white.
- """
+ Returns the arguments used for the HTML <body> tag.
+ Invoked by writeBody().
+
+ With the prevalence of stylesheets (CSS), you can probably skip
+ this particular HTML feature, but for historical reasons this sets
+ the page to black text on white.
+ """
return 'text="black" bgcolor="white"'
def writeHTML(self):
- """
- Writes all the HTML for the page.
+ """Write all the HTML for the page.
- Subclasses may override this method (which is invoked
- by `_respond`) or more commonly its constituent
- methods, `writeDocType`, `writeHead` and `writeBody`
+ Subclasses may override this method (which is invoked by `_respond`)
+ or more commonly its constituent methods, `writeDocType`, `writeHead`
+ and `writeBody`.
You will want to override this method if:
* you want to format the entire HTML page yourself
@@ -84,8 +82,8 @@
* if you want to send non-HTML content (be sure to
call ``self.response().setHeader('Content-type',
'mime/type')`` in this case).
- """
+ """
self.writeDocType()
self.writeln('<html>')
self.writeHead()
@@ -93,7 +91,7 @@
self.writeln('</html>')
def writeDocType(self):
- """
+ """Write the DOCTYPE tag.
Invoked by `writeHTML` to write the ``<!DOCTYPE ...>`` tag.
@@ -108,6 +106,7 @@
You can find out more about doc types by searching for
DOCTYPE on the web, or visiting:
http://www.htmlhelp.com/tools/validator/doctype.html
+
"""
# @@ sgd-2003-01-29 - restored the 4.01 transitional as
# per discussions on the mailing list for the 0.8 release.
@@ -116,19 +115,20 @@
' "http://www.w3.org/TR/html4/loose.dtd">')
def writeHead(self):
- """
+ """Write the <head> element of the page.
+
Writes the ``<head>`` portion of the page by writing the
- ``<head>...</head>`` tags and invoking `writeHeadParts`
- in between.
- """
+ ``<head>...</head>`` tags and invoking `writeHeadParts` in between.
+ """
wr = self.writeln
wr('<head>')
self.writeHeadParts()
wr('</head>')
def writeHeadParts(self):
- """
+ """Write the parts included in the <head> element.
+
Writes the parts inside the ``<head>...</head>`` tags.
Invokes `writeTitle` and `writeStyleSheet`. Subclasses
can override this to add additional items and should
@@ -138,27 +138,34 @@
self.writeStyleSheet()
def writeTitle(self):
- """
- Writes the ``<title>`` portion of the page. Uses `title`,
- which is where you should override..
+ """Write the <title> element of the page.
+
+ Writes the ``<title>`` portion of the page.
+ Uses `title`, which is where you should override..
+
"""
self.writeln('\t<title>%s</title>' % self.title())
def writeStyleSheet(self):
- """
- Writes the style sheet for the page, however, this default
- implementation does nothing. Subclasses should override if
- necessary. A typical implementation is:
+ """Write the style sheet for the page.
+
+ This default implementation does nothing.
+ Subclasses should override if necessary.
+
+ A typical implementation is:
self.writeln('\t<link rel="stylesheet" href="StyleSheet.css" type="text/css">')
+
"""
pass
def writeBody(self):
- """
+ """Write the <body> element of the page.
+
Writes the ``<body>`` portion of the page by writing the
``<body>...</body>`` (making use of `htBodyArgs`) and
invoking `writeBodyParts` in between.
+
"""
wr = self.writeln
bodyArgs = self.htBodyArgs()
@@ -170,16 +177,15 @@
wr('</body>')
def writeBodyParts(self):
- """
+ """Write the parts included in the <body> element.
- Invokes `writeContent`. Subclasses should only
- override this method to provide additional page parts
- such as a header, sidebar and footer, that a subclass
- doesn't normally have to worry about writing.
-
- For writing page-specific content, subclasses should
- override `writeContent`() instead. This method is
- intended to be overridden by your SitePage.
+ Invokes `writeContent`. Subclasses should only override this method
+ to provide additional page parts such as a header, sidebar and footer,
+ that a subclass doesn't normally have to worry about writing.
+
+ For writing page-specific content, subclasses should override
+ `writeContent`() instead. This method is intended to be overridden
+ by your SitePage.
See `SidebarPage` for an example override of this method.
@@ -189,29 +195,32 @@
self.writeContent()
def writeContent(self):
- """
- Writes the unique, central content for the page.
+ """Write the unique, central content for the page.
- Subclasses should override this method (not invoking
- super) to write their unique page content.
+ Subclasses should override this method (not invoking super) to write
+ their unique page content.
Invoked by `writeBodyParts`.
+
"""
self.writeln('<p> This page has not yet customized its content. </p>')
def preAction(self, actionName):
- """
- For a page, we first writeDocType(), <html>, and then
- writeHead().
+ """Things to do before actions.
+
+ For a page, we first writeDocType(), <html>, and then writeHead().
+
"""
self.writeDocType()
self.writeln('<html>')
self.writeHead()
def postAction(self, actionName):
- """
+ """Things to do after actions.
+
Simply close the html tag (</html>).
+
"""
self.writeln('</html>')
@@ -219,27 +228,29 @@
## Convenience Methods ##
def htmlEncode(self, s):
- """
- Alias for `WebUtils.Funcs.htmlEncode`, quotes
- the special characters &, <, >, and \"
+ """HTML encode special characters.
+ Alias for `WebUtils.Funcs.htmlEncode`, quotes the special characters
+ &, <, >, and \"
+
"""
return Funcs.htmlEncode(s)
def htmlDecode(self, s):
- """
- Alias for `WebUtils.Funcs.htmlDecode`. Decodes
- HTML entities.
+ """HTML decode special characters.
+
+ Alias for `WebUtils.Funcs.htmlDecode`. Decodes HTML entities.
+
"""
return Funcs.htmlDecode(s)
- """
- **Validate HTML output** (developer debugging)
- """
+
+ ## Validate HTML output (developer debugging) ##
def validateHTML(self, closingTags='</body></html>'):
- """
- Validate the current response data using Web Design
- Group's HTML validator available at
+ """Validate the response.
+
+ Validate the current response data using Web Design Group's
+ HTML validator available at
http://www.htmlhelp.com/tools/validator/
Make sure you install the offline validator (called
@@ -259,13 +270,12 @@
the string ``</body></html>``. At the point this method
is called (e.g. from `writeBodyParts`) the page is not
yet 100% complete, so we have to fake it.
- """
+ """
# don't bother validating if the servlet has redirected
status = self.response().header('status', None)
if status and status.find('Redirect') != -1:
return
-
response = self.response().rawResponse()
contents = response['contents'] + closingTags
from WebUtils import WDGValidator
@@ -273,4 +283,3 @@
if not errorText:
return
self.write(errorText)
-
Modified: Webware/trunk/WebKit/PickleRPCServlet.py
==============================================================================
--- Webware/trunk/WebKit/PickleRPCServlet.py (original)
+++ Webware/trunk/WebKit/PickleRPCServlet.py Thu Sep 29 13:21:48 2005
@@ -2,18 +2,20 @@
import sys, traceback, types
from time import time
from MiscUtils.PickleRPC import RequestError, SafeUnpickler
+
try:
from cPickle import dumps, PickleError
except ImportError:
from pickle import dumps, PickleError
+
try:
import zlib
except ImportError:
zlib = None
+
class PickleRPCServlet(RPCServlet, SafeUnpickler):
- """
- PickleRPCServlet is a base class for Dict-RPC servlets.
+ """PickleRPCServlet is a base class for Dict-RPC servlets.
The "Pickle" refers to Python's pickle module. This class is
similar to XMLRPCServlet. By using Python pickles you get their
@@ -65,6 +67,7 @@
For the dictionary formats and more information see the docs
for MiscUtils.PickleRPC.
+
"""
def respondToPost(self, trans):
@@ -83,21 +86,28 @@
rawstring = data.read()
req = self.loads(zlib.decompress(rawstring))
except zlib.error:
- raise RequestError, 'Cannot uncompress compressed dict-rpc request'
+ raise RequestError, \
+ 'Cannot uncompress compressed dict-rpc request'
else:
- raise RequestError, 'Cannot handle compressed dict-rpc request'
+ raise RequestError, \
+ 'Cannot handle compressed dict-rpc request'
elif encoding:
- raise RequestError, 'Cannot handle Content-Encoding of %s' % encoding
+ raise RequestError, \
+ 'Cannot handle Content-Encoding of %s' % encoding
else:
req = self.load(data)
except PickleError:
raise RequestError, 'Cannot unpickle dict-rpc request.'
if not isinstance(req, types.DictType):
- raise RequestError, 'Expecting a dictionary for dict-rpc requests, but got %s instead.' % type(dict)
+ raise RequestError, \
+ 'Expecting a dictionary for dict-rpc requests,' \
+ ' but got %s instead.' % type(dict)
if req.get('version', 1)!=1:
- raise RequestError, 'Cannot handle version %s requests.' % req['version']
+ raise RequestError, 'Cannot handle version %s requests.' \
+ % req['version']
if req.get('action', 'call')!='call':
- raise RequestError, 'Cannot handle the request action, %r.' % req['action']
+ raise RequestError, \
+ 'Cannot handle the request action, %r.' % req['action']
try:
methodName = req['methodName']
except KeyError:
@@ -107,7 +117,8 @@
# support PythonWin autoname completion
response['value'] = self.exposedMethods()[args[0]]
else:
- response['value'] = self.call(methodName, *args, **req.get('keywords', {}))
+ response['value'] = self.call(methodName, *args,
+ **req.get('keywords', {}))
except RequestError, e:
response['requestError'] = str(e)
self.sendResponse(trans, response)
@@ -117,7 +128,8 @@
self.sendResponse(trans, response)
self.handleException(trans)
except: # if it's a string exception, this gets triggered
- response['exception'] = self.resultForException(sys.exc_info()[0], trans)
+ response['exception'] = self.resultForException(
+ sys.exc_info()[0], trans)
self.sendResponse(trans, response)
self.handleException(trans)
else:
@@ -130,9 +142,7 @@
self.handleException(trans)
def sendResponse(self, trans, response):
- """
- Timestamp the response dict and send it.
- """
+ """Timestamp the response dict and send it."""
# Generated a pickle string
response['timeResponded'] = time()
if self.useBinaryPickle():
@@ -141,20 +151,23 @@
else:
contentType = 'text/x-python-pickled-dict'
response = dumps(response)
-
+
# Get list of accepted encodings
try:
accept_encoding = trans.request().environ()["HTTP_ACCEPT_ENCODING"]
if accept_encoding:
- accept_encoding = [enc.strip() for enc in accept_encoding.split(',')]
+ accept_encoding = [enc.strip()
+ for enc in accept_encoding.split(',')]
else:
accept_encoding = []
except KeyError:
accept_encoding = []
- # Compress the output if we are allowed to. We'll avoid compressing short responses and
+ # Compress the output if we are allowed to.
+ # We'll avoid compressing short responses and
# we'll use the fastest possible compression -- level 1.
- if zlib is not None and "gzip" in accept_encoding and len(response) > 1000:
+ if zlib is not None and "gzip" in accept_encoding \
+ and len(response) > 1000:
contentEncoding = 'x-gzip'
response = zlib.compress(response, 1)
else:
@@ -162,7 +175,5 @@
self.sendOK(contentType, response, trans, contentEncoding)
def useBinaryPickle(self):
- """
- Override this to return 0 to use the less-efficient text pickling format
- """
+ """Override this to return 0 to use the less-efficient text pickling format."""
return 1
Modified: Webware/trunk/WebKit/PidFile.py
==============================================================================
--- Webware/trunk/WebKit/PidFile.py (original)
+++ Webware/trunk/WebKit/PidFile.py Thu Sep 29 13:21:48 2005
@@ -2,16 +2,19 @@
import sys
import atexit
+
class ProcessRunning(Exception):
pass
+
def removePidFile(pidfile):
pidfile.remove()
+
class PidFile:
def __init__(self, path):
self._path = path
- self._createdPID = 0
+ self._createdPID = 0
if os.path.exists(path):
try:
@@ -20,7 +23,7 @@
f.close()
except (IOError, ValueError, TypeError):
# can't open file or read PID from file. File is probably
- # invalid or stale, so try to delete it.
+ # invalid or stale, so try to delete it.
pid = None
print "%s is invalid or cannot be opened. Attempting to remove it." % path
os.unlink(self._path) # not sure if we should catch errors here or not.
@@ -40,7 +43,7 @@
pidfile.write(str(self.getCurrentPID()))
pidfile.close()
- self._createdPID = 1
+ self._createdPID = 1
# delete the pid file when python exits, so that the pid file is removed
# if the process exits abnormally.
@@ -85,7 +88,7 @@
self.remove()
def remove(self):
- # only remove the file if we created it. Otherwise attempting to start
+ # only remove the file if we created it. Otherwise attempting to start
# a second process will remove the file created by the first.
if self._createdPID:
try:
Modified: Webware/trunk/WebKit/PlugIn.py
==============================================================================
--- Webware/trunk/WebKit/PlugIn.py (original)
+++ Webware/trunk/WebKit/PlugIn.py Thu Sep 29 13:21:48 2005
@@ -7,34 +7,62 @@
class PlugIn(Object):
- """
- A plug-in is a software component that is loaded by WebKit in order to provide additional WebKit functionality without necessarily having to modify WebKit's source.
- The most infamous plug-in is PSP (Python Server Pages) which ships with Webware.
- Plug-ins often provide additional servlet factories, servlet subclasses, examples and documentation. Ultimately, it is the plug-in author's choice as to what to provide and in what manner.
- Instances of this class represent plug-ins which are ultimately Python packages (see the Python Tutorial, 6.4: "Packages" at http://www.python.org/doc/current/tut/node8.html#SECTION008400000000000000000).
- A plug-in must also be a Webware component which at means that it will have a Properties.py file advertising its name, version, requirements, etc. You can ask a plug-in for its properties().
+ """Template for Webware Plug-ins.
+
+ A plug-in is a software component that is loaded by WebKit in order to
+ provide additional WebKit functionality without necessarily having to modify
+ WebKit's source. The most infamous plug-in is PSP (Python Server Pages)
+ which ships with Webware.
+
+ Plug-ins often provide additional servlet factories, servlet subclasses,
+ examples and documentation. Ultimately, it is the plug-in author's choice
+ as to what to provide and in what manner.
+
+ Instances of this class represent plug-ins which are ultimately Python
+ packages (see the Python Tutorial, 6.4: "Packages" at
+ http://www.python.org/doc/current/tut/node8.html#SECTION008400000000000000000).
+
+ A plug-in must also be a Webware component which at means that it will have
+ a Properties.py file advertising its name, version, requirements, etc.
+ You can ask a plug-in for its properties().
+
The plug-in/package must have an __init__.py while must contain a function:
def InstallInWebKit(appServer):
- This function is invoked to take whatever actions are needed to plug the new component into WebKit. See PSP for an example.
- If you ask an AppServer for its plugIns(), you will get a list of instances of this class.
- The path of the plug-in is added to sys.path, if it's not already there. This is convenient, but we may need a more sophisticated solution in the future to avoid name collisions between plug-ins.
- Note that this class is hardly ever subclassed. The software in the plug-in package is what provides new functionality and there is currently no way to tell AppServer to use custom subclasses of this class on a case-by-case basis (and so far there is currently no need).
+ This function is invoked to take whatever actions are needed to plug the
+ new component into WebKit. See PSP for an example.
+
+ If you ask an AppServer for its plugIns(), you will get a list of instances
+ of this class.
+
+ The path of the plug-in is added to sys.path, if it's not already there.
+ This is convenient, but we may need a more sophisticated solution in the
+ future to avoid name collisions between plug-ins.
+
+ Note that this class is hardly ever subclassed. The software in the
+ plug-in package is what provides new functionality and there is currently
+ no way to tell AppServer to use custom subclasses of this class on a
+ case-by-case basis (and so far there is currently no need).
Instructions for invoking:
- p = PlugIn(self, '../Foo') # 'self' is typically AppServer. It gets passed to InstallInWebKit()
+ p = PlugIn(self, '../Foo') # 'self' is typically AppServer. It gets passed to InstallInWebKit()
willNotLoadReason = plugIn.load()
if willNotLoadReason:
print ' Plug-in %s cannot be loaded because:\n %s' % (path, willNotLoadReason)
return None
p.install()
# Note that load() and install() could raise exceptions. You should expect this.
+
"""
## Init, load and install ##
def __init__(self, appServer, path):
- """ Initializes the plug-in with basic information. This lightweight constructor does not access the file system. """
+ """Initializes the plug-in with basic information.
+
+ This lightweight constructor does not access the file system.
+
+ """
self._appServer = appServer
self._path = path
self._dir, self._name = os.path.split(path)
@@ -42,7 +70,12 @@
self._examplePages = None
def load(self):
- """ Loads the plug-in into memory, but does not yet install it. Will return None on success, otherwise a message (string) that says why the plug-in could not be loaded. """
+ """Loads the plug-in into memory, but does not yet install it.
+
+ Will return None on success, otherwise a message (string) that says
+ why the plug-in could not be loaded.
+
+ """
print 'Loading plug-in: %s at %s' % (self._name, self._path)
assert os.path.exists(self._path)
@@ -61,13 +94,16 @@
# Inspect it and verify some required conventions
if not hasattr(self._module, 'InstallInWebKit'):
- raise PlugInError, "Plug-in '%s' in '%s' has no InstallInWebKit() function." % (self._name, self._dir)
+ raise PlugInError, \
+ "Plug-in '%s' in '%s' has no InstallInWebKit() function." \
+ % (self._name, self._dir)
# Give the module a pointer back to us
setattr(self._module, 'plugIn', self)
- # Make a directory for it in Cache/
- cacheDir = os.path.join(self._appServer.serverSidePath(), 'Cache', self._name)
+ # Make a dubdirectory for it in the Cache directory:
+ cacheDir = os.path.join(self._appServer.serverSidePath(),
+ 'Cache', self._name)
if not os.path.exists(cacheDir):
os.mkdir(cacheDir)
@@ -81,7 +117,9 @@
self._examplePages = config.get('examplePages', None)
if self._examplePages is not None:
examplesPath = self.serverSidePath('Examples')
- assert os.path.exists(examplesPath), 'Plug-in %s says it has example pages, but there is no Examples/ subdir.' % self._name
+ assert os.path.exists(examplesPath), \
+ 'Plug-in %s says it has example pages,' \
+ ' but there is no Examples/ subdir.' % self._name
ctxName = self._name + 'Examples'
if not app.hasContext(ctxName):
app.addContext(ctxName, examplesPath)
@@ -97,22 +135,22 @@
return self._examplePages
def install(self):
- """ Installs the plug-in by invoking it's required InstallInWebKit() function. """
+ """Installs the plug-in by invoking it's required InstallInWebKit() function."""
self._module.InstallInWebKit(self._appServer)
## Access ##
def name(self):
- """ Returns the name of the plug-in. Example: 'Foo' """
+ """Return the name of the plug-in. Example: 'Foo'"""
return self._name
def directory(self):
- """ Returns the directory in which the plug-in resides. Example: '..' """
+ """Return the directory in which the plug-in resides. Example: '..'"""
return self._dir
def path(self):
- """ Returns the full path of the plug-in. Example: '../Foo' """
+ """Return the full path of the plug-in. Example: '../Foo'"""
return self._path
def serverSidePath(self, path=None):
@@ -122,20 +160,14 @@
return self._path
def module(self):
- """ Returns the Python module object of the plug-in. """
+ """Return the Python module object of the plug-in."""
return self._module
def properties(self):
- """ Returns the properties, a dictionary-like object, of the plug-in which comes from its Properties.py file. See MiscUtils.PropertiesObject.py. """
- return self._properties
-
+ """Return the properties.
- ## Deprecated ##
+ This is a dictionary-like object, of the plug-in which comes
+ from its Properties.py file. See MiscUtils.PropertiesObject.py.
- def version(self):
"""
- DEPRECATED: PlugIn.version() on 1/25 in ver 0.5. Use self.properties()['versionString'] instead. @
- Returns the version of the plug-in as reported in its Properties.py. Example: (0, 2, 0)
- """
- self.deprecated(self.version)
- return self._properties['version']
+ return self._properties
Modified: Webware/trunk/WebKit/RPCServlet.py
==============================================================================
--- Webware/trunk/WebKit/RPCServlet.py (original)
+++ Webware/trunk/WebKit/RPCServlet.py Thu Sep 29 13:21:48 2005
@@ -5,9 +5,10 @@
class RPCServlet(HTTPServlet):
def call(self, methodName, *args, **keywords):
- """
- Subclasses may override this class for custom handling of
- methods.
+ """Call custom method.
+
+ Subclasses may override this class for custom handling of methods.
+
"""
if methodName in self.exposedMethods():
return getattr(self, methodName)( *args, **keywords)
@@ -15,33 +16,36 @@
raise NotImplementedError, methodName
def exposedMethods(self):
- """
- Subclasses should return a list of methods that will be
- exposed through XML-RPC.
+ """Get exposed methods.
+
+ Subclasses should return a list of methods that will be exposed
+ through XML-RPC.
+
"""
return ['exposedMethods']
def resultForException(self, e, trans):
- """
+ """Get text for exception.
+
Given an unhandled exception, returns the string that should be
sent back in the RPC response as controlled by the
RPCExceptionReturn setting.
+
"""
# report exception back to server
setting = trans.application().setting('RPCExceptionReturn')
- assert setting in ('occurred', 'exception', 'traceback'), 'setting=%r' % setting
- if setting=='occurred':
+ assert setting in ('occurred', 'exception', 'traceback'), \
+ 'setting=%r' % setting
+ if setting == 'occurred':
result = 'unhandled exception'
- elif setting=='exception':
+ elif setting == 'exception':
result = str(e)
- elif setting=='traceback':
+ elif setting == 'traceback':
result = ''.join(traceback.format_exception(*sys.exc_info()))
return result
def sendOK(self, contentType, contents, trans, contentEncoding=None):
- """
- Sends a 200 OK response with the given contents.
- """
+ """Send a 200 OK response with the given contents."""
response = trans.response()
response.setStatus(200, 'OK')
response.setHeader('Content-type', contentType)
@@ -51,13 +55,14 @@
response.write(contents)
def handleException(self, transaction):
- """
- If ReportRPCExceptionsInWebKit is set to true, then
- flush the response (because we don't want the standard HTML traceback
- to be appended to the response) and then handle the exception in the
- standard WebKit way. This means logging it to the console, storing
- it in the error log, sending error email, etc. depending on the
- settings.
+ """Handle exception.
+
+ If ReportRPCExceptionsInWebKit is set to True, then flush the response
+ (because we don't want the standard HTML traceback to be appended to
+ the response) and then handle the exception in the standard WebKit way.
+ This means logging it to the console, storing it in the error log,
+ sending error email, etc. depending on the settings.
+
"""
setting = transaction.application().setting('ReportRPCExceptionsInWebKit')
if setting:
@@ -69,11 +74,11 @@
# @@ GAT: should we also add request, response, session, and application?
def transaction(self):
return self._transaction
-
+
def awake(self, transaction):
HTTPServlet.awake(self, transaction)
self._transaction = transaction
-
+
def sleep(self, transaction):
self._transaction = None
HTTPServlet.sleep(self, transaction)
Modified: Webware/trunk/WebKit/Request.py
==============================================================================
--- Webware/trunk/WebKit/Request.py (original)
+++ Webware/trunk/WebKit/Request.py Thu Sep 29 13:21:48 2005
@@ -4,7 +4,8 @@
class Request(Message):
- """
+ """The abstract request class.
+
Request is a type of Message that offers the following:
* A time stamp (indicating when the request was made)
@@ -19,67 +20,105 @@
* Consider if the local host information should be moved up to Message.
* Locales
* Secure requests, authentication, etc.
+
"""
+
## Init ##
-
+
def __init__(self):
- """ Subclasses are responsible for invoking super and initializing self._time. """
+ """Subclasses are responsible for invoking super and initializing self._time."""
Message.__init__(self)
## Access ##
-
+
def time(self):
return self._time
-
+
def timeStamp(self):
- """ Returns time() as a human readable string, useful for logging and debugging. """
+ """Return time() as a human readable string, useful for logging and debugging."""
return asctime(localtime(self.time()))
## Input ##
def input(self):
- """ Returns a file-style object that the contents can be read from.
- # @@ 2000-05-03 ce: This is bogus. Disregard for now."""
+ """Return a file-style object that the contents can be read from.
+
+ # @@ 2000-05-03 ce: This is bogus. Disregard for now.
+
+ """
pass
## Remote info ##
- # @@ 2000-05-07 ce: Do remoteAddress() and remoteName() have to be implemented here or should it be a subclass responsibility?
-
+ # @@ 2000-05-07 ce: Do remoteAddress() and remoteName()
+ # have to be implemented here or should it be a subclass responsibility?
+
def remoteAddress(self):
- """ Returns a string containing the Internet Protocol (IP) address of the client that sent the request. """
+ """Get the remote address.
+
+ Returns a string containing the Internet Protocol (IP) address
+ of the client that sent the request.
+
+ """
raise NotImplementedError
-
+
def remoteName(self):
- """ Returns the fully qualified name of the client that sent the request, or the IP address of the client if the name cannot be determined. """
+ """Get the remote name.
+
+ Returns the fully qualified name of the client that sent the request,
+ or the IP address of the client if the name cannot be determined.
+
+ """
raise NotImplementedError
-
+
## Local info ##
-
+
def localAddress(self):
- """ Returns a string containing the Internet Protocol (IP) address of the local host (e.g., the server) that received the request. """
+ """Get local address.
+
+ Returns a string containing the Internet Protocol (IP) address
+ of the local host (e.g., the server) that received the request.
+
+ """
raise NotImplementedError
-
+
def localName(self):
- """ Returns the fully qualified name of the local host (e.g., the server) that received the request. """
+ """Get local name.
+
+ Returns the fully qualified name of the local host (e.g., the server)
+ that received the request.
+
+ """
return 'localhost'
-
+
def localPort(self):
- """ Returns the port of the local host (e.g., the server) that received the request. """
+ """Get local port.
+
+ Returns the port of the local host (e.g., the server)
+ that received the request.
+
+ """
raise NotImplementedError
## Security ##
-
+
def isSecure(self):
- """ Returns true if request was made using a secure channel, such as HTTPS. This currently always returns false, since secure channels are not yet supported. """
+ """Check whether this is a secure channel.
+
+ Returns true if request was made using a secure channel,
+ such as HTTPS. This currently always returns false,
+ since secure channels are not yet supported.
+
+ """
return 0
## Cleanup ##
+
def clearTransaction(self):
del self._transaction
Modified: Webware/trunk/WebKit/Response.py
==============================================================================
--- Webware/trunk/WebKit/Response.py (original)
+++ Webware/trunk/WebKit/Response.py Thu Sep 29 13:21:48 2005
@@ -3,7 +3,8 @@
class Response(Message):
- """
+ """The abstract response class.
+
Response is a type of Message that offers the following:
* @@ 20000-04-17 ce
@@ -14,30 +15,38 @@
FUTURE
* Consider implementing the buffer/flush logic here including buffer size.
* Also, consider then having a method that doesn't allow committment until the end.
+
"""
## Init ##
-
+
def __init__(self, trans, strmOut):
Message.__init__(self)
self._strmOut = strmOut
self._transaction = trans
+
## End time ##
def endTime(self):
return self._endTime
-
+
def recordEndTime(self):
- """ Stores the current time as the end time of the response. This should be invoked at the end of deliver(). It may also be invoked by the application for those responses that never deliver due to an error. """
+ """Record the end time of the response.
+
+ Stores the current time as the end time of the response. This should
+ be invoked at the end of deliver(). It may also be invoked by the
+ application for those responses that never deliver due to an error.
+
+ """
self._endTime = time.time()
-
+
## Output ##
def write(self, string):
raise AbstractError, self.__class__
-
+
def isCommitted(self):
raise AbstractError, self.__class__
@@ -50,7 +59,7 @@
def streamOut(self):
return self._strmOut
-
+
## Cleanup ##
def clearTransaction(self):
Modified: Webware/trunk/WebKit/SelectRelease.py
==============================================================================
--- Webware/trunk/WebKit/SelectRelease.py (original)
+++ Webware/trunk/WebKit/SelectRelease.py Thu Sep 29 13:21:48 2005
@@ -1,8 +1,11 @@
-"""
-Used by the AsyncThreadedAppServer module.
+"""Used by the AsyncThreadedAppServer module.
+
+This file implements an object that can force a call to select in the main
+asyncore.poll loop to return. This dispatcher is added to the asyncore polling
+group. It is polled for reads. We make this object available to everyone.
+When we need the asyncore select loop to return, i.e., we have some data ready
+to go, we call the release() method, which does a quick write to it's own socket/file-descriptor. This causes select to return.
-This file implements an object that can force a call to select in the main asyncore.poll loop to return.
-This dispathcher is added to the asyncore polling group. It is polled for reads. We make this object available to everyone. When we need the asyncore select loop to return, ie, we have some data ready to go, we call the release() method, which does a quick write to it's own socket/file-descriptor. This causes select to return.
"""
import asyncore
@@ -17,8 +20,10 @@
if os.name == 'posix':
class SelectRelease (asyncore.file_dispatcher):
- """
- In a posix environment, we can use a file descriptor as the object that we include in the polling loop that we force a read on.
+ """Version for Posix.
+
+ In a Posix environment, we can use a file descriptor as the object
+ that we include in the polling loop that we force a read on.
"""
@@ -49,9 +54,12 @@
else:
class SelectRelease (asyncore.dispatcher):
- """
- MSW can't hanlde file descriptors in a select poll, so a real socket has to be used.
- This method was adapted from a similar module in the Zope Medusa server.
+ """Version for MS Windows.
+
+ Windows can't handle file descriptors in a select poll, so a real
+ socket has to be used. This method was adapted from a similar module
+ in the Zope Medusa server.
+
"""
address = ('127.9.9.9', 19999)
Modified: Webware/trunk/WebKit/Servlet.py
==============================================================================
--- Webware/trunk/WebKit/Servlet.py (original)
+++ Webware/trunk/WebKit/Servlet.py Thu Sep 29 13:21:48 2005
@@ -2,11 +2,24 @@
from MiscUtils.Funcs import excstr
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
+
+
class Servlet(Object):
- """
- A servlet is a key portion of a server-based application that implements the semantics of a particular request by providing a response.
- This abstract class defines servlets at a very high level. Most often, developers will subclass HTTPServlet or even Page.
- Servlets can be created once, then used and destroyed, or they may be reused several times over (it's up to the server). Therefore, servlet developers should take the proper actions in awake() and sleep() so that reuse can occur.
+ """A general servlet.
+
+ A servlet is a key portion of a server-based application that implements
+ the semantics of a particular request by providing a response.
+ This abstract class defines servlets at a very high level.
+ Most often, developers will subclass HTTPServlet or even Page.
+
+ Servlets can be created once, then used and destroyed, or they may be
+ reused several times over (it's up to the server). Therefore, servlet
+ developers should take the proper actions in awake() and sleep()
+ so that reuse can occur.
Objects that participate in a transaction include:
* Application
@@ -16,13 +29,17 @@
* Servlet
* Response
- The awake(), respond() and sleep() methods form a message sandwich. Each is passed an instance of Transaction which gives further access to all the objects involved.
+ The awake(), respond() and sleep() methods form a message sandwich.
+ Each is passed an instance of Transaction which gives further access
+ to all the objects involved.
+
"""
+
## Init ##
def __init__(self):
- """ Subclasses must invoke super. """
+ """Subclasses must invoke super."""
Object.__init__(self)
self._serverSidePath = None
self._factory = None
@@ -31,7 +48,12 @@
## Access ##
def name(self):
- """ Returns the name which is simple the name of the class. Subclasses should *not* override this method. It is used for logging and debugging. """
+ """Return the name which is simple the name of the class.
+
+ Subclasses should *not* override this method.
+ It is used for logging and debugging.
+
+ """
return self.__class__.__name__
@@ -45,15 +67,17 @@
try:
trans.sleep()
except Exception, second:
- # The first exception is more important than the *second* one that comes from sleep()
- # In fact, without this little trick the first exception gets hidden by the second
- # which is often just a result of the first. Then you're stuck scratching your head
- # wondering what the first might have been.
- raise Exception('Two exceptions. first=%s; second=%s' % (excstr(first), excstr(second)))
+ # The first exception is more important than the *second* one
+ # that comes from sleep(). In fact, without this little trick
+ # the first exception gets hidden by the second which is often
+ # just a result of the first. Then you're stuck scratching your
+ # head wondering what the first might have been.
+ raise Exception('Two exceptions. first=%s; second=%s'
+ % (excstr(first), excstr(second)))
else:
- raise # no problems with sleep() so raise the one and only exception
+ raise # no problems with sleep() so raise the one and only exception
else:
- trans.sleep()
+ trans.sleep()
def runMethodForTransaction(self, trans, method, *args, **kw):
self.awake(trans)
@@ -62,38 +86,67 @@
return result
def awake(self, trans):
- """ This message is sent to all objects that participate in the request-response cycle in a top-down fashion, prior to respond(). Subclasses must invoke super. """
+ """Send the awake message.
+
+ This message is sent to all objects that participate in the
+ request-response cycle in a top-down fashion, prior to respond().
+ Subclasses must invoke super.
+
+ """
self._transaction = trans
def respond(self, trans):
+ """Respond to a request."""
raise AbstractError, self.__class__
def sleep(self, trans):
+ """Send the sleep message."""
pass
## Log ##
def log(self, message):
- """ This can be invoked to print messages concerning the servlet. This is often used by self to relay important information back to developers. """
+ """Log a message.
+
+ This can be invoked to print messages concerning the servlet.
+ This is often used by self to relay important information back
+ to developers.
+
+ """
print '[%s] [msg] %s' % (asclocaltime(), message)
## Abilities ##
def canBeThreaded(self):
- """ Returns 0 or 1 to indicate if the servlet can be multithreaded. This value should not change during the lifetime of the object. The default implementation returns 0. Note: This is not currently used. """
- return 0
+ """Return whether the servlet can be multithreaded.
+
+ This value should not change during the lifetime of the object.
+ The default implementation returns False.
+ Note: This is not currently used.
+
+ """
+ return False
def canBeReused(self):
- """ Returns 0 or 1 to indicate if a single servlet instance can be reused. The default is 1, but subclasses con override to return 0. Keep in mind that performance may seriously be degraded if instances can't be reused. Also, there's no known good reasons not to reuse and instance. Remember the awake() and sleep() methods are invoked for every transaction. But just in case, your servlet can refuse to be reused. """
- return 1
+ """Returns whether a single servlet instance can be reused.
+
+ The default is True, but subclasses con override to return False.
+ Keep in mind that performance may seriously be degraded if instances
+ can't be reused. Also, there's no known good reasons not to reuse
+ an instance. Remember the awake() and sleep() methods are invoked
+ for every transaction. But just in case, your servlet can refuse
+ to be reused.
+
+ """
+ return True
## Server side filesystem ##
def serverSidePath(self, path=None):
- """ Returns the filesystem path of the page on the server. """
+ """Return the filesystem path of the page on the server."""
if self._serverSidePath is None:
if hasattr(self, "_request") and self._request is not None:
self._serverSidePath = self._request.serverSidePath()
@@ -107,14 +160,6 @@
## Deprecated ##
- def serverSideDir(self):
- """
- deprecated: Servlet.serverSideDir() on 1/24 in ver 0.5, use serverSidePath() instead @
- Returns the filesytem directory of the page on the server.
- """
- self.deprecated(self.serverSideDir)
- return os.path.dirname(self.serverSidePath())
-
def close(self, trans):
if self._factory:
self._factory.returnServlet(trans, self)
Modified: Webware/trunk/WebKit/ServletFactory.py
==============================================================================
--- Webware/trunk/WebKit/ServletFactory.py (original)
+++ Webware/trunk/WebKit/ServletFactory.py Thu Sep 29 13:21:48 2005
@@ -8,25 +8,33 @@
class ServletFactory(Object):
- """
- ServletFactory is an abstract class that defines the protocol
- for all servlet factories.
+ """Servlet factory template.
+
+ ServletFactory is an abstract class that defines the protocol for
+ all servlet factories.
- Servlet factories are used by the Application to create
- servlets for transactions.
+ Servlet factories are used by the Application to create servlets
+ for transactions.
- A factory must inherit from this class and override
- uniqueness(), extensions() and either loadClass() or
- servletForTransaction(). Do not invoke the base class methods
- as they all raise AbstractErrors.
+ A factory must inherit from this class and override uniqueness(),
+ extensions() and either loadClass() or servletForTransaction().
+ Do not invoke the base class methods as they all raise AbstractErrors.
Each method is documented below.
+
"""
+
+ ## Init ##
+
def __init__(self, application):
- """ Stores a reference to the application in
- self._app, because subclasses may or may not need to
- talk back to the application to do their work."""
+ """Create servlet factory.
+
+ Stores a reference to the application in self._app, because
+ subclasses may or may not need to talk back to the application
+ to do their work.
+
+ """
Object.__init__(self)
self._app = application
self._cacheClasses = self._app.setting("CacheServletClasses",1)
@@ -43,61 +51,59 @@
self._threadsafeServletCache = {}
self._importLock = threading.RLock()
+
+ ## Info ##
+
def name(self):
- """ Returns the name of the factory. This is a
- convenience for the class name."""
+ """Return the name of the factory.
+
+ This is a convenience for the class name.
+
+ """
return self.__class__.__name__
def uniqueness(self):
- """ Returns a string to indicate the uniqueness of the
- ServletFactory's servlets. The Application needs to
- know if the servlets are unique per file, per
- extension or per application. Return values are
- 'file', 'extension' and 'application'. *** NOTE:
- Application only supports 'file' uniqueness at this
- point in time."""
+ """Return uniqueness type.
+
+ Returns a string to indicate the uniqueness of the ServletFactory's
+ servlets. The Application needs to know if the servlets are unique
+ per file, per extension or per application.
+
+ Return values are 'file', 'extension' and 'application'.
+
+ NOTE: Application only supports 'file' uniqueness at this point in time.
+
+ """
raise AbstractError, self.__class__
def extensions(self):
- """ Return a list of extensions that match this
- handler. Extensions should include the dot. An empty
- string indicates a file with no extension and is a
- valid value. The extension '.*' is a special case that
- is looked for a URL's extension doesn't match
- anything."""
- raise AbstractError, self.__class__
+ """Return a list of extensions that match this handler.
- def servletForTransaction(self, transaction):
- """ Returns a new servlet that will handle the
- transaction. This method should do no caching (e.g.,
- it should really create the servlet upon each
- invocation) since caching is already done at the
- Application level."""
- raise AbstractError, self.__class__
+ Extensions should include the dot. An empty string indicates a file
+ with no extension and is a valid value. The extension '.*' is a special
+ case that is looked for a URL's extension doesn't match anything.
- def flushCache(self):
- """
- Clear any caches and start fesh.
"""
raise AbstractError, self.__class__
+
+ ## Import ##
+
def importAsPackage(self, transaction, serverSidePathToImport):
- """
- Imports the module at the given path in the proper
- package/subpackage for the current request. For
- example, if the transaction has the URL
+ """Import requested module.
+
+ Imports the module at the given path in the proper package/subpackage
+ for the current request. For example, if the transaction has the URL
http://localhost/WebKit.cgi/MyContextDirectory/MySubdirectory/MyPage
- and path = 'some/random/path/MyModule.py' and the
- context is configured to have the name 'MyContext'
- then this function imports the module at that path as
- MyContext.MySubdirectory.MyModule . Note that the
- context name may differ from the name of the directory
- containing the context, even though they are usually
- the same by convention.
-
- Note that the module imported may have a different
- name from the servlet name specified in the URL. This
- is used in PSP.
+ and path = 'some/random/path/MyModule.py' and the context is configured
+ to have the name 'MyContext' then this function imports the module at
+ that path as MyContext.MySubdirectory.MyModule . Note that the context
+ name may differ from the name of the directory containing the context,
+ even though they are usually the same by convention.
+
+ Note that the module imported may have a different name from the
+ servlet name specified in the URL. This is used in PSP.
+
"""
debug=0
@@ -107,7 +113,8 @@
contextPath = request.serverSideContextPath()
fullname = request.contextName()
- ## There is no context, so import the module standalone and give it a unique name
+ # There is no context, so import the module standalone
+ # and give it a unique name:
if fullname == None:
remainder = serverSidePathToImport
remainder = string.replace(remainder, '\\', '_')
@@ -115,8 +122,10 @@
fullmodname = string.replace(remainder,'.','_')
if debug: print __file__, "fullmodname=",fullmodname
if len(fullmodname) > 100: fullmodname=fullmodname[:-50]
- modname=os.path.splitext(os.path.basename(serverSidePathToImport))[0]
- fp, pathname, stuff = imp.find_module(modname, [os.path.dirname(serverSidePathToImport)])
+ modname=os.path.splitext(os.path.basename(
+ serverSidePathToImport))[0]
+ fp, pathname, stuff = imp.find_module(modname,
+ [os.path.dirname(serverSidePathToImport)])
module = imp.load_module(fullmodname, fp, pathname, stuff)
module.__donotreload__ = 1
return module
@@ -124,7 +133,8 @@
# First, we'll import the context's package.
directory, contextDirName = os.path.split(contextPath)
- self._importModuleFromDirectory(fullname, contextDirName, directory, isPackageDir=1)
+ self._importModuleFromDirectory(fullname, contextDirName,
+ directory, isPackageDir=1)
directory = contextPath
# Now we'll break up the rest of the path into components.
@@ -134,8 +144,9 @@
# Import all subpackages of the context package
for name in remainder[:-1]:
- fullname = fullname + '.' + name
- self._importModuleFromDirectory(fullname, name, directory, isPackageDir=1)
+ fullname = '%s.%s' % (fullname, name)
+ self._importModuleFromDirectory(fullname, name,
+ directory, isPackageDir=1)
directory = os.path.join(directory, name)
# Finally, import the module itself as though it was part of the package
@@ -143,23 +154,23 @@
moduleFileName = os.path.basename(serverSidePathToImport)
moduleDir = os.path.dirname(serverSidePathToImport)
name, ext = os.path.splitext(moduleFileName)
- fullname = fullname + '.' + name
- module = self._importModuleFromDirectory(fullname, name, moduleDir, forceReload=1)
+ fullname = '%s.%s' % (fullname, name)
+ module = self._importModuleFromDirectory(fullname, name,
+ moduleDir, forceReload=1)
return module
def _importModuleFromDirectory(self, fullModuleName, moduleName, directory, isPackageDir=0, forceReload=0):
- """
- Imports the given module from the given directory.
- fullModuleName should be the full dotted name that
- will be given to the module within Python. moduleName
- should be the name of the module in the filesystem,
- which may be different from the name given in
- fullModuleName. Returns the module object. If
- forceReload is true then this reloads the module even
- if it has already been imported.
+ """Imports the given module from the given directory.
+
+ fullModuleName should be the full dotted name that will be given
+ to the module within Python. moduleName should be the name of the
+ module in the filesystem, which may be different from the name
+ given in fullModuleName. Returns the module object. If forceReload is
+ True then this reloads the module even if it has already been imported.
+
+ If isPackageDir is True, then this function creates an empty
+ __init__.py if that file doesn't already exist.
- If isPackageDir is true, then this function creates an
- empty __init__.py if that file doesn't already exist.
"""
debug = 0
if debug: print __file__, fullModuleName, moduleName, directory
@@ -185,54 +196,45 @@
module.__donotreload__ = 1
return module
- def flushCache(self):
- """
- Flush the cache. Servlets that are currently in
- the wild may find their way back into the cache
- (this may be a problem).
- """
- ## @@ ib 07-2003: I'm unsure how well this works.
- self._importLock.acquire()
- self._classCache = {}
- # We can't just delete all the lists, because returning
- # servlets expect it to exist.
- for key in self._servletPool.keys():
- self._servletPool[key] = []
- self._threadsafeServletCache = {}
- self._importLock.release()
+ def loadClass(self, transaction, path):
+ """Load the appropriate class.
+
+ Given a transaction and a path, load the class for creating these
+ servlets. Caching, pooling, and threadsafeness are all handled by
+ servletForTransaction. This method is not expected to be threadsafe.
- def returnServlet(self, trans, servlet):
- """
- Called by Servlet.close(), which returns the
- servlet to the servlet pool if necessary.
"""
- if servlet.canBeReused() \
- and not servlet.canBeThreaded() \
- and self._cacheInstances:
- path = trans.request().serverSidePath()
- self._servletPool[path].append(servlet)
+ raise AbstractError, self.__class__
+
+
+ ## Servlet Pool ##
def servletForTransaction(self, transaction):
- """
- Returns a servlet given a transaction. This method
- handles caching, and will call loadClass(trans, filepath)
- if no cache is found. Caching is generally controlled
- by servlets with the canBeReused() and canBeThreaded()
- methods.
+ """Return a new servlet that will handle the transaction.
+
+ This method handles caching, and will call loadClass(trans, filepath)
+ if no cache is found. Caching is generally controlled by servlets
+ with the canBeReused() and canBeThreaded() methods.
+
"""
request = transaction.request()
path = request.serverSidePath()
- # Do we need to import/reimport the class because the file changed on disk or isn't in cache?
+ # Do we need to import/reimport the class
+ # because the file changed on disk or isn't in cache?
mtime = os.path.getmtime(path)
- if not self._classCache.has_key(path) or mtime != self._classCache[path]['mtime']:
- # Use a lock to prevent multiple simultaneous imports of the same module
+ if not self._classCache.has_key(path) or \
+ mtime != self._classCache[path]['mtime']:
+ # Use a lock to prevent multiple simultaneous
+ # imports of the same module:
self._importLock.acquire()
try:
- if not self._classCache.has_key(path) or mtime != self._classCache[path]['mtime']:
+ if not self._classCache.has_key(path) or \
+ mtime != self._classCache[path]['mtime']:
theClass = self.loadClass(transaction, path)
if self._cacheClasses:
- self._classCache[path] = {'mtime': mtime, 'class': theClass}
+ self._classCache[path] = {
+ 'mtime': mtime, 'class': theClass}
else:
theClass = self._classCache[path]['class']
finally:
@@ -240,9 +242,9 @@
else:
theClass = self._classCache[path]['class']
- # Try to find a cached servlet of the correct class. (Outdated servlets may
- # have been returned to the pool after a new class was imported, but we don't want
- # to use an outdated servlet.)
+ # Try to find a cached servlet of the correct class.
+ # (Outdated servlets may have been returned to the pool after a new
+ # class was imported, but we don't want to use an outdated servlet.)
if self._threadsafeServletCache.has_key(path):
servlet = self._threadsafeServletCache[path]
if servlet.__class__ is theClass:
@@ -257,10 +259,10 @@
if servlet.__class__ is theClass:
return servlet
- # Use a lock to prevent multiple simultaneous imports
- # of the same module @@ ib 2004-06: Aren't imports
- # threadsafe already? Or maybe not, because we are
- # using tricky import techniques (e.g., __import__)
+ # Use a lock to prevent multiple simultaneous
+ # imports of the same module.
+ # @@ ib 2004-06: Aren't imports threadsafe already? Or maybe not,
+ # because we are using tricky import techniques (e.g., __import__)
self._importLock.acquire()
try:
mtime = os.path.getmtime(path)
@@ -287,23 +289,47 @@
self._servletPool[path] = []
return servlet
- def loadClass(self, transaction, path):
+ def returnServlet(self, trans, servlet):
+ """Return servlet to the pool.
+
+ Called by Servlet.close(), which returns the servlet
+ to the servlet pool if necessary.
+
"""
- Given a transaction and a path, load the class for
- creating these servlets. Caching, pooling, and
- threadsafeness are all handled by
- servletForTransaction. This method is not expected to
- be threadsafe.
+ if servlet.canBeReused() and not servlet.canBeThreaded() \
+ and self._cacheInstances:
+ path = trans.request().serverSidePath()
+ self._servletPool[path].append(servlet)
+
+ def flushCache(self):
+ """Flush the servlet cache and start fresh.
+
+ Servlets that are currently in the wild may find their way back
+ into the cache (this may be a problem).
+
"""
- raise AbstractError, self.__class__
+ # @@ ib 07-2003: I'm unsure how well this works.
+ self._importLock.acquire()
+ self._classCache = {}
+ # We can't just delete all the lists, because returning
+ # servlets expect it to exist.
+ for key in self._servletPool.keys():
+ self._servletPool[key] = []
+ self._threadsafeServletCache = {}
+ self._importLock.release()
class PythonServletFactory(ServletFactory):
+ """The factory for Python servlets.
+
+ This is the factory for ordinary Python servlets whose extensions
+ are empty or .py. The servlets are unique per file since the file
+ itself defines the servlet.
+
"""
- This is the factory for ordinary Python servlets whose
- extensions are empty or .py. The servlets are unique per file
- since the file itself defines the servlet.
- """
+
+
+ ## Info ##
def uniqueness(self):
return 'file'
@@ -311,6 +337,9 @@
def extensions(self):
return ['.py']
+
+ ## Import ##
+
def loadClass(self, transaction, path):
# Import the module as part of the context's package
module = self.importAsPackage(transaction, path)
@@ -330,17 +359,15 @@
# If the mangled name does not exist either, report an error:
if not hasattr(module, name) is None:
raise ValueError, \
- 'Cannot find expected servlet class %r in %r.' % (name, path)
+ 'Cannot find expected servlet class %r in %r.' \
+ % (name, path)
# Pull the servlet class out of the module:
theClass = getattr(module, name)
- # new-style classes aren't ClassType, but they
- # are okay to use. They are subclasses of
- # type. But type isn't a class in older
- # Python versions, it's a builtin function.
- # So we test what type is first, then use
- # isinstance only for the newer Python
- # versions
+ # New-style classes aren't ClassType, but they are okay to use.
+ # They are subclasses of type. But type isn't a class in older
+ # Python versions, it's a builtin function. So we test what type
+ # is first, then use isinstance only for the newer Python versions.
if type(type) is BuiltinFunctionType:
assert type(theClass) is ClassType
else:
Modified: Webware/trunk/WebKit/Session.py
==============================================================================
--- Webware/trunk/WebKit/Session.py (original)
+++ Webware/trunk/WebKit/Session.py Thu Sep 29 13:21:48 2005
@@ -11,7 +11,8 @@
class Session(Object):
- """
+ """Implementation of client sessions.
+
All methods that deal with time stamps, such as creationTime(),
treat time as the number of seconds since January 1, 1970.
@@ -41,6 +42,7 @@
abstract super class to codify that?
"""
+
## Init ##
def __init__(self, trans, identifier=None):
@@ -66,14 +68,17 @@
self._identifier = identifier
else:
attempts = 0
- while attempts<10000:
- self._identifier = self._prefix + string.join(map(lambda x: '%02d' % x, localtime(time())[:6]), '') + '-' + uniqueId(self)
+ while attempts < 10000:
+ self._identifier = self._prefix + ''.join(
+ map(lambda x: '%02d' % x,
+ localtime(time())[:6])) + '-' + uniqueId(self)
if not trans.application().hasSession(self._identifier):
break
attempts = attempts + 1
else:
- raise SessionError, "Can't create valid session id after %d attempts." % attempts
-
+ raise SessionError, \
+ "Can't create valid session id after %d attempts." % attempts
+
if trans.application().setting('Debug')['Sessions']:
print '>> [session] Created session, timeout=%s, id=%s, self=%s' % (
self._timeout, self._identifier, self)
@@ -82,42 +87,55 @@
## Access ##
def creationTime(self):
- """ Returns the time when this session was created. """
+ """Return the time when this session was created."""
return self._creationTime
def lastAccessTime(self):
- """ Returns the last time the user accessed the session through interaction. This attribute is updated in awake(), which is invoked at the beginning of a transaction. """
+ """Get last access time.
+
+ Returns the last time the user accessed the session through
+ interaction. This attribute is updated in awake(), which is
+ invoked at the beginning of a transaction.
+
+ """
return self._lastAccessTime
def identifier(self):
- """ Returns a string that uniquely identifies the session. This method will create the identifier if needed. """
+ """Return a string that uniquely identifies the session.
+
+ This method will create the identifier if needed.
+
+ """
return self._identifier
def isExpired(self):
- """
- Returns true if the session has been previously expired.
+ """Check whether the session has been previously expired.
+
See also: expiring()
+
"""
return getattr(self, '_isExpired', 0)
def isNew(self):
- return self._numTrans<2
+ """Check whether the session is new."""
+ return self._numTrans < 2
def timeout(self):
- """ Returns the timeout for this session in seconds. """
+ """Return the timeout for this session in seconds."""
return self._timeout
def setTimeout(self, timeout):
- """ Set the timeout on this session in seconds. """
+ """Set the timeout on this session in seconds."""
self._timeout = timeout
## Invalidate ##
def invalidate(self):
- """
- Invalidates the session.
+ """Invalidate the session.
+
It will be discarded the next time it is accessed.
+
"""
self._lastAccessTime = 0
self._values = {}
@@ -157,23 +175,43 @@
## Transactions ##
def awake(self, trans):
- """ Invoked during the beginning of a transaction, giving a Session an opportunity to perform any required setup. The default implementation updates the 'lastAccessTime'. """
+ """Let the session awake.
+
+ Invoked during the beginning of a transaction, giving a Session an
+ opportunity to perform any required setup. The default implementation
+ updates the 'lastAccessTime'.
+
+ """
self._lastAccessTime = time()
self._numTrans += 1
def respond(self, trans):
- """ The default implementation does nothing, but could do something in the future. Subclasses should invoke super. """
+ """Let the session respond to a request.
+
+ The default implementation does nothing, but could do something
+ in the future. Subclasses should invoke super.
+
+ """
pass
def sleep(self, trans):
- """ Invoked during the ending of a transaction, giving a Session an opportunity to perform any required shutdown. The default implementation does nothing, but could do something in the future. Subclasses should invoke super. """
+ """Let the session sleep again.
+
+ Invoked during the ending of a transaction, giving a Session an
+ opportunity to perform any required shutdown. The default
+ implementation does nothing, but could do something in the future.
+ Subclasses should invoke super.
+
+ """
pass
def expiring(self):
- """
+ """Let the session expire.
+
Called when session is expired by the application.
Subclasses should invoke super.
Session store __delitem__()s should invoke if not isExpired().
+
"""
self._isExpired = 1
@@ -181,9 +219,7 @@
## Utility ##
def sessionEncode(self, url):
- """
- Encode the session ID as a parameter to a url.
- """
+ """Encode the session ID as a parameter to a url."""
import urlparse
sid = '_SID_=' + self.identifier()
url=list(urlparse.urlparse(url)) #make a list
Modified: Webware/trunk/WebKit/SessionDynamicStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionDynamicStore.py (original)
+++ Webware/trunk/WebKit/SessionDynamicStore.py Thu Sep 29 13:21:48 2005
@@ -5,19 +5,20 @@
debug = 0
class SessionDynamicStore(SessionStore):
- """
- Stores the session in Memory and Files.
+ """Stores the session in Memory and Files.
This can be used either in a persistent app server or a cgi framework.
- To use this Session Store, set SessionStore in Application.config to 'Dynamic'.
- Other variables which can be set in Application.config are:
+ To use this Session Store, set SessionStore in Application.config
+ to 'Dynamic'. Other variables which can be set in Application.config are:
+
+ 'MaxDynamicMemorySessions', which sets the maximum number of sessions
+ that can be in the Memory SessionStore at one time. Default is 10,000.
- 'MaxDynamicMemorySessions', which sets the maximum number of sessions that can be
- in the Memory SessionStore at one time. Default is 10,000.
+ 'DynamicSessionTimeout', which sets the default time for a session to stay
+ in memory with no activity. Default is 15 minutes. When specifying this in
+ Application.config, use minutes.
- 'DynamicSessionTimeout', which sets the default time for a session to stay in memory
- with no activity. Default is 15 minutes. When specifying this in Application.config, use minutes.
"""
@@ -27,24 +28,30 @@
# Create both a file store and a memory store
SessionStore.__init__(self, app)
self._fileStore = SessionFileStore.SessionFileStore(app)
- self._memoryStore = SessionMemoryStore.SessionMemoryStore(app, restoreFiles=0)
+ self._memoryStore = SessionMemoryStore.SessionMemoryStore(
+ app, restoreFiles=0)
self._memoryStore.clear() #fileStore will have the files on disk
- # moveToFileInterval specifies after what period of time a session is automatically moved to file
- self._moveToFileInterval = self.application().setting('DynamicSessionTimeout', 15) * 60
-
- # maxDynamicMemorySessions is what the user actually sets in Application.config
- self._maxDynamicMemorySessions = self.application().setting('MaxDynamicMemorySessions', 10000)
+ # moveToFileInterval specifies after what period of time
+ # a session is automatically moved to file
+ self._moveToFileInterval = self.application().setting(
+ 'DynamicSessionTimeout', 15) * 60
+
+ # maxDynamicMemorySessions is what the user actually sets
+ # in Application.config
+ self._maxDynamicMemorySessions = self.application().setting(
+ 'MaxDynamicMemorySessions', 10000)
# Used to keep track of sweeping the file store
self._fileSweepCount = 0
- # Create a re-entrant lock for thread synchronization. The lock is used to protect
- # all code that modifies the contents of the file store and all code that moves sessions
- # between the file and memory stores, and is also used to protect code that searches in
- # the file store for a session. Using the lock in this way avoids a bug that used to
- # be in this code, where a session was temporarily neither in the file store nor in
- # the memory store while it was being moved from file to memory.
+ # Create a re-entrant lock for thread synchronization. The lock is used
+ # to protect all code that modifies the contents of the file store and
+ # all code that moves sessions between the file and memory stores, and
+ # is also used to protect code that searches in the file store for a
+ # session. Using the lock in this way avoids a bug that used to be in
+ #this code, where a session was temporarily neither in the file store
+ # nor in the memory store while it was being moved from file to memory.
self._lock = threading.RLock()
if debug:
@@ -61,8 +68,8 @@
def __getitem__(self, key):
# First try to grab the session from the memory store without locking,
- # for efficiency. Only if that fails do we acquire the lock and
- # look in the file store.
+ # for efficiency. Only if that fails do we acquire the lock and look
+ # in the file store.
try:
return self._memoryStore[key]
except KeyError:
@@ -79,12 +86,12 @@
def __setitem__(self, key, item):
self._memoryStore[key] = item
# @@ 2001-12-11 gat: Seems like a waste of time to attempt to delete the
- # session from the file store on every single write operation. I see no
+ # session from the file store on every single write operation. I see no
# harm in commenting out the rest of this method.
-## try:
-## del self._fileStore[key]
-## except KeyError:
-## pass
+ # try:
+ # del self._fileStore[key]
+ # except KeyError:
+ # pass
def __delitem__(self, key):
self._lock.acquire()
@@ -137,7 +144,7 @@
return default
finally:
self._lock.release()
-
+
def MovetoMemory(self, key):
self._lock.acquire()
try:
@@ -159,7 +166,7 @@
self._lock.release()
def setEncoderDecoder(self, encoder, decoder):
- # @@ 2002-11-26 jdh: # propogate the encoder/decoder to the
+ # @@ 2002-11-26 jdh: # propogate the encoder/decoder to the
# underlying SessionFileStore
self._fileStore.setEncoderDecoder(encoder, decoder)
SessionStore.setEncoderDecoder(self,encoder,decoder)
@@ -178,16 +185,19 @@
self._lock.release()
def cleanStaleSessions(self, task=None):
- """
- Called by the Application to tell this store to clean out all sessions that have
- exceeded their lifetime.
+ """Clean stale sessions.
+
+ Called by the Application to tell this store to clean out all sessions
+ that have exceeded their lifetime.
We want to have their native class functions handle it, though.
- Ideally, intervalSweep would be run more often than the cleanStaleSessions functions
- for the actual stores. This may need to wait until we get the TaskKit in place, though.
+ Ideally, intervalSweep would be run more often than the
+ cleanStaleSessions functions for the actual stores.
+ This may need to wait until we get the TaskKit in place, though.
The problem is the FileStore.cleanStaleSessions can take a while to run.
So here, we only run the file sweep every fourth time.
+
"""
if debug: print "Session Sweep started"
try:
@@ -200,23 +210,27 @@
self._fileSweepCount = self._fileSweepCount + 1
else:
self._fileSweepCount = 0
- #now move sessions from memory to file as necessary
+ # Now move sessions from memory to file as necessary:
self.intervalSweep()
-#It's OK for a session to moved from memory to file or vice versa in between the time we get the keys and the time we actually ask for the session's access time. It may take a while for the fileStore sweep to get completed.
+# It's OK for a session to moved from memory to file or vice versa in between
+# the time we get the keys and the time we actually ask for the session's
+# access time. It may take a while for the fileStore sweep to get completed.
def intervalSweep(self):
- """
- The interval function moves sessions from memory to file. and can be run more often than the full
- cleanStaleSessions function.
+ """The session sweeper interval function.
+
+ The interval function moves sessions from memory to file
+ and can be run more often than the full cleanStaleSessions function.
"""
global debug
if debug:
print "Starting interval Sweep at %s" % time.ctime(time.time())
- print "Memory Sessions: %s FileSessions: %s" % (len(self._memoryStore), len(self._fileStore))
+ print "Memory Sessions: %s FileSessions: %s" % (
+ len(self._memoryStore), len(self._fileStore))
print "maxDynamicMemorySessions = %s" % self._maxDynamicMemorySessions
print "moveToFileInterval = %s" % self._moveToFileInterval
@@ -241,14 +255,14 @@
except KeyError:
pass
- if debug: print "Finished interval Sweep at %s" % time.ctime(time.time())
- if debug: print "Memory Sessions: %s FileSessions: %s" % (len(self._memoryStore), len(self._fileStore))
+ if debug:
+ print "Finished interval Sweep at %s" % time.ctime(time.time())
+ print "Memory Sessions: %s FileSessions: %s" % (
+ len(self._memoryStore), len(self._fileStore))
def memoryKeysInAccessTimeOrder(self):
- """
- Returns memory store's keys in ascending order of last access time.
- """
+ """Return memory store's keys in ascending order of last access time."""
# This sorting technique is faster than using a comparison function.
accessTimeAndKeys = []
for key in self._memoryStore.keys():
Modified: Webware/trunk/WebKit/SessionFileStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionFileStore.py (original)
+++ Webware/trunk/WebKit/SessionFileStore.py Thu Sep 29 13:21:48 2005
@@ -7,9 +7,10 @@
class SessionFileStore(SessionStore):
- """
- Stores the sessions on disk in the Sessions/ directory, one file
- per session.
+ """A session file store.
+
+ Stores the sessions on disk in the Sessions/ directory,
+ one file per session.
This is useful for various situations:
1. Using the OneShot adapter
@@ -18,8 +19,8 @@
3. Fault tolerance
4. Clustering
- Note that the last two are not yet supported by WebKit (as of 0.4,
- 8/2000).
+ Note that the last two are not yet supported by WebKit.
+
"""
@@ -53,7 +54,7 @@
item = self.decoder()(file)
finally:
file.close()
- except: # session can't be unpickled
+ except: # session can't be unpickled
os.remove(filename) # remove session file
print "Error loading session from disk:", key
self.application().handleException()
@@ -71,7 +72,6 @@
# have to lock the file for the entire time that the servlet is manipulating
# the session, which would block any other servlets from using that session.
# Doesn't seem like a great solution to me.
-
if debug:
print '>> setitem(%s,%s)' % (key, item)
filename = self.filenameForKey(key)
@@ -145,18 +145,21 @@
def storeAllSessions(self):
pass
-## def cleanStaleSessions(self, task=None):
-## """
-## Called by the Application to tell this store to clean out all sessions that
-## have exceeded their lifetime.
-## """
-## we don't know the timeout without opening the session, so his can't work.
-## curTime = time.time()
-## for key in self.keys():
-## mtime = os.path.getmtime(self.filenameForKey(key))
-## if (curTime - mtime) >= sess.timeout() or sess.timeout()==0:
-## sess.expiring()
-## del self[key]
+ # We don't know the timeout without opening the session, so this can't work:
+ # def cleanStaleSessions(self, task=None):
+ # """Clean stale sessions.
+ #
+ # Called by the Application to tell this store to clean out all
+ # sessions that have exceeded their lifetime.
+ # """
+ #
+ # curTime = time.time()
+ # for key in self.keys():
+ # mtime = os.path.getmtime(self.filenameForKey(key))
+ # if (curTime - mtime) >= sess.timeout() or sess.timeout()==0:
+ # sess.expiring()
+ # del self[key]
+
## Self utility ##
Modified: Webware/trunk/WebKit/SessionMemoryStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionMemoryStore.py (original)
+++ Webware/trunk/WebKit/SessionMemoryStore.py Thu Sep 29 13:21:48 2005
@@ -4,10 +4,10 @@
class SessionMemoryStore(SessionStore):
- """
- Stores the session in memory as a dictionary.
+ """Stores the session in memory as a dictionary.
This is fast and secure when you have one, persistent app server.
+
"""
Modified: Webware/trunk/WebKit/SessionStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionStore.py (original)
+++ Webware/trunk/WebKit/SessionStore.py Thu Sep 29 13:21:48 2005
@@ -2,7 +2,8 @@
class SessionStore(Object):
- """
+ """A general session store.
+
SessionStores are dictionary-like objects used by Application to
store session state. This class is abstract and it's up to the
concrete subclass to implement several key methods that determine
@@ -33,6 +34,7 @@
prevent concurrent requests on the same session? If so, that can
probably be done at this level (as opposed to pushing the burden
on various subclasses).
+
"""
@@ -49,7 +51,8 @@
except ImportError:
import pickle
if hasattr(pickle, 'HIGHEST_PROTOCOL'):
- def dump_with_highest_protocol(obj, f, proto=pickle.HIGHEST_PROTOCOL, dump=pickle.dump):
+ def dump_with_highest_protocol(obj, f,
+ proto=pickle.HIGHEST_PROTOCOL, dump=pickle.dump):
return dump(obj, f, proto)
self._encoder = dump_with_highest_protocol
else:
@@ -75,12 +78,14 @@
raise AbstractError, self.__class__
def __delitem__(self, key):
- """
+ """Delete an item.
+
Subclasses are responsible for expiring the session as well.
Something along the lines of:
sess = self[key]
if not sess.isExpired():
sess.expiring()
+
"""
raise AbstractError, self.__class__
@@ -96,6 +101,7 @@
def setdefault(self, key, default):
raise AbstractError, self.__class__
+
## Application support ##
def storeSession(self, session):
@@ -105,9 +111,11 @@
raise AbstractError, self.__class__
def cleanStaleSessions(self, task=None):
- """
- Called by the Application to tell this store to clean out all sessions that
- have exceeded their lifetime.
+ """Clean stale sessions.
+
+ Called by the Application to tell this store to clean out all
+ sessions that have exceeded their lifetime.
+
"""
curTime = time.time()
for key, sess in self.items():
Modified: Webware/trunk/WebKit/ThreadedAppServer.py
==============================================================================
--- Webware/trunk/WebKit/ThreadedAppServer.py (original)
+++ Webware/trunk/WebKit/ThreadedAppServer.py Thu Sep 29 13:21:48 2005
@@ -1,12 +1,12 @@
#!/usr/bin/env python
-"""
-ThreadedAppServer uses a threaded model for handling multiple
-requests. At one time there were other experimental execution
-models for AppServer, but none of these were successful and
-have been removed. The ThreadedAppServer/AppServer distinction
-is thus largely historical.
-"""
+"""Threaded Application Server
+ThreadedAppServer uses a threaded model for handling multiple requests.
+At one time there were other experimental execution models for AppServer,
+but none of these were successful and have been removed.
+The ThreadedAppServer/AppServer distinction is thus largely historical.
+
+"""
from Common import *
import AppServer as AppServerModule
@@ -25,6 +25,11 @@
import traceback
from WebUtils import Funcs
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
+
debug = 0
DefaultConfig = {
@@ -40,35 +45,40 @@
'StartServerThreads': 10,
# @@ 2000-04-27 ce: None of the following settings are implemented
-# 'RequestQueueSize': 16,# 'RequestBufferSize': 64*1024,
-# 'SocketType': 'inet', # inet, unix
+ # 'RequestQueueSize': 16,# 'RequestBufferSize': 64*1024,
+ # 'SocketType': 'inet', # inet, unix
}
-#Need to know this value for communications
-#Note that this limits the size of the dictionary we receive from the AppServer to 2,147,483,647 bytes
+# Need to know this value for communications
+# (note that this limits the size of the dictionary we receive
+# from the AppServer to 2,147,483,647 bytes):
intLength = len(dumps(int(1)))
server = None
+
class NotEnoughDataError(Exception):
pass
class ProtocolError(Exception):
pass
+
class ThreadedAppServer(AppServer):
- """
+ """Threaded Application Server.
+
`ThreadedAppServer` accepts incoming socket requests, spawns a
new thread or reuses an existing one, then dispatches the request
- to the appropriate handler (e.g., an Adapter handler, HTTP
- handler, etc, one for each protocol).
+ to the appropriate handler (e.g., an Adapter handler, HTTP handler,
+ etc., one for each protocol).
The transaction is connected directly to the socket, so that the
response is sent directly (if streaming is used, like if you call
- ``response.flush()``). Thus the ThreadedAppServer packages
- the socket/response, rather than value being returned up the
- call chain.
+ ``response.flush()``). Thus the ThreadedAppServer packages the
+ socket/response, rather than value being returned up the call chain.
+
"""
+
## Init ##
def __init__(self, path=None):
@@ -117,7 +127,8 @@
self.readyForRequests()
def addSocketHandler(self, handlerClass, serverAddress=None):
- """
+ """Add socket handler.
+
Adds a socket handler for `serverAddress` -- `serverAddress`
is a tuple (*host*, *port*), where *host* is the interface
to connect to (for instance, the IP address on a machine
@@ -129,6 +140,7 @@
to handle the actual request -- usually returning control
back to ThreadedAppServer in some fashion. See `Handler`
for more.
+
"""
if serverAddress is None:
@@ -153,8 +165,7 @@
self._sockets[serverAddress] = sock
def isPersistent(self):
- ":ignore:"
- return 1
+ return True
def defaultConfig(self):
if self._defaultConfig is None:
@@ -163,18 +174,17 @@
return self._defaultConfig
def mainloop(self, timeout=1):
- """
+ """Main thread loop.
+
This is the main thread loop that accepts and dispatches
socket requests.
- It goes through an loop as long as ``self.running``
- is true (i.e., ``self.running = 0`` asks the server to
- stop running).
+ It goes through an loop as long as ``self.running`` is True
+ (i.e., ``self.running = False`` asks the server to stop running).
The loop waits for connections, then based on the connecting
port it initiates the proper Handler (e.g.,
- AdapterHandler, HTTPHandler). Handlers are reused when
- possible.
+ AdapterHandler, HTTPHandler). Handlers are reused when possible.
The initiated handlers are put into a queue, and
worker threads poll that queue to look for requests that
@@ -183,15 +193,15 @@
Every so often (every 5 loops) it updates thread usage
information (`updateThreadUsage`), and every
``MaxServerThreads`` * 2 loops it it will manage
- threads (killing or spawning new ones, in
- `manageThreadCount`).
+ threads (killing or spawning new ones, in `manageThreadCount`).
+
"""
from errno import EINTR
threadCheckInterval = self._maxServerThreads*2
- threadUpdateDivisor = 5 #grabstat interval
- threadCheck=0
+ threadUpdateDivisor = 5 # grab stat interval
+ threadCheck = 0
while 1:
if not self.running:
@@ -205,7 +215,7 @@
if not self.running:
return
input = []
- if v[0] == EINTR or v[0]==0: break
+ if v[0] == EINTR or v[0] == 0: break
else: raise
for sock in input:
@@ -223,26 +233,26 @@
self.updateThreadUsage()
if threadCheck > threadCheckInterval:
- threadCheck=0
+ threadCheck = 0
self.manageThreadCount()
else:
threadCheck = threadCheck + 1
self.restartIfNecessary()
- """
- **Thread Management**
- These methods handle the thread pool. The AppServer pre-allocates
- threads, and reuses threads for requests. So as more threads
- are needed with varying load, new threads are spawned, and if there
- are excess threads than threads are removed.
- """
+ ## Thread Management ##
+
+ # These methods handle the thread pool. The AppServer pre-allocates
+ # threads, and reuses threads for requests. So as more threads
+ # are needed with varying load, new threads are spawned, and if there
+ # are excess threads than threads are removed.
def updateThreadUsage(self):
- """
- Update the threadUseCounter list. Called periodically
- from `mainloop`.
+ """Update the threadUseCounter list.
+
+ Called periodically from `mainloop`.
+
"""
count = self.activeThreadCount()
if len(self._threadUseCounter) > self._maxServerThreads:
@@ -250,9 +260,10 @@
self._threadUseCounter.append(count)
def activeThreadCount(self):
- """
- Get a snapshot of the number of threads currently in use.
+ """Get a snapshot of the number of threads currently in use.
+
Called from `updateThreadUsage`.
+
"""
count = 0
for i in self._threadPool:
@@ -261,17 +272,17 @@
return count
def manageThreadCount(self):
- """
- Adjust the number of threads in use. From information
- gleened from `updateThreadUsage`, we see about how
- many threads are being used, to see if we have too
- many threads or too few. Based on this we create or
- absorb threads.
+ """Adjust the number of threads in use.
+
+ From information gleened from `updateThreadUsage`, we see about how
+ many threads are being used, to see if we have too many threads or
+ too few. Based on this we create or absorb threads.
+
"""
- ## @@: This algorithm needs work. The edges (ie at
- ## the minserverthreads) are tricky. When working
- ## with this, remember thread creation is CHEAP
+ # @@: This algorithm needs work. The edges (i.e. at
+ # the minserverthreads) are tricky. When working
+ # with this, remember thread creation is *cheap*.
average = 0
max = 0
@@ -307,32 +318,36 @@
self._threadCount - max)
self.absorbThread(n)
else:
- #cleanup any stale threads that we killed but haven't joined
+ # cleanup any stale threads that we killed but haven't joined
self.absorbThread(0)
def spawnThread(self):
+ """Create a new worker thread.
+
+ Worker threads poll with the `threadloop` method.
+
"""
- Create a new worker thread. Worker threads poll
- with the `threadloop` method.
- """
- debug=0
+ debug = 0
if debug: print "Spawning new thread"
t = Thread(target=self.threadloop)
- t._processing = 0
+ t._processing = False
t.start()
self._threadPool.append(t)
self._threadCount += 1
- if debug: print "New Thread Spawned, threadCount=", self._threadCount
+ if debug:
+ print "New Thread Spawned, threadCount=", self._threadCount
def absorbThread(self, count=1):
- """
- Absorb a thread. We do this by putting a None on the
- Queue. When a thread gets it, that tells it to exit.
+ """Absorb a thread.
+
+ We do this by putting a None on the Queue.
+ When a thread gets it, that tells it to exit.
We also keep track of the threads, so after killing
threads we go through all the threads and find the
thread(s) that have exited, so that we can take them
out of the thread pool.
+
"""
for i in range(count):
self._requestQueue.put(None)
@@ -349,31 +364,29 @@
self._threadPool.remove(i)
if debug: print "Thread Absorbed, Real Thread Count=", len(self.threadPool)
- """
- **Worker Threads**
- """
+
+ ## Worker Threads ##
def threadloop(self):
- """
- This is the main loop for worker threads. Worker
- threads poll the ``_requestQueue`` to find a request
- handler waiting to run. If they find a None in the
- queue, this thread has been selected to die, which is
- the way the loop ends.
-
- The handler object does all the work when its
- `handleRequest` method is called.
-
- `initThread` and `delThread` methods are called at
- the beginning and end of the thread loop, but they
- aren't being used for anything (future use as a
- hook).
+ """The main loop for worker threads.
+
+ Worker threads poll the ``_requestQueue`` to find a request handler
+ waiting to run. If they find a None in the queue, this thread has
+ been selected to die, which is the way the loop ends.
+
+ The handler object does all the work when its `handleRequest` method
+ is called.
+
+ `initThread` and `delThread` methods are called at the beginning and
+ end of the thread loop, but they aren't being used for anything
+ (future use as a hook).
+
"""
self.initThread()
- t=threading.currentThread()
- t.processing=0
+ t = threading.currentThread()
+ t.processing = False
try:
while 1:
@@ -382,12 +395,12 @@
if handler is None: #None means time to quit
if debug: print "Thread retrieved None, quitting"
break
- t.processing=1
+ t.processing = True
try:
handler.handleRequest()
except:
traceback.print_exc(file=sys.stderr)
- t.processing=0
+ t.processing = False
handler.close()
except Queue.Empty:
pass
@@ -396,40 +409,41 @@
if debug: print threading.currentThread(), "Quitting"
def initThread(self):
- """
- Invoked immediately by threadloop() as a hook for
- subclasses. This implementation does nothing and
- subclasses need not invoke super.
+ """Initialize thread.
+
+ Invoked immediately by threadloop() as a hook for subclasses.
+ This implementation does nothing and subclasses need not invoke super.
+
"""
pass
def delThread(self):
- """
- Invoked immediately by threadloop() as a hook for
- subclasses. This implementation does nothing and
- subclasses need not invoke super.
+ """Delete thread.
+
+ Invoked immediately by threadloop() as a hook for subclasses.
+ This implementation does nothing and subclasses need not invoke super.
+
"""
pass
- """
- **Shutting Down**
- """
+
+ ## Shutting Down ##
def shutDown(self):
- """
- Called on shutdown. Also calls `AppServer.shutDown`,
- but first closes all sockets and tells all the threads
- to die.
- """
+ """Called on shutdown.
+
+ Also calls `AppServer.shutDown`, but first closes all sockets
+ and tells all the threads to die.
- self.running=0
+ """
+ self.running = False
self.awakeSelect()
- self._shuttingdown=1 #jsl-is this used anywhere?
+ self._shuttingdown = False # jsl-is this used anywhere?
print "ThreadedAppServer: Shutting Down"
for sock in self._sockets.values():
sock.close()
for i in range(self._threadCount):
- self._requestQueue.put(None)#kill all threads
+ self._requestQueue.put(None) # kill all threads
for i in self._threadPool:
try:
i.join()
@@ -438,10 +452,12 @@
AppServer.shutDown(self)
def awakeSelect(self):
- """
+ """Awake the select() call.
+
The ``select()`` in `mainloop` is blocking, so when
we shut down we have to make a connect to unblock it.
Here's where we do that, called `shutDown`.
+
"""
for host, port in self._sockets.keys():
@@ -455,15 +471,16 @@
except:
pass
- """
- **Misc**
- """
+
+ ## Misc ##
def address(self, settingPrefix):
- """
+ """Get host address.
+
The address for the Adapter (Host/interface, and port),
- taken from ``Configs/AppServer.config``, setting
- ``Host`` and ``AdapterPort``.
+ as taken from ``Configs/AppServer.config``,
+ settings ``Host`` and ``AdapterPort``.
+
"""
try:
return self._addr[settingPrefix]
@@ -472,7 +489,7 @@
self.setting('Host'))
if settingPrefix == 'Adapter':
# jdh 2004-12-01:
- # 'Port' has been renamed to 'AdapterPort'. However, we don't
+ # 'Port' has been renamed to 'AdapterPort'. However, we don't
# want the the default AdapterPort in DefaultConfig above to
# be used if a user still has 'Port' in their config file.
# So for now, we prefer the 'Port' setting if it exists.
@@ -481,7 +498,9 @@
if port is None:
port = self.setting(settingPrefix + 'Port')
else:
- print "WARNING: The 'Port' setting has been renamed to 'AdapterPort'.\n Please update your AppServer.config file"
+ print "WARNING:", \
+ "The 'Port' setting has been renamed to 'AdapterPort'."
+ print "Please update your AppServer.config file."
else:
port = self.setting(settingPrefix + 'Port')
self._addr[settingPrefix] = (
@@ -490,50 +509,52 @@
return self._addr[settingPrefix]
class Handler:
+ """A very general socket handler.
- """
- Handler is an abstract superclass -- specific protocol
- implementations will subclass this. A Handler takes a socket
- to interact with, and creates a raw request.
-
- Handlers will be reused. When a socket is received
- `activate` will be called -- but the handler should not do
- anything, as it is still running in the main thread. The
- handler is put into a queue, and a worker thread picks it
- up and runs `handleRequest`, which subclasses should override.
+ Handler is an abstract superclass -- specific protocol implementations
+ will subclass this. A Handler takes a socket to interact with, and
+ creates a raw request.
+
+ Handlers will be reused. When a socket is received `activate` will be
+ called -- but the handler should not do anything, as it is still running
+ in the main thread. The handler is put into a queue, and a worker thread
+ picks it up and runs `handleRequest`, which subclasses should override.
+
+ Several methods are provided which are typically used by subclasses.
- Several methods are provided which are typically used by
- subclasses.
"""
def __init__(self, server, serverAddress):
- """
+ """Create a new socket handler.
+
Each handler is attached to a specific host and port,
and of course to the AppServer.
+
"""
self._server = server
self._serverAddress = serverAddress
def activate(self, sock, requestID):
- """
- Activates the handler for processing the request.
- `sock` is the incoming socket that this handler
- will work with, and `requestID` is a serial number
- unique for each request.
-
- This isn't where work gets done -- the handler is
- queued after this, and work is done when
- `handleRequest` is called.
+ """Activate the handler for processing the request.
+
+ `sock` is the incoming socket that this handler will work with,
+ and `requestID` is a serial number unique for each request.
+
+ This isn't where work gets done -- the handler is queued after this,
+ and work is done when `handleRequest` is called.
+
"""
self._requestID = requestID
self._sock = sock
def close(self):
- """
- Called when the handler is finished. Closes the socket
- and returns the handler to the pool of inactive handlers.
+ """Close the socket.
+
+ Called when the handler is finished. Closes the socket and
+ returns the handler to the pool of inactive handlers.
+
"""
self._sock = None
self._server._handlerCache[self._serverAddress].append(self)
@@ -546,9 +567,11 @@
pass
def receiveDict(self):
- """
+ """Receive a dictionary from the socket.
+
Utility function to receive a marshalled dictionary from
- the socket. Returns None if the request was empty.
+ the socket. Returns None if the request was empty.
+
"""
chunk = ''
missing = intLength
@@ -561,7 +584,8 @@
return None
else:
# We got a partial request -- something went wrong.
- raise NotEnoughDataError, 'received only %d of %d bytes when receiving dictLength' % (len(chunk), intLength)
+ raise NotEnoughDataError, 'received only %d of %d bytes' \
+ ' when receiving dictLength' % (len(chunk), intLength)
chunk += block
missing = intLength - len(chunk)
try:
@@ -580,8 +604,9 @@
self._sock.close()
return None
print 'ERROR:', msg
- print 'ERROR: you can only connect to %s via an adapter, like mod_webkit or' % self._serverAddress[1]
- print ' wkcgi, not with a browser)'
+ print 'ERROR: you can only connect to', self._serverAddress[1], \
+ 'via an adapter,'
+ print ' like mod_webkit or wkcgi, not with a browser.'
raise
if type(dictLength) != type(1):
self._sock.close()
@@ -592,24 +617,25 @@
block = self._sock.recv(missing)
if not block:
self._sock.close()
- raise NotEnoughDataError, 'received only %d of %d bytes when receiving dict' % (len(chunk), dictLength)
+ raise NotEnoughDataError, 'received only %d of %d bytes' \
+ ' when receiving dict' % (len(chunk), dictLength)
chunk += block
missing = dictLength - len(chunk)
return loads(chunk)
class MonitorHandler(Handler):
+ """Monitor server status.
- """
Monitor is a minimal service that accepts a simple protocol,
and returns a value indicating the status of the server.
The protocol passes a marshalled dict, much like the Adapter
interface, which looks like ``{'format': 'XXX'}``, where XXX
- is a command (``STATUS`` or ``QUIT``). Responds with a simple
+ is a command (``STATUS`` or ``QUIT``). Responds with a simple
string, either the number of requests we've received (for
- ``STATUS``) or ``OK`` for ``QUIT`` (which also stops the
- server)
+ ``STATUS``) or ``OK`` for ``QUIT`` (which also stops the server).
+
"""
# @@ 2003-03 ib: we should have a RESTART command, and
@@ -642,50 +668,49 @@
self.server.shutDown()
-
-
-
-
from WebKit.ASStreamOut import ASStreamOut
class TASASStreamOut(ASStreamOut):
+ """Response stream for ThreadedAppServer.
- """
- The `TASASStreamOut` class streams to a given socket, so that
- when `flush` is called and the buffer is ready to be written,
- it sends the data from the buffer out on the socket. This is
- the response stream used for requests generated by
- ThreadedAppServer.
+ The `TASASStreamOut` class streams to a given socket, so that when `flush`
+ is called and the buffer is ready to be written, it sends the data from the
+ buffer out on the socket. This is the response stream used for requests
+ generated by ThreadedAppServer.
+
+ TAS stands for ThreadedAppServer (AS for AppServer... a little redundant).
- TAS stands for ThreadedAppServer (AS for AppServer... a little
- redundant).
"""
def __init__(self, sock):
- """
- We get an extra `sock` argument, which is the socket
- which we'll stream output to (if we're streaming).
+ """Create stream.
+
+ We get an extra `sock` argument, which is the socket which we'll
+ stream output to (if we're streaming).
+
"""
ASStreamOut.__init__(self)
self._socket = sock
def flush(self):
- """
- Calls `ASStreamOut.ASStreamOut.flush`, and if that
- returns true (indicating the buffer is full enough)
- then we send data from the buffer out on the socket.
+ """Flush stream.
+
+ Calls `ASStreamOut.ASStreamOut.flush`, and if that returns True
+ (indicating the buffer is full enough) then we send data from
+ the buffer out on the socket.
+
"""
- debug=0
+ debug = 0
result = ASStreamOut.flush(self)
- if result: ##a true return value means we can send
+ if result: # a True return value means we can send
reslen = len(self._buffer)
sent = 0
while sent < reslen:
try:
sent = sent + self._socket.send(self._buffer[sent:sent+8192])
except socket.error, e:
- if e[0]==errno.EPIPE: #broken pipe
+ if e[0] == errno.EPIPE: # broken pipe
pass
elif hasattr(errno, 'ECONNRESET') and e[0]==errno.ECONNRESET:
pass
@@ -696,8 +721,8 @@
class AdapterHandler(Handler):
+ """Adapter handler.
- """
Handles the Adapter protocol (as used in mod_webkit, wkcgi,
WebKit.cgi, HTTPAdapter, etc). This protocol passes a marshalled
dictionary which contains the keys ``format`` and ``environ``.
@@ -709,19 +734,20 @@
object based off the socket, which contains the body of the
request (the POST data, for instance). It's left to Application
to handle that data.
+
"""
protocolName = 'address'
settingPrefix = 'Adapter'
def handleRequest(self):
- """
- Creates the request dictionary, and creates a
- `TASASStreamOut` object for the response, then calls
- `Application.dispatchRawRequest`, which does the
- rest of the work (here we just clean up after).
- """
+ """Handle request.
+
+ Creates the request dictionary, and creates a `TASASStreamOut` object
+ for the response, then calls `Application.dispatchRawRequest`, which
+ does the rest of the work (here we just clean up after).
+ """
verbose = self._server._verbose
self._startTime = time.time()
@@ -755,9 +781,7 @@
del transaction
def makeInput(self):
- """
- Create a file-like object from the socket
- """
+ """Create a file-like object from the socket."""
return self._sock.makefile("rb",8012)
# Determines whether the main look should run in another thread.
@@ -770,31 +794,29 @@
def runMainLoopInThread():
return os.name == 'nt'
-doesRunHandleExceptions = True # Set to False in DebugAppServer so Python debuggers can trap exceptions
+# Set to False in DebugAppServer so Python debuggers can trap exceptions:
+doesRunHandleExceptions = True
class RestartAppServerError(Exception):
- """
- Raised by DebugAppServer when needed.
- """
+ """Raised by DebugAppServer when needed."""
pass
+## Script usage ##
+
def run(workDir=None):
- """
- Starts the server (`ThreadedAppServer`).
+ """Start the server (`ThreadedAppServer`).
- `workDir` is the server-side path for the server, which may
- not be the ``Webware/WebKit`` directory (though by default
- it is).
-
- After setting up the ThreadedAppServer we call
- `ThreadedAppServer.mainloop` to start the server main loop.
- It also catches exceptions as a last resort.
- """
+ `workDir` is the server-side path for the server, which may not be
+ the ``Webware/WebKit`` directory (though by default it is).
+
+ After setting up the ThreadedAppServer we call `ThreadedAppServer.mainloop`
+ to start the server main loop. It also catches exceptions as a last resort.
+ """
global server
runAgain = True
- while runAgain: # looping in support of RestartAppServerError
+ while runAgain: # looping in support of RestartAppServerError
try:
try:
runAgain = False
@@ -806,7 +828,7 @@
# that we can re-call it in the main thread.
global exitStatus
exitStatus = None
- def windowsmainloop(server):
+ def _windowsmainloop(server):
global exitStatus
try:
server.mainloop()
@@ -814,14 +836,15 @@
exitStatus = e.code
# Run the server thread
- t = threading.Thread(target=windowsmainloop, args=(server,))
+ t = threading.Thread(target=_windowsmainloop,
+ args=(server,))
t.start()
try:
while server.running:
- time.sleep(1.0)
+ time.sleep(1)
except KeyboardInterrupt:
pass
- server.running = 0
+ server.running = False
t.join()
# re-call sys.exit if necessary
@@ -860,14 +883,9 @@
sys.exit()
-# 2003-03 ib @@: is this right? arg1, arg2?
-def shutDown(arg1,arg2):
- """
- Shut down the server.
- """
-## print "I AM HERE"
+def shutDown():
+ """Shut down the server."""
global server
-## print "S", server
print
print "Shutdown Called", time.asctime(time.localtime(time.time()))
if server:
@@ -879,32 +897,28 @@
signal.signal(signal.SIGINT, shutDown)
signal.signal(signal.SIGTERM, shutDown)
-
-
-
usage = """
-The AppServer is the main process of WebKit. It handles requests for
-servlets from webservers. ThreadedAppServer takes the following
-command line arguments: stop: Stop the currently running Apperver.
-daemon: run as a daemon If AppServer is called with no arguments, it
-will start the AppServer and record the pid of the process in
-appserverpid.txt
+The AppServer is the main process of WebKit. It handles requests for servlets
+from webservers. ThreadedAppServer takes the following command line arguments:
+stop: Stop the currently running Apperver.
+daemon: run as a daemon If AppServer is called with no arguments, it will start
+the AppServer and record the pid of the process in appserverpid.txt
"""
-
import re
settingRE = re.compile(r'^--([a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z][a-zA-Z0-9]*)=')
from MiscUtils import Configurable
def main(args):
- """
- Run by `Launch`, this is the main entrance and command-line
- interface for AppServer.
+ """Command line interface.
+
+ Run by `Launch`, this is the main entrance and command-line interface
+ for ThreadedAppServer.
+
"""
- http = 0
function = run
- daemon = 0
+ daemon = False
workDir = None
for i in args[:]:
@@ -917,7 +931,7 @@
import AppServer
function=AppServer.stop
elif i == "daemon":
- daemon=1
+ daemon = True
elif i == "start":
pass
elif i[:8] == "workdir=":
@@ -927,11 +941,10 @@
if daemon:
if os.name == "posix":
- pid=os.fork()
+ pid = os.fork()
if pid:
sys.exit()
else:
- print "daemon mode not available on your OS"
- function(workDir=workDir)
-
+ print "Daemon mode not available on your OS."
+ function(workDir=workDir)
Modified: Webware/trunk/WebKit/Transaction.py
==============================================================================
--- Webware/trunk/WebKit/Transaction.py (original)
+++ Webware/trunk/WebKit/Transaction.py Thu Sep 29 13:21:48 2005
@@ -4,7 +4,8 @@
class Transaction(Object):
- """
+ """The Transaction container.
+
A transaction serves as:
* A container for all objects involved in the transaction. The
@@ -19,6 +20,7 @@
The life cycle of a transaction begins and ends with Application's
dispatchRequest().
+
"""
@@ -26,14 +28,15 @@
def __init__(self, application, request=None):
Object.__init__(self)
- self._application = application
- self._request = request
- self._response = None
- self._session = None
- self._servlet = None
+ self._application = application
+ self._request = request
+ self._response = None
+ self._session = None
+ self._servlet = None
self._errorOccurred = 0
- attrNames = 'application request response session servlet errorOccurred'.split()
+ attrNames = ('application', 'request', 'response',
+ 'session', 'servlet', 'errorOccurred')
def __repr__(self):
s = []
@@ -63,7 +66,13 @@
return id and self._application.hasSession(id)
def session(self):
- """ Returns the session for the transaction, creating one if necessary. Therefore, this method never returns None. Use hasSession() if you want to find out if there one already exists. """
+ """Return the session for the transaction.
+
+ A new transaction is created if necessary. Therefore, this method
+ never returns None. Use hasSession() if you want to find out if
+ there one already exists.
+
+ """
if not self._session:
self._session = self._application.createSessionForTransaction(self)
self._session.awake(self) # give the new servlet a chance to set up
@@ -73,30 +82,50 @@
self._session = session
def servlet(self):
- """ Return the current servlet that is processing. Remember that servlets can be nested. """
+ """Return the current servlet that is processing.
+
+ Remember that servlets can be nested.
+
+ """
return self._servlet
def setServlet(self, servlet):
self._servlet = servlet
def duration(self):
- """ Returns the duration, in seconds, of the transaction (basically response end time minus request start time). """
+ """Return the duration, in seconds, of the transaction.
+
+ This is basically the response end time minus the request start time.
+
+ """
return self._response.endTime() - self._request.time()
def errorOccurred(self):
return self._errorOccurred
def setErrorOccurred(self, flag):
- """ Invoked by the application if an exception is raised to the application level. """
+ """Set error occured flag.
+
+ Invoked by the application if an exception is raised to the
+ application level.
+
+ """
self._errorOccurred = flag
- #self._servlet = None
- # @@ 2002-02-05 ce: disabled above statement so that custom exception handlers can examine the servlet
+ # self._servlet = None
+ # @@ 2002-02-05 ce: disabled above statement so that
+ # @@ custom exception handlers can examine the servlet
## Transaction stages ##
def awake(self):
- """ Sends awake() the to session (if there is one) and the servlet. Currently, the request and response do not partake in the awake()-respond()-sleep() cycle. This could definitely be added in the future if any use was demonstrated for it. """
+ """Sends awake() to the session (if there is one) and the servlet.
+
+ Currently, the request and response do not partake in the
+ awake()-respond()-sleep() cycle. This could definitely be added
+ in the future if any use was demonstrated for it.
+
+ """
if self._session:
self._session.awake(self)
self._servlet.awake(self)
@@ -107,7 +136,12 @@
self._servlet.respond(self)
def sleep(self):
- """ Note that sleep() is sent in reverse order as awake() (which is typical for shutdown/cleanup methods). """
+ """Sends sleep() to the session and the servlet.
+
+ Note that sleep() is sent in reverse order as awake()
+ (which is typical for shutdown/cleanup methods).
+
+ """
self._servlet.sleep(self)
if self._session:
self._session.sleep(self)
@@ -117,7 +151,7 @@
## Debugging ##
def dump(self, file=None):
- """ Dumps debugging info to stdout. """
+ """Dumps debugging info to stdout."""
if file is None:
file = sys.stdout
wr = file.write
@@ -130,7 +164,15 @@
## Die ##
def die(self):
- """ This method should be invoked when the entire transaction is finished with. Currently, this is invoked by AppServer. This method removes references to the different objects in the transaction, breaking cyclic reference chains and allowing either older versions of Python to collect garbage, or newer versions to collect it faster. """
+ """End of transaction.
+
+ This method should be invoked when the entire transaction is
+ finished with. Currently, this is invoked by AppServer. This method
+ removes references to the different objects in the transaction,
+ breaking cyclic reference chains and allowing either older versions
+ of Python to collect garbage, or newer versions to collect it faster.
+
+ """
from types import InstanceType
for attrName in self.__dict__.keys():
# @@ 2000-05-21 ce: there's got to be a better way!
@@ -143,7 +185,8 @@
## Exception handling ##
- exceptionReportAttrNames = 'application request response session servlet'.split()
+ exceptionReportAttrNames = ('application',
+ 'request', 'response', 'session', 'servlet')
def writeExceptionReport(self, handler):
handler.writeTitle(self.__class__.__name__)
@@ -155,6 +198,7 @@
try:
obj.writeExceptionReport(handler)
except Exception, e:
- handler.writeln('<p> Uncaught exception while asking <b>%s</b> to write report:\n<pre>' % name)
+ handler.writeln('<p>Uncaught exception while asking'
+ ' <b>%s</b> to write report:</p>\n<pre>' % name)
traceback.print_exc(file=handler)
handler.writeln('</pre>')
Modified: Webware/trunk/WebKit/URLParser.py
==============================================================================
--- Webware/trunk/WebKit/URLParser.py (original)
+++ Webware/trunk/WebKit/URLParser.py Thu Sep 29 13:21:48 2005
@@ -1,15 +1,15 @@
-"""
-URL parsing is done through objects which are subclasses of the
-`URLParser` class. `Application` delegates most of the URL parsing
-to these objects.
-
-Application has a single "root" URL parser, which is used to parse
-all URLs. This parser then can pass the request on to other parsers,
-usually taking off parts of the URL with each step.
-
-This root parser is generally `ContextParser`, which is instantiated
-and set up by `Application` (accessible through
-`Application.rootURLParser`).
+"""URLParser
+
+URL parsing is done through objects which are subclasses of the `URLParser`
+class. `Application` delegates most of the URL parsing to these objects.
+
+Application has a single "root" URL parser, which is used to parse all URLs.
+This parser then can pass the request on to other parsers, usually taking off
+parts of the URL with each step.
+
+This root parser is generally `ContextParser`, which is instantiated and set
+up by `Application` (accessible through `Application.rootURLParser`).
+
"""
@@ -19,7 +19,6 @@
import warnings
from WebKit.HTTPExceptions import *
from WebUtils.Funcs import urlDecode
-import AppServer
# Legal characters for use in a module name -- used when turning
# an entire path into a module name.
@@ -27,82 +26,80 @@
_globalApplication = None
def application():
- """
- Returns the global Application.
- :ignore:
- """
+ """Returns the global Application."""
return _globalApplication
+
class URLParser:
+ """URLParser is the base class for all URL parsers.
+
+ Though its functionality is sparse, it may be expanded in the future.
+ Subclasses should implement a `parse` method, and may also want to
+ implement an `__init__` method with arguments that control how the
+ parser works (for instance, passing a starting path for the parser)
+
+ The `parse` method is where most of the work is done. It takes two
+ arguments -- the transaction and the portion of the URL that is still to
+ be parsed. The transaction may (and usually is) modified along the way.
+ The URL is passed through so that you can take pieces off the front,
+ and then pass the reduced URL to another parser. The method should return
+ a servlet (never None).
+
+ If you cannot find a servlet, or some other (somewhat) expected error
+ occurs, you should raise an exception. HTTPNotFound probably being the
+ most interesting.
- """
- URLParser is the base class for all URL parsers. Though
- its functionality is sparse, it may be expanded in the future.
- Subclasses should implement a `parse` method, and may also
- want to implement an `__init__` method with arguments that
- control how the parser works (for instance, passing a starting
- path for the parser)
-
- The `parse` method is where most of the work is done. It takes
- two arguments -- the transaction and the portion of the URL
- that is still to be parsed. The transaction may (and usually
- is) modified along the way. The URL is passed through so that
- you can take pieces off the front, and then pass the reduced
- URL to another parser. The method should return a servlet
- (never None).
-
- If you cannot find a servlet, or some other (somewhat)
- expected error occurs, you should raise an exception.
- HTTPNotFound probably being the most interesting.
"""
def __init__(self):
pass
def findServletForTransaction(self, trans):
- """
- Returns a servlet for the transaction. This is the
- top-level entry point, below it `parse` is used.
+ """Returns a servlet for the transaction.
+
+ This is the top-level entry point, below it `parse` is used.
+
"""
return self.parse(trans, trans.request().urlPath())
+
class ContextParser(URLParser):
+ """Find the context of a request.
- """
- ContextParser uses the ``Application.config`` context settings
- to find the context of the request. It then passes the
- request to a FileParser rooted in the context path.
-
- The context is the first element of the URL, or if no context
- matches that then it is the ``default`` context (and the
- entire URL is passed to the default context's FileParser).
+ ContextParser uses the ``Application.config`` context settings to find
+ the context of the request. It then passes the request to a FileParser
+ rooted in the context path.
+
+ The context is the first element of the URL, or if no context matches
+ that then it is the ``default`` context (and the entire URL is passed
+ to the default context's FileParser).
+
+ There is generally only one ContextParser, which can be found as
+ ``application.rootURLParser()``.
- There is generally only one ContextParser, which can be
- found as ``application.rootURLParser()``.
"""
+
+ ## Init ##
+
def __init__(self, app):
- """
+ """Create ContextParser.
+
ContextParser is usually created by Application, which
passes all requests to it.
In __init__ we take the ``Contexts`` setting from
Application.config and parse it slightly.
- """
+ """
URLParser.__init__(self)
-
- # need to set this here because we need for initialization,
- # during which AppServer.globalAppServer.application() doesn't
- # yet exist
+ # Need to set this here because we need for initialization, during
+ # which AppServer.globalAppServer.application() doesn't yet exist:
self._app = app
-
# self._context will be a dictionary of context
# names and context directories. It is set by
# `addContext`.
self._contexts = {}
-
-
# add all contexts except the default, which we save until
# the end
contexts = app.setting('Contexts')
@@ -113,7 +110,6 @@
else:
name = '/'.join(filter(lambda x: x, name.split('/')))
self.addContext(name, dir)
-
if not defaultContext:
# Examples is a last-case default context, otherwise
# use a context that isn't built into Webware as
@@ -135,28 +131,30 @@
self.addContext('default', defaultContext)
self._defaultContext = 'default'
+
+ ## Context handling ##
+
def resolveDefaultContext(self, name, dest):
- """
+ """Find default context.
+
Figure out if the default context refers to an existing context,
the same directory as an existing context, or a unique directory.
Returns the name of the context that the default context refers to,
or 'default' if the default context is unique.
+
"""
contexts = self._contexts
contextDirs = {}
app = self._app
-
# make a list of existing context paths
for name, path in contexts.items():
if name != 'default':
contextDirs[self.absContextPath(path)] = name
-
if contexts.has_key(dest):
# The default context refers to another context,
# not a unique context. Return the name of that context.
return dest
-
elif contextDirs.has_key(self.absContextPath(dest)):
# The default context has the same directory
# as another context, so it's still not unique
@@ -166,13 +164,13 @@
return 'default'
def addContext(self, name, dir):
- """
- Add a context to the system. The context will be
- imported as a package, going by `name`, from the
- given directory. The directory doesn't have to
- match the context name.
- """
+ """Add a context to the system.
+ The context will be imported as a package, going by `name`,
+ from the given directory. The directory doesn't have to match
+ the context name.
+
+ """
if name == 'default':
dest = self.resolveDefaultContext(name, dir)
self._defaultContext = dest
@@ -195,16 +193,18 @@
mod = imp.load_module(name, *res)
except ImportError, e:
pass
- except TypeError, e: # TypeError can be raised by imp.load_module() when context path does not exist
- pass
+ except TypeError, e: # TypeError can be raised by imp.load_module()
+ pass # when context path does not exist
if e:
print 'Error loading context: %s: %s: dir=%s' % (name, e, dir)
return
if hasattr(mod, 'contextInitialize'):
- # @@ gat 2003-07-23: switched back to old method of passing application as first parameter
- # @@ to contextInitialize for backward compatibility
- result = mod.contextInitialize(application(), os.path.normpath(os.path.join(os.getcwd(), dir)))
+ # @@ gat 2003-07-23: switched back to old method
+ # @@ of passing application as first parameter
+ # @@to contextInitialize for backward compatibility
+ result = mod.contextInitialize(application(),
+ os.path.normpath(os.path.join(os.getcwd(), dir)))
# @@: funny hack...?
if result != None and result.has_key('ContentLocation'):
dir = result['ContentLocation']
@@ -213,27 +213,32 @@
self._contexts[name] = dir
def absContextPath(self, path):
- """
- Resolves relative paths, which are assumed to be
- relative to the Application's serverSidePath (the
- working directory).
+ """Get absolute context path.
+
+ Resolves relative paths, which are assumed to be relative to the
+ Application's serverSidePath (the working directory).
+
"""
if os.path.isabs(path):
return path
else:
return self._app.serverSidePath(path)
+
+ ## Parsing ##
+
def parse(self, trans, requestPath):
- """
- Get the context name, and dispatch to a FileParser
- rooted in the context's path.
+ """Parse request.
+
+ Get the context name, and dispatch to a FileParser rooted
+ in the context's path.
+
+ The context name and file path are stored in the request (accessible
+ through `Request.serverSidePath` and `Request.contextName`).
- The context name and file path are stored in the
- request (accessible through `Request.serverSidePath`
- and `Request.contextName`).
"""
- # This is a hack... this should probably go in the
- # Transaction class:
+ # This is a hack...
+ # this should probably go in the Transaction class:
trans._fileParserInitSeen = {}
if not requestPath:
raise HTTPMovedPermanently(webkitLocation='/')
@@ -264,51 +269,58 @@
class _FileParser(URLParser):
- """
- FileParser dispatches to servlets in the filesystem, as well
- as providing hooks to override the FileParser.
+ """Parse requests to the filesystem.
+
+ FileParser dispatches to servlets in the filesystem, as well as providing
+ hooks to override the FileParser.
- FileParser objects are threadsafe. A factory function is
- used to cache FileParser instances, so for any one path only
- a single FileParser instance will exist. The `_FileParser`
- class is the real class, and `FileParser` is a factory that
- either returns an existant _FileParser object, or creates a
+ FileParser objects are threadsafe. A factory function is used to cache
+ FileParser instances, so for any one path only a single FileParser instance
+ will exist. The `_FileParser` class is the real class, and `FileParser` is
+ a factory that either returns an existant _FileParser object, or creates a
new one if none exists.
- FileParser uses several settings from ``Application.config``,
- which are persistant over the life of the application. These
- are set up in the function `initApp`, as class variables.
- They cannot be set when the module is loaded, because the
- Application is not yet set up, so `initApp` is called in
- `Application.__init__`.
+ FileParser uses several settings from ``Application.config``, which are
+ persistent over the life of the application. These are set up in the
+ function `initApp`, as class variables. They cannot be set when the module
+ is loaded, because the Application is not yet set up, so `initApp` is
+ called in `Application.__init__`.
+
"""
+
+ ## Init ##
+
def __init__(self, path):
- """
- Each parsed directory has a FileParser instance associated
- with it (``self._path``).
+ """Create a FileParser.
+
+ Each parsed directory has a FileParser instance associated with it
+ (``self._path``).
+
"""
URLParser.__init__(self)
self._path = path
self._initModule = None
+
+ ## Parsing ##
+
def parse(self, trans, requestPath):
- """
- Return the servlet. __init__ files will be used for various
- hooks (see `parseInit` for more)
+ """Return the servlet.
+
+ __init__ files will be used for various hooks
+ (see `parseInit` for more).
If the next part of the URL is a directory, it calls
- ``FileParser(dirPath).parse(trans, restOfPath)`` where
- ``restOfPath`` is `requestPath` with the first section
- of the path removed (the part of the path that this
- FileParser just handled).
-
- This uses `fileNamesForBaseName` to find files in its
- directory. That function has several functions to define
- what files are ignored, hidden, etc. See its documentation
- for more information.
- """
+ ``FileParser(dirPath).parse(trans, restOfPath)`` where ``restOfPath``
+ is `requestPath` with the first section of the path removed (the part
+ of the path that this FileParser just handled).
+
+ This uses `fileNamesForBaseName` to find files in its directory.
+ That function has several functions to define what files are ignored,
+ hidden, etc. See its documentation for more information.
+ """
# First decode the URL, since we are dealing with filenames here:
requestPath = urlDecode(requestPath)
@@ -319,7 +331,7 @@
return result
assert not requestPath or requestPath.startswith('/'), \
- "Not what I expected: %s" % repr(requestPath)
+ "Not what I expected: %s" % repr(requestPath)
if not requestPath or requestPath == '/':
return self.parseIndex(trans, requestPath)
@@ -339,15 +351,14 @@
if len(names) > 1:
warnings.warn("More than one file matches %s in %s: %s"
% (requestPath, self._path, names))
- # @@: add info
- raise HTTPNotFound
+ raise HTTPNotFound # @@: add info
elif not names:
return self.parseIndex(trans, requestPath)
name = names[0]
if os.path.isdir(name):
- # directories are dispatched to FileParsers rooted
- # in that directory
+ # directories are dispatched to FileParsers
+ # rooted in that directory
fpp = FileParser(name)
return fpp.parse(trans, restPart)
@@ -360,7 +371,8 @@
def filenamesForBaseName(self, baseName):
- """
+ """Find all files for a given base name.
+
Given a path, like ``/a/b/c``, searches for files in ``/a/b``
that start with ``c``. The final name may include an extension,
which is less ambiguous; though if you ask for file.html,
@@ -393,8 +405,8 @@
extensions exist, only the .tmpl file will be returned.
ExtensionCascadeOrder:
A list of extensions, ordered by priority.
- """
+ """
if baseName.find('*') != -1:
return []
@@ -411,17 +423,15 @@
else:
filenames.append(os.path.join(dir, filename))
elif filename.startswith(fileStart) \
- and os.path.splitext(filename)[0] == fileStart:
+ and os.path.splitext(filename)[0] == fileStart:
filenames.append(os.path.join(dir, filename))
good = dirnames
- # Here's where all the settings (except cascading) come
- # into play -- we filter the possible files based on settings
- # here:
+ # Here's where all the settings (except cascading) come into play --
+ # we filter the possible files based on settings here:
for filename in filenames:
ext = os.path.splitext(filename)[1]
shortFilename = os.path.basename(filename)
-
if ext in self._toIgnore and filename != baseName:
continue
if self._toServe and ext not in self._toServe:
@@ -447,48 +457,47 @@
actualExtension = os.path.splitext(baseName)[1]
for extension in self._cascadeOrder:
if baseName + extension in good \
- or extension == actualExtension:
+ or extension == actualExtension:
return [baseName + extension]
return good
def parseIndex(self, trans, requestPath):
- """
+ """Return index servlet-
+
Return the servlet for a directory index (i.e., ``Main`` or
- ``index``). When `parse` encounters a directory and there's
- nothing left in the URL, or when there is something left
- and no file matches it, then it will try `parseIndex` to
- see if there's an index file.
-
- That means that if ``/a/b/c`` is requested, and in ``/a``
- there's no file or directory matching ``b``, then it'll
- look for an index file (like ``Main.py``), and that
- servlet will be returned. In fact, if no ``a`` was found,
- and the default context had an index (like ``index.html``)
- then that would be called with ``/a/b/c`` as
- `HTTPRequest.extraURLPath`. If you don't want that
- to occur, you should raise an HTTPNotFound in your
- no-extra-url-path-taking servlets.
-
- The directory names are based off the ``Application.config``
- setting ``DirectoryFile``, which is a list of base names,
- by default ``["Main", "index", "main", "Index"]``, which
- are searched in order. A file with any extension is
- allowed, so the index can be an HTML file, a PSP file,
- a Python servlet, etc.
- """
+ ``index``). When `parse` encounters a directory and there's nothing
+ left in the URL, or when there is something left and no file matches
+ it, then it will try `parseIndex` to see if there's an index file.
+
+ That means that if ``/a/b/c`` is requested, and in ``/a`` there's no
+ file or directory matching ``b``, then it'll look for an index file
+ (like ``Main.py``), and that servlet will be returned. In fact, if
+ no ``a`` was found, and the default context had an index (like
+ ``index.html``) then that would be called with ``/a/b/c`` as
+ `HTTPRequest.extraURLPath`. If you don't want that to occur, you
+ should raise an HTTPNotFound in your no-extra-url-path-taking servlets.
+
+ The directory names are based off the ``Application.config`` setting
+ ``DirectoryFile``, which is a list of base names, by default
+ ``["Main", "index", "main", "Index"]``, which are searched in order.
+ A file with any extension is allowed, so the index can be an HTML file,
+ a PSP file, a Python servlet, etc.
+ """
# If requestPath is empty, then we're missing the trailing slash:
if not requestPath:
- raise HTTPMovedPermanently(webkitLocation=trans.request().urlPath() + "/")
+ raise HTTPMovedPermanently(
+ webkitLocation=trans.request().urlPath() + "/")
if requestPath == '/':
requestPath = ''
for directoryFile in self._directoryFile:
names = self.filenamesForBaseName(
os.path.join(self._path, directoryFile))
if len(names) > 1:
- warnings.warn("More than one file matches the index file %s in %s: %s"
- % (directoryFile, self._path, names))
+ warnings.warn(
+ "More than one file matches the index file %s in %s: %s"
+ % (directoryFile, self._path, names))
raise HTTPNotFound
elif names:
if requestPath and not self._extraPathInfo:
@@ -496,15 +505,10 @@
trans.request()._serverSidePath = names[0]
trans.request()._extraURLPath = requestPath
return ServletFactoryManager.servletForFile(trans, names[0])
- # @@: add correct information here
- raise HTTPNotFound
+ raise HTTPNotFound # @@: add correct information here
def initModule(self):
- """
- Get the __init__ module object for this FileParser's
- directory.
- """
-
+ """Get the __init__ module object for this FileParser's directory."""
try:
result = imp.find_module('__init__', [self._path])
if result is None:
@@ -524,12 +528,12 @@
return None
def parseInit(self, trans, requestPath):
- """
- Parse the __init__ file, returning the resulting servlet,
- or None if no __init__ hooks were found.
+ """Partse the __init_ file.
- Hooks are put in by defining special functions or objects
- in your __init__, with specific names:
+ Returns the resulting servlet, or None if no __init__ hooks were found.
+
+ Hooks are put in by defining special functions or objects in your
+ __init__, with specific names:
`urlTransactionHook`:
A function that takes one argument (the transaction).
@@ -593,8 +597,8 @@
Paths are relative to the current directory. If you
don't want the current directory to be a last resort,
you can include '.' in the joins.
- """
+ """
if self._initModule is None:
self._initModule = self.initModule()
mod = self._initModule
@@ -602,12 +606,12 @@
seen = trans._fileParserInitSeen.setdefault(self._path, {})
if not seen.has_key('urlTransactionHook') \
- and hasattr(mod, 'urlTransactionHook'):
+ and hasattr(mod, 'urlTransactionHook'):
seen['urlTransactionHook'] = 1
mod.urlTransactionHook(trans)
if not seen.has_key('urlRedirect') \
- and hasattr(mod, 'urlRedirect'):
+ and hasattr(mod, 'urlRedirect'):
# @@: do we need this shortcircuit?
seen['urlRedirect'] = 1
try:
@@ -632,25 +636,25 @@
return fpp.parse(trans, redirPath)
if not seen.has_key('SubParser') \
- and hasattr(mod, 'SubParser'):
+ and hasattr(mod, 'SubParser'):
seen['SubParser'] = 1
pp = mod.SubParser(self)
return pp.parse(trans, requestPath)
if not seen.has_key('urlParser') \
- and hasattr(mod, 'urlParser'):
+ and hasattr(mod, 'urlParser'):
seen['urlParser'] = 1
pp = mod.urlParser
return pp.parse(trans, requestPath)
if not seen.has_key('urlParserHook') \
- and hasattr(mod, 'urlParserHook'):
+ and hasattr(mod, 'urlParserHook'):
seen['urlParserHook'] = 1
pp = mod.urlParserHook
return pp.parseHook(trans, requestPath, self)
if not seen.has_key('urlJoins') \
- and hasattr(mod, 'urlJoins'):
+ and hasattr(mod, 'urlJoins'):
seen['urlJoins'] = 1
joinPath = mod.urlJoins
if type(joinPath) is type(""):
@@ -670,13 +674,14 @@
FileParser = ParamFactory(_FileParser)
+
class URLParameterParser(URLParser):
- """
- Strips named parameters out of the URL, e.g. ``/path/SID=123/etc`` --
- the ``SID=123`` will be removed from the URL, and a field
- will be set in the request (so long as no field by that name
- already exists -- if a field does exist the variable is thrown
- away). These are put in the place of GET or POST variables.
+ """Strips named parameters out of the URL.
+
+ E.g. in ``/path/SID=123/etc`` the ``SID=123`` will be removed from the URL,
+ and a field will be set in the request (so long as no field by that name
+ already exists -- if a field does exist the variable is thrown away).
+ These are put in the place of GET or POST variables.
It should be put in an __init__, like::
@@ -686,26 +691,30 @@
Or (slightly less efficient):
from WebKit.URLParser import URLParameterParser as SubParser
+
"""
+
+ ## Init ##
+
def __init__(self, fileParser=None):
self.fileParser = fileParser
+
+ ## Parsing ##
+
def parse(self, trans, requestPath):
- """
- Delegates to `parseHook`.
- """
+ """Delegates to `parseHook`."""
return self.parseHook(trans, requestPath, self.fileParser)
def parseHook(self, trans, requestPath, hook):
- """
- Munges the path. The `hook` is the FileParser object
- that originally called this -- we just want to strip
- stuff out of the URL and then give it back to the
- FileParser instance, which can actually find the
- servlet.
- """
+ """Munges the path.
+ The `hook` is the FileParser object that originally called this --
+ we just want to strip stuff out of the URL and then give it back to
+ the FileParser instance, which can actually find the servlet.
+
+ """
parts = requestPath.split('/')
result = []
req = trans.request()
@@ -718,16 +727,21 @@
result.append(part)
return hook.parse(trans, '/'.join(result))
+
class ServletFactoryManagerClass:
+ """Manage servlet factories.
- """
- This singleton (called `ServletFactoryManager`) collects
- and manages all the servlet factories that are installed.
+ This singleton (called `ServletFactoryManager`) collects and manages
+ all the servlet factories that are installed.
+
+ See `addServletFactory` for adding new factories, and `servletForFile`
+ for getting the factories back.
- See `addServletFactory` for adding new factories, and
- `servletForFile` for getting the factories back.
"""
+
+ ## Init ##
+
def __init__(self):
self.reset()
@@ -735,8 +749,12 @@
self._factories = []
self._factoryExtensions = {}
+
+ ## Manager ##
+
def addServletFactory(self, factory):
- """
+ """Add a new servlet factory.
+
Servlet factories can add themselves with::
ServletFactoryManager.addServletFactory(factory)
@@ -746,21 +764,19 @@
``['.ht']``). The special extension ``.*`` will match any
file if no other factory is found. See `ServletFactory`
for more information.
+
"""
self._factories.append(factory)
for ext in factory.extensions():
assert not self._factoryExtensions.has_key(ext), \
- "Extension %s for factory %s was already used by factory %s" \
- % (repr(ext), factory.__name__,
- self._factoryExtensions[ext].__name__)
+ "Extension %s for factory %s was already used by factory %s" \
+ % (repr(ext), factory.__name__,
+ self._factoryExtensions[ext].__name__)
self._factoryExtensions[ext] = factory
def factoryForFile(self, path):
- """
- Gets a factory for a filename.
- """
-
+ """Get a factory for a filename."""
name, ext = os.path.splitext(path)
if self._factoryExtensions.has_key(ext):
return self._factoryExtensions[ext]
@@ -769,28 +785,31 @@
raise HTTPNotFound
def servletForFile(self, trans, path):
- """
- Gets a servlet for a filename and transaction.
+ """Get a servlet for a filename and transaction.
+
Uses `factoryForFile` to find the factory, which
creates the servlet.
- """
+ """
factory = self.factoryForFile(path)
return factory.servletForTransaction(trans)
ServletFactoryManager = ServletFactoryManagerClass()
+
+## Global Init ##
+
def initApp(app):
- """
- Installs the proper servlet factories, and gets some
- settings from Application.config. Also saves the
- application in _globalApplication for future calls
- to the application() function.
+ """Initializes the application.
- This needs to be called before any of the URLParser-
- derived classes are instantiated.
- """
+ Installs the proper servlet factories, and gets some settings from
+ Application.config. Also saves the application in _globalApplication
+ for future calls to the application() function.
+ This needs to be called before any of the URLParser-derived classes
+ are instantiated.
+
+ """
global _globalApplication
_globalApplication = app
@@ -817,4 +836,3 @@
cls._cascadeOrder = app.setting('ExtensionCascadeOrder')
cls._directoryFile = app.setting('DirectoryFile')
cls._extraPathInfo = app.setting('ExtraPathInfo')
-
Modified: Webware/trunk/WebKit/UnknownFileTypeServlet.py
==============================================================================
--- Webware/trunk/WebKit/UnknownFileTypeServlet.py (original)
+++ Webware/trunk/WebKit/UnknownFileTypeServlet.py Thu Sep 29 13:21:48 2005
@@ -4,9 +4,12 @@
debug = 0
+
class UnknownFileTypeServletFactory(ServletFactory):
- """
- This is the factory for files of an unknown type (e.g., not .py .psp, etc).
+ """The servlet factory for unknown file types.
+
+ I.e. all files other than .py, .psp and the other types we support.
+
"""
def uniqueness(self):
@@ -23,14 +26,18 @@
fileCache = {}
- # A cache of the files served up by UnknownFileTypeServlet cached by absolute, server side path.
- # Each content is another dictionary with keys: content, mimeType, mimeEncoding.
- # Previously, this content was stored directly in the attributes of the UnknownFileTypeServlets, but with that approach subclasses cannot dynamically serve content from different locations.
+ # A cache of the files served up by UnknownFileTypeServlet cached by
+ # absolute, server side path. Each content is another dictionary with keys:
+ # content, mimeType, mimeEncoding.
+ # Previously, this content was stored directly in the attributes of the
+ # UnknownFileTypeServlets, but with that approach subclasses cannot
+ # dynamically serve content from different locations.
from HTTPServlet import HTTPServlet
from MiscUtils.Configurable import Configurable
class UnknownFileTypeServlet(HTTPServlet, Configurable):
- """
+ """Servlet for unknown file types.
+
Normally this class is just a "private" utility class for WebKit's
purposes. However, you may find it useful to subclass on occasion,
such as when the server side file path is determined by something
@@ -49,15 +56,17 @@
filename = trans.request().field('i')
filename = os.path.join(self.imageDir, filename)
return filename
+
"""
## Candidates for subclass overrides ##
def filename(self, trans):
- """
- Returns the filename to be served. A subclass could override
- this in order to serve files from other disk locations based
- on some logic.
+ """Return the filename to be served.
+
+ A subclass could override this in order to serve files from other
+ disk locations based on some logic.
+
"""
filename = getattr(self, '_serverSideFilename', None)
if filename is None:
@@ -66,8 +75,13 @@
return filename
def shouldCacheContent(self):
- """
- Returns a boolean that controls whether or not the content served through this servlet is cached. The default behavior is to return the CacheContent setting. Subclasses may override to always True or False, or incorporate some other logic.
+ """Return whether the content should be cached or not.
+
+ Returns a boolean that controls whether or not the content served
+ through this servlet is cached. The default behavior is to return
+ the CacheContent setting. Subclasses may override to always True
+ or False, or incorporate some other logic.
+
"""
return self.setting('CacheContent')
@@ -84,7 +98,12 @@
self._application = application
def userConfig(self):
- """ Get the user config from the 'UnknownFileTypes' section in the Application's configuration. """
+ """Get the user config.
+
+ This is taken from the 'UnknownFileTypes' section
+ in the Application's configuration.
+
+ """
return self._application.setting('UnknownFileTypes')
def configFilename(self):
@@ -97,7 +116,12 @@
return ['serveContent', 'redirectSansAdapter']
def respondToGet(self, trans):
- """ Responds to the transaction by invoking self.foo() for foo is specified by the 'Technique' setting. """
+ """Respond to GET request.
+
+ Responds to the transaction by invoking self.foo() for foo is
+ specified by the 'Technique' setting.
+
+ """
technique = self.setting('Technique')
assert technique in self.validTechniques(), 'technique = %s' % technique
method = getattr(self, technique)
@@ -106,19 +130,33 @@
respondToHead = respondToGet
def respondToPost(self, trans):
- """
- Invokes self.respondToGet().
- Since posts are usually accompanied by data, this might not be the best policy. However, a POST would most likely be for a CGI, which currently no one is mixing in with their WebKit-based web sites.
+ """Respond to POST request.
+
+ Invoke self.respondToGet().
+
+ Since posts are usually accompanied by data, this might not be
+ the best policy. However, a POST would most likely be for a CGI,
+ which currently no one is mixing in with their WebKit-based web sites.
+
"""
# @@ 2001-01-25 ce: See doc string for why this might be a bad idea.
self.respondToGet(trans)
def redirectSansAdapter(self, trans):
- """ Sends a redirect to a URL that doesn't contain the adapter name. Under the right configuration, this will cause the web server to then be responsible for the URL rather than the app server. This has only been test with "*.[f]cgi" adapters.
- Keep in mind that links off the target page will NOT include the adapter in the URL. """
+ """Redirect to web server.
+
+ Sends a redirect to a URL that doesn't contain the adapter name.
+ Under the right configuration, this will cause the web server to
+ then be responsible for the URL rather than the app server.
+ This has only been test with "*.[f]cgi" adapters.
+ Keep in mind that links off the target page will *not* include
+ the adapter in the URL.
+
+ """
# @@ 2000-05-08 ce: the following is horribly CGI specific and hacky
env = trans.request()._environ
- # @@ 2001-01-25 ce: isn't there a func in WebUtils to get script name? because some servers are different?
+ # @@ 2001-01-25 ce: isn't there a func in WebUtils to get script name?
+ # because some servers are different?
newURL = os.path.split(env['SCRIPT_NAME'])[0] + env['PATH_INFO']
newURL = newURL.replace('//', '/') # hacky
trans.response().sendRedirect(newURL)
@@ -156,11 +194,13 @@
file = fileCache.get(filename, None)
if file is not None and mtime != file['mtime']:
# Cache is out of date; clear it.
- if debug: print '>> changed, clearing cache'
+ if debug:
+ print '>> changed, clearing cache'
del fileCache[filename]
file = None
if file is None:
- if debug: print '>> not found in cache'
+ if debug:
+ print '>> not found in cache'
fileType = mimetypes.guess_type(filename)
mimeType = fileType[0]
mimeEncoding = fileType[1]
@@ -175,8 +215,11 @@
if trans.request().method() == 'HEAD':
f.close()
return
- if file is None and self.setting('ReuseServlets') and self.shouldCacheContent() and fileSize < MaxCacheContentSize:
- if debug: print '>> caching'
+ if file is None and self.setting('ReuseServlets') \
+ and self.shouldCacheContent() \
+ and fileSize < MaxCacheContentSize:
+ if debug:
+ print '>> caching'
file = {
'content': f.read(),
'mimeType': mimeType,
@@ -187,15 +230,17 @@
}
fileCache[filename] = file
if file is not None:
- if debug: print '>> sending content from cache'
+ if debug:
+ print '>> sending content from cache'
response.write(file['content'])
else: # too big or not supposed to cache
- if debug: print '>> sending directly'
+ if debug:
+ print '>> sending directly'
numBytesSent = 0
while numBytesSent<fileSize:
data = f.read(min(fileSize-numBytesSent,ReadBufferSize))
if data == '':
- break # unlikely, but safety first
+ break # unlikely, but safety first
response.write(data)
numBytesSent += len(data)
f.close()
Modified: Webware/trunk/WebKit/WebwarePathLocation.py
==============================================================================
--- Webware/trunk/WebKit/WebwarePathLocation.py (original)
+++ Webware/trunk/WebKit/WebwarePathLocation.py Thu Sep 29 13:21:48 2005
@@ -1 +1,2 @@
-## Used solely to have a module to import from a definite location
+
+## Used solely to have a module to import from a definite location ##
Modified: Webware/trunk/WebKit/XMLRPCServlet.py
==============================================================================
--- Webware/trunk/WebKit/XMLRPCServlet.py (original)
+++ Webware/trunk/WebKit/XMLRPCServlet.py Thu Sep 29 13:21:48 2005
@@ -13,19 +13,26 @@
import sys, string, traceback
from RPCServlet import RPCServlet
+try: # backward compatibility for Python < 2.3
+ True, False
+except NameError:
+ True, False = 1, 0
+
+
class XMLRPCServlet(RPCServlet):
- """
- XMLRPCServlet is a base class for XML-RPC servlets.
+ """XMLRPCServlet is a base class for XML-RPC servlets.
+
See Examples/XMLRPCExample.py for sample usage.
For more Pythonic convenience at the cost of language independence,
see PickleRPCServlet.
+
"""
-
+
# Set to False if you do not want to allow None to be marshalled
# as part of a response.
allow_none = True
-
+
def respondToPost(self, transaction):
"""
This is similar to the xmlrpcserver.py example from the xmlrpc
@@ -50,39 +57,43 @@
if type(response) != type(()):
response = (response,)
except xmlrpclib.Fault, fault:
- response = xmlrpclib.dumps(fault, encoding=encoding, allow_none=self.allow_none)
+ response = xmlrpclib.dumps(fault, encoding=encoding,
+ allow_none=self.allow_none)
self.sendOK('text/xml', response, transaction)
self.handleException(transaction)
except Exception, e:
fault = self.resultForException(e, transaction)
- response = xmlrpclib.dumps(xmlrpclib.Fault(1, fault), encoding=encoding, allow_none=self.allow_none)
+ response = xmlrpclib.dumps(xmlrpclib.Fault(1, fault),
+ encoding=encoding, allow_none=self.allow_none)
self.sendOK('text/xml', response, transaction)
self.handleException(transaction)
except: # if it's a string exception, this gets triggered
fault = self.resultForException(sys.exc_info()[0], transaction)
- response = xmlrpclib.dumps(xmlrpclib.Fault(1, fault), encoding=encoding, allow_none=self.allow_none)
+ response = xmlrpclib.dumps(xmlrpclib.Fault(1, fault),
+ encoding=encoding, allow_none=self.allow_none)
self.sendOK('text/xml', response, transaction)
self.handleException(transaction)
else:
- response = xmlrpclib.dumps(response, methodresponse=1, encoding=encoding, allow_none=self.allow_none)
+ response = xmlrpclib.dumps(response, methodresponse=1,
+ encoding=encoding, allow_none=self.allow_none)
self.sendOK('text/xml', response, transaction)
except:
# internal error, report as HTTP server error
print 'XMLRPCServlet internal error'
- print string.join(traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]))
+ print string.join(traceback.format_exception(sys.exc_info()[0],
+ sys.exc_info()[1],sys.exc_info()[2]))
transaction.response().setStatus(500, 'Server Error')
self.handleException(transaction)
-
-# Helper functions
+# Helper functions:
def _getXmlDeclAttr(xml, attName):
- """ gets attribute value from xml declaration (<?xml ... ?>) """
- s = xml[6 : xml.find("?>")] # 'version = "1.0" encoding = "Cp1251"'
+ """Get attribute value from xml declaration (<?xml ... ?>)."""
+ s = xml[6 : xml.find("?>")] # 'version = "1.0" encoding = "Cp1251"'
p = s.find(attName)
- if p==-1:
+ if p == -1:
return None
- s=s[p+len(attName):] # '= "Cp1251"'
- s=s[s.find('=')+1:].strip() # '"Cp1251"'
- return s[1:s.find(s[0],1)] # 'Cp1251'
+ s = s[p+len(attName):] # '= "Cp1251"'
+ s = s[s.find('=')+1:].strip() # '"Cp1251"'
+ return s[1:s.find(s[0],1)] # 'Cp1251'
|