Musings of a Fondue

My First Flask App

I was trying to get a project I had done earlier up and running on this blog. (Click the picture to see it in action.)

The project relied on a CGI file written in Python being executed by the server.

I first tried a simple upload of my files to Heroku hoping that everything would just automagically work… ha! Of course it didn’t work. What follows is the series of adventures it took to get it working.

Where I insist that CGI can work on Heroku

The file structure of the project looked something like this (see the full code on Github),


icons/
index.html
auto_python.cgi
localCGIServer.py
  • Maybe I just needed to add a runtime.txt to specify the Python version Heroku should use. Heroku defaults to python2 and the CGI server code I was using was written for python3. So I added it. Nada!
  • I then added a requirements.txt because Heroku won’t recognize it as a Python app otherwise. Nada!
  • I tweaked localCGIServer.py to use the port passed in by a user. The Procfile’s content (used by Heroku to initialize the app) was changed accordingly from web: python localCGIServer.py to web: python localCGIServer.py $PORT. (See the docs for more on Heroku ports.) Still nada!
  • One of the articles I was reading made me suspect that the project (which worked fine locally on my Windows machine) may not be working because Heroku is linux based. To that end, I tried running the project locally on Ubuntu and - the CGI part didn’t work! We’re getting somewhere!
  • I narrowed down the reason to CRLF. The article explains that CGI files with Windows style line endings (CRLF) versus linux style line endings (LF) will not work on linux servers.
  • Sure enough, when I ran file auto_python.cgi, I got
    • 
      Python script, ASCII text executable, with CRLF line terminators
      
  • To convert the line endings from windows style to linux, I ran fromdos auto_python.cgi
  • After changing the line endings, the project worked locally on Ubuntu. If it works locally on Ubuntu then it will work on Heroku! So I deployed!
  • It didn’t work =/
  • At least the code was now ruled out as the problem. The problem had to be on Heroku’s end.
  • Running heroku logs revealed this error code,
    • 
      sock=backend at=error code=h18 desc=“server request interrupted”
    • I looked up the error code. According to the Heroku docs, “The backend socket, belonging to your app’s web process was closed before the backend returned an HTTP response.”
    • Lovely! I love you too Heroku.
    • Several hours of GoogleFu later, and I could not find anything on how to resolve this error.

Where I concede that CGI can't work on Heroku

At this point, I was done with anything involving CGI and Heroku in the same room! There was an answer on StackOverflow where the person recommended porting a CGI application to Flask in order to get it working in Heroku. Since I was getting nowhere with the CGI file, it was time to learn this Flask thing. I was reluctant to learn a new framework, but one way or another this app was going to go online!

Try I did,
Learn I did,
But in the end Flask it would be.

As I read and through Flask’s documentation, it was

Flask is very lightweight! And painless to use! And it’s in Python.

When porting to Flask, I didn’t change the original code (which you can find here). The main change was getting rid of auto-python.cgi and shoving all of its code into a function called findUsers() in Main.py.

Gone was the localCGIServer.py business - into a black hole where no one will ever find it.

The app’s new file structure looked like this,


static/
    icons/
templates/
    index.html
Main.py

Here’s the code for Main.py


import os
import re
from flask import Flask
from flask import render_template, request, Response, url_for

app = Flask(__name__)


# Home page
@app.route('/')
@app.route('/index.html')
def landingPage():
    return render_template('index.html')


# Thanks page
@app.route('/thanks.html')
def thanksPage():
    return render_template('thanks.html')


# Autocomplete script
@app.route('/auto_python.cgi', methods=['GET', 'POST'])
def findUsers():
    if request.method == 'GET':
        print("why heloo there")

        # Get the query
        q = str( request.args.get("to") )   # http://stackoverflow.com/q/11774265

        # We will store our response HTML here
        html = ''

        # Our limited 'database' contains a few users
        # with their username and full name
        data = [
            {
                "user" : "amon",
                "name" : "N. Equalist"
            },
            ...
            {
                "user" : "sasuke",
                "name" : "Sasuke Uchiha"
            },
            {
                "user" : "vegeta",
                "name" : "Saiyan Prince"
            }
        ]

        # We "search" through the data
        regex = re.escape( q )

        for row in data:
            # Looking for users that match our auto-complete search
                # stackoverflow.com/a/14225664/2354735
                # stackoverflow.com/a/500870/2354735
            if ( re.search(regex, row["user"], re.IGNORECASE) is not None or 
                 re.search(regex, row["name"], re.IGNORECASE) is not None ):  
                
                html += ('<li id="' + row["user"] + '">' +
                         '<img class="icon" src="' + url_for('static', filename='') + 'icons/' + row["user"] + '.png"/>' +
                         '<div id="uDetails">' +
                         '<span id="username">' + row["user"] + '</span>' +
                         '<span> : </span>' +
                         '<span id="fullname">' + row["name"] + '</span>' +
                         '</div></li>')
        
        # And send the "response"
        return Response(html, mimetype='text/html')     # http://stackoverflow.com/a/11774026


# Start Server
if __name__ == '__main__':
    # app.run()
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port)

All the code can be found on Github.

Porting to Flask turned out to be very straightforward! And Flask itself enjoyable to use. And it works seamlessly with Heroku.

Color me impressed!

Comments