我怎样才能将Jinja与Twisted一起使用?

时间:2011-11-04 20:19:51

标签: python orm twisted flask jinja2

我正在计划使用带有Twisted,Storm和Jinja的Python的讨论软件。问题是Jinja不是用于Twisted或异步套接字库,而使用Twisted提供的性能是我不打算使用Flask的原因。

那么,我怎样才能使用Jinja使用Twisted渲染网页?

3 个答案:

答案 0 :(得分:15)

您可以使用Jinja呈现网页,就像在Twisted中使用任何其他Python库一样。你只需要打电话给它。这对Twisted来说可以正常工作,但如果Jinja做了一些阻塞,你可能会遇到性能问题。请注意,可能使用带有Twisted的阻塞库,只需通过deferToThread,或者只是阻止主循环,如果它不是性能问题。所以,我推断你的问题实际上是关于如何在不阻挡的情况下使用Jinja。

Jinja是一个模板库,这意味着它会读取模板,在模板上调用一些视图逻辑,并编写一些HTML输出。所以有三件事可以阻止:

  1. 阅读模板,
  2. 撰写结果。
  3. 运行视图逻辑(您的应用程序代码),
  4. 我不知道Jinja,所以我不确切知道这些东西的API是如何构建的,我不知道该怎么做,但我的猜测是那部分很容易;所以,我将给你一个关于第三方模板库和Twisted的一般答案。

    所以我会解决这些问题中的每一个,尽管不是很合适:

    1。阅读模板

    这里最合理的做法是不关心它。阅读模板可能非常快。这些是经常访问的小文件,您的操作系统几乎肯定会保留在其文件系统缓存中。除非你把它们放在NFS上,否则你不太可能会阻止阅读它们。如果您对应用程序进行概要分析并发现这是一个问题 - 因为,比方说,您的磁盘速度非常慢或远程文件系统 - 只需在启动时将模板读入cStringIO或类似内容并将其提供给jinja之后。

    3。编写响应

    网页并不是那么大,而且Twisted没有提供阻塞API来写入套接字。相反,它提供了一个API,它只是将整个结果缓冲到内存中,直到可以写出来。我的建议是在这里做与读取模板基本相同的事情:除非你有非常大的输出,否则在响应被送到客户端时可能会烧掉一点RAM。

    2。运行View Logic

    这是您最容易遇到问题的区域。 Jinja可能无法处理Deferred的结果。但实际上,实际上并不是Jinja直接给你带来问题:它是Storm。当您访问某些属性时,Storm希望能够阻止,进行数据库查询。与数据库块交谈,这是大多数Web应用程序中阻止I / O的最有意义的来源。所以你需要决定如何处理这个问题。您有几个选择:

    1. 只需在主线程中执行即可,不用担心。也许您的应用程序适用于10人的工作组,您的数据库是本地的。当然,你的I / O会阻塞,但如果它仍然符合其性能要求,谁在乎呢?并非每个应用程序都必须缩放到月球和背部。
    2. deferToThread调用(或类似)中预先从风暴中获取所有内容,并确保Jinja仅访问内存中的对象。这样,您可以在主线程中运行渲染器,在执行数据库I / O的Deferred的回调中运行。如果你只是访问内存中的对象,你的渲染器可能还需要一段时间,但这没关系。这个问题促使我post an article to my blog about the distinction between "blocking" and "running"作为草案搁置了很长一段时间;你可能想读它。
    3. 在线程或子进程中完成整个渲染,并将其视为程序的阻塞组件。这会失去使用Twisted的一些好处,但它仍然是一个非常可行的策略,可以集成阻塞的Jinja / Storm组件和非阻塞的纯Twisted组件,即实际的聊天消息中继部分。
    4. 如果这些选项都不适用于您,则从版本11.0开始Twisted has included a templating library that does support Deferreds。您可以考虑使用twisted.web.template替代Jinja。

答案 1 :(得分:2)

以下是如何实现解决方案3的示例示例,具有基本的延迟返回功能支持:

from jinja2 import Template
from twisted.internet import threads, reactor, defer

def inThread(f):
    def new_f(*args, **kwargs):
        return threads.deferToThread(f, *args, **kwargs)
    return new_f


def fromThread(f):
    def new_f(*args, **kwargs):
        return threads.blockingCallFromThread(reactor, lambda: defer.maybeDeferred(f, *args, **kwargs))
    return new_f


class DeferredTemplate(Template):
    def render(self, **kw):
        hooked_kw = {}
        for k, v in kw.iteritems():
            # decorate the callable so that they are run in the main thread
            if callable(v):
                v = fromThread(v)
            hooked_kw[k] = v
        return inThread(Template.render)(self, **hooked_kw)

from twisted.trial import unittest
class TestJinjaDeferred(unittest.TestCase):
    @defer.inlineCallbacks
    def test_basic(self):
        def getHello():
            d = defer.Deferred()
            reactor.callLater(0.0, lambda: d.callback("Hello"))
            return d

        def getWorldSync():
            return "world"

        template = DeferredTemplate("{{ getHello() }} {{ getWorldSync() }}")
        res = yield template.render(getHello=getHello, getWorldSync=getWorldSync)
        self.assertEqual(u"Hello world", res)

答案 2 :(得分:0)

我认为Tornado模板系统(就像Jinja2模板一样,因为它类似于Django ......)可以在没有龙卷风的情况下使用:

  

我们试图清理代码库以减少模块之间的相互依赖性,因此您(理论上)应该能够在项目中独立使用任何模块,而无需使用整个软件包。

Tornado Templates