Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/tags/cherrypy-3.0.0/cherrypy/_cpengine.py

Revision 1541 (checked in by fumanchu, 2 years ago)

checker:

  1. Moved cherrypy.checker to cherrypy.engine.checker so it can be set to None or another Checker instance in config.
  2. Replaced Checker.checkall with Checker.__call__.
  3. Fixed an output bug in check_compatibility.
  • Property svn:eol-style set to native
Line 
1 """Create and manage the CherryPy application engine."""
2
3 import cgi
4 import os
5 import re
6 import signal
7 import sys
8 import threading
9 import time
10
11 import cherrypy
12 from cherrypy import _cprequest, _cpchecker
13
14 # Use a flag to indicate the state of the application engine.
15 STOPPED = 0
16 STARTING = None
17 STARTED = 1
18
19 try:
20     if hasattr(signal, "SIGHUP"):
21         def SIGHUP(signum=None, frame=None):
22             cherrypy.engine.reexec()
23         signal.signal(signal.SIGHUP, SIGHUP)
24
25     if hasattr(signal, "SIGTERM"):
26         def SIGTERM(signum=None, frame=None):
27             cherrypy.server.stop()
28             cherrypy.engine.stop()
29         signal.signal(signal.SIGTERM, SIGTERM)
30 except ValueError, _signal_exc:
31     if _signal_exc.args[0] != "signal only works in main thread":
32         raise
33
34
35 class PerpetualTimer(threading._Timer):
36    
37     def run(self):
38         while True:
39             self.finished.wait(self.interval)
40             if self.finished.isSet():
41                 return
42             self.function(*self.args, **self.kwargs)
43
44
45 class Engine(object):
46     """Application interface for (HTTP) servers, plus process controls."""
47    
48     # Configurable attributes
49     request_class = _cprequest.Request
50     response_class = _cprequest.Response
51     deadlock_poll_freq = 60
52     autoreload_on = True
53     autoreload_frequency = 1
54     autoreload_match = ".*"
55     checker = _cpchecker.Checker()
56    
57     def __init__(self):
58         self.state = STOPPED
59        
60         # Startup/shutdown hooks
61         self.on_start_engine_list = []
62         self.on_stop_engine_list = []
63         self.on_start_thread_list = []
64         self.on_stop_thread_list = []
65         self.seen_threads = {}
66        
67         self.servings = []
68        
69         self.mtimes = {}
70         self.reload_files = []
71        
72         self.monitor_thread = None
73    
74     def start(self, blocking=True):
75         """Start the application engine."""
76         self.state = STARTING
77        
78         if self.checker:
79             self.checker()
80        
81         for func in self.on_start_engine_list:
82             func()
83        
84         self.state = STARTED
85        
86         freq = self.deadlock_poll_freq
87         if freq > 0:
88             self.monitor_thread = PerpetualTimer(freq, self.monitor)
89             self.monitor_thread.setName("CPEngine Monitor")
90             self.monitor_thread.start()
91        
92         if blocking:
93             self.block()
94    
95     def block(self):
96         """Block forever (wait for stop(), KeyboardInterrupt or SystemExit)."""
97         try:
98             while self.state != STOPPED:
99                 # Note that autoreload_frequency controls
100                 # sleep timer even if autoreload is off.
101                 time.sleep(self.autoreload_frequency)
102                 if self.autoreload_on:
103                     self.autoreload()
104         except KeyboardInterrupt:
105             cherrypy.log("<Ctrl-C> hit: shutting down app engine", "ENGINE")
106             cherrypy.server.stop()
107             self.stop()
108         except SystemExit:
109             cherrypy.log("SystemExit raised: shutting down app engine", "ENGINE")
110             cherrypy.server.stop()
111             self.stop()
112             raise
113         except:
114             # Don't bother logging, since we're going to re-raise.
115             # Note that we don't stop the HTTP server here.
116             self.stop()
117             raise
118    
119     def reexec(self):
120         """Re-execute the current process."""
121         cherrypy.server.stop()
122         self.stop()
123        
124         args = sys.argv[:]
125         cherrypy.log("Re-spawning %s" % " ".join(args), "ENGINE")
126         args.insert(0, sys.executable)
127        
128         if sys.platform == "win32":
129             args = ['"%s"' % arg for arg in args]
130        
131         # Some platforms (OS X) will error if all threads are not
132         # ABSOLUTELY terminated. See http://www.cherrypy.org/ticket/581.
133         for trial in xrange(self.reexec_retry * 10):
134             try:
135                 os.execv(sys.executable, args)
136                 return
137             except OSError, x:
138                 if x.errno != 45:
139                     raise
140                 time.sleep(0.1)
141         else:
142             raise
143    
144     # Number of seconds to retry reexec if os.execv fails.
145     reexec_retry = 2
146    
147     def autoreload(self):
148         """Reload the process if registered files have been modified."""
149         sysfiles = []
150         for k, m in sys.modules.items():
151             if re.match(self.autoreload_match, k):
152                 if hasattr(m, "__loader__"):
153                     if hasattr(m.__loader__, "archive"):
154                         k = m.__loader__.archive
155                 k = getattr(m, "__file__", None)
156                 sysfiles.append(k)
157        
158         for filename in sysfiles + self.reload_files:
159             if filename:
160                 if filename.endswith(".pyc"):
161                     filename = filename[:-1]
162                
163                 oldtime = self.mtimes.get(filename, 0)
164                 if oldtime is None:
165                     # Module with no .py file. Skip it.
166                     continue
167                
168                 try:
169                     mtime = os.stat(filename).st_mtime
170                 except OSError:
171                     # Either a module with no .py file, or it's been deleted.
172                     mtime = None
173                
174                 if filename not in self.mtimes:
175                     # If a module has no .py file, this will be None.
176                     self.mtimes[filename] = mtime
177                 else:
178                     if mtime is None or mtime > oldtime:
179                         # The file has been deleted or modified.
180                         self.reexec()
181    
182     def stop(self):
183         """Stop the application engine."""
184         if self.state != STOPPED:
185             for thread_ident, i in self.seen_threads.iteritems():
186                 for func in self.on_stop_thread_list:
187                     func(i)
188             self.seen_threads.clear()
189            
190             for func in self.on_stop_engine_list:
191                 func()
192            
193             if self.monitor_thread:
194                 self.monitor_thread.cancel()
195                 self.monitor_thread.join()
196                 self.monitor_thread = None
197            
198             self.state = STOPPED
199             cherrypy.log("CherryPy shut down", "ENGINE")
200    
201     def restart(self):
202         """Restart the application engine (does not block)."""
203         self.stop()
204         self.start(blocking=False)
205    
206     def wait(self):
207         """Block the caller until ready to receive requests (or error)."""
208         while not self.ready:
209             time.sleep(.1)
210    
211     def _is_ready(self):
212         return bool(self.state == STARTED)
213     ready = property(_is_ready, doc="Return True if the engine is ready to"
214                                     " receive requests, False otherwise.")
215    
216     def request(self, local_host, remote_host, scheme="http",
217                 server_protocol="HTTP/1.1"):
218         """Obtain an HTTP Request object.
219         
220         local_host should be an http.Host object with the server info.
221         remote_host should be an http.Host object with the client info.
222         scheme: either "http" or "https"; defaults to "http"
223         """
224         if self.state == STOPPED:
225             req = NotReadyRequest("The CherryPy engine has stopped.")
226         elif self.state == STARTING:
227             req = NotReadyRequest("The CherryPy engine could not start.")
228         else:
229             # Only run on_start_thread_list if the engine is running.
230             threadID = threading._get_ident()
231             if threadID not in self.seen_threads:
232                 i = len(self.seen_threads) + 1
233                 self.seen_threads[threadID] = i
234                
235                 for func in self.on_start_thread_list:
236                     func(i)
237             req = self.request_class(local_host, remote_host, scheme,
238                                      server_protocol)
239         cherrypy._serving.request = req
240         cherrypy._serving.response = resp = self.response_class()
241         self.servings.append((req, resp))
242         return req
243    
244     def monitor(self):
245         """Check timeout on all responses."""
246         if self.state == STARTED:
247             for req, resp in self.servings:
248                 resp.check_timeout()
249    
250     def start_with_callback(self, func, args=None, kwargs=None):
251         """Start the given func in a new thread, then start self and block."""
252        
253         if args is None:
254             args = ()
255         if kwargs is None:
256             kwargs = {}
257         args = (func,) + args
258        
259         def _callback(func, *a, **kw):
260             self.wait()
261             func(*a, **kw)
262         t = threading.Thread(target=_callback, args=args, kwargs=kwargs)
263         t.setName("CPEngine Callback " + t.getName())
264         t.start()
265        
266         self.start()
267    
268     # Special thanks to Gavin Baker: http://antonym.org/node/100.
269     try:
270         import pwd, grp
271     except ImportError:
272         try:
273             os.umask
274         except AttributeError:
275             def drop_privileges(self):
276                 """Drop privileges. Not implemented on this platform."""
277                 raise NotImplementedError
278         else:
279             umask = None
280            
281             def drop_privileges(self):
282                 """Drop privileges. Windows version (umask only)."""
283                 if self.umask is not None:
284                     old_umask = os.umask(self.umask)
285                     cherrypy.log('umask old: %03o, new: %03o' %
286                                  (old_umask, self.umask), "PRIV")
287     else:
288         uid = None
289         gid = None
290         umask = None
291        
292         def drop_privileges(self):
293             """Drop privileges. UNIX version (uid, gid, and umask)."""
294             if not (self.uid is None and self.gid is None):
295                 if self.uid is None:
296                     uid = None
297                 elif isinstance(self.uid, basestring):
298                     uid = self.pwd.getpwnam(self.uid)[2]
299                 else:
300                     uid = self.uid
301                
302                 if self.gid is None:
303                     gid = None
304                 elif isinstance(self.gid, basestring):
305                     gid = self.grp.getgrnam(self.gid)[2]
306                 else:
307                     gid = self.gid
308                
309                 def names():
310                     name = self.pwd.getpwuid(os.getuid())[0]
311                     group = self.grp.getgrgid(os.getgid())[0]
312                     return name, group
313                
314                 cherrypy.log('Started as %r/%r' % names(), "PRIV")
315                 if gid is not None:
316                     os.setgid(gid)
317                 if uid is not None:
318                     os.setuid(uid)
319                 cherrypy.log('Running as %r/%r' % names(), "PRIV")
320            
321             if self.umask is not None:
322                 old_umask = os.umask(self.umask)
323                 cherrypy.log('umask old: %03o, new: %03o' %
324                              (old_umask, self.umask), "PRIV")
325
326
327 class NotReadyRequest:
328    
329     throw_errors = True
330     show_tracebacks = True
331     error_page = {}
332    
333     def __init__(self, msg):
334         self.msg = msg
335         self.protocol = (1,1)
336    
337     def close(self):
338         pass
339    
340     def run(self, method, path, query_string, protocol, headers, rfile):
341         self.method = "GET"
342         cherrypy.HTTPError(503, self.msg).set_response()
343         cherrypy.response.finalize()
344         return cherrypy.response
345
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets