I have problem with method open of WebSocketHandler.
I wrapped it with gen.coroutine for using async call inside (get access to redis). But got another problem, any errors inside open does not catches.
Example:
#gen.coroutine
def open(self):
t = 8/0
self._connection_id = yield self._generate_connection_id()
self.write_message('...')
Method open is call inside WebSocketProtocol._run_callback :
def _run_callback(self, callback, *args, **kwargs):
try:
callback(*args, **kwargs)
except Exception:
app_log.error("Uncaught exception in %s",
self.request.path, exc_info=True)
self._abort()
This method hasn't any decorator, so method open return future and this exception doesn't handled.
So how can I use async method inside open, and handle exceptions?
As a general rule, coroutines can only correctly be called by other coroutines, so when overriding a method of a base class like WebSocketHandler.open, unless that method is a coroutine or is documented as "may be a coroutine" or "may return a Future", it is not safe to make it a coroutine. WebSocketHandler.open may not be a coroutine (as of Tornado 4.1).
You may spawn a coroutine from open() with IOLoop.spawn_callback:
def open(self):
IOLoop.current().spawn_callback(self.async_open)
#gen.coroutine
def async_open(self):
#...
Within async_open, you can handle errors as usual with try/except (spawn_callback also logs exceptions for you as a last resort). It is also your responsibility to deal with any timing issues that may arise between async_open and on_message or on_close, since those methods may be called before async_open completes.
Related
I know that in python37 we have a new api asyncio.get_running_loop(), which is easy to use, let us do not need to pass eventloop explicitly when we call a coroutine.
I'm wondering if there's any approach we can use to get the same effect in python36?
# which allows us coding conveniently with this api:
import asyncio
async def test():
print("hello world !")
async def main():
loop = asyncio.get_running_loop()
loop.create_task(test())
asyncio.run(main())
In Python 3.6 you can use asyncio.get_event_loop() for equivalent effect.
According to the documentation, it is equivalent to calling get_event_loop_policy().get_event_loop(), which is in turn documented to return "the currently running event loop" when called from a coroutine.
In other words, when invoked from a coroutine (or from a function invoked by a coroutine), there is no difference between get_event_loop and get_running_loop, both will return the running loop. It is only when no loop is running that get_event_loop() will keep returning the loop associated with the current thread, while get_running_loop() will raise an exception. As long as you are careful to call get_event_loop() while a loop is actually running, it will be equivalent to get_running_loop().
Note that get_event_loop returning the running loop when called from a coroutine is new to Python 3.6 and 3.5.3. Prior to those versions, get_event_loop would always return the event loop associated with the current thread, which could be a different loop from the one that is actually running. This made get_event_loop() fundamentally unreliable and is the reason why old asyncio code would pass the loop argument everywhere. More details here.
override method on_finish().
Decorate this method with .gen.coroutine to make it asynchronous, it worked.
#gen.coroutine
def on_finish(self):
print("==========on_finish==============")
but use async def to make it asynchronous, it failed.
async def on_finish(self):
print("==========on_finish==============")
# RuntimeWarning: coroutine 'BaseHandler.on_finish' was never awaited
self.on_finish()
Why can't I use async def to make it asynchronous?
Coroutines have a different interface than regular functions. When overriding a method, you should make it a coroutine (or not) according to its documentation. All overridable methods in Tornado that may be coroutines say so in their documentation, and on_finish is not one of them.
What happens if you use a coroutine when the superclass doesn't expect it? As you see, this depends on the kind of coroutine. #gen.coroutine is "self-starting", so the coroutine will start, but nothing will wait for it to finish, and if it raises an exception, there is nowhere for that exception to be caught (so it will just be logged by the IOLoop). async def coroutines do not start automatically, something must await them, so if you use one where the caller is not expecting a coroutine, nothing will happen.
If you need to use a coroutine from a method where this is not allowed, you need to decide who's going to wait on the coroutine and handle its exceptions. If you don't need to catch exceptions and can just leave them for the logs, you can use IOLoop.add_callback to explicitly give this responsibility to the IOLoop:
def on_finish(self):
IOLoop.current().add_callback(self.on_finish_async)
async def on_finish_async(self):
...
I'm trying to get my head around Tornado. I'm writing a chat application backed by mongodb and I'm using motor for non-blocking access to it.
What I'm trying to achieve is:
Create a decorator that uses motor to asynchronously pull the user's record from mongo
Validate their credentials (username & token)
Create another decorator that checks that the user_id retrieved in 1. above is permitted access to the chat room. This requires another asynchronous call to mongo with motor to retrieve the 'ChatRoom' record.
Subscribe to the chat room if all is OK
I have decorator 1. working (basically taken from http://tornadogists.org/5251927/):
def authenticated_async(f):
#functools.wraps(f)
#gen.engine
def wrapper(self, *args, **kwargs):
self.current_user = yield gen.Task(self.get_current_user_async)
if self.current_user:
logging.info("User '%s' (%s) successfully authenticated" %
(self.current_user['username'],
self.current_user['_id']))
f(self, *args, **kwargs)
else:
raise tornado.web.HTTPError(401, "User not authenticated, "
"aborting")
return wrapper
The trouble is that for the second decorator, I need to access self.current_user. Because this is set in an asynchronous callback, it's not available when I get into my validation decorator (i.e the validation decorator is called before the auth decorator completes).
Is it just not possible for me to use decorators in this way with asynchronous functions? Do I just need to call the validation method inside the above method after making sure that self.current_user is True so it's more like a callback?
I'd ideally like to have my methods in my Handler wrapped with both of these decorators so I can reuse them elsewhere, i.e.:
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
#gen.coroutine
#validate_invitation_access_async
#authenticated_async
def open(self, invitation_id):
# do stuff here...
Update
In fact, there is no dependency. user_id is provided as a parameter, and that could be used to run both decorators in parallel - one to confirm authentication, the other to see whether the user with that ID is allowed access to the room. The open() method would then only proceed if self.auth_check == True and self.room_check == True.
Could open() ever be called before the async decorators complete though?
You need to switch the order of the decorators so your validate_invitation_access_async wrapper has access to current_user:
#authenticated_async
#validate_invitation_access_async
def open(self, invitation_id):
# do stuff here...
Then the wrapper inside validate_invitation_access_async is the "f" in authenticated_async: it's called after self.current_user is set. Note that you don't need an additional gen.engine decorator, all the wrappers are already engines. Your wrapper could be like:
def validate_invitation_access_async(f):
#gen.engine
def wrapper(self, *args, **kwargs):
# ... use Motor to see if user is authorized ...
# if not, log and redirect. otherwise:
f(self, *args, **kwargs)
You should also update your code to use Tornado 3's gen.coroutine instead of gen.engine: it's much cleaner. But I leave that as an exercise.
I am doing some low level socket work using F# and am making everything asynchronous. I have a socket that I am using to listen for connections using an async workflow to handle them so it is using this to wrap the Socket.BeginListen() and Socket.EndListen().
member socket.AsyncAccept () =
Async.FromBeginEnd( socket.BeginAccept, endOrDisposed socket.EndAccept null )
When I want to stop listening, at the moment I am doing a Socket.Close() on it which will cause the async operation started by Socket.BeginAccept() to complete.
Originally I then ran into a problem as the FromBeginEnd() function would always call the EndXXX() function no matter how the operation started by BeginXXX() completed. In some cases this would lead to an ObjectDisposed exception by the EndXXX() function as by the time it was called the Socket had been closed and disposed. I added a little handler function which would filter those exceptions out thus:
let endOrDisposed endFunc defaultResult iar = try endFunc iar with | _ -> defaultResult
This does the trick, but not when running in debug. I am well aware that the Just My Code option can be used to hide the exception but this could be happening with some other IO operations on a more frequent basis so I also would rather not have processor time wasted on raising and catching exceptions which shouldn't really be there in the first place. Also I might not want to hide where other exceptions are being thrown as they could by in areas where I do need to debug.
I've looked at the code for Async.FromBeginEnd and it is wired to always call the EndXXX() function no matter what, I'm not sure this is the best behaviour, perhaps I should write a replacement for it? Or does anyone have any other ideas for elegant solutions?
I just found (sure I'd looked before though) this in the docs:
To cancel a pending call to the BeginAccept() method, close the
Socket. When the Close() method is called while an asynchronous
operation is in progress, the callback provided to the BeginAccept()
method is called. A subsequent call to the EndAccept() method will
throw an ObjectDisposedException to indicate that the operation has
been cancelled.
I still don't like the exception, even if it is by design. I'd prefer to find a way to not call EndXXX() on a disposed object. Perhaps I can mix some CancellationToken magic into this somehow?
I would make use of the optional cancelAction argument to Async.FromBeginEnd here:
type Socket with
member this.AsyncAccept () =
let canceled = ref false
let endAccept iar = if not !canceled then this.EndAccept iar else null
let cancel () = canceled := true; this.Close ()
Async.FromBeginEnd (this.BeginAccept, endAccept, cancelAction = cancel)
This way you can use Async's builtin cancellation functionality (which is of course based on CancellationToken) without touching a CancellationToken directly. (I.e., you should not be calling Socket.Close for cancellation purposes.)
What's needed is a method to interrupt the Async.FromBeginEnd process and stop it trying to EndAccept in the case where the socket is being closed (this probably extends to other operations besides Accept).
Instead of having calls to Socket.Close() I used a CancellationTokenSource and registered a handler with it which will call Socket.Close():
cts.Token.Register (fun () -> listener.Close()) |> ignore
and then instead of passing Async.FromBeginEnd the EndAccept function directly it now goes via a function that will stop the EndAccept being called if the token has been cancelled.
let endOrDisposed endFunc defaultResult iar = try endFunc iar with | _ -> defaultResult
let endIfNotCancelled endFunc defaultResult (token:CancellationToken) iar = if token.IsCancellationRequested then defaultResult else endOrDisposed endFunc defaultResult iar
type Socket with
member socket.AsyncAccept (cancelToken) =
Async.FromBeginEnd( socket.BeginAccept, endIfNotCancelled socket.EndAccept null cancelToken)
This still uses my function to filter out and ignore exceptions (I don't think exceptions are very exceptional when it comes to networking, and in this apps case there is nothing it would do in response anyway).
I am attempting to implement get_current_user in the RequestHandler for Tornado, but I need the call to block while waiting on the asynchronous call to my database. Decorating the call with #tornado.web.asynchronous will not work because either way the get_current_user method returns before the async query completes and the query callback is executed.
For example:
class MyHandler(BaseHandler):
#tornado.web.asynchronous
#tornado.web.authenticated
def get(self):
self.write('example')
self.finish()
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
def query_cb(self, doc):
return doc or None
database.get(username='test', password='t3st', callback=query_cb)
#tornado.web.authenticated calls get_current_user, but always receives "None" because the BaseHandler does not have time to respond. Is there a way, using tornado, to temporarily block for a call such as the one above?
Do a blocking database operation instead of the non blocking described above (There is a blocking mysql lib shipped with tornado).
From the Tornado wiki page about threads and concurrency:
"Do it synchronously and block the IOLoop. This is most appropriate for things like memcache and database queries that are under your control and should always be fast. If it's not fast, make it fast by adding the appropriate indexes to the database, etc."
https://github.com/facebook/tornado/wiki/Threading-and-concurrency
How about having get_current_user return a Future that you signal when the asynchronous response from your database is returned?
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
future = Future()
def query_cb(user):
future.set_result(user or None)
database.get(username='test', password='t3st', callback=query_cb)
return future
class MainHandler(BaseHandler):
#gen.coroutine
def get(self):
user = yield self.get_current_user()
self.write('user: ' + user)
# ... actual request processing
I thought Tornado allowed you to make either blocking or non-blocking requests.
Here is Tornado being used for both: https://bitbucket.org/nephics/tornado-couchdb/src/147579581b47/couch.py
Disclaimer: I know very little of Python and Tornado.