openmodule 16.1.1__tar.gz → 17.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.
- {openmodule-16.1.1 → openmodule-17.0.0}/PKG-INFO +7 -7
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/alert.py +1 -1
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/core.py +18 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/dispatcher.py +14 -10
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/health.py +2 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/schedule.py +2 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/settings.py +1 -9
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/PKG-INFO +7 -7
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/requires.txt +6 -6
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_core.py +31 -1
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_dispatcher.py +10 -6
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_health.py +31 -2
- {openmodule-16.1.1 → openmodule-17.0.0}/LICENSE +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/README.md +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/__init__.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/config.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/connection_status.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/__init__.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/custom_types.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/database.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/env.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/migration.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/logging.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/messaging.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/__init__.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/access_service.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/alert.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/base.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/io.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/kv_store.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/presence.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/privacy.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/rpc.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/settings.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/signals.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/validation.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/vehicle.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/__init__.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/client.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/common.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/server.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/sentry.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/threading.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/__init__.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/access_service.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/charset.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/cleanup.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/csv_export.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/databox.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/db_helper.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/eventlog.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/io.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/kv_store.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/matching.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/misc_functions.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/package_reader.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/presence.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/signal_listener.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/translation.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/validation.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/SOURCES.txt +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/dependency_links.txt +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/not-zip-safe +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/top_level.txt +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/setup.cfg +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/setup.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_alembic_migrations.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_alert.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_checks.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_config.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_connection_status.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_database.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_interrupt.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_io_listen.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_logging.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_messaging.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_mockrpcclient.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_model.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_rpc.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_sentry.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_test_alert.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_test_gate.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_test_zeromq.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_access_service.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_charset.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_cleanup.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_csv_export.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_databox.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_eventlog.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_kv_store.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_kv_store_multiple.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_matching.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_misc_functions.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_package_reader.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_presence.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_schedule.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_settings.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_signal.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_validation.py +0 -0
- {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_vehicle.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openmodule
|
|
3
|
-
Version:
|
|
3
|
+
Version: 17.0.0
|
|
4
4
|
Summary: Libraries for developing the arivo openmodule
|
|
5
5
|
Home-page: https://gitlab.com/arivo-public/device-python/openmodule.git
|
|
6
6
|
Author: ARIVO
|
|
@@ -13,18 +13,18 @@ Classifier: Programming Language :: Python
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Description-Content-Type: text/markdown; charset=UTF-8
|
|
15
15
|
License-File: LICENSE
|
|
16
|
-
Requires-Dist: pydantic~=2.0
|
|
16
|
+
Requires-Dist: pydantic~=2.12.0
|
|
17
17
|
Requires-Dist: sentry-sdk~=2.19.0
|
|
18
|
-
Requires-Dist: orjson
|
|
18
|
+
Requires-Dist: orjson~=3.11
|
|
19
19
|
Requires-Dist: pyzmq~=26.2
|
|
20
20
|
Requires-Dist: pyyaml<7,>=5.0
|
|
21
|
-
Requires-Dist: editdistance
|
|
21
|
+
Requires-Dist: editdistance~=0.8.1
|
|
22
22
|
Requires-Dist: sqlalchemy~=2.0.0
|
|
23
23
|
Requires-Dist: alembic<2,>=1.5.4
|
|
24
24
|
Requires-Dist: requests<3,>=2.22
|
|
25
|
-
Requires-Dist: python-dateutil
|
|
26
|
-
Requires-Dist: python-dotenv~=0
|
|
27
|
-
Requires-Dist: arivo-settings_models~=2.
|
|
25
|
+
Requires-Dist: python-dateutil~=2.9
|
|
26
|
+
Requires-Dist: python-dotenv~=1.2.0
|
|
27
|
+
Requires-Dist: arivo-settings_models~=2.6.0
|
|
28
28
|
Provides-Extra: test
|
|
29
29
|
Requires-Dist: openmodule_test; extra == "test"
|
|
30
30
|
Provides-Extra: commands
|
|
@@ -78,7 +78,7 @@ class AlertHandler(object):
|
|
|
78
78
|
self._alerts.append(alert)
|
|
79
79
|
return alert["id"]
|
|
80
80
|
|
|
81
|
-
def send_with_alert_id(self, alert_id: int, status: AlertStatus, meta: dict, value: float | None = None):
|
|
81
|
+
def send_with_alert_id(self, alert_id: int, status: AlertStatus, meta: dict | None, value: float | None = None):
|
|
82
82
|
""" Send a status message on the given alert with the corresponding meta/kwarg arguments
|
|
83
83
|
Returns True if alert was send else False"""
|
|
84
84
|
assert status in [AlertStatus.error, AlertStatus.ok, AlertStatus.offline]
|
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
import shutil
|
|
4
4
|
import signal
|
|
5
5
|
import threading
|
|
6
|
+
import time
|
|
6
7
|
import warnings
|
|
7
8
|
from concurrent.futures.thread import ThreadPoolExecutor
|
|
8
9
|
|
|
@@ -17,6 +18,7 @@ from openmodule.logging import init_logging
|
|
|
17
18
|
from openmodule.messaging import _internal_get_pub_socket, get_sub_socket, receive_message_from_socket, \
|
|
18
19
|
wait_for_connection
|
|
19
20
|
from openmodule.models.base import ZMQMessage
|
|
21
|
+
from openmodule.models.settings import SettingsGetRequest, SettingsGetResponse
|
|
20
22
|
from openmodule.sentry import init_sentry, should_activate_sentry, deinit_sentry
|
|
21
23
|
from openmodule.threading import get_thread_wrapper
|
|
22
24
|
from openmodule.connection_status import ConnectionStatusListener
|
|
@@ -134,6 +136,22 @@ def print_environment(core: OpenModuleCore):
|
|
|
134
136
|
)
|
|
135
137
|
|
|
136
138
|
|
|
139
|
+
def wait_for_misc(core: OpenModuleCore, timeout: float = 120, retry_time: float = 3.0):
|
|
140
|
+
"""
|
|
141
|
+
Checks if service misc is answering by calling a "settings" "get" request for "common/garage_settings"
|
|
142
|
+
"""
|
|
143
|
+
from openmodule.rpc import RPCClient
|
|
144
|
+
start = time.time()
|
|
145
|
+
while time.time() - start < timeout:
|
|
146
|
+
try:
|
|
147
|
+
core.rpc_client.rpc("settings", "get", SettingsGetRequest(key="common/garage_settings", scope=""),
|
|
148
|
+
SettingsGetResponse, retry_time)
|
|
149
|
+
return
|
|
150
|
+
except RPCClient.Exception as e:
|
|
151
|
+
core.log.warning(f"Misc did not answer correctly because of {str(e) or repr(e)}. Retry in 3 seconds")
|
|
152
|
+
raise TimeoutError("Could not get any settings in time")
|
|
153
|
+
|
|
154
|
+
|
|
137
155
|
def init_openmodule(config, *, dsn: str = None, sentry=None, logging=True, dsgvo=True,
|
|
138
156
|
health_handler: HealthHandlerType | None = None,
|
|
139
157
|
context=None, database=False, catch_sigterm=True,
|
|
@@ -20,6 +20,9 @@ from openmodule.config import settings
|
|
|
20
20
|
from openmodule.models.base import ZMQMessage
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
DispatcherFilterType = dict | Callable[[dict], bool] | None
|
|
24
|
+
|
|
25
|
+
|
|
23
26
|
class DummyExecutor(Executor):
|
|
24
27
|
def __init__(self):
|
|
25
28
|
self._shutdown = False
|
|
@@ -41,9 +44,9 @@ class DummyExecutor(Executor):
|
|
|
41
44
|
|
|
42
45
|
|
|
43
46
|
class Listener:
|
|
44
|
-
def __init__(self, message_class: type[ZMQMessage], type: str | None,
|
|
47
|
+
def __init__(self, message_class: type[ZMQMessage], type: str | None, filter_: DispatcherFilterType,
|
|
45
48
|
handler: Callable):
|
|
46
|
-
self.
|
|
49
|
+
self.filter_ = filter_
|
|
47
50
|
self.handler = sentry.trace(f"message_handler.{qualname_from_function(handler)}")(handler)
|
|
48
51
|
self.type = type
|
|
49
52
|
self.message_class = message_class
|
|
@@ -52,8 +55,9 @@ class Listener:
|
|
|
52
55
|
if self.type and message.get("type") != self.type:
|
|
53
56
|
return False
|
|
54
57
|
|
|
55
|
-
if self.
|
|
56
|
-
|
|
58
|
+
if self.filter_:
|
|
59
|
+
# this is expected to fail if a non-empty dict is passed as 'filter_' parameter
|
|
60
|
+
return self.filter_(message) # type: ignore
|
|
57
61
|
else:
|
|
58
62
|
return True
|
|
59
63
|
|
|
@@ -144,15 +148,15 @@ class MessageDispatcher:
|
|
|
144
148
|
def register_handler(self, topic: str,
|
|
145
149
|
message_class: type[ZMQMessageSub],
|
|
146
150
|
handler: Callable[[ZMQMessageSub], None], *,
|
|
147
|
-
|
|
151
|
+
filter_: DispatcherFilterType = None,
|
|
148
152
|
match_type=True):
|
|
149
153
|
"""
|
|
150
154
|
registers a message handler. without any filters all messages from the topic are
|
|
151
155
|
sent to the message handler.
|
|
152
|
-
:param
|
|
156
|
+
:param filter_: a dictionary of values which must match in order for the message to
|
|
153
157
|
be further processed
|
|
154
158
|
:param match_type: if set to true the message_class's type field is used as a filter.
|
|
155
|
-
equivalent to setting
|
|
159
|
+
equivalent to setting filter_={"type": message_class.fields["type"].default}
|
|
156
160
|
"""
|
|
157
161
|
|
|
158
162
|
assert isinstance(topic, str), "topic must be a string"
|
|
@@ -169,7 +173,7 @@ class MessageDispatcher:
|
|
|
169
173
|
else:
|
|
170
174
|
type_ = None
|
|
171
175
|
|
|
172
|
-
listener = Listener(message_class, type_,
|
|
176
|
+
listener = Listener(message_class, type_, filter_, handler)
|
|
173
177
|
self.listeners[topic].append(listener)
|
|
174
178
|
|
|
175
179
|
if settings.TESTING and hasattr(handler, "__global__") and "/tests/" not in handler.__globals__['__file__']:
|
|
@@ -231,12 +235,12 @@ class SubscribingMessageDispatcher(MessageDispatcher):
|
|
|
231
235
|
def register_handler(self, topic: str,
|
|
232
236
|
message_class: type[ZMQMessageSub],
|
|
233
237
|
handler: Callable[[ZMQMessageSub], None], *,
|
|
234
|
-
|
|
238
|
+
filter_: DispatcherFilterType = None,
|
|
235
239
|
match_type=True):
|
|
236
240
|
assert isinstance(topic, str), "channel must be a string"
|
|
237
241
|
|
|
238
242
|
self.subscribe(topic)
|
|
239
|
-
return super().register_handler(topic, message_class, handler,
|
|
243
|
+
return super().register_handler(topic, message_class, handler, filter_=filter_, match_type=match_type)
|
|
240
244
|
|
|
241
245
|
def unregister_handler(self, listener: Listener):
|
|
242
246
|
super().unregister_handler(listener)
|
|
@@ -145,6 +145,7 @@ class Healthz:
|
|
|
145
145
|
description=description,
|
|
146
146
|
state=HealthCheckState.no_data
|
|
147
147
|
)
|
|
148
|
+
self.checks = dict(sorted(self.checks.items(), key=lambda x: x[0]))
|
|
148
149
|
|
|
149
150
|
def remove_check(self, check_id, *, parent: bool | None = False, package: str | None = None):
|
|
150
151
|
key = self._check_id(check_id, parent, package)
|
|
@@ -185,6 +186,7 @@ class Healthz:
|
|
|
185
186
|
description=description,
|
|
186
187
|
labels=list(labels),
|
|
187
188
|
)
|
|
189
|
+
self.metrics = dict(sorted(self.metrics.items(), key=lambda x: x[0]))
|
|
188
190
|
|
|
189
191
|
def remove_metric(self, metric_id: str, *, parent: bool | None = False, package: str | None = None):
|
|
190
192
|
key = self._check_id(metric_id, parent, package)
|
|
@@ -6,7 +6,7 @@ from typing import Any, TypeVar, Literal, overload
|
|
|
6
6
|
from settings_models.serialization import parse_setting_from_obj
|
|
7
7
|
from settings_models.settings.common import GarageName, Gates, Rates, ParkingAreas, Parksettings, PrivacySettings, \
|
|
8
8
|
CostGroups, GarageSettings, Location, BillingSettings, SupportSettings, Urls
|
|
9
|
-
from settings_models.settings.device_keys import PairingKey
|
|
9
|
+
from settings_models.settings.device_keys import PairingKey
|
|
10
10
|
from settings_models.settings.enforcement import EnforcementSettings
|
|
11
11
|
from settings_models.settings.gate_control import GateMode, DayMode
|
|
12
12
|
from settings_models.settings.intercom import IntercomSettings
|
|
@@ -186,14 +186,6 @@ class SettingsProvider:
|
|
|
186
186
|
def get(self, key: Literal["device_keys/pairing"]) -> PairingKey: # pragma: no cover
|
|
187
187
|
...
|
|
188
188
|
|
|
189
|
-
@overload
|
|
190
|
-
def get(self, key: Literal["device_keys/cert"]) -> Certificate: # pragma: no cover
|
|
191
|
-
...
|
|
192
|
-
|
|
193
|
-
@overload
|
|
194
|
-
def get(self, key: Literal["device_keys/otp"]) -> Otp: # pragma: no cover
|
|
195
|
-
...
|
|
196
|
-
|
|
197
189
|
@overload
|
|
198
190
|
def get(self, key: Literal["feature_flags"]) -> dict: # pragma: no cover
|
|
199
191
|
...
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openmodule
|
|
3
|
-
Version:
|
|
3
|
+
Version: 17.0.0
|
|
4
4
|
Summary: Libraries for developing the arivo openmodule
|
|
5
5
|
Home-page: https://gitlab.com/arivo-public/device-python/openmodule.git
|
|
6
6
|
Author: ARIVO
|
|
@@ -13,18 +13,18 @@ Classifier: Programming Language :: Python
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Description-Content-Type: text/markdown; charset=UTF-8
|
|
15
15
|
License-File: LICENSE
|
|
16
|
-
Requires-Dist: pydantic~=2.0
|
|
16
|
+
Requires-Dist: pydantic~=2.12.0
|
|
17
17
|
Requires-Dist: sentry-sdk~=2.19.0
|
|
18
|
-
Requires-Dist: orjson
|
|
18
|
+
Requires-Dist: orjson~=3.11
|
|
19
19
|
Requires-Dist: pyzmq~=26.2
|
|
20
20
|
Requires-Dist: pyyaml<7,>=5.0
|
|
21
|
-
Requires-Dist: editdistance
|
|
21
|
+
Requires-Dist: editdistance~=0.8.1
|
|
22
22
|
Requires-Dist: sqlalchemy~=2.0.0
|
|
23
23
|
Requires-Dist: alembic<2,>=1.5.4
|
|
24
24
|
Requires-Dist: requests<3,>=2.22
|
|
25
|
-
Requires-Dist: python-dateutil
|
|
26
|
-
Requires-Dist: python-dotenv~=0
|
|
27
|
-
Requires-Dist: arivo-settings_models~=2.
|
|
25
|
+
Requires-Dist: python-dateutil~=2.9
|
|
26
|
+
Requires-Dist: python-dotenv~=1.2.0
|
|
27
|
+
Requires-Dist: arivo-settings_models~=2.6.0
|
|
28
28
|
Provides-Extra: test
|
|
29
29
|
Requires-Dist: openmodule_test; extra == "test"
|
|
30
30
|
Provides-Extra: commands
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
pydantic~=2.0
|
|
1
|
+
pydantic~=2.12.0
|
|
2
2
|
sentry-sdk~=2.19.0
|
|
3
|
-
orjson
|
|
3
|
+
orjson~=3.11
|
|
4
4
|
pyzmq~=26.2
|
|
5
5
|
pyyaml<7,>=5.0
|
|
6
|
-
editdistance
|
|
6
|
+
editdistance~=0.8.1
|
|
7
7
|
sqlalchemy~=2.0.0
|
|
8
8
|
alembic<2,>=1.5.4
|
|
9
9
|
requests<3,>=2.22
|
|
10
|
-
python-dateutil
|
|
11
|
-
python-dotenv~=0
|
|
12
|
-
arivo-settings_models~=2.
|
|
10
|
+
python-dateutil~=2.9
|
|
11
|
+
python-dotenv~=1.2.0
|
|
12
|
+
arivo-settings_models~=2.6.0
|
|
13
13
|
|
|
14
14
|
[commands]
|
|
15
15
|
openmodule_commands
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import threading
|
|
2
|
+
import time
|
|
2
3
|
from unittest import mock
|
|
3
4
|
|
|
4
5
|
from openmodule.alert import AlertHandleType
|
|
5
6
|
from openmodule.config import override_settings
|
|
6
|
-
from openmodule.core import core, init_openmodule, shutdown_openmodule
|
|
7
|
+
from openmodule.core import core, init_openmodule, shutdown_openmodule, wait_for_misc
|
|
7
8
|
from openmodule.models.base import ZMQMessage
|
|
9
|
+
from openmodule.models.settings import SettingsGetResponse
|
|
10
|
+
from openmodule.rpc import RPCClient
|
|
8
11
|
from openmodule_test.alert import AlertTestMixin
|
|
12
|
+
from openmodule_test.core import OpenModuleCoreTestMixin
|
|
9
13
|
from openmodule_test.health import HealthTestMixin
|
|
14
|
+
from openmodule_test.rpc import MockRPCClient
|
|
10
15
|
from openmodule_test.sentry import SentryTestMixin
|
|
11
16
|
|
|
12
17
|
|
|
@@ -95,3 +100,28 @@ class OpenModuleCoreTest(AlertTestMixin, HealthTestMixin, SentryTestMixin):
|
|
|
95
100
|
self.assertTrue(dispatch_done.wait(timeout=3))
|
|
96
101
|
self.assertFalse(handler.called)
|
|
97
102
|
self.assertEqual(error, "")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class WaitForMiscTest(OpenModuleCoreTestMixin):
|
|
106
|
+
def test_wait_for_misc(self):
|
|
107
|
+
with self.assertRaises(TimeoutError):
|
|
108
|
+
wait_for_misc(self.core, 3, 1)
|
|
109
|
+
|
|
110
|
+
def raise_error(*args, **kwargs):
|
|
111
|
+
time.sleep(1)
|
|
112
|
+
raise RPCClient.RPCServerError("Bad request")
|
|
113
|
+
|
|
114
|
+
self.core.rpc_client = MockRPCClient(immediate_callbacks={("settings", "get"): raise_error})
|
|
115
|
+
with self.assertRaises(TimeoutError):
|
|
116
|
+
wait_for_misc(self.core, 3, 1)
|
|
117
|
+
|
|
118
|
+
def set_rpc_client():
|
|
119
|
+
time.sleep(2.0)
|
|
120
|
+
self.core.rpc_client = MockRPCClient(responses={("settings", "get"): SettingsGetResponse(success=True)})
|
|
121
|
+
|
|
122
|
+
thread = threading.Thread(target=set_rpc_client)
|
|
123
|
+
thread.start()
|
|
124
|
+
wait_for_misc(self.core, 3, 1)
|
|
125
|
+
thread.join()
|
|
126
|
+
|
|
127
|
+
wait_for_misc(self.core, 3, 1)
|
|
@@ -51,7 +51,7 @@ class MessageDispatcherBasicsTestCase(MessageDispatcherBaseTest):
|
|
|
51
51
|
def test_filter(self):
|
|
52
52
|
self.dispatcher.register_handler("test", ZMQMessage, self._set_true_handler,
|
|
53
53
|
match_type=False,
|
|
54
|
-
|
|
54
|
+
filter_=lambda msg: msg.get("type") == "some-type")
|
|
55
55
|
|
|
56
56
|
# filter does not match
|
|
57
57
|
self.dispatcher.dispatch("test", self.dummy_message(type="incorrect"))
|
|
@@ -68,10 +68,10 @@ class MessageDispatcherBasicsTestCase(MessageDispatcherBaseTest):
|
|
|
68
68
|
|
|
69
69
|
self.dispatcher.register_handler("test", ZMQMessage, partial(self._set_true_handler, var="message1"),
|
|
70
70
|
match_type=False,
|
|
71
|
-
|
|
71
|
+
filter_=lambda msg: msg.get("type") == "some-type")
|
|
72
72
|
self.dispatcher.register_handler("test", ZMQMessage, partial(self._set_true_handler, var="message2"),
|
|
73
73
|
match_type=False,
|
|
74
|
-
|
|
74
|
+
filter_=lambda msg: msg.get("type") == "some-type")
|
|
75
75
|
|
|
76
76
|
# filter does not match
|
|
77
77
|
self.dispatcher.dispatch("test", self.dummy_message(type="incorrect"))
|
|
@@ -171,6 +171,10 @@ class MessageDispatcherWithExecutorTestCase(TestCase):
|
|
|
171
171
|
IoListener(MessageDispatcher(executor=ThreadPoolExecutor(max_workers=2)))
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
def dummy_handler(_: ZMQMessage) -> None:
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
174
178
|
class SubscribingMessageDispatcherTestCase(TestCase):
|
|
175
179
|
subscriptions = set()
|
|
176
180
|
|
|
@@ -188,12 +192,12 @@ class SubscribingMessageDispatcherTestCase(TestCase):
|
|
|
188
192
|
)
|
|
189
193
|
|
|
190
194
|
def test_subscribe(self):
|
|
191
|
-
self.dispatcher.register_handler("topic1", ZMQMessage,
|
|
195
|
+
self.dispatcher.register_handler("topic1", ZMQMessage, dummy_handler, match_type=False)
|
|
192
196
|
self.assertEqual({"topic1"}, self.subscriptions)
|
|
193
197
|
|
|
194
198
|
def test_unsubscribe(self):
|
|
195
|
-
listener1 = self.dispatcher.register_handler("topic1", ZMQMessage,
|
|
196
|
-
listener2 = self.dispatcher.register_handler("topic1", ZMQMessage,
|
|
199
|
+
listener1 = self.dispatcher.register_handler("topic1", ZMQMessage, dummy_handler, match_type=False)
|
|
200
|
+
listener2 = self.dispatcher.register_handler("topic1", ZMQMessage, dummy_handler, match_type=False)
|
|
197
201
|
|
|
198
202
|
self.assertEqual({"topic1"}, self.subscriptions)
|
|
199
203
|
|
|
@@ -37,6 +37,34 @@ class HealthTestCase(HealthTestMixin, TestCase):
|
|
|
37
37
|
self.assertEqual(health["pong"]["status"], "error")
|
|
38
38
|
self.assertEqual(health["pong"]["message"], "error")
|
|
39
39
|
|
|
40
|
+
def test_checks_ordered(self):
|
|
41
|
+
self.core.health.add_check("z", "z", "test z")
|
|
42
|
+
self.core.health.add_check("y", "y", "test y")
|
|
43
|
+
self.assertListEqual(
|
|
44
|
+
list(self.core.health.checks.keys()),
|
|
45
|
+
[("y", "test"), ("z", "test")]
|
|
46
|
+
)
|
|
47
|
+
self.core.health.add_check("a", "a", "test a")
|
|
48
|
+
self.core.health.add_check("f", "f", "test f")
|
|
49
|
+
self.assertListEqual(
|
|
50
|
+
list(self.core.health.checks.keys()),
|
|
51
|
+
[("a", "test"), ("f", "test"), ("y", "test"), ("z", "test")]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def test_metrics_sorted(self):
|
|
55
|
+
self.core.health.add_metric("z", HealthMetricType.count, "Test Z", "test z")
|
|
56
|
+
self.core.health.add_metric("y", HealthMetricType.count, "Test Y", "test y")
|
|
57
|
+
self.assertListEqual(
|
|
58
|
+
list(self.core.health.metrics.keys()),
|
|
59
|
+
[("y", "test"), ("z", "test")]
|
|
60
|
+
)
|
|
61
|
+
self.core.health.add_metric("a", HealthMetricType.count, "Test A", "test a")
|
|
62
|
+
self.core.health.add_metric("f", HealthMetricType.count, "Test F", "test f")
|
|
63
|
+
self.assertListEqual(
|
|
64
|
+
list(self.core.health.metrics.keys()),
|
|
65
|
+
[("a", "test"), ("f", "test"), ("y", "test"), ("z", "test")]
|
|
66
|
+
)
|
|
67
|
+
|
|
40
68
|
def test_metrics(self):
|
|
41
69
|
self.core.health.add_metric("test_counter", HealthMetricType.count, "Test Count", "counter for testing",
|
|
42
70
|
labels={"lc1", "lc2"})
|
|
@@ -256,9 +284,10 @@ class HealthTestCase(HealthTestMixin, TestCase):
|
|
|
256
284
|
self.core.health.metric_set("test_int", 123)
|
|
257
285
|
self.core.health.metric_set("test_float", 123.123)
|
|
258
286
|
health = HealthPongMessage.model_validate(self.get_health())
|
|
259
|
-
|
|
287
|
+
# NOTE: metrics and checks are sorted by keys!
|
|
288
|
+
self.assertEqual(123.123, health.metrics[0].values['{"compute_id":"1"}'])
|
|
260
289
|
self.assertEqual(123, health.metrics[1].values['{"compute_id":"1"}'])
|
|
261
|
-
self.assertEqual(123
|
|
290
|
+
self.assertEqual("123", health.metrics[2].values['{"compute_id":"1"}'])
|
|
262
291
|
|
|
263
292
|
def test_metric_clear(self):
|
|
264
293
|
self.core.health.add_metric("test_counter", HealthMetricType.count, "Test Count", "counter for testing",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|