openmodule 16.0.0rc0__tar.gz → 16.0.2__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.0.0rc0 → openmodule-16.0.2}/PKG-INFO +1 -1
  2. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/base.py +28 -2
  3. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/server.py +2 -2
  4. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/presence.py +1 -1
  5. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/validation.py +1 -1
  6. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/PKG-INFO +1 -1
  7. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_model.py +23 -0
  8. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_rpc.py +4 -4
  9. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_eventlog.py +3 -2
  10. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_vehicle.py +1 -1
  11. {openmodule-16.0.0rc0 → openmodule-16.0.2}/LICENSE +0 -0
  12. {openmodule-16.0.0rc0 → openmodule-16.0.2}/README.md +0 -0
  13. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/__init__.py +0 -0
  14. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/alert.py +0 -0
  15. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/config.py +0 -0
  16. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/connection_status.py +0 -0
  17. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/core.py +0 -0
  18. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/__init__.py +0 -0
  19. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/custom_types.py +0 -0
  20. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/database.py +0 -0
  21. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/env.py +0 -0
  22. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/database/migration.py +0 -0
  23. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/dispatcher.py +0 -0
  24. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/health.py +0 -0
  25. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/logging.py +0 -0
  26. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/messaging.py +0 -0
  27. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/__init__.py +0 -0
  28. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/access_service.py +0 -0
  29. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/alert.py +0 -0
  30. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/io.py +0 -0
  31. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/kv_store.py +0 -0
  32. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/presence.py +0 -0
  33. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/privacy.py +0 -0
  34. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/rpc.py +0 -0
  35. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/settings.py +0 -0
  36. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/signals.py +0 -0
  37. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/validation.py +0 -0
  38. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/models/vehicle.py +0 -0
  39. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/__init__.py +0 -0
  40. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/client.py +0 -0
  41. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/rpc/common.py +0 -0
  42. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/sentry.py +0 -0
  43. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/threading.py +0 -0
  44. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/__init__.py +0 -0
  45. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/access_service.py +0 -0
  46. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/charset.py +0 -0
  47. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/cleanup.py +0 -0
  48. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/csv_export.py +0 -0
  49. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/databox.py +0 -0
  50. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/db_helper.py +0 -0
  51. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/eventlog.py +0 -0
  52. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/io.py +0 -0
  53. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/kv_store.py +0 -0
  54. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/matching.py +0 -0
  55. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/misc_functions.py +0 -0
  56. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/package_reader.py +0 -0
  57. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/schedule.py +0 -0
  58. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/settings.py +0 -0
  59. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/signal_listener.py +0 -0
  60. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule/utils/translation.py +0 -0
  61. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/SOURCES.txt +0 -0
  62. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/dependency_links.txt +0 -0
  63. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/not-zip-safe +0 -0
  64. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/requires.txt +0 -0
  65. {openmodule-16.0.0rc0 → openmodule-16.0.2}/openmodule.egg-info/top_level.txt +0 -0
  66. {openmodule-16.0.0rc0 → openmodule-16.0.2}/setup.cfg +0 -0
  67. {openmodule-16.0.0rc0 → openmodule-16.0.2}/setup.py +0 -0
  68. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_alembic_migrations.py +0 -0
  69. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_alert.py +0 -0
  70. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_checks.py +0 -0
  71. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_config.py +0 -0
  72. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_connection_status.py +0 -0
  73. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_core.py +0 -0
  74. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_database.py +0 -0
  75. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_dispatcher.py +0 -0
  76. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_health.py +0 -0
  77. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_interrupt.py +0 -0
  78. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_io_listen.py +0 -0
  79. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_logging.py +0 -0
  80. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_messaging.py +0 -0
  81. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_mockrpcclient.py +0 -0
  82. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_sentry.py +0 -0
  83. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_test_alert.py +0 -0
  84. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_test_gate.py +0 -0
  85. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_test_zeromq.py +0 -0
  86. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_access_service.py +0 -0
  87. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_charset.py +0 -0
  88. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_cleanup.py +0 -0
  89. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_csv_export.py +0 -0
  90. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_databox.py +0 -0
  91. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_kv_store.py +0 -0
  92. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_kv_store_multiple.py +0 -0
  93. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_matching.py +0 -0
  94. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_misc_functions.py +0 -0
  95. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_package_reader.py +0 -0
  96. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_presence.py +0 -0
  97. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_schedule.py +0 -0
  98. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_settings.py +0 -0
  99. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_signal.py +0 -0
  100. {openmodule-16.0.0rc0 → openmodule-16.0.2}/tests/test_utils_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openmodule
3
- Version: 16.0.0rc0
3
+ Version: 16.0.2
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
@@ -5,13 +5,13 @@ from datetime import datetime
5
5
  from decimal import Decimal
6
6
  from enum import StrEnum
7
7
  from json.encoder import ESCAPE_ASCII
8
- from typing import Annotated
8
+ from typing import Annotated, Optional, Any, Self, ClassVar
9
9
 
10
10
  import orjson
11
11
  from pydantic_core import PydanticUndefined
12
12
  import zmq
13
13
  from dateutil.tz import UTC
14
- from pydantic import AfterValidator, ConfigDict, Field, BaseModel, RootModel
14
+ from pydantic import AfterValidator, ConfigDict, Field, BaseModel, PrivateAttr, RootModel, model_validator, field_validator
15
15
 
16
16
  from openmodule import sentry
17
17
  from openmodule.config import run_checks, settings
@@ -158,7 +158,11 @@ def datetime_to_timestamp(dt: datetime):
158
158
 
159
159
 
160
160
  class ZMQMessage(OpenModuleModel):
161
+ _ALLOW_CUSTOM_TIMESTAMP: ClassVar[bool] = False # set to true for tools scripts, where you need to set the timestamp manually
162
+
161
163
  timestamp: Datetime = Field(default_factory=lambda: utcnow())
164
+ _using_default_timestamp: bool = PrivateAttr()
165
+
162
166
  name: str = Field(default_factory=lambda: settings.NAME)
163
167
  type: str
164
168
  baggage: str | None = None
@@ -179,6 +183,28 @@ class ZMQMessage(OpenModuleModel):
179
183
  data["timestamp"] = datetime_to_timestamp(data["timestamp"])
180
184
  return data
181
185
 
186
+ @classmethod
187
+ def model_validate(cls, obj: Any, *, context: Optional[Any] = None, **kwargs) -> Self:
188
+ context = {**(context or {}), "from_model_validate": True}
189
+ return super().model_validate(obj, context=context, **kwargs)
190
+
191
+ @model_validator(mode="before")
192
+ @classmethod
193
+ def check_default_timestamp(cls, data: dict):
194
+ cls._using_default_timestamp = "timestamp" not in data
195
+ return data
196
+
197
+ @field_validator("timestamp", mode="before")
198
+ @classmethod
199
+ def check_custom_timestamp(cls, v, info):
200
+ from_model_validate = (info.context or {}).get("from_model_validate", False) is True
201
+ using_default_timestamp = cls._using_default_timestamp is True
202
+ custom_constructor_timestamp_allowed = cls._ALLOW_CUSTOM_TIMESTAMP is True
203
+
204
+ if not using_default_timestamp and not custom_constructor_timestamp_allowed and not from_model_validate:
205
+ raise ValueError("timestamp must not be set manually")
206
+ return v
207
+
182
208
 
183
209
  class Direction(StrEnum):
184
210
  UNKNOWN = ""
@@ -185,7 +185,7 @@ class RPCServer(object):
185
185
  handler: HandlerEntry = self.find_handler(channel, message.type)
186
186
  if handler:
187
187
  try:
188
- request = handler.request_class(**message.request)
188
+ request = handler.request_class.model_validate(message.request)
189
189
  except ValidationError as e:
190
190
  self.log.exception("exception in parsing request. request not processed and error returned")
191
191
  return {"status": RPCServerError.validation_error, "exception": e.json()}
@@ -225,7 +225,7 @@ class RPCServer(object):
225
225
  return
226
226
 
227
227
  try:
228
- message = RPCRequest(**message)
228
+ message = RPCRequest.model_validate(message)
229
229
  message_type = message.type
230
230
  rpc_id = message.rpc_id
231
231
  except ValidationError as e:
@@ -210,7 +210,7 @@ class PresenceListener:
210
210
  present_vehicle = self.present_vehicles.get(message.gateway.gate)
211
211
  if present_vehicle and present_vehicle.id == message.vehicle_id:
212
212
  self.log.error(f"Got {message.type} without a leave. We generate a leave ourself.")
213
- self._on_leave(PresenceLeaveMessage(**message.model_dump()))
213
+ self._on_leave(PresenceLeaveMessage.model_validate(message.model_dump()))
214
214
 
215
215
  def _execute_leave(self, vehicle: Vehicle, gateway: Gateway):
216
216
  if vehicle.leave_time is None:
@@ -25,7 +25,7 @@ class Validation:
25
25
  rpc_server.register_handler("validation_provider", "validate", request_class=ValidateRequest,
26
26
  response_class=ValidateResponse, handler=self.rpc_validate_ticket)
27
27
 
28
- def _validation_provider_filter(self, request, message, handler) -> bool:
28
+ def _validation_provider_filter(self, request, _message, _handler) -> bool:
29
29
  validation_provider = request.name
30
30
  if not validation_provider:
31
31
  return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openmodule
3
- Version: 16.0.0rc0
3
+ Version: 16.0.2
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
@@ -16,6 +16,12 @@ class TestModel(OpenModuleModel):
16
16
 
17
17
 
18
18
  class ModelTestCase(TestCase):
19
+ def setUp(self):
20
+ ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = True
21
+
22
+ def tearDown(self):
23
+ ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = False
24
+
19
25
  def test_json_is_forbidden(self):
20
26
  instance = TestModel(value="Hello World")
21
27
  with self.assertRaises(AssertionError):
@@ -72,6 +78,12 @@ class ModelTestCase(TestCase):
72
78
 
73
79
 
74
80
  class ZMQMessageTestCase(TestCase):
81
+ def setUp(self):
82
+ ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = True
83
+
84
+ def tearDown(self):
85
+ ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = False
86
+
75
87
  def test_timestamp_to_datetime(self):
76
88
  instance = ZMQMessage(timestamp=1607586354631, name="test", type="test")
77
89
  self.assertEqual(instance.timestamp.isoformat(), "2020-12-10T07:45:54.631000")
@@ -136,3 +148,14 @@ class ZMQMessageTestCase(TestCase):
136
148
 
137
149
  res1.pop("timestamp")
138
150
  self.assertDictEqual(res1, {"name": "tä\nst", "type": "tö\tst"})
151
+
152
+ def test_allow_custom_timestamp(self):
153
+ ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = False
154
+ with self.assertRaises(ValueError):
155
+ ZMQMessage(timestamp=1607586354631, name="test", type="test")
156
+ instance = ZMQMessage(name="test", type="test")
157
+ instance.type="asdf"
158
+ with self.assertRaises(ValueError):
159
+ instance.timestamp = datetime.datetime(2020, 1, 1, 0, 0, 0, 0)
160
+ ZMQMessage._ALLOW_CUSTOM_TIMESTAMP = True
161
+ instance.timestamp = datetime.datetime(2020, 1, 1, 0, 0, 0, 0)
@@ -148,7 +148,7 @@ class RPCServerTestCase(RPCServerTestMixin, OpenModuleCoreTestMixin):
148
148
  self.assertTrue(self.called_types.get("test-type"))
149
149
 
150
150
  def test_exception_in_filter_function(self):
151
- def bad_filter(*args, **kwargs) -> bool:
151
+ def bad_filter(_request, _message, _handler) -> bool:
152
152
  raise Exception("Error123")
153
153
 
154
154
  self.server.register_handler("channel", "test-type", TestRPCRequest, TestRPCResponse, self.set_called)
@@ -160,7 +160,7 @@ class RPCServerTestCase(RPCServerTestMixin, OpenModuleCoreTestMixin):
160
160
  self.assertEqual("Error123", str(e.exception))
161
161
 
162
162
  def test_filter(self):
163
- def allow_only_type_test(_request: OpenModuleModel, message: RPCRequest, _handler: None) -> bool:
163
+ def allow_only_type_test(_request: None, message: RPCRequest, _handler: None) -> bool:
164
164
  return message.type == "test-type"
165
165
 
166
166
  self.server.register_handler("channel", "test-type", TestRPCRequest, TestRPCResponse, self.set_called)
@@ -188,7 +188,7 @@ class RPCServerTestCase(RPCServerTestMixin, OpenModuleCoreTestMixin):
188
188
  self.assertNotIn("test-type2", self.called_types)
189
189
 
190
190
  def test_filter_channel_type(self):
191
- def f(*args, **kwargs):
191
+ def f(_request: None, _message: None, _handler: None):
192
192
  return False
193
193
 
194
194
  def check_ok():
@@ -511,7 +511,7 @@ class RpcClientTest(RPCServerTestMixin, OpenModuleCoreTestMixin):
511
511
  return TestRPCResponse2(some_payload="abc")
512
512
 
513
513
  @staticmethod
514
- def broken_filter(message: RPCRequest, **kwargs) -> bool:
514
+ def broken_filter(_request: None, _message: None, _handler: None) -> bool:
515
515
  assert False
516
516
 
517
517
  @staticmethod
@@ -1,5 +1,6 @@
1
1
  from datetime import datetime
2
2
 
3
+ from openmodule.models.base import ZMQMessage
3
4
  from openmodule.utils.eventlog import send_event, EventInfo, MessageKwarg, EventlogMessage, AnonymizationType, \
4
5
  PlateFormatData
5
6
  from openmodule_test.core import OpenModuleCoreTestMixin
@@ -19,7 +20,7 @@ class TestEvents(OpenModuleCoreTestMixin):
19
20
  self.assertIn("INFO:Eventlog:Sending event at gate1: LPR plate='G ARIVO 1' country='A': "
20
21
  "customer Test User, medium QR1, price 1", cm.output)
21
22
 
22
- msg = EventlogMessage(**self.zmq_client.wait_for_message_on_topic("eventlog"))
23
+ msg = EventlogMessage.model_validate(self.zmq_client.wait_for_message_on_topic("eventlog"))
23
24
  self.assertEqual(msg.event.infos.type, "test_1")
24
25
  self.assertEqual(msg.event.infos.timestamp, datetime.min)
25
26
  self.assertEqual(msg.event.infos.gate, "gate1")
@@ -47,5 +48,5 @@ class TestEvents(OpenModuleCoreTestMixin):
47
48
  with self.assertLogs("Eventlog") as cm:
48
49
  send_event(EventInfo.create("test_1"), "test")
49
50
  self.assertIn("INFO:Eventlog:Sending event: test", cm.output)
50
- msg = EventlogMessage(**self.zmq_client.wait_for_message_on_topic("eventlog"))
51
+ msg = EventlogMessage.model_validate(self.zmq_client.wait_for_message_on_topic("eventlog"))
51
52
  self.assertIsNotNone(msg.event.infos.timestamp)
@@ -66,6 +66,6 @@ class VehicleTestCase(TestCase):
66
66
  "id":"ZH 614198","no_countrylogic_id":"ZH 614198","track_id":1638169339870027893,"type":"lpr"}},"name":
67
67
  "forward-om_alpr_tracking_3","present-area-name":"einfahrt-garage","source":"einfahrt-garage",
68
68
  "timestamp":1638169352.606454,"type":"forward","unsure":false,"vehicle_id":1638169340812374101}"""
69
- parsed_message = PresenceForwardMessage(**orjson.loads(message))
69
+ parsed_message = PresenceForwardMessage.model_validate(orjson.loads(message))
70
70
  except ValidationError as e:
71
71
  assert False, "Parsing Presence Message failed"
File without changes
File without changes
File without changes
File without changes