Plugin Subsystems - Subs

Plugin Subsystems - or Subs - are the basic containers for code in Plugin Oriented Programming. Subs contain both plugins and data. These Subs are available to the application via the hub.

The concept of the Sub is roughly as central and critical to Plugin Oriented Programming as the concept of a class is to Object Oriented Programming. This is therefore the central focus of how to create a plugin oriented application.

Subs Contain Plugins

Subs appear to simply contain plugins. This is similar to classes, on the surface, as they appear to just contain data and functions. Like classes, Subs are a canvas for the developer, allowing for the complexity of the software to be simplified into reusable, flexible compartments. While the Sub is comparable to the criticality of classes in OOP, it should not be confused with classes! Plugin Oriented Programming does not have the concept of inheritance, for instance.

When a Sub is created it gets placed on the hierarchical namespace - the hub - so that any application can call into functions and data exposed by the Sub.

The Sub’s primary purpose is to present plugins. The plugins are individual files containing tight code compartments that express an interface.

Adding Subs to a hub

Once a hub is available then it is easy to add a Sub to the hub. Keep in mind that there are many ways to construct a Sub. In the end a Sub only needs to be a directory with .py files in it, nothing more. With that said, there are many ways to identify how to find the directory, or directories!

But do not despair. pop presents many flexible options, but retains a strong opinion about how to do things right.

Using Dynamic Names

The best way to add a Sub to your hub is to use Dynamic Names. Using Dynamic Names is easy. It requires you to register your Sub with the pop configuration system.

Assuming you started with a pop-create project called “poppy”, open up the project’s conf.py file and add a Sub called rpc. The conf.py file can be found at poppy/conf.py AKA <Project name>/conf.py:

DYNE = {
    "rpc": ["rpc"],
}

The conf.py will already exist as pop-create creates it. When you add a DYNE entry to the conf.py, it registers that under this project’s plugins for the Sub - in this case named rpc - where it can be found.

This means that multiple codebases can contribute plugins to the Sub! Third party developers can very easily extend your Sub without modifying your code to do it! This is one of the most powerful aspects of Plugin Oriented Programming called Vertical App Merging.

Now that your Sub is registered, it can be added to a hub by calling hub.pop.sub.add and passing in the dyne_name argument:

hub.pop.sub.add(dyne_name="rpc")

Since the hub is available anywhere in your application, it can be called from anywhere. A plugin can add a new Sub. This is a core design consideration of Plugin Oriented Programming. A plugin cannot be a dead end. It needs to be able to always add globally available Subs onto the hub.

Using PyPath

Using the pypath system in pop is the simplest way to add a Sub to your hub, but it does not activate Vertical App Merging.

Using pypath is simple, just call hub.pop.sub.add and pass in the Python import path that, when imported, will lead the way to the directory with plugins.

hub.pop.sub.add('poppy.poppy')

That’s it! Now you have a new Sub on your hub called poppy that will find your plugins by importing poppy.poppy, looking at the imported module’s path variable and translating that into a directory which is then scanned for plugins.

Recursive Sub Loading

When a Sub gets added to the hub it can be desirable to find any directories under the found director(ies) and add them to the Sub as nested Subs. This can be an excellent way to organize code, allowing for Subs to exist nested within each other on the hub.

To recursively scan for nested Subs just call hub.pop.sub.load_subdirs(hub.subname, recurse=True). This will look in all of the directories defined in the loaded Sub and scan for any subdirectories. If those subdirectories exist they will be found and loaded onto the hub as nested Subs.

Plugin Loading

By default, plugins in pop are lazy loaded. They only get imported when they are first accessed. This greatly speeds up the creation of a Sub. If a Sub contained hundreds of Plugins then it would take some time to load all of the plugins, typically a few seconds. Lazy loading allows this time consumption to be bypassed.

Sometimes it may be desirable to pre-load all of your plugins. To do this just call hub.pop.sub.load_all(hub.subname).

Some events will trigger loading all plugins. In particular, if you decide to iterate over a Sub then the load_all call will be executed on the Sub if it has not been called already, therefore ensuring that all plugins are loaded and can be cleanly iterated over.

The init System

Now that your Sub is on your hub, let’s take a look at the Init system used by pop. This system allows you to initialize your new Sub. In a nutshell, you can place an optional plugin in your Sub named init.py and this plugin will be automatically loaded when your Sub gets created. Think of the init.py as the plugin that defines how your Sub will function. Typically the Sub’s pattern is defined in the init.py, but we will cover patterns in more depth in the chapter on patterns!

The __init__ Function

Just like Classes in Python, plugins in pop can be initialized. Just create an optional function called __init__. This function will be called when the plugin gets loaded.

Also, as in Python classes, the __init__ function should not be used to call code that runs or starts the pattern expressed in the Sub. If we were to do this then it would violate App Merging! The control of a Sub should always be separated from the initialization. This makes the __init__ function perfect for setting up data structures on the hub that will be needed by the plugins of the Sub.

Subs Express Patterns

When creating a Sub it should always express a pattern. Patterns in Plugin Oriented Programming are used as a consistent, predictable way to make your Subs pluggable.

Read on to the next chapter to learn more about patterns and why they are so central to Plugin Oriented Programming.