vortexpy 4.1.5__tar.gz → 4.1.7__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 (111) hide show
  1. {vortexpy-4.1.5 → vortexpy-4.1.7}/PKG-INFO +1 -1
  2. {vortexpy-4.1.5 → vortexpy-4.1.7}/pyproject.toml +1 -1
  3. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadEndpoint.py +4 -0
  4. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadIO.py +9 -2
  5. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadResponse.py +7 -3
  6. vortexpy-4.1.7/vortex/PayloadResponse_test.py +42 -0
  7. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/__init__.py +1 -1
  8. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleActionProcessorProxy.py +1 -1
  9. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableProxyHandler.py +1 -1
  10. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataObserverClient.py +2 -2
  11. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/rpc/RPC.py +50 -4
  12. vortexpy-4.1.7/vortex/rpc/RPC_test.py +114 -0
  13. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortexpy.egg-info/PKG-INFO +1 -1
  14. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortexpy.egg-info/SOURCES.txt +2 -0
  15. {vortexpy-4.1.5 → vortexpy-4.1.7}/README.md +0 -0
  16. {vortexpy-4.1.5 → vortexpy-4.1.7}/setup.cfg +0 -0
  17. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/DataWrapTuple.py +0 -0
  18. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/DeferUtil.py +0 -0
  19. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/Jsonable.py +0 -0
  20. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/Payload.py +0 -0
  21. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadEndpointTest.py +0 -0
  22. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadEnvelope.py +0 -0
  23. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadFilterKeys.py +0 -0
  24. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadPriority.py +0 -0
  25. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadResponseTest.py +0 -0
  26. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PayloadTest.py +0 -0
  27. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/PerformTestActionTuple.py +0 -0
  28. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/SerialiseUtil.py +0 -0
  29. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/SerialiseUtil_test.py +0 -0
  30. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TestTuple.py +0 -0
  31. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/Tuple.py +0 -0
  32. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TupleAction.py +0 -0
  33. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TupleDataForTest.py +0 -0
  34. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TuplePerfTest.py +0 -0
  35. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TuplePolymorphicTupleTypeFieldArgTest.py +0 -0
  36. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TupleRestful.py +0 -0
  37. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TupleRestfulTest.py +0 -0
  38. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TupleSelector.py +0 -0
  39. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/TupleTest.py +0 -0
  40. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexABC.py +0 -0
  41. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexAccessControlContextABC.py +0 -0
  42. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexAuthenticatorABC.py +0 -0
  43. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexClientHttp.py +0 -0
  44. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexClientHttpTest.py +0 -0
  45. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexClientTcp.py +0 -0
  46. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexClientWebsocketFactory.py +0 -0
  47. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexConnectionABC.py +0 -0
  48. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexEndpointPermission.py +0 -0
  49. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexFactory.py +0 -0
  50. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexPayloadProtocol.py +0 -0
  51. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexPermission.py +0 -0
  52. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexPermissionError.py +0 -0
  53. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexServer.py +0 -0
  54. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexServerConnection.py +0 -0
  55. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexServerHttpResource.py +0 -0
  56. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexServerTcp.py +0 -0
  57. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexServerWebsocket.py +0 -0
  58. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexSession.py +0 -0
  59. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexTcpMemoryLeakPayloadTest.py +0 -0
  60. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexTcpMemoryLeakRawTest.py +0 -0
  61. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexTest.py +0 -0
  62. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexUtil.py +0 -0
  63. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/VortexWritePushProducer.py +0 -0
  64. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLiveLockManager.py +0 -0
  65. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoader.py +0 -0
  66. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoaderDelegate.py +0 -0
  67. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoaderTupleABC.py +0 -0
  68. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoaderTuples.py +0 -0
  69. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/data_loader/__init__.py +0 -0
  70. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/event_bus/TupleChangeEventABC.py +0 -0
  71. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/event_bus/TupleChangeEventBus.py +0 -0
  72. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/event_bus/TupleChangeEventBusObserverABC.py +0 -0
  73. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/event_bus/__init__.py +0 -0
  74. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleActionProcessor.py +0 -0
  75. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleActionProcessorTestHandler.py +0 -0
  76. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataAccessGranterABC.py +0 -0
  77. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataActionClient.py +0 -0
  78. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableCache.py +0 -0
  79. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableHandler.py +0 -0
  80. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableTestHandler.py +0 -0
  81. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/TupleDataProviderABC.py +0 -0
  82. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/VortexJSTupleLoaderTestHandler.py +0 -0
  83. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/handler/__init__.py +0 -0
  84. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/mypy_plugin.py +0 -0
  85. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/py.typed +0 -0
  86. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/GzippedDataHttpClient.py +0 -0
  87. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/GzippedDataHttpClientTest.py +0 -0
  88. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/GzippedPayloadHttpClient.py +0 -0
  89. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/RestfulHttpClient.py +0 -0
  90. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/RestfulHttpClientTest.py +0 -0
  91. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/RestfulResource.py +0 -0
  92. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/RestfulResourceTest.py +0 -0
  93. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/restful/__init__.py +0 -0
  94. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/rpc/RPCTest.py +0 -0
  95. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/rpc/__init__.py +0 -0
  96. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/sqla_orm/OrmCrudHandler.py +0 -0
  97. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/sqla_orm/TupleProviderForDb.py +0 -0
  98. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/sqla_orm/VortexTupleJson.py +0 -0
  99. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/sqla_orm/__init__.py +0 -0
  100. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/storage/TupleStorageSqlite.py +0 -0
  101. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/storage/__init__.py +0 -0
  102. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/tuple_json_schema/__init__.py +0 -0
  103. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/tuple_json_schema/constants.py +0 -0
  104. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/tuple_json_schema/json_schema_node_tuple.py +0 -0
  105. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/tuple_json_schema/tuple_json_schema_builder.py +0 -0
  106. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/tuple_json_schema/tuple_json_schema_test.py +0 -0
  107. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortex/tuple_json_schema/tuple_json_schema_test_tuples.py +0 -0
  108. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortexpy.egg-info/dependency_links.txt +0 -0
  109. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortexpy.egg-info/entry_points.txt +0 -0
  110. {vortexpy-4.1.5 → vortexpy-4.1.7}/vortexpy.egg-info/requires.txt +0 -0
  111. {vortexpy-4.1.5 → vortexpy-4.1.7}/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.1.5
3
+ Version: 4.1.7
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.1.5"
7
+ version = "4.1.7"
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."
@@ -71,6 +71,7 @@ class PayloadEndpoint:
71
71
  acceptOnlyFromVortex: Optional[Union[str, tuple]] = None,
72
72
  ignoreFromVortex: Optional[Union[str, tuple]] = None,
73
73
  requiredPermissions: Optional[PermissionsType] = None,
74
+ logProcessException: bool = True,
74
75
  ) -> None:
75
76
  """
76
77
  :param filt: The filter to match against payloads
@@ -82,10 +83,13 @@ class PayloadEndpoint:
82
83
  the caller's session must hold. Pass an empty frozenset to
83
84
  explicitly declare "no permissions required". Omitting the
84
85
  parameter (``None``) emits a deprecation warning.
86
+ :param logProcessException: When True, handler exceptions log at ERROR.
87
+ When False, the same events log at DEBUG with neutral wording.
85
88
  """
86
89
  self._requiredPermissions: Optional[PermissionsType] = (
87
90
  requiredPermissions
88
91
  )
92
+ self._logProcessException = logProcessException
89
93
 
90
94
  if requiredPermissions is None:
91
95
  logger.warning(self._NO_PERMISSIONS_WARNING, filt)
@@ -115,8 +115,15 @@ class PayloadIO(object):
115
115
  except Exception as e:
116
116
  logger.exception(e)
117
117
 
118
- vortexLogFailure(failure, logger)
119
- logger.error(payloadEnvelope.filt)
118
+ if endpoint._logProcessException:
119
+ vortexLogFailure(failure, logger)
120
+ logger.error(payloadEnvelope.filt)
121
+ else:
122
+ logger.debug(
123
+ "Payload handler response for filt=%s: %s",
124
+ payloadEnvelope.filt,
125
+ failure.getErrorMessage(),
126
+ )
120
127
 
121
128
  def callback(value, blocking=False):
122
129
  # A blocking call taking more than 100ms is BAD
@@ -67,7 +67,7 @@ class PayloadResponse(Deferred):
67
67
  destVortexUuid: Optional[str] = None,
68
68
  timeout: Optional[float] = None,
69
69
  resultCheck=True,
70
- logTimeoutError=True,
70
+ logTimeoutException=True,
71
71
  ) -> None:
72
72
  """Constructor
73
73
 
@@ -81,6 +81,8 @@ class PayloadResponse(Deferred):
81
81
  :param timeout The timeout to wait for a response
82
82
  :param resultCheck Should the response payload.result be checked, if it fails
83
83
  it errback will be called.
84
+ :param logTimeoutException: When True, payload timeouts log at ERROR.
85
+ When False, timeouts log at DEBUG with neutral wording.
84
86
  """
85
87
  Deferred.__init__(self)
86
88
 
@@ -88,7 +90,7 @@ class PayloadResponse(Deferred):
88
90
  timeout = self.TIMEOUT
89
91
 
90
92
  self._resultCheck = resultCheck
91
- self._logTimeoutError = logTimeoutError
93
+ self._logTimeoutException = logTimeoutException
92
94
 
93
95
  # uuid4 can have collisions
94
96
  self._messageId = str(uuid4()) + str(PayloadResponse.__SEQ)
@@ -142,8 +144,10 @@ class PayloadResponse(Deferred):
142
144
  self._endpoint.shutdown()
143
145
  self._endpoint = None
144
146
 
145
- if self._logTimeoutError:
147
+ if self._logTimeoutException:
146
148
  logger.error("Timed out for payload %s", self._filt)
149
+ else:
150
+ logger.debug("Payload response timed out for %s", self._filt)
147
151
  self._status = self.TIMED_OUT
148
152
  return failure
149
153
 
@@ -0,0 +1,42 @@
1
+ import unittest
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ from twisted.internet.defer import TimeoutError
5
+ from twisted.python.failure import Failure
6
+
7
+ from vortex.PayloadEnvelope import PayloadEnvelope
8
+ from vortex.PayloadResponse import PayloadResponse
9
+
10
+
11
+ class PayloadResponseLoggingTest(unittest.TestCase):
12
+ @patch("vortex.PayloadResponse.VortexFactory.sendVortexMsg")
13
+ @patch("vortex.PayloadResponse.logger")
14
+ def testTimeoutLogsDebugWhenLogTimeoutExceptionFalse(
15
+ self, mockLogger: MagicMock, _mockSend: MagicMock
16
+ ) -> None:
17
+ pr = PayloadResponse(
18
+ PayloadEnvelope(filt={"key": "example"}),
19
+ timeout=30.0,
20
+ logTimeoutException=False,
21
+ )
22
+ pr._timedOut(Failure(TimeoutError("timed out")))
23
+ mockLogger.error.assert_not_called()
24
+ mockLogger.debug.assert_called_once()
25
+
26
+ @patch("vortex.PayloadResponse.VortexFactory.sendVortexMsg")
27
+ @patch("vortex.PayloadResponse.logger")
28
+ def testTimeoutLogsErrorWhenLogTimeoutExceptionTrue(
29
+ self, mockLogger: MagicMock, _mockSend: MagicMock
30
+ ) -> None:
31
+ pr = PayloadResponse(
32
+ PayloadEnvelope(filt={"key": "example"}),
33
+ timeout=30.0,
34
+ logTimeoutException=True,
35
+ )
36
+ pr._timedOut(Failure(TimeoutError("timed out")))
37
+ mockLogger.debug.assert_not_called()
38
+ mockLogger.error.assert_called_once()
39
+
40
+
41
+ if __name__ == "__main__":
42
+ unittest.main()
@@ -6,4 +6,4 @@
6
6
  # Website : https://flyingion.com
7
7
  # Support : contact@flyingion.com
8
8
 
9
- __version__ = '4.1.5'
9
+ __version__ = '4.1.7'
@@ -167,7 +167,7 @@ class TupleActionProcessorProxy:
167
167
  payloadEnvelope,
168
168
  timeout=PayloadResponse.TIMEOUT - 5, # 5 seconds less
169
169
  resultCheck=False,
170
- logTimeoutError=False,
170
+ logTimeoutException=False,
171
171
  )
172
172
 
173
173
  # This is not a lambda, so that it can have a breakpoint
@@ -355,7 +355,7 @@ class TupleDataObservableProxyHandler(TupleDataObservableCache):
355
355
  pr = PayloadResponse(
356
356
  payloadEnvelope,
357
357
  timeout=PayloadResponse.TIMEOUT - 5, # 5 seconds less
358
- logTimeoutError=False,
358
+ logTimeoutException=False,
359
359
  )
360
360
 
361
361
  pr.addErrback(self._handlePrFailure, tupleSelector)
@@ -83,7 +83,7 @@ class TupleDataObserverClient(TupleDataObservableCache):
83
83
  TupleDataObservableCache.shutdown(self)
84
84
 
85
85
  def pollForTuples(
86
- self, tupleSelector: TupleSelector, logTimeoutError: bool = True
86
+ self, tupleSelector: TupleSelector, logTimeoutException: bool = True
87
87
  ) -> Deferred:
88
88
  startFilt = copy(self._sendFilt)
89
89
  startFilt.update({"subscribe": False, "tupleSelector": tupleSelector})
@@ -97,7 +97,7 @@ class TupleDataObserverClient(TupleDataObservableCache):
97
97
  pr = PayloadResponse(
98
98
  payloadEnvelope=PayloadEnvelope(startFilt),
99
99
  destVortexName=self._destVortexName,
100
- logTimeoutError=logTimeoutError,
100
+ logTimeoutException=logTimeoutException,
101
101
  )
102
102
  pr.addCallback(updateCacheCallback)
103
103
  pr.addCallback(
@@ -21,7 +21,7 @@ from twisted.internet.defer import (
21
21
  from twisted.internet.threads import deferToThread
22
22
  from twisted.python.failure import Failure
23
23
 
24
- from vortex.DeferUtil import yesMainThread
24
+ from vortex.DeferUtil import yesMainThread, vortexLogFailure
25
25
  from vortex.Payload import Payload
26
26
  from vortex.PayloadEndpoint import PayloadEndpoint
27
27
  from vortex.PayloadEnvelope import PayloadEnvelope
@@ -103,6 +103,8 @@ class _VortexRPC:
103
103
  deferToThread: bool,
104
104
  inlineCallbacks: bool,
105
105
  requiredPermissions: Optional[PermissionsType] = None,
106
+ logTimeoutException: bool = True,
107
+ logRpcException: bool = True,
106
108
  ) -> None:
107
109
  """
108
110
 
@@ -124,6 +126,15 @@ class _VortexRPC:
124
126
  :param inlineCallbacks: Should the function be wrapped in the twisted
125
127
  @inlinecallbacks decorator before it's called?.
126
128
 
129
+ :param logTimeoutException: When True, RPC timeouts log at ERROR in
130
+ PayloadResponse. When False, timeouts log at DEBUG with neutral
131
+ wording. Callers that expect timeouts (e.g. backend readiness
132
+ probes) should leave this False.
133
+
134
+ :param logRpcException: When True, RPC handler and caller paths log
135
+ exceptions at ERROR. When False, the same events log at DEBUG
136
+ with neutral wording so log analysers are not triggered.
137
+
127
138
  """
128
139
 
129
140
  self.__func = func
@@ -138,6 +149,8 @@ class _VortexRPC:
138
149
  self.__requiredPermissions: Optional[PermissionsType] = (
139
150
  requiredPermissions
140
151
  )
152
+ self.__logTimeoutException = logTimeoutException
153
+ self.__logRpcException = logRpcException
141
154
 
142
155
  self.__funcAcceptsAccessControlContext = (
143
156
  checkAccessControlContextSignature(func)
@@ -176,6 +189,7 @@ class _VortexRPC:
176
189
  self._filt,
177
190
  self._processCall,
178
191
  requiredPermissions=self.__requiredPermissions,
192
+ logProcessException=self.__logRpcException,
179
193
  )
180
194
  logger.debug("RPC %s listening", self.__funcName)
181
195
 
@@ -315,7 +329,9 @@ class _VortexRPC:
315
329
  payloadEnvelope,
316
330
  timeout=self.__timeoutSeconds,
317
331
  resultCheck=False,
318
- logTimeoutError=False,
332
+ logTimeoutException=(
333
+ self.__logRpcException and self.__logTimeoutException
334
+ ),
319
335
  destVortexName=vortexName,
320
336
  destVortexUuid=vortexUuid,
321
337
  )
@@ -340,6 +356,18 @@ class _VortexRPC:
340
356
  """
341
357
 
342
358
  if not payloadEnvelope.result in (None, True):
359
+ if self.__logRpcException:
360
+ logger.error(
361
+ "Received RPC failure for %s: %s",
362
+ self.__funcName,
363
+ payloadEnvelope.result,
364
+ )
365
+ else:
366
+ logger.debug(
367
+ "RPC response for %s: %s",
368
+ self.__funcName,
369
+ payloadEnvelope.result,
370
+ )
343
371
  return Failure(
344
372
  Exception(payloadEnvelope.result).with_traceback(stack),
345
373
  exc_tb=stack,
@@ -366,8 +394,6 @@ class _VortexRPC:
366
394
  """
367
395
 
368
396
  if failure.check(TimeoutError):
369
- logger.error("Received RPC timeout for %s", self.__funcName)
370
-
371
397
  return Failure(
372
398
  Exception(
373
399
  "RPC call timed out for %s", self.__funcName
@@ -375,6 +401,15 @@ class _VortexRPC:
375
401
  exc_tb=stack,
376
402
  )
377
403
 
404
+ if not failure.check(TimeoutError) and self.__logRpcException:
405
+ vortexLogFailure(failure, logger)
406
+ elif not failure.check(TimeoutError):
407
+ logger.debug(
408
+ "RPC response for %s: %s",
409
+ self.__funcName,
410
+ failure.getErrorMessage(),
411
+ )
412
+
378
413
  return failure
379
414
 
380
415
  def callLocally(
@@ -423,6 +458,8 @@ def vortexRPC(
423
458
  deferToThread: bool = False,
424
459
  inlineCallbacks: bool = False,
425
460
  requiredPermissions: Optional[PermissionsType] = None,
461
+ logTimeoutException: bool = False,
462
+ logRpcException: bool = True,
426
463
  ):
427
464
  """Vortex RPC Decorator
428
465
 
@@ -444,6 +481,13 @@ def vortexRPC(
444
481
  :param inlineCallbacks: Should the function be wrapped in the twisted
445
482
  @inlinecallbacks decorator before it's called?.
446
483
 
484
+ :param logTimeoutException: When True, RPC timeouts log at ERROR.
485
+ When False, timeouts log at DEBUG with neutral wording.
486
+
487
+ :param logRpcException: When True, RPC handler and caller paths log at
488
+ ERROR. When False, the same events log at DEBUG with neutral
489
+ wording.
490
+
447
491
  :return A wrapped function, that will now work as an RPC call.
448
492
 
449
493
 
@@ -485,6 +529,8 @@ def vortexRPC(
485
529
  deferToThread,
486
530
  inlineCallbacks,
487
531
  requiredPermissions=requiredPermissions,
532
+ logTimeoutException=logTimeoutException,
533
+ logRpcException=logRpcException,
488
534
  )
489
535
 
490
536
  return decorator
@@ -0,0 +1,114 @@
1
+ import unittest
2
+ from types import SimpleNamespace
3
+ from unittest.mock import ANY, MagicMock, patch
4
+
5
+ from twisted.internet.defer import TimeoutError
6
+ from twisted.python.failure import Failure
7
+
8
+ from vortex.PayloadEndpoint import PayloadEndpoint
9
+ from vortex.PayloadEnvelope import PayloadEnvelope
10
+ from vortex.rpc.RPC import _VortexRPC
11
+
12
+
13
+ def _makeFunc(qualname: str = "testFunc") -> MagicMock:
14
+ func = MagicMock()
15
+ func.__qualname__ = qualname
16
+ func.__globals__ = {"__spec__": SimpleNamespace(name="vortex.rpc.RPC_test")}
17
+ return func
18
+
19
+
20
+ class RpcLoggingTest(unittest.TestCase):
21
+ def setUp(self) -> None:
22
+ _VortexRPC._VortexRPC__registeredFuncNames.clear()
23
+
24
+ def _makeRpc(
25
+ self,
26
+ *,
27
+ logRpcException: bool = True,
28
+ logTimeoutException: bool = False,
29
+ qualname: str = "testFunc",
30
+ ) -> _VortexRPC:
31
+ return _VortexRPC(
32
+ _makeFunc(qualname),
33
+ "listenVortexName",
34
+ 5.0,
35
+ None,
36
+ {},
37
+ False,
38
+ False,
39
+ logTimeoutException=logTimeoutException,
40
+ logRpcException=logRpcException,
41
+ )
42
+
43
+ @patch("vortex.rpc.RPC.logger")
44
+ def testRemoteResultLogsDebugWhenLogRpcExceptionFalse(
45
+ self, mockLogger: MagicMock
46
+ ) -> None:
47
+ rpc = self._makeRpc(logRpcException=False, qualname="testFuncRemote")
48
+ payloadEnvelope = PayloadEnvelope(filt={}, result="probe not ready")
49
+
50
+ rpc._processResponseCallback(payloadEnvelope, None)
51
+
52
+ mockLogger.error.assert_not_called()
53
+ mockLogger.debug.assert_called_once_with(
54
+ "RPC response for %s: %s",
55
+ rpc._VortexRPC__funcName,
56
+ "probe not ready",
57
+ )
58
+
59
+ @patch("vortex.rpc.RPC.logger")
60
+ def testRemoteResultLogsErrorWhenLogRpcExceptionTrue(
61
+ self, mockLogger: MagicMock
62
+ ) -> None:
63
+ rpc = self._makeRpc(logRpcException=True, qualname="testFuncRemote2")
64
+ payloadEnvelope = PayloadEnvelope(filt={}, result="probe not ready")
65
+
66
+ rpc._processResponseCallback(payloadEnvelope, None)
67
+
68
+ mockLogger.debug.assert_not_called()
69
+ mockLogger.error.assert_called_once()
70
+
71
+ @patch("vortex.rpc.RPC.logger")
72
+ def testTimeoutErrbackDoesNotLogWhenPayloadResponseAlreadyDid(
73
+ self, mockLogger: MagicMock
74
+ ) -> None:
75
+ rpc = self._makeRpc(logRpcException=False, logTimeoutException=True)
76
+ rpc._processResponseErrback(Failure(TimeoutError("timed out")), None)
77
+ mockLogger.error.assert_not_called()
78
+ mockLogger.debug.assert_not_called()
79
+
80
+ @patch("vortex.rpc.RPC.vortexLogFailure")
81
+ @patch("vortex.rpc.RPC.logger")
82
+ def testCallerExceptionLogsDebugWhenLogRpcExceptionFalse(
83
+ self, mockLogger: MagicMock, mockLogFailure: MagicMock
84
+ ) -> None:
85
+ rpc = self._makeRpc(logRpcException=False, qualname="testFunc2")
86
+ rpc._processResponseErrback(Failure(ValueError("boom")), None)
87
+ mockLogFailure.assert_not_called()
88
+ mockLogger.error.assert_not_called()
89
+ mockLogger.debug.assert_called_once_with(
90
+ "RPC response for %s: %s",
91
+ rpc._VortexRPC__funcName,
92
+ "boom",
93
+ )
94
+
95
+ @patch("vortex.rpc.RPC.vortexLogFailure")
96
+ def testCallerExceptionLoggedWhenLogRpcExceptionTrue(
97
+ self, mockLogFailure: MagicMock
98
+ ) -> None:
99
+ rpc = self._makeRpc(logRpcException=True, qualname="testFunc3")
100
+ failure = Failure(ValueError("boom"))
101
+ rpc._processResponseErrback(failure, None)
102
+ mockLogFailure.assert_called_once_with(failure, ANY)
103
+
104
+ def testPayloadEndpointStoresLogProcessException(self) -> None:
105
+ endpoint = PayloadEndpoint(
106
+ dict(key="example"),
107
+ lambda **kwargs: None,
108
+ logProcessException=False,
109
+ )
110
+ self.assertFalse(endpoint._logProcessException)
111
+
112
+
113
+ if __name__ == "__main__":
114
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortexpy
3
- Version: 4.1.5
3
+ Version: 4.1.7
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
@@ -12,6 +12,7 @@ vortex/PayloadIO.py
12
12
  vortex/PayloadPriority.py
13
13
  vortex/PayloadResponse.py
14
14
  vortex/PayloadResponseTest.py
15
+ vortex/PayloadResponse_test.py
15
16
  vortex/PayloadTest.py
16
17
  vortex/PerformTestActionTuple.py
17
18
  vortex/SerialiseUtil.py
@@ -86,6 +87,7 @@ vortex/restful/RestfulResourceTest.py
86
87
  vortex/restful/__init__.py
87
88
  vortex/rpc/RPC.py
88
89
  vortex/rpc/RPCTest.py
90
+ vortex/rpc/RPC_test.py
89
91
  vortex/rpc/__init__.py
90
92
  vortex/sqla_orm/OrmCrudHandler.py
91
93
  vortex/sqla_orm/TupleProviderForDb.py
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