Code

- add comment to clarify semantics if pytz is installed
[roundup.git] / roundup / cgi / apache.py
1 # mod_python interface for Roundup Issue Tracker
2 #
3 # This module is free software, you may redistribute it
4 # and/or modify under the same terms as Python.
5 #
6 # This module provides Roundup Web User Interface
7 # using mod_python Apache module.  Initially written
8 # with python 2.3.3, mod_python 3.1.3, roundup 0.7.0.
9 #
10 # This module operates with only one tracker
11 # and must be placed in the tracker directory.
12 #
14 import cgi
15 import os
16 import threading
18 from mod_python import apache
20 import roundup.instance
21 from roundup.cgi import TranslationService
23 class Headers(dict):
25     """HTTP headers wrapper"""
27     def __init__(self, headers):
28         """Initialize with `apache.table`"""
29         super(Headers, self).__init__(headers)
30         self.getheader = self.get
32 class Request(object):
34     """`apache.Request` object wrapper providing roundup client interface"""
36     def __init__(self, request):
37         """Initialize with `apache.Request` object"""
38         self._req = request
39         # .headers.getheader()
40         self.headers = Headers(request.headers_in)
41         # .wfile.write()
42         self.wfile = self._req
44     def start_response(self, headers, response):
45         self.send_response(response)
46         for key, value in headers:
47             self.send_header(key, value)
48         self.end_headers()
50     def send_response(self, response_code):
51         """Set HTTP response code"""
52         self._req.status = response_code
54     def send_header(self, name, value):
55         """Set output header"""
56         # value may be an instance of roundup.cgi.exceptions.HTTPException
57         value = str(value)
58         # XXX default content_type is "text/plain",
59         #   and ain't overrided by "Content-Type" header
60         if name == "Content-Type":
61             self._req.content_type = value
62         else:
63             self._req.headers_out.add(name, value)
65     def end_headers(self):
66         """NOOP. There aint no such thing as 'end_headers' in mod_python"""
67         pass
69  
70     def sendfile(self, filename, offset = 0, len = -1):
71         """Send 'filename' to the user."""
73         return self._req.sendfile(filename, offset, len)
75 __tracker_cache = {}
76 """A cache of optimized tracker instances.
77  
78 The keys are strings giving the directories containing the trackers.
79 The values are tracker instances."""
81 __tracker_cache_lock = threading.Lock()
82 """A lock used to guard access to the cache."""
85 def handler(req):
86     """HTTP request handler"""
87     _options = req.get_options()
88     _home = _options.get("TrackerHome")
89     _lang = _options.get("TrackerLanguage")
90     _timing = _options.get("TrackerTiming", "no")
91     if _timing.lower() in ("no", "false"):
92         _timing = ""
93     _debug = _options.get("TrackerDebug", "no")
94     _debug = _debug.lower() not in ("no", "false")
96     # We do not need to take a lock here (the fast path) because reads
97     # from dictionaries are atomic.
98     if not _debug and _home in __tracker_cache:
99         _tracker = __tracker_cache[_home]
100     else:
101         if not (_home and os.path.isdir(_home)):
102             apache.log_error(
103                 "PythonOption TrackerHome missing or invalid for %(uri)s"
104                 % {'uri': req.uri})
105             return apache.HTTP_INTERNAL_SERVER_ERROR
106         if _debug:
107             _tracker = roundup.instance.open(_home, optimize=0)
108         else:
109             __tracker_cache_lock.acquire()
110             try:
111                 # The tracker may have been added while we were acquiring
112                 # the lock.
113                 if _home in __tracker_cache:
114                     _tracker = __tracker_cache[home]
115                 else:
116                     _tracker = roundup.instance.open(_home, optimize=1)
117                     __tracker_cache[_home] = _tracker
118             finally:
119                 __tracker_cache_lock.release()
120     # create environment
121     # Note: cookies are read from HTTP variables, so we need all HTTP vars
122     req.add_common_vars()
123     _env = dict(req.subprocess_env)
124     # XXX classname must be the first item in PATH_INFO.  roundup.cgi does:
125     #       path = string.split(os.environ.get('PATH_INFO', '/'), '/')
126     #       os.environ['PATH_INFO'] = string.join(path[2:], '/')
127     #   we just remove the first character ('/')
128     _env["PATH_INFO"] = req.path_info[1:]
129     if _timing:
130         _env["CGI_SHOW_TIMING"] = _timing
131     _form = cgi.FieldStorage(req, environ=_env)
132     _client = _tracker.Client(_tracker, Request(req), _env, _form,
133         translator=TranslationService.get_translation(_lang,
134             tracker_home=_home))
135     _client.main()
136     return apache.OK
138 # vim: set et sts=4 sw=4 :