The gnarliest gear in the world 🤙
Project description
Gnar Gear: Gnarly Python Apps
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
- Flask app with auto blueprint registration
- Bjoern WSGI Server
- Why Bjoern? Check out these benchmarks!
- And also these benchmarks
- Postgres database connection via Postgres.py
- SES client connection via Boto 3
- JWT configuration via Flaks-JWT-Extended
- Flask-Bcrypt singleton
- Logger configuration
- Error handler with traceback
- Overridable and extendable class-based design
Requirements
Bjoern
Bjoern
requires libev
(high performance event loop)
- Install
libev
withbrew 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
parameterGNAR_LOG_LEVEL
environment variableINFO
- 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
, andall
attach_instance
- Attaches the
GnarApp
instance to theapp.main
module. This enables easy access to the Gnar app from anywhere in the application usingfrom <top-level-module>.main import app
- The
GnarApp
's runtime assets aredb
,bcrypt
,flask
, andget_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 theapp
folder for blueprints. - Each Flask
Blueprint
must be assigned to a global-levelblueprint
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>
- Without a
- Each item in the list of module names may use one of two formats:
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>}
- Logs the error message and its
configure_jwt
- Sets the Flask
JWT_SECRET_KEY
variable to the value of theGNAR_JWT_SECRET_KEY
environment variable. - Sets the Flask
JWT_ACCESS_TOKEN_EXPIRES
variable to the value of theGNAR_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
, andunauthorized_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
, andaws_secret_access_key
parameters of theboto3.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 thesend_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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for gnar_gear-1.0.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 76fa95d90e266dde5540fe86a42ee9987699eb1b0fc4ce281ddb09e9a2ee6741 |
|
MD5 | eb2bbfeacc75094e63c222843fe8ad12 |
|
BLAKE2b-256 | 1c05ff9b918f8a7277249090bb3d87fe28ad0fb9c765e15b785f041651416008 |