NETCONF client with truly async capabilities
Project description
pyNetX
pyNetX is a Python library that facilitates both synchronous and asynchronous client-side scripting and application development around the NETCONF protocol. Developed by Sambhu Nampoothiri G, pyNetX provides a modern, efficient interface for interacting with NETCONF-enabled network devices — with truly asynchronous capabilities using non blocking connections.
Current Versions: Stable: v2.0.3
v2.0.3 — 2026-05-28
Highlights
-
Hardened NETCONF notification handling
- Fixed a crash path where exceptions from the notification reactor thread could escape into C++
std::threadand terminate the Python process. - Notification reactor callbacks now log read failures, unregister the affected file descriptor, mark the subscription inactive, and allow the Python process to continue running.
- The notification reactor now stores weak references to
NetconfClientinstances, preventing stale raw-pointer access if a Python client object is destroyed while a notification FD is still registered.
- Fixed a crash path where exceptions from the notification reactor thread could escape into C++
-
Safer notification subscription startup
- Notification sockets are now registered with the epoll reactor only after the
<create-subscription>RPC has completed successfully. - This prevents the reactor thread from accidentally reading the subscription
<rpc-reply>beforesubscribe_async()/subscribe_sync()receives it.
- Notification sockets are now registered with the epoll reactor only after the
-
Improved notification cleanup
- Added mutex protection around notification resources, including the notification session, channel, socket, and subscription state flags.
- Cleanup paths now unregister notification FDs before resetting notification resources.
is_subscription_active()now safely reports whether the notification subscription is still usable.
-
Hardened async bridge and worker threads
- Detached pybind11 watcher threads now have top-level exception guards, so Python event-loop shutdown or callback scheduling errors are logged instead of terminating the process.
- Thread-pool worker tasks now have defensive exception guards to prevent unexpected C++ exceptions from escaping worker threads.
-
Notification queue behavior
notif_queue_size=-1means the notification queue is unbounded.- A non-negative
notif_queue_sizelimits the number of queued notifications. - When the queue is full, new notifications are dropped and a message is logged.
Upgrade notes
- This release is intended to be a drop-in stability update.
set_notification_reactor_count(n)is optional. If it is not called, pyNetX creates one notification reactor automatically when the first subscription is registered.- For large deployments, call
set_notification_reactor_count(n)before creating many subscriptions. next_notification()is a synchronous polling method. Do not useawait client.next_notification(). Useclient.next_notification()directly, even inside an async function.set_threadpool_size(n)should be called before starting async NETCONF operations. Runtime resizing during active operations is not recommended.- If the Python event loop closes before an async operation completes, pyNetX logs the callback scheduling failure instead of aborting the interpreter.
pip install pyNetX==2.0.3
v2.0.2 — 2026-04-01
Highlights
-
Improved exception handling to prevent Python process crashes
- Fixes a critical issue introduced in v1.0.9 where, under high load, destructors did not reliably release memory objects. In some cases this raised an exception, triggered
std::terminate, and caused Python processes to crash. - This release improves memory cleanup and adds safer exception handling across the API surface to prevent those crashes.
- Fixes a critical issue introduced in v1.0.9 where, under high load, destructors did not reliably release memory objects. In some cases this raised an exception, triggered
-
Added
notif_queue_sizefor internal notification queues- A new
notif_queue_sizeparameter is available when creating the internal notification queue for each device. - This setting controls how many notifications are buffered until they are consumed.
- If the queue exceeds that limit, newer notifications are discarded and a message is logged to the console.
- The default value is
-1, which means the queue size is unbounded. - This parameter must be specified when creating the
NetconfClientobject.
- A new
-
Global release build with builds for 3.11, 3.12, 3.13 and 3.14
- This version of pyNetX supports multiple python versions upto 3.14.
-
Why it matters
- This release improves runtime stability under load, reduces the risk of unexpected Python process termination, and gives users better control over notification queue growth to help prevent memory pressure and queue overflows.
Internal changes
- Minor cleanup and implementation updates in the pybind11 wrapper lambdas.
Bug fixes
- Fixes the crash behavior introduced in v1.0.9.
- No new functional regressions were introduced in v2.0.2.
Upgrade notes
- Safe drop-in upgrade. There are no API-breaking changes compared with v1.0.9.
- If you previously installed pyNetX from Test PyPI, install the updated wheel with:
pip install pyNetX==2.0.2
v1.0.9 — 2025-07-03
Highlights
- Cancellation-safe asyncio bridge
- Added a guard (
fut_pending()) in the C++ wrapper so callbacks skipset_result()/set_exception()if the Pythonasyncio.Futurehas already been cancelled or finished. - Why it matters: eliminates sporadic
asyncio.exceptions.InvalidStateError: invalid stateseen when a running task is cancelled or times out while waiting for an RPC reply.
- Added a guard (
Internal changes
- Minor code changes in the pybind11 wrapper lambdas.
Bug fixes
- No functional regressions introduced by v1.0.8.
Upgrade notes
- Safe to drop-in. There are no API changes compared with v1.0.8.
- If you previously installed pyNetX from Test PyPI, grab the new wheel with
pip install pyNetX==1.0.9
v1.0.8 — 2025-06-30
Highlights
-
Epoll-based Notification Subsystem
- Re-implemented the internal notification reactor on top of Linux
epoll, eliminating the legacy select-based notification loop. - Why it matters:
- Scales linearly with the number of active NETCONF notification streams.
- Dramatically reduces CPU wake-ups under heavy load (measured ~85 % drop at 500 FDs).
- Lower latency for bursts of notifications, especially when many devices are idle most of the time.
- No new threads are created for each notification arrival; a fixed pool started at program launch can handle hundreds of devices per thread.
- Re-implemented the internal notification reactor on top of Linux
-
Smarter Task-Pool Sharing
- The global task pool now assigns workers to devices dynamically based on real-time queue depth rather than static round-robin.
- This allows tasks to be spread across queues more efficiently as per current load, minimizing task queue depth and improving aggregate throughput by up to 40 % in mixed-traffic scenarios.
Internal changes
- Added
set_notification_reactor_count()to let applications resize the epoll reactor pool on the fly. - Reworked
set_threadpool_size()so the pool can grow or shrink without restarting clients; existing futures stay intact.
Bug fixes
- Fixed a hard-coded NETCONF base 1.0 header in
send_rpc_async(rpc="…"); the call now follows the user mentioned version.
Deprecations
receive_notification_async()has been removed; migrate tonext_notification()before v1.0.8.
Upgrade tip: If you scaled your own thread/reactor counts manually, call the new setters after creating all client objects to rebalance existing connections.
Documentation
The full documentation (with detailed API references and more usage examples) is here.
pyNetX Official Documentation GitHub Repository PyPI Medium
Requirements
- Python: 3.11+
- Build Dependencies:
setuptools,wheel,cmake,scikit-build, andpybind11 - System Libraries:
libxml2,libxslt(for XML processing)libssh2,tinyxml2, and audit tools (if required, install via your system’s package manager)
Note: On Debian/Ubuntu, you might install the system libraries with:
sudo apt-get install libxml2-dev libxslt1-dev libssh2-dev tinyxml2-dev audit
Installation
You can install pyNetX in either of the following ways:
-
From PyPI:
pip install pyNetX
-
From Source:
git clone https://github.com/jackofsometrades99/pyNetX.git cd pyNetX python setup.py install
Examples
Synchronous Usage
Below is an example of how to retrieve a device’s running configuration synchronously:
from pyNetX import (
NetconfClient,
NetconfConnectionRefusedError,
NetconfAuthError,
NetconfChannelError,
NetconfException
)
try:
# Create a NETCONF client instance
client = NetconfClient(
hostname="192.168.1.1",
port=830,
username="admin",
password="admin",
connect_timeout=30, # CONNECT TIMEOUT FROM CHANNEL. DEFAULT IS 60 SECONDS
read_timeout=30 # READ TIMEOUT FROM CHANNEL. DEFAULT IS 60 SECONDS
)
# Establish a connection
status = client.connect_sync()
# Retrieve the running configuration
config = client.get_config_sync(source="running")
print("Running Configuration:")
print(config)
# Disconnect from the device
client.disconnect_sync()
except (Exception, NetconfConnectionRefusedError, NetconfAuthError, NetconfChannelError, NetconfException) as error:
pass
Asynchronous Usage
The asynchronous API methods are provided with an _async suffix and integrate with Python’s asyncio. For example:
import asyncio
from pyNetX import (
NetconfClient,
NetconfConnectionRefusedError,
NetconfAuthError,
NetconfChannelError,
NetconfException
)
async def main():
try:
client = NetconfClient(
hostname="192.168.1.1",
port=830,
username="admin",
password="admin",
connect_timeout=30, # CONNECT TIMEOUT FROM CHANNEL. DEFAULT IS 60 SECONDS
read_timeout=30 # READ TIMEOUT FROM CHANNEL. DEFAULT IS 60 SECONDS
)
# Asynchronously connect to the device
status = await client.connect_async()
# Retrieve configuration asynchronously
config = await client.get_config_async(source="running")
print("Running Configuration:")
print(config)
# Asynchronously disconnect from the device
await client.disconnect_async()
except (Exception, NetconfConnectionRefusedError, NetconfAuthError, NetconfChannelError, NetconfException) as error:
pass
# Run the asynchronous main function
asyncio.run(main())
API Overview
The main class provided by pyNetX is NetconfClient, which offers both synchronous and asynchronous methods for NETCONF operations.
Synchronous Methods
-
connect_sync()
Establishes a NETCONF session with the target device. -
disconnect_sync()
Closes the NETCONF session. -
send_rpc_sync(rpc)
Sends a custom RPC command. -
get_sync(filter="")
Retrieves device information using an optional filter. -
get_config_sync(source="running", filter="")
Retrieves the device configuration. -
copy_config_sync(target, source)
Copies configuration from one datastore to another. -
delete_config_sync(target)
Deletes configuration from the specified target. -
validate_sync(source="running")
Validates the configuration. -
edit_config_sync(target, config, do_validate=False)
Edits the device configuration. -
subscribe_sync(stream="NETCONF", filter="")
Subscribes to NETCONF notifications. -
receive_notification_sync()Fetches a single received notification from the notification channel. -
lock_sync(target="running")andunlock_sync(target="running")
Lock and unlock a configuration datastore, respectively. -
commit_sync()
Commits any configuration changes. -
locked_edit_config_sync(target, config, do_validate=False)
Performs an edit configuration operation while holding a lock.
Asynchronous Methods
For every synchronous method, there is an asynchronous counterpart that returns an asyncio Future:
connect_async()disconnect_async()send_rpc_async(rpc="")next_notification()Polls the internal notification queue. This method is not awaitable; call it directly.get_async(filter="")get_config_async(source="running", filter="")copy_config_async(target, source)delete_config_async(target)validate_async(source="running")edit_config_async(target, config, do_validate=False)subscribe_async(stream="NETCONF", filter="")lock_async(target="running")unlock_async(target="running")commit_async()locked_edit_config_async(target, config, do_validate=False)
Common Methods.
These methods can be used in both synchronous and asynchronous operations:
-
delete_subscription()Unsubscribe from recieving notifications. -
set_threadpool_size(nThreads)Sets the number of threads in the shared task pool. The default is 4 threads. The number of threads in the pool determines how many tasks or operations can run concurrently. Note that for each device, operations (such as get_async, edit_config_async, etc.) are executed sequentially using a lock to avoid channel corruption. This nThreads value controls the total number of concurrent operations across all clients (devices) in the application. To use this, you can simply:import pyNetX pyNetX.set_threadpool_size(10)
-
set_notification_reactor_count(nThreads)Configures how many background epoll reactor threads pyNetX uses to monitor notification sockets.If this function is not called, pyNetX automatically creates one reactor when the first notification subscription is registered.
For large deployments, call this before creating many subscriptions:
import pyNetX # Create 8 epoll‐based reactors to handle your notification streams pyNetX.set_notification_reactor_count(8)
Existing subscriptions are rebalanced when the reactor count is changed, but applications should prefer configuring this once during startup.
Exception Handling
pyNetX defines custom exceptions to handle various NETCONF-related errors:
-
NetconfConnectionRefusedError
Raised when a connection attempt is refused. -
NetconfAuthError
Raised when authentication fails. -
NetconfChannelError
Raised for channel-related errors. -
NetconfException
The base exception for NETCONF-related issues.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pynetx-2.0.3.tar.gz.
File metadata
- Download URL: pynetx-2.0.3.tar.gz
- Upload date:
- Size: 37.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e9422630f851c34ec6b34903ef5ab6e7deda9a61afa084f53e7500758c10d95
|
|
| MD5 |
8ca28d11c72df2dff9f756542abd5bc3
|
|
| BLAKE2b-256 |
5a71b9f5568ab46a71f6fd630b70c38856526510a19692c4c5da23892e39be01
|
File details
Details for the file pynetx-2.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: pynetx-2.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 4.2 MB
- Tags: CPython 3.14, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d8f83a4c4d4ad0a5dc672950f030968ad1463648597d9f379291ff3aa89d49b
|
|
| MD5 |
3dfdb2dab825b833f06494fea3d7e6d9
|
|
| BLAKE2b-256 |
0b9877c62a795668f68d2080ebf83d0fcc7e6e529fe291588398600223999372
|
File details
Details for the file pynetx-2.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: pynetx-2.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 4.2 MB
- Tags: CPython 3.13, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
764c72a8495f1d25d9a802de1647b3ef245c0795b43e17f45b038ed037e3c485
|
|
| MD5 |
dabc845df86fd5942e28e320a3746c9f
|
|
| BLAKE2b-256 |
3cb7f5a968efb52714c703a788599a2fdd57c12b75c3c5e7f405eef565943fa0
|
File details
Details for the file pynetx-2.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: pynetx-2.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 4.2 MB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca5e494de046c4b624692cfa4d812818abb4748c9c436cf94467a05488bc983b
|
|
| MD5 |
9a31a3cc762c0627c0870606290ecd74
|
|
| BLAKE2b-256 |
485811fe3a1a53e638110ec39fc1cd7a40a4fccd66b6935e81a990c868ca7fa8
|
File details
Details for the file pynetx-2.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: pynetx-2.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 4.2 MB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14d4c5b03585589c7976d0b55974d0774938f9c30adf152dfecb74ef4a22402a
|
|
| MD5 |
a758128c13f697b567a4a853f2146239
|
|
| BLAKE2b-256 |
19d2f390e585525e478ef60a9c380fbc2f8755c9628d9121c287291908095e57
|