app.py - Instantiate a Flask application and config

Imports

These are listed in the order prescribed by PEP 8.

Standard library

import sys
import os
from pathlib import Path, PurePosixPath

Third-party imports

Used to open files with unknown encodings and to run docutils itself.

from flask import Flask

Setup

Return a list of files produced a glob of the results relative to a root path.

For example, assuming C:\temp contains 1.txt and 2.txt, path_relative_to_glob(Path('C:\temp'), '*') would return [ '1.txt', '2.txt'].

def path_relative_to_glob(

The root Path which results should be relative to.

  root,

A string or Path giving the desired glob.

  glob_str):

    return [g.relative_to(root) for g in root.glob(str(glob_str))]
 

Given a Path, which (on Windows) contains backslashes, convert it to a Flask URL containing forward slashes.

def path_to_url(

A Path object, which will be converted to a URL.

  path):
 

Flask URLs begin with a flash. The path passed to us therefore begins with a slash. For some reason, Python 3.5.1 reports:

>>> str(PurePosixPath(Path('/build')))
'\\/build'

on Windows. ???. So, work around this. (I assume this won’t happen on Unix.)

    path_str = str(PurePosixPath(path))
    if path_str[0] == '\\':
        path_str = path_str[1:]
    return path_str
 

Use a Class-based config to avoid needing a 2nd file os.getenv() enables configuration through OS environment variables

class ConfigClass(object):

Flask settings

To set up Heroku, use heroku addons:create heroku-postgresql.

    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///book_webapp.sqlite')
    CSRF_ENABLED = True
 

Book settings

    FLASK_ROOT_PATH = Path(__file__).resolve().parent
    SOURCE_ROOT_PATH = FLASK_ROOT_PATH.parent.parent

Note that Flask URLs begin with a leading slash and are relative to the SOURCE_ROOT_PATH.

    TEMPLATES_ROOT_URL = '/' / FLASK_ROOT_PATH.relative_to(SOURCE_ROOT_PATH) / 'templates'
    WEB_ROOT_PATH = SOURCE_ROOT_PATH / '_build' / 'html'
    STATIC_PATH = Path('_static')
    IMAGES_PATH = Path('_images')
    NON_BOOK_FILES = frozenset([
            Path('conf.py.html'),
            Path('genindex.html'),
            Path('index.html'),
            Path('search.html'),
            Path('.gitignore.html'),

This file doesn’t exist, but CodeChat documents ask for it, so putting it here returns a “not found” instead of redirecting to the login screen.

            Path('_static/html4css1.css'),
        ] +
        path_relative_to_glob(WEB_ROOT_PATH, STATIC_PATH / '*.js') +
        path_relative_to_glob(WEB_ROOT_PATH, STATIC_PATH / '*.css') +
        path_relative_to_glob(WEB_ROOT_PATH, IMAGES_PATH / '*') +
        path_relative_to_glob(WEB_ROOT_PATH, 'build/**/*') +
        path_relative_to_glob(WEB_ROOT_PATH, '2016-summer/**/*')
    )
    PAGE_NOT_FOUND_PATH = WEB_ROOT_PATH / FLASK_ROOT_PATH.relative_to(SOURCE_ROOT_PATH) / 'page_not_found.html'
 

Flask-User settings

We use the same template as URL, which enables Sphinx to link to the desired URL nicely – see Account. See https://pythonhosted.org/Flask-User/base_templates.html and https://pythonhosted.org/Flask-User/customization.html#urls.

    USER_CHANGE_PASSWORD_URL      = path_to_url(TEMPLATES_ROOT_URL / 'change_password.html')
    USER_CHANGE_USERNAME_URL      = path_to_url(TEMPLATES_ROOT_URL / 'change_username.html')

I can’t find an existing template for this. ???

    ##USER_CONFIRM_EMAIL_URL        = path_to_url(TEMPLATES_ROOT_URL / 'resend_confirm_email' / '<token>')
    USER_FORGOT_PASSWORD_URL      = path_to_url(TEMPLATES_ROOT_URL / 'forgot_password.html')
    USER_INVITE_URL               = path_to_url(TEMPLATES_ROOT_URL / 'invite.html')                  # v0.6.2 and up
    USER_LOGIN_URL                = path_to_url(TEMPLATES_ROOT_URL / 'login.html')
    USER_LOGOUT_URL               = path_to_url(TEMPLATES_ROOT_URL / 'logout.html')

This is only used if a UserEmail class is defined, which allows multiple e-mails for an account.

    #USER_MANAGE_EMAILS_URL        = path_to_url(TEMPLATES_ROOT_URL / 'manage_emails.html')
    USER_REGISTER_URL             = path_to_url(TEMPLATES_ROOT_URL / 'register.html')
    USER_RESEND_CONFIRM_EMAIL_URL = path_to_url(TEMPLATES_ROOT_URL / 'resend_confirm_email.html')    # v0.5.0 and up
    USER_RESET_PASSWORD_URL       = path_to_url(TEMPLATES_ROOT_URL / 'reset_password.html' / '<token>')
    USER_CHANGE_PASSWORD_TEMPLATE       = USER_CHANGE_PASSWORD_URL
    USER_CHANGE_USERNAME_TEMPLATE       = USER_CHANGE_USERNAME_URL
    USER_FORGOT_PASSWORD_TEMPLATE       = USER_FORGOT_PASSWORD_URL

v0.6.2 and up

    USER_INVITE_TEMPLATE                = USER_INVITE_URL

v0.6.2 and up

    USER_INVITE_ACCEPT_TEMPLATE         = USER_REGISTER_URL
    USER_LOGIN_TEMPLATE                 = USER_LOGIN_URL
    USER_LOGOUT_TEMPLATE                = USER_LOGOUT_URL

v0.5.1 and up

    #USER_MANAGE_EMAILS_TEMPLATE         = USER_MANAGE_EMAILS_URL
    USER_REGISTER_TEMPLATE              = USER_REGISTER_URL

v0.5.0 and up

    USER_RESEND_CONFIRM_EMAIL_TEMPLATE  = USER_RESEND_CONFIRM_EMAIL_URL
    USER_RESET_PASSWORD_TEMPLATE        = path_to_url(TEMPLATES_ROOT_URL / 'reset_password.html')
 

Flask-SQLAlchemy settings

    SQLALCHEMY_TRACK_MODIFICATIONS = False
 

Stripe settings.

    ACCOUNT_URL = TEMPLATES_ROOT_URL.parent / 'account'
    PAYMENT_URL = path_to_url(ACCOUNT_URL / 'payment.html')
    SELECT_CLASS_URL = path_to_url(ACCOUNT_URL / 'select_class.html')
    RECEIPT_URL = path_to_url(ACCOUNT_URL / 'receipt.html')
 

Platform-specific settings.

For Heroku (Linux). This follows the recommended approach.

    if sys.platform.startswith('linux'):
        XC16_PATH = FLASK_ROOT_PATH.parent / 'xc16/v1.30/bin'

For Windows.

    elif sys.platform == 'win32':
        XC16_PATH = Path('C:\\Program Files (x86)\\Microchip\\xc16\\v1.30\\bin')

app = Flask(__name__, template_folder=str(ConfigClass.WEB_ROOT_PATH))

See http://flask.pocoo.org/docs/0.11/api/#configuration.

app.config.from_object(__name__ + '.ConfigClass')
app.config.from_pyfile('config.py')