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 details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

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

Uploaded Python 3

File details

Details for the file gnar-gear-1.0.1.tar.gz.

File metadata

  • Download URL: gnar-gear-1.0.1.tar.gz
  • Upload date:
  • Size: 9.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for gnar-gear-1.0.1.tar.gz
Algorithm Hash digest
SHA256 97636f6958f5fb0aa6ce82bfd77b0cb12f0e95c85f77b8808f0be623cd68a6df
MD5 d1e2b2c33b9d1e08761735d0a4870999
BLAKE2b-256 9ac465f77a8cca40880751ea9a2745deca196a13ebab54fb31a3e0b2da8a4e8d

See more details on using hashes here.

File details

Details for the file gnar_gear-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for gnar_gear-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 76fa95d90e266dde5540fe86a42ee9987699eb1b0fc4ce281ddb09e9a2ee6741
MD5 eb2bbfeacc75094e63c222843fe8ad12
BLAKE2b-256 1c05ff9b918f8a7277249090bb3d87fe28ad0fb9c765e15b785f041651416008

See more details on using hashes here.

Supported by

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