Skip to content

Basic Usage & Lifecycle (ConnectionProxy)

This page shows how to use connection proxies in function blocks and how to manage connection lifecycle correctly.


Before you start (two common gotchas)

1) Plugin registration is import-time

Plugins register themselves when their modules are imported. If you never import any plugin modules, the registry may be empty and you’ll get PluginNotFoundError even if plugins exist in the SDK.

The simplest way to ensure the built-in plugins are registered is:

import neops_worker_sdk.connection.plugins  # noqa: F401

# noqa: F401 tells linters to ignore “unused import” warnings. This import has side effects (plugin registration) even though you don’t reference the module directly.

If you write custom plugins, you still need to import your plugin modules somewhere before use (same idea: import-time registration).

2) Device object requirements

To connect successfully, the resolved base plugin typically requires:

  • device.platform (dict/object with short_name, or a string platform)
  • device.ip
  • device.username
  • device.password

Missing any of these usually raises ConnectionValidationError during connect.

The safest pattern is to use the proxy’s context manager so connections are always cleaned up:

import neops_worker_sdk.connection.plugins  # noqa: F401

from neops_worker_sdk.connection.proxy import ConnectionProxy
from neops_worker_sdk.connection.capabilities import DeviceInfoCapability
from neops_worker_sdk.workflow import Device


# First, define your proxy class (see 33-writing-capabilities-and-proxies.md for details)
class DeviceInfoProxy(ConnectionProxy, DeviceInfoCapability):
    pass


device = Device(
    id=1,
    platform={"id": 1, "name": "ios", "short_name": "ios"},
    ip="192.168.1.1",
    username="admin",
    password="secret",
    skip_if_identifier_exists=False,
    identifier_fields=[],
)

with DeviceInfoProxy.connect(
    device,
    connection_type="ssh",
    connection_library="scrapli",
    connect=True,  # default
    fallback_to_default=False,  # default
    validate_capabilities=True,  # default
) as proxy:
    version = proxy.get_version()

What you get:

  • automatic cleanup via proxy.close() in finally
  • clearer code in long workflows

Manual lifecycle: MyProxy.get_connection() + close()

If you need manual lifecycle control, do this:

proxy = DeviceInfoProxy.get_connection(device, "ssh", "scrapli")
try:
    version = proxy.get_version()
finally:
    proxy.close()

Warning: Don’t rely on GC / object destruction to close connections. Always call close() if you used MyProxy.get_connection() directly.

Note: After close(), the proxy will still delegate method calls to the plugin, but the plugin’s get_raw_connection() will typically return None. Most capability methods should then raise ConnectionCreationError (“No connection available…”). Treat a closed proxy as unusable.


Parameters you should know

Both get_connection() and connect() accept these important keyword parameters:

  • connect: bool = True: if False, creates the connection wrapper without establishing a live connection (advanced/testing use).
  • fallback_to_default: bool = False: opt-in default selection when the library is omitted or resolution is ambiguous.
  • validate_capabilities: bool = True: warn if the resolved plugin doesn’t implement capability methods required by the proxy.

Deferred connect (advanced)

proxy = DeviceInfoProxy.get_connection(device, "ssh", "scrapli", connect=False)
# ... inspect proxy/plugin, do other setup ...
proxy.plugin.initialize_connection(connect=True)

Picking connection type and library

ConnectionProxy.get_connection() takes:

  • connection_type: "ssh", "api", "netconf", "restconf", …
  • connection_library: "netmiko", "scrapli", "httpx", "ncclient", …

In most real deployments you should specify both to avoid ambiguity:

proxy = DeviceInfoProxy.get_connection(device, "ssh", "scrapli")

See 30-device-connections.md for the current conventions and examples.


Defaults are opt-in (fallback_to_default=True)

Plugins can register as defaults:

  • platform default: used when you don’t specify connection type/library
  • platform + connection_type default: used when you specify type but not library

Defaults are only considered when you pass fallback_to_default=True.

Example: platform + connection_type default

proxy = DeviceInfoProxy.get_connection(device, "ssh", fallback_to_default=True)

Example: platform default

proxy = DeviceInfoProxy.get_connection(device, fallback_to_default=True)

Note: If the registry still finds multiple candidates and no applicable default exists, you’ll get AmbiguousPluginError. See 32-method-resolution-priority.md.


Errors you should expect (and handle)

Common exceptions raised during resolution/connection:

  • ConnectionValidationError: device is missing required fields (ip/username/password/platform)
  • PluginNotFoundError: no plugin exists for the platform/type/library/capability requirements
  • AmbiguousPluginError: multiple plugins qualify (and no default selection was possible)
  • ConnectionCreationError: dependency missing or the library couldn’t connect

Missing capability methods at call time raise:

  • NotImplementedForThisPlatform: rich context (proxy, method, interface, platform, plugin, device id)

Example: handling common failures

from neops_worker_sdk.connection.exceptions import (
    AmbiguousPluginError,
    ConnectionCreationError,
    ConnectionValidationError,
    NotImplementedForThisPlatform,
    PluginNotFoundError,
)

try:
    with DeviceInfoProxy.connect(device, "ssh", "scrapli") as proxy:
        version = proxy.get_version()
except ConnectionValidationError as exc:
    # device data is incomplete (fix device fields; retrying won't help)
    raise
except PluginNotFoundError as exc:
    # plugin modules not imported (add: import neops_worker_sdk.connection.plugins)
    # OR no plugin exists for your platform/type/library combination
    raise
except AmbiguousPluginError as exc:
    # multiple plugins match (specify library or opt into defaults)
    raise
except ConnectionCreationError as exc:
    # auth/network/dependency problems; consider retry with backoff
    raise
except NotImplementedForThisPlatform as exc:
    # capability method missing on the selected plugin (use proxy override or different plugin)
    raise

Capability validation warnings (early signal)

By default, proxy creation uses validate_capabilities=True, which warns if the resolved plugin is missing methods for the proxy’s declared capabilities.

This is intentionally a warning (not an error) because the system supports best‑match fallback and custom proxy overrides. See 33-writing-capabilities-and-proxies.md for how to override methods on the proxy.