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.
Files changed (100) hide show
  1. {openmodule-16.1.1 → openmodule-17.0.0}/PKG-INFO +7 -7
  2. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/alert.py +1 -1
  3. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/core.py +18 -0
  4. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/dispatcher.py +14 -10
  5. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/health.py +2 -0
  6. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/schedule.py +2 -0
  7. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/settings.py +1 -9
  8. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/PKG-INFO +7 -7
  9. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/requires.txt +6 -6
  10. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_core.py +31 -1
  11. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_dispatcher.py +10 -6
  12. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_health.py +31 -2
  13. {openmodule-16.1.1 → openmodule-17.0.0}/LICENSE +0 -0
  14. {openmodule-16.1.1 → openmodule-17.0.0}/README.md +0 -0
  15. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/__init__.py +0 -0
  16. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/config.py +0 -0
  17. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/connection_status.py +0 -0
  18. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/__init__.py +0 -0
  19. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/custom_types.py +0 -0
  20. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/database.py +0 -0
  21. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/env.py +0 -0
  22. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/database/migration.py +0 -0
  23. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/logging.py +0 -0
  24. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/messaging.py +0 -0
  25. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/__init__.py +0 -0
  26. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/access_service.py +0 -0
  27. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/alert.py +0 -0
  28. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/base.py +0 -0
  29. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/io.py +0 -0
  30. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/kv_store.py +0 -0
  31. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/presence.py +0 -0
  32. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/privacy.py +0 -0
  33. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/rpc.py +0 -0
  34. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/settings.py +0 -0
  35. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/signals.py +0 -0
  36. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/validation.py +0 -0
  37. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/models/vehicle.py +0 -0
  38. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/__init__.py +0 -0
  39. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/client.py +0 -0
  40. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/common.py +0 -0
  41. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/rpc/server.py +0 -0
  42. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/sentry.py +0 -0
  43. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/threading.py +0 -0
  44. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/__init__.py +0 -0
  45. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/access_service.py +0 -0
  46. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/charset.py +0 -0
  47. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/cleanup.py +0 -0
  48. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/csv_export.py +0 -0
  49. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/databox.py +0 -0
  50. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/db_helper.py +0 -0
  51. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/eventlog.py +0 -0
  52. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/io.py +0 -0
  53. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/kv_store.py +0 -0
  54. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/matching.py +0 -0
  55. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/misc_functions.py +0 -0
  56. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/package_reader.py +0 -0
  57. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/presence.py +0 -0
  58. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/signal_listener.py +0 -0
  59. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/translation.py +0 -0
  60. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule/utils/validation.py +0 -0
  61. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/SOURCES.txt +0 -0
  62. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/dependency_links.txt +0 -0
  63. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/not-zip-safe +0 -0
  64. {openmodule-16.1.1 → openmodule-17.0.0}/openmodule.egg-info/top_level.txt +0 -0
  65. {openmodule-16.1.1 → openmodule-17.0.0}/setup.cfg +0 -0
  66. {openmodule-16.1.1 → openmodule-17.0.0}/setup.py +0 -0
  67. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_alembic_migrations.py +0 -0
  68. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_alert.py +0 -0
  69. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_checks.py +0 -0
  70. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_config.py +0 -0
  71. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_connection_status.py +0 -0
  72. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_database.py +0 -0
  73. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_interrupt.py +0 -0
  74. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_io_listen.py +0 -0
  75. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_logging.py +0 -0
  76. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_messaging.py +0 -0
  77. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_mockrpcclient.py +0 -0
  78. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_model.py +0 -0
  79. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_rpc.py +0 -0
  80. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_sentry.py +0 -0
  81. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_test_alert.py +0 -0
  82. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_test_gate.py +0 -0
  83. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_test_zeromq.py +0 -0
  84. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_access_service.py +0 -0
  85. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_charset.py +0 -0
  86. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_cleanup.py +0 -0
  87. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_csv_export.py +0 -0
  88. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_databox.py +0 -0
  89. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_eventlog.py +0 -0
  90. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_kv_store.py +0 -0
  91. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_kv_store_multiple.py +0 -0
  92. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_matching.py +0 -0
  93. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_misc_functions.py +0 -0
  94. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_package_reader.py +0 -0
  95. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_presence.py +0 -0
  96. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_schedule.py +0 -0
  97. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_settings.py +0 -0
  98. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_signal.py +0 -0
  99. {openmodule-16.1.1 → openmodule-17.0.0}/tests/test_utils_validation.py +0 -0
  100. {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: 16.1.1
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<4,>=3.4.7
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>=0.8.1
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>=2.7.2
26
- Requires-Dist: python-dotenv~=0.15
27
- Requires-Dist: arivo-settings_models~=2.5.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, filter: Callable | None,
47
+ def __init__(self, message_class: type[ZMQMessage], type: str | None, filter_: DispatcherFilterType,
45
48
  handler: Callable):
46
- self.filter = filter
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.filter:
56
- return self.filter(message)
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
- filter: dict | Callable[[dict], bool] | None = None,
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 filter: a dictionary of values which must match in order for the message to
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 filter={"type": message_class.fields["type"].default}
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_, filter, handler)
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
- filter: dict | None = None,
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, filter=filter, match_type=match_type)
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)
@@ -61,6 +61,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
61
61
  THE SOFTWARE.
62
62
  """
63
63
 
64
+ # VERSION: 1.2.2 (May 25, 2024)
65
+
64
66
  from collections.abc import Hashable
65
67
  import datetime
66
68
  import functools
@@ -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, Certificate, Otp
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: 16.1.1
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<4,>=3.4.7
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>=0.8.1
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>=2.7.2
26
- Requires-Dist: python-dotenv~=0.15
27
- Requires-Dist: arivo-settings_models~=2.5.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<4,>=3.4.7
3
+ orjson~=3.11
4
4
  pyzmq~=26.2
5
5
  pyyaml<7,>=5.0
6
- editdistance>=0.8.1
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>=2.7.2
11
- python-dotenv~=0.15
12
- arivo-settings_models~=2.5.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
- filter=lambda msg: msg.get("type") == "some-type")
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
- filter=lambda msg: msg.get("type") == "some-type")
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
- filter=lambda msg: msg.get("type") == "some-type")
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, lambda *x: x, match_type=False)
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, lambda *x: x, match_type=False)
196
- listener2 = self.dispatcher.register_handler("topic1", ZMQMessage, lambda *x: x, match_type=False)
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
- self.assertEqual("123", health.metrics[0].values['{"compute_id":"1"}'])
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.123, health.metrics[2].values['{"compute_id":"1"}'])
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