📜 ⬆️ ⬇️

From Python Script to WSGI Applications

There was a task to write a web device management interface. The device will be controlled by the Raspberry Pi. The control logic is python, respectively, and the interface would be desirable for python. I want to share my experience.



1. To solve the problem "in the forehead" lighttpd was raised with mod_cgi:

sudo apt-get install lighttpd sudo nano /etc/lighttpd/lighttpd.conf 

Excerpt lighttpd.conf:
')
 #mod_cgi shoud be on server.modules = ( "mod_access", "mod_alias", "mod_compress", "mod_redirect", "mod_cgi", "mod_rewrite", ) #rule enables cgi script cgi.assign = (".py" => "/usr/bin/python") 

/var/www/index.py:

 print "Content-Type: text/html\n\n" print "Hello World!" 

localhost / index.py now responded with a cheerful “Hello World!”

When lighttpd encounters a file with the .py extension, it sends it to python, and its result responds to the request. Roughly speaking redirects stdout.
After some attempts to write the interface from scratch, the HtmlGenerator was born, which allowed not to overload the code with html-tags, rather simplified, but still did not solve the problems in the complex.

2. It was decided to experiment with web frameworks.
Wep.py got caught up in the hand , unpretentious and lightweight.
code.py:

 #! /usr/bin/python # import web urls = ( '/', 'index',) class index: def GET(self): return "Hello, world!" if __name__ == "__main__": web.application(urls, globals()).run() 

The minimum code and on port 8080 hangs our web application
It would seem to forward an alias to port 8080, organize the auto launch of the script and everything is ready.
Yes, but no, experiments on a weak computer showed that the presence of our script makes the machine pretty “sulk.” In addition, there is a lighttpd with mod_cgi.

How to connect a simple script and a web application.

3. According to the WSGI description, its implementation requires an interface of this type.
 #! /usr/bin/python # def myapp(environ, start_response): status = '200 OK' response_headers = [('Content-type','text/plain')] start_response(status, response_headers) return ['Hello World!\n'] 

Nothing military, but something does not work, something is missing.
The moment that was unclear after reading the wiki and other articles, what all the same will launch our interface.

4. To run the WSGI application, you need a server. An example of a script that can act as a simple WSGI server:
wsgi.py:
 #! /usr/bin/python import os import sys def run_with_cgi(application): environ = dict(os.environ.items()) environ['wsgi.input'] = sys.stdin environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1, 0) environ['wsgi.multithread'] = False environ['wsgi.multiprocess'] = True environ['wsgi.run_once'] = True if environ.get('HTTPS', 'off') in ('on', '1'): environ['wsgi.url_scheme'] = 'https' else: environ['wsgi.url_scheme'] = 'http' headers_set = [] headers_sent = [] def write(data): if not headers_set: raise AssertionError("write() before start_response()") elif not headers_sent: status, response_headers = headers_sent[:] = headers_set sys.stdout.write('Status: %s\r\n' % status) for header in response_headers: sys.stdout.write('%s: %s\r\n' % header) sys.stdout.write('\r\n') sys.stdout.write(data) sys.stdout.flush() def start_response(status, response_headers, exc_info=None): if exc_info: try: if headers_sent: raise exc_info[0], exc_info[1], exc_info[2] finally: exc_info = None elif headers_set: raise AssertionError("Headers already set!") headers_set[:] = [status, response_headers] return write result = application(environ, start_response) try: for data in result: if data: write(data) if not headers_sent: write('') finally: if hasattr(result, 'close'): result.close() 

Now adding its launch to our interface, we’ll get a script that will respond to our lighttpd or apache, at localhost / app.py
/var/www/app.py:
 #! /usr/bin/python include wsgi def myapp(environ, start_response): status = '200 OK' response_headers = [('Content-type','text/plain')] start_response(status, response_headers) return ['Hello World!\n'] if __name__ == '__main__': wsgi.run_with_cgi(myapp) 


5. For python 2.7, a wsgiref module is available which can implement a WSGI server.
 #! /usr/bin/python import wsgiref.handlers def myapp(environ, start_response): status = '200 OK' response_headers = [('Content-type','text/plain')] start_response(status, response_headers) return ['Hello World!\n'] if __name__ == '__main__': wsgiref.handlers.CGIHandler().run(myapp) 


6. Implementing WSGI with a flup:
install flup
 sudo apt-get install python-flup 

 #! /usr/bin/python import flup.server.fcgi def myapp(environ, start_response): status = '200 OK' response_headers = [('Content-type','text/plain')] start_response(status, response_headers) return ['Hello World!\n'] if __name__ == '__main__': flup.server.fcgi.WSGIServer(myapp).run() 


7. A simple web.py application using flup:
/var/www/app.py:
 #! /usr/bin/python import web urls = ( '/', 'index', ) class index: def GET(self): return "Hello World!" if __name__ == '__main__': web.application(urls, globals()).run() 

the application will be available at localhost / app.py

8. By default, web.py uses flup, but you can do without it.
To run web.py on wsgiref:
 web.application(urls, globals()).cgirun() 

In links to web.py scripts, do not forget to put '/' (app.py/) at the end, otherwise the answer will be “not found”. In an amicable way, you must create a rewrite rule:
 # mod_rewrite configuration. url.rewrite-once = ( "^/favicon.ico$" => "/favicon.ico", "^/(.*)$" => "app.py/$1" ,) 

For debugging in scripts it is useful to add:
 import cgitb cgitb.enable() 

then errors will be seen.

It remains to try:
modwsgi
paste
pylons

Useful links:
WSGI wiki
wep.py
WSGI is a web server communication protocol with a python application.
WSGI introduction
How to serve a WSGI application via CGI
WSGI.org
Comparison of the effectiveness of ways to run web applications in Python

Source: https://habr.com/ru/post/256481/


All Articles