vortexpy 4.3.4__tar.gz → 4.3.5__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 (130) hide show
  1. {vortexpy-4.3.4 → vortexpy-4.3.5}/PKG-INFO +1 -1
  2. {vortexpy-4.3.4 → vortexpy-4.3.5}/pyproject.toml +1 -1
  3. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleBson_test.py +24 -0
  4. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleDocumentConverter.py +121 -0
  5. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleRestful_test.py +35 -0
  6. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/__init__.py +1 -1
  7. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataObservableProxyHandler.py +4 -0
  8. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/tuple_test_fixtures.py +8 -0
  9. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortexpy.egg-info/PKG-INFO +1 -1
  10. {vortexpy-4.3.4 → vortexpy-4.3.5}/README.md +0 -0
  11. {vortexpy-4.3.4 → vortexpy-4.3.5}/setup.cfg +0 -0
  12. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/DataWrapTuple.py +0 -0
  13. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/DeferUtil.py +0 -0
  14. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/DeferUtil_test.py +0 -0
  15. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/Jsonable.py +0 -0
  16. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/MtlsAuthorizedClientRegistry.py +0 -0
  17. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/MtlsAuthorizedClientRegistry_test.py +0 -0
  18. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/MtlsServerSslOptions.py +0 -0
  19. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/Payload.py +0 -0
  20. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadEndpoint.py +0 -0
  21. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadEndpointTest.py +0 -0
  22. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadEnvelope.py +0 -0
  23. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadFilterKeys.py +0 -0
  24. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadIO.py +0 -0
  25. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadPriority.py +0 -0
  26. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadResponse.py +0 -0
  27. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadResponseTest.py +0 -0
  28. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadResponse_test.py +0 -0
  29. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PayloadTest.py +0 -0
  30. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/PerformTestActionTuple.py +0 -0
  31. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/SerialiseUtil.py +0 -0
  32. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/SerialiseUtil_test.py +0 -0
  33. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TestTuple.py +0 -0
  34. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/Tuple.py +0 -0
  35. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleAction.py +0 -0
  36. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleBson.py +0 -0
  37. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleDataForTest.py +0 -0
  38. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TuplePerfTest.py +0 -0
  39. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TuplePolymorphicTupleTypeFieldArgTest.py +0 -0
  40. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleRestful.py +0 -0
  41. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleSelector.py +0 -0
  42. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/TupleTest.py +0 -0
  43. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexABC.py +0 -0
  44. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexAccessControlContextABC.py +0 -0
  45. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexAuthenticatorABC.py +0 -0
  46. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexClientHttp.py +0 -0
  47. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexClientHttpTest.py +0 -0
  48. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexClientTcp.py +0 -0
  49. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexClientWebsocketFactory.py +0 -0
  50. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexConnectionABC.py +0 -0
  51. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexEndpointPermission.py +0 -0
  52. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexFactory.py +0 -0
  53. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexFactory_test.py +0 -0
  54. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexPayloadProtocol.py +0 -0
  55. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexPermission.py +0 -0
  56. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexPermissionError.py +0 -0
  57. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexServer.py +0 -0
  58. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexServerConnection.py +0 -0
  59. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexServerConnection_test.py +0 -0
  60. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexServerHttpResource.py +0 -0
  61. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexServerTcp.py +0 -0
  62. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexServerWebsocket.py +0 -0
  63. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexSession.py +0 -0
  64. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexSession_test.py +0 -0
  65. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexTcpMemoryLeakPayloadTest.py +0 -0
  66. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexTcpMemoryLeakRawTest.py +0 -0
  67. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexTest.py +0 -0
  68. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexUtil.py +0 -0
  69. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/VortexWritePushProducer.py +0 -0
  70. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/data_loader/TupleDataLiveLockManager.py +0 -0
  71. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/data_loader/TupleDataLoader.py +0 -0
  72. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/data_loader/TupleDataLoaderDelegate.py +0 -0
  73. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/data_loader/TupleDataLoaderTupleABC.py +0 -0
  74. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/data_loader/TupleDataLoaderTuples.py +0 -0
  75. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/data_loader/__init__.py +0 -0
  76. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/event_bus/TupleChangeEventABC.py +0 -0
  77. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/event_bus/TupleChangeEventBus.py +0 -0
  78. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/event_bus/TupleChangeEventBusObserverABC.py +0 -0
  79. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/event_bus/__init__.py +0 -0
  80. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleActionProcessor.py +0 -0
  81. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleActionProcessorProxy.py +0 -0
  82. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleActionProcessorTestHandler.py +0 -0
  83. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleActionProcessor_shutdown_test.py +0 -0
  84. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleActionPushOffline.py +0 -0
  85. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleActionPushOffline_test.py +0 -0
  86. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataAccessGranterABC.py +0 -0
  87. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataActionClient.py +0 -0
  88. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataObservableCache.py +0 -0
  89. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataObservableHandler.py +0 -0
  90. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataObservableHandler_test.py +0 -0
  91. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataObservableTestHandler.py +0 -0
  92. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataObserverClient.py +0 -0
  93. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/TupleDataProviderABC.py +0 -0
  94. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/VortexJSTupleLoaderTestHandler.py +0 -0
  95. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/handler/__init__.py +0 -0
  96. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/mypy_plugin.py +0 -0
  97. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/payload_many_tuples_benchmark_test.py +0 -0
  98. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/py.typed +0 -0
  99. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/GzippedDataHttpClient.py +0 -0
  100. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/GzippedDataHttpClientTest.py +0 -0
  101. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/GzippedPayloadHttpClient.py +0 -0
  102. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/RestfulHttpClient.py +0 -0
  103. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/RestfulResource.py +0 -0
  104. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/__init__.py +0 -0
  105. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/restful_http_client_test.py +0 -0
  106. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/restful/restful_resource_test.py +0 -0
  107. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/rpc/RPC.py +0 -0
  108. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/rpc/RPCTest.py +0 -0
  109. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/rpc/RPC_test.py +0 -0
  110. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/rpc/__init__.py +0 -0
  111. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/sqla_orm/OrmCrudHandler.py +0 -0
  112. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/sqla_orm/TupleProviderForDb.py +0 -0
  113. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/sqla_orm/VortexTupleJson.py +0 -0
  114. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/sqla_orm/__init__.py +0 -0
  115. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/storage/TupleActionStorageSqlite.py +0 -0
  116. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/storage/TupleStorageSqlite.py +0 -0
  117. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/storage/__init__.py +0 -0
  118. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/tuple_json_schema/__init__.py +0 -0
  119. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/tuple_json_schema/constants.py +0 -0
  120. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/tuple_json_schema/json_schema_node_tuple.py +0 -0
  121. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/tuple_json_schema/tuple_json_schema_builder.py +0 -0
  122. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/tuple_json_schema/tuple_json_schema_test.py +0 -0
  123. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/tuple_json_schema/tuple_json_schema_test_tuples.py +0 -0
  124. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/twisted_runtime_metrics.py +0 -0
  125. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortex/twisted_runtime_metrics_test.py +0 -0
  126. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortexpy.egg-info/SOURCES.txt +0 -0
  127. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortexpy.egg-info/dependency_links.txt +0 -0
  128. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortexpy.egg-info/entry_points.txt +0 -0
  129. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortexpy.egg-info/requires.txt +0 -0
  130. {vortexpy-4.3.4 → vortexpy-4.3.5}/vortexpy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortexpy
3
- Version: 4.3.4
3
+ Version: 4.3.5
4
4
  Summary: Flying Ion's observable, routable, data serialisation and transport code.
5
5
  Author-email: Flying Ion Pty Ltd <contact@flyingion.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vortexpy"
7
- version = "4.3.4"
7
+ version = "4.3.5"
8
8
  license = {text = "MIT"}
9
9
  authors = [{name = "Flying Ion Pty Ltd", email = "contact@flyingion.com"}]
10
10
  description = "Flying Ion's observable, routable, data serialisation and transport code."
@@ -2,6 +2,7 @@
2
2
  # See LICENSE for the full license text.
3
3
 
4
4
  from datetime import UTC, datetime
5
+ from unittest.mock import patch
5
6
 
6
7
  from ddt import data, ddt
7
8
  import unittest
@@ -12,6 +13,7 @@ from vortex.tuple_test_fixtures import (
12
13
  BsonEnumTestTuple,
13
14
  BsonObjectTypeE,
14
15
  BsonOptionalDatetimeTestTuple,
16
+ BsonOptionalFloatTestTuple,
15
17
  BsonPriorityE,
16
18
  BsonTestTuple,
17
19
  makeBsonTestTuple,
@@ -49,6 +51,28 @@ class TupleBsonTest(unittest.TestCase):
49
51
  deserialisedTuple = self._testSerialisation(origTuple)
50
52
  self.assertEqual(TupleHash(origTuple), TupleHash(deserialisedTuple))
51
53
 
54
+ def testBsonIntegerParsesAsOptionalFloatField(self) -> None:
55
+ deserialisedTuple = Tuple.bsonJsonDictToTupleWithValidation(
56
+ dict(requiredFloat=151, optionalFloat=0),
57
+ BsonOptionalFloatTestTuple,
58
+ )
59
+ self.assertEqual(deserialisedTuple.requiredFloat, 151.0)
60
+ self.assertEqual(deserialisedTuple.optionalFloat, 0.0)
61
+
62
+ def testBsonFractionalDoubleParsesAsIntFieldWithWarning(self) -> None:
63
+ bsonDict = validBsonDict()
64
+ bsonDict["aInt"] = 151.7
65
+ with patch(
66
+ "vortex.TupleDocumentConverter.logger.warning"
67
+ ) as mockWarning:
68
+ deserialisedTuple = Tuple.bsonJsonDictToTupleWithValidation(
69
+ bsonDict, BsonTestTuple
70
+ )
71
+ self.assertEqual(deserialisedTuple.aInt, 151)
72
+ mockWarning.assert_called_once()
73
+ warningMessage = mockWarning.call_args[0][0]
74
+ self.assertIn("lost precision", warningMessage)
75
+
52
76
  def testDatetimeRoundTrip(self) -> None:
53
77
  origTuple = BsonOptionalDatetimeTestTuple()
54
78
  origTuple.requiredDate = datetime(2020, 1, 2, 3, 4, 5, 678000, tzinfo=UTC)
@@ -15,6 +15,7 @@ Subclasses supply datetime and plain-dict coercion at the document boundary.
15
15
  from __future__ import annotations
16
16
 
17
17
  import enum
18
+ import logging
18
19
  from typing import TYPE_CHECKING, Annotated, Any, NamedTuple, Optional, Type
19
20
 
20
21
  from typing import Union, get_args
@@ -29,6 +30,8 @@ from .Tuple import TupleFieldValidatorABC
29
30
  if TYPE_CHECKING:
30
31
  from .Tuple import Tuple
31
32
 
33
+ logger = logging.getLogger(__name__)
34
+
32
35
  _UNSET = object()
33
36
  _ROOT_PATH = "$"
34
37
 
@@ -287,6 +290,90 @@ class TupleDocumentConverterBase:
287
290
 
288
291
  return value, None
289
292
 
293
+ @classmethod
294
+ def _scalarTypeFromHint(cls, typeHint: Any, scalarType: type) -> bool:
295
+ """Return whether typeHint is ``scalarType`` or ``scalarType | None`` only."""
296
+ if typeHint is scalarType:
297
+ return True
298
+
299
+ unionArgs = get_args(typeHint)
300
+ if not unionArgs:
301
+ return False
302
+
303
+ nonNoneArgs = [
304
+ unionArg for unionArg in unionArgs if unionArg is not type(None)
305
+ ]
306
+ return len(nonNoneArgs) == 1 and nonNoneArgs[0] is scalarType
307
+
308
+ @classmethod
309
+ def _floatTypeFromHint(cls, typeHint: Any) -> bool:
310
+ """Return whether typeHint accepts float (including ``float | None``)."""
311
+ return cls._scalarTypeFromHint(typeHint, float)
312
+
313
+ @classmethod
314
+ def _tryParseFloatFieldValue(
315
+ cls,
316
+ field_path: str,
317
+ fieldName: str,
318
+ TupleClass: Type[Tuple],
319
+ typeHint: Any,
320
+ value: Any,
321
+ ) -> tuple[Any, TupleValidationError | None]:
322
+ """Parse JSON integer tokens as ``float`` when the field type is ``float``."""
323
+ if value is None:
324
+ return value, None
325
+
326
+ if not cls._floatTypeFromHint(typeHint):
327
+ return value, None
328
+
329
+ if isinstance(value, float):
330
+ return value, None
331
+
332
+ if isinstance(value, int) and not isinstance(value, bool):
333
+ return float(value), None
334
+
335
+ return value, None
336
+
337
+ @classmethod
338
+ def _intTypeFromHint(cls, typeHint: Any) -> bool:
339
+ """Return whether typeHint accepts int (including ``int | None`` only)."""
340
+ return cls._scalarTypeFromHint(typeHint, int)
341
+
342
+ @classmethod
343
+ def _tryParseIntFieldValue(
344
+ cls,
345
+ field_path: str,
346
+ fieldName: str,
347
+ TupleClass: Type[Tuple],
348
+ typeHint: Any,
349
+ value: Any,
350
+ ) -> tuple[Any, TupleValidationError | None]:
351
+ """Parse JSON float tokens as ``int`` when the field type is ``int``."""
352
+ if value is None:
353
+ return value, None
354
+
355
+ if not cls._intTypeFromHint(typeHint):
356
+ return value, None
357
+
358
+ if isinstance(value, int) and not isinstance(value, bool):
359
+ return value, None
360
+
361
+ if isinstance(value, float):
362
+ parsedInt = int(value)
363
+ if not value.is_integer():
364
+ logger.warning(
365
+ "At %s: parsing %s field %s lost precision converting "
366
+ "float %r to int %s",
367
+ field_path,
368
+ TupleClass.__name__,
369
+ fieldName,
370
+ value,
371
+ parsedInt,
372
+ )
373
+ return parsedInt, None
374
+
375
+ return value, None
376
+
290
377
  @classmethod
291
378
  def _nestedTupleTypeFromHint(cls, typeHint: Any) -> Any | None:
292
379
  """Return nested tuple class for a field hint, including ``X | None`` unions."""
@@ -413,6 +500,26 @@ class TupleDocumentConverterBase:
413
500
  if enumError:
414
501
  errors.append(enumError)
415
502
  continue
503
+ item, floatError = cls._tryParseFloatFieldValue(
504
+ item_path,
505
+ fieldName,
506
+ TupleClass,
507
+ listItemTypeHint,
508
+ item,
509
+ )
510
+ if floatError:
511
+ errors.append(floatError)
512
+ continue
513
+ item, intError = cls._tryParseIntFieldValue(
514
+ item_path,
515
+ fieldName,
516
+ TupleClass,
517
+ listItemTypeHint,
518
+ item,
519
+ )
520
+ if intError:
521
+ errors.append(intError)
522
+ continue
416
523
  itemError = cls._checkType(
417
524
  TupleClass,
418
525
  item_path,
@@ -578,6 +685,20 @@ class TupleDocumentConverterBase:
578
685
  errors.append(enumError)
579
686
  return _ConvertResult(_UNSET, errors)
580
687
 
688
+ value, floatError = cls._tryParseFloatFieldValue(
689
+ field_path, fieldName, TupleClass, typeHint, value
690
+ )
691
+ if floatError:
692
+ errors.append(floatError)
693
+ return _ConvertResult(_UNSET, errors)
694
+
695
+ value, intError = cls._tryParseIntFieldValue(
696
+ field_path, fieldName, TupleClass, typeHint, value
697
+ )
698
+ if intError:
699
+ errors.append(intError)
700
+ return _ConvertResult(_UNSET, errors)
701
+
581
702
  typeError = cls._checkType(
582
703
  TupleClass, field_path, fieldName, typeHint, value, validator
583
704
  )
@@ -3,6 +3,7 @@
3
3
 
4
4
  import json
5
5
  from datetime import UTC, datetime
6
+ from unittest.mock import patch
6
7
 
7
8
  from ddt import data, ddt
8
9
  import unittest
@@ -102,6 +103,40 @@ class TupleRestfulTest(unittest.TestCase):
102
103
  self.assertNotIn("aBoolFalse", jsonDict)
103
104
  self.assertIn("aBoolTrue", jsonDict)
104
105
 
106
+ def testRestfulIntegerJsonParsesAsFloatField(self) -> None:
107
+ jsonDict = _validRestfulJsonDict()
108
+ jsonDict["aFloat"] = 151
109
+ deserialisedTuple = Tuple.restfulJsonDictToTupleWithValidation(
110
+ jsonDict, RestfulTestTuple
111
+ )
112
+ self.assertEqual(deserialisedTuple.aFloat, 151.0)
113
+
114
+ def testRestfulWholeFloatJsonParsesAsIntField(self) -> None:
115
+ jsonDict = _validRestfulJsonDict()
116
+ jsonDict["aInt"] = 151.0
117
+ with patch(
118
+ "vortex.TupleDocumentConverter.logger.warning"
119
+ ) as mockWarning:
120
+ deserialisedTuple = Tuple.restfulJsonDictToTupleWithValidation(
121
+ jsonDict, RestfulTestTuple
122
+ )
123
+ self.assertEqual(deserialisedTuple.aInt, 151)
124
+ mockWarning.assert_not_called()
125
+
126
+ def testRestfulFractionalFloatJsonParsesAsIntFieldWithWarning(self) -> None:
127
+ jsonDict = _validRestfulJsonDict()
128
+ jsonDict["aInt"] = 151.7
129
+ with patch(
130
+ "vortex.TupleDocumentConverter.logger.warning"
131
+ ) as mockWarning:
132
+ deserialisedTuple = Tuple.restfulJsonDictToTupleWithValidation(
133
+ jsonDict, RestfulTestTuple
134
+ )
135
+ self.assertEqual(deserialisedTuple.aInt, 151)
136
+ mockWarning.assert_called_once()
137
+ warningMessage = mockWarning.call_args[0][0]
138
+ self.assertIn("lost precision", warningMessage)
139
+
105
140
  def testRestfulConverterClassMethodsMatchTupleDelegates(self) -> None:
106
141
  origTuple = makeRestfulTestTuple()
107
142
  viaTuple = origTuple.tupleToRestfulJsonDict()
@@ -6,4 +6,4 @@
6
6
  # Website : https://flyingion.com
7
7
  # Support : contact@flyingion.com
8
8
 
9
- __version__ = '4.3.4'
9
+ __version__ = '4.3.5'
@@ -174,6 +174,7 @@ class TupleDataObservableProxyHandler(TupleDataObservableCache):
174
174
  payloadEnvelope,
175
175
  vortexUuid,
176
176
  sendResponse,
177
+ vortexName,
177
178
  vortexSession=vortexSession,
178
179
  )
179
180
  if d:
@@ -186,6 +187,7 @@ class TupleDataObservableProxyHandler(TupleDataObservableCache):
186
187
  payloadEnvelope,
187
188
  vortexUuid,
188
189
  sendResponse,
190
+ vortexName,
189
191
  vortexSession=vortexSession,
190
192
  )
191
193
  if d:
@@ -199,6 +201,7 @@ class TupleDataObservableProxyHandler(TupleDataObservableCache):
199
201
  payloadEnvelope: PayloadEnvelope,
200
202
  vortexUuid: str,
201
203
  sendResponse: SendVortexMsgResponseCallable,
204
+ vortexName: str,
202
205
  vortexSession=None,
203
206
  ):
204
207
 
@@ -210,6 +213,7 @@ class TupleDataObservableProxyHandler(TupleDataObservableCache):
210
213
  payloadEnvelope=payloadEnvelope,
211
214
  vortexUuid=vortexUuid,
212
215
  sendResponse=sendResponse,
216
+ vortexName=vortexName,
213
217
  vortexSession=vortexSession,
214
218
  )
215
219
 
@@ -149,6 +149,14 @@ class BsonEnumTestTuple(Tuple):
149
149
  priorities: List[BsonPriorityE] = TupleField()
150
150
 
151
151
 
152
+ @addTupleType
153
+ class BsonOptionalFloatTestTuple(Tuple):
154
+ __tupleType__ = "flyingion.vortex.BsonOptionalFloatTestTuple"
155
+
156
+ requiredFloat: float = TupleField()
157
+ optionalFloat: float | None = TupleField()
158
+
159
+
152
160
  @addTupleType
153
161
  class BsonOptionalDatetimeTestTuple(Tuple):
154
162
  __tupleType__ = "flyingion.vortex.BsonOptionalDatetimeTestTuple"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortexpy
3
- Version: 4.3.4
3
+ Version: 4.3.5
4
4
  Summary: Flying Ion's observable, routable, data serialisation and transport code.
5
5
  Author-email: Flying Ion Pty Ltd <contact@flyingion.com>
6
6
  License: MIT
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