Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体


10 questions online user: 8

1
votes
answers
49 views
+10

Apache中反向代理的無限超時

我在阿帕奇背後運行龍捲風。我已經創建了代理服務器。

ProxyRequests On
ProxyPass /chat/ http://localhost:8888/chat/

這段代碼效果很好,並將我的所有請求傳遞給龍捲風並將響應返回給客戶端。

現在,我正在使用龍捲風進行長時間的民意調查。一些在短時間內完成的請求說不到1分鐘,這個反向代理工作正常。但是某些長輪詢請求會產生502代理錯誤。此代理錯誤的原因是Apache可以持有長輪詢請求僅一分鐘(默認情況下)。它關閉請求,因此收到代理錯誤。

現在,我將指令修改為

ProxyRequests On
ProxyPass /chat/ http://localhost:8888/chat/ timeout=12000

即我將默認超時更改為12000秒。

目前這對我來說很好。這不是問題的最佳解決方案。理想情況下,長輪詢請求可以超過指定的任何超時。所以我的問題是

  1. 如何使超時無限?即Apache的請求永遠不會被關閉。
  2. 還請評論:通過Apache作為代理服務器,龍捲風的性能是否會降低?
沙发
+10

我遇到了與Nginx類似的問題並以與您相同的方式解決了它。但我將超時更改為1天,因為它在我的情況下足夠大。

我想你不能廢除這個。這背後的基本原理是Apache(或任何代理服務器)必須保持其性能,如果它必須保持陳舊或不活動的連接,它顯然不能。您寧願讓代理服務器代理比非活動連接更活躍的連接。

因此,無法關閉Apache中的ProxyTimeout,甚至無法關閉Nginx(使用proxy_read_timeout配置)。因此,如果您的代理服務器在超時內沒有發送任何響應,那麼您的應用程序服務器響應時間太長或者您的應用程序服務器出現問題,或者客戶端沒有請求任何響應。在第一種情況下,您可以進行安全估算以設置適當的超時。在第二種情況下,您需要修復應用程序服務器。在第三種情況下,您必須優雅地處理客戶端上的情況並在需要時重新連接。

回到第二個問題,除了Apache和Tornado服務器之間的延遲之外,應該沒有任何區別。您可以直接向全世界展示您的Tornado服務器,但這將帶來一些挑戰:1。更多操作工作 - 確保Tornado進程始終正常運行。2.代理和負載平衡將變得更加困難。3.由於您編寫了該代碼而不是數千名專家貢獻者,因此安全性更差。所以你不應該考慮以root身份運行這個服務器。但是你仍然可以安全地使用Apache或Nginx做同樣的事情。

當然,上述問題是可以解決的,但為什麼要解決已經解決的問題。:)

22
votes
answers
41 views
+10

Tornado AsyncHTTPClient獲取回調:額外的參數?

我對這整個異步遊戲(主要是一個Django人)都有點新意,但我想知道:我如何將額外的參數傳遞給Tornado的AsyncHTTPClient.fetch回調?例如,我正在跟踪調用回調的次數(為了等到某個數字在執行數據之前已執行),我想做的事情如下: def getPage(self, items,iteration): http = AsyncHTTPClient() http.fetch(feed, callback=self.resp(items,iteration)) def resp(self, response, items, iteration): #do stuff self.finish()
up vote 22 down vote accepted favorite
沙发
+220
+50

You need to "bind" your additional arguments. Use functools.partial, like this:

items = ..
iteration = ..
cb = functools.partial(self.resp, items, iteration)

or you could use lambda, like this:

cb = lambda : self.resp(items, iteration)

(you probably need to add the signature to def resp(self, items, iteration, response):)

謝謝!這很棒,而且正是我想要的,但是我已經重新編寫了程序而沒有異步,因為我意識到這不是我正在做的事情(聚合RSS提要上的數據)。不過,我相信我會再次使用它! - Cara Esten Hurtle 2011年5月31日3:50

+1,部分是要走的路。 - waldecir 2011年9月21日0:55

我正在考慮關閉,但這似乎更清潔。 - vartec 12年12月26日16:32

別人注意:我發現functools.partial(self.resp,items,iteration)方法需要響應才能成為回調簽名中的最後一個參數。例如def resp(self,items,iteration,response): - MechEthan 2013年7月29日22:57

@MechEthan你總是可以通過def resp(* args)檢查參數的位置:print args - MK Yung 2014年3月16日16:43

0

you might also consider the gen.coroutine decorator if you're calling fetch from inside a RequestHandler. in that case, you have no need to add extra parameters to the callback because you have the result visible in the same scope as the call to fetch.

“雖然這個鏈接可以回答這個問題,但最好在這裡包含答案的基本部分並提供參考鏈接。如果鏈接頁面發生變化,僅鏈接答案可能會無效。” - 零323年11月4日11:57

47
votes
answers
35 views
+10

是否有更好的方法來處理與龍捲風的index.html?

我想知道是否有更好的方法來處理我的Tornado index.html文件。 我對所有請求使用StaticFileHandler,並使用特定的MainHandler來處理我的主要請求。如果我只使用StaticFileHandler,我得到403:Forbidden錯誤 GET http://localhost:9000/ WARNING:root:403 GET / (127.0.0.1): is not a file 這是我現在的表現: import os import tornado.ioloop import tornado.web from tornado import web __author__ = 'gvincent' root = os.path.dirname(__file__) port = 9999 class MainHandler(tornado.web.RequestHandler): def get(self): try: with open(os.path.join(root, 'index.html')) as f: self.write(f.read()) except IOError as e: self.write("404: Not Found") application = tornado.web.Application([ (r"/", MainHandler), (r"/(.*)", web.StaticFileHandler, dict(path=root)), ]) if __name__ == '__main__': application.listen(port) tornado.ioloop.IOLoop.instance().start()
up vote 22 down vote accepted favorite
沙发
+220
+50

Turns out that Tornado's StaticFileHandler already includes default filename functionality.

Feature was added in Tornado release 1.2.0: https://github.com/tornadoweb/tornado/commit/638a151d96d681d3bdd6ba5ce5dcf2bd1447959c

To specify a default file name you need to set the "default_filename" parameter as part of the WebStaticFileHandler initialization.

Updating your example:

import os
import tornado.ioloop
import tornado.web

root = os.path.dirname(__file__)
port = 9999

application = tornado.web.Application([
    (r"/(.*)", tornado.web.StaticFileHandler, {"path": root, "default_filename": "index.html"})
])

if __name__ == '__main__':
    application.listen(port)
    tornado.ioloop.IOLoop.instance().start()

This handles root requests:

  • / -> /index.html

sub-directory requests:

  • /tests/ -> /tests/index.html

and appears to correctly handle redirects for directories, which is nice:

  • /tests -> /tests/index.html

我可以在這些.html文件中使用模板標籤嗎? - Bastin Robin 2016年6月6日9:29

這花了我大約5個小時來實際找到這個驚人的答案,萬分感謝! - 風聲於17年11月11日在6:57

+120

Thanks to the previous answer, here is the solution I prefer:

import Settings
import tornado.web
import tornado.httpserver


class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r"/", MainHandler)
        ]
        settings = {
            "template_path": Settings.TEMPLATE_PATH,
            "static_path": Settings.STATIC_PATH,
        }
        tornado.web.Application.__init__(self, handlers, **settings)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")


def main():
    applicaton = Application()
    http_server = tornado.httpserver.HTTPServer(applicaton)
    http_server.listen(9999)

    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

And Settings.py

import os
dirname = os.path.dirname(__file__)

STATIC_PATH = os.path.join(dirname, 'static')
TEMPLATE_PATH = os.path.join(dirname, 'templates')

您只需刷新瀏覽器並且無需重新啟動應用即可看到對index.html的更改嗎? - Xuan 12年12月24日12:20

@xuan我不明白你想讓我做什麼? - Guillaume Vincent 13年12月24日13:48

對不起,應該更清楚。在開發應用程序期間,您可以對index.html進行更改,並且希望從瀏覽器中看到更改。如果index.html是一個渲染模板而不是一個靜態文件,你能看到更改只刷新頁面嗎? - Xuan 12年12月24日13:53

與tornado.autoreload當然:tornadoweb.org/en/stable/autoreload.html - Guillaume Vincent 12年12月24日在14:04

注意ABSOLUTE路徑,而不是相對靜態路徑! - 代碼於2013年12月24日15:15

+50

Use this code instead

class IndexDotHTMLAwareStaticFileHandler(tornado.web.StaticFileHandler):
    def parse_url_path(self, url_path):
        if not url_path or url_path.endswith('/'):
            url_path += 'index.html'

        return super(IndexDotHTMLAwareStaticFileHandler, self).parse_url_path(url_path)

now use that class instead of vanilla StaticFileHandler in your Application... job's done!

+40

There is no need to explicitly add a StaticFileHandler; just specify the static_path and it will serve those pages.

You are correct that you need a MainHandler, as for some reason Tornado will not serve the index.html file, even if you append the filename to the URL.

In that case, this slight modification to your code should work for you:

import os
import tornado.ioloop
import tornado.web
from tornado import web

__author__ = 'gvincent'

root = os.path.dirname(__file__)
port = 9999

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

application = tornado.web.Application([
    (r"/", MainHandler),
    ], template_path=root,
    static_path=root)

if __name__ == '__main__':
    application.listen(port)
    tornado.ioloop.IOLoop.instance().start()

我無法使用此解決方案在我的根文件夾中提供* .html文件。我也需要這些文件的處理程序。 - Guillaume Vincent 2013年1月18日13:30

您使用的是哪種版本的龍捲風?我測試了你的原始和我的答案,每個都可以提供任意* .html文件。我正在使用Tornado版本=“2.4.post1”,version_info =(2,4,0,1)(在__init__.py中找到)。 - Wesley Baugh 2013年1月18日20:55

version =“2.4.1”version_info =(2,4,1,0)我使用相同的版本 - Guillaume Vincent 2013年1月21日9:00

這些映射到/ static / - vrdhn 2013年4月9日11:07

+40

I have been trying this. Don't use render it has additional overhead of parsing templates and gives error on template type strings in static html. I found this is the simplest way. Tornado is looking for a capturing parenthesis in regex , just give it an empty capturing group.

import os
import tornado.ioloop
import tornado.web

root = os.path.dirname(__file__)
port = 9999

application = tornado.web.Application([
    (r"/()", tornado.web.StaticFileHandler, {"path": root, "default_filename": "index.html"})
])

This has effect of resolving / to index.html and also avoid unwanted resolves like /views.html to static_dir/views.html

-10

This worked for me From the tornado docs:

To serve a file like index.html automatically when a directory is requested, set static_handler_args=dict(default_filename="index.html") in your application settings, or add default_filename as an initializer argument for your StaticFileHandler.

27
votes
answers
41 views
+10

@ tornado.web.asynchronous decorator是什麼意思?

如果代碼沒有使用這個裝飾器,它是否是非阻塞的? 為什麼這個名字是異步的,這意味著添加裝飾器讓代碼異步? 為什麼@ tornado.gen總是和@ tornado.web.asynchronous一起使用?
沙发
+220

@tornado.web.asynchronous prevents the the RequestHandler from automatically calling self.finish(). That's it; it just means Tornado will keep the connection open until you manually call self.finish().

  1. Code not using this decorator can block, or not. Using the decorator doesn't change that in any way.

  2. As @Steve Peak said, you use the decorator for asynchronous requests, e.g. database retrieval.

  3. Updated for Tornado 3.1+: If you use @gen.coroutine, you don't need to use @asynchronous as well. The older @gen.engine interface still requires @asynchronous, I believe.

龍捲風文檔的鏈接已被破壞。我猜它與tornadoweb.org/en/stable/gen.html類似? - Cuadue 2013年5月14日17:12

這是概述。現在修復,謝謝! - Cole Maclean 2013年5月21日12:17

看來,在你的GET / POST結束後(協同程序完成),當使用@ tornado.gen.coroutine作為裝飾器時,請求會自動完成。使用異步時,你必須調用self.finish() - cfy 2014年4月16日2:55

板凳
+50
  1. Answered here: asynchronous vs non-blocking

  2. Think of it like this. When you need to make a request to say a database or another url to retrieve data you do not want to block your tornado IO. So the @tornado.web.asynchronous will allow the IO to handle other requests while it waits for the content to load (ex. database or url).

  3. They are simular. You most likely will use @tornado.web.asynchronous.

“在這裡閱讀更多”是一個破碎的鏈接 - 馬修詹姆斯戴維斯2016年2月13日在3:22

地板
0

@tornado.web.asynchronous is essentially a just a marker you put on a handler method like get() or post() that tells the framework that it shouldn't call finish() automatically when the method returns, because it contains code that is going to set up finish() to be called at a later time.

12
votes
answers
42 views
+10

Tornado是“一個用Python編寫的相對簡單,無阻塞的Web服務器框架” - 可以解釋一下這意味著什麼?

這可能是一個愚蠢的問題,但究竟什麼是“非阻塞Web服務器”?所有的Web服務器在技術上都是非阻塞的,不是嗎?否則他們怎麼能處理同時連接?Apache2使用fork()和pthreads的組合實現了這一點。龍捲風(和Twisted)究竟有何不同?他們只是將一堆套接字設置為非bocking模式,構建FD列表(或等效的),然後使用一個大的select()sys調用來循環它嗎? 你會在哪裡使用這樣的框架,以及它們可以通過Apache2(或其他流行的服務器)給你帶來什麼好處?謝謝
up vote 12 down vote accepted favorite
沙发
+120
+50

This article on EventMachine may also give you a hint:

Steeped in the tradition of forking / threaded web-servers I found myself rather surprised when I joined one of the research projects at University of Waterloo a couple of years back: we were benchmarking different web-server architectures, and top performers were all event-driven servers.

As I pestered everyone with questions, I quickly realized why - in an environment with hundreds of thousands requests a second, forking and context switching associated with thread management become prohibitively expensive (fork is worst performer, as it does a memory copy on the parent process every time). Whereas by comparison, a tight and highly optimized event-loop really shines when it comes to performance under heavy loads.

謝謝,我想知道一個事件循環select()/ epoll()體系結構是如此引人注目(我在TCP書中看到的是yeaaaars之前的事情),以及為什麼它可能比線程更好。 - - 09年9月14日凌晨4點45分

46
votes
answers
29 views
+10

無法導入Tornado子模塊

嘗試首次安裝Tornado(在EC2 Linux實例上)。我做到了 pip install tornado 然後嘗試運行hello world示例:http://www.tornadoweb.org/en/stable/#hello-world import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start() 然後我嘗試: python hello.py 但得到: 回溯(最近一次調用最後一次):文件“testing / tornado.py”,第1行, 導入tornado.ioloop文件“/opt/pdf_engine/testing/tornado.py”,第1行, 導入tornado.ioloop ImportError:否名為ioloop的模塊
up vote 44 down vote accepted favorite
沙发
+440
+50

Don't name your file tornado.py; it shadows the actual Tornado import. Name it something like what you used in your example, e.g. hello.py

Right now, your import tornado.ioloop is trying to import the member ioloop from your own file, because it's named tornado and in the current directory which has the highest import precedence.

謝謝Amber,我很蠢 - Yarin 2013年6月26日15:05

如何停止服務器。我也試過hello world的例子。但我不知道如何阻止它 - Jatin Malwal 2013年8月24日12:25

嘗試按Ctrl + C來停止它。 - 琥珀2013年8月24日16:47

並且很有可能你必須刪除生成的tornado.pyc - Robert於2014年6月6日14:28

它的假解決方案:( - Ashish Gupta 17年1月27日17:15

+20

If you named your file tornado.py and rename it to another name,don't forget to remove tornado.pyc in your directory.

43
votes
answers
37 views
+10

龍捲風:識別/跟踪websockets的連接?

我有一個基本的Tornado websocket測試: import tornado.httpserver import tornado.websocket import tornado.ioloop import tornado.web class WSHandler(tornado.websocket.WebSocketHandler): def open(self): print 'new connection' self.write_message("Hello World") def on_message(self, message): print 'message received %s' % message def on_close(self): print 'connection closed' application = tornado.web.Application([ (r'/ws', WSHandler), ]) if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start() 我希望能夠處理多個連接(它似乎已經存在),但也能夠引用其他連接。我沒有看到識別和跟踪各個連接的方法,只是為了能夠處理連接打開,消息接收和連接關閉的事件。 [編輯] 想創建一個dict,其中鍵是Sec-websocket-key,而值是WSHandler對象......想法?我不確定Sec-websocket-key的獨特性是多麼可靠。
up vote 23 down vote accepted favorite
沙发
+230
+50

The simplest method is just to keep a list or dict of WSHandler instances:

class WSHandler(tornado.websocket.WebSocketHandler):
    clients = []

    def open(self):
        self.clients.append(self)
        print 'new connection'
        self.write_message("Hello World")

    def on_message(self, message):
        print 'message received %s' % message

    def on_close(self):
        self.clients.remove(self)
        print 'closed connection'

If you want to identify connections, e.g. by user, you'll probably have to send that information over the socket.

如果你想識別連接,例如用戶,你可能需要通過套接字發送信息..我不明白你的意思?如何識別用戶的連接? - securecurve 12年12月30日6:17

一種方法(例如)是通過套接字發送會話ID,在on_message中處理它,並將其存儲在WSHandler實例(或任何地方)。 - Cole Maclean 2013年1月3日13:35

我明白了,謝謝老兄:)) - securecurve 2013年1月3日14:36

@ColeMaclean很棒的回答!但是,如果在具有多個用戶的系統中,我想發送從一個也更新數據庫的websocket客戶端(browser1)接收的數據,並將該數據傳遞給另一個客戶端(browser2)? - phraniiac 2015年12月25日17:28

@ direwolf7你需要在on_message中處理所有這些(或者最好調用其他的東西) - Cole Maclean 2016年1月22日19:49

+190

Cole Maclean asnwer is good as simple solution, when you just need list of all connections. However, if you want something more complex, that can be monitored outside of WSHandler instance - be brave do it like this:

class WSHandler(tornado.websocket.WebSocketHandler):

    def open(self):
        self.id = uuid.uuid4()
        external_storage[self.id] = {'id':self.id}
        print 'new connection'
        self.write_message("Hello World")

    def on_message(self, message):
        #Some message parsing here
        if message.type == 'set_group':
           external_storage[self.id]['group'] = message.group
        print 'message received %s' % message

    def on_close(self):
        external_storage.remove(self.id)
        print 'closed connection'

Cole的答案符合我的要求,但如果我確實需要在WSHandler之外進行更多控制,我會記住你的。感謝幫助! - 約瑟夫7月30日在15:13

@Nikolay,非常好的回答尼古拉,很好的想法跟踪用戶而不讓他們發送他們是誰,你可以輕鬆識別他們並發送消息/關閉連接相應...好工作:))。通常,使用Redis可以存儲此類信息。 - 安全曲線於2013年1月1日6:41發布

絕對是處理這種情況的可靠方法。 - kniteli 2013年7月29日17:13

我可以將處理程序對象實際存儲在外部緩存中,如memcached嗎?我認為對象本身可以被序列化但是它與連接相關聯,我不確定是否會丟失... - PeiSong 10年9月9日在6:23

如果我有多個龍捲風服務器怎麼辦?我如何在他們和客戶(瀏覽器)之間進行通信? - madzohan 2015年9月20日15:44

+10

If your users have an access token, this can be appended to your websocket endpoint, say, and fetched in initialising your socket even before it's opened (but please work over SSL).

If an access token is not available, either because the user hasn't supplied one or the token they supplied has expired, the user is not authenticated and you will kill the socket at the earliest opportunity.

However you do this, the access token should be associated to a user who will have an identifier and that identifier can then be tied to the socket even before it has been opened. The identifier can serve as a dictionary key whose value is a set of sockets tied to this user.

0

I have solved this issue by checking the origin of the conexion. So, overrriding the method def check_origin(self, origin) may help. For example:

clients = {}

class WSHandler(tornado.websocket.WebSocketHandler):


    def check_origin(self, origin): 

        clients[origin] = self
        print(clients)
        return True
29
votes
answers
34 views
+10

如何使用模擬框架模擬龍捲風協程功能進行單元測試?

標題簡單描述了我的問題。我想用特定的返回值來模擬“_func_inner_1”。謝謝你的任何建議:) 被測代碼: from tornado.gen import coroutine, Return from tornado.testing import gen_test from tornado.testing import AsyncTestCase import mock @coroutine def _func_inner_1(): raise Return(1) @coroutine def _func_under_test_1(): temp = yield _func_inner_1() raise Return(temp + 1) 但是,這種直觀的解決方案無效 class Test123(AsyncTestCase): @gen_test @mock.patch(__name__ + '._func_inner_1') def test_1(self, mock_func_inner_1): mock_func_inner_1.side_effect = Return(9) result_1 = yield _func_inner_1() print 'result_1', result_1 result = yield _func_under_test_1() self.assertEqual(10, result, result) 如果出現以下錯誤,則_func_inner_1似乎沒有修補,因為它具有協同性質 AssertionError: 2 如果我添加coroutine補丁返回模擬功能 @gen_test @mock.patch(__name__ + '._func_inner_1') def test_1(self, mock_func_inner_1): mock_func_inner_1.side_effect = Return(9) mock_func_inner_1 = coroutine(mock_func_inner_1) result_1 = yield _func_inner_1() print 'result_1', result_1 result = yield _func_under_test_1() self.assertEqual(10, result, result) 錯誤變成: Traceback (most recent call last): File "tornado/testing.py", line 118, in __call__ result = self.orig_method(*args, **kwargs) File "tornado/testing.py", line 494, in post_coroutine timeout=timeout) File "tornado/ioloop.py", line 418, in run_sync return future_cell[0].result() File "tornado/concurrent.py", line 109, in result raise_exc_info(self._exc_info) File "tornado/gen.py", line 175, in wrapper yielded = next(result) File "coroutine_unit_test.py", line 39, in test_1 mock_func_inner_1 = coroutine(mock_func_inner_1) File "tornado/gen.py", line 140, in coroutine return _make_coroutine_wrapper(func, replace_callback=True) File "tornado/gen.py", line 150, in _make_coroutine_wrapper @functools.wraps(func) File "functools.py", line 33, in update_wrapper setattr(wrapper, attr, getattr(wrapped, attr)) File "mock.py", line 660, in __getattr__ raise AttributeError(name) AttributeError: __name__ 這是我能找到的最接近的解決方案,但是在測試用例執行後不會重置模擬函數,這與補丁不同 @gen_test def test_4(self): global _func_inner_1 mock_func_inner_1 = mock.create_autospec(_func_inner_1) mock_func_inner_1.side_effect = Return(100) mock_func_inner_1 = coroutine(mock_func_inner_1) _func_inner_1 = mock_func_inner_1 result = yield _func_under_test_1() self.assertEqual(101, result, result)
up vote 29 down vote accepted favorite
沙发
+290
+50

There are two issues here:

First is the interaction between @mock.patch and @gen_test. gen_test works by converting a generator into a "normal" function; mock.patch only works on normal functions (as far as the decorator can tell, the generator returns as soon as it reaches the first yield, so mock.patch undoes all its work). To avoid this problem, you can either reorder the decorators (always put @mock.patch before @gen_test, or use the with form of mock.patch instead of the decorator form.

Second, coroutines should never raise an exception. Instead, they return a Future which will contain a result or an exception. The special Return exception is encapsulated by the coroutine system; you would never raise it from a Future. When you create your mocks, you must create the appropriate Future and set it as the return value instead of using side_effect to raise on exception.

The complete solution is:

from tornado.concurrent import Future
from tornado.gen import coroutine, Return
from tornado.testing import gen_test
from tornado.testing import AsyncTestCase

import mock

@coroutine
def _func_inner_1():
    raise Return(1)

@coroutine
def _func_under_test_1():
    temp = yield _func_inner_1()
    raise Return(temp + 1)

class Test123(AsyncTestCase):

    @mock.patch(__name__ + '._func_inner_1')
    @gen_test
    def test_1(self, mock_func_inner_1):
        future_1 = Future()
        future_1.set_result(9)
        mock_func_inner_1.return_value = future_1
        result_1 = yield _func_inner_1()
        print 'result_1', result_1
        result = yield _func_under_test_1()
        self.assertEqual(10, result, result)

import unittest
unittest.main()

它完美無缺,非常感謝:) - Jim Horng 2015年4月17日1:50

24
votes
answers
33 views
+10

刪除龍捲風中的安全Cookie

我剛開始學習網絡服務器,特別是龍捲風。 我想寫一個帶登錄功能的簡單網頁。如Tornado文檔中所述,我在用戶成功插入後創建了一個安全的cookie: self.set_secure_cookie("user", self.get_argument("user")) 但是,如何為用戶提供註銷方式?如前所述,我不熟悉Web服務器,但是當我在用戶嘗試進入主頁時檢查此cookie時,我想我只需要在用戶按下“註銷”後將其刪除? 不幸的是,我無法在龍捲風文件中找到任何關於此的信息,也無法在SO上找到。
up vote 24 down vote accepted favorite
沙发
+240
+50

You could reset the cookie to an empty string via

self.clear_cookie("user")

Which would make the call to get_current_user return False. Take a look at the blog demo for an example (using OAuth, but still relevant).

48
votes
answers
42 views
+10

Python JSON編碼器支持datetime?

是否有任何優雅的方式使Python JSON編碼器支持日期時間?一些第三方模塊或簡單的黑客? 我正在使用tornado的數據庫包裝器從db中獲取一些行來生成json。查詢結果包括常規MySQL時間戳列。 Python的默認json編碼器不支持自己的日期時間類型,這是非常煩人的,這在所有類型的數據庫查詢中都很常見。 我不想修改Python自己的json編碼器。任何好的做法?非常感謝! ps:我通過修改Python JSON編碼器默認方法找到了一個臟黑客: 更改: def default(self, o): raise TypeError(repr(o) + " is not JSON serializable") 至: def default(self, o): from datetime import date from datetime import datetime if isinstance(o, datetime): return o.isoformat() elif isinstance(o, date): return o.isoformat() else: raise TypeError(repr(o) + " is not JSON serializable") 好吧,它只是開發環境的臨時解決方案。 但對於長期解決方案或生產環境,這非常難看,每次部署到新服務器時都必須進行修改。 有沒有更好的辦法?我不想修改Python代碼本身,也不想修改Tornado源代碼。我能用自己的項目代碼做些什麼來實現這一目標嗎?最好是一步到位。 非常感謝!
up vote 19 down vote accepted favorite
沙发
+190
+50

The docs suggest subclassing JSONEncoder and implementing your own default method. Seems like you're basically there, and it's not a "dirty hack".

The reason dates aren't handled by the default encoder is there is no standard representation of a date in JSON. Some people are using the format /Date(1198908717056)/, but I prefer ISO format personally.

import datetime

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        elif isinstance(obj, datetime.date):
            return obj.isoformat()
        elif isinstance(obj, datetime.timedelta):
            return (datetime.datetime.min + obj).time().isoformat()
        else:
            return super(DateTimeEncoder, self).default(obj)

DateTimeEncoder().encode(object)

非常感謝。這個isoformat()讓它看起來更好。:) - horacex 12年6月26日在9:33

你可以利用你為return obj.isoformat()和isinstance支持元組執行相同操作的事實來組合if和first elif:isinstance(obj,(datetime.datetime,datetime.date)) - Bailey帕克4月12日5:21

+170

json.dumps(thing, default=str)

如果您認為這符合問題中提到的“輕鬆黑客”的定義,請立即投票。 - 大衛瓊斯14年11月4日14:32

我想這應該是正確的答案,不知道這個答案有什麼問題? - titogeo 18年10月9日11:04

整潔的回答!不知道為什麼不接受這個! - mccbala 1月25日11:36

我覺得很有趣。5年後,這仍然是克服這個問題的最簡單和Pythonic方式,這個答案仍然被低估了。 - Fran Roura 5月3日3:37

+80

I made my own classes for my project:

import datetime
import decimal
import json
import sys

class EnhancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            ARGS = ('year', 'month', 'day', 'hour', 'minute',
                     'second', 'microsecond')
            return {'__type__': 'datetime.datetime',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.date):
            ARGS = ('year', 'month', 'day')
            return {'__type__': 'datetime.date',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.time):
            ARGS = ('hour', 'minute', 'second', 'microsecond')
            return {'__type__': 'datetime.time',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.timedelta):
            ARGS = ('days', 'seconds', 'microseconds')
            return {'__type__': 'datetime.timedelta',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, decimal.Decimal):
            return {'__type__': 'decimal.Decimal',
                    'args': [str(obj),]}
        else:
            return super().default(obj)


class EnhancedJSONDecoder(json.JSONDecoder):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, object_hook=self.object_hook,
                         **kwargs)

    def object_hook(self, d): 
        if '__type__' not in d:
            return d
        o = sys.modules[__name__]
        for e in d['__type__'].split('.'):
            o = getattr(o, e)
        args, kwargs = d.get('args', ()), d.get('kwargs', {})
        return o(*args, **kwargs)

if __name__ == '__main__':
    j1 = json.dumps({'now': datetime.datetime.now(),
        'val': decimal.Decimal('9.3456789098765434987654567')},
        cls=EnhancedJSONEncoder)
    print(j1)
    o1 = json.loads(j1, cls=EnhancedJSONDecoder)
    print(o1)

Result:

{"val": {"args": ["9.3456789098765434987654567"], "__type__": "decimal.Decimal"}, "now": {"args": [2014, 4, 29, 11, 44, 57, 971600], "__type__": "datetime.datetime"}}
{'val': Decimal('9.3456789098765434987654567'), 'now': datetime.datetime(2014, 4, 29, 11, 44, 57, 971600)}

References:

Note: It can be made more flexible by passing a custom dictionary with types as keys and args, kwargs as values to the encoder's __init__() and use that (or a default dictionary) in the default() method.

+30
json.dumps(r, default=lambda o: o.isoformat() if hasattr(o, 'isoformat') else o)

雖然此代碼段可以解決問題,但包括解釋確實有助於提高帖子的質量。請記住,您將來會回答讀者的問題,而這些人可能不知道您的代碼建議的原因。 - andreas 2016年10月14日19:51

+10

The Tryton project has a JSONEncoder implementation for datetime.datetime, datetime.date and datetime.time objects (with others). It is used for JSON RPC communication between the server and client.

See http://hg.tryton.org/2.4/trytond/file/ade5432ac476/trytond/protocols/jsonrpc.py#l53

0

Convert the datetime type into a unix timestamp, then encode the contents into a json.

e.g. : http://codepad.org/k3qF09Kr

你的意思是改變Python的默認日期時間類型?該怎麼辦?風險太大了嗎?它會破壞東西嗎? - horacex 2012年8月25日12:37

@horacex不,只需修改來自tornado數據庫包裝器的結果集。 - DhruvPathak 12年8月25日12:40

0

Create a custom decoder/encoder:

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return http_date(obj)
        if isinstance(obj, uuid.UUID):
            return str(obj)
        return json.JSONEncoder.default(self, obj)

class CustomJSONDecoder(json.JSONDecoder):
    def __init__(self, *args, **kwargs):
        json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)

    def object_hook(self, source):
        for k, v in source.items():
            if isinstance(v, str):
                try:
                    source[k] = datetime.datetime.strptime(str(v), '%a, %d %b %Y %H:%M:%S %Z')
                except:
                    pass
        return source
-10

Just create a custom encoder

(the small but important addition to Cole's answer is the handling of pd.NaT (or null/empty timestamp values), since without the addition you will get very weird timestamp conversions for NaT/missing timestamp data)

class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if pd.isnull(obj):
            return None
        elif isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, date):
            return obj.isoformat()
        elif isinstance(obj, timedelta):
            return (datetime.min + obj).time().isoformat()
        else:
            return super(CustomEncoder, self).default(obj)

Then use it to encode a dataframe:

df_as_dict = df.to_dict(outtype = 'records')  # transform to dict

df_as_json = CustomEncoder().encode(df_as_dict) #transform to json

Since the encoder standardized the data, the regular decoder will act fine in transforming it back to a dataframe:

result_as_dict = json.JSONDecoder().decode(df_as_json) # decode back to dict

result_df = pd.DataFrame(result)  # transform dict back to dataframe

Of course this will also work if you put the dataframe into a larger dict before encoding, e.g

input_dict = {'key_1':val_1,'key_2':val_2,...,'df_as_dict':df_as_dict}
input_json = CustomEncoder().encode(input_dict)
input_json_back_as_dict = json.JSONDecoder().decode(input_json)
input_df_back_as_dict = input_json_back_as_dict['df_as_dict']
input_df_back_as_df = pd.DataFrame(input_df_back_as_dict)