ohmqtt 0.0.3__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.
Files changed (81) hide show
  1. ohmqtt-0.0.3/LICENSE +7 -0
  2. ohmqtt-0.0.3/MANIFEST.in +10 -0
  3. ohmqtt-0.0.3/PKG-INFO +103 -0
  4. ohmqtt-0.0.3/README.md +76 -0
  5. ohmqtt-0.0.3/examples/__init__.py +0 -0
  6. ohmqtt-0.0.3/examples/args.py +10 -0
  7. ohmqtt-0.0.3/examples/publish.py +34 -0
  8. ohmqtt-0.0.3/examples/publish_retain.py +41 -0
  9. ohmqtt-0.0.3/examples/publish_wait_for_ack.py +32 -0
  10. ohmqtt-0.0.3/examples/rpc_caller.py +92 -0
  11. ohmqtt-0.0.3/examples/rpc_server.py +62 -0
  12. ohmqtt-0.0.3/noxfile.py +19 -0
  13. ohmqtt-0.0.3/ohmqtt/__init__.py +0 -0
  14. ohmqtt-0.0.3/ohmqtt/client.py +266 -0
  15. ohmqtt-0.0.3/ohmqtt/connection/__init__.py +117 -0
  16. ohmqtt-0.0.3/ohmqtt/connection/address.py +91 -0
  17. ohmqtt-0.0.3/ohmqtt/connection/decoder.py +132 -0
  18. ohmqtt-0.0.3/ohmqtt/connection/fsm.py +179 -0
  19. ohmqtt-0.0.3/ohmqtt/connection/handlers.py +86 -0
  20. ohmqtt-0.0.3/ohmqtt/connection/keepalive.py +139 -0
  21. ohmqtt-0.0.3/ohmqtt/connection/selector.py +113 -0
  22. ohmqtt-0.0.3/ohmqtt/connection/states.py +468 -0
  23. ohmqtt-0.0.3/ohmqtt/connection/timeout.py +41 -0
  24. ohmqtt-0.0.3/ohmqtt/connection/types.py +98 -0
  25. ohmqtt-0.0.3/ohmqtt/error.py +10 -0
  26. ohmqtt-0.0.3/ohmqtt/logger.py +14 -0
  27. ohmqtt-0.0.3/ohmqtt/mqtt_spec.py +110 -0
  28. ohmqtt-0.0.3/ohmqtt/packet/__init__.py +78 -0
  29. ohmqtt-0.0.3/ohmqtt/packet/auth.py +57 -0
  30. ohmqtt-0.0.3/ohmqtt/packet/base.py +29 -0
  31. ohmqtt-0.0.3/ohmqtt/packet/connect.py +252 -0
  32. ohmqtt-0.0.3/ohmqtt/packet/ping.py +53 -0
  33. ohmqtt-0.0.3/ohmqtt/packet/publish.py +253 -0
  34. ohmqtt-0.0.3/ohmqtt/packet/subscribe.py +200 -0
  35. ohmqtt-0.0.3/ohmqtt/persistence/__init__.py +0 -0
  36. ohmqtt-0.0.3/ohmqtt/persistence/base.py +118 -0
  37. ohmqtt-0.0.3/ohmqtt/persistence/in_memory.py +142 -0
  38. ohmqtt-0.0.3/ohmqtt/persistence/sqlite.py +275 -0
  39. ohmqtt-0.0.3/ohmqtt/platform.py +11 -0
  40. ohmqtt-0.0.3/ohmqtt/property.py +329 -0
  41. ohmqtt-0.0.3/ohmqtt/protected.py +53 -0
  42. ohmqtt-0.0.3/ohmqtt/py.typed +0 -0
  43. ohmqtt-0.0.3/ohmqtt/serialization.py +181 -0
  44. ohmqtt-0.0.3/ohmqtt/session.py +224 -0
  45. ohmqtt-0.0.3/ohmqtt/subscriptions.py +429 -0
  46. ohmqtt-0.0.3/ohmqtt/topic_alias.py +135 -0
  47. ohmqtt-0.0.3/ohmqtt/topic_filter.py +91 -0
  48. ohmqtt-0.0.3/ohmqtt.egg-info/PKG-INFO +103 -0
  49. ohmqtt-0.0.3/ohmqtt.egg-info/SOURCES.txt +79 -0
  50. ohmqtt-0.0.3/ohmqtt.egg-info/dependency_links.txt +1 -0
  51. ohmqtt-0.0.3/ohmqtt.egg-info/requires.txt +3 -0
  52. ohmqtt-0.0.3/ohmqtt.egg-info/top_level.txt +1 -0
  53. ohmqtt-0.0.3/pyproject.toml +117 -0
  54. ohmqtt-0.0.3/setup.cfg +4 -0
  55. ohmqtt-0.0.3/tests/__init__.py +0 -0
  56. ohmqtt-0.0.3/tests/conftest.py +156 -0
  57. ohmqtt-0.0.3/tests/data/test_address.yml +283 -0
  58. ohmqtt-0.0.3/tests/data/test_packet.yml +441 -0
  59. ohmqtt-0.0.3/tests/data/test_serialization.yml +191 -0
  60. ohmqtt-0.0.3/tests/test_address.py +76 -0
  61. ohmqtt-0.0.3/tests/test_client.py +258 -0
  62. ohmqtt-0.0.3/tests/test_connection.py +180 -0
  63. ohmqtt-0.0.3/tests/test_decoder.py +129 -0
  64. ohmqtt-0.0.3/tests/test_error.py +9 -0
  65. ohmqtt-0.0.3/tests/test_fsm.py +280 -0
  66. ohmqtt-0.0.3/tests/test_keepalive.py +106 -0
  67. ohmqtt-0.0.3/tests/test_packet.py +285 -0
  68. ohmqtt-0.0.3/tests/test_persistence_handles.py +38 -0
  69. ohmqtt-0.0.3/tests/test_persistence_sqlite.py +234 -0
  70. ohmqtt-0.0.3/tests/test_property.py +18 -0
  71. ohmqtt-0.0.3/tests/test_protected.py +51 -0
  72. ohmqtt-0.0.3/tests/test_selector.py +82 -0
  73. ohmqtt-0.0.3/tests/test_serialization.py +178 -0
  74. ohmqtt-0.0.3/tests/test_session.py +140 -0
  75. ohmqtt-0.0.3/tests/test_states.py +1165 -0
  76. ohmqtt-0.0.3/tests/test_subscriptions.py +275 -0
  77. ohmqtt-0.0.3/tests/test_timeout.py +47 -0
  78. ohmqtt-0.0.3/tests/test_topic_alias.py +108 -0
  79. ohmqtt-0.0.3/tests/test_topic_filter.py +108 -0
  80. ohmqtt-0.0.3/tests/util/__init__.py +0 -0
  81. ohmqtt-0.0.3/tests/util/selfsigned.py +93 -0
ohmqtt-0.0.3/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 Matt Vollrath
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ graft ohmqtt
2
+ graft tests
3
+ graft examples
4
+
5
+ include pyproject.toml
6
+ include noxfile.py
7
+ include LICENSE
8
+ include README.md
9
+
10
+ global-exclude *.pyc
ohmqtt-0.0.3/PKG-INFO ADDED
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: ohmqtt
3
+ Version: 0.0.3
4
+ Summary: Persistent MQTT 5.0 client
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/ohmqtt/ohmqtt_python
7
+ Project-URL: Issues, https://github.com/ohmqtt/ohmqtt_python/issues
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Framework :: Pytest
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Communications
19
+ Classifier: Topic :: Home Automation
20
+ Classifier: Topic :: Internet
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: typing-extensions>=4.12.2; python_version < "3.11"
26
+ Dynamic: license-file
27
+
28
+ # ΩQTT
29
+
30
+ *pron. "ohm cue tee tee" or "omega cutie"*
31
+
32
+ A reliable and persistent MQTT 5.0 client library for Python.
33
+
34
+ ## Features
35
+
36
+ ### QoS and Persistence
37
+
38
+ ΩQTT supports publish and subscribing all QoS levels with optional persistence to disk for QoS >0.
39
+ When not persisting QoS >0 messages, a fast (but volatile) in memory store is used.
40
+ Either way, publishing a message returns a handle with a method to wait for the message to be fully acknowledged by the broker.
41
+
42
+ ### Connectivity
43
+
44
+ Connect to a remote broker with optional TLS over TCP over IPV4 or IPV6.
45
+ Connect to a local broker with either TCP or Unix domain socket.
46
+
47
+ ### Properties
48
+
49
+ Access all optional properties of all MQTT control packet types.
50
+ If you ever wanted to check the user properties of SUBACK and UNSUBACK (among others), welcome home.
51
+
52
+ ### Automatic Topic Alias
53
+
54
+ Set an alias policy when publishing a message and a topic alias will be generated, if allowed by the broker.
55
+ If bandwidth is tight, set your QoS 0 publications to require a topic alias.
56
+ In this case, an error will be raised if the server does not offer enough alias values.
57
+
58
+ ### Toolkit
59
+
60
+ ΩQTT is built on a toolkit for efficiently serializing MQTT control messages.
61
+ Use it to build your own custom implementation, or to serialize your own payloads.
62
+
63
+ ### Portability
64
+
65
+ ΩQTT is tested on Linux, Windows and MacOS with CPython versions 3.10-3.13.
66
+ It should work on any platform that CPython runs on.
67
+
68
+ ### Reliability
69
+
70
+ ΩQTT has been implemented to a high standard of test coverage and static analysis, from the beginning.
71
+ It continues to improve.
72
+
73
+ ### Performance
74
+
75
+ Every drop of pure Python performance has been squeezed out of serialization and the event loop.
76
+ You're not using Python because it's fast, but it can't hurt.
77
+
78
+ ## TODO for 0.1
79
+
80
+ * Auth
81
+ * Instructions
82
+ * Error handling and validation
83
+ * Refactor Session
84
+
85
+ ## TODO for 1.0
86
+
87
+ * E2E Tests
88
+ * Autodoc
89
+ * Publish automation
90
+
91
+ ## Development
92
+
93
+ This project uses `nox` and `uv` to run the tests against all supported Python versions.
94
+
95
+ To do all of this in a venv:
96
+
97
+ ```bash
98
+ python3 -m venv .venv
99
+ source .venv/bin/activate
100
+ pip install nox uv
101
+ nox
102
+ ```
103
+
ohmqtt-0.0.3/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # ΩQTT
2
+
3
+ *pron. "ohm cue tee tee" or "omega cutie"*
4
+
5
+ A reliable and persistent MQTT 5.0 client library for Python.
6
+
7
+ ## Features
8
+
9
+ ### QoS and Persistence
10
+
11
+ ΩQTT supports publish and subscribing all QoS levels with optional persistence to disk for QoS >0.
12
+ When not persisting QoS >0 messages, a fast (but volatile) in memory store is used.
13
+ Either way, publishing a message returns a handle with a method to wait for the message to be fully acknowledged by the broker.
14
+
15
+ ### Connectivity
16
+
17
+ Connect to a remote broker with optional TLS over TCP over IPV4 or IPV6.
18
+ Connect to a local broker with either TCP or Unix domain socket.
19
+
20
+ ### Properties
21
+
22
+ Access all optional properties of all MQTT control packet types.
23
+ If you ever wanted to check the user properties of SUBACK and UNSUBACK (among others), welcome home.
24
+
25
+ ### Automatic Topic Alias
26
+
27
+ Set an alias policy when publishing a message and a topic alias will be generated, if allowed by the broker.
28
+ If bandwidth is tight, set your QoS 0 publications to require a topic alias.
29
+ In this case, an error will be raised if the server does not offer enough alias values.
30
+
31
+ ### Toolkit
32
+
33
+ ΩQTT is built on a toolkit for efficiently serializing MQTT control messages.
34
+ Use it to build your own custom implementation, or to serialize your own payloads.
35
+
36
+ ### Portability
37
+
38
+ ΩQTT is tested on Linux, Windows and MacOS with CPython versions 3.10-3.13.
39
+ It should work on any platform that CPython runs on.
40
+
41
+ ### Reliability
42
+
43
+ ΩQTT has been implemented to a high standard of test coverage and static analysis, from the beginning.
44
+ It continues to improve.
45
+
46
+ ### Performance
47
+
48
+ Every drop of pure Python performance has been squeezed out of serialization and the event loop.
49
+ You're not using Python because it's fast, but it can't hurt.
50
+
51
+ ## TODO for 0.1
52
+
53
+ * Auth
54
+ * Instructions
55
+ * Error handling and validation
56
+ * Refactor Session
57
+
58
+ ## TODO for 1.0
59
+
60
+ * E2E Tests
61
+ * Autodoc
62
+ * Publish automation
63
+
64
+ ## Development
65
+
66
+ This project uses `nox` and `uv` to run the tests against all supported Python versions.
67
+
68
+ To do all of this in a venv:
69
+
70
+ ```bash
71
+ python3 -m venv .venv
72
+ source .venv/bin/activate
73
+ pip install nox uv
74
+ nox
75
+ ```
76
+
File without changes
@@ -0,0 +1,10 @@
1
+ import argparse
2
+
3
+
4
+ parser = argparse.ArgumentParser(description="ohmqtt example")
5
+ parser.add_argument(
6
+ "-a", "--address",
7
+ type=str,
8
+ default="localhost:1883",
9
+ help="MQTT broker address (default: localhost:1883)",
10
+ )
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+ """This example demonstrates connecting to a broker, publishing messages to a topic, and then disconnecting."""
3
+
4
+ import argparse
5
+ import time
6
+
7
+ from ohmqtt.client import Client
8
+
9
+
10
+ def main(args: argparse.Namespace) -> None:
11
+ with Client() as client:
12
+
13
+ print("*** Connecting to broker...")
14
+ client.connect(args.address)
15
+
16
+ client.wait_for_connect(timeout=5.0)
17
+ print("*** Connected to broker")
18
+
19
+ for n in range(1, 9):
20
+ client.publish("ohmqtt/examples/publish", b"test_payload: " + str(n).encode(), qos=0)
21
+ print(f"*** Published message {n}")
22
+ time.sleep(1.0)
23
+
24
+ print("*** Disconnecting from broker...")
25
+ client.disconnect()
26
+
27
+ client.wait_for_disconnect(timeout=5.0)
28
+ print("*** Disconnected from broker")
29
+
30
+
31
+ if __name__ == "__main__":
32
+ from .args import parser
33
+ args = parser.parse_args()
34
+ main(args)
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env python3
2
+ """This example demonstrates publishing a retained message,
3
+ then subscribing to the topic and receiving the retained message."""
4
+
5
+ import argparse
6
+ from queue import Queue
7
+
8
+ from ohmqtt.client import Client
9
+ from ohmqtt.packet import MQTTPublishPacket
10
+
11
+
12
+ def main(args: argparse.Namespace) -> None:
13
+ with Client() as client:
14
+
15
+ client.connect("localhost")
16
+ client.wait_for_connect(timeout=5.0)
17
+ print("*** Connected to broker")
18
+
19
+ pub = client.publish("ohmqtt/examples/publish_retain", b"test_payload", qos=1, retain=True)
20
+ assert pub.wait_for_ack()
21
+
22
+ q: Queue[MQTTPublishPacket] = Queue()
23
+ def callback(_: Client, msg: MQTTPublishPacket) -> None:
24
+ q.put(msg)
25
+ client.subscribe("ohmqtt/examples/publish_retain", callback)
26
+ msg = q.get(timeout=5.0)
27
+ assert msg.topic == "ohmqtt/examples/publish_retain"
28
+ assert msg.payload == b"test_payload"
29
+ assert msg.qos == 1
30
+ assert msg.retain
31
+ print(f"*** Received retained message: {msg!s}")
32
+
33
+ client.disconnect()
34
+ client.wait_for_disconnect(timeout=5.0)
35
+ print("*** Disconnected from broker")
36
+
37
+
38
+ if __name__ == "__main__":
39
+ from .args import parser
40
+ args = parser.parse_args()
41
+ main(args)
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env python3
2
+ """This example demonstrates waiting for a published message to be fully acknowledged by the broker
3
+ before proceeding to send the next message.
4
+
5
+ It also demonstrates the debug logging output of the client.
6
+ """
7
+
8
+ import argparse
9
+ import logging
10
+
11
+ from ohmqtt.client import Client
12
+
13
+
14
+ def main(args: argparse.Namespace) -> None:
15
+ logging.basicConfig(level=logging.DEBUG)
16
+ with Client() as client:
17
+
18
+ client.connect(args.address)
19
+ client.wait_for_connect(timeout=5.0)
20
+
21
+ for n in range(1, 9):
22
+ publish_handle = client.publish("ohmqtt/examples/publish_wait_for_ack", b"test_payload: " + str(n).encode(), qos=2)
23
+ assert publish_handle.wait_for_ack()
24
+
25
+ client.disconnect()
26
+ client.wait_for_disconnect(timeout=5.0)
27
+
28
+
29
+ if __name__ == "__main__":
30
+ from .args import parser
31
+ args = parser.parse_args()
32
+ main(args)
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env python3
2
+ """This example demonstrates a simple RPC caller.
3
+
4
+ It sends an RPC request to a specific topic and waits for the response.
5
+
6
+ The ResponseTopic property is used to specify the topic to which the response should be sent."""
7
+
8
+ import argparse
9
+ import threading
10
+ import uuid
11
+ from typing import Callable
12
+
13
+ from ohmqtt.client import Client
14
+ from ohmqtt.packet import MQTTPublishPacket
15
+ from ohmqtt.property import MQTTPublishProps
16
+
17
+
18
+ RPCCallback = Callable[[bytes], None]
19
+
20
+
21
+ class RPCCaller:
22
+ """A simple RPC caller."""
23
+ def __init__(self, client: Client) -> None:
24
+ self.client = client
25
+ self.callbacks: dict[str, RPCCallback] = {}
26
+
27
+ def send_request(self, payload: bytes, callback: RPCCallback) -> None:
28
+ # Expect the response to be sent to a unique topic.
29
+ unique_id = str(uuid.uuid4())
30
+ response_topic = f"ohmqtt/examples/rpc/response/{unique_id}"
31
+
32
+ # Store the callback for this request.
33
+ self.callbacks[unique_id] = callback
34
+
35
+ # Subscribe to the response topic.
36
+ self.client.subscribe(response_topic, self.handle_response)
37
+
38
+ # Publish the request with the necessary properties.
39
+ self.client.publish(
40
+ "ohmqtt/examples/rpc/request",
41
+ payload,
42
+ qos=2,
43
+ properties=MQTTPublishProps(ResponseTopic=response_topic),
44
+ ).wait_for_ack()
45
+
46
+ print(f"Sent RPC request with response topic: {response_topic}")
47
+
48
+ def handle_response(self, client: Client, msg: MQTTPublishPacket) -> None:
49
+ """Handle incoming RPC responses."""
50
+ try:
51
+ callback = self.callbacks.pop(msg.topic.split("/")[-1])
52
+ except KeyError:
53
+ print("Couldn't find callback for response")
54
+ return
55
+ else:
56
+ # Call the callback with the response payload.
57
+ callback(msg.payload)
58
+ finally:
59
+ # Unsubscribe from the response topic.
60
+ client.unsubscribe(msg.topic, self.handle_response)
61
+
62
+
63
+ def main(args: argparse.Namespace) -> None:
64
+ with Client() as client:
65
+ rpc_caller = RPCCaller(client)
66
+ client.connect(args.address)
67
+ print("*** Waiting for connection...")
68
+ client.wait_for_connect(timeout=5.0)
69
+
70
+ # Set up a callback which helps us block until a response is received.
71
+ recvd = threading.Event()
72
+ def callback(payload: bytes) -> None:
73
+ print(f"*** Received RPC response: {payload.decode()}")
74
+ recvd.set() # Signal that the response has been received.
75
+
76
+ while True:
77
+ recvd.clear() # Reset the event for the next request.
78
+ try:
79
+ payload = input("*** Enter something fun (or 'exit' to quit): ")
80
+ if payload.lower() == "exit":
81
+ break
82
+ rpc_caller.send_request(payload.encode(), callback)
83
+ recvd.wait(timeout=5.0) # Wait for the response to be received.
84
+ except (EOFError, KeyboardInterrupt):
85
+ print("\n*** Shutting down RPC caller...")
86
+ break
87
+
88
+
89
+ if __name__ == "__main__":
90
+ from .args import parser
91
+ args = parser.parse_args()
92
+ main(args)
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+ """This example demonstrates a simple RPC server.
3
+
4
+ It listens for incoming RPC requests on a specific topic and responds with the result of the RPC call.
5
+
6
+ The ResponseTopic property is used by the requestor to specify the topic to which the response should be sent.
7
+
8
+ See the "rpc_caller" example for the request side of this RPC implementation.
9
+
10
+ This also demonstrates running the client loop in the main thread."""
11
+
12
+ import argparse
13
+
14
+ from ohmqtt.client import Client
15
+ from ohmqtt.packet import MQTTPublishPacket
16
+ from ohmqtt.property import MQTTPublishProps
17
+
18
+
19
+ class RPCServer:
20
+ """A simple stateless RPC server."""
21
+ def handle_request(self, client: Client, msg: MQTTPublishPacket) -> None:
22
+ """Handle incoming RPC requests."""
23
+ print(f"*** Received RPC request: {msg!s}")
24
+
25
+ # Find the response topic in the message properties.
26
+ if not msg.properties.ResponseTopic:
27
+ print("Request was missing required response topic property")
28
+ return
29
+ response_topic = msg.properties.ResponseTopic
30
+
31
+ response_props = MQTTPublishProps()
32
+ # If the request includes correlation data, send it back in the response.
33
+ if msg.properties.CorrelationData is not None:
34
+ response_props.CorrelationData = msg.properties.CorrelationData
35
+
36
+ # Simulate some processing.
37
+ response = f"This is a good day for {msg.payload.decode()}"
38
+
39
+ # Send the response back to the specified topic.
40
+ client.publish(response_topic, response.encode(), qos=2, properties=response_props)
41
+
42
+
43
+ def main(args: argparse.Namespace) -> None:
44
+ rpc_server = RPCServer()
45
+ client = Client()
46
+ client.connect(args.address)
47
+ print("*** Waiting for connection...")
48
+ assert client.loop_until_connected(timeout=5.0), "Timeout waiting for connection"
49
+
50
+ client.subscribe("ohmqtt/examples/rpc/request", rpc_server.handle_request)
51
+ print("*** Waiting for RPC requests...")
52
+
53
+ try:
54
+ client.loop_forever() # Wait indefinitely for incoming messages.
55
+ except KeyboardInterrupt:
56
+ print("\n*** Shutting down RPC server...")
57
+
58
+
59
+ if __name__ == "__main__":
60
+ from .args import parser
61
+ args = parser.parse_args()
62
+ main(args)
@@ -0,0 +1,19 @@
1
+ import nox
2
+ import sys
3
+
4
+ nox.options.default_venv_backend = "uv|virtualenv"
5
+ py_versions = ("3.10", "3.11", "3.12", "3.13")
6
+
7
+
8
+ @nox.session(python=py_versions)
9
+ def tests(session):
10
+ session.install(".")
11
+ session.install("--group", "dev")
12
+ session.run("ruff", "check")
13
+ session.run("typos")
14
+ session.run("mypy")
15
+ if sys.platform != "win32":
16
+ session.run("complexipy", "-d", "low", "ohmqtt", "examples", "tests")
17
+ else:
18
+ session.log("Skipping complexipy check on Windows: https://github.com/rohaquinlop/complexipy/issues/67")
19
+ session.run("pytest", "--cov-report=term-missing")
File without changes