Skip to main content

The gnarliest gear in the world 🤙

Project description

Gnar Gear: Gnarly Python Apps

MIT license codecov pipeline status Python versions PyPI version

Sets up a powerful Flask-based Python service with two lines of code:

from gnar_gear import GnarApp

...

GnarApp('my_gnarly_app', production=True, port=80).run()

Installation

pip3 install gnar_gear

Feature List

Requirements

Bjoern

Bjoern requires libev (high performance event loop)

  • Install libev with brew install libev on Mac, or find your platform-specific installation command here

Application Structure

GnarApp expects to be instantiated in main.py at <top-level-module>/app, i.e. the minimum app folder structure is

+ <top-level-module>
    + app
        main.py
    __init__.py

It is recommended (not required) to place your apis in segregated folders under app and the tests in a test folder under the <top-level-module>, e.g.

+ <top-level-module>
    + app
        + admin
            apis.py
            constants.py
            services.py
        + user
            apis.py
            constants.py
            services.py
        __init__.py
        main.py
    + test
        constants.py
        test_app.py
    __init__.py

Blueprints

Each Flask Blueprint must be assigned to a global-level blueprint variable in its module, e.g.

from flask import Blueprint, jsonify


api_name = 'user'
url_prefix = '/{}'.format(api_name)

blueprint = Blueprint(api_name, __name__, url_prefix=url_prefix)
^^^^^^^^^

@blueprint.route('/get', methods=['GET'])
def user_get():
    return jsonify({'status': 'ok'})

By default, the GnarApp picks up every blueprint in an auto-scan of the application code.

Overview

The GnarApp class provides a highly configurable, feature-rich, production-ready Flask-based app.

Parameters

Args (required)

  • name: The name of the application's top-level module
  • production: Boolean flag indicating whether or not the build is in production mode
  • port: The port to bind to the WSGI server

Kwargs (optional)

  • env_prefix: Environment variable prefix (defaults to GNAR)
  • log_level: Log level override - see configure_logger for log level overview
  • blueprint_modules: List of modules to find Flask blueprints (default is auto-scan)
  • no_db: Boolean flag - specify True if the app does not need a Postgres connection
  • no_jwt: Boolean flag - specify True if the app does not use JWT headers (i.e. non-api services)

Overridable Behavior

GnarApp.run simply calls a set of steps in the class. Here is an example of how to override any of the steps:

def postconfig():
    log.info('My Postconfig Step!')

ga = GnarApp('my_gnarly_app', production=True, port=80)
ga.postconfig = postconfig
ga.run()

Run Steps

The run steps rely on a set of environment variables which use a configurable prefix (i.e. the env_prefix parameter). The default env_prefix is GNAR. An example of a Gnar environment variable using a custom prefix is GnarApp( ..., env_prefix='MY_APP') and then instead of reading GNAR_LOG_LEVEL, the configure_logger step will read MY_APP_LOG_LEVEL.

preconfig

  • No default behavior - provided as an optional initial step in the app configuration.

configure_flask

  • Attaches a Flask instance to the Gnar app.

configure_logger

  • Attaches the root logger to sys.stdout.
  • Sets the logging level to the first defined:
    • log_level parameter
    • GNAR_LOG_LEVEL environment variable
    • INFO
    • Reminder: Valid settings (in increasing order of severity) are DEBUG, INFO, WARNING, ERROR, CRITICAL
  • Sets the log format to the first defined:
    • GNAR_LOG_FORMAT
    • '%(asctime)s %(levelname)-8s %(name)s:%(lineno)d %(message)s', e.g.:
    2018-07-09 15:41:46.420 INFO     gear.gnar_app:75   Logging at INFO
    
  • Sets the log format default_msec_format to the first defined:
    • GNAR_LOG_FORMAT_MSEC
    • '%s.%03d' (e.g. .001)

configure_bcrypt

  • Attaches a Bcrypt singleton (for password hashing) to the Gnar app.
  • To hash a password using Bcrypt:
    from <top-level-module>.main import app
    hash = app.bcrypt.generate_password_hash(<plain text password>).decode('utf8')
    
    Note that this creates a salted hash based on the Blowfish cipher
  • To validate a password with Bcrypt:
    from <top-level-module>.main import app
    is_valid = app.bcrypt.check_password_hash(<password hash from database>, <plain text password>)
    

configure_database

  • Creates a Postgres database connection and attaches it to the Gnar app
  • Reads the following environment variables to set the host, dbname, user, password connection string parameters, respectively:
    • GNAR_PG_ENDPOINT
    • GNAR_PG_DATABASE
    • GNAR_PG_USERNAME
    • GNAR_PG_PASSWORD
  • Note: The Postgres API primarily consists of run, one, and all

attach_instance

  • Attaches the GnarApp instance to the app.main module. This enables easy access to the Gnar app from anywhere in the application using
    from <top-level-module>.main import app
    
  • The GnarApp's runtime assets are db, bcrypt, flask, and get_ses_client
  • For example, to fetch one result (or None) from the database:
    app.db.one("SELECT * FROM foo WHERE bar='buz'")
    

configure_blueprints

  • By default, GnarApp auto-scans every Python module under the app folder for blueprints.
  • Each Flask Blueprint must be assigned to a global-level blueprint variable in its module.
  • If you prefer to skip the auto-scan, you can provide a list (or single string) of blueprint modules.
    • Each item in the list of module names may use one of two formats:
      • Without a . in the module name: GnarApp will look for the module in <top-level-module>.app.<module name>.apis
      • With a . in the module: GnarApp will look for the module in <top-level-module>.app.<module name>

configure_errorhandler

  • Defines a generic (Exception-level) Flask error handler which:
    • Logs the error message and its traceback (format_exec)
    • Returns a 200-level json response containing {"error": <error message>, "traceback": <traceback>}

configure_jwt

  • Sets the Flask JWT_SECRET_KEY variable to the value of the GNAR_JWT_SECRET_KEY environment variable.
  • Sets the Flask JWT_ACCESS_TOKEN_EXPIRES variable to the value of the GNAR_JWT_ACCESS_TOKEN_EXPIRES_MINUTES environment variable (default 5 mins).
  • Attaches a JWTManager instance to the GnarApp.
  • Defines functions for expired_token_loader, invalid_token_loader, and unauthorized_loader which return meaningful error messages as 200-level json responses containing {"error": <error message>}.

configure_after_request

  • Adds a JWT Authorization header (Bearer token) to responses which received a valid JWT token in the request.
  • In non-production mode, adds CORS headers to the response (so that you don't need to bother with circumventing CORS in development).

postconfig

  • No default behavior - provided as an optional initial step in the app configuration.

Runtime Functionality

get_ses_client

  • Exposed as runtime functionality (as opposed to creating the client at initialization) because AWS will close the client after a short period of time
  • Returns an SES connection (using boto3)
  • Reads the following environment variables to set the region_name, aws_access_key_id, and aws_secret_access_key parameters of the boto3.client call, respectively:
    • GNAR_SES_REGION_NAME
    • GNAR_SES_ACCESS_KEY_ID
    • GNAR_SES_SECRET_ACCESS_KEY
  • Usage:
    from <top-level-module>.main import app
    app.get_ses_client().send_email( ... )
    
  • See the Boto 3 Docs for the send_email request syntax.

Environment Variables

  • The environment variables (with configurable prefix) used by GnarApp are:
    • GNAR_JWT_SECRET_KEY
    • GNAR_LOG_LEVEL
    • GNAR_PG_DATABASE
    • GNAR_PG_ENDPOINT
    • GNAR_PG_PASSWORD
    • GNAR_PG_USERNAME
    • GNAR_SES_ACCESS_KEY_ID
    • GNAR_SES_REGION_NAME
    • GNAR_SES_SECRET_ACCESS_KEY
  • See the relevant sections above for details

Made with ❤ by a Canadian living in Redwood City, California | Keep it Rad, friends 🤙

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gnar-gear-1.0.1.tar.gz (9.0 kB view hashes)

Uploaded Source

Built Distribution

gnar_gear-1.0.1-py3-none-any.whl (8.1 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page