1 "Things to do when TurboGears is imported."
2
3 import os
4 import errno
5 import logging
6 import sys
7 import time
8 import atexit
9 import signal
10
11 import pkg_resources
12 import cherrypy
13 from cherrypy import _cputil, request, server
14 from formencode.variabledecode import NestedVariables
15 from cherrypy._cpwsgi import wsgiApp, CPHTTPRequest
16 from cherrypy._cpwsgiserver import CherryPyWSGIServer
17
18 from turbogears import config, scheduler, database
19 from turbogears import view
20 from turbogears.database import hub_registry, EndTransactionsFilter
21
22 log = logging.getLogger("turbogears.startup")
23
24 pkg_resources.require("TurboGears")
25
26
28 """Monkeypatch for the reloader provided by CherryPy.
29
30 This reloader is designed to reload a single package. This is
31 more efficient and, more important, compatible with zipped
32 libraries that may not provide access to the individual files."""
33
34 def archive_selector(module):
35 if hasattr(module, '__loader__'):
36 if hasattr(module.__loader__, 'archive'):
37 return module.__loader__.archive
38 return module
39
40 mtimes = {}
41 package = config.get("autoreload.package", None)
42 if package is None:
43 print \
44 """TurboGears requires autoreload.package to be set. It can be an empty
45 value, which will use CherryPy's default behavior which is to check
46 every module. Setting an actual package makes the check much faster."""
47 return
48 while cherrypy.lib.autoreload.RUN_RELOADER:
49 if package:
50 modnames = filter(lambda modname: modname.startswith(package),
51 sys.modules.keys())
52 modlist = [sys.modules[modname] for modname in modnames]
53 else:
54 modlist = map(archive_selector, sys.modules.values())
55 for filename in filter(lambda v: v,
56 map(lambda m: getattr(m, "__file__", None), modlist)):
57 if filename.endswith(".kid") or filename == "<string>":
58 continue
59 orig_filename = filename
60 if filename.endswith(".pyc"):
61 filename = filename[:-1]
62 try:
63 mtime = os.stat(filename).st_mtime
64 except OSError, e:
65 if orig_filename.endswith('.pyc') and e[0] == errno.ENOENT:
66
67
68
69 try: os.unlink(orig_filename)
70 except: pass
71 sys.exit(3)
72 if filename not in mtimes:
73 mtimes[filename] = mtime
74 continue
75 if mtime > mtimes[filename]:
76 sys.exit(3)
77 time.sleep(freq)
78
79 cherrypy.lib.autoreload.reloader_thread = reloader_thread
80
81 webpath = ''
82
83 DNS_SD_PID = None
84
85
87 global DNS_SD_PID
88 if DNS_SD_PID:
89 return
90 if not getattr(cherrypy, 'root', None):
91 return
92 if not package:
93 package = cherrypy.root.__module__
94 package = package[:package.find(".")]
95
96 host = config.get('server.socket_host', '')
97 port = str(config.get('server.socket_port'))
98 env = config.get('server.environment')
99 name = package + ": " + env
100 type = "_http._tcp"
101
102 cmds = [['/usr/bin/avahi-publish-service', ["-H", host, name, type, port]],
103 ['/usr/bin/dns-sd', ['-R', name, type, "."+host, port, "path=/"]]]
104
105 for cmd, args in cmds:
106
107
108
109
110 if os.path.exists(cmd):
111 DNS_SD_PID = os.spawnv(os.P_NOWAIT, cmd, [cmd]+args)
112 atexit.register(stop_bonjour)
113 break
114
115
117 if not DNS_SD_PID:
118 return
119 try:
120 os.kill(DNS_SD_PID, signal.SIGTERM)
121 except OSError:
122 pass
123
124
126 """Filter that makes CherryPy ignorant of a URL root path.
127
128 That is, you can mount your app so the URI "/users/~rdel/myapp/"
129 maps to the root object "/".
130
131 """
132
138
140 """Determine the relevant path info by stripping of prefixes.
141
142 Strips webpath and SCRIPT_NAME from request.object_path and
143 sets request.path_info (since CherryPy 2 does not set it).
144
145 """
146 webpath = self.webpath
147 try:
148 webpath += request.wsgi_environ['SCRIPT_NAME'].rstrip('/')
149 except (AttributeError, KeyError):
150 pass
151 if webpath:
152 if request.object_path.startswith(webpath):
153 request.object_path = request.object_path[len(webpath):] or '/'
154 if request.path.startswith(webpath):
155 request.path_info = request.path[len(webpath):] or '/'
156 else:
157 request.path_info = request.path
158
159 try:
160 if not request.wsgi_environ['HTTP_X_FORWARDED_SERVER']:
161 raise KeyError
162 except (AttributeError, KeyError):
163 raise cherrypy.NotFound(request.path)
164 else:
165 request.path_info = request.path
166
167
173
174
176 """Handles TurboGears tasks when the CherryPy server starts.
177
178 This adds the "tg_js" configuration to make MochiKit accessible.
179 It also turns on stdlib logging when in development mode.
180
181 """
182 config.update({"/tg_static":
183 {
184 "static_filter.on": True,
185 "static_filter.dir":
186 os.path.abspath(pkg_resources.resource_filename(__name__, "static")),
187 'log_debug_info_filter.on' : False,
188 }
189 })
190 config.update({"/tg_js" :
191 {
192 "static_filter.on" : True,
193 "static_filter.dir" :
194 os.path.abspath(pkg_resources.resource_filename(__name__, "static/js")),
195 'log_debug_info_filter.on' : False,
196 }
197 })
198 cherrypy.config.environments['development']['log_debug_info_filter.on'] = False
199
200 if config.get("decoding_filter.on", path="/") is None:
201 config.update({"/": {
202 "decoding_filter.on" : True,
203 "decoding_filter.encoding" : config.get(
204 "kid.encoding", "utf8")
205 }})
206
207 view.load_engines()
208 view.loadBaseTemplates()
209 global webpath
210 webpath = config.get('server.webpath') or ''
211
212 if getattr(cherrypy, 'root', None):
213 if not hasattr(cherrypy.root, '_cp_filters'):
214 cherrypy.root._cp_filters = []
215 cherrypy.root._cp_filters.extend([VirtualPathFilter(webpath),
216 EndTransactionsFilter(), NestedVariablesFilter()])
217
218 webpath = webpath.lstrip('/')
219 if webpath and not webpath.endswith('/'):
220 webpath += '/'
221
222 isdev = config.get('server.environment') == 'development'
223 if not config.get("tg.new_style_logging"):
224 if config.get('server.log_to_screen'):
225 setuplog = logging.getLogger()
226 setuplog.setLevel(logging.DEBUG)
227 fmt = logging.Formatter("%(asctime)s %(name)s "
228 "%(levelname)s %(message)s")
229 handler = logging.StreamHandler(sys.stdout)
230 handler.setLevel(logging.DEBUG)
231 handler.setFormatter(fmt)
232 setuplog.addHandler(handler)
233
234 logfile = config.get("server.log_file")
235 if logfile:
236 setuplog = logging.getLogger("turbogears.access")
237 setuplog.propagate = 0
238 fmt = logging.Formatter("%(message)s")
239 handler = logging.FileHandler(logfile)
240 handler.setLevel(logging.INFO)
241 handler.setFormatter(fmt)
242 setuplog.addHandler(handler)
243
244 bonjoursetting = config.get("tg.bonjour", None)
245 if bonjoursetting or isdev:
246 start_bonjour(bonjoursetting)
247
248 if config.get("sqlalchemy.dburi"):
249 database.get_engine()
250
251
252 extensions = pkg_resources.iter_entry_points("turbogears.extensions")
253 for entrypoint in extensions:
254 ext = entrypoint.load()
255 if hasattr(ext, "start_extension"):
256 ext.start_extension()
257
258 for item in call_on_startup:
259 item()
260
261 if config.get("tg.scheduler", False):
262 scheduler._start_scheduler()
263 log.info("Scheduler started")
264
265
288
289
290 old_object_trail = _cputil.get_object_trail
291
292
294 trail = old_object_trail(object_path)
295 try:
296 request.object_trail = trail
297 except AttributeError:
298 pass
299 return trail
300
301 _cputil.get_object_trail = get_object_trail
302
303
305 """A WSGI server that accepts a WSGI application as a parameter."""
306 RequestHandlerClass = CPHTTPRequest
307
309 conf = cherrypy.config.get
310 wsgi_app = wsgiApp
311 if conf('server.environment') == 'development':
312 try:
313 from paste.evalexception.middleware import EvalException
314 except ImportError:
315 pass
316 else:
317 wsgi_app = EvalException(wsgi_app, global_conf={})
318 cherrypy.config.update({'server.throw_errors':True})
319 bind_addr = (conf('server.socket_host'), conf('server.socket_port'))
320 CherryPyWSGIServer.