Aiohttp: рдЬрдм рдХрдиреЗрдХреНрд╢рди рдЕрдирдкреЗрдХреНрд╖рд┐рдд рд░реВрдк рд╕реЗ рдмрдВрдж рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рд╡реЗрдмрд╕реЛрдХреЗрдЯ рд░реАрдбрд┐рдВрдЧ рдореИрд╕реЗрдЬ рд▓реВрдк рдирд┐рдореНрди-рд╕реНрддрд░реАрдп CanceledError рдЙрдард╛рддрд╛ рд╣реИ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 6 рдЬреБрд▓ре░ 2017  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: aio-libs/aiohttp

рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реНрдпрд╡рд╣рд╛рд░

рд╕рдВрджреЗрд╢ рд▓реВрдк рдкрдврд╝рдирд╛ async for msg in ws: рдирд┐рдореНрди-рд╕реНрддрд░ concurrent.futures._base.CancelledError рдЙрдард╛рддрд╛ рд╣реИ рдЬрдм рдХрдиреЗрдХреНрд╢рди рдЕрдкреНрд░рддреНрдпрд╛рд╢рд┐рдд рд░реВрдк рд╕реЗ рдмрдВрдж рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред

рдЕрдкреЗрдХреНрд╖рд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░

aiohtto.http_websocket.WSMsgType.ERROR рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд╛рде рд╕рдВрджреЗрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЙрдореНрдореАрдж рд╣реИ, рдпрд╛ рдЪреБрдкрдЪрд╛рдк рд▓реВрдк рдХреЛ рд░реЛрдХреЗрдВ, рдпрд╛ рдХрдо рд╕реЗ рдХрдо aiohtto.http_websocket.WebSocketError ред

рдкреНрд░рдЬрдирди рдХреЗ рдЪрд░рдг

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рджреЛ рд╕реНрдХреНрд░рд┐рдкреНрдЯ server.py рдФрд░ client.py рдЪрд▓рд╛рдПрдБ, рдлрд┐рд░ client.py рдХреЛ Ctrl+C рд░реЛрдХреЗрдВред

рд╕рд░реНрд╡рд░.py

import logging

from aiohttp import web


logger = logging.getLogger(__name__)


async def index(request):
    ws = web.WebSocketResponse()
    request.app['websockets'].add(ws)

    try:
        await ws.prepare(request)
        logger.debug('Connected')
        async for msg in ws:
            logger.info('Received: %r', msg.data)
    except Exception:
        logger.exception('Error')
    logger.debug('Disconnected')

    request.app['websockets'].discard(ws)
    return ws


async def on_shutdown(app):
    for ws in app['websockets']:
        await ws.close()
    app['websockets'].clear()


def main():
    logging.basicConfig(level=logging.DEBUG)

    app = web.Application()
    app['websockets'] = set()
    app.router.add_get('/', index)
    app.on_shutdown.append(on_shutdown)

    web.run_app(app, host='127.0.0.1', port=9000)


if __name__ == '__main__':
    main()

Client.py

import asyncio

import aiohttp


async def communicate(loop):
    async with aiohttp.ClientSession(loop=loop) as session:
        async with session.ws_connect('http://127.0.0.1:9000') as ws:
            while True:
                await ws.send_str('Hello')
                await asyncio.sleep(1, loop=loop)


def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(communicate(loop))


if __name__ == '__main__':
    main()

server.py рдХрд╛ рд▓реЙрдЧ рдЖрдЙрдЯрдкреБрдЯ

$ python server.py 
DEBUG:asyncio:Using selector: EpollSelector
======== Running on http://127.0.0.1:9000 ========
(Press CTRL+C to quit)
DEBUG:__main__:Connected
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
INFO:__main__:Received: 'Hello'
ERROR:__main__:Error
Traceback (most recent call last):
  File "server.py", line 16, in index
    async for msg in ws:
  File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/web_ws.py", line 343, in __anext__
    msg = yield from self.receive()
  File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/web_ws.py", line 273, in receive
    msg = yield from self._reader.read()
  File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/streams.py", line 627, in read
    return (yield from super().read())
  File "/home/vagrant/project/workspace/pyenv_dev/lib64/python3.5/site-packages/aiohttp/streams.py", line 509, in read
    yield from self._waiter
  File "/usr/lib64/python3.5/asyncio/futures.py", line 380, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib64/python3.5/asyncio/tasks.py", line 304, in _wakeup
    future.result()
  File "/usr/lib64/python3.5/asyncio/futures.py", line 285, in result
    raise CancelledError
concurrent.futures._base.CancelledError
DEBUG:__main__:Disconnected
INFO:aiohttp.access:- - - [06/Jul/2017:11:41:25 +0000] "GET / HTTP/1.1" 101 0 "-" "Python/3.5 aiohttp/2.2.3"

рдЖрдкрдХрд╛ рд╡рд╛рддрд╛рд╡рд░рдг

рдУрдПрд╕: рд╕реЗрдВрдЯреЛрд╕ рд▓рд┐рдирдХреНрд╕ 7
рд▓рд┐рдирдХреНрд╕ рдХрд░реНрдиреЗрд▓: 3.10.0-514.16.1.el7.x86_64
рдкрд╛рдпрдерди: 3.5.3
aiohttp: 2.2.3

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдПрдХ рдЕрд▓рдЧ рдХрдиреЗрдХреНрд╢рди рдмрдВрдж рдЕрдкрд╡рд╛рдж рдкреЗрд╢ рдХрд░рдирд╛ рдмреЗрд╣рддрд░ рд╣реЛ, рд╡реИрд╕реЗ рд╣реА рдЬреИрд╕реЗ websockets рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдореЗрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛?

рд╕рднреА 4 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рддрдХрдиреАрдХреА рд░реВрдк рд╕реЗ aiohttp рдкреНрд░рддрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рдЕрдиреБрд░реЛрдз рдкрд░ рдПрдХ рдХрд╛рд░реНрдп рдмрдирд╛рддрд╛ рд╣реИред
рдХреНрд▓рд╛рдЗрдВрдЯ рдбрд┐рд╕реНрдХрдиреЗрдХреНрд╢рди рдкрд░ рд╕рд┐рд╕реНрдЯрдо ASAP рдХрд╛рд░реНрдп рдХреЛ рд░реЛрдХ рджреЗрддрд╛ рд╣реИред
рдЗрд╕реЗ рдХрд░рдиреЗ рдХрд╛ рдПрдХрдорд╛рддреНрд░ рддрд░реАрдХрд╛ рдХрд╛рд░реНрдп рд░рджреНрдж рдХрд░рдирд╛ рд╣реИ (рдорд╛рди рд▓реЗрдВ рдХрд┐ рд╡реЗрдм рд╣реИрдВрдбрд▓рд░ рдбреАрдмреА рдпрд╛ рдЕрдиреНрдп рд╕реЗрд╡рд╛ рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░ рд░рд╣рд╛ рд╣реИ, рд╣рдо рдЗрд╕реЗ рд╡реЗрдмрд╕реЛрдХреЗрдЯ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рдХрдиреЗрдХреНрд╢рди рдкрд░ рд╕реНрдкрд╖реНрдЯ рд╕рдВрдЪрд╛рд▓рди рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд┐рдП рдмрд┐рдирд╛ рднреА рд░рджреНрдж рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ)ред

Task.cancel() asyncio.CancelledError рдЕрдкрд╡рд╛рдж рднреЗрдЬрдХрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЕрдкрд╡рд╛рдж рд╡рд░реНрдЧ рдорд╛рдирдХ Exception ред рдпрд╣ asyncio рд╡реНрдпрд╡рд╣рд╛рд░ рд╣реИ, aiohttp рдХреЗ рд▓рд┐рдП рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреБрдЫ рднреА рдирд╣реАрдВ рд╣реИред

рдХреЗрд╡рд▓ рдПрдХ рдЪреАрдЬ рдЬреЛ рдореИрдВ рд╕реБрдЭрд╛ рд╕рдХрддрд╛ рдерд╛ рд╡рд╣ рд╣реИ CancelledError рдХреЛ рдЖрдкрдХреЗ рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдкрдХрдбрд╝рдирд╛:

try:
    ...
except asyncio.CancelledError:
    pass
except Exception as exc:
    log(exc)

рдпрд╛ рдЖрдк Exception рдЬреИрд╕реЗ рд╡реНрдпрд╛рдкрдХ рдкреНрд░рдХрд╛рд░ рдХреЛ рдирд╣реАрдВ рдкрдХрдбрд╝ рд╕рдХреЗред

рдореБрдЭреЗ рджреЛ рд╡рд┐рдХрд▓реНрдк рджрд┐рдЦрд╛рдИ рджреЗрддреЗ рд╣реИрдВ:

  • рдХреБрдЫ рднреА рди рдмрджрд▓реЗрдВ, CancelledError async рджреБрдирд┐рдпрд╛ рдореЗрдВ рд╕рд╛рдорд╛рдиреНрдп рд╣реИред
  • CancelledError рдФрд░ closed рд╕рдВрджреЗрд╢ рд▓реМрдЯрд╛рдПрдВред рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ, рдпрд╣ рд╡реЗрдмрдХреЗрдЯ рд╣реИрдВрдбрд▓рд░ рдХреЗ рд▓рд┐рдП рдмреЗрд╣рддрд░ рд╕рдорд╛рдзрд╛рди рд╣реИред

рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдПрдХ рдЕрд▓рдЧ рдХрдиреЗрдХреНрд╢рди рдмрдВрдж рдЕрдкрд╡рд╛рдж рдкреЗрд╢ рдХрд░рдирд╛ рдмреЗрд╣рддрд░ рд╣реЛ, рд╡реИрд╕реЗ рд╣реА рдЬреИрд╕реЗ websockets рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдореЗрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛?

рддрдХрдиреАрдХреА рд░реВрдк рд╕реЗ aiohttp рдкреНрд░рддрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рдЕрдиреБрд░реЛрдз рдкрд░ рдПрдХ рдХрд╛рд░реНрдп рдмрдирд╛рддрд╛ рд╣реИред
рдХреНрд▓рд╛рдЗрдВрдЯ рдбрд┐рд╕реНрдХрдиреЗрдХреНрд╢рди рдкрд░ рд╕рд┐рд╕реНрдЯрдо ASAP рдХрд╛рд░реНрдп рдХреЛ рд░реЛрдХ рджреЗрддрд╛ рд╣реИред
рдЗрд╕реЗ рдХрд░рдиреЗ рдХрд╛ рдПрдХрдорд╛рддреНрд░ рддрд░реАрдХрд╛ рдХрд╛рд░реНрдп рд░рджреНрдж рдХрд░рдирд╛ рд╣реИ (рдорд╛рди рд▓реЗрдВ рдХрд┐ рд╡реЗрдм рд╣реИрдВрдбрд▓рд░ рдбреАрдмреА рдпрд╛ рдЕрдиреНрдп рд╕реЗрд╡рд╛ рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░ рд░рд╣рд╛ рд╣реИ, рд╣рдо рдЗрд╕реЗ рд╡реЗрдмрд╕реЛрдХреЗрдЯ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рдХрдиреЗрдХреНрд╢рди рдкрд░ рд╕реНрдкрд╖реНрдЯ рд╕рдВрдЪрд╛рд▓рди рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд┐рдП рдмрд┐рдирд╛ рднреА рд░рджреНрдж рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ)ред

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдореБрдЭреЗ рдЕрднреА рдЗрд╕ рд╡реНрдпрд╡рд╣рд╛рд░ рд╕реЗ рдХрд╛рдЯ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдореЗрд░реЗ рдкрд╛рд╕ рдХреБрдЫ рдРрд╕рд╛ рдХреЛрдб рдерд╛:

async def handler(request):
    ws = web.WebSocketResponse()
    await ws.prepare(request)

    async with contextlib.AsyncExitStack() as stack:
        # acquire_resource_X are async context managers
        await stack.enter_async_context(acquire_resource_1())
        await stack.enter_async_context(acquire_resource_2())
        await stack.enter_async_context(acquire_resource_3())

        async for msg in ws:
            # do stuff

    await ws.close()

    return ws

рдЗрд╕реЗ рдЙрддреНрдкрд╛рджрди рдореЗрдВ рд▓рдЧрд╛рдиреЗ рдХреЗ рдмрд╛рдж рдореИрдВрдиреЗ рдкрд╛рдпрд╛ рдХрд┐ acquire_resource_3() рдХреЗ рдмрд╛рд╣рд░ рдирд┐рдХрд▓рдиреЗ рд╡рд╛рд▓реЗ рд╣рд┐рд╕реНрд╕реЗ рдХреЛ рдЪреБрдкрдЪрд╛рдк рдЫреЛрдбрд╝ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЕрдзрд┐рдХ рд▓реЙрдЧрд┐рдВрдЧ рд╕реЗ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ CancelledError рдЕрдВрджрд░ рдПрдХ acquire_resource_3 рдЙрдард╛рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рдерд╛ред рдпрд╣рд╛рдБ рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд╣реБрдЖ:

  • рдХреНрд▓рд╛рдЗрдВрдЯ рд╡реЗрдмрд╕реЙрдХреЗрдЯ рдмрдВрдж рдХрд░ рджреЗрддрд╛ рд╣реИ
  • async for msg in ws рд▓реВрдк рдмрд╛рд╣рд░ рдирд┐рдХрд▓рддрд╛ рд╣реИ, AsyncExitStack рдЦреЛрд▓рдирд╛ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ, acquire_resource_3 рдХрд╛ рдирд┐рдХрд╛рд╕ рднрд╛рдЧ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рд╣реЛрдирд╛ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ, рдПрдХ await рд╣рд┐рдЯ рдХрд░рддрд╛ рд╣реИ
  • aiohttp рд╣реИрдВрдбрд▓рд░ рдХрд╛рд░реНрдп рдХреЛ рд░рджреНрдж рдХрд░рддрд╛ рд╣реИ
  • CancelledError рд╡рд░реНрддрдорд╛рди await handler рдкрд░ acquire_resource_3 рдЕрдВрджрд░ рд╣реИ, рдЗрд╕рд▓рд┐рдП acquire_resource_3 рдХрд╛ рд╢реЗрд╖ рднрд╛рдЧ рдЫреЛрдбрд╝ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ
  • acquire_resource_2 рдФрд░ acquire_resource_1 рдХрд╛ рдирд┐рдХрд╛рд╕ рднрд╛рдЧ рдЕрднреА рднреА рд╕рд╛рдорд╛рдиреНрдп рд░реВрдк рд╕реЗ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рд╣реЛрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЙрдирдХреЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ рд╡реЗ рдХреЗрд╡рд▓ рдПрдХ рдЕрдкрд╡рд╛рдж рдкрд░ рдПрдХ async рд╕рдВрджрд░реНрдн рд╕реЗ рдмрд╛рд╣рд░ рдирд┐рдХрд▓ рд░рд╣реЗ рд╣реИрдВ

рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдПрдХ рдЕрдЬреАрдм рд╕рдорд╕реНрдпрд╛ рд╣реИ, рдЦрд╛рд╕рдХрд░ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЗрд╕ рдЙрдореНрдореАрдж рдХреЛ рдХреИрд╕реЗ рддреЛрдбрд╝рддрд╛ рд╣реИ рдХрд┐ рдПрдХ рд╕рдВрджрд░реНрдн рдкреНрд░рдмрдВрдзрдХ рдХрд╛ рдмрд╛рд╣рд░ рдирд┐рдХрд▓рдиреЗ рд╡рд╛рд▓рд╛ рд╣рд┐рд╕реНрд╕рд╛ рд╣рдореЗрд╢рд╛ рдЪрд▓реЗрдЧрд╛ред рдореБрдЭреЗ рдореВрд▓ рд░реВрдк рд╕реЗ рд╕рднреА async рд╕рдВрджрд░реНрднреЛрдВ рдХреЛ рд░рджреНрдж рдХрд░рдиреЗ рд╕реЗ рдмрдЪрд╛рдирд╛ рдерд╛, рдЬреИрд╕реЗ:

async def handler(request):
    ws = web.WebSocketResponse()
    await ws.prepare(request)

    await asyncio.shield(asyncio.ensure_future(actually_do_stuff(ws)))

    return ws

async def actually_do_stuff(ws):
    async with contextlib.AsyncExitStack() as stack:
        # acquire_resource_X are async context managers
        await stack.enter_async_context(acquire_resource_1())
        await stack.enter_async_context(acquire_resource_2())
        await stack.enter_async_context(acquire_resource_3())

        async for msg in ws:
            # do stuff

    await ws.close()

рдХреНрдпрд╛ рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕рд╕реЗ рдЕрдЪреНрдЫрд╛ рддрд░реАрдХрд╛ рд╣реИ?

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

dcramer picture dcramer  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

sersorrel picture sersorrel  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

ZeusFSX picture ZeusFSX  ┬╖  5рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

rckclmbr picture rckclmbr  ┬╖  5рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

jonringer picture jonringer  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ