pysnmp-sync-adapter 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pysnmp_sync_adapter-1.0.0/LICENCE.txt +1 -0
- pysnmp_sync_adapter-1.0.0/PKG-INFO +190 -0
- pysnmp_sync_adapter-1.0.0/README.md +1 -8
- pysnmp_sync_adapter-1.0.0/pyproject.toml +24 -0
- pysnmp_sync_adapter-1.0.0/pysnmp_sync_adapter/__init__.py +4 -0
- pysnmp_sync_adapter-1.0.0/pysnmp_sync_adapter/sync_adapters.py +63 -0
- pysnmp_sync_adapter-1.0.0/pysnmp_sync_adapter.egg-info/PKG-INFO +190 -0
- pysnmp_sync_adapter-1.0.0/pysnmp_sync_adapter.egg-info/SOURCES.txt +11 -0
- pysnmp_sync_adapter-1.0.0/pysnmp_sync_adapter.egg-info/dependency_links.txt +1 -0
- pysnmp_sync_adapter-1.0.0/pysnmp_sync_adapter.egg-info/requires.txt +1 -0
- pysnmp_sync_adapter-1.0.0/pysnmp_sync_adapter.egg-info/top_level.txt +1 -0
- pysnmp_sync_adapter-1.0.0/setup.cfg +4 -0
- pysnmp_sync_adapter-1.0.0/tests/test_pysnmp_sync.py +1 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysnmp_sync_adapter
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Synchronous wrapper adapters for pysnmp v1arch asyncio HLAPI
|
|
5
|
+
Author: Ircama
|
|
6
|
+
License-Expression: EUPL-1.2
|
|
7
|
+
Project-URL: Homepage, https://github.com/Ircama/pysnmp_sync_adapter
|
|
8
|
+
Project-URL: Repository, https://github.com/Ircama/pysnmp_sync_adapter
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENCE.txt
|
|
12
|
+
Requires-Dist: pysnmp>=5.0.0
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# pysnmp_sync_adapter
|
|
16
|
+
|
|
17
|
+
[](https://pypi.org/project/pysnmp_sync_adapter/)
|
|
18
|
+
[](https://pypi.python.org/pypi/pysnmp_sync_adapter/)
|
|
19
|
+
|
|
20
|
+
**Lightweight Synchronous Adapter for PySNMP v1arch AsyncIO HLAPI**
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
This package provides lightweight, blocking wrappers around `pysnmp.hlapi.v1arch.asyncio`, enabling synchronous use of the SNMPv1 high-level API without requiring direct `asyncio` management.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
* Drop-in synchronous alternatives: `get_cmd_sync`, `walk_cmd_sync`, `set_cmd_sync`, and others.
|
|
29
|
+
* Reuses or creates a single shared event loop for efficiency.
|
|
30
|
+
* Pre-creates and reuses `UdpTransportTarget` to minimize connection overhead.
|
|
31
|
+
|
|
32
|
+
These adapters allow to call the familiar HLAPI functions in a purely synchronous style (e.g. in scripts, GUIs like Tkinter, or blocking contexts) without having to manage `asyncio` directly.
|
|
33
|
+
|
|
34
|
+
This restores the synchronous experience familiar from earlier [PySNMP](https://github.com/lextudio/pysnmp) versions. Native sync HLAPI wrappers were [deprecated](https://github.com/lextudio/pysnmp/issues/104) in recent releases in favor of `asyncio`.
|
|
35
|
+
|
|
36
|
+
### Provided Methods
|
|
37
|
+
|
|
38
|
+
| Synchronous Function | AsyncIO Equivalent |
|
|
39
|
+
| -------------------- | --------------------------- |
|
|
40
|
+
| `get_cmd_sync` | `get_cmd` |
|
|
41
|
+
| `next_cmd_sync` | `next_cmd` |
|
|
42
|
+
| `set_cmd_sync` | `set_cmd` |
|
|
43
|
+
| `bulk_cmd_sync` | `bulk_cmd` |
|
|
44
|
+
| `walk_cmd_sync` | `walk_cmd` (async-gen) |
|
|
45
|
+
| `bulk_walk_cmd_sync` | `bulk_walk_cmd` (async-gen) |
|
|
46
|
+
|
|
47
|
+
### Internal Utilities
|
|
48
|
+
|
|
49
|
+
* `ensure_loop()` — retrieves or creates the event loop, ensuring one loop per process
|
|
50
|
+
* `create_transport()` — pre-awaits `UdpTransportTarget.create()` once per host/port
|
|
51
|
+
* `_sync_coro()` — runs a coroutine to completion synchronously
|
|
52
|
+
* `_sync_agen()` — consumes async generators (e.g., for walk operations) into a list
|
|
53
|
+
* `make_sync()` — decorator to convert HLAPI coroutines into sync functions
|
|
54
|
+
|
|
55
|
+
By avoiding per-call event loop instantiation and by reusing transport targets, this implementation significantly reduces runtime overhead in tight polling or query loops.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install pysnmp_sync_adapter
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from pysnmp_sync_adapter import (
|
|
69
|
+
get_cmd_sync, next_cmd_sync, set_cmd_sync, bulk_cmd_sync,
|
|
70
|
+
walk_cmd_sync, bulk_walk_cmd_sync, create_transport
|
|
71
|
+
)
|
|
72
|
+
from pysnmp.hlapi.v1arch.asyncio import SnmpDispatcher, CommunityData, ObjectType, ObjectIdentity
|
|
73
|
+
|
|
74
|
+
dispatcher = SnmpDispatcher()
|
|
75
|
+
transport = create_transport('demo.pysnmp.com', 161)
|
|
76
|
+
|
|
77
|
+
err, status, index, var_binds = get_cmd_sync(
|
|
78
|
+
dispatcher,
|
|
79
|
+
CommunityData('public', mpModel=0),
|
|
80
|
+
transport,
|
|
81
|
+
ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'))
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
for name, val in var_binds:
|
|
85
|
+
print(f'{name} = {val}')
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Usage
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import asyncio
|
|
92
|
+
import platform
|
|
93
|
+
from pysnmp.hlapi.v1arch.asyncio import *
|
|
94
|
+
from pysnmp_sync_adapter import (
|
|
95
|
+
get_cmd_sync, next_cmd_sync, set_cmd_sync, bulk_cmd_sync,
|
|
96
|
+
walk_cmd_sync, bulk_walk_cmd_sync, create_transport
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if platform.system()=='Windows':
|
|
100
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
101
|
+
|
|
102
|
+
community = 'public'
|
|
103
|
+
dispatcher = SnmpDispatcher()
|
|
104
|
+
auth_data = CommunityData(community, mpModel=0)
|
|
105
|
+
|
|
106
|
+
print("\n--> get_cmd_sync")
|
|
107
|
+
error_indication, error_status, error_index, var_binds = get_cmd_sync(
|
|
108
|
+
dispatcher,
|
|
109
|
+
auth_data,
|
|
110
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
111
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
|
|
112
|
+
)
|
|
113
|
+
print(error_indication, error_status, error_index)
|
|
114
|
+
for name, val in var_binds:
|
|
115
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
116
|
+
|
|
117
|
+
print("\n--> set_cmd_sync")
|
|
118
|
+
error_indication, error_status, error_index, var_binds = set_cmd_sync(
|
|
119
|
+
dispatcher,
|
|
120
|
+
auth_data,
|
|
121
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
122
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386')
|
|
123
|
+
)
|
|
124
|
+
print(error_indication, error_status, error_index)
|
|
125
|
+
for name, val in var_binds:
|
|
126
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
127
|
+
|
|
128
|
+
print("\n--> next_cmd_sync")
|
|
129
|
+
error_indication, error_status, error_index, var_binds = next_cmd_sync(
|
|
130
|
+
dispatcher,
|
|
131
|
+
auth_data,
|
|
132
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
133
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
134
|
+
)
|
|
135
|
+
print(error_indication, error_status, error_index)
|
|
136
|
+
for name, val in var_binds:
|
|
137
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
138
|
+
|
|
139
|
+
print("\n--> bulk_cmd_sync")
|
|
140
|
+
error_indication, error_status, error_index, var_binds = bulk_cmd_sync(
|
|
141
|
+
dispatcher,
|
|
142
|
+
CommunityData('public'),
|
|
143
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
144
|
+
0, 2,
|
|
145
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
146
|
+
)
|
|
147
|
+
print(error_indication, error_status, error_index)
|
|
148
|
+
for name, val in var_binds:
|
|
149
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
150
|
+
|
|
151
|
+
print("\n--> walk_cmd_sync")
|
|
152
|
+
objects = walk_cmd_sync(
|
|
153
|
+
dispatcher,
|
|
154
|
+
auth_data,
|
|
155
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
156
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))
|
|
157
|
+
)
|
|
158
|
+
for error_indication, error_status, error_index, var_binds in objects:
|
|
159
|
+
for name, val in var_binds:
|
|
160
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
161
|
+
|
|
162
|
+
print("\n--> bulk_walk_cmd_sync")
|
|
163
|
+
objects = bulk_walk_cmd_sync(
|
|
164
|
+
dispatcher,
|
|
165
|
+
CommunityData('public'),
|
|
166
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
167
|
+
0, 25,
|
|
168
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr')))
|
|
169
|
+
for error_indication, error_status, error_index, var_binds in objects:
|
|
170
|
+
for name, val in var_binds:
|
|
171
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Limitations
|
|
175
|
+
|
|
176
|
+
- These adapters block the calling thread until the SNMP operation completes.
|
|
177
|
+
- If the host app already drives an asyncio loop, calling these wrappers on that same loop can error or deadlock unless isolated (e.g. in a separate thread).
|
|
178
|
+
- The only way those calls give up on a slow or unresponsive SNMP peer is via the low-level socket own timeout; there’s no exposed mechanism to cancel the underlying asyncio task.
|
|
179
|
+
|
|
180
|
+
## Contributing
|
|
181
|
+
|
|
182
|
+
Contributions are welcome! Please follow standard guidelines:
|
|
183
|
+
|
|
184
|
+
- Fork the repository
|
|
185
|
+
- Create a feature branch
|
|
186
|
+
- Submit a Pull Request
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
EUPL-1.2 License - See [LICENSE](LICENSE.txt) for details.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# pysnmp_sync_adapter
|
|
-
--
|
|
|
1
|
-
--
|
|
2
2
|
get_cmd_sync, next_cmd_sync, set_cmd_sync, bulk_cmd_sync,
|
|
3
3
|
walk_cmd_sync, bulk_walk_cmd_sync, create_transport
|
|
4
4
|
dispatcher,
|
|
5
5
|
CommunityData('public', mpModel=0),
|
|
6
6
|
transport,
|
|
7
7
|
ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'))
|
|
8
8
|
print(f'{name} = {val}')
|
|
9
9
|
get_cmd_sync, next_cmd_sync, set_cmd_sync, bulk_cmd_sync,
|
|
10
10
|
walk_cmd_sync, bulk_walk_cmd_sync, create_transport
|
|
11
11
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
12
12
|
dispatcher,
|
|
13
13
|
auth_data,
|
|
14
14
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
15
15
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
|
|
16
16
|
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
17
17
|
dispatcher,
|
|
18
18
|
auth_data,
|
|
19
19
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
20
20
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386')
|
|
21
21
|
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
22
22
|
dispatcher,
|
|
23
23
|
auth_data,
|
|
24
24
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
25
25
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
26
26
|
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
27
27
|
dispatcher,
|
|
28
28
|
CommunityData('public'),
|
|
29
29
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
30
30
|
0, 2,
|
|
31
31
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
32
32
|
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
33
33
|
dispatcher,
|
|
34
34
|
auth_data,
|
|
35
35
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
36
36
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))
|
|
37
37
|
for name, val in var_binds:
|
|
38
38
|
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
39
39
|
dispatcher,
|
|
40
40
|
CommunityData('public'),
|
|
41
41
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
42
42
|
0, 25,
|
|
43
43
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr')))
|
|
44
44
|
for name, val in var_binds:
|
|
45
45
|
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
46
|
-
These adapters block the calling thread until the SNMP operation completes.
|
|
47
|
-
If the host app already drives an asyncio loop, calling these wrappers on that same loop can error or deadlock unless isolated (e.g. in a separate thread).
|
|
48
|
-
The only way those calls give up on a slow or unresponsive SNMP peer is via the low-level socket own timeout; there’s no exposed mechanism to cancel the underlying asyncio task.
|
|
49
|
-
Fork the repository
|
|
50
|
-
Create a feature branch
|
|
51
|
-
Submit a Pull Request
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pysnmp_sync_adapter"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Synchronous wrapper adapters for pysnmp v1arch asyncio HLAPI"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name="Ircama" }
|
|
7
|
+
]
|
|
8
|
+
license = "EUPL-1.2"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pysnmp>=5.0.0"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
"Homepage" = "https://github.com/Ircama/pysnmp_sync_adapter"
|
|
17
|
+
"Repository" = "https://github.com/Ircama/pysnmp_sync_adapter"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["setuptools>=67.0", "wheel"]
|
|
21
|
+
build-backend = "setuptools.build_meta"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools]
|
|
24
|
+
packages = ["pysnmp_sync_adapter"]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import functools
|
|
3
|
+
from pysnmp.hlapi.v1arch.asyncio import *
|
|
4
|
+
|
|
5
|
+
def ensure_loop():
|
|
6
|
+
try:
|
|
7
|
+
loop = asyncio.get_event_loop()
|
|
8
|
+
except RuntimeError:
|
|
9
|
+
loop = asyncio.new_event_loop()
|
|
10
|
+
asyncio.set_event_loop(loop)
|
|
11
|
+
return loop
|
|
12
|
+
|
|
13
|
+
def create_transport(host: str, port: int, timeout: float = 1.0):
|
|
14
|
+
"""
|
|
15
|
+
Await the async factory to build UdpTransportTarget once,
|
|
16
|
+
using our shared loop.
|
|
17
|
+
"""
|
|
18
|
+
loop = ensure_loop()
|
|
19
|
+
coro = UdpTransportTarget.create((host, port), timeout=timeout)
|
|
20
|
+
return loop.run_until_complete(coro)
|
|
21
|
+
|
|
22
|
+
def _sync_coro(coro):
|
|
23
|
+
"""
|
|
24
|
+
Run the given coroutine to completion on the shared loop,
|
|
25
|
+
scheduling if needed.
|
|
26
|
+
"""
|
|
27
|
+
loop = ensure_loop()
|
|
28
|
+
if loop.is_running():
|
|
29
|
+
fut = asyncio.ensure_future(coro)
|
|
30
|
+
return loop.run_until_complete(fut)
|
|
31
|
+
return loop.run_until_complete(coro)
|
|
32
|
+
|
|
33
|
+
def _sync_agen(agen):
|
|
34
|
+
"""
|
|
35
|
+
Consume an async-generator into a list synchronously.
|
|
36
|
+
"""
|
|
37
|
+
async def _collector():
|
|
38
|
+
items = []
|
|
39
|
+
async for item in agen:
|
|
40
|
+
items.append(item)
|
|
41
|
+
return items
|
|
42
|
+
|
|
43
|
+
return _sync_coro(_collector())
|
|
44
|
+
|
|
45
|
+
def make_sync(fn):
|
|
46
|
+
"""Turn any pysnmp async‐HLAPI fn into a sync wrapper."""
|
|
47
|
+
@functools.wraps(fn)
|
|
48
|
+
def wrapper(*args, **kwargs):
|
|
49
|
+
return _sync_coro(fn(*args, **kwargs))
|
|
50
|
+
return wrapper
|
|
51
|
+
|
|
52
|
+
get_cmd_sync = make_sync(get_cmd)
|
|
53
|
+
next_cmd_sync = make_sync(next_cmd)
|
|
54
|
+
set_cmd_sync = make_sync(set_cmd)
|
|
55
|
+
bulk_cmd_sync = make_sync(bulk_cmd)
|
|
56
|
+
|
|
57
|
+
def walk_cmd_sync(*args, **kwargs):
|
|
58
|
+
"""Sync wrapper for walk_cmd (async generator)."""
|
|
59
|
+
return _sync_agen(walk_cmd(*args, **kwargs))
|
|
60
|
+
|
|
61
|
+
def bulk_walk_cmd_sync(*args, **kwargs):
|
|
62
|
+
"""Sync wrapper for bulk_walk_cmd (async generator)."""
|
|
63
|
+
return _sync_agen(bulk_walk_cmd(*args, **kwargs))
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysnmp_sync_adapter
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Synchronous wrapper adapters for pysnmp v1arch asyncio HLAPI
|
|
5
|
+
Author: Ircama
|
|
6
|
+
License-Expression: EUPL-1.2
|
|
7
|
+
Project-URL: Homepage, https://github.com/Ircama/pysnmp_sync_adapter
|
|
8
|
+
Project-URL: Repository, https://github.com/Ircama/pysnmp_sync_adapter
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENCE.txt
|
|
12
|
+
Requires-Dist: pysnmp>=5.0.0
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# pysnmp_sync_adapter
|
|
16
|
+
|
|
17
|
+
[](https://pypi.org/project/pysnmp_sync_adapter/)
|
|
18
|
+
[](https://pypi.python.org/pypi/pysnmp_sync_adapter/)
|
|
19
|
+
|
|
20
|
+
**Lightweight Synchronous Adapter for PySNMP v1arch AsyncIO HLAPI**
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
This package provides lightweight, blocking wrappers around `pysnmp.hlapi.v1arch.asyncio`, enabling synchronous use of the SNMPv1 high-level API without requiring direct `asyncio` management.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
* Drop-in synchronous alternatives: `get_cmd_sync`, `walk_cmd_sync`, `set_cmd_sync`, and others.
|
|
29
|
+
* Reuses or creates a single shared event loop for efficiency.
|
|
30
|
+
* Pre-creates and reuses `UdpTransportTarget` to minimize connection overhead.
|
|
31
|
+
|
|
32
|
+
These adapters allow to call the familiar HLAPI functions in a purely synchronous style (e.g. in scripts, GUIs like Tkinter, or blocking contexts) without having to manage `asyncio` directly.
|
|
33
|
+
|
|
34
|
+
This restores the synchronous experience familiar from earlier [PySNMP](https://github.com/lextudio/pysnmp) versions. Native sync HLAPI wrappers were [deprecated](https://github.com/lextudio/pysnmp/issues/104) in recent releases in favor of `asyncio`.
|
|
35
|
+
|
|
36
|
+
### Provided Methods
|
|
37
|
+
|
|
38
|
+
| Synchronous Function | AsyncIO Equivalent |
|
|
39
|
+
| -------------------- | --------------------------- |
|
|
40
|
+
| `get_cmd_sync` | `get_cmd` |
|
|
41
|
+
| `next_cmd_sync` | `next_cmd` |
|
|
42
|
+
| `set_cmd_sync` | `set_cmd` |
|
|
43
|
+
| `bulk_cmd_sync` | `bulk_cmd` |
|
|
44
|
+
| `walk_cmd_sync` | `walk_cmd` (async-gen) |
|
|
45
|
+
| `bulk_walk_cmd_sync` | `bulk_walk_cmd` (async-gen) |
|
|
46
|
+
|
|
47
|
+
### Internal Utilities
|
|
48
|
+
|
|
49
|
+
* `ensure_loop()` — retrieves or creates the event loop, ensuring one loop per process
|
|
50
|
+
* `create_transport()` — pre-awaits `UdpTransportTarget.create()` once per host/port
|
|
51
|
+
* `_sync_coro()` — runs a coroutine to completion synchronously
|
|
52
|
+
* `_sync_agen()` — consumes async generators (e.g., for walk operations) into a list
|
|
53
|
+
* `make_sync()` — decorator to convert HLAPI coroutines into sync functions
|
|
54
|
+
|
|
55
|
+
By avoiding per-call event loop instantiation and by reusing transport targets, this implementation significantly reduces runtime overhead in tight polling or query loops.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install pysnmp_sync_adapter
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from pysnmp_sync_adapter import (
|
|
69
|
+
get_cmd_sync, next_cmd_sync, set_cmd_sync, bulk_cmd_sync,
|
|
70
|
+
walk_cmd_sync, bulk_walk_cmd_sync, create_transport
|
|
71
|
+
)
|
|
72
|
+
from pysnmp.hlapi.v1arch.asyncio import SnmpDispatcher, CommunityData, ObjectType, ObjectIdentity
|
|
73
|
+
|
|
74
|
+
dispatcher = SnmpDispatcher()
|
|
75
|
+
transport = create_transport('demo.pysnmp.com', 161)
|
|
76
|
+
|
|
77
|
+
err, status, index, var_binds = get_cmd_sync(
|
|
78
|
+
dispatcher,
|
|
79
|
+
CommunityData('public', mpModel=0),
|
|
80
|
+
transport,
|
|
81
|
+
ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'))
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
for name, val in var_binds:
|
|
85
|
+
print(f'{name} = {val}')
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Usage
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import asyncio
|
|
92
|
+
import platform
|
|
93
|
+
from pysnmp.hlapi.v1arch.asyncio import *
|
|
94
|
+
from pysnmp_sync_adapter import (
|
|
95
|
+
get_cmd_sync, next_cmd_sync, set_cmd_sync, bulk_cmd_sync,
|
|
96
|
+
walk_cmd_sync, bulk_walk_cmd_sync, create_transport
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if platform.system()=='Windows':
|
|
100
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
101
|
+
|
|
102
|
+
community = 'public'
|
|
103
|
+
dispatcher = SnmpDispatcher()
|
|
104
|
+
auth_data = CommunityData(community, mpModel=0)
|
|
105
|
+
|
|
106
|
+
print("\n--> get_cmd_sync")
|
|
107
|
+
error_indication, error_status, error_index, var_binds = get_cmd_sync(
|
|
108
|
+
dispatcher,
|
|
109
|
+
auth_data,
|
|
110
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
111
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
|
|
112
|
+
)
|
|
113
|
+
print(error_indication, error_status, error_index)
|
|
114
|
+
for name, val in var_binds:
|
|
115
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
116
|
+
|
|
117
|
+
print("\n--> set_cmd_sync")
|
|
118
|
+
error_indication, error_status, error_index, var_binds = set_cmd_sync(
|
|
119
|
+
dispatcher,
|
|
120
|
+
auth_data,
|
|
121
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
122
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386')
|
|
123
|
+
)
|
|
124
|
+
print(error_indication, error_status, error_index)
|
|
125
|
+
for name, val in var_binds:
|
|
126
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
127
|
+
|
|
128
|
+
print("\n--> next_cmd_sync")
|
|
129
|
+
error_indication, error_status, error_index, var_binds = next_cmd_sync(
|
|
130
|
+
dispatcher,
|
|
131
|
+
auth_data,
|
|
132
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
133
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
134
|
+
)
|
|
135
|
+
print(error_indication, error_status, error_index)
|
|
136
|
+
for name, val in var_binds:
|
|
137
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
138
|
+
|
|
139
|
+
print("\n--> bulk_cmd_sync")
|
|
140
|
+
error_indication, error_status, error_index, var_binds = bulk_cmd_sync(
|
|
141
|
+
dispatcher,
|
|
142
|
+
CommunityData('public'),
|
|
143
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
144
|
+
0, 2,
|
|
145
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
146
|
+
)
|
|
147
|
+
print(error_indication, error_status, error_index)
|
|
148
|
+
for name, val in var_binds:
|
|
149
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
150
|
+
|
|
151
|
+
print("\n--> walk_cmd_sync")
|
|
152
|
+
objects = walk_cmd_sync(
|
|
153
|
+
dispatcher,
|
|
154
|
+
auth_data,
|
|
155
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
156
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))
|
|
157
|
+
)
|
|
158
|
+
for error_indication, error_status, error_index, var_binds in objects:
|
|
159
|
+
for name, val in var_binds:
|
|
160
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
161
|
+
|
|
162
|
+
print("\n--> bulk_walk_cmd_sync")
|
|
163
|
+
objects = bulk_walk_cmd_sync(
|
|
164
|
+
dispatcher,
|
|
165
|
+
CommunityData('public'),
|
|
166
|
+
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
167
|
+
0, 25,
|
|
168
|
+
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr')))
|
|
169
|
+
for error_indication, error_status, error_index, var_binds in objects:
|
|
170
|
+
for name, val in var_binds:
|
|
171
|
+
print(name.prettyPrint(), '=', val.prettyPrint())
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Limitations
|
|
175
|
+
|
|
176
|
+
- These adapters block the calling thread until the SNMP operation completes.
|
|
177
|
+
- If the host app already drives an asyncio loop, calling these wrappers on that same loop can error or deadlock unless isolated (e.g. in a separate thread).
|
|
178
|
+
- The only way those calls give up on a slow or unresponsive SNMP peer is via the low-level socket own timeout; there’s no exposed mechanism to cancel the underlying asyncio task.
|
|
179
|
+
|
|
180
|
+
## Contributing
|
|
181
|
+
|
|
182
|
+
Contributions are welcome! Please follow standard guidelines:
|
|
183
|
+
|
|
184
|
+
- Fork the repository
|
|
185
|
+
- Create a feature branch
|
|
186
|
+
- Submit a Pull Request
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
EUPL-1.2 License - See [LICENSE](LICENSE.txt) for details.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENCE.txt
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
pysnmp_sync_adapter/__init__.py
|
|
5
|
+
pysnmp_sync_adapter/sync_adapters.py
|
|
6
|
+
pysnmp_sync_adapter.egg-info/PKG-INFO
|
|
7
|
+
pysnmp_sync_adapter.egg-info/SOURCES.txt
|
|
8
|
+
pysnmp_sync_adapter.egg-info/dependency_links.txt
|
|
9
|
+
pysnmp_sync_adapter.egg-info/requires.txt
|
|
10
|
+
pysnmp_sync_adapter.egg-info/top_level.txt
|
|
11
|
+
tests/test_pysnmp_sync.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pysnmp>=5.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pysnmp_sync_adapter
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
get_cmd_sync, next_cmd_sync, set_cmd_sync, bulk_cmd_sync,
|
|
1
3
|
walk_cmd_sync, bulk_walk_cmd_sync, create_transport
|
|
2
4
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
3
5
|
err, status, index, binds = get_cmd_sync(
|
|
4
6
|
dispatcher,
|
|
5
7
|
auth_data,
|
|
6
8
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
7
9
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
|
|
8
10
|
)
|
|
9
11
|
assert err is None
|
|
10
12
|
assert not status
|
|
11
13
|
assert binds
|
|
12
14
|
assert len(binds) == 1
|
|
13
15
|
assert len(list(binds[0])) == 2
|
|
14
16
|
name, value = binds[0]
|
|
15
17
|
assert name.prettyPrint() == "SNMPv2-MIB::sysDescr.0"
|
|
16
18
|
assert value.prettyPrint() == "#SNMP Agent on .NET Standard"
|
|
17
19
|
err, status, index, binds = set_cmd_sync(
|
|
18
20
|
dispatcher,
|
|
19
21
|
auth_data,
|
|
20
22
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
21
23
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
|
|
22
24
|
)
|
|
23
25
|
assert err is None
|
|
24
26
|
assert str(status) == 'noSuchName'
|
|
25
27
|
assert binds
|
|
26
28
|
assert len(binds) == 1
|
|
27
29
|
assert len(list(binds[0])) == 2
|
|
28
30
|
name, value = binds[0]
|
|
29
31
|
assert name.prettyPrint() == "SNMPv2-MIB::sysDescr.0"
|
|
30
32
|
assert value.prettyPrint() == ""
|
|
31
33
|
err, status, index, binds = next_cmd_sync(
|
|
32
34
|
dispatcher,
|
|
33
35
|
auth_data,
|
|
34
36
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
35
37
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
|
|
36
38
|
)
|
|
37
39
|
assert err is None
|
|
38
40
|
assert str(status) == 'noError'
|
|
39
41
|
assert binds
|
|
40
42
|
assert len(binds) == 1
|
|
41
43
|
assert len(list(binds[0])) == 2
|
|
42
44
|
name, value = binds[0]
|
|
43
45
|
assert name.prettyPrint() == "SNMPv2-MIB::sysObjectID.0"
|
|
44
46
|
assert value.prettyPrint() == "SNMPv2-SMI::internet"
|
|
45
47
|
err, status, index, binds = bulk_cmd_sync(
|
|
46
48
|
dispatcher,
|
|
47
49
|
CommunityData(community),
|
|
48
50
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
49
51
|
0, 2,
|
|
50
52
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
51
53
|
)
|
|
52
54
|
assert err is None
|
|
53
55
|
assert str(status) == 'noError'
|
|
54
56
|
assert binds
|
|
55
57
|
assert len(binds) == 2
|
|
56
58
|
assert len(list(binds[0])) == 2
|
|
57
59
|
assert len(list(binds[1])) == 2
|
|
58
60
|
name, value = binds[0]
|
|
59
61
|
assert name.prettyPrint() == "SNMPv2-MIB::sysDescr.0"
|
|
60
62
|
assert value.prettyPrint() == "#SNMP Agent on .NET Standard"
|
|
61
63
|
name, value = binds[1]
|
|
62
64
|
assert name.prettyPrint() == "SNMPv2-MIB::sysObjectID.0"
|
|
63
65
|
assert value.prettyPrint() == "SNMPv2-SMI::internet"
|
|
64
66
|
objects = walk_cmd_sync(
|
|
65
67
|
dispatcher,
|
|
66
68
|
auth_data,
|
|
67
69
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
68
70
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
69
71
|
)
|
|
70
72
|
assert len(objects) == 69
|
|
71
73
|
err, status, index, binds = objects[0]
|
|
72
74
|
assert str(status) == 'noError'
|
|
73
75
|
assert binds
|
|
74
76
|
assert len(binds) == 1
|
|
75
77
|
assert len(list(binds[0])) == 2
|
|
76
78
|
name, value = binds[0]
|
|
77
79
|
assert name.prettyPrint() == "SNMPv2-MIB::sysDescr.0"
|
|
78
80
|
assert value.prettyPrint() == "#SNMP Agent on .NET Standard"
|
|
79
81
|
objects = bulk_walk_cmd_sync(
|
|
80
82
|
dispatcher,
|
|
81
83
|
CommunityData(community),
|
|
82
84
|
create_transport('demo.pysnmp.com', 161, timeout=2),
|
|
83
85
|
0, 25,
|
|
84
86
|
ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))
|
|
85
87
|
)
|
|
86
88
|
assert len(objects) == 3
|
|
87
89
|
err, status, index, binds = objects[0]
|
|
88
90
|
assert str(status) == 'noError'
|
|
89
91
|
assert binds
|
|
90
92
|
assert len(binds) == 25
|
|
91
93
|
assert len(list(binds[0])) == 2
|
|
92
94
|
name, value = binds[0]
|
|
93
95
|
assert name.prettyPrint() == "SNMPv2-MIB::sysDescr.0"
|
|
94
96
|
assert value.prettyPrint() == "#SNMP Agent on .NET Standard"
|