Async and sync Python client for the LibreNMS API
Project description
libreclient
Async and sync Python client for the LibreNMS API.
- Dual interface โ use
LibreClientAsyncfor async/await orLibreClientSyncfor traditional blocking calls. - Typed responses โ all endpoints return Pydantic models with full IDE autocomplete.
- Environment-driven config โ configure via
LIBRENMS_URLandLIBRENMS_TOKENenv vars or pass values directly.
Installation
pip install libreclient
Or with uv:
uv add libreclient
Quick Start
Synchronous
from libreclient import LibreClientSync
client = LibreClientSync(url="https://librenms.example.com", token="your-api-token")
# List all devices
response = client.devices.list_devices()
for device in response.devices:
print(device["hostname"])
# Get a specific alert
alert = client.alerts.get_alert(42)
Asynchronous
import asyncio
from libreclient import LibreClientAsync
async def main():
client = LibreClientAsync(url="https://librenms.example.com", token="your-api-token")
response = await client.devices.list_devices()
for device in response.devices:
print(device["hostname"])
await client.close()
asyncio.run(main())
Context Manager
# Sync
with LibreClientSync(url="https://librenms.example.com", token="your-api-token") as client:
print(client.system.ping())
# Async
async with LibreClientAsync(url="https://librenms.example.com", token="your-api-token") as client:
print(await client.system.ping())
Configuration
Configuration is handled by pydantic-settings. You can pass values directly or set environment variables:
| Env Variable | Description | Default |
|---|---|---|
LIBRENMS_URL |
Base URL of your LibreNMS instance | (required) |
LIBRENMS_TOKEN |
API token (X-Auth-Token) |
(required) |
LIBRENMS_VERIFY_SSL |
Verify TLS certificates | true |
LIBRENMS_API_VERSION |
API version path segment | v0 |
A .env file in your working directory is also supported. Copy the included sample to get started:
cp sample.env .env
# Edit .env with your LibreNMS URL and API token
Available Route Namespaces
All route namespaces are accessible as properties on the client:
| Property | Description |
|---|---|
client.alerts |
Alert management and alert rules/templates |
client.arp |
ARP table lookups |
client.bills |
Billing data and graphs |
client.device_groups |
Device group management |
client.devices |
Device CRUD, discovery, components, graphs |
client.index |
List available API endpoints |
client.inventory |
Hardware inventory |
client.locations |
Location management |
client.logs |
Event, syslog, alert, and auth logs |
client.poller_groups |
Poller group info |
client.pollers |
Poller status |
client.port_groups |
Port group management |
client.port_security |
Port security (802.1X/MAB) |
client.ports |
Port info, search, and descriptions |
client.routing |
BGP, OSPF, VRF, MPLS, IPsec |
client.services |
Service monitoring |
client.switching |
VLANs, links, FDB, NAC |
client.system |
Ping and system info |
Contributing
Contributions are welcome! We're actively looking for contributors to help with:
- ๐ Bug fixes and edge case handling
- โจ Support for new routes as LibreNMS adds API endpoints
- ๐ Documentation improvements
- ๐งช Test coverage expansion
- ๐ง Tooling and CI improvements
Getting started:
- Fork the repo and create a branch from
dev - Follow the Development setup below
- Make your changes with tests
- Open a PR against
devโ CI will lint, test, and auto-fix formatting
See CONTRIBUTING.md for detailed guidelines, or just open an issue to discuss your idea first.
Development
Prerequisites
- Python 3.12+
- uv (package manager)
Setup
git clone https://github.com/jjeff07/libreclient.git
cd libreclient
uv sync
Running Tests
# Unit tests
uv run pytest tests/unit
# Functional tests (requires .env with LIBRENMS_URL and LIBRENMS_TOKEN)
uv run pytest tests/functional
Linting & Formatting
This project uses Ruff for both linting and formatting:
# Check for lint issues
uv run ruff check
# Auto-fix lint issues
uv run ruff check --fix
# Format code
uv run ruff format
# Check formatting without changing files
uv run ruff format --check
Complexity Checks
complexipy is used to enforce a maximum cognitive complexity of 15 per function:
uv run complexipy .
Results are output to complexipy-results.json. Any function exceeding the threshold will cause the check to fail.
Architecture
The project uses a single-implementation pattern: each route is written once as an async class. The synchronicity library then wraps each async class to produce a synchronous counterpart at runtime.
src/py_librenms/routes/alerts.py
โโโ class Alerts โ async implementation (the only code you write)
โโโ AlertsSync = synchronizer.wrap(Alerts, ...) โ sync wrapper (auto-generated at import)
This means:
- You only maintain one implementation per route.
- Both
LibreClientAsyncandLibreClientSyncshare the same logic. - No code duplication between sync and async interfaces.
Type Stubs
Because synchronicity generates wrapper classes dynamically, IDEs can't infer their method signatures. To restore full
autocomplete and type checking, .pyi stub files are auto-generated.
Regenerate stubs locally:
uv run python generate_stubs.py
Stubs are generated automatically during the GitHub Actions release workflow, so you don't need to commit them โ they're
in .gitignore.
Adding a New Route
- Create
src/py_librenms/routes/myroute.pywith an async class andMyRouteSync = synchronizer.wrap(...)at the bottom. - Create
src/py_librenms/models/myroute.pywith Pydantic response models. - Add exports to
src/py_librenms/models/__init__.py. - Add exports to
src/py_librenms/routes/__init__.py. - Wire up the route in
src/py_librenms/client.py(both sync and async clients). - Run
uv run python generate_stubs.py. - Add tests in
tests/unit/routes/test_myroute.pyandtests/unit/models/test_myroute.py.
Commit Convention
This project enforces Conventional Commits via commitizen. A git hook validates every commit message automatically.
Setup the hook (once per clone):
git config core.hooksPath .githooks
Format:
type(scope)?: description
[optional body]
[optional footer]
Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, bump
Examples:
feat(routing): add OSPFv3 port listing
fix: handle empty response from list_devices
docs: add upstream tracking section to README
test: add functional tests for switching routes
Upstream API Tracking
This project tracks which LibreNMS release tag the route implementations are based on. The pinned version is stored in
upstream_tracking.toml.
# Check if upstream has a newer release
python check_upstream.py
# See which API doc files changed
python check_upstream.py --diff
# See full unified diffs of changed docs
python check_upstream.py --full
# Compare against a specific tag instead of latest
python check_upstream.py --diff --tag 26.6.0
# Bump the pinned tag after reviewing changes
python check_upstream.py --bump
Project Structure
libreclient/
โโโ src/py_librenms/
โ โโโ __init__.py # Public API exports
โ โโโ client.py # LibreClientSync & LibreClientAsync
โ โโโ config.py # Pydantic-settings configuration
โ โโโ _base_client.py # Shared HTTP transport logic
โ โโโ models/ # Pydantic response models
โ โโโ routes/ # Route namespaces (async + sync wrappers)
โ โโโ _types.py # ClientProtocol & utilities
โ โโโ _synchronicity.py # Shared Synchronizer instance
โ โโโ alerts.py # Example route implementation
โ โโโ ...
โโโ tests/
โ โโโ unit/
โ โ โโโ models/ # Model validation tests
โ โ โโโ routes/ # Route logic tests (MockClient)
โ โโโ functional/ # Live API tests (requires .env)
โโโ check_upstream.py # Detect upstream API doc changes
โโโ upstream_tracking.toml # Pinned LibreNMS release tag
โโโ generate_stubs.py # .pyi stub generator
โโโ pyproject.toml
โโโ CHANGELOG.md
โโโ LICENSE
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 libreclient-0.1.2.tar.gz.
File metadata
- Download URL: libreclient-0.1.2.tar.gz
- Upload date:
- Size: 30.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d7957f6cb4a00ec519dad63625c4ed3ac1491fd370a3875b70ea6d9aeb85bba
|
|
| MD5 |
e4809fde3f0d1bfc1033c8ced6c5712d
|
|
| BLAKE2b-256 |
9df1031e784f9415dcdf3f6af582bd4c625cb46ad1e6eadc88fe0971dc374f02
|
File details
Details for the file libreclient-0.1.2-py3-none-any.whl.
File metadata
- Download URL: libreclient-0.1.2-py3-none-any.whl
- Upload date:
- Size: 61.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79e41f1442bcf4af4173eaf0ecc6b8b6f6334f3d272ad2f8fe87dbe0e236cbcf
|
|
| MD5 |
4aa51681343fe2797a6223a76088d5df
|
|
| BLAKE2b-256 |
e4b2f268761544220482721e942fb4406746479e8a1cee4bc28f971de26d0277
|