Edgewall Software

Version 1 (modified by alastair@…, 14 years ago) ( diff )

Added patch for mod_python 2.7 so that you can use it with Trac

Trac and mod_python 2.7

Trac was written to use Apache 2, but if you want to use Apache 1.3, you couldn't use mod_python because mod_python 3 only runs on Apache 2. Since this wasn't an option in my case, I patched mod_python 2.7.11 and also made a couple of small changes to Trac to make it work.

There are two patches, one for mod_python 2.7.11, and one for Trac 0.8.4. They have only been tested on Mac OS X, with these specific versions, so your mileage may vary.

Here's the mod_python patch.

diff -r -u mod_python-2.7.11.orig/lib/python/mod_python/util.py mod_python-2.7.11/lib/python/mod_python/util.py
--- mod_python-2.7.11.orig/lib/python/mod_python/util.py	2000-12-13 23:45:48.000000000 +0000
+++ mod_python-2.7.11/lib/python/mod_python/util.py	2005-09-16 12:58:07.000000000 +0100
@@ -47,6 +47,9 @@
 import string
 import StringIO
 
+from types import *
+from exceptions import *
+
 parse_qs = apache.parse_qs
 parse_qsl = apache.parse_qsl
 
@@ -95,6 +98,14 @@
     def __del__(self):
         self.file.close()
 
+class StringField(str):
+    """ This class is basically a string with
+    a value attribute for compatibility with std lib cgi.py
+    """
+
+    def __init__(self, str=""):
+	str.__init__(self, str)
+	self.value = self.__str__()
 
 class FieldStorage:
 
@@ -239,10 +250,10 @@
         found = []
         for item in self.list:
             if item.name == key:
-                if isinstance(item.file, StringIO.StringIO):
-                    found.append(item.value)
-                else:
-                    found.append(item)
+		if isinstance(item.file, FileType):
+		    found.append(item)
+		else:
+		    found.append(StringField(item.value))
         if not found:
             raise KeyError, key
         if len(found) == 1:
@@ -250,6 +261,12 @@
         else:
             return found
 
+    def get(self, key, default):
+	try:
+	    return self.__getitem__(key)
+	except KeyError:
+	    return default
+
     def keys(self):
         """Dictionary style keys() method."""
         if self.list is None:
@@ -271,23 +288,121 @@
         """Dictionary style len(x) support."""
         return len(self.keys())
 
-            
+    def getfirst(self, key, default=None):
+        """ return the first value received """
+        for item in self.list:
+            if item.name == key:
+                if isinstance(item.file, FileType):
+                    return item
+                else:
+                    return StringField(item.value)
+        return default
+                                                                    
+    def getlist(self, key):
+        """ return a list of received values """
+        if self.list is None:
+            raise TypeError, "not indexable"
+        found = []
+        for item in self.list:
+            if item.name == key:
+                if isinstance(item.file, FileType):
+                    found.append(item)
+                else:
+                    found.append(StringField(item.value))
+        return found
+
 def parse_header(line):
     """Parse a Content-type like header.
 
     Return the main content-type and a dictionary of options.
 
     """
-    plist = map(string.strip, string.splitfields(line, ';'))
-    key = string.lower(plist[0])
+    
+    plist = map(lambda a: a.strip(), line.split(';'))
+    key = plist[0].lower()
     del plist[0]
     pdict = {}
     for p in plist:
-        i = string.find(p, '=')
+        i = p.find('=')
         if i >= 0:
-            name = string.lower(string.strip(p[:i]))
-            value = string.strip(p[i+1:])
+            name = p[:i].strip().lower()
+            value = p[i+1:].strip()
             if len(value) >= 2 and value[0] == value[-1] == '"':
                 value = value[1:-1]
             pdict[name] = value
     return key, pdict
+
+def apply_fs_data(object, fs, **args):
+    """
+    Apply FieldStorage data to an object - the object must be
+    callable. Examine the args, and match then with fs data,
+    then call the object, return the result.
+    """
+
+    # add form data to args
+    for field in fs.list:
+        if field.filename:
+            val = field
+        else:
+            val = field.value
+        args.setdefault(field.name, []).append(val)
+
+    # replace lists with single values
+    for arg in args:
+        if ((type(args[arg]) is ListType) and
+            (len(args[arg]) == 1)):
+            args[arg] = args[arg][0]
+
+    # we need to weed out unexpected keyword arguments
+    # and for that we need to get a list of them. There
+    # are a few options for callable objects here:
+
+    if type(object) is InstanceType:
+        # instances are callable when they have __call__()
+        object = object.__call__
+
+    expected = []
+    if hasattr(object, "func_code"):
+        # function
+        fc = object.func_code
+        expected = fc.co_varnames[0:fc.co_argcount]
+    elif hasattr(object, 'im_func'):
+        # method
+        fc = object.im_func.func_code
+        expected = fc.co_varnames[1:fc.co_argcount]
+    elif type(object) is ClassType:
+        # class
+        fc = object.__init__.im_func.func_code
+        expected = fc.co_varnames[1:fc.co_argcount]
+
+    # remove unexpected args unless co_flags & 0x08,
+    # meaning function accepts **kw syntax
+    if not (fc.co_flags & 0x08):
+        for name in args.keys():
+            if name not in expected:
+                del args[name]
+
+    return object(**args)
+
+def redirect(req, location, permanent=0, text=None):
+    """
+    A convenience function to provide redirection
+    """
+
+    if req.sent_bodyct:
+        raise IOError, "Cannot redirect after headers have already been sent."
+
+    req.err_headers_out["Location"] = location
+    if permanent:
+        req.status = apache.HTTP_MOVED_PERMANENTLY
+    else:
+        req.status = apache.HTTP_MOVED_TEMPORARILY
+
+    if text is None:
+        req.write('<p>The document has moved' 
+                  ' <a href="%s">here</a></p>\n'
+                  % location)
+    else:
+        req.write(text)
+
+    raise apache.SERVER_RETURN, apache.OK
diff -r -u mod_python-2.7.11.orig/src/include/mod_python.h mod_python-2.7.11/src/include/mod_python.h
--- mod_python-2.7.11.orig/src/include/mod_python.h	2003-12-08 04:36:10.000000000 +0000
+++ mod_python-2.7.11/src/include/mod_python.h	2005-09-16 01:01:47.000000000 +0100
@@ -67,7 +67,7 @@
 #include "http_protocol.h"
 #include "util_script.h"
 #include "http_log.h"
-
+#include "multithread.h"
 
 /* Python headers */
 /* this gets rid of some comile warnings */
diff -r -u mod_python-2.7.11.orig/src/mod_python.c mod_python-2.7.11/src/mod_python.c
--- mod_python-2.7.11.orig/src/mod_python.c	2001-05-28 21:00:41.000000000 +0100
+++ mod_python-2.7.11/src/mod_python.c	2005-09-16 01:38:05.000000000 +0100
@@ -264,6 +264,9 @@
        server.register_cleanup() */
     pool *child_init_pool = NULL;
 
+    /* Fudge for OS X */
+    static int initialized = 0;
+
     /* mod_python version */
     ap_add_version_component(VERSION_COMPONENT);
     
@@ -272,8 +275,9 @@
     ap_add_version_component(buff);
 
     /* initialize global Python interpreter if necessary */
-    if (! Py_IsInitialized()) 
+    if (!initialized || !Py_IsInitialized()) 
     {
+      initialized = 1;
 
 	/* initialze the interpreter */
 	Py_Initialize();
diff -r -u mod_python-2.7.11.orig/src/requestobject.c mod_python-2.7.11/src/requestobject.c
--- mod_python-2.7.11.orig/src/requestobject.c	2001-05-23 03:49:43.000000000 +0100
+++ mod_python-2.7.11/src/requestobject.c	2005-09-16 02:18:33.000000000 +0100
@@ -849,6 +849,7 @@
     {"filename",           T_STRING,    OFF(filename),               },
     {"path_info",          T_STRING,    OFF(path_info),            RO},
     {"args",               T_STRING,    OFF(args),                 RO},
+    {"user",               T_STRING,                                 },
     /* XXX - test an array header */
     /* XXX finfo */
     /* XXX parsed_uri */
@@ -1015,6 +1016,15 @@
     else if (strcmp(name, "hstack") == 0) {
 	return PyString_FromString(self->hstack);
     }
+    else if (strcmp(name, "user") == 0) {
+        if (!self->request_rec->connection
+	    || !self->request_rec->connection->user) {
+	    Py_INCREF(Py_None);
+	    return Py_None;
+        }
+
+        return PyString_FromString(self->request_rec->connection->user);
+    }
     else if (strcmp(name, "_content_type_set") == 0) {
 	return PyInt_FromLong(self->content_type_set);
     }
@@ -1058,6 +1068,16 @@
 	    ap_pstrdup(self->request_rec->pool, PyString_AsString(value));
 	return 0;
     }
+    else if (strcmp(name, "user") == 0) {
+        if (!self->request_rec->connection) {
+	    PyErr_SetString(PyExc_AttributeError, "no connection");
+	    return -1;
+        }
+
+        self->request_rec->connection->user =
+	    ap_pstrdup(self->request_rec->pool, PyString_AsString(value));
+	return 0;
+    }
     else if (strcmp(name, "hstack") == 0) {
 	self->hstack = ap_pstrdup(self->request_rec->pool, PyString_AsString(value));
 	return 0;

You also need the following patch for Trac's ModPythonHandler.py:

diff -r -u trac-0.8.4.orig/trac/ModPythonHandler.py trac-0.8.4/trac/ModPythonHandler.py
--- trac-0.8.4.orig/trac/ModPythonHandler.py    2005-06-17 19:22:32.000000000 +0100
+++ trac-0.8.4/trac/ModPythonHandler.py 2005-09-16 16:54:13.000000000 +0100
@@ -99,7 +99,7 @@
         self.req.write(data)
 
     def get_header(self, name):
-        return self.req.headers_in.get(name)
+        return self.req.headers_in[name]
 
     def send_response(self, code):
         self.req.status = code
@@ -108,11 +108,10 @@
         if name.lower() == 'content-type':
             self.req.content_type = value
         else:
-            self.req.headers_out.add(name, str(value))
+            self.req.headers_out[name] = str(value)
 
     def end_headers(self):
-        pass
-
+       self.req.send_http_header()
 
 class TracFieldStorage(util.FieldStorage):
     """
Note: See TracWiki for help on using the wiki.