Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/cherrypy/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'cherrypy/__init__.py')
-rwxr-xr-xcherrypy/__init__.py620
1 files changed, 620 insertions, 0 deletions
diff --git a/cherrypy/__init__.py b/cherrypy/__init__.py
new file mode 100755
index 0000000..eb7cabf
--- /dev/null
+++ b/cherrypy/__init__.py
@@ -0,0 +1,620 @@
+"""CherryPy is a pythonic, object-oriented HTTP framework.
+
+
+CherryPy consists of not one, but four separate API layers.
+
+The APPLICATION LAYER is the simplest. CherryPy applications are written as
+a tree of classes and methods, where each branch in the tree corresponds to
+a branch in the URL path. Each method is a 'page handler', which receives
+GET and POST params as keyword arguments, and returns or yields the (HTML)
+body of the response. The special method name 'index' is used for paths
+that end in a slash, and the special method name 'default' is used to
+handle multiple paths via a single handler. This layer also includes:
+
+ * the 'exposed' attribute (and cherrypy.expose)
+ * cherrypy.quickstart()
+ * _cp_config attributes
+ * cherrypy.tools (including cherrypy.session)
+ * cherrypy.url()
+
+The ENVIRONMENT LAYER is used by developers at all levels. It provides
+information about the current request and response, plus the application
+and server environment, via a (default) set of top-level objects:
+
+ * cherrypy.request
+ * cherrypy.response
+ * cherrypy.engine
+ * cherrypy.server
+ * cherrypy.tree
+ * cherrypy.config
+ * cherrypy.thread_data
+ * cherrypy.log
+ * cherrypy.HTTPError, NotFound, and HTTPRedirect
+ * cherrypy.lib
+
+The EXTENSION LAYER allows advanced users to construct and share their own
+plugins. It consists of:
+
+ * Hook API
+ * Tool API
+ * Toolbox API
+ * Dispatch API
+ * Config Namespace API
+
+Finally, there is the CORE LAYER, which uses the core API's to construct
+the default components which are available at higher layers. You can think
+of the default components as the 'reference implementation' for CherryPy.
+Megaframeworks (and advanced users) may replace the default components
+with customized or extended components. The core API's are:
+
+ * Application API
+ * Engine API
+ * Request API
+ * Server API
+ * WSGI API
+
+These API's are described in the CherryPy specification:
+http://www.cherrypy.org/wiki/CherryPySpec
+"""
+
+__version__ = "3.2.0"
+
+from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode
+from cherrypy._cpcompat import basestring, unicodestr
+
+from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect
+from cherrypy._cperror import NotFound, CherryPyException, TimeoutError
+
+from cherrypy import _cpdispatch as dispatch
+
+from cherrypy import _cptools
+tools = _cptools.default_toolbox
+Tool = _cptools.Tool
+
+from cherrypy import _cprequest
+from cherrypy.lib import httputil as _httputil
+
+from cherrypy import _cptree
+tree = _cptree.Tree()
+from cherrypy._cptree import Application
+from cherrypy import _cpwsgi as wsgi
+
+from cherrypy import process
+try:
+ from cherrypy.process import win32
+ engine = win32.Win32Bus()
+ engine.console_control_handler = win32.ConsoleCtrlHandler(engine)
+ del win32
+except ImportError:
+ engine = process.bus
+
+
+# Timeout monitor
+class _TimeoutMonitor(process.plugins.Monitor):
+
+ def __init__(self, bus):
+ self.servings = []
+ process.plugins.Monitor.__init__(self, bus, self.run)
+
+ def acquire(self):
+ self.servings.append((serving.request, serving.response))
+
+ def release(self):
+ try:
+ self.servings.remove((serving.request, serving.response))
+ except ValueError:
+ pass
+
+ def run(self):
+ """Check timeout on all responses. (Internal)"""
+ for req, resp in self.servings:
+ resp.check_timeout()
+engine.timeout_monitor = _TimeoutMonitor(engine)
+engine.timeout_monitor.subscribe()
+
+engine.autoreload = process.plugins.Autoreloader(engine)
+engine.autoreload.subscribe()
+
+engine.thread_manager = process.plugins.ThreadManager(engine)
+engine.thread_manager.subscribe()
+
+engine.signal_handler = process.plugins.SignalHandler(engine)
+
+
+from cherrypy import _cpserver
+server = _cpserver.Server()
+server.subscribe()
+
+
+def quickstart(root=None, script_name="", config=None):
+ """Mount the given root, start the builtin server (and engine), then block.
+
+ root: an instance of a "controller class" (a collection of page handler
+ methods) which represents the root of the application.
+ script_name: a string containing the "mount point" of the application.
+ This should start with a slash, and be the path portion of the URL
+ at which to mount the given root. For example, if root.index() will
+ handle requests to "http://www.example.com:8080/dept/app1/", then
+ the script_name argument would be "/dept/app1".
+
+ It MUST NOT end in a slash. If the script_name refers to the root
+ of the URI, it MUST be an empty string (not "/").
+ config: a file or dict containing application config. If this contains
+ a [global] section, those entries will be used in the global
+ (site-wide) config.
+ """
+ if config:
+ _global_conf_alias.update(config)
+
+ tree.mount(root, script_name, config)
+
+ if hasattr(engine, "signal_handler"):
+ engine.signal_handler.subscribe()
+ if hasattr(engine, "console_control_handler"):
+ engine.console_control_handler.subscribe()
+
+ engine.start()
+ engine.block()
+
+
+from cherrypy._cpcompat import threadlocal as _local
+
+class _Serving(_local):
+ """An interface for registering request and response objects.
+
+ Rather than have a separate "thread local" object for the request and
+ the response, this class works as a single threadlocal container for
+ both objects (and any others which developers wish to define). In this
+ way, we can easily dump those objects when we stop/start a new HTTP
+ conversation, yet still refer to them as module-level globals in a
+ thread-safe way.
+ """
+
+ request = _cprequest.Request(_httputil.Host("127.0.0.1", 80),
+ _httputil.Host("127.0.0.1", 1111))
+ """
+ The request object for the current thread. In the main thread,
+ and any threads which are not receiving HTTP requests, this is None."""
+
+ response = _cprequest.Response()
+ """
+ The response object for the current thread. In the main thread,
+ and any threads which are not receiving HTTP requests, this is None."""
+
+ def load(self, request, response):
+ self.request = request
+ self.response = response
+
+ def clear(self):
+ """Remove all attributes of self."""
+ self.__dict__.clear()
+
+serving = _Serving()
+
+
+class _ThreadLocalProxy(object):
+
+ __slots__ = ['__attrname__', '__dict__']
+
+ def __init__(self, attrname):
+ self.__attrname__ = attrname
+
+ def __getattr__(self, name):
+ child = getattr(serving, self.__attrname__)
+ return getattr(child, name)
+
+ def __setattr__(self, name, value):
+ if name in ("__attrname__", ):
+ object.__setattr__(self, name, value)
+ else:
+ child = getattr(serving, self.__attrname__)
+ setattr(child, name, value)
+
+ def __delattr__(self, name):
+ child = getattr(serving, self.__attrname__)
+ delattr(child, name)
+
+ def _get_dict(self):
+ child = getattr(serving, self.__attrname__)
+ d = child.__class__.__dict__.copy()
+ d.update(child.__dict__)
+ return d
+ __dict__ = property(_get_dict)
+
+ def __getitem__(self, key):
+ child = getattr(serving, self.__attrname__)
+ return child[key]
+
+ def __setitem__(self, key, value):
+ child = getattr(serving, self.__attrname__)
+ child[key] = value
+
+ def __delitem__(self, key):
+ child = getattr(serving, self.__attrname__)
+ del child[key]
+
+ def __contains__(self, key):
+ child = getattr(serving, self.__attrname__)
+ return key in child
+
+ def __len__(self):
+ child = getattr(serving, self.__attrname__)
+ return len(child)
+
+ def __nonzero__(self):
+ child = getattr(serving, self.__attrname__)
+ return bool(child)
+ # Python 3
+ __bool__ = __nonzero__
+
+# Create request and response object (the same objects will be used
+# throughout the entire life of the webserver, but will redirect
+# to the "serving" object)
+request = _ThreadLocalProxy('request')
+response = _ThreadLocalProxy('response')
+
+# Create thread_data object as a thread-specific all-purpose storage
+class _ThreadData(_local):
+ """A container for thread-specific data."""
+thread_data = _ThreadData()
+
+
+# Monkeypatch pydoc to allow help() to go through the threadlocal proxy.
+# Jan 2007: no Googleable examples of anyone else replacing pydoc.resolve.
+# The only other way would be to change what is returned from type(request)
+# and that's not possible in pure Python (you'd have to fake ob_type).
+def _cherrypy_pydoc_resolve(thing, forceload=0):
+ """Given an object or a path to an object, get the object and its name."""
+ if isinstance(thing, _ThreadLocalProxy):
+ thing = getattr(serving, thing.__attrname__)
+ return _pydoc._builtin_resolve(thing, forceload)
+
+try:
+ import pydoc as _pydoc
+ _pydoc._builtin_resolve = _pydoc.resolve
+ _pydoc.resolve = _cherrypy_pydoc_resolve
+except ImportError:
+ pass
+
+
+from cherrypy import _cplogging
+
+class _GlobalLogManager(_cplogging.LogManager):
+ """A site-wide LogManager; routes to app.log or global log as appropriate.
+
+ This :class:`LogManager<cherrypy._cplogging.LogManager>` implements
+ cherrypy.log() and cherrypy.log.access(). If either
+ function is called during a request, the message will be sent to the
+ logger for the current Application. If they are called outside of a
+ request, the message will be sent to the site-wide logger.
+ """
+
+ def __call__(self, *args, **kwargs):
+ """Log the given message to the app.log or global log as appropriate."""
+ # Do NOT use try/except here. See http://www.cherrypy.org/ticket/945
+ if hasattr(request, 'app') and hasattr(request.app, 'log'):
+ log = request.app.log
+ else:
+ log = self
+ return log.error(*args, **kwargs)
+
+ def access(self):
+ """Log an access message to the app.log or global log as appropriate."""
+ try:
+ return request.app.log.access()
+ except AttributeError:
+ return _cplogging.LogManager.access(self)
+
+
+log = _GlobalLogManager()
+# Set a default screen handler on the global log.
+log.screen = True
+log.error_file = ''
+# Using an access file makes CP about 10% slower. Leave off by default.
+log.access_file = ''
+
+def _buslog(msg, level):
+ log.error(msg, 'ENGINE', severity=level)
+engine.subscribe('log', _buslog)
+
+# Helper functions for CP apps #
+
+
+def expose(func=None, alias=None):
+ """Expose the function, optionally providing an alias or set of aliases."""
+ def expose_(func):
+ func.exposed = True
+ if alias is not None:
+ if isinstance(alias, basestring):
+ parents[alias.replace(".", "_")] = func
+ else:
+ for a in alias:
+ parents[a.replace(".", "_")] = func
+ return func
+
+ import sys, types
+ if isinstance(func, (types.FunctionType, types.MethodType)):
+ if alias is None:
+ # @expose
+ func.exposed = True
+ return func
+ else:
+ # func = expose(func, alias)
+ parents = sys._getframe(1).f_locals
+ return expose_(func)
+ elif func is None:
+ if alias is None:
+ # @expose()
+ parents = sys._getframe(1).f_locals
+ return expose_
+ else:
+ # @expose(alias="alias") or
+ # @expose(alias=["alias1", "alias2"])
+ parents = sys._getframe(1).f_locals
+ return expose_
+ else:
+ # @expose("alias") or
+ # @expose(["alias1", "alias2"])
+ parents = sys._getframe(1).f_locals
+ alias = func
+ return expose_
+
+def popargs(*args, **kwargs):
+ """A decorator for _cp_dispatch
+ (cherrypy.dispatch.Dispatcher.dispatch_method_name).
+
+ Optional keyword argument: handler=(Object or Function)
+
+ Provides a _cp_dispatch function that pops off path segments into
+ cherrypy.request.params under the names specified. The dispatch
+ is then forwarded on to the next vpath element.
+
+ Note that any existing (and exposed) member function of the class that
+ popargs is applied to will override that value of the argument. For
+ instance, if you have a method named "list" on the class decorated with
+ popargs, then accessing "/list" will call that function instead of popping
+ it off as the requested parameter. This restriction applies to all
+ _cp_dispatch functions. The only way around this restriction is to create
+ a "blank class" whose only function is to provide _cp_dispatch.
+
+ If there are path elements after the arguments, or more arguments
+ are requested than are available in the vpath, then the 'handler'
+ keyword argument specifies the next object to handle the parameterized
+ request. If handler is not specified or is None, then self is used.
+ If handler is a function rather than an instance, then that function
+ will be called with the args specified and the return value from that
+ function used as the next object INSTEAD of adding the parameters to
+ cherrypy.request.args.
+
+ This decorator may be used in one of two ways:
+
+ As a class decorator:
+ @cherrypy.popargs('year', 'month', 'day')
+ class Blog:
+ def index(self, year=None, month=None, day=None):
+ #Process the parameters here; any url like
+ #/, /2009, /2009/12, or /2009/12/31
+ #will fill in the appropriate parameters.
+
+ def create(self):
+ #This link will still be available at /create. Defined functions
+ #take precedence over arguments.
+
+ Or as a member of a class:
+ class Blog:
+ _cp_dispatch = cherrypy.popargs('year', 'month', 'day')
+ #...
+
+ The handler argument may be used to mix arguments with built in functions.
+ For instance, the following setup allows different activities at the
+ day, month, and year level:
+
+ class DayHandler:
+ def index(self, year, month, day):
+ #Do something with this day; probably list entries
+
+ def delete(self, year, month, day):
+ #Delete all entries for this day
+
+ @cherrypy.popargs('day', handler=DayHandler())
+ class MonthHandler:
+ def index(self, year, month):
+ #Do something with this month; probably list entries
+
+ def delete(self, year, month):
+ #Delete all entries for this month
+
+ @cherrypy.popargs('month', handler=MonthHandler())
+ class YearHandler:
+ def index(self, year):
+ #Do something with this year
+
+ #...
+
+ @cherrypy.popargs('year', handler=YearHandler())
+ class Root:
+ def index(self):
+ #...
+
+ """
+
+ #Since keyword arg comes after *args, we have to process it ourselves
+ #for lower versions of python.
+
+ handler = None
+ handler_call = False
+ for k,v in kwargs.items():
+ if k == 'handler':
+ handler = v
+ else:
+ raise TypeError(
+ "cherrypy.popargs() got an unexpected keyword argument '{0}'" \
+ .format(k)
+ )
+
+ import inspect
+
+ if handler is not None \
+ and (hasattr(handler, '__call__') or inspect.isclass(handler)):
+ handler_call = True
+
+ def decorated(cls_or_self=None, vpath=None):
+ if inspect.isclass(cls_or_self):
+ #cherrypy.popargs is a class decorator
+ cls = cls_or_self
+ setattr(cls, dispatch.Dispatcher.dispatch_method_name, decorated)
+ return cls
+
+ #We're in the actual function
+ self = cls_or_self
+ parms = {}
+ for arg in args:
+ if not vpath:
+ break
+ parms[arg] = vpath.pop(0)
+
+ if handler is not None:
+ if handler_call:
+ return handler(**parms)
+ else:
+ request.params.update(parms)
+ return handler
+
+ request.params.update(parms)
+
+ #If we are the ultimate handler, then to prevent our _cp_dispatch
+ #from being called again, we will resolve remaining elements through
+ #getattr() directly.
+ if vpath:
+ return getattr(self, vpath.pop(0), None)
+ else:
+ return self
+
+ return decorated
+
+def url(path="", qs="", script_name=None, base=None, relative=None):
+ """Create an absolute URL for the given path.
+
+ If 'path' starts with a slash ('/'), this will return
+ (base + script_name + path + qs).
+ If it does not start with a slash, this returns
+ (base + script_name [+ request.path_info] + path + qs).
+
+ If script_name is None, cherrypy.request will be used
+ to find a script_name, if available.
+
+ If base is None, cherrypy.request.base will be used (if available).
+ Note that you can use cherrypy.tools.proxy to change this.
+
+ Finally, note that this function can be used to obtain an absolute URL
+ for the current request path (minus the querystring) by passing no args.
+ If you call url(qs=cherrypy.request.query_string), you should get the
+ original browser URL (assuming no internal redirections).
+
+ If relative is None or not provided, request.app.relative_urls will
+ be used (if available, else False). If False, the output will be an
+ absolute URL (including the scheme, host, vhost, and script_name).
+ If True, the output will instead be a URL that is relative to the
+ current request path, perhaps including '..' atoms. If relative is
+ the string 'server', the output will instead be a URL that is
+ relative to the server root; i.e., it will start with a slash.
+ """
+ if isinstance(qs, (tuple, list, dict)):
+ qs = _urlencode(qs)
+ if qs:
+ qs = '?' + qs
+
+ if request.app:
+ if not path.startswith("/"):
+ # Append/remove trailing slash from path_info as needed
+ # (this is to support mistyped URL's without redirecting;
+ # if you want to redirect, use tools.trailing_slash).
+ pi = request.path_info
+ if request.is_index is True:
+ if not pi.endswith('/'):
+ pi = pi + '/'
+ elif request.is_index is False:
+ if pi.endswith('/') and pi != '/':
+ pi = pi[:-1]
+
+ if path == "":
+ path = pi
+ else:
+ path = _urljoin(pi, path)
+
+ if script_name is None:
+ script_name = request.script_name
+ if base is None:
+ base = request.base
+
+ newurl = base + script_name + path + qs
+ else:
+ # No request.app (we're being called outside a request).
+ # We'll have to guess the base from server.* attributes.
+ # This will produce very different results from the above
+ # if you're using vhosts or tools.proxy.
+ if base is None:
+ base = server.base()
+
+ path = (script_name or "") + path
+ newurl = base + path + qs
+
+ if './' in newurl:
+ # Normalize the URL by removing ./ and ../
+ atoms = []
+ for atom in newurl.split('/'):
+ if atom == '.':
+ pass
+ elif atom == '..':
+ atoms.pop()
+ else:
+ atoms.append(atom)
+ newurl = '/'.join(atoms)
+
+ # At this point, we should have a fully-qualified absolute URL.
+
+ if relative is None:
+ relative = getattr(request.app, "relative_urls", False)
+
+ # See http://www.ietf.org/rfc/rfc2396.txt
+ if relative == 'server':
+ # "A relative reference beginning with a single slash character is
+ # termed an absolute-path reference, as defined by <abs_path>..."
+ # This is also sometimes called "server-relative".
+ newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
+ elif relative:
+ # "A relative reference that does not begin with a scheme name
+ # or a slash character is termed a relative-path reference."
+ old = url().split('/')[:-1]
+ new = newurl.split('/')
+ while old and new:
+ a, b = old[0], new[0]
+ if a != b:
+ break
+ old.pop(0)
+ new.pop(0)
+ new = (['..'] * len(old)) + new
+ newurl = '/'.join(new)
+
+ return newurl
+
+
+# import _cpconfig last so it can reference other top-level objects
+from cherrypy import _cpconfig
+# Use _global_conf_alias so quickstart can use 'config' as an arg
+# without shadowing cherrypy.config.
+config = _global_conf_alias = _cpconfig.Config()
+config.defaults = {
+ 'tools.log_tracebacks.on': True,
+ 'tools.log_headers.on': True,
+ 'tools.trailing_slash.on': True,
+ 'tools.encode.on': True
+ }
+config.namespaces["log"] = lambda k, v: setattr(log, k, v)
+config.namespaces["checker"] = lambda k, v: setattr(checker, k, v)
+# Must reset to get our defaults applied.
+config.reset()
+
+from cherrypy import _cpchecker
+checker = _cpchecker.Checker()
+engine.subscribe('start', checker)