【问题标题】:CherryPy with Cheetah as plugin + tool - blank pagesCherryPy 与 Cheetah 作为插件 + 工具 - 空白页
【发布时间】:2014-08-26 01:24:17
【问题描述】:

CherryPy 不断返回空白页或我在控制器中返回的值。我重写了一个确实有效的 django 和 jinja2 版本,显然这个版本与前面提到的几乎相同。

我在工具位中做了一些 pprint,它确实用解析的 html 填充 request.body,但在控制器中设置 pass 时不输出它。如果我在以简单“用户”形式显示的控制器中返回 {'user':True}。

通过一些在线示例和 SickBeard 的代码,我得出以下结论:

控制器:

class RootController(object):
    @cherrypy.expose
    @cherrypy.tools.render(template="page/home.html")
    def index(self):
        pass

工具:

class CheetahTool(cherrypy.Tool):
    def __init__(self):
        cherrypy.Tool.__init__(self, 'on_start_resource',
                               self._render,
                               priority=30)

    def _render(self, template=None, debug=False):
        if cherrypy.response.status > 399:
            return

        # retrieve the data returned by the handler
        data = cherrypy.response.body or {}
        template = cherrypy.engine.publish("lookup-template", template).pop()

        if template and isinstance(data, dict):
            for k,v in data:
                template.__setattr__(k, v)

            # dump the template using the dictionary
            if debug:
                try:
                    cherrypy.response.body = unicode(template).encode('utf-8', 'xmlcharrefreplace')
                except Exception as e:
                    from pprint import pprint
                    pprint(e.message)
            else:
                cherrypy.response.body = template.respond()

插件:

class PageTemplate(Template):
    """
    Thank you SickBeard
    """
    def __init__(self, base_dir, template, *args, **KWs):
        KWs['file'] = os.path.join(base_dir, template)
        super(PageTemplate, self).__init__(*args, **KWs)
        application = cherrypy.tree.apps['']
        config = application.config 
        self.sbRoot = base_dir
        self.sbHttpPort = config['global']['server.socket_port']
        self.sbHttpsPort = self.sbHttpPort
        self.sbHttpsEnabled = False
        if cherrypy.request.headers['Host'][0] == '[':
            self.sbHost = re.match("^\[.*\]", cherrypy.request.headers['Host'], re.X|re.M|re.S).group(0)
        else:
            self.sbHost = re.match("^[^:]+", cherrypy.request.headers['Host'], re.X|re.M|re.S).group(0)

        if "X-Forwarded-Host" in cherrypy.request.headers:
            self.sbHost = cherrypy.request.headers['X-Forwarded-Host']
        if "X-Forwarded-Port" in cherrypy.request.headers:
            self.sbHttpPort = cherrypy.request.headers['X-Forwarded-Port']
            self.sbHttpsPort = self.sbHttpPort
        if "X-Forwarded-Proto" in cherrypy.request.headers:
            self.sbHttpsEnabled = True if cherrypy.request.headers['X-Forwarded-Proto'] == 'https' else False

        self.sbPID = str(aquapi.PID)
        self.menu = [
            { 'title': 'Home',            'key': 'home'           },
            { 'title': 'Users',           'key': 'users'          },
            { 'title': 'Config',          'key': 'config'         },
        ]

    def render(self):
        return unicode(self).encode('utf-8', 'xmlcharrefreplace')


class CheetahTemplatePlugin(plugins.SimplePlugin):
    def __init__(self, bus, base_dir=None, base_cache_dir=None, 
                 collection_size=50, encoding='utf-8'):
        plugins.SimplePlugin.__init__(self, bus)
        self.base_dir = base_dir
        self.base_cache_dir = base_cache_dir or tempfile.gettempdir()
        self.encoding = encoding
        self.collection_size = collection_size

    def start(self):
        self.bus.log('Setting up Cheetah resources')
        self.bus.subscribe("lookup-template", self.get_template)

    def stop(self):
        self.bus.log('Freeing up Cheetah resources')
        self.bus.unsubscribe("lookup-template", self.get_template)
        self.lookup = None

    def get_template(self, name):
        """
        Returns Cheetah's template by name.
        """
        return PageTemplate(self.base_dir, name)

初始化:

        # Template engine tool
        from aquapi.web.tools.template import CheetahTool
        cherrypy.tools.render = CheetahTool()

        # Tool to load the logged in user or redirect
        # the client to the login page
        from aquapi.web.tools.user import UserTool
        cherrypy.tools.user = UserTool()


        from aquapi.web.controllers import RootController 
        webapp = RootController()

        # Let's mount the application so that CherryPy can serve it
        app = cherrypy.tree.mount(webapp, '/', os.path.join(self.base_dir, "app.cfg"))

        # Template engine plugin
        from aquapi.web.plugin.template import CheetahTemplatePlugin
        engine.cheetah = CheetahTemplatePlugin(engine, 
                                        os.path.join(self.base_dir, 'aquapi/web/templates'),
                                        os.path.join(self.base_dir, 'cache'))
        engine.cheetah.subscribe()

【问题讨论】:

    标签: python django webserver cherrypy cheetah


    【解决方案1】:

    总的来说,对我来说,您的 sn-ps 中发生了某种过度设计。 CherryPy 插件通常用于系统任务(例如,在引擎启动时放置 PID 文件,在停止时将其删除)或异步任务(例如,在单独的线程中发送电子邮件)。模板渲染显然与请求处理同步发生,因此我看不出从 CherryPy 工具中提取此逻辑的意义。 CherryPy 中有一个类cherrypy._cptools.HandlerWrapperTool,它演示了包装处理程序返回值的建议方法。

    我从未使用过 Cheetah,所以我的示例是基于 Jinja2 的。您只需更改模板引擎实例(为 Cheetah)并更正其调用。其余的都是一样的。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    
    import os
    import types
    
    import cherrypy
    import jinja2
    
    
    path   = os.path.abspath(os.path.dirname(__file__))
    config = {
      'global' : {
        'server.socket_host' : '127.0.0.1',
        'server.socket_port' : 8080,
        'server.thread_pool' : 4
      }
    }
    
    
    class TemplateTool(cherrypy.Tool):
    
      _engine = None
      '''Jinja environment instance'''
    
    
      def __init__(self):
        viewLoader   = jinja2.FileSystemLoader(os.path.join(path, 'view'))
        self._engine = jinja2.Environment(loader = viewLoader)
    
        cherrypy.Tool.__init__(self, 'before_handler', self.render)
    
      def __call__(self, *args, **kwargs):
        if args and isinstance(args[0], (types.FunctionType, types.MethodType)):
          # @template
          args[0].exposed = True
          return cherrypy.Tool.__call__(self, **kwargs)(args[0])
        else:
          # @template()
          def wrap(f):
            f.exposed = True
            return cherrypy.Tool.__call__(self, *args, **kwargs)(f)
          return wrap
    
      def render(self, name = None):
        cherrypy.request.config['template'] = name
    
        handler = cherrypy.serving.request.handler
        def wrap(*args, **kwargs):
          return self._render(handler, *args, **kwargs)
        cherrypy.serving.request.handler = wrap
    
      def _render(self, handler, *args, **kwargs):
        template = cherrypy.request.config['template']
        if not template:
          parts = []
          if hasattr(handler.callable, '__self__'):
            parts.append(handler.callable.__self__.__class__.__name__.lower())
          if hasattr(handler.callable, '__name__'):
            parts.append(handler.callable.__name__.lower())
          template = u'/'.join(parts)
    
        data     = handler(*args, **kwargs) or {}
        renderer = self._engine.get_template(u'{0}.html'.format(template))
    
        return renderer.render(**data)
    
    
    cherrypy.tools.template = TemplateTool()
    
    
    class App:
    
      @cherrypy.expose
      def index(self):
        '''No renderer applied, CherryPy outputs dict keys'''
        return {'user': 123}
    
      @cherrypy.tools.template
      def auto(self):
        return {'user': 123}
    
      @cherrypy.tools.template(name = 'app/auto')
      def manual(self):
        return {'user': 234}
    
    
    if __name__ == '__main__':
      cherrypy.quickstart(App(), '/', config)
    

    沿着python文件,创建目录view/app并将以下内容放入名为auto.html的文件中。

    <!DOCTYPE html>
    <html>
      <head>
        <meta http-equiv='content-type' content='text/html; charset=utf-8' />
        <title>Test</title>
      </head>
      <body>
        <p>User: <em>{{ user }}</em></p>
      </body>
    </html>
    

    关于TemplateTool 的一些说明。首先,您可以通过两种方式将其用作装饰器:不进行调用,以及使用模板名称参数进行调用。您可以将该工具用作配置中的任何其他 CherryPy 工具(例如,将所有控制器方法用于渲染模板)。其次,遵循convention-over-configuration的原则,工具在没有提供模板名称时将使用classname/methodname.html。第三,装饰器暴露了方法,所以不需要在上面加上@cherrypy.expose

    【讨论】:

    • 会调查此事并尽快回复您!谢谢
    猜你喜欢
    • 1970-01-01
    • 2012-06-04
    • 2019-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-24
    相关资源
    最近更新 更多