Skip to content

Commit 3140f63

Browse files
committed
Refactor classes Struct, Service, and Job to mqttwarn.model
1 parent 91a5344 commit 3140f63

File tree

8 files changed

+98
-95
lines changed

8 files changed

+98
-95
lines changed

mqttwarn/core.py

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
import threading
88
import time
99
import typing as t
10-
from builtins import chr, object, str
10+
from builtins import chr, str
1111
from datetime import datetime
12-
from functools import total_ordering
1312
from queue import Queue
1413

1514
import paho.mqtt.client as paho
@@ -18,10 +17,9 @@
1817
import mqttwarn.configuration
1918
from mqttwarn.context import FunctionInvoker, RuntimeContext
2019
from mqttwarn.cron import PeriodicThread
21-
from mqttwarn.model import StatusInformation
20+
from mqttwarn.model import Job, Service, StatusInformation, Struct
2221
from mqttwarn.util import (
2322
Formatter,
24-
Struct,
2523
asbool,
2624
load_function,
2725
load_module_by_name,
@@ -73,24 +71,6 @@
7371
service_plugins: t.Dict[str, t.Dict[str, t.Any]] = dict()
7472

7573

76-
# Class with helper functions which is passed to each plugin
77-
# and its global instantiation
78-
class Service(object):
79-
def __init__(self, mqttc, logger):
80-
81-
# Reference to MQTT client object
82-
self.mqttc = mqttc
83-
84-
# Reference to all mqttwarn globals, for using its machinery from plugins
85-
self.mwcore = globals()
86-
87-
# Reference to logging object
88-
self.logging = logger
89-
90-
# Name of self ("mqttwarn", mostly)
91-
self.SCRIPTNAME = SCRIPTNAME
92-
93-
9474
def make_service(mqttc=None, name=None):
9575
"""
9676
Service object factory.
@@ -103,37 +83,10 @@ def make_service(mqttc=None, name=None):
10383
"""
10484
name = name or "unknown"
10585
logger = logging.getLogger(name)
106-
service = Service(mqttc, logger)
86+
service = Service(mqttc=mqttc, logger=logger, mwcore=globals(), program=SCRIPTNAME)
10787
return service
10888

10989

110-
@total_ordering
111-
class Job(object):
112-
def __init__(self, prio, service, section, topic, payload, data, target):
113-
self.prio = prio
114-
self.service = service
115-
self.section = section
116-
self.topic = topic
117-
self.payload = payload # raw payload
118-
self.data = data # decoded payload
119-
self.target = target
120-
121-
logger.debug("New `%s:%s' job: %s" % (service, target, topic))
122-
return
123-
124-
# The `__cmp__()` special method is no longer honored in Python 3.
125-
# https://portingguide.readthedocs.io/en/latest/comparisons.html#rich-comparisons
126-
127-
def __eq__(self, other):
128-
return self.prio == other.prio
129-
130-
def __ne__(self, other):
131-
return not (self.prio == other.prio)
132-
133-
def __lt__(self, other):
134-
return self.prio < other.prio
135-
136-
13790
def render_template(filename, data):
13891
text = None
13992
if HAVE_JINJA is True:
@@ -352,6 +305,7 @@ def get_key(item):
352305
sendtos = [target]
353306

354307
for sendto in sendtos:
308+
logger.debug("New `%s:%s' job: %s" % (service, sendto, topic))
355309
job = Job(1, service, section, topic, payload, data, sendto)
356310
q_in.put(job)
357311

mqttwarn/model.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,43 @@
44
import platform
55
import sys
66
from dataclasses import dataclass, field
7+
from functools import total_ordering
78
from typing import Dict, List, Optional, Union
89

910
from mqttwarn import __version__
1011

1112

13+
class Struct:
14+
"""
15+
Data container for feeding information into service plugins - V1.
16+
"""
17+
18+
# Convert Python dict to object?
19+
# http://stackoverflow.com/questions/1305532/
20+
21+
def __init__(self, **entries):
22+
self.__dict__.update(entries)
23+
24+
def __repr__(self):
25+
return "<%s>" % str("\n ".join("%s: %s" % (k, repr(v)) for (k, v) in list(self.__dict__.items())))
26+
27+
def get(self, key, default=None):
28+
if key in self.__dict__ and self.__dict__[key] is not None:
29+
return self.__dict__[key]
30+
else:
31+
return default
32+
33+
def enum(self):
34+
item = {}
35+
for (k, v) in list(self.__dict__.items()):
36+
item[k] = v
37+
return item
38+
39+
1240
@dataclass
1341
class ProcessorItem:
1442
"""
15-
A processor item for feeding information into service handlers.
43+
Data container for feeding information into service plugins - V2.
1644
"""
1745

1846
service: Optional[str] = None
@@ -38,3 +66,47 @@ class StatusInformation:
3866
mqttwarn_version: str = __version__
3967
os_platform: str = sys.platform
4068
python_version: str = platform.python_version()
69+
70+
71+
class Service:
72+
"""
73+
Class with helper functions which is passed to each plugin and its global instantiation.
74+
"""
75+
76+
def __init__(self, mqttc, logger, mwcore, program):
77+
78+
# Reference to MQTT client object.
79+
self.mqttc = mqttc
80+
81+
# Reference to all mqttwarn globals, for using its machinery from plugins.
82+
self.mwcore = mwcore
83+
84+
# Reference to logging object.
85+
self.logging = logger
86+
87+
# Name of self ("mqttwarn", mostly).
88+
self.SCRIPTNAME = program
89+
90+
91+
@total_ordering
92+
class Job:
93+
def __init__(self, prio, service, section, topic, payload, data, target):
94+
self.prio = prio
95+
self.service = service
96+
self.section = section
97+
self.topic = topic
98+
self.payload = payload # raw payload
99+
self.data = data # decoded payload
100+
self.target = target
101+
102+
# The `__cmp__()` special method is no longer honored in Python 3.
103+
# https://portingguide.readthedocs.io/en/latest/comparisons.html#rich-comparisons
104+
105+
def __eq__(self, other):
106+
return self.prio == other.prio
107+
108+
def __ne__(self, other):
109+
return not (self.prio == other.prio)
110+
111+
def __lt__(self, other):
112+
return self.prio < other.prio

mqttwarn/testing/fixtures.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from mqttwarn.core import Service
5+
from mqttwarn.model import Service
66

77

88
@pytest.fixture
@@ -11,4 +11,5 @@ def mqttwarn_service():
1111
A service instance for propagating to the plugin.
1212
"""
1313
logger = logging.getLogger(__name__)
14-
return Service(mqttc=None, logger=logger)
14+
# FIXME: Should propagate `mqttwarn.core.globals()` to `mwcore`.
15+
return Service(mqttc=None, logger=logger, mwcore={}, program="mqttwarn-testdrive")

mqttwarn/util.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,11 @@
66
import os
77
import re
88
import string
9-
from builtins import str
109

1110
import pkg_resources
1211
from six import string_types
1312

1413

15-
class Struct:
16-
"""
17-
Convert Python dict to object?
18-
http://stackoverflow.com/questions/1305532/
19-
"""
20-
21-
def __init__(self, **entries):
22-
self.__dict__.update(entries)
23-
24-
def __repr__(self):
25-
return "<%s>" % str("\n ".join("%s: %s" % (k, repr(v)) for (k, v) in list(self.__dict__.items())))
26-
27-
def get(self, key, default=None):
28-
if key in self.__dict__ and self.__dict__[key] is not None:
29-
return self.__dict__[key]
30-
else:
31-
return default
32-
33-
def enum(self):
34-
item = {}
35-
for (k, v) in list(self.__dict__.items()):
36-
item[k] = v
37-
return item
38-
39-
4014
class Formatter(string.Formatter):
4115
"""
4216
A custom string formatter. See also:

tests/services/test_desktopnotify.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from surrogate import surrogate
88

99
from mqttwarn.model import ProcessorItem as Item
10-
from mqttwarn.util import Struct, load_module_by_name
10+
from mqttwarn.model import Struct
11+
from mqttwarn.util import load_module_by_name
1112

1213

1314
@pytest.fixture

tests/services/test_smtp.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import pytest
88

99
from mqttwarn.model import ProcessorItem as Item
10-
from mqttwarn.util import Struct, load_module_from_file
10+
from mqttwarn.model import Struct
11+
from mqttwarn.util import load_module_from_file
1112

1213

1314
@pytest.mark.skipif(sys.version_info < (3, 8), reason="This test only works on Python >= 3.8")

tests/test_model.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# -*- coding: utf-8 -*-
2-
# (c) 2022 The mqttwarn developers
2+
# (c) 2018-2022 The mqttwarn developers
33
from copy import deepcopy
44

5-
from mqttwarn.core import Job, make_service
5+
from mqttwarn.core import make_service
6+
from mqttwarn.model import Job, Struct
67

78
JOB_PRIO1 = dict(
89
prio=1, service="service", section="section", topic="topic", payload="payload", data="data", target="target"
@@ -18,7 +19,7 @@ def test_make_service():
1819
Verify creation of `Service` instance.
1920
"""
2021
service = make_service(name="foo")
21-
assert "<mqttwarn.core.Service object at" in str(service)
22+
assert "<mqttwarn.model.Service object at" in str(service)
2223

2324

2425
def test_job_equality():
@@ -49,3 +50,13 @@ def test_job_ordering_by_priority():
4950
job2 = Job(**JOB_PRIO2)
5051

5152
assert sorted([job2, job1]) == [job1, job2]
53+
54+
55+
def test_struct():
56+
data = {"hello": "world"}
57+
struct = Struct(**data)
58+
assert struct.hello == "world"
59+
assert struct.get("hello") == "world"
60+
assert struct.get("unknown", default=42) == 42
61+
assert repr(struct) == "<hello: 'world'>"
62+
assert struct.enum() == data

tests/test_util.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from mqttwarn.util import (
1313
Formatter,
14-
Struct,
1514
asbool,
1615
get_resource_content,
1716
import_module,
@@ -26,16 +25,6 @@
2625
from tests import configfile_full, funcfile_bad, funcfile_good
2726

2827

29-
def test_struct():
30-
data = {"hello": "world"}
31-
struct = Struct(**data)
32-
assert struct.hello == "world"
33-
assert struct.get("hello") == "world"
34-
assert struct.get("unknown", default=42) == 42
35-
assert repr(struct) == "<hello: 'world'>"
36-
assert struct.enum() == data
37-
38-
3928
def test_formatter_basic():
4029
result = Formatter().format("{foo}", **{"foo": "Räuber Hotzenplotz"}).encode("utf-8")
4130
assert result == b"R\xc3\xa4uber Hotzenplotz"

0 commit comments

Comments
 (0)