Storing data into the CherryPy thread

102 views
Skip to first unread message

Christopher Grebs

unread,
Aug 12, 2007, 6:14:40 AM8/12/07
to cherrypy-users
Hi all together!

I was building a WSGI application that handles requests and
application data in `threading._local` objects.

The API was very nice and I loved it to work with.

Now I'm using CherryPy and just want to ask if I can use the CherryPy
thread to store internal application functions that stores and returns
data from that thread as CherryPy does with the request object.

I saw a `thread_data` object in the cherrypy __init__.py. Is that the
object where I can store my data?


Regards,
Christopher Grebs

Robert Brewer

unread,
Aug 12, 2007, 11:08:40 AM8/12/07
to cherryp...@googlegroups.com
Christopher Grebs wrote:
> Now I'm using CherryPy and just want to ask if I can
> use the CherryPy thread to store internal application
> functions that stores and returns data from that thread
> as CherryPy does with the request object.
>
> I saw a `thread_data` object in the cherrypy __init__.py.
> Is that the object where I can store my data?

Absoluteley. Stick any data you want into thread_data.
More info at http://www.cherrypy.org/wiki/ApplicationReference


Robert Brewer
fuma...@aminus.org

winmail.dat

Christopher Grebs

unread,
Aug 12, 2007, 12:03:00 PM8/12/07
to cherrypy-users
Wonderful.

But if there is still one problem.

My code looks like the following:


import new, sys

import wwws

from cherrypy import thread_data
from cherrypy import request, response, tools

apis = {}

# it's better to work with only one object
for obj in (request, response, request.app,
request.dispatch, tools.sessions):
if obj:
if hasattr(obj, '__attrname__'):
# ThreadLocalProxy
apis[obj.__attrname__] = obj
continue
apis[obj.__class__.__name__] = obj
thread_data.__dict__.update(apis)

def to_api(func):
apis[func.__name__] = func
func.is_api = True
return func


#### ***** Our APIs ***** ######

# template environment
from jinja import Environment
from jinja.loaders import FileSystemLoader

thread_data.template_env = Environment(loader=FileSystemLoader(
searchpath='./templates',
use_memcache=False, #:XXX: enable it in production mode
cache_folder=None, #:XXX: enable it in production mode
auto_reload=True, #:XXX: disable it in production mode
cache_salt='wwws_webshox'
))

@to_api
def get_app():
return getattr(thread_data, 'app', None)

@to_api
def get_active_session():
return getattr(thread_data, 'session', None)

@to_api
def url_for(endpoint, _external=False, **args):
return thread_data.dispatch.url_map.build(endpoint, args,
_external)

@to_api
def render_template(template_name, **context):
"""Renders a template."""
tmpl = thread_data.template_env.get_template(template_name)
return tmpl.render(context)


__all__ = list(x for x in locals() if x in apis)


Now if I call `render_template` from my view object (that returns the
output to the client) there is no `template_env` object attached to
the `thread_data`. And of course all other objects I've stored in the
thread_data aren't stored in that object the `render_template`
function gets.

Any idea how to fix that problem?

Christopher

> fuman...@aminus.org
>
> winmail.dat
> 3KDownload

Robert Brewer

unread,
Aug 12, 2007, 5:07:28 PM8/12/07
to cherryp...@googlegroups.com
Christopher Grebs wrote:
> Now if I call `render_template` from my view object
> (that returns the output to the client) there is no
> `template_env` object attached to the `thread_data`.
> And of course all other objects I've stored in the
> thread_data aren't stored in that object the
> `render_template` function gets.
>
> Any idea how to fix that problem?

Yeah; thread_data is a threadlocal object, which means each thread gets its own instance of that object. You've only attached template_env to the main thread's instance of thread_data. If you really want one instance of template_env per thread (and looking at your code, I don't see why you do), you need to register a callback for that which will be invoked every time a new thread is started. For the trunk version of CP, that looks something like:

def set_template(threadid):
cherrypy.thread_data.template_env = Environment(loader=FileSystemLoader(


searchpath='./templates',
use_memcache=False, #:XXX: enable it in production mode
cache_folder=None, #:XXX: enable it in production mode
auto_reload=True, #:XXX: disable it in production mode
cache_salt='wwws_webshox'
))

cherrypy.engine.subscribe('start_thread', set_template)

But somehow I don't think that's what you want. Why not have a single template_env for the entire app (instead of one per thread)? Note that request, response, etc are also all threadlocals, so when you cache them, you're caching the main thread's instance of each of them. You need to delay looking up those objects, probably with a function that looks them up lazily. I'd rewrite your code thusly:

import new, sys
import wwws

import cherrypy


__all__ = []

def to_api(func):
__all__.append(func.__name__)


func.is_api = True
return func

#### ***** Our APIs ***** ######

# template environment
from jinja import Environment
from jinja.loaders import FileSystemLoader

template_env = Environment(loader=FileSystemLoader(


searchpath='./templates',
use_memcache=False, #:XXX: enable it in production mode
cache_folder=None, #:XXX: enable it in production mode
auto_reload=True, #:XXX: disable it in production mode
cache_salt='wwws_webshox'
))

@to_api
def get_app():
return cherrypy.request.app

@to_api
def get_active_session():
return cherrypy.session

@to_api
def url_for(endpoint, _external=False, **args):

args = "&".join(["%s=%s" % (k, v) for k, v in args.iteritems()])
return cherrypy.url(path=endpoint, qs=args, relative=not _external)

@to_api
def render_template(template_name, **context):
"""Renders a template."""

tmpl = template_env.get_template(template_name)
return tmpl.render(context)


Robert Brewer
fuma...@aminus.org

winmail.dat

Christopher Grebs

unread,
Aug 13, 2007, 2:50:12 AM8/13/07
to cherrypy-users
WoW, that's looking nice.

Thanks for your response and your rewrite of my code.

I already thought about some rewrites because it would be better to
apply the request object to every view (I'm using my own Dispatcher)
and attach all my own required objects to that.

Somehow I thought that it could be nice to have one API object that
must be imported to access all internal objects (including the actual
request and some others).
The '' cherrypy.engine.subscribe() '' function looks nice I and I'll
have a closer look to it.

best regards,
Christopher Grebs

> fuman...@aminus.org
>
> winmail.dat
> 5KHerunterladen

Reply all
Reply to author
Forward
0 new messages