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 <https://bitbucket.org/cherrypy/cherrypy/wiki/CherryPySpec>`_.
57 """
58
59 __version__ = "3.5.0"
60
61 from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode
62 from cherrypy._cpcompat import basestring, unicodestr, set
63
64 from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect
65 from cherrypy._cperror import NotFound, CherryPyException, TimeoutError
66
67 from cherrypy import _cpdispatch as dispatch
68
69 from cherrypy import _cptools
70 tools = _cptools.default_toolbox
71 Tool = _cptools.Tool
72
73 from cherrypy import _cprequest
74 from cherrypy.lib import httputil as _httputil
75
76 from cherrypy import _cptree
77 tree = _cptree.Tree()
78 from cherrypy._cptree import Application
79 from cherrypy import _cpwsgi as wsgi
80
81 from cherrypy import process
82 try:
83 from cherrypy.process import win32
84 engine = win32.Win32Bus()
85 engine.console_control_handler = win32.ConsoleCtrlHandler(engine)
86 del win32
87 except ImportError:
88 engine = process.bus
89
90
91
92
93 engine.listeners['before_request'] = set()
94 engine.listeners['after_request'] = set()
95
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
129
130 """Handle signals from other processes based on the configured
131 platform handlers above."""
132
135
137 """Add the handlers based on the platform"""
138 if hasattr(self.bus, "signal_handler"):
139 self.bus.signal_handler.subscribe()
140 if hasattr(self.bus, "console_control_handler"):
141 self.bus.console_control_handler.subscribe()
142
143 engine.signals = _HandleSignalsPlugin(engine)
144
145
146 from cherrypy import _cpserver
147 server = _cpserver.Server()
148 server.subscribe()
149
150
151 -def quickstart(root=None, script_name="", config=None):
152 """Mount the given root, start the builtin server (and engine), then block.
153
154 root: an instance of a "controller class" (a collection of page handler
155 methods) which represents the root of the application.
156 script_name: a string containing the "mount point" of the application.
157 This should start with a slash, and be the path portion of the URL
158 at which to mount the given root. For example, if root.index() will
159 handle requests to "http://www.example.com:8080/dept/app1/", then
160 the script_name argument would be "/dept/app1".
161
162 It MUST NOT end in a slash. If the script_name refers to the root
163 of the URI, it MUST be an empty string (not "/").
164 config: a file or dict containing application config. If this contains
165 a [global] section, those entries will be used in the global
166 (site-wide) config.
167 """
168 if config:
169 _global_conf_alias.update(config)
170
171 tree.mount(root, script_name, config)
172
173 engine.signals.subscribe()
174 engine.start()
175 engine.block()
176
177
178 from cherrypy._cpcompat import threadlocal as _local
179
180
182
183 """An interface for registering request and response objects.
184
185 Rather than have a separate "thread local" object for the request and
186 the response, this class works as a single threadlocal container for
187 both objects (and any others which developers wish to define). In this
188 way, we can easily dump those objects when we stop/start a new HTTP
189 conversation, yet still refer to them as module-level globals in a
190 thread-safe way.
191 """
192
193 request = _cprequest.Request(_httputil.Host("127.0.0.1", 80),
194 _httputil.Host("127.0.0.1", 1111))
195 """
196 The request object for the current thread. In the main thread,
197 and any threads which are not receiving HTTP requests, this is None."""
198
199 response = _cprequest.Response()
200 """
201 The response object for the current thread. In the main thread,
202 and any threads which are not receiving HTTP requests, this is None."""
203
204 - def load(self, request, response):
207
209 """Remove all attributes of self."""
210 self.__dict__.clear()
211
212 serving = _Serving()
213
214
269
270
271
272
273 request = _ThreadLocalProxy('request')
274 response = _ThreadLocalProxy('response')
275
276
277
278
280
281 """A container for thread-specific data."""
282 thread_data = _ThreadData()
283
284
285
286
287
288
290 """Given an object or a path to an object, get the object and its name."""
291 if isinstance(thing, _ThreadLocalProxy):
292 thing = getattr(serving, thing.__attrname__)
293 return _pydoc._builtin_resolve(thing, forceload)
294
295 try:
296 import pydoc as _pydoc
297 _pydoc._builtin_resolve = _pydoc.resolve
298 _pydoc.resolve = _cherrypy_pydoc_resolve
299 except ImportError:
300 pass
301
302
303 from cherrypy import _cplogging
304
305
307
308 """A site-wide LogManager; routes to app.log or global log as appropriate.
309
310 This :class:`LogManager<cherrypy._cplogging.LogManager>` implements
311 cherrypy.log() and cherrypy.log.access(). If either
312 function is called during a request, the message will be sent to the
313 logger for the current Application. If they are called outside of a
314 request, the message will be sent to the site-wide logger.
315 """
316
327
335
336
337 log = _GlobalLogManager()
338
339 log.screen = True
340 log.error_file = ''
341
342 log.access_file = ''
343
344
347 engine.subscribe('log', _buslog)
348
349
350
351
352 -def expose(func=None, alias=None):
353 """Expose the function, optionally providing an alias or set of aliases."""
354 def expose_(func):
355 func.exposed = True
356 if alias is not None:
357 if isinstance(alias, basestring):
358 parents[alias.replace(".", "_")] = func
359 else:
360 for a in alias:
361 parents[a.replace(".", "_")] = func
362 return func
363
364 import sys
365 import types
366 if isinstance(func, (types.FunctionType, types.MethodType)):
367 if alias is None:
368
369 func.exposed = True
370 return func
371 else:
372
373 parents = sys._getframe(1).f_locals
374 return expose_(func)
375 elif func is None:
376 if alias is None:
377
378 parents = sys._getframe(1).f_locals
379 return expose_
380 else:
381
382
383 parents = sys._getframe(1).f_locals
384 return expose_
385 else:
386
387
388 parents = sys._getframe(1).f_locals
389 alias = func
390 return expose_
391
392
394 """A decorator for _cp_dispatch
395 (cherrypy.dispatch.Dispatcher.dispatch_method_name).
396
397 Optional keyword argument: handler=(Object or Function)
398
399 Provides a _cp_dispatch function that pops off path segments into
400 cherrypy.request.params under the names specified. The dispatch
401 is then forwarded on to the next vpath element.
402
403 Note that any existing (and exposed) member function of the class that
404 popargs is applied to will override that value of the argument. For
405 instance, if you have a method named "list" on the class decorated with
406 popargs, then accessing "/list" will call that function instead of popping
407 it off as the requested parameter. This restriction applies to all
408 _cp_dispatch functions. The only way around this restriction is to create
409 a "blank class" whose only function is to provide _cp_dispatch.
410
411 If there are path elements after the arguments, or more arguments
412 are requested than are available in the vpath, then the 'handler'
413 keyword argument specifies the next object to handle the parameterized
414 request. If handler is not specified or is None, then self is used.
415 If handler is a function rather than an instance, then that function
416 will be called with the args specified and the return value from that
417 function used as the next object INSTEAD of adding the parameters to
418 cherrypy.request.args.
419
420 This decorator may be used in one of two ways:
421
422 As a class decorator:
423 @cherrypy.popargs('year', 'month', 'day')
424 class Blog:
425 def index(self, year=None, month=None, day=None):
426 #Process the parameters here; any url like
427 #/, /2009, /2009/12, or /2009/12/31
428 #will fill in the appropriate parameters.
429
430 def create(self):
431 #This link will still be available at /create. Defined functions
432 #take precedence over arguments.
433
434 Or as a member of a class:
435 class Blog:
436 _cp_dispatch = cherrypy.popargs('year', 'month', 'day')
437 #...
438
439 The handler argument may be used to mix arguments with built in functions.
440 For instance, the following setup allows different activities at the
441 day, month, and year level:
442
443 class DayHandler:
444 def index(self, year, month, day):
445 #Do something with this day; probably list entries
446
447 def delete(self, year, month, day):
448 #Delete all entries for this day
449
450 @cherrypy.popargs('day', handler=DayHandler())
451 class MonthHandler:
452 def index(self, year, month):
453 #Do something with this month; probably list entries
454
455 def delete(self, year, month):
456 #Delete all entries for this month
457
458 @cherrypy.popargs('month', handler=MonthHandler())
459 class YearHandler:
460 def index(self, year):
461 #Do something with this year
462
463 #...
464
465 @cherrypy.popargs('year', handler=YearHandler())
466 class Root:
467 def index(self):
468 #...
469
470 """
471
472
473
474
475 handler = None
476 handler_call = False
477 for k, v in kwargs.items():
478 if k == 'handler':
479 handler = v
480 else:
481 raise TypeError(
482 "cherrypy.popargs() got an unexpected keyword argument '{0}'"
483 .format(k)
484 )
485
486 import inspect
487
488 if handler is not None \
489 and (hasattr(handler, '__call__') or inspect.isclass(handler)):
490 handler_call = True
491
492 def decorated(cls_or_self=None, vpath=None):
493 if inspect.isclass(cls_or_self):
494
495 cls = cls_or_self
496 setattr(cls, dispatch.Dispatcher.dispatch_method_name, decorated)
497 return cls
498
499
500 self = cls_or_self
501 parms = {}
502 for arg in args:
503 if not vpath:
504 break
505 parms[arg] = vpath.pop(0)
506
507 if handler is not None:
508 if handler_call:
509 return handler(**parms)
510 else:
511 request.params.update(parms)
512 return handler
513
514 request.params.update(parms)
515
516
517
518
519 if vpath:
520 return getattr(self, vpath.pop(0), None)
521 else:
522 return self
523
524 return decorated
525
526
527 -def url(path="", qs="", script_name=None, base=None, relative=None):
528 """Create an absolute URL for the given path.
529
530 If 'path' starts with a slash ('/'), this will return
531 (base + script_name + path + qs).
532 If it does not start with a slash, this returns
533 (base + script_name [+ request.path_info] + path + qs).
534
535 If script_name is None, cherrypy.request will be used
536 to find a script_name, if available.
537
538 If base is None, cherrypy.request.base will be used (if available).
539 Note that you can use cherrypy.tools.proxy to change this.
540
541 Finally, note that this function can be used to obtain an absolute URL
542 for the current request path (minus the querystring) by passing no args.
543 If you call url(qs=cherrypy.request.query_string), you should get the
544 original browser URL (assuming no internal redirections).
545
546 If relative is None or not provided, request.app.relative_urls will
547 be used (if available, else False). If False, the output will be an
548 absolute URL (including the scheme, host, vhost, and script_name).
549 If True, the output will instead be a URL that is relative to the
550 current request path, perhaps including '..' atoms. If relative is
551 the string 'server', the output will instead be a URL that is
552 relative to the server root; i.e., it will start with a slash.
553 """
554 if isinstance(qs, (tuple, list, dict)):
555 qs = _urlencode(qs)
556 if qs:
557 qs = '?' + qs
558
559 if request.app:
560 if not path.startswith("/"):
561
562
563
564 pi = request.path_info
565 if request.is_index is True:
566 if not pi.endswith('/'):
567 pi = pi + '/'
568 elif request.is_index is False:
569 if pi.endswith('/') and pi != '/':
570 pi = pi[:-1]
571
572 if path == "":
573 path = pi
574 else:
575 path = _urljoin(pi, path)
576
577 if script_name is None:
578 script_name = request.script_name
579 if base is None:
580 base = request.base
581
582 newurl = base + script_name + path + qs
583 else:
584
585
586
587
588 if base is None:
589 base = server.base()
590
591 path = (script_name or "") + path
592 newurl = base + path + qs
593
594 if './' in newurl:
595
596 atoms = []
597 for atom in newurl.split('/'):
598 if atom == '.':
599 pass
600 elif atom == '..':
601 atoms.pop()
602 else:
603 atoms.append(atom)
604 newurl = '/'.join(atoms)
605
606
607
608 if relative is None:
609 relative = getattr(request.app, "relative_urls", False)
610
611
612 if relative == 'server':
613
614
615
616 newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
617 elif relative:
618
619
620 old = url(relative=False).split('/')[:-1]
621 new = newurl.split('/')
622 while old and new:
623 a, b = old[0], new[0]
624 if a != b:
625 break
626 old.pop(0)
627 new.pop(0)
628 new = (['..'] * len(old)) + new
629 newurl = '/'.join(new)
630
631 return newurl
632
633
634
635 from cherrypy import _cpconfig
636
637
638 config = _global_conf_alias = _cpconfig.Config()
639 config.defaults = {
640 'tools.log_tracebacks.on': True,
641 'tools.log_headers.on': True,
642 'tools.trailing_slash.on': True,
643 'tools.encode.on': True
644 }
645 config.namespaces["log"] = lambda k, v: setattr(log, k, v)
646 config.namespaces["checker"] = lambda k, v: setattr(checker, k, v)
647
648 config.reset()
649
650 from cherrypy import _cpchecker
651 checker = _cpchecker.Checker()
652 engine.subscribe('start', checker)
653