Dylan Storey

Recovering academic, hacker, tinkerer, scientist.

Building A Larger Flask Application: 01 - Setup

This is the first in a series of blog posts where I’ll outline and document how I’m going about structuring a larger flask application. This is mainly an excercise for me to distill about 2 weeks worth of tinkering and looking through others code bases into something that I both understand and like.

Frombicate

Frombicate is a completely made up app that does nothing. The entire point of this app is to demonstrate how to setup a large, expandable flask app. That is all.

General goals of this application:

  • installable through pip
  • both modular and expandable
  • provides both the flask application and supporting programs through a minimal CLI
  • this application will at a minimum provide a restAPI and CLI. It will probably also provide a flask application.
  • We’ll also need to provide asynchronous computing through Celery.

Getting Started

Setting up our project

The general idea is to provide a package that is installable through pip. To start with we’ll get a pip styled project setup.

mkdir frombicate
mkdir frombicate/frombicate
touch frombicate/frombicate/__init__.py
touch frombicate/frombicate/config.py
touch frombicate/README.md
touch frombicate/setup.py
touch frombicate/requirements.txt
touch frombicate/VERSION

Resulting in a project with the following structure:

frombicate/
├── frombicate/
|   ├── config.py
│   └── __init__.py
├── README.md
├── requirements.txt
├── setup.py
└── VERSION

Description of files

  • frombicate is simply the root folder for the project
  • frombicate/frombicate is the folder for the frombicate module. This is where the program actually resides.
  • config.py this holds configuration settings for the project
  • README.md a top level README. Within githost services (gitlab/github), this will be displayed on the landing page for the repository.
  • requirements.txt the packages required to run the application
  • setup.py The setup script. Describes the application and how it interacts with the system.
  • VERSION A file that simply tracks the version number.

Building our application

From here on out, we’re assuming that we’re in the root directory

Enviroments

Enviroments help isolate your development enviroment from the rest of your computing environment.

There are a couple ways to handle virtual enviroments. I’m choosing to use conda here.

Lets start up by actually getting an environment stood up.

conda -n frombicate python=3.5

The goal of this series is not to discuss enviroments, but this is a simple reminder to use them.

Requirements

As we’ve already decided on a few our requirements; we’ll start with requirements.txt

I’m assuming you have a general idea of what the requirements files does and dictates. If you want to read more about about go here

Because I’m forward thinking ( or more likely because I’ve done this before ), we’re going to provide requirements for different situations : production, development, and testing.

mkdir frombicate/requirements
touch frombicate/requirements/base.txt
touch frombicate/requirements/dev.txt
touch frombicate/requirements/test.txt

Within requirements.txt add :

-r frombicate/requirements/base.txt

This simply tells pip to import everything in base.txt .

Within dev.txt and test.txt add the following :

-r base.txt

Because we’ve already determined what some of our libraries are going to be, we’ll add them to base now.

Within base.txt add the following lines:

flask==0.12.2
SQLAlchemy=1.1.12
Flask-SQLAlchemy==2.2
celery==4.1.0

setup.py

This provides installation instructions for modules and resulting application(s) that they build.

Let’s walk through some of the less vanilla pieces of it :

Parsing requirements from the requirements.txt files. This function allows us to rope in different requirements at different times and respects the linking functionality (-r /path/to/file) that we’re using in our requirements files.

def read_requirements(file):
    '''
    CREDIT FOR THE CREDIT GODS
    CITATION FOR THE CITATION THRONE
    http://alexanderwaldin.github.io/packaging-python-project.html
    '''
    reqs_path = file
    install_reqs = parse_requirements(reqs_path, session=PipSession())
    reqs = [str(ir.req) for ir in install_reqs]
    [print(i,file=sys.stdout) for i in install_reqs]
    return reqs

This allows us to use pip to install different enviroments depending on need. By default the production enviroment will be used, but this way we can choose to lode dev and test enviroments as well. (After all its not like we need coverall in our production code)

extras_require={
        'dev' : read_requirements('{}/requirements/development.txt'.format(PROGRAM_NAME)),
        'test'  : read_requirements('{}/requirements/testing.txt'.format(PROGRAM_NAME)),
        }

To actually use this functionality:

$ pip install -e .[dev]
# OR
$ pip install -e .[test]

This may not be new to many people, but it was pretty new to me until recently and since finding out about it has made my life much easier :

entry_points={
    'console_scripts': [
        'frombicate=frombicate:main'
        ]
    },

Entry points describe, well , where you can enter the program from to execture some sort of functionality. This simply registers the command frombicate with your system, and tells it to execute the main function, in the frombicate module. I personally use these to not just handle the “main” usage pattern of the application, but for helpers lik: setup,teardown, starting sevices, monitoring, testing, etc.

I personally like this pattern because it allows me to structure my code and keep things segregated and easy to find.

config.py

This is a pretty cool pattern I’ve stumbled across on a few projects and I really liked it.

This file is structured as a series of classes that inherit each other in a hierarchy.

         +-----------+
         | HardCoded |     # HardCoded Variables Go Here
         +-----+-----+
               |
     +---------+--------+
     | SQLAlchemyConfig |    #SQLAlchemyConfig Variables Go Here
     +---------+--------+
               |
           +---+----+
           | Config |      #Default Variable Values Go Here (Development)
      +----+--------+-----+
      |                   |
      |                   |
      |                   |
+-----+----+        +-----+------+
|  Testing |        | Production |   # Modified Stuff Here
+----------+        +------------+

Extending them is pretty easy just make a new class that inherts from the correct level.

Basic Usage:

from flask import Flask

app = Flask(__name__)
app.config.from_object('bigapp.Config')

this = app.config['VARIABLE']

It is important to note that all configurations are kept in a single file and will be available throughout the application as a result. If you have a hard coded value that’s local maybe don’t put it in here. Also be careful about collisions when picking variable names yourself.

This may need to be extended later on to accommodate passwords or secret keys.

Done for Now

That’s all for first steps, the next piece will be to set up our data base integrations !

The code for this can be found here.

blog comments powered by Disqus