如何将 Jinja 与 Twisted 结合使用?

发布于 2024-12-14 01:49:06 字数 166 浏览 2 评论 0原文

我正在计划使用 Python 与 Twisted、Storm 和 Jinja 一起开发一个讨论软件。问题是 Jinja 不是为 Twisted 或异步套接字库而设计的,并且使用 Twisted 提供的性能是我不打算使用 Flask 的原因。

那么,如何使用 Jinja 来渲染 Twisted 网页呢?

I'm planning up a discussion software using Python with Twisted, Storm, and Jinja. The problem is that Jinja was not made for Twisted or asynchronous socket libraries, and the performance provided by using Twisted is why I don't plan on using Flask.

So, how can I have Twisted render webpages using Jinja?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雅心素梦 2024-12-21 01:49:07

您可以使用 Jinja 渲染网页,就像在 Twisted 中使用任何其他 Python 库一样。你只需调用它即可。这对于 Twisted 来说可以很好地工作,尽管如果 Jinja 执行了某些阻塞操作,您可能会遇到性能问题。请注意,可以将阻塞库与 Twisted 一起使用,可以通过 deferToThread 或仅阻塞主循环(如果不是性能问题)。所以,我推断你的问题实际上是关于如何在不阻塞的情况下使用 Jinja。

Jinja 是一个模板库,这意味着它读取模板,调用模板上的一些视图逻辑,并写入一些 HTML 输出。因此,有 3 件事可能会阻塞:

  1. 读取模板、
  2. 写入结果。
  3. 运行视图逻辑(您的应用程序代码),

我不了解 Jinja,所以我不确切地知道每个事物的 API 是如何构造的,我无法告诉您该怎么做,但我的猜测是那部分很简单;因此,我将为您提供有关第 3 方模板库和 Twisted 的一般性答案。

因此,我将逐一解决这些问题,尽管顺序不完全:

1. 阅读模板

实际上,这里最合理的做法是不关心它。阅读模板可能真的很快。这些是经常访问的小文件,您的操作系统几乎肯定会将其保存在其文件系统缓存中。除非您正在做一些疯狂的事情,例如将它们放在 NFS 上,否则您不太可能会阻止读取它们。如果您分析您的应用程序并发现这是一个问题 - 因为,比方说,您的磁盘或远程文件系统非常慢 - 只需在启动时将模板读入 cStringIO 或类似的东西并提供之后就到jinja了。

3. 编写响应

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

2. 运行视图逻辑

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

  1. 只需在主线程中执行即可,不用担心。也许您的应用程序适用于 10 人的工作组,并且您的数据库位于本地。当然,您的 I/O 会阻塞,但如果它仍然满足性能要求,谁在乎呢?并非每个应用程序都必须扩展到月球并返回。
  2. 在 deferToThread 调用(或类似调用)中预取 Storm 中的所有内容,并确保 Jinja 仅访问内存中的对象。这样,您就可以在主线程中、在执行数据库 I/O 的 Deferred 回调中运行渲染器。如果您只访问内存中的对象,您的渲染器可能仍然需要一些时间,但这没关系。这个问题促使我在我的博客上发布一篇关于“阻塞”和“运行”之间区别的文章 作为草稿已经挂了很长一段时间了;你可能想去读一下。
  3. 在线程或子进程中进行整个渲染,并将其视为程序的阻塞组件。这失去了使用 Twisted 的一些好处,但集成阻塞 Jinja/Storm 组件和非阻塞纯 Twisted 组件(实际的聊天消息中继部分)仍然是一个完全可行的策略。

如果这些选项都不适合您,Twisted 包含了一个模板库,可以 从 11.0 版本开始支持Deferreds。您可以考虑使用 twisted.web.template 作为 Jinja 的替代品。

You can render web pages using Jinja the same way you would use any other Python library in Twisted. You just call into it. This will work fine with Twisted, although you may run into performance issues if Jinja does something blocking. Note that it is possible to use blocking libraries with Twisted just fine, either via deferToThread or just blocking the mainloop if it's not a performance problem. So, I am inferring that your question is really about how to use Jinja without blocking.

Jinja is a templating library, which means that it reads a template, invokes some view logic on the template, and writes some HTML output. So there are 3 things that can block:

  1. reading the template,
  2. writing the result.
  3. running the view logic (your application code),

I don't know Jinja, so I don't know exactly how the APIs for each of these things is structured and I can't tell you what to do, but my guess is that that part's easy; so, I'm going to give you a general answer about 3rd-party templating libraries and Twisted.

So I'll address each of these concerns, although not quite in order:

1. Reading the Template

Really the most reasonable thing to do here is to not care about it. Reading the template is probably really fast. These are frequently-accessed, tiny files, which your operating system is almost certainly keeping in its filesystem cache. It's unlikely that you're ever going to block on reading them unless you're doing something nuts like putting them on NFS. If you profile your application and find that this is a problem – because, let's say, you have extremely slow disks or a remote filesystem – just read the template into a cStringIO or something similar at startup time and feed it to jinja after that.

3. Writing the Response

Web pages are not all that big, and Twisted doesn't provide a blocking API to write to sockets. Instead, it offers an API which just buffers the whole result in memory until it can be written out. My suggestion is to do basically the same thing here as with reading the template: unless you have seriously huge output, it's probably fine to burn up a little bit of RAM while the response is being fed to the client.

2. Running your View Logic

This is the area where you are most likely to run into problems. Jinja probably doesn't handle the results of Deferreds. But actually, it's not actually Jinja which is going to give you problems directly: it's Storm. Storm expects to be able to block, to make database queries, when you access certain attributes. Talking to the database blocks, and this is the most signifiant source of blocking I/O in most web applications. So you need to decide how you're going to deal with that. You have a few options:

  1. Just do it in the main thread and don't worry about it. Maybe your application is for a workgroup of 10 people and your database is local. Sure, your I/O is going to block, but if it still meets its performance requirements, who cares? Not every application has to scale to the moon and back.
  2. Pre-fetch everything from storm in a deferToThread call (or similar) and make sure Jinja is only accessing objects in memory. This way you can run your renderers in your main thread, in a callback on a Deferred that was doing database I/O. If you're only accessing objects in memory, your renderer still might take a little while, but that's fine. This question prompted me to post an article to my blog about the distinction between "blocking" and "running" which had been hanging out as a draft for quite a while; you may want to go read it.
  3. Do your whole render in a thread or subprocess, and treat it as a blocking component of your program. This loses some of the benefits of using Twisted, but it's still a perfectly viable strategy to integrate a blocking Jinja/Storm component and a non-blocking pure-Twisted component, the actual chat-message relaying part.

If none of these options work for you, Twisted has included a templating library that does support Deferreds since version 11.0. You may consider using twisted.web.template as an alternative to Jinja.

故事灯 2024-12-21 01:49:07

以下是有关如何实现解决方案 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)

Here is sample example on how to implement solution 3, with basic deferred returning function support:

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)
り繁华旳梦境 2024-12-21 01:49:07

我认为Tornado模板系统(它就像Jinja2模板,因为它是一个类似Django的...)可以在没有tornado本身的情况下使用:

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

龙卷风模板

i think Tornado template system (it's like the Jinja2 template since it's a Django-like...) can be used without the tornado itself:

We attempted to clean up the code base to reduce interdependencies between modules, so you should (theoretically) be able to use any of the modules independently in your project without using the whole package.

Tornado Templates

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文