├── chat ├── requirements.txt ├── reader.py ├── tutorial1.py ├── client_dontwork.py ├── tutorial2.py ├── tutorial3.py ├── client.html ├── tutorial4.py └── server.py └── README.md /chat/requirements.txt: -------------------------------------------------------------------------------- 1 | websockets==2.4 2 | aiohttp==0.15.3 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A simple async chat server written in Python. 2 | 3 | 4 | [![Join the chat at https://gitter.im/oursky/pycon2015](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/oursky/pycon2015?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -------------------------------------------------------------------------------- /chat/reader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import websockets 5 | 6 | @asyncio.coroutine 7 | def hello(): 8 | websocket = yield from websockets.connect('ws://localhost:8765/') 9 | while True: 10 | msg = yield from websocket.recv() 11 | if msg is None: 12 | break 13 | print("< {}".format(msg)) 14 | 15 | asyncio.get_event_loop().run_until_complete(hello()) 16 | 17 | -------------------------------------------------------------------------------- /chat/tutorial1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import cgi 5 | import datetime 6 | import websockets 7 | 8 | 9 | def welcome_msg(): 10 | return "Welcome from server! Time now: {}".format( 11 | datetime.datetime.now().strftime('%H:%M:%S')) 12 | 13 | # ANSWER 14 | #@asyncio.coroutine 15 | #def hello(websocket, path): 16 | # yield from websocket.send(welcome_msg()) 17 | 18 | 19 | 20 | 21 | #FIXME 22 | def hello(websocket, path): 23 | websocket.send(welcome_msg()) #FIXME 24 | 25 | 26 | 27 | 28 | server = websockets.serve(hello, 'localhost', 8765) 29 | asyncio.get_event_loop().run_until_complete(server) 30 | asyncio.get_event_loop().run_forever() 31 | -------------------------------------------------------------------------------- /chat/client_dontwork.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import websockets 5 | 6 | 7 | @asyncio.coroutine 8 | def hello(): 9 | websocket = yield from websockets.connect('ws://localhost:8765/') 10 | asyncio.get_event_loop().create_task(display(websocket)) 11 | 12 | # Note: This does not work. It is because `input` blocks 13 | # the event loop. 14 | #while True: 15 | # name = input("Your message: ") 16 | # yield from websocket.send(name) 17 | # print("> {}".format(name)) 18 | 19 | 20 | @asyncio.coroutine 21 | def display(socket): 22 | while True: 23 | msg = yield from socket.recv() 24 | if msg is None: 25 | asyncio.get_event_loop().stop() 26 | break 27 | 28 | 29 | asyncio.get_event_loop().run_until_complete(hello()) 30 | -------------------------------------------------------------------------------- /chat/tutorial2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import datetime 5 | import time 6 | import websockets 7 | 8 | 9 | def welcome_msg(): 10 | return "Welcome from server! Time now: {}".format( 11 | datetime.datetime.now().strftime('%H:%M:%S')) 12 | 13 | # ANSWER 14 | #@asyncio.coroutine 15 | #def hello(websocket, path): 16 | # while True: 17 | # yield from websocket.send(welcome_msg()) 18 | # yield from asyncio.sleep(1) 19 | 20 | 21 | 22 | 23 | @asyncio.coroutine 24 | def hello(websocket, path): 25 | while True: 26 | yield from websocket.send(welcome_msg()) 27 | time.sleep(5) #FIXME 28 | 29 | 30 | 31 | 32 | server = websockets.serve(hello, 'localhost', 8765) 33 | asyncio.get_event_loop().run_until_complete(server) 34 | asyncio.get_event_loop().run_forever() 35 | -------------------------------------------------------------------------------- /chat/tutorial3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import cgi 5 | import datetime 6 | import websockets 7 | 8 | 9 | def welcome_msg(): 10 | return "Welcome from server! Time now: {}".format( 11 | datetime.datetime.now().strftime('%H:%M:%S')) 12 | 13 | 14 | 15 | sockets = [] 16 | 17 | 18 | 19 | 20 | @asyncio.coroutine 21 | def hello(websocket, path): 22 | yield from websocket.send(welcome_msg()) 23 | sockets.append(websocket) 24 | while True: 25 | msg = yield from websocket.recv() 26 | if msg is None: 27 | break 28 | msg = cgi.escape(msg) 29 | for s in sockets: 30 | yield from s.send("New message: " + msg) 31 | 32 | sockets.remove(websocket) 33 | 34 | 35 | 36 | 37 | server = websockets.serve(hello, 'localhost', 8765) 38 | asyncio.get_event_loop().run_until_complete(server) 39 | asyncio.get_event_loop().run_forever() 40 | -------------------------------------------------------------------------------- /chat/client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 50 | -------------------------------------------------------------------------------- /chat/tutorial4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import aiohttp 4 | import asyncio 5 | import cgi 6 | import datetime 7 | import websockets 8 | 9 | 10 | def welcome_msg(): 11 | return "Welcome from server! Time now: {}".format( 12 | datetime.datetime.now().strftime('%H:%M:%S')) 13 | 14 | 15 | 16 | sockets = [] 17 | 18 | 19 | 20 | 21 | @asyncio.coroutine 22 | def cat_html(): 23 | # Fetch cat html with image asynchronously 24 | resp = yield from aiohttp.request('GET', 25 | 'http://thecatapi.com/api/images/get?format=html') 26 | return (yield from resp.text()) 27 | 28 | 29 | 30 | 31 | @asyncio.coroutine 32 | def handle_msg(websocket, msg): 33 | # Find cat if client asked for `cat` 34 | if msg == 'cat': 35 | yield from websocket.send("Your cat is on the way.") 36 | output = yield from cat_html() 37 | else: 38 | output = "New message: " + msg 39 | 40 | # Broadcast `output` to all clients 41 | for s in sockets: 42 | yield from s.send(output) 43 | 44 | 45 | 46 | 47 | 48 | 49 | @asyncio.coroutine 50 | def hello(websocket, path): 51 | yield from websocket.send(welcome_msg()) 52 | sockets.append(websocket) 53 | while True: 54 | msg = yield from websocket.recv() 55 | if msg is None: 56 | break 57 | msg = cgi.escape(msg) 58 | yield from handle_msg(websocket, msg) 59 | sockets.remove(websocket) 60 | 61 | 62 | 63 | 64 | server = websockets.serve(hello, 'localhost', 8765) 65 | asyncio.get_event_loop().run_until_complete(server) 66 | asyncio.get_event_loop().run_forever() 67 | -------------------------------------------------------------------------------- /chat/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asyncio 4 | import datetime 5 | import websockets 6 | 7 | 8 | ANNOUNCE_TIME_INTERVAL = 30 9 | 10 | 11 | """ 12 | A list of sockets to send messages to. 13 | """ 14 | clients = [] 15 | 16 | 17 | @asyncio.coroutine 18 | def broadcast(msg): 19 | """ 20 | Send a message to all currently connected clients. 21 | """ 22 | for socket in clients: 23 | # Send network message asynchronously. 24 | yield from socket.send(msg) 25 | 26 | 27 | @asyncio.coroutine 28 | def announce_time(): 29 | """ 30 | Send a message containing the current time to all clients. 31 | 32 | This function loop infinitely and asynchronously sleep for an interval 33 | of time before broadcasting the message. 34 | """ 35 | while True: 36 | # `time.sleep` is synchronous, which will block the event loop. 37 | # `asyncio.sleep` is an asynchronous invariant that does 38 | # not block the event loop. 39 | yield from asyncio.sleep(ANNOUNCE_TIME_INTERVAL) 40 | time_now = datetime.datetime.now().strftime('%H:%M:%S') 41 | msg = "Current time is {}.".format(time_now) 42 | 43 | # `broadcast` is a coroutine function, to schedule the function 44 | # from execution, use `yield from`. 45 | yield from broadcast(msg) 46 | 47 | 48 | def announce_time2(): 49 | """ 50 | Send a message containing the current time to all clients. 51 | 52 | When the message is sent to all clients, this function schedule 53 | itself to be called by the event main loop after an interval of time. 54 | 55 | This function is not a coroutine because: 56 | 57 | 1. The `call_soon`/`call_later` functions of the event loop 58 | expects a callback instead of a coroutine object. 59 | 2. To demonstrate callback style function instead of a coroutine 60 | style function. 61 | """ 62 | time_now = datetime.datetime.now().strftime('%H:%M:%S') 63 | msg = "Current time is {}.".format(time_now) 64 | loop = asyncio.get_event_loop() 65 | 66 | # `broadcast` function is a coroutine function that returns a coroutine 67 | # object. `create_task` turns it into a `Task` (a future) that 68 | # is scheduled to run in the loop. 69 | loop.create_task(broadcast(msg)) 70 | 71 | # Schedule this function to be called again later to achieve a 72 | # periodically executed function. 73 | loop.call_later(ANNOUNCE_TIME_INTERVAL, announce_time2) 74 | 75 | 76 | @asyncio.coroutine 77 | def hello(websocket, path): 78 | yield from broadcast("Welcome our new user.") 79 | clients.append(websocket) 80 | try: 81 | while True: 82 | msg = yield from websocket.recv() 83 | if msg is None: 84 | break 85 | yield from broadcast(msg) 86 | finally: 87 | clients.remove(websocket) 88 | 89 | 90 | start_server = websockets.serve(hello, 'localhost', 8765) 91 | asyncio.get_event_loop().run_until_complete(start_server) 92 | #asyncio.get_event_loop().create_task(announce_time()) 93 | asyncio.get_event_loop().call_soon(announce_time2) 94 | asyncio.get_event_loop().run_forever() 95 | --------------------------------------------------------------------------------