user340307 user340307 - 2 months ago 8
Python Question

use asyncio update some data timely and present via aiohttp?

I am in trying to write a snippet to study Python asyncio. The basic idea is:


  1. use a "simple" web server (aiohttp) to present some data to user

  2. the data return to user will change promptly



here is the code:

import asyncio
import random
from aiohttp import web

userfeed = [] # the data suppose to return to the user via web browsers

async def data_updater(): #to simulate data change promptly
while True:
await asyncio.sleep(3)
userfeed = [x for x in range(random.randint(1, 20))]
print('user date updated: ', userfeed)


async def web_handle(request):
text = str(userfeed)
#print('in handler:', text) # why text is empty?
return web.Response(text=text)

async def init(loop):
app = web.Application(loop=loop)
app.router.add_route('GET', '/', web_handle)
srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)
print('Server started @ http://127.0.0.1:8000...')
return srv

loop = asyncio.get_event_loop()
asyncio.ensure_future(data_updater())
asyncio.ensure_future(init(loop))
loop.run_forever()


the problem is, the code is running (python 3.5), but the
userfeed
is always empty in browsers and also in
web_handler()
:-(


  1. why
    userfeed
    is not been updated?

  2. regarding this
    timely date update
    function, because the update mechanism might be more complex later say async IO wait may be involved, is there a better way instead of using
    while True: await asyncio.sleep(3)
    in
    data_updater()
    to get "more roughly precise" timer?


mgc mgc
Answer

The main problem is that you forget the statement global userfeed in both data_updater and web_handle functions. So, according to how python resolves scopes, in web_handle it was referring to the global variable you defined and in data_updater to a local one, created by the statement userfeed = [x for x .... Using global variables in this context is explicitly discouraged so there is an example using the dict interface of the aiohttp.web.Application object to safely refer to your variable between the functions.

import asyncio
import random
from aiohttp import web


async def data_updater(app):
    while True:
        await asyncio.sleep(3)
        app["userfeed"] = [x for x in range(random.randint(1, 20))]

async def web_handle(request):
    userfeed = request.app["userfeed"]
    return web.Response(text=str(userfeed))

async def init(loop, port=8000):
    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', web_handle)
    handler = app.make_handler()
    srv = await loop.create_server(
        handler, '127.0.0.1', port=port)
    return srv, app, handler

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    srv, app, handler = loop.run_until_complete(init(loop, 8000))
    app['userfeed'] = []
    asyncio.ensure_future(data_updater(app))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        srv.close()
        loop.run_until_complete(srv.wait_closed())
        loop.run_until_complete(app.shutdown())
        loop.run_until_complete(handler.finish_connections(60.0))
        loop.run_until_complete(app.cleanup())
    loop.close()

When you refresh the page on 127.0.0.1:8000 you should have some new random numbers as they are updated every 3 seconds server-side (you can put back the print statement in data_updater to verify it).