Skip to content

Commit a968aee

Browse files
committed
Load service plugins from both modules and files
1 parent e86bf23 commit a968aee

File tree

5 files changed

+98
-15
lines changed

5 files changed

+98
-15
lines changed

CHANGES.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ in progress
77
===========
88

99

10-
2021-06-02 0.21.0
10+
2021-06-03 0.21.0
1111
=================
1212
- [xmpp] Add slixmpp plugin and documentation. Thanks, Remi!
1313
- [mysql] Fix unicode vs byte issue for Python 3. Thanks, Filip!
@@ -18,6 +18,7 @@ in progress
1818
responds with MQTT publish. Thanks, Jörg!
1919
- [core] Remove "os.chdir" as it is apparently not needed anymore. Thanks, Dan!
2020
- [ci] Run tests on Python 3.9, remove testing on Python 3.5
21+
- [core] Load service plugins from both modules and files
2122

2223

2324
2020-10-20 0.20.0

HANDBOOK.md

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ I've written an introductory post, explaining [what mqttwarn can be used for](ht
2323
+ [Custom functions](#custom-functions)
2424
+ [Templates](#templates)
2525
* [Periodic tasks](#periodic-tasks)
26-
* [Running with Docker](#running-with-docker)
27-
+ [Run the Image](#run-the-image)
28-
+ [Build the image](#build-the-image)
26+
* [Running with Docker](#docker)
27+
* [Loading external services](#loading-external-services)
2928
* [Examples](#examples)
3029
+ [Low battery notifications](#low-battery-notifications)
3130
+ [Producing JSON](#producing-json)
@@ -3404,6 +3403,55 @@ pinger = 10.5; now=true
34043403
In order to run `mqttwarn` on Docker, please follow up at [DOCKER.md](DOCKER.md).
34053404
34063405
3406+
## Loading external services
3407+
3408+
In order to bring in custom emitter machinery to `mqttwarn` in form of service
3409+
plugins, there are two options.
3410+
3411+
3412+
### Service plugin from package
3413+
3414+
This configuration snippet outlines how to load a custom plugin from a Python
3415+
module referenced in "dotted" notation.
3416+
3417+
```ini
3418+
[defaults]
3419+
; name the service providers you will be using.
3420+
launch = log, file, tests.acme.foobar
3421+
3422+
[test/plugin-module]
3423+
; echo '{"name": "temperature", "value": 42.42}' | mosquitto_pub -h localhost -t test/plugin-module -l
3424+
targets = tests.acme.foobar:default
3425+
format = {name}: {value}
3426+
3427+
[config:tests.acme.foobar]
3428+
targets = {
3429+
'default' : [ 'default' ],
3430+
}
3431+
```
3432+
3433+
### Service plugin from file
3434+
3435+
This configuration snippet outlines how to load a custom plugin from a Python
3436+
file referenced by file name.
3437+
3438+
```ini
3439+
[defaults]
3440+
; name the service providers you will be using.
3441+
launch = log, file, tests/acme/foobar.py
3442+
3443+
[test/plugin-file]
3444+
; echo '{"name": "temperature", "value": 42.42}' | mosquitto_pub -h localhost -t test/plugin-file -l
3445+
targets = tests/acme/foobar.py:default
3446+
format = {name}: {value}
3447+
3448+
[config:tests/acme/foobar.py]
3449+
targets = {
3450+
'default' : [ 'default' ],
3451+
}
3452+
```
3453+
3454+
34073455
## Examples ##
34083456
34093457
This section contains some examples of how `mqttwarn` can be used with some more complex configurations.

mqttwarn/core.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -532,20 +532,27 @@ def load_services(services):
532532

533533
module = cf.g('config:' + service, 'module', service)
534534

535-
if '.' in module:
535+
if module.endswith(".py"):
536+
modulefile = module
537+
538+
elif '.' in module:
539+
logger.debug('Trying to load service "{}" from module "{}"'.format(service, module))
536540
try:
537541
service_plugins[service]['module'] = load_module_by_name(module)
538542
logger.info('Successfully loaded service "{}" from module "{}"'.format(service, module))
543+
continue
539544
except Exception as ex:
540545
logger.exception('Unable to load service "{}" from module "{}": {}'.format(service, module, ex))
541546

542547
else:
543548
modulefile = resource_filename('mqttwarn.services', module + '.py')
544-
try:
545-
service_plugins[service]['module'] = load_module_from_file(modulefile)
546-
logger.info('Successfully loaded service "{}"'.format(service))
547-
except Exception as ex:
548-
logger.exception('Unable to load service "{}" from file "{}": {}'.format(service, modulefile, ex))
549+
550+
logger.debug('Trying to load service "{}" from file "{}"'.format(service, modulefile))
551+
try:
552+
service_plugins[service]['module'] = load_module_from_file(modulefile)
553+
logger.info('Successfully loaded service "{}"'.format(service))
554+
except Exception as ex:
555+
logger.exception('Unable to load service "{}" from file "{}": {}'.format(service, modulefile, ex))
549556

550557

551558
def connect():

tests/selftest.ini

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
; MQTT
1717
; ----
1818

19-
hostname = 'localhost' ; default
19+
hostname = 'localhost'
2020
port = 1883
2121
username = None
2222
password = None
@@ -55,7 +55,7 @@ loglevel = DEBUG
5555
functions = 'tests/selftest.py'
5656

5757
; name the service providers you will be using.
58-
launch = log, file, tests.acme.foobar
58+
launch = log, file, tests.acme.foobar, tests/acme/foobar.py
5959

6060
[config:log]
6161
targets = {
@@ -78,6 +78,11 @@ targets = {
7878
'default' : [ 'default' ],
7979
}
8080

81+
[config:tests/acme/foobar.py]
82+
targets = {
83+
'default' : [ 'default' ],
84+
}
85+
8186

8287
; -------
8388
; Targets
@@ -103,11 +108,16 @@ format = {name}: {value}
103108
targets = file:test-2
104109
format = {item}
105110

106-
[test/plugin-1]
107-
; echo '{"name": "temperature", "value": 42.42}' | mosquitto_pub -h localhost -t test/plugin-1 -l
111+
[test/plugin-module]
112+
; echo '{"name": "temperature", "value": 42.42}' | mosquitto_pub -h localhost -t test/plugin-module -l
108113
targets = tests.acme.foobar:default
109114
format = {name}: {value}
110115

116+
[test/plugin-file]
117+
; echo '{"name": "temperature", "value": 42.42}' | mosquitto_pub -h localhost -t test/plugin-file -l
118+
targets = tests/acme/foobar.py:default
119+
format = {name}: {value}
120+
111121
[test/datamap]
112122
targets = log:info
113123
datamap = datamap_dummy()

tests/test_core.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,24 @@ def test_plugin_module(caplog):
178178
core_bootstrap(configfile=configfile)
179179

180180
# Signal mocked MQTT message to the core machinery for processing
181-
send_message(topic='test/plugin-1', payload='{"name": "temperature", "value": 42.42}')
181+
send_message(topic='test/plugin-module', payload='{"name": "temperature", "value": 42.42}')
182+
183+
# Proof that the message has been routed to the "log" plugin properly
184+
assert 'Plugin invoked' in caplog.text, caplog.text
185+
186+
187+
def test_plugin_file(caplog):
188+
"""
189+
Check if using a module with dotted name also works.
190+
"""
191+
192+
with caplog.at_level(logging.DEBUG):
193+
194+
# Bootstrap the core machinery without MQTT
195+
core_bootstrap(configfile=configfile)
196+
197+
# Signal mocked MQTT message to the core machinery for processing
198+
send_message(topic='test/plugin-file', payload='{"name": "temperature", "value": 42.42}')
182199

183200
# Proof that the message has been routed to the "log" plugin properly
184201
assert 'Plugin invoked' in caplog.text, caplog.text

0 commit comments

Comments
 (0)