Architecture

pyNetX has a Python API backed by a C++ extension module.

Async RPC flow

Python await
  -> pybind11 binding
  -> C++ std::future
  -> global C++ ThreadPool
  -> libssh2 NETCONF channel
  -> C++ result/exception
  -> AsyncFutureDispatcher
  -> Python event loop
  -> asyncio.Future resolved

Async methods are asyncio-friendly wrappers around C++ worker-thread tasks. The Python event loop is not blocked while the C++ backend performs NETCONF work.

Per-client RPC serialization

Operations on the same NetconfClient primary RPC session are serialized to preserve request/reply ordering on the NETCONF channel.

Use separate NetconfClient objects for separate devices or independent sessions.

Notification flow

NETCONF device
  -> notification SSH/NETCONF session
  -> epoll notification reactor
  -> persistent notification stream parser
  -> per-client notification queue
  -> next_notification() / next_notification_async()
  -> NotificationHealthEvent stream when pressure or errors occur

Normal RPC traffic and notification traffic use separate SSH/NETCONF sessions. The notification path keeps a per-client receive buffer so coalesced frames, fragmented frames, and malformed fragments can be classified before data is exposed through the queue.

Global components

Thread pool

The global C++ thread pool executes async NETCONF operations.

pyNetX.set_threadpool_size(16)

Configure this during process startup.

Async future dispatcher

The dispatcher bridges completed C++ futures back into the Python event loop. This avoids one watcher thread per async operation.

Notification reactors

Notification reactors use epoll to monitor notification sockets.

pyNetX.set_notification_reactor_count(8)

Configure this during process startup before creating subscriptions.

Notification stream parser

NETCONF notifications arrive over SSH as bytes, not as one notification per read. pyNetX v2.0.7 stores unread or incomplete notification bytes in a persistent per-client receive buffer. The parser repeatedly extracts complete ]]>]]>-delimited frames, keeps trailing partial bytes for later reads, and raises health events when malformed stream data is detected.

The parser handles multiple complete notifications in one read, complete frames followed by partial trailing data, partial frames completed by later reads, abandoned partial frames when a new <notification> starts too early, empty EOM frames, orphan bytes before a notification start tag, and complete notifications recovered without an EOM before the next notification.

Event bus

The process-wide NotificationEventBus stores bounded health events. Python consumers read events with next_notification_event() or next_notification_event_async().

NETCONF framing

pyNetX v2.0.7 uses NETCONF 1.0 EOM framing with ]]>]]>.

Custom RPC callers should provide only the XML RPC payload. pyNetX appends the EOM marker internally.