Skip to content

Writing Capability Interfaces

A capability interface defines a set of related operations that a connection plugin can provide. The SDK ships with DeviceInfoCapability; when your automation needs operations that no existing capability covers, you create a new one.


When to Create a New Capability

Create a new capability when:

  • The operation is reusable across platforms (configuration backup, interface stats, routing tables).
  • No existing capability covers the use case.
  • You want compile-time typing and runtime validation for that operation.

Do not create a capability for a one-off command on a single platform. Use get_raw_connection() directly instead (see Best Practices).


Step-by-Step

1. Subclass CapabilityInterface

Every capability inherits from CapabilityInterface, the marker base class the metaclass uses for detection.

2. Define Abstract Methods

Declare each operation as an @abstractmethod with explicit return types. The method bodies should contain only ... (Ellipsis).

3. Write Docstrings

Docstrings are critical -- they are the first thing users see when NotImplementedForThisPlatform is raised. Describe the expected return structure and semantics.

4. Keep Interfaces Cohesive

Each capability should address one concern. Prefer two small interfaces over one large interface that mixes configuration retrieval with inventory collection.


Example: ConfigBackupCapability

from abc import abstractmethod

from neops_worker_sdk.connection.capabilities import CapabilityInterface


class ConfigBackupCapability(CapabilityInterface):
    """Capability for retrieving device configuration."""

    @abstractmethod
    def get_running_config(self) -> str:
        """Return the current running configuration as a string."""
        ...

    @abstractmethod
    def get_startup_config(self) -> str:
        """Return the saved startup configuration as a string."""
        ...

Create a proxy that requires this capability:

from neops_worker_sdk.connection.proxy import ConnectionProxy  # noqa: E402


class ConfigBackupProxy(ConnectionProxy, ConfigBackupCapability):
    pass

Proxies can compose multiple capabilities:

from neops_worker_sdk.connection.capabilities import DeviceInfoCapability  # noqa: E402


class FullBackupProxy(ConnectionProxy, ConfigBackupCapability, DeviceInfoCapability):
    pass

Design Guidelines

Guideline Rationale
One concern per interface Plugins can implement exactly the capabilities they support
Unique method names across interfaces Avoids ambiguity in __getattribute__ delegation
Explicit return types Enables static analysis and IDE autocompletion
Descriptive docstrings Appear in NotImplementedForThisPlatform context

Method name collisions

Two capability interfaces must not define methods with the same name. The metaclass maps each method name to the first interface encountered in the MRO; collisions are resolved silently and may cause confusing delegation behavior.


Capability-Driven Plugin Selection

Capabilities influence plugin resolution at connection time. When a proxy declares capabilities, the resolver filters candidates to those implementing the required interfaces. This means:

  • A proxy with ConfigBackupCapability will only match plugins that inherit from ConfigBackupCapability.
  • If no plugin implements all required capabilities, the resolver picks the best-match plugin (most capabilities) and logs a warning.
  • Calling a method that the selected plugin does not implement raises NotImplementedForThisPlatform at runtime.

See Resolution and Defaults for the full algorithm.


IDE and Type Checker Notes

Capability interfaces use @abstractmethod, so static analyzers may report that a proxy "does not implement abstract methods." This is expected. At runtime, the ProxyMeta metaclass injects fallback implementations and clears __abstractmethods__, making the proxy fully instantiable.

If your IDE complains, suppress the warning locally (e.g., PyCharm # noinspection PyAbstractClass or a type-ignore comment on the class line).


Existing Capabilities

The SDK ships with:

Capability Methods Purpose
DeviceInfoCapability get_version() Vendor, model, serial, software release

Import from neops_worker_sdk.connection.capabilities.


Next Steps