.. _subs_overview:

==========================
Plugin Subsystems Overview
==========================

Plugin subsystems, or `subs`, are the main container type used inside of `pop`.
These ``subs`` contains the collections of plugins, patterns, contracts, and interfaces
that drive your application. Fortunately adding ``subs`` to your ``hub`` is easy to do and
adding plugins to your ``subs`` is equally as easy. All of the namespaces, tricky plugin
loading, and tracking are all taken care of in `pop`.

When you add a ``sub`` you have many options. Most of the demo subs you see will be
very simple, just a call to `hub.pop.sub.add` with only the pypath variable
specified. This is all you need in most cases! But you can do much more powerful things
when loading up a new `sub`!

The init.__init__ Function
==========================

The first thing to be aware of in ``pop`` is the `init.__init__` function. When you make a new `sub`
the *init.py* file is treated as the initializer, or pattern definer of the `sub`. This file
does not need to exist to have a sub, but it exposes extra functionality. The main thing to be
aware of is that if the ``__init__`` function is defined inside the *init.py* file then it will
be executed when the subsystem is loaded. The first argument passed to the ``__init__`` function
is, as usual, the hub, and the sub object that the *init.py* has been loaded onto is available.
This makes it easy to initialize any data structures that might be needed on the `sub`.

Directories and How to Find Them
================================

Plugin loading is all based on the directories that contain the *.py* files that constitute
plugins. When the ``pypath`` argument is passed in python imports that path, then it derives
what directory that path is and adds it to the directories to be scanned. The directories
used by the ``sub`` can be loaded via a number of options:

    pypath: A string or list of python imports that import a python package containing plugins

    static: A string or list of strings that are directory paths containing plugins.

    contracts_pypath: A string or list of strings that import a python package containing plugins to load contracts

    contracts_static: A string or list of things that are directory paths containing plugins to load contracts

Dynamic Name
============

The Dynamic Name function is amazingly powerful. It allows you to specify a dynamic loader name
that pop will detect in your Python path and auto load extra plugins from external Python
packages that have defined them. This is an amazing way to dynamically make your plugin
subsystem even more pluggable by allowing external applications to extend your system.

The Dynamic Name system is used by adding the option `dyne_name`. It is the only required
option when enabling dynamic name, but it also requires that your application adds the
`DYNE` flag to the conf.py file in the root of your project.

    dyne_name: A string which defines the name of the subsystem, and how to map it using the
    Dynamic Name system

For more information on Dynamic Names please see the doc outlining how the Dynamic Names system
works and how to use it: :ref:`dyne_name`

Omitting Components From the Sub
================================

By default, when modules are loaded, they omit objects that start with an underscore. This is set
to allow for objects to be kept private to the module and not expose them. The character used
to determine if the object should be omitted can be changed, or it can be set as an endwith char:

    omit_start: The char to look for at the start of all objects to determine if it should be omitted, defaults to '_'

    omit_end: The char to look for at the end of all objects to determine if it should be omitted, disabled by default

    omit_func: Set to True to omit all functions in the sub

    omit_class: Set to True to omit all classes in the sub

    omit_vars: Set to True to omit all vars from a sub

If you choose to change any of these values in your default settings for your ``sub`` it should be heavily
documented, as it will really confuse users of your sub. It is strongly discouraged!!

Stopping on Load Failures
=========================

It can be good to set the sub loading to traceback if a plugin fails to load. Because plugin
interfaces allow end users to add plugins and potentially dirty up the code, by default
if a plugin fails to load it does not stop the sub from loading.
If you do want the sub to traceback set:

    stop_on_failures: Set to True to make the sub traceback on failures to load plugins propagate up

Virtual Execution
=================

When modules are loaded they execute the ``__virtual__`` function. The ``__virtual__`` function
can be disabled for a sub when it is loaded. This is typically only used for debugging.

Modify the Initializer
======================

By default the ``__init__`` function is run when the sub loads. This can be disabled by setting
the ``init`` value to False:

    init: Set to False to disable running the ``__init__`` functions for all modules

Sub Alias Options
=================

Sometimes it makes sense to allow a sub to be called via an alias. This can be easily added
to a sub by setting the ``__sub_alias__`` value in the sub's `init.py`:

.. code-block:: python

   __sub_alias__ = ["red", "blue", "green"]

Now the sub can be refered to as the primary name used, but also as red, blue or green.
If there is a conflict, where a sub's primary name is overwritten by an alias, the primary
name will win.

Multiple Python Module Objects
==============================

When plugins are loaded they are imported into the python module tracking system in a specific
module path. If you want to be able to load the plugins multiple times and have them exist
in multiple namespaces then you can via `mod_basename`. You only need to do this if you are
loading persisted data onto the module level. If you are doing this then move your data
onto the `hub`:

    mod_basename: Pass a string to specify the Python sys.modules namespace to load the module onto
