Keg 0.11.1
A web framework built on Flask & SQLAlchemy.  Somewhere North of Flask but South of Django.
License: BSD

.. default-role:: code

Keg: more than Flask
####################

.. image:: https://codecov.io/github/level12/keg/coverage.svg?branch=master
    :target: https://codecov.io/github/level12/keg?branch=master

.. image:: https://img.shields.io/pypi/v/Keg.svg
    :target: https://img.shields.io/pypi/v/Keg.svg

.. image:: https://img.shields.io/pypi/l/keg.svg
    :target: https://img.shields.io/pypi/l/keg.svg

.. image:: https://img.shields.io/pypi/pyversions/keg.svg
    :target: https://img.shields.io/pypi/pyversions/keg.svg

.. image:: https://img.shields.io/pypi/status/Keg.svg
    :target: https://img.shields.io/pypi/status/Keg.svg

.. image:: https://ci.appveyor.com/api/projects/status/wm35hheykxs8851r
    :alt: AppVeyor Build
    :target: https://ci.appveyor.com/project/level12/keg-6gnlh

Keg is an opinionated but flexible web framework built on Flask and SQLAlchemy.


Keg's Goal
==========

The goal for this project is to encapsulate Flask best practices and libraries so devs can avoid
boilerplate and work on the important stuff.

We will lean towards being opinionated on the big things (like SQLAlchemy as our ORM) while
supporting hooks and customizations as much as possible.

Think North of Flask but South of Django.

Features
========

Default Logging Configuration
-----------------------------

We highly recommend good logging practices and, as such, a Keg application does basic setup of the
Python logging system:

- Sets the log level on the root logger to INFO
- Creates two handlers and assigns them to the root logger:

  - outputs to stderr
  - outputs to syslog

- Provides an optional json formatter

The thinking behind that is:

- In development, a developer will see log messages on stdout and doesn't have to monitor a file.
- Log messages will be in syslog by default and available for review there if no other action is
  taken by the developer or sysadmin.  This avoids the need to manage log placement, permissions,
  rotation, etc.
- It's easy to configure syslog daemons to forward log messages to different files or remote log
  servers and it's better to handle that type of need at the syslog level than in the app.
- Structured log files (json) provide metadata details in a easy-to-parse format and should be
  easy to generate.
- The options and output should be easily configurable from the app to account for different needs
  in development and deployed scenarios.
- Keg's logging setup should be easy to turn off and/or completely override for situations where it
  hurts more than it helps.

Installation
============

`pip install keg`


App Configuration
=================

CLI Command
-----------

The command `<myapp> develop config` will give detailed information about the files and objects
being used to configure an application.

Profile Priority
----------------

All configuration classes with the name `DefaultProfile` will be applied to the app's config
first.

Then, the configuration classes that match the "selected" profile will be applied on top of the
app's existing configuration. This makes the settings from the "selected" profile override any
settings from the `DefaultProfile.`

Practically speaking, any configuration that applies to the entire app regardless of what context
it is being used in will generally go in `myapp.config` in the `DefaultProfile` class.

Selecting a Configuration Profile
---------------------------------

The "selected" profile is the name of the objects that the Keg configuration handling code will
look for.  It should be a string.

A Keg app considers the "selected" profile as follows:

* If `config_profile` was passed into `myapp.init()` as an argument, use it as the
  selected profile.  The `--profile` cli option uses this method to set the selected profile and
  therefore has the highest priority.
* Look in the app's environment namespace for "CONFIG_PROFILE".  If found, use it.
* If running tests, use "TestProfile".  Whether or not the app is operating in this mode is
  controlled by the use of:

  - `myapp.init(use_test_profile=True)` which is used by `MyApp.testing_prep()`
  - looking in the app's environment namespace for "USE_TEST_PROFILE" which is used by
    `keg.testing.invoke_command()`

* Look in the app's main config file (`app.config`) and all it's other
  config files for the variable `DEFAULT_PROFILE`.  If found, use the value from the file with
  highest priority.


Views
=====

While generic Flask views will certainly work in this framework, Keg provides a BaseView that
applies a certain amount of magic around route and blueprint setup. BaseView is based on Flask's
MethodView. Best practice is to set up a blueprint and attach the views to it via the `blueprint`
attribute. Be aware, BaseView will set up some route, endpoint, and template location defaults,
but these can be configured if needed.

Blueprint Setup
---------------

Adding views to a blueprint is accomplished via the `blueprint` attribute on the view. Note,
BaseView magic kicks in when the class is created, so assigning the blueprint later on will not
currently have the desired effect.

    import flask
    from keg.web import BaseView

    blueprint = flask.Blueprint('routing', __name__)


    class VerbRouting(BaseView):
        blueprint = blueprint

        def get(self):
            return 'method get'

Once the blueprint is created, you must attach it to the app via the `use_blueprints` app attribute:

    from keg.app import Keg
    from my_app.views import blueprint


    class MyApp(Keg):
        import_name = 'myapp'
        use_blueprints = (blueprint, )

Blueprints take some parameters for URL prefix and template path. BaseView will respect these when
generating URLs and finding templates:

    blueprint = flask.Blueprint(
        'custom',
        __name__,
        template_folder='../templates/specific-path',
        url_prefix='/tanagra')

    class BlueprintTest(BaseView):
        # template "blueprint_test.html" will be expected in specific-path
        # endpoint is custom.blueprint-test
        # URL is /tanagra/blueprint-test
        blueprint = blueprint

        def get(self):
            return self.render()

Template Discovery
------------------

To avoid requiring the developer to configure all the things, BaseView will attempt to discover the
correct template for a view, based on the view class name. Generally, this is a camel-case to
underscore-notation conversion. Blueprint name is included in the path, unless the blueprint has
its own `template_path` defined.

* `class MyBestView` in blueprint named "public" -> `<app>/templates/public/my_best_view.html`
* `class View2` in blueprint named "other" with template path "foo" -> `<app>/foo/view2.html`

A view may be given a `template_name` attribute to override the default filename, although the same
path is used for discovery:

    class TemplateOverride(BaseView):
        blueprint = blueprint
        template_name = 'my-special-template.html'

        def get(self):
            return self.render()

URL and Endpoint Calculation
----------------------------

BaseView has `calc_url` and `calc_endpoint` class methods which will allow the developer to avoid
hard-coding those types of values throughout the code. These methods will both produce the full
URL/endpoint, including the blueprint prefix (if any).

Route Generation
----------------

BaseView will, by default, create rules for views on their respective blueprints. Generally, this
is based on the view class name as a camel-case to dash-notation conversion:

* `class MyBestView` in blueprint named "public": `/my-best-view` -> `public.my-best-view`
* `class View2` in blueprint named "other" with URL prefix "foo": `/foo/view2` -> `other.view2`

Note that BaseView is a MethodView implementation, so methods named `get`, `post`, etc. will be
respected as the appropriate targets in the request/response cycle.

A view may be given a `url` attribute to override the default:

    class RouteOverride(BaseView):
        blueprint = blueprint
        url = '/something-other-than-the-default'

        def get(self):
            return self.render()

See `keg_apps/web/views/routing.py` for other routing possibilities that BaseView supports.


Components
==========

Keg components follow the paradigm of flask extensions, and provide some defaults for the
purpose of setting up model/view structure. Using components, a project may be broken down into
logical blocks, each having their own entities, blueprints, templates, tests, etc.

* Components need to be registered in config at `KEG_REGISTERED_COMPONENTS`

  * The path given here should be a full dotted path to the top level of the component

    * e.g. `my_app.components.blog`

  * At the top level of the component, `__component__` must be defined as an instance of KegComponent

    * Depending on the needs of the component, model and view discovery may be driven by the subclasses
      of KegComponent that have path defaults
    * Examples:

      * `__component__ = KegModelComponent('blog')`
      * `__component__ = KegViewComponent('blog')`
      * `__component__ = KegModelViewComponent('blog')`

* Component discovery

  * A component will attempt to load model and blueprints on app init
  * The default paths relative to the component may be modified or extended on the component's definition
  * Default model path in "model" components: `.model.entities`

    * Override via the component's `db_visit_modules` list of relative import paths

  * Default blueprint path for "view" components: `.views.component_bp`

    * Use the `create_named_blueprint` or `create_blueprint` helpers on the component's `__component__`
      to create blueprints with configured template folders
    * Override via the component's `load_blueprints` list

      * List elements are a tuple of the relative import path and the name of the blueprint attribute

    * Components have their own template stores, in a `templates` folder

      * Override the component's template path via the `template_folder` attribute

  * Paths may also be supplied to the constructor

    * e.g. `__component__ = KegComponent('blog', db_visit_modules=('.somewhere.else', ))`


Keg Development
===============

To develop on keg, begin by installing dependencies and running the tests::

    git clone https://github.com/level12/keg keg-src
    cd keg-src

    docker-compose up -d

    # Create a virtualenv with a supported python version.  Activate it.
    vex -m keg

    # Install editable keg and test dependencies
    pip install -e .[tests]

    # Run tests
    pytest keg

You can then examine tox.ini for insights into our development process.  In particular, we:

* use `pytest` for testing (and coverage analysis)
* use `flake8` for linting

Preview Readme
--------------

When updating the readme, use `restview --long-description` to preview changes.

Links
=====

* Documentation: https://keg.readthedocs.io/en/stable/index.html
* Releases: https://pypi.org/project/Keg/
* Code: https://github.com/level12/keg
* Issue tracker: https://github.com/level12/keg/issues
* Questions & comments: http://groups.google.com/group/blazelibs

Current Status
==============

* Stable in a relatively small number of production environments.
* API is likely to change with smaller compatibility breaks happening more frequently than larger ones.


Changelog
=========

0.11.1 released 2024-12-17
--------------------------

- update python testing targets (34a04ed_)
- support newer python-json-logger (4cd5376_)

.. _34a04ed: https://github.com/level12/keg/commit/34a04ed
.. _4cd5376: https://github.com/level12/keg/commit/4cd5376


0.11.0 released 2023-03-03
--------------------------

- **BREAKING CHANGES**

  - app context is no longer pushed as part of test suite setup (046ce40_)

- fix docs build for CI, needs pytz dependency explicitly since babel doesn't include it now (5215f63_)
- support SQLAlchemy 2.0 (3dc3c3f_)

.. _5215f63: https://github.com/level12/keg/commit/5215f63
.. _3dc3c3f: https://github.com/level12/keg/commit/3dc3c3f
.. _046ce40: https://github.com/level12/keg/commit/046ce40


0.10.2 released 2022-12-07
--------------------------

- fix request context requirement in assets parsing (1d6b60f_)

.. _1d6b60f: https://github.com/level12/keg/commit/1d6b60f


0.10.1 released 2022-11-03
--------------------------

- fix break in CLI test context usage (b035e7c_)

.. _b035e7c: https://github.com/level12/keg/commit/b035e7c


0.10.0 released 2022-11-01
--------------------------

- **BREAKING CHANGES**

  - no longer add default route when at least one rule present (15b003e_)
  - remove default blueprint with ping and exception-test routes (b09d55e_)
  - remove six dependency and python 2 support (f2339df_)

- **DEPRECATIONS**

  - use dash-named templates by default and warn for underscore-named templates (32e426a_)

- support Flask 2.2 and Flask-SQLAlchemy 3.0 (edde099_)
- allow bind-specific dialect options (a528fe2_)
- ensure plugin commands are loaded when running a command (a2e0c0a_)
- add developer note to readme for how to handle internationalization files (98a792a_)
- drop morphi-related warning filter (d45a44e_)
- clarify template used for exception test (f720a76_)
- protect destructive CLI commands with prompts (0a071d1_)
- fix docs formatting and clarify db_before_import signal usage (4337461_)
- resolve pytest deprecation warning about nose methods (e1d1a6e_)
- resolve mssql/odbc tests in CI (c8e4b47_)

.. _15b003e: https://github.com/level12/keg/commit/15b003e
.. _b09d55e: https://github.com/level12/keg/commit/b09d55e
.. _98a792a: https://github.com/level12/keg/commit/98a792a
.. _a528fe2: https://github.com/level12/keg/commit/a528fe2
.. _f2339df: https://github.com/level12/keg/commit/f2339df
.. _d45a44e: https://github.com/level12/keg/commit/d45a44e
.. _f720a76: https://github.com/level12/keg/commit/f720a76
.. _32e426a: https://github.com/level12/keg/commit/32e426a
.. _a2e0c0a: https://github.com/level12/keg/commit/a2e0c0a
.. _0a071d1: https://github.com/level12/keg/commit/0a071d1
.. _4337461: https://github.com/level12/keg/commit/4337461
.. _e1d1a6e: https://github.com/level12/keg/commit/e1d1a6e
.. _c8e4b47: https://github.com/level12/keg/commit/c8e4b47
.. _edde099: https://github.com/level12/keg/commit/edde099


0.9.1 released 2021-06-09
-------------------------

- BaseView: expose template name calculation (47d95af_)
- fix translation files (3e1375e_)

.. _47d95af: https://github.com/level12/keg/commit/47d95af
.. _3e1375e: https://github.com/level12/keg/commit/3e1375e


0.9.0 released 2021-06-07
-------------------------

- BREAKING CHANGE: Revert "quiet and verbose flags to (in/de)crement log level" (a64ff15_)
- Merge Flask 2.x support & related (8578b2f_)


.. _a64ff15: https://github.com/level12/keg/commit/a64ff15
.. _8578b2f: https://github.com/level12/keg/commit/8578b2f


0.8.9 released 2021-05-07
-------------------------

- update python support indicators and drop pathlib dependency (fef7088_)

.. _fef7088: https://github.com/level12/keg/commit/fef7088


0.8.8 released 2021-04-20
-------------------------

- limit sqlalchemy and flask-sqlalchemy updates until we support (ab46a44_)
- correct db test case (abf4919_)

.. _ab46a44: https://github.com/level12/keg/commit/ab46a44
.. _abf4919: https://github.com/level12/keg/commit/abf4919


0.8.7 released 2021-02-26
-------------------------

- Deprecate KEG_DB_ENGINE_OPTIONS config in favor of Flask-SQLAlchemy's SQLALCHEMY_ENGINE_OPTIONS (6e1038d_)
- Remove make_shell_context from app, as the Flask app has that method now since 0.11 (75e4541_)
- Update db init to create needed schemas from dialect options. Deprecates prep_empty (8def18a_)
- Allow quiet and verbose CLI options to influence log level (ac5fad6_)
- Add inrequest decorator and context manager for testing views without full stack (041a0cf_)
- Link documentation in readme (e12044b_)
- Allow replacing class view response via pre_response lifecycle method (741c60e_)
- Support python-dotenv use (1100b39_)

.. _6e1038d: https://github.com/level12/keg/commit/6e1038d
.. _75e4541: https://github.com/level12/keg/commit/75e4541
.. _8def18a: https://github.com/level12/keg/commit/8def18a
.. _ac5fad6: https://github.com/level12/keg/commit/ac5fad6
.. _041a0cf: https://github.com/level12/keg/commit/041a0cf
.. _e12044b: https://github.com/level12/keg/commit/e12044b
.. _741c60e: https://github.com/level12/keg/commit/741c60e
.. _1100b39: https://github.com/level12/keg/commit/1100b39


0.8.6 released 2021-01-27
-------------------------

- BaseView.assign_blueprint allows adding blueprint after class declaration (6f454fc_)
- Allow returning an empty string as response (96f3e04_)
- Provide a signal hook db_before_import prior to loading db entities (948ba82_)
- Add a setting to enforce SQLite foreign key (a7450ba_)
- Allow blueprint class to be specified for component (78ac281_)
- Add testing.app_config context manager for injecting config during starting (4b97985_)
- Resolve failures in config resolution, and provide better reporting in CLI (e9537df_)

.. _6f454fc: https://github.com/level12/keg/commit/6f454fc
.. _96f3e04: https://github.com/level12/keg/commit/96f3e04
.. _948ba82: https://github.com/level12/keg/commit/948ba82
.. _a7450ba: https://github.com/level12/keg/commit/a7450ba
.. _78ac281: https://github.com/level12/keg/commit/78ac281
.. _4b97985: https://github.com/level12/keg/commit/4b97985
.. _e9537df: https://github.com/level12/keg/commit/e9537df
.. _295f5df: https://github.com/level12/keg/commit/295f5df


0.8.5 released 2020-11-25
-------------------------

- follow flask namespace recommendations for component blueprint templates (b5d6cf8_)

.. _b5d6cf8: https://github.com/level12/keg/commit/b5d6cf8


0.8.4 released 2020-05-12
-------------------------

- remove unused oauth code (use keg-auth instead) (8e6a2c0_)
- check translations in CI (b377eee_)
- fix test suite issues (06320e9_)
- adding sphinx docs (69e5c31_)
- remove keyring support (500994d_)

.. _8e6a2c0: https://github.com/level12/keg/commit/8e6a2c0
.. _b377eee: https://github.com/level12/keg/commit/b377eee
.. _06320e9: https://github.com/level12/keg/commit/06320e9
.. _69e5c31: https://github.com/level12/keg/commit/69e5c31
.. _500994d: https://github.com/level12/keg/commit/500994d


0.8.3 released 2019-11-29
-------------------------

- Remove Flask version pin (0b03f12_)

.. _0b03f12: https://github.com/level12/keg/commit/0b03f12


0.8.2 released 2019-11-12
-------------------------

- fix bug in calculating URL with prefix-less blueprints (7d02b01_)

.. _7d02b01: https://github.com/level12/keg/commit/7d02b01


0.8.1 released 2019-11-06
-------------------------

- Add basic component structure for app organization into logical blocks (830f93b_)
- Add `--help-all` option to print out nested tree of app commands (b11fe7e_)
- Clean up view's use of blueprint attributes to discover templates and calculate URLs/endpoints (949c578_)
- Limit flask to <1.1.0 until context breakage is resolved (217246f_)

.. _830f93b: https://github.com/level12/keg/commit/830f93b
.. _b11fe7e: https://github.com/level12/keg/commit/b11fe7e
.. _949c578: https://github.com/level12/keg/commit/949c578
.. _217246f: https://github.com/level12/keg/commit/217246f


0.8.0 released 2019-03-25
-------------------------

- BREAKING CHANGE: Remove web.BaseView awareness of xhr() method and remove dependency on the
  deprecated flask.request.is_xhr (0899c5d_)
- improve CI (3311389_)
- Update to support Flask 1.0+ (63e3667_)
- remove Pipenv including updated readme & tox (03b3920_)

.. _3311389: https://github.com/level12/keg/commit/3311389
.. _63e3667: https://github.com/level12/keg/commit/63e3667
.. _03b3920: https://github.com/level12/keg/commit/03b3920
.. _0899c5d: https://github.com/level12/keg/commit/0899c5d


0.7.0 released 2019-02-07
-------------------------

- Enable setting engine options from KEG variable (5bb807f_)

.. _5bb807f: https://github.com/level12/keg/commit/5bb807f


0.6.6 released 2018-11-13
-------------------------

- Add optional i18n support using morphi (d75a8fb_)
- Update pipenv dependencies to remove warning (b3b089e_)
- Pass through CLI invocation arguments and allow STDIN in CLI tests (bac2e3b_)

.. _d75a8fb: https://github.com/level12/keg/commit/d75a8fb
.. _b3b089e: https://github.com/level12/keg/commit/b3b089e
.. _bac2e3b: https://github.com/level12/keg/commit/bac2e3b


0.6.5 released 2018-05-28
-------------------------

- Update readme, start using pipenv, pin Flask < 1.0 (abdc9bf_)

.. _abdc9bf: https://github.com/level12/keg/commit/abdc9bf


0.6.4 released 2018-01-09
-------------------------

- when testing, don't log to syslog by default (304a0a7_)

.. _304a0a7: https://github.com/level12/keg/commit/304a0a7


0.6.3 released 2018-01-09
-------------------------

- add '@cee:' prefix to json syslog formatter (b7ea5d3_)

.. _b7ea5d3: https://github.com/level12/keg/commit/b7ea5d3


0.6.2 released 2017-12-19
-------------------------

- db: get rid of code to replace session object (149b42c_)

.. _149b42c: https://github.com/level12/keg/commit/149b42c


0.6.1 released 2017-11-16
-------------------------

- fix quiet logging (e46fd2b_)
- a few small updates/fixes to readme (2044439_)

.. _e46fd2b: https://github.com/level12/keg/commit/e46fd2b
.. _2044439: https://github.com/level12/keg/commit/2044439


0.6.0 released 2017-08-18
-------------------------

- ADD: make CLIBase operate off `current_app` as last resort (1b358c1_)
- ADD: --quiet option to script options (6eb723f_)
- BREAKING CHANGE: adjust cli API on KegApp (af45880_)

.. _1b358c1: https://github.com/level12/keg/commit/1b358c1
.. _6eb723f: https://github.com/level12/keg/commit/6eb723f
.. _af45880: https://github.com/level12/keg/commit/af45880


0.5.1 released 2017-08-15
-------------------------

- ADD: mitigate CSRF bug in Flask-WTF (42a2e70_)
- ADD: config, init, and routing enhancements (cdfa901_)
- MAINT: upgrade to CircleCI 2.0 (60e3bfa_)

.. _42a2e70: https://github.com/level12/keg/commit/42a2e70
.. _cdfa901: https://github.com/level12/keg/commit/cdfa901
.. _60e3bfa: https://github.com/level12/keg/commit/60e3bfa


0.5.0 released 2017-06-27
-------------------------

- prep for pyp usage (23424b9_)
- Merge branch 'logging-improvements' (PR66_)

.. _23424b9: https://github.com/level12/keg/commit/23424b9
.. _PR66: https://github.com/level12/keg/pull/66



0.4.1 - 2017-02-09
------------------

* BUG: Properly quote pgsql identifiers during create (86852ad_)

.. _86852ad: https://github.com/level12/keg/commit/86852ad



0.4.0 - 2016-12-19
------------------

* BUG: Properly Update Keyring Config Data (7f1908f_)
* MSSQL dialect support (df7e89d_)
* MAINT: Refactor keyring to accept bytes (15bc04b_)
* MAINT: Remove deprecated flask hooks (4f7e2bf_)
* Remove unicode_literal futures (dc2fa85_)
* MAINT: Create windows build environment (983e040_)
* MAINT: Run CI with Docker (bc7a877_)
* Remove extra cp in readme (7e94815_)

.. _7f1908f: https://github.com/level12/keg/commit/7f1908f
.. _df7e89d: https://github.com/level12/keg/commit/df7e89d
.. _15bc04b: https://github.com/level12/keg/commit/15bc04b
.. _4f7e2bf: https://github.com/level12/keg/commit/4f7e2bf
.. _dc2fa85: https://github.com/level12/keg/commit/dc2fa85
.. _983e040: https://github.com/level12/keg/commit/983e040
.. _bc7a877: https://github.com/level12/keg/commit/bc7a877
.. _7e94815: https://github.com/level12/keg/commit/7e94815