Skip to content

Plugin mechanism for Controller subclasses via entry points #376

@coretl

Description

@coretl

After #360 we should do this

Summary

We need a way for external packages (e.g. fastcs_foo) to register Controller subclasses so that fastcs can discover and instantiate them dynamically (e.g. from YAML configuration), without requiring explicit imports of those packages.

This would enable a proper plugin ecosystem for controllers.


Proposed approach

Use Python packaging entry points as an extension mechanism.

  • Define an entry point group: fastcs.controllers
  • Each plugin package registers its Controller subclasses there
  • fastcs discovers them at runtime using importlib.metadata.entry_points

Example plugin (fastcs_foo)

Implementation

# fastcs_foo/foo_controller.py

from fastcs.controller import Controller

class FooController(Controller):
    def run(self):
        print(f"Foo running with {self.config}")

Registration

# pyproject.toml

[project.entry-points."fastcs.controllers"]
FooController = "fastcs_foo.foo_controller:FooController"

YAML configuration

Controllers are referenced by:

<module>.<ClassName>

Example:

controllers:
  - type: fastcs_foo.FooController
    config:
      speed: 10

Discovery + resolution (in fastcs)

from importlib.metadata import entry_points
from fastcs.controller import Controller


def discover_controllers():
    registry = {}

    for ep in entry_points(group="fastcs.controllers"):
        cls = ep.load()

        namespace = cls.__module__.split(".", 1)[0]
        name = ep.name

        registry[(namespace, name)] = cls

    return registry


def resolve_controller(type_string: str):
    namespace, class_name = type_string.split(".", 1)

    registry = discover_controllers()

    key = (namespace, class_name)

    if key not in registry:
        raise ValueError(f"Unknown controller '{type_string}'")

    cls = registry[key]

    if not issubclass(cls, Controller):
        raise TypeError(f"{type_string} is not a Controller subclass")

    return cls

Benefits

  • No explicit imports required
  • Clean separation between core and plugins
  • Works with standard Python packaging
  • Controllers can live in independently installable packages
  • YAML remains simple and explicit (fastcs_foo.FooController)

Notes / future considerations

  • Could validate distribution name vs module root more strictly
  • Could support aliases or versioning
  • Could cache discovery results for performance

Outcome

This would provide a robust, extensible plugin system for Controller implementations, similar to how pytest and other tools manage plugins.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions