1 """CherryPy is a pythonic, object-oriented HTTP framework.
2
3
4 CherryPy consists of not one, but four separate API layers.
5
6 The APPLICATION LAYER is the simplest. CherryPy applications are written as
7 a tree of classes and methods, where each branch in the tree corresponds to
8 a branch in the URL path. Each method is a 'page handler', which receives
9 GET and POST params as keyword arguments, and returns or yields the (HTML)
10 body of the response. The special method name 'index' is used for paths
11 that end in a slash, and the special method name 'default' is used to
12 handle multiple paths via a single handler. This layer also includes:
13
14 * the 'exposed' attribute (and cherrypy.expose)
15 * cherrypy.quickstart()
16 * _cp_config attributes
17 * cherrypy.tools (including cherrypy.session)
18 * cherrypy.url()
19
20 The ENVIRONMENT LAYER is used by developers at all levels. It provides
21 information about the current request and response, plus the application
22 and server environment, via a (default) set of top-level objects:
23
24 * cherrypy.request
25 * cherrypy.response
26 * cherrypy.engine
27 * cherrypy.server
28 * cherrypy.tree
29 * cherrypy.config
30 * cherrypy.thread_data
31 * cherrypy.log
32 * cherrypy.HTTPError, NotFound, and HTTPRedirect
33 * cherrypy.lib
34
35 The EXTENSION LAYER allows advanced users to construct and share their own
36 plugins. It consists of:
37
38 * Hook API
39 * Tool API
40 * Toolbox API
41 * Dispatch API
42 * Config Namespace API
43
44 Finally, there is the CORE LAYER, which uses the core API's to construct
45 the default components which are available at higher layers. You can think
46 of the default components as the 'reference implementation' for CherryPy.
47 Megaframeworks (and advanced users) may replace the default components
48 with customized or extended components. The core API's are:
49
50 * Application API
51 * Engine API
52 * Request API
53 * Server API
54 * WSGI API
55
56 These API's are described in the CherryPy specification:
57 http://www.cherrypy.org/wiki/CherryPySpec
58 """
59
60 __version__ = "3.2.2"
61
62 from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode
63 from cherrypy._cpcompat import basestring, unicodestr, set
64
65 from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect
66 from cherrypy._cperror import NotFound, CherryPyException, TimeoutError
67
68 from cherrypy import _cpdispatch as dispatch
69
70 from cherrypy import _cptools
71 tools = _cptools.default_toolbox
72 Tool = _cptools.Tool
73
74 from cherrypy import _cprequest
75 from cherrypy.lib import httputil as _httputil
76
77 from cherrypy import _cptree
78 tree = _cptree.Tree()
79 from cherrypy._cptree import Application
80 from cherrypy import _cpwsgi as wsgi
81
82 from cherrypy import process
83 try:
84 from cherrypy.process import win32
85 engine = win32.Win32Bus()
86 engine.console_control_handler = win32.ConsoleCtrlHandler(engine)
87 del win32
88 except ImportError:
89 engine = process.bus
90
91
92
93
94 engine.listeners['before_request'] = set()
95 engine.listeners['after_request'] = set()
96
98
102
105
111
113 """Check timeout on all responses. (Internal)"""
114 for req, resp in self.servings:
115 resp.check_timeout()
116 engine.timeout_monitor = _TimeoutMonitor(engine)
117 engine.timeout_monitor.subscribe()
118
119 engine.autoreload = process.plugins.Autoreloader(engine)
120 engine.autoreload.subscribe()
121
122 engine.thread_manager = process.plugins.ThreadManager(engine)
123 engine.thread_manager.subscribe()
124
125 engine.signal_handler = process.plugins.SignalHandler(engine)
126
127
128 from cherrypy import _cpserver
129 server = _cpserver.Server()
130 server.subscribe()
131
132
133 -def quickstart(root=None, script_name="", config=None):
134 """Mount the given root, start the builtin server (and engine), then block.
135
136 root: an instance of a "controller class" (a collection of page handler
137 methods) which represents the root of the application.
138 script_name: a string containing the "mount point" of the application.
139 This should start with a slash, and be the path portion of the URL
140 at which to mount the given root. For example, if root.index() will
141 handle requests to "http://www.example.com:8080/dept/app1/", then
142 the script_name argument would be "/dept/app1".
143
144 It MUST NOT end in a slash. If the script_name refers to the root
145 of the URI, it MUST be an empty string (not "/").
146 config: a file or dict containing application config. If this contains
147 a [global] section, those entries will be used in the global
148 (site-wide) config.
149 """
150 if config:
151 _global_conf_alias.update(config)
152
153 tree.mount(root, script_name, config)
154
155 if hasattr(engine, "signal_handler"):
156 engine.signal_handler.subscribe()
157 if hasattr(engine, "console_control_handler"):
158 engine.console_control_handler.subscribe()
159
160 engine.start()
161 engine.block()
162
163
164 from cherrypy._cpcompat import threadlocal as _local
165
167 """An interface for registering request and response objects.
168
169 Rather than have a separate "thread local" object for the request and
170 the response, this class works as a single threadlocal container for
171 both objects (and any others which developers wish to define). In this
172 way, we can easily dump those objects when we stop/start a new HTTP
173 conversation, yet still refer to them as module-level globals in a
174 thread-safe way.
175 """
176
177 request = _cprequest.Request(_httputil.Host("127.0.0.1", 80),
178 _httputil.Host("127.0.0.1", 1111))
179 """
180 The request object for the current thread. In the main thread,
181 and any threads which are not receiving HTTP requests, this is None."""
182
183 response = _cprequest.Response()
184 """
185 The response object for the current thread. In the main thread,
186 and any threads which are not receiving HTTP requests, this is None."""
187
188 - def load(self, request, response):
191
193 """Remove all attributes of self."""
194 self.__dict__.clear()
195
196 serving = _Serving()
197
198
253
254
255
256
257 request = _ThreadLocalProxy('request')
258 response = _ThreadLocalProxy('response')
259
260
262 """A container for thread-specific data."""
263 thread_data = _ThreadData()
264
265
266
267
268
269
271 """Given an object or a path to an object, get the object and its name."""
272 if isinstance(thing, _ThreadLocalProxy):
273 thing = getattr(serving, thing.__attrname__)
274 return _pydoc._builtin_resolve(thing, forceload)
275
276 try:
277 import pydoc as _pydoc
278 _pydoc._builtin_resolve = _pydoc.resolve
279 _pydoc.resolve = _cherrypy_pydoc_resolve
280 except ImportError:
281 pass
282
283
284 from cherrypy import _cplogging
285
287 """A site-wide LogManager; routes to app.log or global log as appropriate.
288
289 This :class:`LogManager<cherrypy._cplogging.LogManager>` implements
290 cherrypy.log() and cherrypy.log.access(). If either
291 function is called during a request, the message will be sent to the
292 logger for the current Application. If they are called outside of a
293 request, the message will be sent to the site-wide logger.
294 """
295
304
311
312
313 log = _GlobalLogManager()
314
315 log.screen = True
316 log.error_file = ''
317
318 log.access_file = ''
319
322 engine.subscribe('log', _buslog)
323
324
325
326
327 -def expose(func=None, alias=None):
328 """Expose the function, optionally providing an alias or set of aliases."""
329 def expose_(func):
330 func.exposed = True
331 if alias is not None:
332 if isinstance(alias, basestring):
333 parents[alias.replace(".", "_")] = func
334 else:
335 for a in alias:
336 parents[a.replace(".", "_")] = func
337 return func
338
339 import sys, types
340 if isinstance(func, (types.FunctionType, types.MethodType)):
341 if alias is None:
342
343 func.exposed = True
344 return func
345 else:
346
347 parents = sys._getframe(1).f_locals
348 return expose_(func)
349 elif func is None:
350 if alias is None:
351
352 parents = sys._getframe(1).f_locals
353 return expose_
354 else:
355
356
357 parents = sys._getframe(1).f_locals
358 return expose_
359 else:
360
361
362 parents = sys._getframe(1).f_locals
363 alias = func
364 return expose_
365
367 """A decorator for _cp_dispatch
368 (cherrypy.dispatch.Dispatcher.dispatch_method_name).
369
370 Optional keyword argument: handler=(Object or Function)
371
372 Provides a _cp_dispatch function that pops off path segments into
373 cherrypy.request.params under the names specified. The dispatch
374 is then forwarded on to the next vpath element.
375
376 Note that any existing (and exposed) member function of the class that
377 popargs is applied to will override that value of the argument. For
378 instance, if you have a method named "list" on the class decorated with
379 popargs, then accessing "/list" will call that function instead of popping
380 it off as the requested parameter. This restriction applies to all
381 _cp_dispatch functions. The only way around this restriction is to create
382 a "blank class" whose only function is to provide _cp_dispatch.
383
384 If there are path elements after the arguments, or more arguments
385 are requested than are available in the vpath, then the 'handler'
386 keyword argument specifies the next object to handle the parameterized
387 request. If handler is not specified or is None, then self is used.
388 If handler is a function rather than an instance, then that function
389 will be called with the args specified and the return value from that
390 function used as the next object INSTEAD of adding the parameters to
391 cherrypy.request.args.
392
393 This decorator may be used in one of two ways:
394
395 As a class decorator:
396 @cherrypy.popargs('year', 'month', 'day')
397 class Blog:
398 def index(self, year=None, month=None, day=None):
399 #Process the parameters here; any url like
400 #/, /2009, /2009/12, or /2009/12/31
401 #will fill in the appropriate parameters.
402
403 def create(self):
404 #This link will still be available at /create. Defined functions
405 #take precedence over arguments.
406
407 Or as a member of a class:
408 class Blog:
409 _cp_dispatch = cherrypy.popargs('year', 'month', 'day')
410 #...
411
412 The handler argument may be used to mix arguments with built in functions.
413 For instance, the following setup allows different activities at the
414 day, month, and year level:
415
416 class DayHandler:
417 def index(self, year, month, day):
418 #Do something with this day; probably list entries
419
420 def delete(self, year, month, day):
421 #Delete all entries for this day
422
423 @cherrypy.popargs('day', handler=DayHandler())
424 class MonthHandler:
425 def index(self, year, month):
426 #Do something with this month; probably list entries
427
428 def delete(self, year, month):
429 #Delete all entries for this month
430
431 @cherrypy.popargs('month', handler=MonthHandler())
432 class YearHandler:
433 def index(self, year):
434 #Do something with this year
435
436 #...
437
438 @cherrypy.popargs('year', handler=YearHandler())
439 class Root:
440 def index(self):
441 #...
442
443 """
444
445
446
447
448 handler = None
449 handler_call = False
450 for k,v in kwargs.items():
451 if k == 'handler':
452 handler = v
453 else:
454 raise TypeError(
455 "cherrypy.popargs() got an unexpected keyword argument '{0}'" \
456 .format(k)
457 )
458
459 import inspect
460
461 if handler is not None \
462 and (hasattr(handler, '__call__') or inspect.isclass(handler)):
463 handler_call = True
464
465 def decorated(cls_or_self=None, vpath=None):
466 if inspect.isclass(cls_or_self):
467
468 cls = cls_or_self
469 setattr(cls, dispatch.Dispatcher.dispatch_method_name, decorated)
470 return cls
471
472
473 self = cls_or_self
474 parms = {}
475 for arg in args:
476 if not vpath:
477 break
478 parms[arg] = vpath.pop(0)
479
480 if handler is not None:
481 if handler_call:
482 return handler(**parms)
483 else:
484 request.params.update(parms)
485 return handler
486
487 request.params.update(parms)
488
489
490
491
492 if vpath:
493 return getattr(self, vpath.pop(0), None)
494 else:
495 return self
496
497 return decorated
498
499 -def url(path="", qs="", script_name=None, base=None, relative=None):
500 """Create an absolute URL for the given path.
501
502 If 'path' starts with a slash ('/'), this will return
503 (base + script_name + path + qs).
504 If it does not start with a slash, this returns
505 (base + script_name [+ request.path_info] + path + qs).
506
507 If script_name is None, cherrypy.request will be used
508 to find a script_name, if available.
509
510 If base is None, cherrypy.request.base will be used (if available).
511 Note that you can use cherrypy.tools.proxy to change this.
512
513 Finally, note that this function can be used to obtain an absolute URL
514 for the current request path (minus the querystring) by passing no args.
515 If you call url(qs=cherrypy.request.query_string), you should get the
516 original browser URL (assuming no internal redirections).
517
518 If relative is None or not provided, request.app.relative_urls will
519 be used (if available, else False). If False, the output will be an
520 absolute URL (including the scheme, host, vhost, and script_name).
521 If True, the output will instead be a URL that is relative to the
522 current request path, perhaps including '..' atoms. If relative is
523 the string 'server', the output will instead be a URL that is
524 relative to the server root; i.e., it will start with a slash.
525 """
526 if isinstance(qs, (tuple, list, dict)):
527 qs = _urlencode(qs)
528 if qs:
529 qs = '?' + qs
530
531 if request.app:
532 if not path.startswith("/"):
533
534
535
536 pi = request.path_info
537 if request.is_index is True:
538 if not pi.endswith('/'):
539 pi = pi + '/'
540 elif request.is_index is False:
541 if pi.endswith('/') and pi != '/':
542 pi = pi[:-1]
543
544 if path == "":
545 path = pi
546 else:
547 path = _urljoin(pi, path)
548
549 if script_name is None:
550 script_name = request.script_name
551 if base is None:
552 base = request.base
553
554 newurl = base + script_name + path + qs
555 else:
556
557
558
559
560 if base is None:
561 base = server.base()
562
563 path = (script_name or "") + path
564 newurl = base + path + qs
565
566 if './' in newurl:
567
568 atoms = []
569 for atom in newurl.split('/'):
570 if atom == '.':
571 pass
572 elif atom == '..':
573 atoms.pop()
574 else:
575 atoms.append(atom)
576 newurl = '/'.join(atoms)
577
578
579
580 if relative is None:
581 relative = getattr(request.app, "relative_urls", False)
582
583
584 if relative == 'server':
585
586
587
588 newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
589 elif relative:
590
591
592 old = url(relative=False).split('/')[:-1]
593 new = newurl.split('/')
594 while old and new:
595 a, b = old[0], new[0]
596 if a != b:
597 break
598 old.pop(0)
599 new.pop(0)
600 new = (['..'] * len(old)) + new
601 newurl = '/'.join(new)
602
603 return newurl
604
605
606
607 from cherrypy import _cpconfig
608
609
610 config = _global_conf_alias = _cpconfig.Config()
611 config.defaults = {
612 'tools.log_tracebacks.on': True,
613 'tools.log_headers.on': True,
614 'tools.trailing_slash.on': True,
615 'tools.encode.on': True
616 }
617 config.namespaces["log"] = lambda k, v: setattr(log, k, v)
618 config.namespaces["checker"] = lambda k, v: setattr(checker, k, v)
619
620 config.reset()
621
622 from cherrypy import _cpchecker
623 checker = _cpchecker.Checker()
624 engine.subscribe('start', checker)
625