vortexpy 4.1.6__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.6 → vortexpy-4.1.7}/PKG-INFO +1 -1
  2. {vortexpy-4.1.6 → vortexpy-4.1.7}/pyproject.toml +1 -1
  3. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadEndpoint.py +4 -0
  4. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadIO.py +9 -2
  5. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadResponse.py +7 -3
  6. vortexpy-4.1.7/vortex/PayloadResponse_test.py +42 -0
  7. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/__init__.py +1 -1
  8. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleActionProcessorProxy.py +1 -1
  9. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableProxyHandler.py +1 -1
  10. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataObserverClient.py +2 -2
  11. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/rpc/RPC.py +48 -14
  12. vortexpy-4.1.7/vortex/rpc/RPC_test.py +114 -0
  13. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortexpy.egg-info/PKG-INFO +1 -1
  14. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortexpy.egg-info/SOURCES.txt +2 -0
  15. {vortexpy-4.1.6 → vortexpy-4.1.7}/README.md +0 -0
  16. {vortexpy-4.1.6 → vortexpy-4.1.7}/setup.cfg +0 -0
  17. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/DataWrapTuple.py +0 -0
  18. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/DeferUtil.py +0 -0
  19. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/Jsonable.py +0 -0
  20. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/Payload.py +0 -0
  21. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadEndpointTest.py +0 -0
  22. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadEnvelope.py +0 -0
  23. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadFilterKeys.py +0 -0
  24. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadPriority.py +0 -0
  25. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadResponseTest.py +0 -0
  26. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PayloadTest.py +0 -0
  27. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/PerformTestActionTuple.py +0 -0
  28. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/SerialiseUtil.py +0 -0
  29. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/SerialiseUtil_test.py +0 -0
  30. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TestTuple.py +0 -0
  31. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/Tuple.py +0 -0
  32. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TupleAction.py +0 -0
  33. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TupleDataForTest.py +0 -0
  34. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TuplePerfTest.py +0 -0
  35. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TuplePolymorphicTupleTypeFieldArgTest.py +0 -0
  36. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TupleRestful.py +0 -0
  37. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TupleRestfulTest.py +0 -0
  38. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TupleSelector.py +0 -0
  39. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/TupleTest.py +0 -0
  40. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexABC.py +0 -0
  41. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexAccessControlContextABC.py +0 -0
  42. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexAuthenticatorABC.py +0 -0
  43. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexClientHttp.py +0 -0
  44. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexClientHttpTest.py +0 -0
  45. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexClientTcp.py +0 -0
  46. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexClientWebsocketFactory.py +0 -0
  47. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexConnectionABC.py +0 -0
  48. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexEndpointPermission.py +0 -0
  49. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexFactory.py +0 -0
  50. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexPayloadProtocol.py +0 -0
  51. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexPermission.py +0 -0
  52. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexPermissionError.py +0 -0
  53. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexServer.py +0 -0
  54. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexServerConnection.py +0 -0
  55. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexServerHttpResource.py +0 -0
  56. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexServerTcp.py +0 -0
  57. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexServerWebsocket.py +0 -0
  58. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexSession.py +0 -0
  59. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexTcpMemoryLeakPayloadTest.py +0 -0
  60. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexTcpMemoryLeakRawTest.py +0 -0
  61. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexTest.py +0 -0
  62. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexUtil.py +0 -0
  63. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/VortexWritePushProducer.py +0 -0
  64. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLiveLockManager.py +0 -0
  65. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoader.py +0 -0
  66. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoaderDelegate.py +0 -0
  67. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoaderTupleABC.py +0 -0
  68. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/data_loader/TupleDataLoaderTuples.py +0 -0
  69. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/data_loader/__init__.py +0 -0
  70. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/event_bus/TupleChangeEventABC.py +0 -0
  71. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/event_bus/TupleChangeEventBus.py +0 -0
  72. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/event_bus/TupleChangeEventBusObserverABC.py +0 -0
  73. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/event_bus/__init__.py +0 -0
  74. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleActionProcessor.py +0 -0
  75. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleActionProcessorTestHandler.py +0 -0
  76. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataAccessGranterABC.py +0 -0
  77. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataActionClient.py +0 -0
  78. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableCache.py +0 -0
  79. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableHandler.py +0 -0
  80. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataObservableTestHandler.py +0 -0
  81. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/TupleDataProviderABC.py +0 -0
  82. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/VortexJSTupleLoaderTestHandler.py +0 -0
  83. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/handler/__init__.py +0 -0
  84. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/mypy_plugin.py +0 -0
  85. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/py.typed +0 -0
  86. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/GzippedDataHttpClient.py +0 -0
  87. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/GzippedDataHttpClientTest.py +0 -0
  88. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/GzippedPayloadHttpClient.py +0 -0
  89. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/RestfulHttpClient.py +0 -0
  90. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/RestfulHttpClientTest.py +0 -0
  91. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/RestfulResource.py +0 -0
  92. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/RestfulResourceTest.py +0 -0
  93. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/restful/__init__.py +0 -0
  94. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/rpc/RPCTest.py +0 -0
  95. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/rpc/__init__.py +0 -0
  96. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/sqla_orm/OrmCrudHandler.py +0 -0
  97. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/sqla_orm/TupleProviderForDb.py +0 -0
  98. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/sqla_orm/VortexTupleJson.py +0 -0
  99. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/sqla_orm/__init__.py +0 -0
  100. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/storage/TupleStorageSqlite.py +0 -0
  101. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/storage/__init__.py +0 -0
  102. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/tuple_json_schema/__init__.py +0 -0
  103. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/tuple_json_schema/constants.py +0 -0
  104. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/tuple_json_schema/json_schema_node_tuple.py +0 -0
  105. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/tuple_json_schema/tuple_json_schema_builder.py +0 -0
  106. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/tuple_json_schema/tuple_json_schema_test.py +0 -0
  107. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortex/tuple_json_schema/tuple_json_schema_test_tuples.py +0 -0
  108. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortexpy.egg-info/dependency_links.txt +0 -0
  109. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortexpy.egg-info/entry_points.txt +0 -0
  110. {vortexpy-4.1.6 → vortexpy-4.1.7}/vortexpy.egg-info/requires.txt +0 -0
  111. {vortexpy-4.1.6 → 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.6
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.6"
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.6'
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,7 +103,8 @@ class _VortexRPC:
103
103
  deferToThread: bool,
104
104
  inlineCallbacks: bool,
105
105
  requiredPermissions: Optional[PermissionsType] = None,
106
- logTimeoutError: bool = False,
106
+ logTimeoutException: bool = True,
107
+ logRpcException: bool = True,
107
108
  ) -> None:
108
109
  """
109
110
 
@@ -125,9 +126,14 @@ class _VortexRPC:
125
126
  :param inlineCallbacks: Should the function be wrapped in the twisted
126
127
  @inlinecallbacks decorator before it's called?.
127
128
 
128
- :param logTimeoutError: When False, RPC timeouts do not log at ERROR in
129
- PayloadResponse or the response errback. Callers that expect
130
- timeouts (e.g. backend readiness probes) should leave this False.
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.
131
137
 
132
138
  """
133
139
 
@@ -143,7 +149,8 @@ class _VortexRPC:
143
149
  self.__requiredPermissions: Optional[PermissionsType] = (
144
150
  requiredPermissions
145
151
  )
146
- self.__logTimeoutError = logTimeoutError
152
+ self.__logTimeoutException = logTimeoutException
153
+ self.__logRpcException = logRpcException
147
154
 
148
155
  self.__funcAcceptsAccessControlContext = (
149
156
  checkAccessControlContextSignature(func)
@@ -182,6 +189,7 @@ class _VortexRPC:
182
189
  self._filt,
183
190
  self._processCall,
184
191
  requiredPermissions=self.__requiredPermissions,
192
+ logProcessException=self.__logRpcException,
185
193
  )
186
194
  logger.debug("RPC %s listening", self.__funcName)
187
195
 
@@ -321,7 +329,9 @@ class _VortexRPC:
321
329
  payloadEnvelope,
322
330
  timeout=self.__timeoutSeconds,
323
331
  resultCheck=False,
324
- logTimeoutError=self.__logTimeoutError,
332
+ logTimeoutException=(
333
+ self.__logRpcException and self.__logTimeoutException
334
+ ),
325
335
  destVortexName=vortexName,
326
336
  destVortexUuid=vortexUuid,
327
337
  )
@@ -346,6 +356,18 @@ class _VortexRPC:
346
356
  """
347
357
 
348
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
+ )
349
371
  return Failure(
350
372
  Exception(payloadEnvelope.result).with_traceback(stack),
351
373
  exc_tb=stack,
@@ -372,9 +394,6 @@ class _VortexRPC:
372
394
  """
373
395
 
374
396
  if failure.check(TimeoutError):
375
- if self.__logTimeoutError:
376
- logger.error("Received RPC timeout for %s", self.__funcName)
377
-
378
397
  return Failure(
379
398
  Exception(
380
399
  "RPC call timed out for %s", self.__funcName
@@ -382,6 +401,15 @@ class _VortexRPC:
382
401
  exc_tb=stack,
383
402
  )
384
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
+
385
413
  return failure
386
414
 
387
415
  def callLocally(
@@ -430,7 +458,8 @@ def vortexRPC(
430
458
  deferToThread: bool = False,
431
459
  inlineCallbacks: bool = False,
432
460
  requiredPermissions: Optional[PermissionsType] = None,
433
- logTimeoutError: bool = False,
461
+ logTimeoutException: bool = False,
462
+ logRpcException: bool = True,
434
463
  ):
435
464
  """Vortex RPC Decorator
436
465
 
@@ -452,8 +481,12 @@ def vortexRPC(
452
481
  :param inlineCallbacks: Should the function be wrapped in the twisted
453
482
  @inlinecallbacks decorator before it's called?.
454
483
 
455
- :param logTimeoutError: When False, RPC timeouts do not log at ERROR.
456
- Defaults to False so callers can handle expected timeouts locally.
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.
457
490
 
458
491
  :return A wrapped function, that will now work as an RPC call.
459
492
 
@@ -496,7 +529,8 @@ def vortexRPC(
496
529
  deferToThread,
497
530
  inlineCallbacks,
498
531
  requiredPermissions=requiredPermissions,
499
- logTimeoutError=logTimeoutError,
532
+ logTimeoutException=logTimeoutException,
533
+ logRpcException=logRpcException,
500
534
  )
501
535
 
502
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.6
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