How to run Python WSGIServer using gunicorn? - python

I have implemented a simple Flask-like framework using HTTPServer and BaseHTTPRequestHandler from http.server for educational purposes.
I have found out that I can replace HTTPServer with WSGIServer from wsgiref.simple_server and the code runs without any other modification.
The run method of my implementation works as follows:
def run(self, host: str, port: int):
web_server = WSGIServer((host, port), Request)
try:
print("Server is running at port", port)
web_server.serve_forever()
except KeyboardInterrupt:
web_server.server_close()
print("Server stopped")
When I read the gunicorn docs, I have found out that gunicorn expects a function like this:
def app(environ, start_response):
"""Simplest possible application object"""
data = b'Hello, World!\n'
status = '200 OK'
response_headers = [
('Content-type', 'text/plain'),
('Content-Length', str(len(data)))
]
start_response(status, response_headers)
return iter([data])
And that wsgiref.simple_server.make_server takes a function like the above and instantiates a WSGIServer and calls the serve_forever() method like I did in my implementation.
I have tried to return the app from thr WSGIServer from the WSGIServer.get_app() method but it returns None.
I would like to know how to run my WSGIServer class using gunicorn from the terminal.

If you have a framework-like app, then one best way to serve it is to wrap the framework entry point as a callable class, and pass this as a whole to gunicorn.
Here are examples for Waitress web server and Gunicorn web server how to make it serve the same WSGI app
# Router.__call__() is the entry point for the request processing
def setup_app() -> Router:
context.init_console_logging()
app = create_app(production=True, oracle_context=context)
check_oracle_db()
check_web_db()
from tradingstrategy import __version__
from .api import version as api_version
logger.info("Server version is %s, client library version %s", api_version, __version__)
return app
# Waitress main entry point
def main():
app = setup_app()
port = int(os.environ.get("BACKEND_PORT", 3456))
scheme = os.environ.get("WSGI_SCHEME", "http")
logger.info("Matilda backend server starting at port %d", port)
serve(app,
host='127.0.0.1',
trusted_proxy="127.0.0.1",
threads=32,
port=port,
trusted_proxy_headers="forwarded",
url_scheme=scheme,
url_prefix='/api')
def gunicorn_entry_point() -> Router:
"""Gunicorn calls this function for every worker process.
To run::
gunicorn --bind 127.0.0.1:3456 --workers 1 --threads 1 "backend.server.server:gunicorn_entry_point()"
"""
app = setup_app()
return app
See Pyramid Router implementation for WSGI app. It is a good example to look upon if you are unsure how to deal with WSGI framework.
class Router:
# ...
def __call__(self, environ, start_response):
"""
Accept ``environ`` and ``start_response``; create a
:term:`request` and route the request to a :app:`Pyramid`
view based on introspection of :term:`view configuration`
within the application registry; call ``start_response`` and
return an iterable.
"""
response = self.execution_policy(environ, self)
return response(environ, start_response)

Related

How can i invoke a flask endpoint directly from code without using requests

Hey everyone I was tasked a few days ago to create an API style application that listens over a TCP socket for some commands then return some responses mainly success/failures (i know it's dumb but it's the client request) since I have some validation/database stuff I thought of flask directly but I am still stuck on how I am going to invoke the specific endpoints in code directly. here is a small snippet on how I am imagining things would be
from flask import Flask
import threading
data = 'foo'
app = Flask(__name__)
#app.route("/SomeCommand")
def SomeCommand():
return { 'Some' : 'Response'}
def flaskThread():
app.run()
def TcpListenner():
# logic that listens over tcp socket then invoks the flask app
# I was thinking about calling app.something() from here
pass
if __name__ == "__main__":
flaskApp = threading.Thread(target=flaskThread)
flaskApp.start()
listenner = threading.Thread(target=TcpListenner)
listenner.start()
any help/ideas would be much appreciated, thank you
You can use flask_socketio with which the flask app and socket i.e. tcp listener both start together...
Based on what I've understood, you can do something like this:
That the client will first make a connection to the flask socket.
Then, to send a command to the flask app, the client will send a message to the flask socket with the command in its message.
The flask socket will be listening for messages. So when it receives a message for the specific command, then it emits a response based on that command to the socket which will then be received by the client.
Below is an example code for the flask socket app:
import eventlet
eventlet.monkey_patch()
from flask import Flask
from flask_socketio import SocketIO, send, emit
app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*", logger=True, engineio_logger=True)
# other flask APIs can come here and can be called by the client...
def someCommandOneResponse(): # function that sends response to client when flask socket gets command 1
commandOneResponse = 'Success'
socketio.emit('message', commandOneResponse)
def someCommandTwoResponse(): # function that sends response to client when flask socket gets command 2
commandTwoResponse = 'Failure'
socketio.emit('message', commandTwoResponse)
#socketio.on('message') # when any command is received on the socket from the client
def handleMessage(cmd):
print('\nCommand Received: ' + cmd + '\n')
if( cmd == 'SomeCommand1' ): # if the client has sent a message for command 1
print('Got Some Command 1')
someCommandOneResponse()
elif( cmd == 'SomeCommand2' ): # if the client has sent a message for command 2
print('Got Some Command 2')
someCommandTwoResponse
send(cmd, broadcast=True)
if __name__ == '__main__':
socketio.run(app, port=3000) # starts the flask socket (tcp listener) as well as the flask app

How to run flask server from another module?

I am trying to run a flask server from another module and then close the server (it's not the main point of the application), currently the code looks like this for webserver.py
from flask import Flask, render_template
from flask_classful import FlaskView
import os
from werkzeug.utils import secure_filename
from flask import request
host = 'localhost'
debug = True
app = Flask (__name__)
app.config
#app.route('/')
def main():
return render_template("index.html")
def run():
if __name__ == 'Refactor2':
app.run(ssl_context = (r"path_to_cert", r"path_to_key"), host = "localhost", port = 443)
def shutdown_server():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
def shutdownServer():
shutdown_server()
return "Server is shutting down"
I am then trying to run this in the following module (named Refactor2):
import webserver
class buyer:
def __init__(self, email, password):
self.email = email
self.password = password
def server_run(self):
run()
print("Server has been started")
However, the server does not run and I cannot navigate to the webpage. There are no error messages shown.
Seems like an unusual way to launch a Flask app, but with that aside...
Assuming you're running this with python Refactor2.py, __name__ will be 'webserver'.
This can be demonstrated with a minimal example:
$ cat webserver.py
def run():
print (__name__)
$ cat Refactor2.py
import webserver
webserver.run()
$ python Refactor2.py
webserver
So in your webserver module you could do do:
def run():
if __name__ == 'webserver':
app.run(ssl_context = (r"path_to_cert", r"path_to_key"), host = "localhost", port = 443)
Or just:
def run():
app.run(ssl_context = (r"path_to_cert", r"path_to_key"), host = "localhost", port = 443)
And call it like:
import webserver
# ...
class buyer:
# ...
def server_run(self):
webserver.run()
print("Server has been started")
Of course, that 'Server has been started' won't actually be reached until the server (the webserver.run call) has completed and the server has stopped.
Again, this seems like an odd way to launch an application. I suggest you investigate the docs on how to run Flask in production, as it seems what you're trying to acheive is the job of an orchestration system, and not the python process itself. In a production env, you should use a WSGI server like gunicorn, instead of the dev server (app.run). Granted, perhaps you know this already ;)
__name__ gives the name of the current module. So in your run() method __name__ is probably resolving to webserver. I'd suggest passing the name as a variable to run() from server_run()
webserver.py
def run(name):
if name == 'Refactor2':
app.run(ssl_context = (r"path_to_cert", r"path_to_key"), host = "localhost", port = 443)
Refactor2
import webserver
class buyer:
def __init__(self, email, password):
self.email = email
self.password = password
def server_run(self):
run(__name__)
print("Server has been started")
Note that if Refactor2 is in a package you might get a dotted module name, like 'src.Refactor2'. It might be better to pass a more explicit name to run, as if you rename Refactor2 you'll need to update your hardcode in webserver.py too. Something like
def server_run(self):
webserver.run('buyer')
print("Server has been started")
and
(webserver.py)
def run(name):
if name == 'buyer':
app.run(ssl_context = (r"path_to_cert", r"path_to_key"), host = "localhost", port = 443)
edit: not sure if you provided a full code example, but it also looks like maybe you're not calling the right run() method in server_run() - it looks like it should be called on the webserver module, which I edited my answer to reflect

Add Tensorboard server to Flask endpoint

I have a Flask App and a Tensorboad server. Is there a way by which I can map the Tensorboard server to one of the endpoints of Flask so that as soon as I hit that endpoint it triggers the Tensorboard server?
Flask application
from flask import Flask, jsonify, request
app = Flask(__name__)
#app.route('/hello-world', methods=['GET', 'POST'])
def say_hello():
return jsonify({'result': 'Hello world'})
if __name__ == "__main__":
app.run(host=host, port=5000)
Tensorboard server code:
from tensorboard.program import TensorBoard, setup_environment
def tensorboard_main(host, port, logdir):
configuration = list([""])
configuration.extend(["--host", host])
configuration.extend(["--port", port])
configuration.extend(["--logdir", logdir])
tensorboard = TensorBoard()
tensorboard.configure(configuration)
tensorboard.main()
if __name__ == "__main__":
host = "0.0.0.0"
port = "7070"
logdir = '/tmp/logdir'
tensorboard_main(host, port, logdir)
I tried creating an endpoint in Flask app and then added tensorboard_main(host, port, logdir) in the hope that if I hit the endpoint then the server will start but I got no luck.
I found out that to integrate a TensorBoard server into a larger Flask app, the best way would be to compose the applications at the WSGI level.
The tensorboard.program API allows us to pass a custom server
class. The signature here is that server_class should be a callable which takes the TensorBoard WSGI app and returns a server that satisfies the TensorBoardServer interface.
Hence the code is:
import os
import flask
import tensorboard as tb
from werkzeug import serving
from werkzeug.middleware import dispatcher
HOST = "0.0.0.0"
PORT = 7070
flask_app = flask.Flask(__name__)
#flask_app.route("/hello-world", methods=["GET", "POST"])
def say_hello():
return flask.jsonify({"result": "Hello world"})
class CustomServer(tb.program.TensorBoardServer):
def __init__(self, tensorboard_app, flags):
del flags # unused
self._app = dispatcher.DispatcherMiddleware(
flask_app, {"/tensorboard": tensorboard_app}
)
def serve_forever(self):
serving.run_simple(HOST, PORT, self._app)
def get_url(self):
return "http://%s:%s" % (HOST, PORT)
def print_serving_message(self):
pass # Werkzeug's `serving.run_simple` handles this
def main():
program = tb.program.TensorBoard(server_class=CustomServer)
program.configure(logdir=os.path.expanduser("~/tensorboard_data"))
program.main()
if __name__ == "__main__":
main()
You can use multiprocessing here: create one process for flask and another for tensorboard, then run it on the same host.
Code:
from multiprocessing import Process
from tensorboard.program import TensorBoard
from flask import Flask, jsonify
app = Flask(__name__)
def tensorboard_main(host, port, logdir):
configuration = list([""])
configuration.extend(["--host", host])
configuration.extend(["--port", port])
configuration.extend(["--logdir", logdir])
tensorboard = TensorBoard()
tensorboard.configure(configuration)
tensorboard.main()
def flask_main(app, host, port):
return app.run(host=host, port=port)
#app.route("/hello-world", methods=["GET", "POST"])
def say_hello():
return jsonify({"result": "Hello world"})
if __name__ == "__main__":
host = "0.0.0.0"
port_for_tensorboard = "7070"
port_for_flask = "5000"
logdir = "/tmp/logdir"
process_for_tensorboard = Process(target=tensorboard_main, args=(host, port_for_tensorboard, logdir))
process_for_flask = Process(target=flask_main, args=(app, host, port_for_flask))
process_for_tensorboard.start()
process_for_flask.start()
process_for_tensorboard.join()
process_for_flask.join()
If you want that inside flask endpoint tensorboard will be show some things, then you need to look at shared data from one process to another (think it would be more complicated than this example)

web.py - specify address and port

How to specify listening address and port in web.py? Something like:
web.application( urls, host="33.44.55.66", port=8080 )
Edit
I would like to avoid using the default web.py command line parsing
From API docmentation of web.py:
module web.httpserver
function runsimple(func,server_address=('0.0.0.0', 8080))
Runs CherryPy WSGI server hosting WSGI app func. The directory static/ is hosted statically.
Example code
import web
class MyApplication(web.application):
def run(self, port=8080, *middleware):
func = self.wsgifunc(*middleware)
return web.httpserver.runsimple(func, ('0.0.0.0', port))
if __name__ == "__main__":
app = MyApplication(urls, globals())
app.run(port=8888)
If you're using web.py's built-in webserver, you can just append the port to the command:
python app.py 8080
I haven't tried ever with the listening address, but perhaps it will accept 1.2.3.4:8080 as the format.
you can see the follow code in wsgi.py:
server_addr = validip(listget(sys.argv, 1, ''))
if os.environ.has_key('PORT'): # e.g. Heroku
server_addr = ('0.0.0.0', intget(os.environ['PORT']))
return httpserver.runsimple(func, server_addr)
so, you can set the web server port by add environ variable:
import os
os.environ["PORT"] = "80"
URLS = ("/", index)
class index:
def GET:
....
if __name__ == "__main__":
app = web.application(URLS, globals())
web.httpserver.runsimple(app.wsgifunc(), ("0.0.0.0", 8888))

Bottle web framework - How to stop?

When starting a bottle webserver without a thread or a subprocess, there's no problem. To exit the bottle app -> CTRL + c.
In a thread, how can I programmatically stop the bottle web server ?
I didn't find a stop() method or something like that in the documentation. Is there a reason ?
For the default (WSGIRef) server, this is what I do (actually it is a cleaner approach of Vikram Pudi's suggestion):
from bottle import Bottle, ServerAdapter
class MyWSGIRefServer(ServerAdapter):
server = None
def run(self, handler):
from wsgiref.simple_server import make_server, WSGIRequestHandler
if self.quiet:
class QuietHandler(WSGIRequestHandler):
def log_request(*args, **kw): pass
self.options['handler_class'] = QuietHandler
self.server = make_server(self.host, self.port, handler, **self.options)
self.server.serve_forever()
def stop(self):
# self.server.server_close() <--- alternative but causes bad fd exception
self.server.shutdown()
app = Bottle()
#app.route('/')
def index():
return 'Hello world'
#app.route('/stop') # not working from here, it has to come from another thread
def stopit():
server.stop()
server = MyWSGIRefServer(port=80)
try:
app.run(server=server)
except:
print('Bye')
When I want to stop the bottle application, from another thread, I do the following:
server.stop()
I had trouble closing a bottle server from within a request as bottle seems to run requests in subprocesses.
I eventually found the solution was to do:
sys.stderr.close()
inside the request (that got passed up to the bottle server and axed it).
An updated version of mike's answer.
from bottlepy.bottle import WSGIRefServer, run
from threading import Thread
import time
class MyServer(WSGIRefServer):
def run(self, app): # pragma: no cover
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
from wsgiref.simple_server import make_server
import socket
class FixedHandler(WSGIRequestHandler):
def address_string(self): # Prevent reverse DNS lookups please.
return self.client_address[0]
def log_request(*args, **kw):
if not self.quiet:
return WSGIRequestHandler.log_request(*args, **kw)
handler_cls = self.options.get('handler_class', FixedHandler)
server_cls = self.options.get('server_class', WSGIServer)
if ':' in self.host: # Fix wsgiref for IPv6 addresses.
if getattr(server_cls, 'address_family') == socket.AF_INET:
class server_cls(server_cls):
address_family = socket.AF_INET6
srv = make_server(self.host, self.port, app, server_cls, handler_cls)
self.srv = srv ### THIS IS THE ONLY CHANGE TO THE ORIGINAL CLASS METHOD!
srv.serve_forever()
def shutdown(self): ### ADD SHUTDOWN METHOD.
self.srv.shutdown()
# self.server.server_close()
def begin():
run(server=server)
server = MyServer(host="localhost", port=8088)
Thread(target=begin).start()
time.sleep(2) # Shut down server after 2 seconds
server.shutdown()
The class WSGIRefServer is entirely copied with only 1 line added to the run() method is added. Also add a simple shutdown() method. Unfortunately, this is necessary because of the way bottle creates the run() method.
You can make your thread a daemon by setting the daemon property to True before calling start.
mythread = threading.Thread()
mythread.daemon = True
mythread.start()
A deamon thread will stop whenever the main thread that it is running in is killed or dies. The only problem is that you won't be able to make the thread run any code on exit and if the thread is in the process of doing something, it will be stopped immediately without being able to finish the method it is running.
There's no way in Python to actually explicitly stop a thread. If you want to have more control over being able to stop your server you should look into Python Processes from the multiprocesses module.
Since bottle doesn't provide a mechanism, it requires a hack. This is perhaps the cleanest one if you are using the default WSGI server:
In bottle's code the WSGI server is started with:
srv.serve_forever()
If you have started bottle in its own thread, you can stop it using:
srv.shutdown()
To access the srv variable in your code, you need to edit the bottle source code and make it global. After changing the bottle code, it would look like:
srv = None #make srv global
class WSGIRefServer(ServerAdapter):
def run(self, handler): # pragma: no cover
global srv #make srv global
...
Here's one option: provide custom server (same as default), that records itself:
import bottle
class WSGI(bottle.WSGIRefServer):
instances = []
def run(self, *args, **kw):
self.instances.append(self)
super(WSGI, self).run(*args, **kw)
# some other thread:
bottle.run(host=ip_address, port=12345, server=WSGI)
# control thread:
logging.warn("servers are %s", WSGI.instances)
This is exactly the same method than sepero's and mike's answer, but now much simpler with Bottle version 0.13+:
from bottle import W, run, route
from threading import Thread
import time
#route('/')
def index():
return 'Hello world'
def shutdown():
time.sleep(5)
server.srv.shutdown()
server = WSGIRefServer(port=80)
Thread(target=shutdown).start()
run(server=server)
Also related: https://github.com/bottlepy/bottle/issues/1229 and https://github.com/bottlepy/bottle/issues/1230.
Another example with a route http://localhost/stop to do the shutdown:
from bottle import WSGIRefServer, run, route
from threading import Thread
#route('/')
def index():
return 'Hello world'
#route('/stop')
def stopit():
Thread(target=shutdown).start()
def shutdown():
server.srv.shutdown()
server = WSGIRefServer(port=80)
run(server=server)
PS: it requires at least Bottle 0.13dev.
Console log of Bottle server tells us that the official way of shutting down the server is "Hit Ctrl-C":
Bottle v0.12.19 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.
Why not simply follow it programmatically?
Hitting "Ctrl-C" is nothing but sending SIGINT to the process, and we can achieve it with just built-in modules:
Get current PID with os.getpid().
Kill the process with os.kill(). Remember passing SIGINT so it will be exactly same as "Hit Ctrl-C".
Wrap the 'kill' in another thread and start it after a few seconds so the client won't get error.
Here is the server code:
from bottle import route, run
import os
import signal
from threading import Thread
import time
#route('/hello')
def return_hello():
return 'Hello'
#route('/stop')
def handle_stop_request():
# Handle "stop server" request from client: start a new thread to stop the server
Thread(target=shutdown_server).start()
return ''
def shutdown_server():
time.sleep(2)
pid = os.getpid() # Get process ID of the current Python script
os.kill(pid, signal.SIGINT)
# Kill the current script process with SIGINT, which does same as "Ctrl-C"
run(host='localhost', port=8080)
Here is the client code:
import requests
def request_service(service_key):
url = f'http://127.0.0.1:8080/{service_key}'
response = requests.get(url)
content = response.content.decode('utf-8')
print(content)
request_service('hello')
request_service('stop')
Note that in function "handle_stop_request" we didn't stop the server immediately but rather started a thread then returned empty string. With this mechanism, when a client requests "http://127.0.0.1:8080/stop", it can get response (the empty string) normally. After that, the server will shutdown. If we otherwise shutdown the server in function "handle_stop_request", the server will close the connection before returning to the client, and hence the client will get "ConnectionError".
Server side output:
Bottle v0.12.19 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.
127.0.0.1 - - [23/Nov/2021 11:18:08] "GET /hello HTTP/1.1" 200 5
127.0.0.1 - - [23/Nov/2021 11:18:08] "GET /stop HTTP/1.1" 200 0
Client side output:
Hello
The code was tested under Python 3.7 and Bottle 0.12.
I suppose that the bottle webserver runs forever until it terminates. There are no methonds like stop().
But you can make something like this:
from bottle import route, run
import threading, time, os, signal, sys, operator
class MyThread(threading.Thread):
def __init__(self, target, *args):
threading.Thread.__init__(self, target=target, args=args)
self.start()
class Watcher:
def __init__(self):
self.child = os.fork()
if self.child == 0:
return
else:
self.watch()
def watch(self):
try:
os.wait()
except KeyboardInterrupt:
print 'KeyBoardInterrupt'
self.kill()
sys.exit()
def kill(self):
try:
os.kill(self.child, signal.SIGKILL)
except OSError: pass
def background_process():
while 1:
print('background thread running')
time.sleep(1)
#route('/hello/:name')
def index(name='World'):
return '<b>Hello %s!</b>' % name
def main():
Watcher()
MyThread(background_process)
run(host='localhost', port=8080)
if __name__ == "__main__":
main()
Then you can use Watcher.kill() when you need to kill your server.
Here is the code of run() function of the bottle:
try:
app = app or default_app()
if isinstance(app, basestring):
app = load_app(app)
if not callable(app):
raise ValueError("Application is not callable: %r" % app)
for plugin in plugins or []:
app.install(plugin)
if server in server_names:
server = server_names.get(server)
if isinstance(server, basestring):
server = load(server)
if isinstance(server, type):
server = server(host=host, port=port, **kargs)
if not isinstance(server, ServerAdapter):
raise ValueError("Unknown or unsupported server: %r" % server)
server.quiet = server.quiet or quiet
if not server.quiet:
stderr("Bottle server starting up (using %s)...\n" % repr(server))
stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
stderr("Hit Ctrl-C to quit.\n\n")
if reloader:
lockfile = os.environ.get('BOTTLE_LOCKFILE')
bgcheck = FileCheckerThread(lockfile, interval)
with bgcheck:
server.run(app)
if bgcheck.status == 'reload':
sys.exit(3)
else:
server.run(app)
except KeyboardInterrupt:
pass
except (SyntaxError, ImportError):
if not reloader: raise
if not getattr(server, 'quiet', False): print_exc()
sys.exit(3)
finally:
if not getattr(server, 'quiet', False): stderr('Shutdown...\n')
As you can see there are no other way to get off the run loop, except some exceptions.
The server.run function depends on the server you use, but there are no universal quit-method anyway.
This equally kludgy hack has the advantage that is doesn't have you copy-paste any code from bottle.py:
# The global server instance.
server = None
def setup_monkey_patch_for_server_shutdown():
"""Setup globals to steal access to the server reference.
This is required to initiate shutdown, unfortunately.
(Bottle could easily remedy that.)"""
# Save the original function.
from wsgiref.simple_server import make_server
# Create a decorator that will save the server upon start.
def stealing_make_server(*args, **kw):
global server
server = make_server(*args, **kw)
return server
# Patch up wsgiref itself with the decorated function.
import wsgiref.simple_server
wsgiref.simple_server.make_server = stealing_make_server
setup_monkey_patch_for_server_shutdown()
def shutdown():
"""Request for the server to shutdown."""
server.shutdown()
I've found this solution to be the easiest, but it does require that the "psutil" package is installed, to get the current process. It also requires the "signals" module, but that's part of the standard library.
#route('/shutdown')
def shutdown():
current_process = psutil.Process()
current_process.send_signal(signal.CTRL_C_EVENT)
return 'Shutting down the web server'
Hope that's of use to someone!
This question was top in my google search, so i will post my answer:
When the server is started with the Bottle() class, it has a method close() to stop the server. From the source code:
""" Close the application and all installed plugins. """
For example:
class Server:
def __init__(self, host, port):
self._host = host
self._port = port
self._app = Bottle()
def stop(self):
# close ws server
self._app.close()
def foo(self):
# More methods, routes...
Calling stop method will stop de server.

Categories