0% found this document useful (0 votes)
110 views

Python Dev Web Ser

The document discusses setting up different Python web servers for developing Flask applications. It evaluates CherryPy, Gevent, Gunicorn, and Rocket based on requirements like automatic reloading of code changes, printing output to stdout, and ease of setup. CherryPy and Gevent meet most requirements out of the box. Gunicorn requires additional tools like Supervisor and Watchdog for automatic reloading. Overall, the document recommends CherryPy or Gevent for development due to their simplicity.

Uploaded by

Hemant Homkar
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
110 views

Python Dev Web Ser

The document discusses setting up different Python web servers for developing Flask applications. It evaluates CherryPy, Gevent, Gunicorn, and Rocket based on requirements like automatic reloading of code changes, printing output to stdout, and ease of setup. CherryPy and Gevent meet most requirements out of the box. Gunicorn requires additional tools like Supervisor and Watchdog for automatic reloading. Overall, the document recommends CherryPy or Gevent for development due to their simplicity.

Uploaded by

Hemant Homkar
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

Fotsies Technology Blog

Setting up a Rock Solid Python Development Web Server


Posted on December 8, 2012

So you want to start developing a Python application do ya? Let’s assume we are planning to use a smaller framework like Flask which is a
framework I’m really loving at the moment.

Update (6th of April 2014): In the past, I had found Flask’s in-built development server to be a bit unstable which is why I put together this
entry but this is no longer the case. As such, I now recommend using the dev server that comes with Flask for development purposes.
However, if you prefer to use another server instead, please read on.

In this article, I’ll show you how to setup various web servers to serve Flask applications using WSGI which may also be suitable for
production use later on.

The requirements I have for a development server are as follows:

It must print access and error log output to stdout in realtime


It must display stdout when the print function is used in the code (which is really handy for debugging)
The server should reliably reload automatically when any code is changed
Exceptions should be printed to stderr or the browser if something goes wrong
The web server needs to be easy to setup
(Bonus) It would be ideal if the web server works with Jython 2.5.x too

So let’s get into it.

CherryPy
CherryPy’s web server is very well regarded and was one of the rst I looked at. My main criticism of CherryPy was its documentation
which I found extremely di cult to read through and grasp. With a bit of digging around, I managed to get everything to work. You must
use the Paste library for logging.

Installation of CherryPy is super easy:

1 pip install cherrypy paste

And here’s the Flask script:

1 #!/usr/bin/env python
2
3 from flask import Flask
4 import cherrypy
5 from paste.translogger import TransLogger
6
7 app = Flask(__name__)
8 app.debug = True
9
10
11 @app.route("/")
12 def hello():
13 return "Hello World!"
14
15
16 def run_server():
17 # Enable WSGI access logging via Paste
18 app_logged = TransLogger(app)
19
20 # Mount the WSGI callable object (app) on the root directory
21 cherrypy.tree.graft(app_logged, '/')
22
23 # Set the configuration of the web server
24 cherrypy.config.update({
25 'engine.autoreload_on': True,
26 'log.screen': True,
27 'server.socket_port': 5000,
28 'server.socket_host': '0.0.0.0'
29 })
30
31 # Start the CherryPy WSGI web server
32 cherrypy.engine.start()
33 cherrypy.engine.block()
34
35 if __name__ == "__main__":
36 run_server()

With a bit of extra e ort, we can customise the access logging from Paste to be consistent with CherryPy if desired:

1 #!/usr/bin/env python
2
3 import time
4
5 from flask import Flask
6 import cherrypy
7 from paste.translogger import TransLogger
8
9 app = Flask(__name__)
10 app.debug = True
11
12
13 @app.route("/")
14 def hello():
15 return "Hello World!"
16
17
18 class FotsTransLogger(TransLogger):
19 def write_log(self, environ, method, req_uri, start, status, bytes):
20 """ We'll override the write_log function to remove the time offset so
21 that the output aligns nicely with CherryPy's web server logging
22
23 i.e.
24
25 [08/Jan/2013:23:50:03] ENGINE Serving on 0.0.0.0:5000
26 [08/Jan/2013:23:50:03] ENGINE Bus STARTED
27 [08/Jan/2013:23:50:45 +1100] REQUES GET 200 / (192.168.172.1) 830
28
29 becomes
30
31 [08/Jan/2013:23:50:03] ENGINE Serving on 0.0.0.0:5000
32 [08/Jan/2013:23:50:03] ENGINE Bus STARTED
33 [08/Jan/2013:23:50:45] REQUES GET 200 / (192.168.172.1) 830
34 """
35
36 if bytes is None:
37 bytes = '-'
38 remote_addr = '-'
39 if environ.get('HTTP_X_FORWARDED_FOR'):
40 remote_addr = environ['HTTP_X_FORWARDED_FOR']
41 elif environ.get('REMOTE_ADDR'):
42 remote_addr = environ['REMOTE_ADDR']
43 d = {
44 'REMOTE_ADDR': remote_addr,
45 'REMOTE_USER': environ.get('REMOTE_USER') or '-',
46 'REQUEST_METHOD': method,
47 'REQUEST_URI': req_uri,
48 'HTTP_VERSION': environ.get('SERVER_PROTOCOL'),
49 'time': time.strftime('%d/%b/%Y:%H:%M:%S', start),
50 'status': status.split(None, 1)[0],
51 'bytes': bytes,
52 'HTTP_REFERER': environ.get('HTTP_REFERER', '-'),
53 'HTTP_USER_AGENT': environ.get('HTTP_USER_AGENT', '-'),
54 }
55 message = self.format % d
56 self.logger.log(self.logging_level, message)
57
58
59 def run_server():
60 # Enable custom Paste access logging
61 log_format = (
62 '[%(time)s] REQUES %(REQUEST_METHOD)s %(status)s %(REQUEST_URI)s '
63 '(%(REMOTE_ADDR)s) %(bytes)s'
64 )
65 app_logged = FotsTransLogger(app, format=log_format)
66
67 # Mount the WSGI callable object (app) on the root directory
68 cherrypy.tree.graft(app_logged, '/')
69
70 # Set the configuration of the web server
71 cherrypy.config.update({
72 'engine.autoreload_on': True,
73 'log.screen': True,
74 'server.socket_port': 5000,
75 'server.socket_host': '0.0.0.0'
76 })
77
78 # Start the CherryPy WSGI web server
79 cherrypy.engine.start()
80 cherrypy.engine.block()
81
82 if __name__ == "__main__":
83 run_server()

Gevent
http://www.gevent.org/ appears to be one of the fastest WSGI web servers out there and provides all the features we are after too!

Installing Gevent is a bit more of a pain due to the libevent dependency:

1 sudo apt-get install build-essential python-dev libevent-dev


2 pip install gevent

And here’s the Flask script:

1 #!/usr/bin/env python
2
3 from flask import Flask
4 import gevent.wsgi
5 import gevent.monkey
6 import werkzeug.serving
7
8 gevent.monkey.patch_all()
9 app = Flask(__name__)
10 app.debug = True
11
12
13 @app.route("/")
14 def hello():
15 return "Hello World!"
16
17
18 @werkzeug.serving.run_with_reloader
19 def run_server():
20 ws = gevent.wsgi.WSGIServer(listener=('0.0.0.0', 5000),
21 application=app)
22 ws.serve_forever()
23
24 if __name__ == "__main__":
25 run_server()

Gunicorn
Gunicorn is a production-ready web server for Python. I must commend the designer of the site who shows that even Python-related sites
can look beautiful! Unfortunately, Gunicorn (being a server aimed at production use) makes it a lot harder to get auto-restart capabilities as
this functionality is not natively included.

Installing Gunicorn is painless:

1 pip install gunicorn

To make it all happen with Gunicorn, we’re going to need supervisor and watchdog to monitor for changes and trigger a restart of
Gunicorn.

These tools rely on several C libraries, so there’s a bit more to it than just using pip:

1 sudo apt-get install build-essential python-dev libyaml-dev


2 pip install supervisor watchdog

The supervisor con guration is as follows:

1 [supervisord]
2 logfile=test.log
3 loglevel=debug
4 nodaemon=true
5
6 [program:test]
7 autostart=true
8 command=gunicorn --pid /tmp/flask-project.pid --workers 4 --log-level debug -b 0.0.0.0:5000 test:app
9
10 [program:test-reloader]
11 autostart=true
12 autorestart=false
13 command=watchmedo shell-command --patterns="*.py;*.html;*.css;*.js" --recursive --command='kill -HUP $(cat /tmp/flask

The Python script stays super clean and simple which is nice:

1 #!/usr/bin/env python
2
3 from flask import Flask
4
5 app = Flask(__name__)
6 app.debug = True
7
8
9 @app.route("/")
10 def hello():
11 return "Hello World!"

We can now launch the web server using supervisor as follows:

1 supervisord -c test.conf
Overall though, I don’t see myself using Gunicorn for development purposes due to the added complexity involved. Another point worth
noting is that print statements to stdout do not appear on the console with Gunicorn, unlike the rest of the web servers tested here.

Rocket
Rocket is a newer pure Python WSGI web server which is also production ready. I thought it would be worth giving it a try too.

Installing Rocket is very simple

1 pip install rocket

And here’s the Flask script:

1 #!/usr/bin/env python
2
3 import logging
4 import sys
5
6 from flask import Flask
7 from rocket import Rocket
8
9 app = Flask(__name__)
10 app.debug = True
11
12
13 @app.route("/")
14 def hello():
15 return "Hello World!"
16
17
18 def run_server():
19 # Setup logging
20 log = logging.getLogger('Rocket')
21 log.setLevel(logging.INFO)
22 log.addHandler(logging.StreamHandler(sys.stdout))
23
24 # Set the configuration of the web server
25 server = Rocket(interfaces=('0.0.0.0', 5000), method='wsgi',
26 app_info={"wsgi_app": app})
27
28 # Start the Rocket web server
29 server.start()
30
31 if __name__ == "__main__":
32 run_server()

Unfortunately, Rocket (much like Gunicorn) is primarily aimed at production deployments, so it doesn’t include an auto-restart feature.

To restart it, you may send a SIGUSR1 to the pid:

1 kill -SIGUSR1 <pid>

You may shutdown the process using SIGTERM:

1 kill -SIGTERM <pid>

Tornado
Tornado appears to be well respected too and has no C dependencies.

Note: The latest Tornado 3.x has a signi cantly changed API and therefore the code below will not work with it. I may look into rewriting
the code below to work with Tornado 3.x when I have a spare moment.
Installing Tornado is as easy as CherryPy:

1 pip install tornado==2.4

And here’s the Flask script:

1 #!/usr/bin/env python
2
3 from flask import Flask
4 import tornado.wsgi
5 import tornado.httpserver
6 import tornado.ioloop
7 import tornado.options
8 import tornado.autoreload
9
10 app = Flask(__name__)
11 app.debug = True
12
13
14 @app.route("/")
15 def hello():
16 return "Hello World!"
17
18
19 def run_server():
20 # Create the HTTP server
21 http_server = tornado.httpserver.HTTPServer(
22 tornado.wsgi.WSGIContainer(app)
23 )
24 http_server.listen(5000)
25
26 # Reads args given at command line (this also enables logging to stderr)
27 tornado.options.parse_command_line()
28
29 # Start the I/O loop with autoreload
30 io_loop = tornado.ioloop.IOLoop.instance()
31 tornado.autoreload.start(io_loop)
32 try:
33 io_loop.start()
34 except KeyboardInterrupt:
35 pass
36
37 if __name__ == "__main__":
38 run_server()

Further WSGI Debugging with Werkzeug


Flask’s Werkzeug has an awesome debugging module which you will lose access to when not using the default web server. But don’t fear,
we can add it back in!

1 ...
2 from werkzeug.debug import DebuggedApplication
3 ...
4
5 def run_server():
6 # Enable the Werkzeug Debugger
7 app_debug = DebuggedApplication(app, evalex=True)
8 ...

Now simply ensure that you pass app_debug into sebsequent functions instead of app as we did above.

Further WSGI Logging with wsgilog


An additional module you can plug in into your application is wsgilog which provides further logging options for capturing output of WSGI
applications.

Installation goes something like this:

1 pip install wsgilog

You may use it as follows:

1 ...
2 import wsgilog
3
4 app = Flask(__name__)
5 app.debug = True
6
7 app_logged_wsgi = wsgilog.WsgiLog(app, tohtml=True, tofile='wsgi.log',
8 tostream=True, toprint=True)
9 ...

Now when initialising the web server, pass in app_logged_wsgi instead of app.

Final Words
To summarise, the following web servers failed to meet one or more criteria above:

Gunicorn: Does not display stdout via the print statement. Gunicorn is also more work to setup for a development server compared
to the rest.
Rocket: Doesn’t include auto-restart ability, but is less troublesome to work with in comparison to Gunicorn.

As far as Jython is concerned, I’m sorry to say that none of the web servers worked with it. I also tried them with 2.7b1 and still no dice.

← PREVIOUS POST NEXT POST →

16 Comments Fotsies Technology Blog 


1 Login

 Recommend 1 t Tweet f Share Sort by Best

Join the discussion…

LOG IN WITH
OR SIGN UP WITH DISQUS ?

Name

Ayed • 6 years ago


You ROCK !!

Thank you for sharing.


1△ ▽ • Reply • Share ›

fgimian Mod > Ayed • 6 years ago


My pleasure, thanks for the kind words! :)
△ ▽ • Reply • Share ›

Frank • 3 years ago


Thanks for this. I was impressed with the walkthrough of CherryPy and it saved me at least a day of head-banging. But to top it off you cover a ton of other libraries too
which helps build perspective. Nice work.
△ ▽ • Reply • Share ›

fgimian Mod > Frank • 3 years ago


My pleasure, really glad you found it useful :)
△ ▽ • Reply • Share ›

Isaac Philip • 3 years ago


thanks, the autoreload feature was needed for me! If you do happen to rewrite the tornado to latest version do keep this version too as many systems will have this version
only setup.
△ ▽ • Reply • Share ›

fgimian Mod > Isaac Philip • 3 years ago • edited


You're welcome, and no problem re Tornado. I may consider checking out the latest version in the future.
△ ▽ • Reply • Share ›

Tony Brown • 3 years ago


Thanks for the writeup, loving python's ease of use
△ ▽ • Reply • Share ›

fgimian Mod > Tony Brown • 3 years ago


Sorry for the late reply. You're welcome, I love Python too :)
△ ▽ • Reply • Share ›

Tony Brown > fgimian • 3 years ago


No worries
△ ▽ • Reply • Share ›

Matt Stromberg • 5 years ago


I am limited at my work to using Windows for a internal web app (show sales employees performance reports in tables with SQL Server backend). The app itself will serve
simple html+js content and hit a few databases such as SQL Server and MongoDB. I'm being pushed into using IIS, which is a nightmare to work with for a flask app. For
dealing with 20-30 users on a very powerful machine (32+gig ram, etc.), can I use CherryPy to serve the static content, handle routing, and use Flask for the dynamic (and
back-end data pulls)? In essence, can I eliminate IIS and just use CherryPy+Flask completely?
△ ▽ • Reply • Share ›

fgimian Mod > Matt Stromberg • 5 years ago


Hey Matt, assuming your site is low traffic, then I see no reason at all that you couldn't use CherryPy to serve the entire site. I have even used the built-in Flask
development server at times when I'm lazy and it works just fine for tiny sites. But perhaps choosing something like CherryPy would be safer in your case.

Cheers
Fotis
△ ▽ • Reply • Share ›

Matt Stromberg > fgimian • 5 years ago • edited


Hey Fotis, the site would be internal and probably max out at 60 users simultaneously. I'm thinking of using cherry + flask with cherrypy for the static and flask
for the dynamic. Any thoughts, concerns, or anything else I should know about?

I lean towards using flask for the dynamic stuff due to its simple nature and extension modules like flask-login.
△ ▽ • Reply • Share ›

Show more replies

Stephen • 5 years ago


For development purposes, why not just use Flask's built-in dev server?
△ ▽ • Reply • Share ›

fgimian Mod > Stephen • 5 years ago


Good point. At the time that I wrote this article, I was experiencing some instability with the Flask web server but that's no longer the case. I recommend adding
Flask-Failsafe and using the included dev server now. I'll update the title of this blog post to reflect this. Thanks for your question :)
△ ▽ • Reply • Share ›

Richard Bann • 5 years ago


Hi, great post.

What I did is calling 'supervisorclt restart test' in the watchmedo command. Isn't it cleaner?
△ ▽ • Reply • Share ›

fgimian Mod > Richard Bann • 5 years ago


Thanks for the feedback and sorry for the very late reply. Sure, that's a nice solution if it works well :)
△ ▽ • Reply • Share ›

ALSO ON FOTSIES TECHNOLOGY BLOG

Building Git on SLES 10 SP3 - Fotsies Technology Blog Curing RSI (Repetitive Strain Injury) - Fotsies Technology Blog
4 comments • 6 years ago 6 comments • 6 years ago
fgimian — Fantastic, glad my guide was helpful! :) fgimian — Great job! Hope this can help many people :)

Building Ruby 1.9.3 and 2.0.0 with RVM and Rails on SLES 10 SP3 - Essential Python Libraries - Fotsies Technology Blog
Fotsies … 6 comments • 5 years ago
8 comments • 6 years ago fgimian — Yup, it's one fascinating implementation :)
fgimian — Hey there Deepak,I'm sorry but I don't have any experience with SLES 11.
Our company is using RHEL for all new builds …

✉ Subscribe d Add Disqus to your site 🔒 Disqus' Privacy Policy


comments powered by Disqus


 
 
 
 

Fotis Gimian  •  2018  •  Fotsies Technology Blog

Theme by beautiful-jekyll

You might also like