puzzleThe Plugin

What is a Plugin?

A Plugin is where your actual functionality lives. It's the concrete implementation that users interact with. Plugins:

  • Are attached to a specific Handler via the @Plugin.for_handler() decorator

  • Receive services automatically through injection

  • Define metadata (name, version, description)

  • Can be extended through inheritance for polymorphic behavior


Attaching Plugins to Handlers

Every plugin must be attached to a handler using the @Plugin.for_handler() decorator:

from gl_plugin.plugin.plugin import Plugin

@Plugin.for_handler(CalculatorHandler)
class MathPlugin(Plugin):
    name = "MathPlugin"
    version = "1.0.0"
    description = "A math plugin"

This binding tells the framework:

  • Which handler manages this plugin

  • What services to inject (from that handler's create_injections())

  • How to initialize the plugin (via that handler's initialize_plugin())

circle-exclamation

Plugin Metadata

Every plugin requires three metadata attributes:

Attribute
Purpose

name

Unique identifier used with manager.get_plugin("name")

version

Version tracking for your plugin

description

Documents what the plugin does

circle-info

The name must be unique within the manager. Registering two plugins with the same name will cause conflicts and typically, the latest registered one will overwrite the previous one.


Abstract vs Concrete Plugins

Plugins support inheritance, allowing you to define abstract base plugins with shared behavior and concrete implementations with specific functionality.

Abstract Base Plugin

Define a base plugin with shared services and abstract methods:

Concrete Implementations

Extend the base plugin with specific implementations:

Benefits of This Pattern

This pattern allows you to:

  • Define a consistent interface via the base plugin

  • Share service injections across all implementations

  • Add new operations without modifying existing code

  • Process all plugins of a type uniformly


Overriding Injected Services

Sometimes you need to replace an injected service with your own instance. GL Plugin's injection mechanism makes this straightforward using Python's __new__ vs __init__ lifecycle.

Understanding the Lifecycle

  1. __new__ — Instance is created, services are injected

  2. __init__ — Your initialization code runs, services already exist

Because injection happens at __new__, any assignment in __init__ overwrites the injected service.

Basic Override

Override Order Matters

If your parent class uses the service in its __init__, override before calling super().__init__():

If the parent doesn't use the service in __init__, order doesn't matter:


When to Override Services

Testing with Mocks

Replace real services with mocks for unit testing:

Special Cases Requiring Custom Instances

When a specific plugin needs different configuration:

Concrete Implementations Needing Different Behavior

When extending a plugin but needing a specialized service:


Summary

Concept
Description

@Plugin.for_handler()

Binds a plugin to its handler

Metadata

name, version, description required

Abstract plugins

Define shared interface and services

Concrete plugins

Implement specific functionality

Inheritance

Child plugins inherit services and behavior

Overriding

Assign in __init__ to replace injected services

Override order

Override before super().__init__() if parent uses the service

Last updated