News for hackers. Stuff that matters.
Re:Problems I Have with Python by Loic on Wednesday January 25, 2017 @04:28AM (#13480000)
The one problem I have with Python and would like to solve, is to be able, from within a request rendering function/method of my web application (think Flask) to run something like:

    handle1 = call_webservice_1(args)
    handle2 = call_webservice_2(args)
    handle3 = call_webservice_3(args)
    (realres1, realres2, realres3) = wait_until_timeout(500, handle1, handle2, handle3) 
    # here, I have my results in realresX or None if timeout
    # the call_webservice_X would be non blocking
This way I can dispatch my requests to my backends and degrade gracefully if one request fails within the time. I was doing that in PHP using zeromq to send the requests and listening to the answers with a unique id on each request, but now I would prefer to stay with an HTTP based protocol to communicate with my backends.
Re:Problems I Have with Python by orf on Wednesday January 25, 2017 @05:44AM (#13480321)

    from asyncio import wait, gather, get_event_loop
    from aiohttp import web

    async my_handler(request):
        handle1 = call_webservice_1(args)
        handle2 = call_webservice_2(args)
        handle3 = call_webservice_3(args)

        await wait(gather(handle1, handle2, handle3), 500)
    
        return web.Response({'finished': True})


    app = web.Application()
    app.router.add_route('GET', '/test/', my_handler)

    loop = asyncio.get_event_loop()
    server = loop.create_server(app.make_handler())
    loop.run_until_complete(server)
Done. Not flask, but if you need to make lots of parallel network calls during a web request why the hell are you using flask?
Re:Problems I Have with Python by spookylukey on Wednesday January 25, 2017 @06:19AM (#13480496)
That's really helpful. Can you make an example which doesn't require making the my_handler function 'async'? For cases when you don't want to make an entire async stack, you just want to slot some async code into your existing code.

If the answer is "to get some async goodness, just use this easy code, plus rewrite your entire project to use a different framework and set of libraries", then we are only fooling ourselves.

Re:Problems I Have with Python by AnkhMorporkian on Wednesday January 25, 2017 @06:56AM (#13480669)
You need to use the asyncio (or equivalent) event loop if you want to use the asnycio module. loop.run_until_complete() is synchronous though, so you would simply call that and it will block the control flow despite that function being async. You can definitely mix it with legacy code.

I would recommend against it, but if you had an existing framework, you could just make the endpoints lambdas that are something like:

    app.route("/whatever", lambda: loop.run_until_complete(async_handler_function()))
Re:Problems I Have with Python by orf on Wednesday January 25, 2017 @08:39AM (#13481264)
^ this, but be aware that it's only worth it if you do > 1 external call in parallel. I.E this is pointless:

    res = await get('https://somesite.com')
    return Response(res['data'])
As you'd get the same thing if you just did it synchronously (without the await). But if you want to fetch 2 or more pages in parallel when it really pays off.
Re:Problems I Have with Python by AnkhMorporkian on Wednesday January 25, 2017 @08:53AM (#13481336)
Yeah, absolutely. I am not a fan of mixing synchronous and asynchronous code, but the design of asyncio makes it very easy to do. I think that most people struggling with the concept don't realize that asyncio is inherently blocking when its being used (well, with the caveat of run_in_executor, but that's best left ignored for the purposes here)
Re:Problems I Have with Python by orf on Wednesday January 25, 2017 @07:07AM (#13480732)
Sure, you can make an async function and call `loop.run_until_complete` in your handler.
Re:Problems I Have with Python by Loic on Wednesday January 25, 2017 @06:12AM (#13480454)
I am using flask because you know, long string of development with flask over the years, your application is growing and then you hit some walls. I do not want to rewrite everything just for a couple of views within my application.

Flask with gunicorn has its own even loop and asyncio io another one and at the end it is hard to be sure if something is working because I am lucky or because it is the way to do things.

Anyway, thank you for your example, it is really clear and easy to understand! Maybe we need a flask like framework which is asyncio based. Which mean I will need to upgrade the code to Python 3 :)

Re:Problems I Have with Python by loganekz on Wednesday January 25, 2017 @02:21PM (#13484957)
If you haven't already, check out sanic [1] which is powered by uvloop [2] which itself is pretty amazing.

[1] https://github.com/channelcat/sanic

[2] https://github.com/MagicStack/uvloop

Re:Problems I Have with Python by orf on Wednesday January 25, 2017 @06:19AM (#13480494)
Re:Problems I Have with Python by ojii on Wednesday January 25, 2017 @04:55AM (#13480104)
Quick two minute implementation:

    import multiprocessing
    
    import time
    
    
    NULL = object()
    
    
    def call_webservice(fail=False):
        if fail:
            time.sleep(10)
            return False
        else:
            return True
    
    
    def wait_until_timeout(timeout, *async_results):
        results = [NULL] * len(async_results)
        end = time.time() + timeout
        while time.time() < end:
            for index, result in enumerate(async_results):
                if results[index] is NULL and result.ready():
                    results[index] = result.get()
            if not results.count(NULL): break
        return tuple(None if result is NULL else result for result in results)
    
    
    def handler():
        with multiprocessing.Pool() as pool:
            handle1 = pool.apply_async(call_webservice, (True,))
            handle2 = pool.apply_async(call_webservice, (False,))
            start = time.time()
            realres1, realres2 = wait_until_timeout(0.5, handle1, handle2)
            duration = time.time() - start
            print(f'Took {duration:.3f} seconds')
        print(realres1, realres2)
    
    
    if __name__ == '__main__':
        handler()
Re:Problems I Have with Python by darkf on Wednesday January 25, 2017 @04:43AM (#13480053)
You could probably do that fine with coroutines and/or asyncio.