from aiohttp import web
async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
app = web.Application()
app.add_routes([web.get('/', handle),
web.get('/{name}', handle)])
if __name__ == '__main__':
web.run_app(app)
The above example is from oficial documentation.
The question is how can i terminate the web.run_app(app) operations with no KeyBoardInterrupt (Ctrl+C)?
I am looking something like:
await app.shutdown()
await app.cleanup()
but i don't know where can i put this code and how can i use it.
From the source, it looks response for KeyboardInterrupt and GracefulExit:
try:
asyncio.set_event_loop(loop)
loop.run_until_complete(main_task)
except (GracefulExit, KeyboardInterrupt): # pragma: no cover
pass
finally:
_cancel_tasks({main_task}, loop)
_cancel_tasks(asyncio.all_tasks(loop), loop)
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()
asyncio.set_event_loop(None)
As you don't want to stop in manual, you could raise GracefulExit in your own code, something like next:
test.py:
import signal
from aiohttp import web
from aiohttp.web_runner import GracefulExit
async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
async def shutdown(request):
print("will shutdown now")
raise GracefulExit()
app = web.Application()
app.add_routes([web.get('/shutdown', shutdown),
web.get('/', handle),
web.get('/{name}', handle)])
if __name__ == '__main__':
web.run_app(app)
Once you want to close the server, you could send http://ip:8080/shutdown to server.
Additional, if you want to exit the server directly after receive a request, you may also use signal.alarm(3), then you no need to define a handler with /shutdown. This means send a alarm to program after 3 seconds after receive any request (aiohttp internal already register a signal handler with loop.add_signal_handler(signal.SIGINT, _raise_graceful_exit)):
async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
signal.alarm(3)
return web.Response(text=text)
Anyway, the way to close the server is either raise GracefulExit or send signal, but the timing to exit the server depends on your scenario.
Based on #atline answer i wrote this code:
import signal
from aiohttp import web
from aiohttp.web_runner import GracefulExit
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from multiprocessing import cpu_count
import requests
import time
async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
async def shutdown(request):
print("will shutdown now")
raise GracefulExit()
app = web.Application()
app.add_routes([web.get('/shutdown', shutdown),
web.get('/', handle),
web.get('/{name}', handle)])
def init_aiohttp():
web.run_app(app, host="127.0.0.1", port="8080", ssl_context=None)
if __name__ == '__main__':
executor = ProcessPoolExecutor()
for i in range(0, 1):
aiohttp = executor.submit(init_aiohttp)
total_time = 0
while(True):
time.sleep(1)
total_time += 1
if total_time == 5:
try:
requests.get('http://127.0.0.1:8080/shutdown')
except Exception as e:
print(e)
print(total_time)
The above code:
Starts an aiohttp server.
Close the server after 5 second (fetching the shutdown url: http://127.0.0.1:8080/shutdown)
Hope that will help someone in the future.
Related
I am trying to emulate the reading of a NFC chip by pressing the cmd key in a little python script that acts as a socket-io server. It's job is to notify a client after the cmd key has been pressed. It works, but emulate_nfc_tag() takes ages to execute after i press cmd. I suspect it has to do with how create_task works. I hope someone can spot how i could optimize this code.
from aiohttp import web
import socketio
import asyncio
from pynput import keyboard
import random
import string
sio = socketio.AsyncServer(cors_allowed_origins='*',
async_mode='aiohttp')
app = web.Application()
sio.attach(app)
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
async def emulate_nfc_tag():
print("sending out nfc id")
await sio.emit("nfc", id_generator())
def on_press(key):
if key == keyboard.Key.shift:
print("here we go")
loop.create_task(emulate_nfc_tag())
if key == keyboard.Key.esc:
# Stop listener
return False
async def index(request):
"""Serve the client-side application."""
with open('../client/dist/index.html') as f:
return web.Response(text=f.read(), content_type='text/html')
#sio.event
def connect(sid, environ):
print("connect ", sid)
#sio.event
def disconnect(sid):
print('disconnect ', sid)
#sio.event
async def msg(sid, data):
print('message ', data)
await sio.emit("msg", "Hi back")
app.router.add_static('/assets', '../client/dist/assets')
app.router.add_get('/', index)
if __name__ == '__main__':
# web.run_app(app)
loop = asyncio.get_event_loop()
listener = keyboard.Listener(on_press=on_press)
listener.start()
# set up aiohttp - like run_app, but non-blocking
runner = web.AppRunner(app)
loop.run_until_complete(runner.setup())
site = web.TCPSite(runner)
loop.run_until_complete(site.start())
# add more stuff to the loop
loop.run_forever()
The handlers that you configure for your keyboard run on a background thread and not in the thread that holds your asyncio application. You are calling the create_task() function from this handler, which is invalid because this function must be called from the asyncio thread.
Instead, you may want to try with the run_coroutine_threadsafe(), which is specifically designed for your use case in which you want to start a coroutine in the asyncio thread, but doing it from another thread.
Code:
#!/usr/bin/env python
import asyncio
import os
import socket
import time
import traceback
from aiohttp import web
from concurrent.futures import ProcessPoolExecutor
from multiprocessing import cpu_count
CPU_COUNT = cpu_count()
print("CPU Count:", CPU_COUNT)
def mk_socket(host="127.0.0.1", port=9090, reuseport=False):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if reuseport:
SO_REUSEPORT = 15
sock.setsockopt(socket.SOL_SOCKET, SO_REUSEPORT, 1)
sock.bind((host, port))
return sock
async def index(request):
icecast_index_path = os.path.abspath("../test/icecast/icecast_index.html")
print(icecast_index_path)
try:
content = open(icecast_index_path, encoding="utf8").read()
return web.Response(content_type="text/html", text=content)
except Exception as e:
return web.Response(content_type="text/html", text="<!doctype html><body><h1>Error: "+str(e)+"</h1></body></html>")
async def start_server():
try:
host = "127.0.0.1"
port=8080
reuseport = True
app = web.Application()
app.add_routes([web.get('/', index)])
runner = web.AppRunner(app)
await runner.setup()
sock = mk_socket(host, port, reuseport=reuseport)
srv = web.SockSite(runner, sock)
await srv.start()
print('Server started at http://127.0.0.1:8080')
return srv, app, runner
except Exception:
traceback.print_exc()
raise
async def finalize(srv, app, runner):
sock = srv.sockets[0]
app.loop.remove_reader(sock.fileno())
sock.close()
#await handler.finish_connections(1.0)
await runner.cleanup()
srv.close()
await srv.wait_closed()
await app.finish()
def init():
loop = asyncio.get_event_loop()
srv, app, runner = loop.run_until_complete(start_server())
try:
loop.run_forever()
except KeyboardInterrupt:
loop.run_until_complete((finalize(srv, app, runner)))
if __name__ == '__main__':
with ProcessPoolExecutor() as executor:
for i in range(0, int(CPU_COUNT/2)):
executor.submit(init)
#after the aiohttp start i want to execute more code
#for example:
print("Hello world.")
#in actual programm the ProcessPoolExecutor is called
#inside a pyqt5 app
#so i don't want the pyqt5 app to freeze.
The problem is that with that code i can't execute code after the ProcessPoolExecutor calls.
How can i fix that?
I tried to remove this part:
try:
loop.run_forever()
except KeyboardInterrupt:
loop.run_until_complete((finalize(srv, app, runner)))
in init() method but after that the aiohttp server is closed instantly.
Edit: If i use a thread instead of ProcessPoolExecutor then there is an aiohttp errors that says:
RuntimeError: set_wakeup_fd only works in main thread
RuntimeError: There is no current event loop in thread
Aiohttp + asyncio related errors.
Maybe i may use a # signature up to def declarations (i suppose).
Using with with an Executor will cause your process to block until the jobs in the executor are completed; since they’re running infinite event loops, they will never complete and your Executor will never unblock.
Instead, just use the executor to kick off the jobs, and run your stuff afterwards. When you’re finally done, call .shutdown() to wait for processes to exit:
executor = ProcessPoolExecutor()
for i in range(0, int(CPU_COUNT/2)):
executor.submit(init)
# other code…
executor.shutdown()
I would like to implement Python Tornado Websocket Server inside another Python (main) and trigger send messages when needed. The main creates two threads. One of them is for Python Server and the other is for my loop that will trigger message.
When I start server from initial, server works fine however because its endless following main files doesn't run. So I start server inside a thread but this time I receive "RuntimeError: There is no current event loop in thread 'Thread-1 (start_server)'"
Main.py
import tornadoserver
import time
from threading import Lock, Thread
class Signal:
def __init__(self):
#self.socket = tornadoserver.initiate_server()
print("start")
def start_server(self):
print("start Server")
self.socket = tornadoserver.initiate_server()
def brd(self):
print("start Broad")
i = 0
while True:
time.sleep(3)
self.socket.send(i)
i = i + 1
def job(self):
# --------Main--------
threads = []
for func in [self.start_server, self.brd, ]:
threads.append(Thread(target=func))
threads[-1].start()
for thread in threads:
thread.join()
Signal().job()
tornadoserver.py
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.websocket as ws
from tornado.options import define, options
import time
define('port', default=4041, help='port to listen on')
ws_clients = []
class web_socket_handler(ws.WebSocketHandler):
#classmethod
def route_urls(cls):
return [(r'/', cls, {}), ]
def simple_init(self):
self.last = time.time()
self.stop = False
def open(self):
self.simple_init()
if self not in ws_clients:
ws_clients.append(self)
print("New client connected")
self.write_message("You are connected")
def on_message(self, message):
if self in ws_clients:
print("received message {}".format(message))
self.write_message("You said {}".format(message))
self.last = time.time()
def on_close(self):
if self in ws_clients:
ws_clients.remove(self)
print("connection is closed")
self.loop.stop()
def check_origin(self, origin):
return True
def send_message(self, message):
self.write_message("You said {}".format(message))
def send(message):
for c in ws_clients:
c.write_message(message)
def initiate_server():
# create a tornado application and provide the urls
app = tornado.web.Application(web_socket_handler.route_urls())
# setup the server
server = tornado.httpserver.HTTPServer(app)
server.listen(options.port)
# start io/event loop
tornado.ioloop.IOLoop.instance().start()
Using Google I found tornado issue
Starting server in separate thread gives... RuntimeError: There is no current event loop in thread 'Thread-4' · Issue #2308 · tornadoweb/tornado
and it shows that it has to use
asyncio.set_event_loop(asyncio.new_event_loop())
to run event loop in new thread
Something like this
import asyncio
# ...
def initiate_server():
asyncio.set_event_loop(asyncio.new_event_loop()) # <---
# create a tornado application and provide the urls
app = tornado.web.Application(web_socket_handler.route_urls())
# setup the server
server = tornado.httpserver.HTTPServer(app)
server.listen(options.port)
# start io/event loop
tornado.ioloop.IOLoop.instance().start()
How do you send and receive messages using pythons asyncio and the websockets library?
I am using Django Channels as the socket server. So basically I am trying to send and receive from this socket server.
I can easily send messages to my websocket server using:
#!/usr/bin/python3
async def main():
uri = f"{wsPrefix}stream/{machineSerial}/?{token}"
async with websockets.connect(uri, ping_interval = None) as websocket:
try:
cap = acapture.open(0)
except:
asyncio.sleep(1)
cap = acapture.open(0)
while True:
check,frame = cap.read()
if not websocket.open:
print("### reconnecting ###")
await websockets.connect(uri,ping_interval = None)
if check:
frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
check, jpeg = cv2.imencode('.jpg', frame)
frame = jpeg.tobytes()
frame = bytearray(frame)
await websocket.send(frame)
await asyncio.sleep(0.05)
if __name__ == "__main__":
import websockets
import asyncio
import acapture
import cv2
from deviceSpecificVals import machineSerial, token
import deviceSpecificVals.url
urlPrefix = deviceSpecificVals.url
wsPrefix = deviceSpecificVals.wsPrefix
asyncio.run(main())
But how can you add another async function here to always be listening for a message? I want the receiver to finish the script and close when it receives a 'close' message.
I tried:
#!/usr/bin/python3
async def connect():
uri = f"{wsPrefix}stream/{machineSerial}/?{token}"
async with websockets.connect(uri, ping_interval = None) as websocket:
return websocket
async def rcv(websocket):
while True:
msg = await websocket.recv()
print(f"< {msg}")
async def send(websocket):
uri = f"{wsPrefix}stream/{machineSerial}/?{token}"
try:
cap = acapture.open(0)
except:
asyncio.sleep(1)
cap = acapture.open(0)
while True:
check,frame = cap.read()
if not websocket.open:
print("### reconnecting ###")
await websockets.connect(uri,ping_interval = None)
if check:
frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
check, jpeg = cv2.imencode('.jpg', frame)
frame = jpeg.tobytes()
frame = bytearray(frame)
await websocket.send(frame)
await asyncio.sleep(0.05)
async def main():
websocket = await connect()
asyncio.ensure_future(send(websocket))
asyncio.ensure_future(rcv(websocket))
await asyncio.sleep(100000)
if __name__ == "__main__":
import websockets
import asyncio
import acapture
import cv2
from deviceSpecificVals import machineSerial, token
import deviceSpecificVals.url
urlPrefix = deviceSpecificVals.url
wsPrefix = deviceSpecificVals.wsPrefix
asyncio.run(main())
This will not connect to websocket, it is almost as if the connect function is not returning the websocket object. I am also very new to async, and trying to understand what is happening here
Websocket is a protocol, so you have to run a web engine that supports that protocol. I like gevent better since you can run both the webserver and the websocket on the same server and it will upgrade the connection for you if it sees it.
Here is a template for you to get started. Obviously you need to connect this to a websocket client.
Instead of AsyncIO I use gevent since the monkey patching makes the async process seamless.
from gevent import monkey, signal_handler as sig, sleep
monkey.patch_all()
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
import signal
import traceback
import ujson as json
import bottle
from bottle import route, get, post, request, response
#route('/ws')
def handle_websocket():
ws = request.environ.get('wsgi.websocket')
if not ws:
abort(400, 'Expected WebSocket request.')
while 1:
message = None
try:
with Timeout(2, False) as timeout: # this adds a timeout to the wait for recieve, since it will wait forever.
message = ws.receive()
if message:
message = json.loads(message)
except WebSocketError:
break
except Exception as exc:
traceback.print_exc()
sleep(1)
if __name__ == '__main__':
print('Server Running...')
botapp = bottle.app()
server = WSGIServer(("0.0.0.0", 80), botapp, handler_class=WebSocketHandler)
def shutdown():
print('Shutting down ...')
server.stop(timeout=60)
exit(signal.SIGTERM)
sig(signal.SIGTERM, shutdown)
sig(signal.SIGINT, shutdown)
server.serve_forever()
I am building a pure python application with flask socket-io. Currently, I am trying to have the server emit an event to a specific client and wait for the callback before moving on.
This is my Server.py
import socketio
import eventlet
sio = socketio.Server(async_handlers=False)
app = socketio.WSGIApp(sio)
#sio.event
def connect(sid, environ):
nm = None
def namee(name):
print(name) # this has the value and is trying to assign it to nm
nonlocal nm
nm = name
sio.emit('name_', "name plz", callback=namee)
print(nm) # this shouldn't be None, but it is
print(sid, "in lobby")
#sio.event
def disconnect(sid):
print('disconnect', sid)
if __name__ == '__main__':
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)
And this is my client.py
import sys
import socketio
sio = socketio.Client()
#sio.event
def connect():
print("you have connected to the server")
#sio.event
def connect_error(data):
print("The connection failed!")
#sio.event
def disconnect():
print("You have left the server")
#sio.event
def name_(data):
print("name asked for")
return "test"
def main():
sio.connect('http://localhost:5000')
print('Your sid is', sio.sid)
if __name__ == '__main__':
main()
I tried using time.sleep() but that delayed the whole process. I also tried making a while loop
while nm is None:
pass
but that kicked the client off the server and a while later, the server crashed.
You can use the call() method instead of emit().
nm = sio.call('name_', "name plz")
print(nm)
I recommend that you move this logic outside of the connect handler though. This handler is just to accept or reject the connection, it is not supposed to block for a long period of time. Do this in a regular handler that you use after the connection has been established.