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
ConfigBackupCapabilitywill only match plugins that inherit fromConfigBackupCapability. - 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
NotImplementedForThisPlatformat 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
- Writing Plugins -- implement your capability in a platform plugin
- Resolution and Defaults -- how capabilities influence plugin selection