Wiki

Clone wiki

CherryPy / UpgradeTo30

[This document is complete up to rev 1460.]

How to upgrade to CherryPy 3.0

This page discusses changes between CherryPy 2.2 (February 2006) and 3.0 (December 2006) which might require you to modify your code when you upgrade. Bugfixes aren't listed here.

Configuration

In CherryPy 2.2, configuration entries for the server, the application, and the request process were all mixed together. In CherryPy 3.0, they have been separated by two mechanisms. First, each mounted application now has its own config (which is usually provided in the tree.mount call); this holds per-application config (in a "/" section) and per-path config. Second, cherrypy.config is now for global entries only (things which affect all applications). Third, all config names are now of the form "namespace.key", where "namespace" is one of the following: "engine", "hooks", "log", "request", "response", "server", "tools". Entries from each namespace may be placed in the global, application root ("/") or per-path config, or a combination:

ScopeGlobalApplication RootApp Path
engineX
hooksXXX
logXX
requestXXX
responseXXX
serverX
toolsXXX
wsgiX

In addition to specifying config entries in a config file, you may now attach config data directly to page handlers or their containers via the _cp_config attribute (a dict). This attribute (in conjunction with hooks and tools, see below) replaces the old _cp_filters, _cp_on_error, and _cp_on_http_error attributes. Note that _cp_config entries apply to each ''handler'' (or branch in the CherryPy tree), whereas entries in config files apply based on the ''URI'', which is not always mapped 1:1 with handlers.

You may now instantiate objects in config. See [1045].

Hooks replace Filters

By far the biggest change is the transformation of filters to hooks and tools. All use of the word "filter" has been purged to make it easier for you to find places in your code that need upgrading.

Every Request object now possesses a "hooks" attribute, a _cprequest.HookMap object. A HookMap works like a dict, where the keys are "hook points" and the values are lists of Hooks:

  • The "hook points" are the old filter method names: 'on_start_resource', 'before_request_body', 'before_handler', 'before_finalize', 'on_end_resource', 'on_end_request', 'before_error_response', and 'after_error_response'.
  • A Hook is a callback function, wrapped up into a Hook object. Each Hook instance also possesses a 'failsafe' attribute, a bool. If True, the callback is guaranteed to run, even if other Hooks at the same hook point fail. It also possesses a 'priority', a number from 0 to 100. Lower numbers run first.

The HookMap has an "attach" method to make this process easier. Call it like this: cherrypy.request.hooks.attach(point, callback, failsafe=None, priority=None, kwargs). Any code can do this, and can do it on the fly! See the new caching tool for an example; if the request is served from cache inside the "before_handler" hook, then the logic which would cache the page handler output is never attached, and therefore never invoked.

Tools package up Hooks

Tools provide a standard way to "wrap up" a set of hooks and other behavior into a single component. Tools provide:

  • '''Standardized config''' All registered tools are automatically configurable: "tools.mytool.on = True" turns on the tool, and all "tools.mytool.xxx = yyy" entries are automatically sent to the hook as "mytool(xxx=yyy)" keyword arguments.
  • '''Common invocation''' The cherrypy.Tool base class automatically makes your callback usable as a decorator: "@tools.mytool(xxx=yyy)". The HandlerTool base class provides a "handler" method, so you can write, for example, "class Root: files = tools.staticdir.handler(section='/files', dir='static', root=absDir)".
  • '''Easy reflection''' When you wrap your callback up in a tool, all of its function arguments become attributes of the Tool, so that users of the Tool can see the arguments easily. If your IDE provides code-completion, just "import tools" while writing your config; the IDE can pop up a calltip when you've typed, say, "tools.gzip. ", showing you which arguments the gzip tool takes.

Rewriting filters as tools is a fairly straightforward operation. If your filter only has one method, pull it out of the class and rename it to say what it *really* does (like "gzip" or "staticfile"). Then, make a Tool for it like so: cherrypy.tools.mytool = cherrypy.Tool('before_handler', mymodule.myfunc). If you have more complicated needs, you can subclass cherrypy.Tool and override the _setup method. See cherrypy._cptools for some examples.

Starting, mounting, and stopping

Starting with [1092], the Server class (which controls an HTTP server) is no longer a subclass of Engine (the app server). This means that each can and must be started and stopped independently. In addition, you must now use cherrypy.tree.mount instead of cherrypy.root. In other words, instead of writing:

cherrypy.root = Root()
cherrypy.server.start()

you must now write:

cherrypy.tree.mount(Root())
cherrypy.server.quickstart()
cherrypy.engine.start()

However, rather than make 3 separate calls to tree.mount, server.quickstart, and engine.start, there's a new cherrypy.quickstart function. To do the same thing as above, you can simply write:

cherrypy.quickstart(Root())

Here's the function in its entirety:

def quickstart(root, script_name="", config=None):
    """Mount the given app, start the engine and builtin server, then block."""
    if config:
        cherrypy.config.update(config)
    tree.mount(root, script_name, config)
    server.quickstart()
    engine.start()
    engine.block()

If you used the old init_only argument to server.start, you should now write this:

cherrypy.tree.mount(Root())
cherrypy.server.quickstart()
cherrypy.engine.start(blocking=False)

Nested Generator Responses

If you were using this extremely rare feature of CherryPy 2, you need to turn on a Tool now. A 'nested generator' is a page handler that is a (or returns a) generator that itself yields generators. In CherryPy 2, support for these was automatic. In CherryPy 3, just add tools.flatten.on = True to your config for such handlers.

Module changes

1. The cherrypy/lib/filter folder, which was emptied in 2.2, has been removed. 2. The cherrypy/filters folder has been replaced by the _cptools module. 3. The deprecated cherrypy/lib/form and cherrypy/lib/defaultformmask modules have been removed. 4. _cphttptools is now called _cprequest. 5. cherrypy/lib/httptools is now cherrypy/lib/http. 6. cherrypy.config was a module, and is now an instance of the new cherrypy._cpconfig.Config. 7. All of the test modules have been renamed, dropping the word "filter". 8. _cpwsgiserver is now called wsgiserver (see [1460]).

Name changes

'''NOTE:''' All of the name changes which were optional in !CherryPy 2.2 are now mandatory (unless changed further below). If you did not upgrade those names when [wiki:UpgradeTo2.2 moving from 2.1 to 2.2], you should do so now and then come back here.

Beginning with CP 3.0 beta 2 [1373], there is a new cherrypy._cpconfig.check_compatibility(config) function to help you migrate. Pass it a config file, filename, or dict, and it will warn you of obsolete entries.

'''Filter changes'''

'''Old name''''''New name'''
baseurl_filtertools.proxy
cache_filtertools.caching
cache_filter.cacheClasstools.caching.cache_class
decoding_filtertools.decode
encoding_filtertools.encode
gzip_filtertoos.gzip
log_debug_info_filter''no equivalent''
nsgmls_filtertools.nsgmls
response_headers_filtertools.response_headers
session_authenticate_filtertools.session_auth
session_authenticate_filter.load_user_by_usernametools.session_auth.on_check
session_authenticate_filter.check_login_and_passwordtools.session_auth.check_username_and_password
session_authenticate_filter.not_logged_intools.session_auth.anonymous
session_auth form param: 'login'form param: 'username'
session_filtertools.sessions
session_filter.clean_up_delaytools.sessions.clean_freq
session_filter.cookie_nametools.sessions.name
session_filter.cookie_domaintools.sessions.domain
session_filter.cookie_pathtools.sessions.path
session_filter.cookie_securetools.sessions.secure
session_filter.cookie_path_from_headertools.sessions.path_header
static_filter.dirtools.staticdir.dir
static_filter.filetools.staticfile.filename
tidy_filtertools.tidy
virtual_host_filterrequest.dispatch: cherrypy.dispatch.VirtualHost({domain: path})
wsgiappfiltertools.wsgiapp
xmlrpc_filterrequest.dispatch: cherrypy.dispatch.XMLRPCDispatcher()

'''Config changes'''

'''Old name''''''New name'''
server.default_content_typetools.response_headers.headers=[('Content-Type', 'text/html')]
log_access_filelog.access_file
log_config_options''no equivalent''
log_filelog.error_file
log_file_not_found''no equivalent''
log_request_headerstools.log_headers.on = True
log_to_screenlog.screen
show_tracebacksrequest.show_tracebacks
throw_errorsrequest.throw_errors
profiler.oncherrypy.tree.mount(profiler.make_app(cherrypy.Application(Root())))
environment = 'development'''on by default''
environment = 'embedded'environment = 'production'
autoreload.onengine.autoreload_on
autoreload.frequencyengine.autoreload_frequency
autoreload.matchengine.autoreload_match

'''Other name changes'''

'''Old name''''''New name'''
server.on_start_server_listengine.on_start_engine_list
server.on_stop_server_listengine.on_stop_engine_list
server.on_start_thread_listengine.on_start_thread_list
server.on_stop_thread_listengine.on_stop_thread_list
cherrypy.server.httpservercherrypy.server.httpservers.keys()[0]
cherrypy.server.startcherrypy.server.quickstart; cherrypy.engine.start
cheryrpy.server.request(clientAddress, remoteHost, scheme='http')cherrypy.engine.request(local_host, remote_host, scheme="http", server_protocol="HTTP/1.1")
cherrypy.server.start_with_callback(func, args=None, kwargs=None, server_class=<object object>, serverClass=None)cherrypy.server.quickstart(server=None); cherrypy.engine.start_with_callback(func, args=None, kwargs=None)
request.object_pathrequest.path_info
''no equivalent''request.script_name
request.path''strict'': request.script_name + request.path_infobrBut you should probably use cherrypy.url() or just request.path_info instead.
request.browser_urlcherrypy.url()
request.remote_addrrequest.remote.ip
request.remote_portrequest.remote.port
request.remote_hostrequest.remote.name
request.mapPathToObjectrequest.dispatch
request.simple_cookierequest.cookie
response.simple_cookieresponse.cookie
HeaderMap.sorted_listHeaderMap.output
cherrypy.response.versioncherrypy.request.protocol
_cp_log_messagelog.error
_cp_log_accesslog.access
_cp_log_messagelog.error
_cputil.logtimecherrypy.log.time
_cp_on_http_error''no equivalent''
_cp_on_errorrequest.error_response
cherrypy.servingcherrypy._serving
cherrypy.lowercase_api''not needed''
cherrypy.InternalError''no equivalent''
cherrypy.NotReadycherrypy.HTTPError(503)
cherrypy.WrongConfigValue''no equivalent''
cherrypy.RequestHandledrequest.handler = None
cptools.decorate''no equivalent''
cptools.decorateAll''no equivalent''
cptools.ExposeItems''no equivalent''
before_mainbefore_handler
filters.init''not needed''
filters.applyFiltersrequest.hooks.run
filters.input_filters/output_filtersrequest.hooks.attach
config.getAll''no equivalent''
config.dict_from_config_file_cpconfig._Parser.dict_from_file
cherrypy.rootcherrypy.request.app.root
_cptree.Root/Branch''no equivalent''
cherrypy.tree.mount(root, conf={})cherrypy.tree.mount(root, conf'''ig'''={})
cherrypy.tree.mount_pointcherrypy.tree.script_name
cherrypy.tree.mount_pointscherrypy.tree.apps
_cputil.bareError_cperror.bare_error
_cputil.formatExc_cperror.format_exc
lib.cptools.serveFile(path, contentType, disposition, name)lib.static.serve_file(path, content_type, disposition, name)
_cpwsgi.WSGIServer_cpwsgi.CPWSGIServer
cherrypy.session["_id"]cherrypy.session.id

Updated