Tuesday, February 22, 2005

ISAPI-WSGI - Runs a WSGIUtils application

As part of researching for my WSGI talk I have been working with Colin Stewart's WSGIUtils. It is a package of wsgi helper libraries that should make the development of wsgi enabled apps easier. The two main components are:

wsgiServer is a multi-threaded WSGI web server based on SimpleHTTPServer.

wsgiAdaptor is a simple WSGI application that provides a Basic authentication, signed cookies and persistent sessions.

After getting his examples working under wsgiServer, I decided to see if the wsgiAdaptor examples worked under isapi_wsgi. Apart from needing to make a change to where the session dbm is created (due to permission issues) and an issue with how my copy of IE 6.0 handles cookies :-(, it worked very well. Below is how to do it:


""" Basic Example

Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
All rights reserved.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

If you make any bug fixes or feature enhancements please let me know!

A simple example - a counting web site.
Note that increments in modern browsers go up twice because two
requests are made - one for '/' and one for '/favicon.ico'
"""
import logging, time
import isapi_wsgi

from wsgiutils import SessionClient, wsgiAdaptor, wsgiServer
class TestApp:
def requestHandler (self, request):
session = request.getSession()
count = session.get ('counter', 0)
count += 1
session ['counter'] = count
request.sendContent ("<html><body><h1>Visits: %s</h1><body></html>" % str (count), "text/html")


def __ExtensionFactory__():
return isapi_wsgi.ISAPISimpleHandler(test = testadaptor.wsgiHook)

testclient = SessionClient.LocalSessionClient('T:/session.dbm', 'testappid')
testadaptor = wsgiAdaptor.wsgiAdaptor (TestApp(), 'siteCookieKey', testclient)

if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="isapi-wsgi-wsgiutils",
Description = "ISAPI-WSGI WSGIUtils Test",
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)

Looking at other WSGI implementations

Life has been very busy on the family front. My son and I currently are sharing the house with 4 woman. Not the best environment for software development :-), so haven't progressed very far with the threaded version of isapi_wsgi.

Looks like I will be giving a short presentation on WSGI at the next Sydney Python Meetup, so I have been looking at other WSGI implementations and adaptors. To date my presentation demos have run with a number of them, so the promise of WSGI looks like reality.

Thursday, February 10, 2005

ISAPI-WSGI - Runs a CherryPy2 application

In my ongoing testing of ISAPI-WSGI, with a Subversion checkout of CherryPy2 I have some of the tutorials running as an ISAPI extension. Only issue discovered relates to how CherryPy handles an empty PATH_INFO but I am sure this will be resolved soon. For the time being the url to access the example must have a trailing backslash. This has now been resolved. Thanks to Remco and Remi for helping me with it.

So now that I have proved CherryPy2 can work with ISAPI-WSGI, Subway should be able to run as an ISAPI extension.

I will add the example to ISAPI-WSGI subversion repository tomorrow. Below is how to get tutorial 3 working:


# Demo of tutorial 3 running cherrypy2 under isapi-wsgi
#
# Executing this script (or any server config script) will install the extension
# into your web server and will create a "loader" DLL _cherrypy.dll in the
# current directory. As the server executes, the PyISAPI framework will load
# this module and create the Extension object.
# A Virtual Directory named "isapi-wsgi-cherrypy" is setup. This dir has the ISAPI
# WSGI extension as the only application, mapped to file-extension '*'.
# Therefore, isapi_wsgi extension handles *all* requests in this directory.
#
# To launch this application from a web browser use a url similar to:
#
# http://localhost/isapi-wsgi-cherrypy/test/
#
# NOTE: Due to an issue in how CherryPy2 currently handles PATH_INFO
# the url must have the trailing /

import isapi_wsgi
import cherrypy
from cherrypy import wsgiapp
from cherrypy import cpg

class WelcomePage:

def index(self):
# Ask for the user's name.
return '''
<form action="greetUser" method="get">
What is your name?
<input style="BACKGROUND-COLOR: #ffffa0" name="name">
<input type="submit" value="Submit Query">
</form>
'''

index.exposed = True


def greetUser(self, name = None):
# CherryPy passes all GET and POST variables as method parameters.
# It doesn't make a difference where the variables come from, how
# large their contents are, and so on.
#
# You can define default parameter values as usual. In this
# example, the "name" parameter defaults to None so we can check
# if a name was actually specified.

if name:
# Greet the user!
return "Hey %s, what's up?" % name
else:
# No name was specified
return 'Please enter your name <a href="./">here</a>.'

greetUser.exposed = True


class CherryPyWsgiApp:
cpg.root = WelcomePage()
wsgiapp.init(configDict = {'socketPort': 80, 'sessionStorageType': 'ram'})
def __call__(self, environ, start_response):
return wsgiapp.wsgiApp(environ, start_response)

# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return isapi_wsgi.ISAPISimpleHandler(test = CherryPyWsgiApp())

if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="isapi-wsgi-cherrypy",
Description = "ISAPI-WSGI Cherrypy Test",
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)

Monday, February 07, 2005

ISAPI-WSGI - Runs a Quixote application using QWIP

Titus Brown asked me to test if his simple Quixote-WSGI adapter (QWIP) would work with ISAPI-WSGI. The first launch worked, but every other launch failed until IIS was restarted. Traced it to the write method in ISAPISimpleHandler where I was using "print >> self.ecb, data" and this was raising a 10053 error 'An established connection was aborted by the software in your host machine.' Changing it to "self.ecb.WriteData(data)" fixed the problem. Why? will investigate later. Change checked-in as revision 6.

So running quixote.demo with QWIP using ISAPI=WSGI now works. I think this proves that WSGI is a very important standard for Python and the web.

Below is the code to do it:

import isapi_wsgi

import qwip

# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return isapi_wsgi.ISAPISimpleHandler(demo = qwip.QWIP('quixote.demo'))

if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="isapi-wsgi-qwip-test",
Description = "ISAPI-WSGI QWIP Test",
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)

Saturday, February 05, 2005

Announce:ISAPI-WSGI 0.4 Beta

Created a distribution and sent the following to the Python Web-Sig today:

Hello everyone,

I am happy to announce the release of ISAPI-WSGI 0.4 beta.

ISAPI-WSGI will (hopefully) allow any WSGI application to run inside a windows webserver that supports ISAPI. I believe that it meets the requirements of the WSGI PEP. It has been only tested against the examples from the PEP, Ian Bickings' echo example from wsgi-webkit, and Titus Browns' WSGI enabled Simple Commenting System under IIS 5.1. It has one major limitation being that it is only single threaded. I am currently working on a fully threaded version, but wanted to release it now so others could have a look at it. I am interested in any feedback, suggestions or bug reports.

See http://isapi-wsgi.python-hosting.com/wiki/DocsPage for info and get the python source & some examples at http://isapi-wsgi.python-hosting.com/wiki/ISAPISimpleHandler-0.4-beta

Regards

Mark Rees

Friday, February 04, 2005

ISAPI-WSGI runs a real WSGI application

In a previous post I wrote about wanting to test isapi_wsgi using a real WSGI enabled app rather than my simple test apps. The good news is I was able to get Titus Brown's WSGI middleware: a simple commenting system running.

Now working on getting CherryPy2 running under it. But now happy to try and release beta of isapi_wsgi.py this weekend. Also the source code has been published under Subversion at http://isapi-wsgi.python-hosting.com/browser/

Thursday, February 03, 2005

ISAPI-WSGI hosting url has changed.

As Ian Bicking pointed out to me, underscores in a url is not allowed (even if some OS's supported it). This was causing problems for some people accessing the site, so the guys have changed the url for me. Thanks Richard & Remi. The url is now http://isapi-wsgi.python-hosting.com.