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
Absoluteley. Stick any data you want into thread_data.
More info at http://www.cherrypy.org/wiki/ApplicationReference
Robert Brewer
fuma...@aminus.org
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
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
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