Python Flask with Heroku and TravisCI

Python Flask with Heroku and TravisCI

Attempting to get involved with a nonprofit organization MAGIC. They use a lot of Python, so here’s how I went about getting started!

I recently met with a few people from a non profit organization MAGIC, that I’m hoping I will be able to help out in some manner. They do a few major events throughout the year, as well as a cyber club that takes place at the local library. Most of their work is done in Python, so I figured I better familiarize myself a bit more with it.

It’s been a while since I’ve had much of a chance to do any blogging. We had another baby recently, and I’ve been super busy! Between the new baby, old baby, and work, I haven’t had much time or inspiration to do much of anything aside from stay alive.

MAGIC currently has (although from my understanding has not used) a python API for one of their events. I thought it would be a good opportunity to figure out how to build and host a python application using the same framework - flask.

Flask is described as:

Flask is a lightweight WSGI web application framework. It is designed to make getting started quick and easy, with the ability to scale up to complex applications. It began as a simple wrapper around Werkzeug and Jinja and has become one of the most popular Python web application frameworks.

With the little bit of code I’ve looked at around Flask, it seems to solve a similar problem that MVC and/or Web API solve from the .net world. I’d like to see what it takes to get a minimal flask application up, running, and hosted on the interwebs!

The flask application

I’m only going to be using a hello world type application with Flask. In the future I hope to have a chance to further explore flask, and just python in general. From the tutorial on the flask site, I’ll be making a “app.py” with the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'


if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)

Flask is not resolving here, and that’s because I have not installed it yet. Working briefly with python before, I recalled using a virtual environment to house my project’s dependencies; hopefully that is still the standard.

From https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/, I ran:
py -m pip install --user virtualenv, py -m venv env, then .\env\Scripts\activate.

Next, I defined a file called “requirements.txt” and added:

1
2
Flask
pytest

Now to install the requirements into the virtual environment:

1
pip install -r requirements.txt

This will install Flask, pytest, and all of their dependencies into the virtual environment created previously. Now the “flask” import is resolving from the “app.py” that was created.

python tests

We don’t really have much to test right now, but why not just throw a few stubs out there so that we have something to run when we get to the travis build coming up.

app_test.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from . import app


def func(x):
return x + 1


def test_answer():
assert func(3) == 4


def test_hello_world():
result = app.hello_world()
expected_result = 'Hello, World!'

assert expected_result == result

the function “func(x)” is a “hello world” type test I found when googling about pytest, and the second test “test_hello_world” tests the endpoint defined in app.py returns the expected string.

Next, we’ll run pytest from the command line, and check that our tests are passing:

pytest output

Run the application

we can run the application with a python app.py:

cmd - running the app
web - the running app

Travis CI

As stated at the beginning of the article, I’d like to have this application hosted on the internet somewhere, I’m going to use Heroku, since they have a free tier of hosting. Prior to getting to the hosting however, I’d like to set up Travis to test my application, even with its extremely limited functionality and tests.

I’ve used Travis previously for .net projects, so it couldn’t be that difficult to get set up for python, python isn’t even compiled! :D

So to get travis to run on our repository, we simply goto https://travis-ci.org, set up an account if we don’t have one, and “enable” the functionality for the repository we want to have tested with travis. In my case I want it to run against all PRs and commits. Next, we’ll need a .travis.yml file with the following content:

1
2
3
4
5
6
7
dist: xenial
language: python
python:
- "3.7"
install:
- pip install -r requirements.txt
script: pytest

The above should be pretty self explanatory, although the “dist: xenial” is apparently required for the most up to date versions of python. We’re installing our requirements (flask and pytest), then running pytest.

Now, on commit and PR, travis will run out build, and test against our tests - this will give much better assurances that the incoming commit/PR didn’t “break” the build. This build currently looks like this:

Travis build

Heroku

Now that our project is building and being tested, we can set up Heroku to serve our website, and for free! The set up of a heroku account is pretty straight forward, and there’s lots of tutorials. You just need to point heroku to your github repo, then tell it to auto deploy pushes to master (note that travis CI ideally will fail builds to master if tests don’t pass, so we’re getting some level of assurance we’re not pushing “bad code”).

One thing I did not see from the tutorials I was finding, was information on how heroku assigns a random port to your application, and how to go about using that port from your flask app. After weeks (though this was just mostly passive time) of trial and error, I found out it was simply using a Procfile to forward the $PORT$ to my flask app as so:

Procfile:

1
web: gunicorn src.app:app -b 0.0.0.0:$PORT

which will get utilized in the app.py lines:

1
2
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)

Where “port” (when defined and passed in by heroku) will be plugged in, otherwise the port 5000 is used. gunicorn was (at least) a recommended way to serve the python app, so that was an additional dependency added to my requirements. txt.

On the next build of the application, it successfully deployed to https://kritnerflaskhelloworld.herokuapp.com/. Note that with the free tier of heroku, apps will “go down” after a bout of inactivity, so the first few seconds after hitting the URL is just the application being brought back up.

I think that’s it! Code for this post can be found at: https://github.com/Kritner-Blogs/FlaskHelloWorld

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×