How do I run twisted with flask? - python

I want to be able to run twisted servers on multiple different directories (exp: /example1, /example2...etc), So I thought I'd use flask. Here is what I have so far:
from flask import Flask
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
from twisted.web.proxy import ReverseProxyResource
from twisted.web.resource import Resource
app = Flask(__name__)
#app.route('/example1')
def index():
return 'My Twisted Flask'
flask_site = WSGIResource(reactor, reactor.getThreadPool(), app)
root = Resource()
root.putChild('my_flask', flask_site)
site_example = ReverseProxyResource('www.example.com', 80, ''.encode('utf-8'))
root.putChild('example1', site_example)
reactor.listenTCP(80, Site(root))
reactor.run()
The only problem is that it doesn't work, I'm not sure what I'm doing wrong. I appreciate any help, thanks!

My personal opinion: running Flask in Twisted's reactor isn't a good idea because Twisted's reactor is blocked when a request is processed by Flask.
I think you might be interested in Klein, which provided API similar to Flask, but works on Twisted out of the box: http://klein.readthedocs.io/en/latest/
Another option: I'd take a look into nginx as a reverse proxy for Flask applications instead of Twisted. nginx runs in a separate process and isn't blocked while a request is processed by Flask.
https://www.nginx.com/resources/admin-guide/reverse-proxy/

You can use twisted web, as documented on the Flask deploy documentation. Here's how I managed to run a server on my machine:
pip3 install twisted[tls]
export PYTHONPATH=${PYTHONPATH}:${PWD} # exports python path
twistd web -n --port tcp:5000 --wsgi path-to-your-app-root --logfile log.txt
Though I've had some issues with the server after it's up and running for my particular scenario, this might work for you

Related

Using flask-socketio, how can I asynchornously emit multiple messages in one function?

I'm building a Flask web app using the flask-socketio module to implement websockets. It generally works fine, but when I try to emit multiple messages from the server to the client in a for loop, all the messages are actually sent at once - that is, as soon as all of them have been created.
I read that the solution might be to use an eventlet server capable of asynchronous task handling:
"The simplest deployment strategy is to have eventlet or gevent installed, and start the web server by calling socketio.run(app) as shown in examples above. This will run the application on the eventlet or gevent web servers, whichever is installed." (taken from the Flask-SocketIO docs)
Sadly, that doesn't solve the problem. I've never worked with websockets before, so I'm a bit lost. Here is a simplified version of my code:
from flask import Flask
from flask_socketio import SocketIO, emit
import eventlet
app = Flask(__name__)
socketio = SocketIO(app)
#socketio.on("connect")
def handle_connect():
print("server and client connected")
#socketio.on("text")
def text(question):
for _ in range(3):
answer = my_module.generate_answer(question)
emit("message", {"msg": answer})
socketio.run(app)
Just assume that my_module.generate_answer() generates a sentence based on some user input sent via the websocket. Each generation takes 5-10 seconds. That's also the reason why I want the answers to be sent via a WebSocket once they're generated - my frontend could already display the first answer while waiting for the next ones.
Thank you so much for your help!
You need to monkey patch at the very top of your file.
Your import statements should look like this:
import eventlet
eventlet.monkey_patch()
from flask import Flask
from flask_socketio import SocketIO, emit

Trying to deploy a Flask app on CherryPy server

I was trying to deploy my Flask app on CherryPy server. I liked its simplistic and minimalistic nature.
So I PIP'ed CherryPy like below
pip install CherryPy-15.0.0-py2.py3-none-any.whl
and wrote script like below -very common suggested by many sources
from cherrypy import wsgiserver
from hello import app
d = wsgiserver.WSGIPathInfoDispatcher({'/': app})
server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
if __name__ == '__main__':
try:
server.start()
except KeyboardInterrupt:
server.stop()
To my surprise, I had imports errors. After a few googling around, I learned that I had to change my import lines to cheroot to make it work.
from cheroot.wsgi import Server
from cheroot.wsgi import PathInfoDispatcher
Now, my code is working fine.
However, I am a bit confused if this is the right way of using CherryPy WSGI server or if I pip'ed a wrong version of CherryPy. I am confused because Cheroot seems to be more than year old (dates all the way back to 2014), yet all the information I found around Flask on CherryPy WSGI server is using from cherrypy import wsgiserver, not from cheroot.wsgi import Server, even the latest postings.
This makes me unsure if I am doing the right thing or not.
Can someone please shed light on this confusion?
Cheroot (src) is a low-level HTTP and WSGI server, which used to be a part of CherryPy (src) once, but has been factored out into a separate repo a while back. So former cherrypy.wsgiserver has moved to cheroot.wsgi module.
It's completely replaceable and designed to allow developers to depend on Cheroot directly if they only use WSGI server, not requiring other parts of CherryPy.
So here's how you can use it in a version-agnostic way:
try:
from cheroot.wsgi import Server as WSGIServer, PathInfoDispatcher
except ImportError:
from cherrypy.wsgiserver import CherryPyWSGIServer as WSGIServer, WSGIPathInfoDispatcher as PathInfoDispatcher
from hello import app
d = PathInfoDispatcher({'/': app})
server = WSGIServer(('0.0.0.0', 80), d)
if __name__ == '__main__':
try:
server.start()
except KeyboardInterrupt:
server.stop()

How to run twisted with flask?

I wanna be able to run multiple twisted proxy servers on different directories on the same port simultaneously, and I figured I might use flask.
so here's my code:
from flask import Flask
from twisted.internet import reactor
from twisted.web import proxy, server
app = Flask(__name__)
#app.route('/example')
def index():
site = server.Site(proxy.ReverseProxyResource('www.example.com', 80, ''.encode("utf-8")))
reactor.listenTCP(80, site)
reactor.run()
app.run(port=80, host='My_IP')
But whenever I run this script, I get an Internal Server Error, I'm assuming because when app.run is called on port 80, the reactor.run can't be listening on port 80 as well. I wondering if there is some kind of work around to this, or what it is I'm doing wrong. Any help is greatly appreciated, Thanks!!
You can use the WSGIResource from Twisted istead of a ReverseProxy.
UPDATE: Added a more complex example that sets up a WSGIResource at /my_flask and a ReverseProxy at /example
from flask import Flask
from twisted.internet import reactor
from twisted.web.proxy import ReverseProxyResource
from twisted.web.resource import Resource
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
app = Flask(__name__)
#app.route('/example')
def index():
return 'My Twisted Flask'
flask_site = WSGIResource(reactor, reactor.getThreadPool(), app)
root = Resource()
root.putChild('my_flask', flask_site)
site_example = ReverseProxyResource('www.example.com', 80, '/')
root.putChild('example', site_example)
reactor.listenTCP(8081, Site(root))
reactor.run()
Try running the above in your localhost and then visiting localhost:8081/my_flask/example or localhost:8081/example
You should give klein a try. It's made and used by most of the twisted core devs. The syntax is very much like flask so you won't have to rewrite much if you already have a working flask app. So something like the following should work:
from twisted.internet import reactor
from twisted.web import proxy, server
from klein import Klein
app = Klein()
#app.route('/example')
def home(request):
site = server.Site(proxy.ReverseProxyResource('www.example.com', 80, ''.encode("utf-8")))
reactor.listenTCP(80, site)
app.run('localhost', 8000) # start the klein app on port 8000 and reactor event loop
Links
Klein Docs
Klein Github
The accepted answer does not cover how to run twisted with Flask, and points to a different framework. The answer with an example no longer works either.
Here are two different examples. The first one is the same as the first answer, but fixed to work on Python 3
from flask import Flask
from twisted.internet import reactor
from twisted.web.proxy import ReverseProxyResource
from twisted.web.resource import Resource
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
app = Flask(__name__)
#app.route('/example')
def index():
return 'My Twisted Flask'
flask_site = WSGIResource(reactor, reactor.getThreadPool(), app)
root = Resource()
root.putChild(b'my_flask', flask_site)
site_example = ReverseProxyResource('www.example.com', 80, b'/')
root.putChild(b'example', site_example)
reactor.listenTCP(8081, Site(root))
reactor.run()
For this example, run it and open any of these:
localhost:8081/my_flask/example
localhost:8081/example
This other example is recommended, since it sets up two services and provides them through a .tac file to twistd.
Take the base code from here: https://github.com/pika/pika/blob/master/examples/twisted_service.py
"""Modify the bottom of the file to pick the new MultiService"""
# ... all the code from twisted_service.py goes here.
# Scroll to the bottom of the file and
# remove everything below application = service.Application("pikaapplication")
# You should keep the PikaService, PikaProtocol and PikaFactory
# classes, since we need them for this code:
from pika.connection import ConnectionParameters
from pika.credentials import PlainCredentials
from twisted.application import service, strports
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from flask import Flask
# This IServiceCollection will hold Pika and Flask
flask_pika_multiservice = service.MultiService()
# FLASK SERVICE SETUP
app = Flask("demoapp")
#app.route('/')
def hello_world():
return 'Hello, World!'
flask_site = Site(WSGIResource(reactor, reactor.getThreadPool(), app))
# New resources can be added, such as WSGI, or proxies by creating
# a root resource in the place of the flask_site, and adding the
# new resources to the root.
# root = Resource()
# root.putChild(b'my_flask_site', flask_site)
# from twisted.web.proxy import ReverseProxyResource
# site_example = ReverseProxyResource('www.example.com', 80, b'/')
# root.putChild(b'example', site_example)
i = strports.service(f"tcp:8088", flask_site)
i.setServiceParent(flask_pika_multiservice)
# PIKA SERVICE SETUP
ps = PikaService(
ConnectionParameters(
host="localhost",
virtual_host="/",
credentials=PlainCredentials("guest", "guest")))
ps.setServiceParent(flask_pika_multiservice)
# Application setup
application = service.Application('flask_pika')
flask_pika_multiservice.setServiceParent(application)
Now you can run it with:
PYTHONPATH=. twistd -ny twisted_service.py
you can skip the python path if you don't want to import anything from the same path. twisted expects projects to actually be installed, and does not support running them directly from the source folder unless you use that workaround.
This second example establishes two services, on different ports. It's for pika and flask running simultaneously on the same twisted server. The best part is that it shows how to set up flask as a service, that can be part of an IServiceCollection

How to launch a Bottle application over a CherryPy standalone web server?

I have a python web app developed using the bottle framework. My bottle app is web API that provide methods that return JSon data, so no static content is needed. I am trying to deploy it to production using a CherryPy server which is supposed to be robust for production applications.
My web_api.py file (my bottle app) looks something like this:
from bottle import Bottle, request
app = Bottle()
#app.get('/stuff')
def do_stuff():
'''
Method that does stuff.
'''
stuff = {'data': 'some data'}
# Return the environment info as Json data
return stuff
I have a server.py file to launch the Bottle app over the CherryPy server that looks like this:
from my_package.web_api import app
from cherrypy.wsgiserver import CherryPyWSGIServer
server = CherryPyWSGIServer(
('0.0.0.0', 80),
app,
server_name='My_App',
numthreads=30)
server.start()
so when I run my server using this command:
python server.py
My server is successfully started and start listening in port 80 as expected. However once I start my web server I cannot stop it any more. I have tried Ctrl + C which works with the development server but has no effect here. Am I starting the server the right way? How do I stop it once it is running? Is this the correct way to launch a Bottle app over CherryPy?
BTW, I am running python 2.7 in Windows 8.
Your code is correct, you just need to add a try/catch statement:
from my_package.web_api import app
from cherrypy.wsgiserver import CherryPyWSGIServer
server = CherryPyWSGIServer(
('0.0.0.0', 80),
app,
server_name='My_App',
numthreads=30)
try:
server.start()
except KeyboardInterrupt:
server.stop()
You might wanna also consider to do some logging with wsgi-request-logger or something similar.
This are three alternative ways on hosting a WSGI application within cherrypy:
import cherrypy as cp
from cherrypy.wsgiserver import CherryPyWSGIServer
from cherrypy.process.servers import ServerAdapter
from bottle import Bottle
app = Bottle()
#app.get('/stuff')
def do_stuff():
'''
Method that does stuff.
'''
stuff = {'data': 'some dataX'}
return stuff
def run_decoupled(app, host='0.0.0.0', port=8080, **config):
server = CherryPyWSGIServer((host, port), app, **config)
try:
server.start()
except KeyboardInterrupt:
server.stop()
def run_in_cp_tree(app, host='0.0.0.0', port=8080, **config):
cp.tree.graft(app, '/')
cp.config.update(config)
cp.config.update({
'server.socket_port': port,
'server.socket_host': host
})
cp.engine.signals.subscribe() # optional
cp.engine.start()
cp.engine.block()
def run_with_adapter(app, host='0.0.0.0', port=8080, config=None, **kwargs):
cp.server.unsubscribe()
bind_addr = (host, port)
cp.server = ServerAdapter(cp.engine,
CherryPyWSGIServer(bind_addr, app, **kwargs),
bind_addr).subscribe()
if config:
cp.config.update(config)
cp.engine.signals.subscribe() # optional
cp.engine.start()
cp.engine.block()
The run_in_cp_tree and run_with_adapter functions are using the cherrypy engine, which enables the use of plugins to have off-the-shelf auto-reload, pidfile, daemonization, signal management and some more goodies, along with the possibility to create one of your own.
Notice that you can also use the WSGIPathInfoDispatcher to attach multiple wsgi applications on the CherryPyWSGIServer.
Trying to connect any WSGI server to my BottlePy app here in 2019 turned out to be rather tricky(to a noobie like me).
I tried connecting several ones, spent most off my time with CherryPy, which has changed his syntax.
The simpliest to me turned out to be waitress https://waitress.readthedocs.io/en/latest/usage.html
After i figured out how to use it on waitress i got it in cherrypy also. So:
CherryPy http://docs.cherrypy.org/en/latest/advanced.html?highlight=WSGi#host-a-foreign-wsgi-application-in-cherrypy
1)add after imports
import cherrypy as cp
app = bottle.Bottle()
2) change in routes "#bottle" to "#app"
3)add this as main function
cp.tree.graft(app, '/')
cp.server.start()
Waitress
1)add after imports
import waitress
app = bottle.Bottle()
2)add this as main function
waitress.serve(app, listen='*:44100')
3) change in routes "#bottle" to "#app"

arduino yun uhttpd flask setup

I'm trying to set up python and flask on the arduino yun. I've managed to run python files via the /etc/config/uhttpd configuration file:
...
list interpreter ".py=/usr/bin/python"
...
The default path for the website's root is: /www in which I've placed a soft link (apps) to the sd card. So now I can run python programs: http://[ip arduino]/apps/helloworld.py
And when I make my first helloflask.py program and run that via python helloflask.py I can see the result at: http://[ip arduino]:5000
But now I want to configure the uhttpd mini webserver (which is capable to exchange information via CGI) to use the flask setup. The URI: http://flask.pocoo.org/docs/deploying/cgi/#server-setup shows some instructions... but I just don't get it. I've made a directory ../apps/uno in which I've placed a __init__.py file with the following content:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "He Flask!"
In the apps dir I've put a file: cgi.py with this content:
from wsgiref.handlers import CGIHandler
from uno import app
CGIHandler().run(app)
Now I when I browse: http://[ip arduino]/cgi.py get a server error occured, contact the administrator (I think this is the CGI interface from uhttpd).
I just don't grasp the CGI configuration for Flask/uhttpd
I looked into this too and got a little further, I was able to setup a simple hello world but once I tried to do something non-trivial I ran into a big issue that uhttpd doesn't support URL rewriting/aliasing. This means your flask app can only be served at the URL of its .py file instead of at a root like http:// (arduino IP) /flaskapp/. None of the routes inside the app will be visible and makes the whole thing unusable.
However, instead of trying to force flask into uhttpd I had great success running the built in server that flask provides. Take a look at this guide I wrote up that uses flask to serve data from a Yun: https://learn.adafruit.com/smart-measuring-cup/overview
The thing to do is add a call to app.run when the script is run, for example make your flask app look like:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello Flask!"
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, threaded=True)
Then log in to the Yun and run the script using python. Flask's built in server should start serving the app on http:// (arduino IP) :5000/. Make sure to include the host='0.0.0.0' as it's required to listen on the Yun's external network interface. You probably also want debug=True so there are better error messages (and live reloading of the server when the code changes), and I found threaded=True helps because the default server only handles one connection at a time. The Yun is a relatively slow processor so don't expect to service a lot of concurrent requests, however it's quite capable for providing a simple REST API or web application for a few users.
If you want this server to always run on bootup, edit the /etc/rc.local file to include a call to python and your script.

Categories