Edgewall Software

Changes between Initial Version and Version 1 of TracModPython2.7


Ignore:
Timestamp:
Oct 13, 2005, 2:49:04 PM (19 years ago)
Author:
alastair@…
Comment:

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

Legend:

Unmodified
Added
Removed
Modified
  • TracModPython2.7

    v1 v1  
     1= Trac and mod_python 2.7 =
     2
     3Trac was written to use Apache 2, but if you want to use Apache 1.3, you couldn't use [http://www.modpython.org/ mod_python] because [http://www.modpython.org/ mod_python] 3 only runs on Apache 2. Since this wasn't an option in my case, I patched [http://www.modpython.org/ mod_python] 2.7.11 and also made a couple of small changes to Trac to make it work.
     4
     5There are two patches, one for [http://www.modpython.org/ 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.
     6
     7Here's the [http://www.modpython.org/ mod_python] patch.
     8
     9{{{
     10diff -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
     11--- mod_python-2.7.11.orig/lib/python/mod_python/util.py        2000-12-13 23:45:48.000000000 +0000
     12+++ mod_python-2.7.11/lib/python/mod_python/util.py     2005-09-16 12:58:07.000000000 +0100
     13@@ -47,6 +47,9 @@
     14 import string
     15 import StringIO
     16 
     17+from types import *
     18+from exceptions import *
     19+
     20 parse_qs = apache.parse_qs
     21 parse_qsl = apache.parse_qsl
     22 
     23@@ -95,6 +98,14 @@
     24     def __del__(self):
     25         self.file.close()
     26 
     27+class StringField(str):
     28+    """ This class is basically a string with
     29+    a value attribute for compatibility with std lib cgi.py
     30+    """
     31+
     32+    def __init__(self, str=""):
     33+       str.__init__(self, str)
     34+       self.value = self.__str__()
     35 
     36 class FieldStorage:
     37 
     38@@ -239,10 +250,10 @@
     39         found = []
     40         for item in self.list:
     41             if item.name == key:
     42-                if isinstance(item.file, StringIO.StringIO):
     43-                    found.append(item.value)
     44-                else:
     45-                    found.append(item)
     46+               if isinstance(item.file, FileType):
     47+                   found.append(item)
     48+               else:
     49+                   found.append(StringField(item.value))
     50         if not found:
     51             raise KeyError, key
     52         if len(found) == 1:
     53@@ -250,6 +261,12 @@
     54         else:
     55             return found
     56 
     57+    def get(self, key, default):
     58+       try:
     59+           return self.__getitem__(key)
     60+       except KeyError:
     61+           return default
     62+
     63     def keys(self):
     64         """Dictionary style keys() method."""
     65         if self.list is None:
     66@@ -271,23 +288,121 @@
     67         """Dictionary style len(x) support."""
     68         return len(self.keys())
     69 
     70-           
     71+    def getfirst(self, key, default=None):
     72+        """ return the first value received """
     73+        for item in self.list:
     74+            if item.name == key:
     75+                if isinstance(item.file, FileType):
     76+                    return item
     77+                else:
     78+                    return StringField(item.value)
     79+        return default
     80+                                                                   
     81+    def getlist(self, key):
     82+        """ return a list of received values """
     83+        if self.list is None:
     84+            raise TypeError, "not indexable"
     85+        found = []
     86+        for item in self.list:
     87+            if item.name == key:
     88+                if isinstance(item.file, FileType):
     89+                    found.append(item)
     90+                else:
     91+                    found.append(StringField(item.value))
     92+        return found
     93+
     94 def parse_header(line):
     95     """Parse a Content-type like header.
     96 
     97     Return the main content-type and a dictionary of options.
     98 
     99     """
     100-    plist = map(string.strip, string.splitfields(line, ';'))
     101-    key = string.lower(plist[0])
     102+   
     103+    plist = map(lambda a: a.strip(), line.split(';'))
     104+    key = plist[0].lower()
     105     del plist[0]
     106     pdict = {}
     107     for p in plist:
     108-        i = string.find(p, '=')
     109+        i = p.find('=')
     110         if i >= 0:
     111-            name = string.lower(string.strip(p[:i]))
     112-            value = string.strip(p[i+1:])
     113+            name = p[:i].strip().lower()
     114+            value = p[i+1:].strip()
     115             if len(value) >= 2 and value[0] == value[-1] == '"':
     116                 value = value[1:-1]
     117             pdict[name] = value
     118     return key, pdict
     119+
     120+def apply_fs_data(object, fs, **args):
     121+    """
     122+    Apply FieldStorage data to an object - the object must be
     123+    callable. Examine the args, and match then with fs data,
     124+    then call the object, return the result.
     125+    """
     126+
     127+    # add form data to args
     128+    for field in fs.list:
     129+        if field.filename:
     130+            val = field
     131+        else:
     132+            val = field.value
     133+        args.setdefault(field.name, []).append(val)
     134+
     135+    # replace lists with single values
     136+    for arg in args:
     137+        if ((type(args[arg]) is ListType) and
     138+            (len(args[arg]) == 1)):
     139+            args[arg] = args[arg][0]
     140+
     141+    # we need to weed out unexpected keyword arguments
     142+    # and for that we need to get a list of them. There
     143+    # are a few options for callable objects here:
     144+
     145+    if type(object) is InstanceType:
     146+        # instances are callable when they have __call__()
     147+        object = object.__call__
     148+
     149+    expected = []
     150+    if hasattr(object, "func_code"):
     151+        # function
     152+        fc = object.func_code
     153+        expected = fc.co_varnames[0:fc.co_argcount]
     154+    elif hasattr(object, 'im_func'):
     155+        # method
     156+        fc = object.im_func.func_code
     157+        expected = fc.co_varnames[1:fc.co_argcount]
     158+    elif type(object) is ClassType:
     159+        # class
     160+        fc = object.__init__.im_func.func_code
     161+        expected = fc.co_varnames[1:fc.co_argcount]
     162+
     163+    # remove unexpected args unless co_flags & 0x08,
     164+    # meaning function accepts **kw syntax
     165+    if not (fc.co_flags & 0x08):
     166+        for name in args.keys():
     167+            if name not in expected:
     168+                del args[name]
     169+
     170+    return object(**args)
     171+
     172+def redirect(req, location, permanent=0, text=None):
     173+    """
     174+    A convenience function to provide redirection
     175+    """
     176+
     177+    if req.sent_bodyct:
     178+        raise IOError, "Cannot redirect after headers have already been sent."
     179+
     180+    req.err_headers_out["Location"] = location
     181+    if permanent:
     182+        req.status = apache.HTTP_MOVED_PERMANENTLY
     183+    else:
     184+        req.status = apache.HTTP_MOVED_TEMPORARILY
     185+
     186+    if text is None:
     187+        req.write('<p>The document has moved'
     188+                  ' <a href="%s">here</a></p>\n'
     189+                  % location)
     190+    else:
     191+        req.write(text)
     192+
     193+    raise apache.SERVER_RETURN, apache.OK
     194diff -r -u mod_python-2.7.11.orig/src/include/mod_python.h mod_python-2.7.11/src/include/mod_python.h
     195--- mod_python-2.7.11.orig/src/include/mod_python.h     2003-12-08 04:36:10.000000000 +0000
     196+++ mod_python-2.7.11/src/include/mod_python.h  2005-09-16 01:01:47.000000000 +0100
     197@@ -67,7 +67,7 @@
     198 #include "http_protocol.h"
     199 #include "util_script.h"
     200 #include "http_log.h"
     201-
     202+#include "multithread.h"
     203 
     204 /* Python headers */
     205 /* this gets rid of some comile warnings */
     206diff -r -u mod_python-2.7.11.orig/src/mod_python.c mod_python-2.7.11/src/mod_python.c
     207--- mod_python-2.7.11.orig/src/mod_python.c     2001-05-28 21:00:41.000000000 +0100
     208+++ mod_python-2.7.11/src/mod_python.c  2005-09-16 01:38:05.000000000 +0100
     209@@ -264,6 +264,9 @@
     210        server.register_cleanup() */
     211     pool *child_init_pool = NULL;
     212 
     213+    /* Fudge for OS X */
     214+    static int initialized = 0;
     215+
     216     /* mod_python version */
     217     ap_add_version_component(VERSION_COMPONENT);
     218     
     219@@ -272,8 +275,9 @@
     220     ap_add_version_component(buff);
     221 
     222     /* initialize global Python interpreter if necessary */
     223-    if (! Py_IsInitialized())
     224+    if (!initialized || !Py_IsInitialized())
     225     {
     226+      initialized = 1;
     227 
     228        /* initialze the interpreter */
     229        Py_Initialize();
     230diff -r -u mod_python-2.7.11.orig/src/requestobject.c mod_python-2.7.11/src/requestobject.c
     231--- mod_python-2.7.11.orig/src/requestobject.c  2001-05-23 03:49:43.000000000 +0100
     232+++ mod_python-2.7.11/src/requestobject.c       2005-09-16 02:18:33.000000000 +0100
     233@@ -849,6 +849,7 @@
     234     {"filename",           T_STRING,    OFF(filename),               },
     235     {"path_info",          T_STRING,    OFF(path_info),            RO},
     236     {"args",               T_STRING,    OFF(args),                 RO},
     237+    {"user",               T_STRING,                                 },
     238     /* XXX - test an array header */
     239     /* XXX finfo */
     240     /* XXX parsed_uri */
     241@@ -1015,6 +1016,15 @@
     242     else if (strcmp(name, "hstack") == 0) {
     243        return PyString_FromString(self->hstack);
     244     }
     245+    else if (strcmp(name, "user") == 0) {
     246+        if (!self->request_rec->connection
     247+           || !self->request_rec->connection->user) {
     248+           Py_INCREF(Py_None);
     249+           return Py_None;
     250+        }
     251+
     252+        return PyString_FromString(self->request_rec->connection->user);
     253+    }
     254     else if (strcmp(name, "_content_type_set") == 0) {
     255        return PyInt_FromLong(self->content_type_set);
     256     }
     257@@ -1058,6 +1068,16 @@
     258            ap_pstrdup(self->request_rec->pool, PyString_AsString(value));
     259        return 0;
     260     }
     261+    else if (strcmp(name, "user") == 0) {
     262+        if (!self->request_rec->connection) {
     263+           PyErr_SetString(PyExc_AttributeError, "no connection");
     264+           return -1;
     265+        }
     266+
     267+        self->request_rec->connection->user =
     268+           ap_pstrdup(self->request_rec->pool, PyString_AsString(value));
     269+       return 0;
     270+    }
     271     else if (strcmp(name, "hstack") == 0) {
     272        self->hstack = ap_pstrdup(self->request_rec->pool, PyString_AsString(value));
     273        return 0;
     274}}}
     275
     276You also need the following patch for Trac's ModPythonHandler.py:
     277
     278{{{
     279diff -r -u trac-0.8.4.orig/trac/ModPythonHandler.py trac-0.8.4/trac/ModPythonHandler.py
     280--- trac-0.8.4.orig/trac/ModPythonHandler.py    2005-06-17 19:22:32.000000000 +0100
     281+++ trac-0.8.4/trac/ModPythonHandler.py 2005-09-16 16:54:13.000000000 +0100
     282@@ -99,7 +99,7 @@
     283         self.req.write(data)
     284 
     285     def get_header(self, name):
     286-        return self.req.headers_in.get(name)
     287+        return self.req.headers_in[name]
     288 
     289     def send_response(self, code):
     290         self.req.status = code
     291@@ -108,11 +108,10 @@
     292         if name.lower() == 'content-type':
     293             self.req.content_type = value
     294         else:
     295-            self.req.headers_out.add(name, str(value))
     296+            self.req.headers_out[name] = str(value)
     297 
     298     def end_headers(self):
     299-        pass
     300-
     301+       self.req.send_http_header()
     302 
     303 class TracFieldStorage(util.FieldStorage):
     304     """
     305}}}
     306