2
0
Fork 0

Add docs for handlers, app handler

This commit is contained in:
Lance Edgar 2023-11-24 15:49:57 -06:00
parent 6b110e567a
commit 4a7729a702
13 changed files with 153 additions and 14 deletions

View file

@ -25,9 +25,9 @@ Glossary
virtual environment. virtual environment.
app handler app handler
Python object representing the core of the :term:`app`. There is Python object representing the core :term:`handler` for the
normally just one "global" app handler, which is an instance of :term:`app`. There is normally just one "global" app handler;
:class:`~wuttjamaican.app.AppHandler`. see also :doc:`narr/handlers/app`.
app name app name
Code-friendly name for the underlying app/config system Code-friendly name for the underlying app/config system
@ -85,6 +85,14 @@ Glossary
.. _Python Packaging User Guide: https://packaging.python.org/en/latest/specifications/entry-points/ .. _Python Packaging User Guide: https://packaging.python.org/en/latest/specifications/entry-points/
handler
Similar to a "plugin" concept but only *one* handler may be used
for a given purpose. See also :doc:`narr/handlers/index`.
package
Generally refers to a proper Python package, i.e. a collection of
modules etc. which is installed via ``pip``.
settings table settings table
Table in the :term:`app database` which is used to store Table in the :term:`app database` which is used to store
:term:`config settings<config setting>`. :term:`config settings<config setting>`.

View file

@ -2,7 +2,12 @@
WuttJamaican WuttJamaican
============ ============
This package provides a "base layer" for custom apps. This package provides a "base layer" for custom apps, regardless of
environment/platform:
* console
* web
* GUI
It mostly is a distillation of certain patterns developed within the It mostly is a distillation of certain patterns developed within the
`Rattail Project`_, which are deemed generally useful. (At least, `Rattail Project`_, which are deemed generally useful. (At least,
@ -24,6 +29,7 @@ Features
* flexible configuration, using config files and/or DB settings table * flexible configuration, using config files and/or DB settings table
* flexible command line interface, with arbitrary top-level and * flexible command line interface, with arbitrary top-level and
subcommands subcommands
* flexible architecture, abstracting various portions of the overall app
Contents Contents

View file

@ -110,8 +110,8 @@ are really aliases) or can use different functions:
poser = poser.commands:poser_main poser = poser.commands:poser_main
wutta-poser = poser.commands:wutta_poser_main wutta-poser = poser.commands:wutta_poser_main
Next time your ``poser`` package is installed, the command will be Next time your ``poser`` :term:`package` is installed, the command
available: will be available:
.. code-block:: sh .. code-block:: sh

View file

@ -6,8 +6,8 @@ The command line interface is an important part of app automation and
may be thought of in a couple ways: may be thought of in a couple ways:
First there is the :term:`ad hoc script` which is a single file and First there is the :term:`ad hoc script` which is a single file and
can be placed anywhere, but is not installed as part of a package. can be placed anywhere, but is not installed as part of a
See :doc:`scripts`. :term:`package`. See :doc:`scripts`.
But the "real" command line interface uses :term:`commands<command>` But the "real" command line interface uses :term:`commands<command>`
and :term:`subcommands<subcommand>`; these are installed as part of a and :term:`subcommands<subcommand>`; these are installed as part of a

View file

@ -10,8 +10,8 @@ A script is just a text file with Python code. To run it you
generally must invoke the Python interpreter somehow and explicitly generally must invoke the Python interpreter somehow and explicitly
tell it the path to your script. tell it the path to your script.
Note that a script is (usually) not installed as part of a package. Note that a script is (usually) not installed as part of a
They can live anywhere. :term:`package`. They can live anywhere.
Below we'll walk through creating a script. Below we'll walk through creating a script.

View file

@ -82,8 +82,8 @@ with underscore for sake of the subcommand entry point:
wutta_poser.subcommands = wutta_poser.subcommands =
hello = poser.commands:Hello hello = poser.commands:Hello
Next time your ``poser`` package is installed, the subcommand will be Next time your ``poser`` :term:`package` is installed, the subcommand
available, so you can e.g.: will be available, so you can e.g.:
.. code-block:: sh .. code-block:: sh

View file

@ -0,0 +1,68 @@
App Handler
===========
There is one special "global" type of :term:`handler` which
corresponds to the :term:`app` itself, whereas all other handlers
correspond to some "portion" of the app.
The :term:`app handler` provides:
* various "global" utilities
* primary interface for obtaining all other handlers
The base class and default app handler is
:class:`wuttjamaican.app.AppHandler`.
The :term:`config object` is responsible for creating the app handler
via :meth:`~wuttjamaican.conf.WuttaConfig.get_app()`::
from wuttjamaican.conf import make_config
config = make_config()
app = config.get_app()
Overriding the App Handler
--------------------------
It is expected that many apps will want a more customized app handler.
To do this, first create your app handler class e.g. in
``poser/app.py``::
from wuttjamaican.app import AppHandler
class PoserAppHandler(AppHandler):
"""
Custom app handler for the Poser system.
"""
def make_session(self, **kwargs):
"""
Override this method to specify extra/default params etc.
"""
#kwargs.setdefault('foo', 'bar')
session = super().make_session(**kwargs)
return session
def hello(self):
"""
Extra method to print a hello message.
"""
print("hello from", self.appname)
Then in your config file, specify that your app handler should be used
instead of the default. Note that the config section will need to
match whatever the :term:`app name` is. (And note that the app name
is not necessarily the same as your :term:`package` name!)
.. code-block:: ini
# nb. this is the default
[wutta]
app.handler = poser.app:PoserAppHandler
# but if appname is 'foobar' then it should be this
[foobar]
app.handler = poser.app:PoserAppHandler

View file

@ -0,0 +1,31 @@
Architecture
============
Handlers are similar to a "plugin" concept in that multiple handlers
may be installed e.g. by different packages. But whereas one might
"enable" multiple plugins, only *one* handler may be used, for a given
purpose.
There can be many "types" of handlers; each is responsible for a
certain aspect of the overall app. So it can be thought of as,
"*Which* plugin should *handle* this aspect of the app?"
What a Handler Does
-------------------
Each type of handler does something different. For instance there
might be an "auth handler" responsible for authenticating user
credentials.
The app itself will define the need for a handler. For instance if a
user login mechanism is needed, then the app might define the "auth
handler" (e.g. ``AuthHandler``) base class, and add a way to locate
and use it at runtime.
Other packages might then also define "auth handlers" derived from the
base class, and perhaps a way for the app to locate them as well.
The app should probably have a way for the "choice" of auth handler to
be configurable, and possibly expose this choice via admin UI.

View file

@ -0,0 +1,10 @@
Handlers
========
.. toctree::
:maxdepth: 2
overview
arch
app

View file

@ -0,0 +1,10 @@
Overview
========
The :term:`handler` concept is central to the :term:`app` architecture.
They are analogous to "plugins" but only *one* handler is used for a
given purpose. See :doc:`arch`.
So far there is only one handler type defined; see :doc:`app`.

View file

@ -8,3 +8,4 @@ Documentation
install/index install/index
config/index config/index
cli/index cli/index
handlers/index

View file

@ -31,15 +31,18 @@ from wuttjamaican.util import load_entry_points, load_object, parse_bool
class AppHandler: class AppHandler:
""" """
Base class and default implementation for top-level app handler. Base class and default implementation for top-level :term:`app
handler`.
aka. "the handler to handle all handlers" aka. "the handler to handle all handlers"
aka. "one handler to bind them all" aka. "one handler to bind them all"
For more info see :doc:`/narr/handlers/app`.
There is normally no need to create one of these yourself; rather There is normally no need to create one of these yourself; rather
you should call :meth:`~wuttjamaican.conf.WuttaConfig.get_app()` you should call :meth:`~wuttjamaican.conf.WuttaConfig.get_app()`
on the config object if you need the app handler. on the :term:`config object` if you need the app handler.
:param config: Config object for the app. This should be an :param config: Config object for the app. This should be an
instance of :class:`~wuttjamaican.conf.WuttaConfig`. instance of :class:`~wuttjamaican.conf.WuttaConfig`.

View file

@ -562,6 +562,8 @@ class WuttaConfig:
""" """
Returns the global :class:`~wuttjamaican.app.AppHandler` Returns the global :class:`~wuttjamaican.app.AppHandler`
instance, creating it if necessary. instance, creating it if necessary.
See also :doc:`/narr/handlers/app`.
""" """
if not hasattr(self, 'app'): if not hasattr(self, 'app'):
spec = self.get(f'{self.appname}.app.handler', usedb=False, spec = self.get(f'{self.appname}.app.handler', usedb=False,