From 4a7729a7027d1249dadf8e74db55565e15babc51 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Nov 2023 15:49:57 -0600 Subject: [PATCH] Add docs for handlers, app handler --- docs/glossary.rst | 14 +++++-- docs/index.rst | 8 +++- docs/narr/cli/commands.rst | 4 +- docs/narr/cli/overview.rst | 4 +- docs/narr/cli/scripts.rst | 4 +- docs/narr/cli/subcommands.rst | 4 +- docs/narr/handlers/app.rst | 68 +++++++++++++++++++++++++++++++++ docs/narr/handlers/arch.rst | 31 +++++++++++++++ docs/narr/handlers/index.rst | 10 +++++ docs/narr/handlers/overview.rst | 10 +++++ docs/narr/index.rst | 1 + src/wuttjamaican/app.py | 7 +++- src/wuttjamaican/conf.py | 2 + 13 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 docs/narr/handlers/app.rst create mode 100644 docs/narr/handlers/arch.rst create mode 100644 docs/narr/handlers/index.rst create mode 100644 docs/narr/handlers/overview.rst diff --git a/docs/glossary.rst b/docs/glossary.rst index b0f3fa8..03a287a 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -25,9 +25,9 @@ Glossary virtual environment. app handler - Python object representing the core of the :term:`app`. There is - normally just one "global" app handler, which is an instance of - :class:`~wuttjamaican.app.AppHandler`. + Python object representing the core :term:`handler` for the + :term:`app`. There is normally just one "global" app handler; + see also :doc:`narr/handlers/app`. app name 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/ + 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 Table in the :term:`app database` which is used to store :term:`config settings`. diff --git a/docs/index.rst b/docs/index.rst index 1882c46..b5f37cf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,7 +2,12 @@ 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 `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 command line interface, with arbitrary top-level and subcommands +* flexible architecture, abstracting various portions of the overall app Contents diff --git a/docs/narr/cli/commands.rst b/docs/narr/cli/commands.rst index efde347..66445d4 100644 --- a/docs/narr/cli/commands.rst +++ b/docs/narr/cli/commands.rst @@ -110,8 +110,8 @@ are really aliases) or can use different functions: poser = poser.commands:poser_main wutta-poser = poser.commands:wutta_poser_main -Next time your ``poser`` package is installed, the command will be -available: +Next time your ``poser`` :term:`package` is installed, the command +will be available: .. code-block:: sh diff --git a/docs/narr/cli/overview.rst b/docs/narr/cli/overview.rst index 6fa164a..0589f38 100644 --- a/docs/narr/cli/overview.rst +++ b/docs/narr/cli/overview.rst @@ -6,8 +6,8 @@ The command line interface is an important part of app automation and may be thought of in a couple ways: 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. -See :doc:`scripts`. +can be placed anywhere, but is not installed as part of a +:term:`package`. See :doc:`scripts`. But the "real" command line interface uses :term:`commands` and :term:`subcommands`; these are installed as part of a diff --git a/docs/narr/cli/scripts.rst b/docs/narr/cli/scripts.rst index 3dfe877..cb6cc7d 100644 --- a/docs/narr/cli/scripts.rst +++ b/docs/narr/cli/scripts.rst @@ -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 tell it the path to your script. -Note that a script is (usually) not installed as part of a package. -They can live anywhere. +Note that a script is (usually) not installed as part of a +:term:`package`. They can live anywhere. Below we'll walk through creating a script. diff --git a/docs/narr/cli/subcommands.rst b/docs/narr/cli/subcommands.rst index aa9fc44..3df761f 100644 --- a/docs/narr/cli/subcommands.rst +++ b/docs/narr/cli/subcommands.rst @@ -82,8 +82,8 @@ with underscore for sake of the subcommand entry point: wutta_poser.subcommands = hello = poser.commands:Hello -Next time your ``poser`` package is installed, the subcommand will be -available, so you can e.g.: +Next time your ``poser`` :term:`package` is installed, the subcommand +will be available, so you can e.g.: .. code-block:: sh diff --git a/docs/narr/handlers/app.rst b/docs/narr/handlers/app.rst new file mode 100644 index 0000000..b211611 --- /dev/null +++ b/docs/narr/handlers/app.rst @@ -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 diff --git a/docs/narr/handlers/arch.rst b/docs/narr/handlers/arch.rst new file mode 100644 index 0000000..2d17dbb --- /dev/null +++ b/docs/narr/handlers/arch.rst @@ -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. diff --git a/docs/narr/handlers/index.rst b/docs/narr/handlers/index.rst new file mode 100644 index 0000000..24c6642 --- /dev/null +++ b/docs/narr/handlers/index.rst @@ -0,0 +1,10 @@ + +Handlers +======== + +.. toctree:: + :maxdepth: 2 + + overview + arch + app diff --git a/docs/narr/handlers/overview.rst b/docs/narr/handlers/overview.rst new file mode 100644 index 0000000..dd0f89f --- /dev/null +++ b/docs/narr/handlers/overview.rst @@ -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`. diff --git a/docs/narr/index.rst b/docs/narr/index.rst index 0f8f7e4..04c4268 100644 --- a/docs/narr/index.rst +++ b/docs/narr/index.rst @@ -8,3 +8,4 @@ Documentation install/index config/index cli/index + handlers/index diff --git a/src/wuttjamaican/app.py b/src/wuttjamaican/app.py index 131f971..9f2f64b 100644 --- a/src/wuttjamaican/app.py +++ b/src/wuttjamaican/app.py @@ -31,15 +31,18 @@ from wuttjamaican.util import load_entry_points, load_object, parse_bool 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. "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 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 instance of :class:`~wuttjamaican.conf.WuttaConfig`. diff --git a/src/wuttjamaican/conf.py b/src/wuttjamaican/conf.py index 504db29..542723a 100644 --- a/src/wuttjamaican/conf.py +++ b/src/wuttjamaican/conf.py @@ -562,6 +562,8 @@ class WuttaConfig: """ Returns the global :class:`~wuttjamaican.app.AppHandler` instance, creating it if necessary. + + See also :doc:`/narr/handlers/app`. """ if not hasattr(self, 'app'): spec = self.get(f'{self.appname}.app.handler', usedb=False,