vortexpy 4.0.23__py3-none-any.whl

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 (87) hide show
  1. vortex/DataWrapTuple.py +27 -0
  2. vortex/DeferUtil.py +224 -0
  3. vortex/Jsonable.py +304 -0
  4. vortex/Payload.py +115 -0
  5. vortex/PayloadEndpoint.py +216 -0
  6. vortex/PayloadEndpointTest.py +164 -0
  7. vortex/PayloadEnvelope.py +166 -0
  8. vortex/PayloadFilterKeys.py +15 -0
  9. vortex/PayloadIO.py +152 -0
  10. vortex/PayloadPriority.py +11 -0
  11. vortex/PayloadResponse.py +173 -0
  12. vortex/PayloadResponseTest.py +39 -0
  13. vortex/PayloadTest.py +136 -0
  14. vortex/PerformTestActionTuple.py +30 -0
  15. vortex/SerialiseUtil.py +273 -0
  16. vortex/TestTuple.py +61 -0
  17. vortex/Tuple.py +968 -0
  18. vortex/TupleAction.py +72 -0
  19. vortex/TupleActionVortex.py +5 -0
  20. vortex/TupleDataForTest.py +25 -0
  21. vortex/TuplePerfTest.py +71 -0
  22. vortex/TuplePolymorphicTupleTypeFieldArgTest.py +74 -0
  23. vortex/TupleRestfulTest.py +140 -0
  24. vortex/TupleSelector.py +105 -0
  25. vortex/TupleTest.py +127 -0
  26. vortex/VortexABC.py +55 -0
  27. vortex/VortexClientHttp.py +308 -0
  28. vortex/VortexClientHttpTest.py +71 -0
  29. vortex/VortexClientTcp.py +355 -0
  30. vortex/VortexClientWebsocketFactory.py +383 -0
  31. vortex/VortexConnectionABC.py +88 -0
  32. vortex/VortexFactory.py +932 -0
  33. vortex/VortexPayloadProtocol.py +180 -0
  34. vortex/VortexServer.py +367 -0
  35. vortex/VortexServerConnection.py +127 -0
  36. vortex/VortexServerHttpResource.py +193 -0
  37. vortex/VortexServerTcp.py +104 -0
  38. vortex/VortexServerWebsocket.py +287 -0
  39. vortex/VortexTcpMemoryLeakPayloadTest.py +611 -0
  40. vortex/VortexTcpMemoryLeakRawTest.py +87 -0
  41. vortex/VortexTest.py +22 -0
  42. vortex/VortexUtil.py +314 -0
  43. vortex/VortexWritePushProducer.py +321 -0
  44. vortex/__init__.py +10 -0
  45. vortex/data_loader/TupleDataLiveLockManager.py +383 -0
  46. vortex/data_loader/TupleDataLoader.py +326 -0
  47. vortex/data_loader/TupleDataLoaderDelegate.py +90 -0
  48. vortex/data_loader/TupleDataLoaderTupleABC.py +10 -0
  49. vortex/data_loader/TupleDataLoaderTuples.py +53 -0
  50. vortex/data_loader/__init__.py +0 -0
  51. vortex/event_bus/TupleChangeEventABC.py +15 -0
  52. vortex/event_bus/TupleChangeEventBus.py +192 -0
  53. vortex/event_bus/TupleChangeEventBusObserverABC.py +11 -0
  54. vortex/event_bus/__init__.py +0 -0
  55. vortex/handler/TupleActionProcessor.py +270 -0
  56. vortex/handler/TupleActionProcessorProxy.py +218 -0
  57. vortex/handler/TupleActionProcessorTestHandler.py +27 -0
  58. vortex/handler/TupleDataActionClient.py +67 -0
  59. vortex/handler/TupleDataObservableCache.py +201 -0
  60. vortex/handler/TupleDataObservableHandler.py +371 -0
  61. vortex/handler/TupleDataObservableProxyHandler.py +340 -0
  62. vortex/handler/TupleDataObservableTestHandler.py +60 -0
  63. vortex/handler/TupleDataObserverClient.py +158 -0
  64. vortex/handler/VortexJSTupleLoaderTestHandler.py +42 -0
  65. vortex/handler/__init__.py +0 -0
  66. vortex/restful/GzippedDataHttpClient.py +170 -0
  67. vortex/restful/GzippedDataHttpClientTest.py +103 -0
  68. vortex/restful/GzippedPayloadHttpClient.py +181 -0
  69. vortex/restful/RestfulHttpClient.py +107 -0
  70. vortex/restful/RestfulHttpClientTest.py +60 -0
  71. vortex/restful/RestfulResource.py +153 -0
  72. vortex/restful/RestfulResourceTest.py +100 -0
  73. vortex/restful/__init__.py +0 -0
  74. vortex/rpc/RPC.py +446 -0
  75. vortex/rpc/RPCTest.py +12 -0
  76. vortex/rpc/__init__.py +0 -0
  77. vortex/sqla_orm/OrmCrudHandler.py +431 -0
  78. vortex/sqla_orm/TupleProviderForDb.py +42 -0
  79. vortex/sqla_orm/VortexTupleJson.py +24 -0
  80. vortex/sqla_orm/__init__.py +0 -0
  81. vortex/storage/TupleStorageSqlite.py +359 -0
  82. vortex/storage/__init__.py +0 -0
  83. vortexpy-4.0.23.dist-info/LICENSE +21 -0
  84. vortexpy-4.0.23.dist-info/METADATA +43 -0
  85. vortexpy-4.0.23.dist-info/RECORD +87 -0
  86. vortexpy-4.0.23.dist-info/WHEEL +5 -0
  87. vortexpy-4.0.23.dist-info/top_level.txt +1 -0
@@ -0,0 +1,11 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : http://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+
10
+ RPC_PRIORITY = 10
11
+ DEFAULT_PRIORITY = 100
@@ -0,0 +1,173 @@
1
+ import logging
2
+ import sys
3
+ from copy import copy
4
+ from datetime import datetime
5
+ from typing import Optional
6
+ from uuid import uuid4
7
+
8
+ import pytz
9
+ from twisted.internet import reactor
10
+ from twisted.internet.defer import Deferred
11
+ from twisted.python.failure import Failure
12
+
13
+ from vortex.PayloadEndpoint import PayloadEndpoint
14
+ from vortex.PayloadEnvelope import PayloadEnvelope
15
+ from vortex.VortexFactory import VortexFactory
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class PayloadResponse(Deferred):
21
+ """Payload Response
22
+
23
+ This class is used to catch responses from a sent payload.
24
+ If the remote end is going to send back a payload, with the same filt, this class can
25
+ be used to capture this can and call it's call back.
26
+
27
+ If the response is not received within the timeout, the errback is called.
28
+
29
+ Here is some example usage.
30
+
31
+ ::
32
+
33
+ logger = logging.getLogger("Example Payload Response")
34
+ payload = Payload(filt={"rapuiServerEcho":True})
35
+ responseDeferred = PayloadResponse(payload)
36
+ VortexFactory.sendVortexMsg(payload.toVortexMsg())
37
+ logger.info("Payload Sent")
38
+
39
+ responseDeferred.addCallbacks(logger.info, logger.error)
40
+
41
+ """
42
+
43
+ MESSAGE_ID_KEY = "PayloadResponse.messageId"
44
+
45
+ PROCESSING = "Processing"
46
+ # NO_ENDPOINT = "No Endpoint"
47
+ FAILED = "Failed"
48
+ SUCCESS = "Success"
49
+ TIMED_OUT = "Timed Out"
50
+
51
+ TIMEOUT: float = 30.00
52
+
53
+ __SEQ = 1
54
+
55
+ def __init__(
56
+ self,
57
+ payloadEnvelope: PayloadEnvelope,
58
+ destVortexName: Optional[str] = None,
59
+ destVortexUuid: Optional[str] = None,
60
+ timeout: Optional[float] = None,
61
+ resultCheck=True,
62
+ logTimeoutError=True,
63
+ ) -> None:
64
+ """Constructor
65
+
66
+ Tag and optionally send a payload.
67
+
68
+ The timeout starts as soon as the constructor is called.
69
+
70
+ :param payloadEnvelope The payloadEnvelope to send to the remote and, and wait for a response for
71
+ :param destVortexName The name of the vortex to send to.
72
+ :param destVortexUuid The UUID of the vortex to send a payload to.
73
+ :param timeout The timeout to wait for a response
74
+ :param resultCheck Should the response payload.result be checked, if it fails
75
+ it errback will be called.
76
+ """
77
+ Deferred.__init__(self)
78
+
79
+ if not timeout:
80
+ timeout = self.TIMEOUT
81
+
82
+ self._resultCheck = resultCheck
83
+ self._logTimeoutError = logTimeoutError
84
+
85
+ # uuid4 can have collisions
86
+ self._messageId = str(uuid4()) + str(PayloadResponse.__SEQ)
87
+ PayloadResponse.__SEQ += 1
88
+
89
+ payloadEnvelope.filt[self.MESSAGE_ID_KEY] = self._messageId
90
+ self._filt = copy(payloadEnvelope.filt)
91
+ self._destVortexName = destVortexName
92
+
93
+ self._status = self.PROCESSING
94
+ self._date = datetime.now(pytz.utc)
95
+
96
+ self._endpoint = PayloadEndpoint(self._filt, self._process)
97
+
98
+ if destVortexName or destVortexUuid:
99
+ d: Deferred = payloadEnvelope.toVortexMsgDefer()
100
+ d.addCallback(
101
+ VortexFactory.sendVortexMsg,
102
+ destVortexName=destVortexName,
103
+ destVortexUuid=destVortexUuid,
104
+ )
105
+ d.addErrback(self.errback)
106
+
107
+ try:
108
+ raise Exception()
109
+ except:
110
+ self._stack = sys.exc_info()[2]
111
+
112
+ # noinspection PyTypeChecker
113
+ self.addTimeout(timeout, reactor)
114
+ self.addErrback(self._timedOut)
115
+
116
+ @classmethod
117
+ def isResponsePayloadEnvelope(cls, payloadEnvelope: PayloadEnvelope):
118
+ """Is Response Payload Envelope
119
+
120
+ The PayloadResponse tags the payloads, so it expects a unique message back.
121
+
122
+ :returns: True if this payload has been tagged by a PayloadResponse class
123
+ """
124
+ return cls.MESSAGE_ID_KEY in payloadEnvelope.filt
125
+
126
+ @property
127
+ def status(self):
128
+ return self._status
129
+
130
+ def _timedOut(self, failure: Failure):
131
+ if self._endpoint:
132
+ self._endpoint.shutdown()
133
+ self._endpoint = None
134
+
135
+ if self._logTimeoutError:
136
+ logger.error("Timed out for payload %s", self._filt)
137
+ self._status = self.TIMED_OUT
138
+ return failure
139
+
140
+ def _process(self, payloadEnvelope: PayloadEnvelope, vortexName, **kwargs):
141
+ if self._endpoint:
142
+ self._endpoint.shutdown()
143
+ self._endpoint = None
144
+
145
+ if self._destVortexName and vortexName != self._destVortexName:
146
+ logger.debug(
147
+ "Received response from a vortex other than the dest vortex, "
148
+ "Expected %s, Received %s",
149
+ self._destVortexName,
150
+ vortexName,
151
+ )
152
+ return
153
+
154
+ if self.called:
155
+ logger.error("Received response after timeout for %s" % self._filt)
156
+ return
157
+
158
+ if self._resultCheck and not payloadEnvelope.result in (None, True):
159
+ self._status = self.FAILED
160
+ self.errback(
161
+ Failure(
162
+ Exception(payloadEnvelope.result).with_traceback(
163
+ self._stack
164
+ )
165
+ )
166
+ )
167
+
168
+ else:
169
+ self._status = self.SUCCESS
170
+ self.callback(payloadEnvelope)
171
+
172
+ # Return self, so that PayloadIO knows this is a asyncronous method
173
+ return self
@@ -0,0 +1,39 @@
1
+ from twisted.internet import defer
2
+ from twisted.trial import unittest
3
+
4
+ from vortex.Payload import Payload
5
+ from vortex.PayloadIO import PayloadIO
6
+ from vortex.PayloadResponse import PayloadResponse
7
+
8
+
9
+ class PayloadResponseTest(unittest.TestCase):
10
+ def testSuccesCb(self):
11
+ payload = Payload(filt={"key": "PayloadResponseTest"})
12
+
13
+ payloadReponse = PayloadResponse(payload)
14
+ payloadReponse.addCallback(lambda pl: self.assertIs(pl, payload))
15
+ payloadReponse.addErrback(lambda pl: self.assertTrue(False))
16
+
17
+ PayloadIO().process(payload, "", "", None, lambda _: None)
18
+
19
+ return payloadReponse
20
+
21
+ def testTimeoutErrback(self):
22
+ payload = Payload(filt={"key": "PayloadResponseTest"})
23
+
24
+ payloadReponse = PayloadResponse(payload, timeout=3)
25
+ payloadReponse.addCallback(lambda pl: self.assertFalse(True))
26
+ self.assertFailure(payloadReponse, defer.TimeoutError)
27
+
28
+ # PayloadIO().process(payload)
29
+
30
+ return payloadReponse
31
+
32
+ def testSerialiseDeserialise(self):
33
+ payload = Payload(filt={"key": "PayloadResponseTest"})
34
+
35
+ d = PayloadResponse(payload, timeout=0)
36
+ d.addErrback(lambda _: True) # Handle the errback
37
+
38
+ vortexMsg = payload.toVortexMsg()
39
+ Payload().fromVortexMsg(vortexMsg)
vortex/PayloadTest.py ADDED
@@ -0,0 +1,136 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : http://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+ import gc
10
+
11
+ from twisted.trial import unittest
12
+
13
+ from vortex.Payload import Payload
14
+ from vortex.PayloadFilterKeys import rapuiClientEcho
15
+ from vortex.Tuple import TupleHash
16
+ from vortex.VortexTcpMemoryLeakPayloadTest import MemoryCheckerTestMixin
17
+
18
+
19
+ def makeTestPayloadA(tupleDataSize=10 * 1024):
20
+ payload = Payload()
21
+ payload.filt[rapuiClientEcho] = None
22
+ payload.tuples = ["12345678" * int(tupleDataSize / 8)]
23
+ return payload
24
+
25
+
26
+ class PayloadPyTest(unittest.TestCase, MemoryCheckerTestMixin):
27
+ PERF_TEST_COUNT = 10000
28
+
29
+ def setUp(self):
30
+ pass
31
+
32
+ def tearDown(self):
33
+ pass
34
+
35
+ # @profile
36
+ def _makeStr(self, size):
37
+ _ = ["12345678" * int(size / 8)]
38
+ del _
39
+ gc.collect()
40
+
41
+ # @profile
42
+ def _payloadEncodeDecode(self, size):
43
+ origPayload = makeTestPayloadA(size)
44
+ origPayload.date = None
45
+
46
+ encodedPayload = origPayload.toEncodedPayload()
47
+ payload = Payload().fromEncodedPayload(encodedPayload)
48
+
49
+ self.assertEqual(payload.tuples[0], origPayload.tuples[0])
50
+
51
+ def testMakeStr_100m_x100(self):
52
+ self._memCheckFunction(self._makeStr, 1000, 100 * 1024**2)
53
+
54
+ def testPayloadToFromEncodedPayload_10k_x1000(self):
55
+ self._memCheckFunction(self._payloadEncodeDecode, 1000, 10 * 1024)
56
+
57
+ def testPayloadToFromEncodedPayload_100m_x100(self):
58
+ self._memCheckFunction(self._payloadEncodeDecode, 100, 10 * 1024**2)
59
+
60
+ def testPayloadToFromEncodedPayload_1m_x1000(self):
61
+ self._memCheckFunction(self._payloadEncodeDecode, 1000, 1 * 1024**2)
62
+
63
+ def testPayloadToFromEncodedPayload10mb(self):
64
+ # Create Payload
65
+ origPayload = makeTestPayloadA()
66
+ origPayload.tuples = ["1234567890" * 1024**2]
67
+
68
+ encodedPayload = origPayload.toEncodedPayload()
69
+ payload = Payload().fromEncodedPayload(encodedPayload)
70
+
71
+ self.assertEqual(payload.tuples[0], origPayload.tuples[0])
72
+
73
+ def testPayloadToFromEncodedPayload(self):
74
+ # Create Payload
75
+ origPayload = makeTestPayloadA()
76
+
77
+ # To VortexServer Message
78
+ encodedPayload = origPayload.toEncodedPayload()
79
+ # print encodedPayload
80
+
81
+ payload = Payload().fromEncodedPayload(encodedPayload)
82
+
83
+ self.assertEqual(TupleHash(payload), TupleHash(payload))
84
+ self.assertEqual(TupleHash(origPayload), TupleHash(origPayload))
85
+
86
+ self.assertEqual(
87
+ TupleHash(origPayload),
88
+ TupleHash(payload),
89
+ "Payload serialise -> deserialize error",
90
+ )
91
+
92
+ def testPayloadToFromJson(self):
93
+ # Create Payload
94
+ origPayload = makeTestPayloadA()
95
+
96
+ # To JSON
97
+ jsonStr = origPayload._toJson()
98
+ # print encodedPayload
99
+ # print(jsonStr)
100
+
101
+ payload = Payload()._fromJson(jsonStr)
102
+
103
+ self.assertEqual(TupleHash(payload), TupleHash(payload))
104
+ self.assertEqual(TupleHash(origPayload), TupleHash(origPayload))
105
+
106
+ self.assertEqual(
107
+ TupleHash(origPayload),
108
+ TupleHash(payload),
109
+ "Payload serialise -> deserialise error",
110
+ )
111
+
112
+ def testPayloadJsonPerformanceSingle(self):
113
+ origPayload = makeTestPayloadA()
114
+ for _ in range(self.PERF_TEST_COUNT):
115
+ origPayload.tuples.append(makeTestPayloadA())
116
+ jsonStr = origPayload._toJson()
117
+ payload = Payload()._fromJson(jsonStr)
118
+
119
+ def testPayloadEncodedPayloadPerformanceSingle(self):
120
+ origPayload = makeTestPayloadA()
121
+ for x in range(self.PERF_TEST_COUNT):
122
+ origPayload.tuples.append(makeTestPayloadA())
123
+ encodedPayload = origPayload.toEncodedPayload()
124
+ payload = Payload().fromEncodedPayload(encodedPayload)
125
+
126
+ def testPayloadJsonPerformanceMultiple(self):
127
+ origPayload = makeTestPayloadA()
128
+ for _ in range(self.PERF_TEST_COUNT):
129
+ jsonStr = origPayload._toJson()
130
+ payload = Payload()._fromJson(jsonStr)
131
+
132
+ def testPayloadEncodedPayloadPerformanceMultiple(self):
133
+ origPayload = makeTestPayloadA()
134
+ for _ in range(self.PERF_TEST_COUNT):
135
+ encodedPayload = origPayload.toEncodedPayload()
136
+ payload = Payload().fromEncodedPayload(encodedPayload)
@@ -0,0 +1,30 @@
1
+ """
2
+ *
3
+ * Copyright Synerty Pty Ltd 2013
4
+ *
5
+ * This software is proprietary, you are not free to copy
6
+ * or redistribute this code in any format.
7
+ *
8
+ * All rights to this software are reserved by
9
+ * Synerty Pty Ltd
10
+ *
11
+ * Website : http://www.synerty.com
12
+ * Support : support@synerty.com
13
+ *
14
+ """
15
+
16
+ from vortex.Tuple import TupleField, Tuple, addTupleType
17
+
18
+
19
+ @addTupleType
20
+ class PerformTestActionTuple(Tuple):
21
+ """Perform Test Action Tuple
22
+
23
+ This tuple is used for testing the action code.
24
+
25
+ """
26
+
27
+ __tupleType__ = "synerty.vortex.PerformTestActionTuple"
28
+ actionDataInt = TupleField(typingType=int)
29
+ actionDataUnicode = TupleField(typingType=str)
30
+ failProcessing = TupleField(typingType=bool)
@@ -0,0 +1,273 @@
1
+ """
2
+ * Created by Synerty Pty Ltd
3
+ *
4
+ * This software is open source, the MIT license applies.
5
+ *
6
+ * Website : http://www.synerty.com
7
+ * Support : support@synerty.com
8
+ """
9
+
10
+ import decimal
11
+ import logging
12
+ from base64 import b64encode, b64decode
13
+ from collections import defaultdict
14
+ from datetime import datetime, date
15
+
16
+ import pytz
17
+ from sqlalchemy.orm.collections import InstrumentedList
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ try:
22
+ from geoalchemy2.elements import WKBElement
23
+
24
+ except ImportError:
25
+
26
+ class WKBElement:
27
+ def __init__(self):
28
+ raise NotImplementedError("")
29
+
30
+
31
+ try:
32
+ from geoalchemy2.elements import WKBElement
33
+
34
+ except ImportError:
35
+
36
+ class WKBElement:
37
+ def __init__(self):
38
+ raise NotImplementedError("")
39
+
40
+
41
+ ###############################################################################
42
+ # Utility functions
43
+ ###############################################################################
44
+
45
+ T_RAPUI_TUPLE = "rt"
46
+ T_RAPUI_PAYLOAD = "rp"
47
+ T_RAPUI_PAYLOAD_ENVELOPE = "rpe"
48
+ T_GENERIC_CLASS = "gen" # NOT SUPPORTED
49
+ T_FLOAT = "float"
50
+ T_INT = "int"
51
+ T_STR = "str"
52
+ T_BYTES = "bytes"
53
+ T_BOOL = "bool"
54
+ T_DATETIME = "datetime"
55
+ T_DICT = "dict"
56
+ T_LIST = "list"
57
+ T_WKB = "wkb" # Well Known Binary, a part of the GEO library
58
+
59
+ V_NULL = "null"
60
+ V_TRUE = "1"
61
+ V_FALSE = "0"
62
+
63
+ ISO8601_ORA = "YYYY-MM-DD HH24:MI:SS.FF6"
64
+
65
+ # This will print a date using TO_CHAR(), in the format fromStr() below expects
66
+ ISO8601_ORA_WITH_TZ = "YYYY-MM-DD HH24:MI:SS.FF6TZHTZM"
67
+ ISO8601 = "%Y-%m-%d %H:%M:%S.%f%z"
68
+ ISO8601_JS_REGEXP = r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[Z]{1}"
69
+ ISO8601_JS_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
70
+ ISO8601_REGEXP = r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}[+-]\d{4}$"
71
+ ISO8601_NOTZ = "%Y-%m-%d %H:%M:%S.%f"
72
+
73
+ TYPE_MAP_PY_TO_RAPUI = {
74
+ decimal.Decimal: T_FLOAT,
75
+ float: T_FLOAT,
76
+ str: T_STR,
77
+ bytes: T_BYTES,
78
+ int: T_INT,
79
+ bool: T_BOOL,
80
+ datetime: T_DATETIME,
81
+ date: T_DATETIME,
82
+ dict: T_DICT,
83
+ defaultdict: T_DICT,
84
+ list: T_LIST,
85
+ set: T_LIST,
86
+ tuple: T_LIST,
87
+ WKBElement: T_WKB,
88
+ InstrumentedList: T_LIST,
89
+ }
90
+
91
+
92
+ def className(o):
93
+ cls = o if o.__class__ == type else o.__class__
94
+ return str(cls).split("'")[1]
95
+
96
+
97
+ def convertFromShape(shapelyShape):
98
+ from shapely.geometry.polygon import Polygon
99
+
100
+ if isinstance(shapelyShape, Polygon):
101
+ coords = shapelyShape.exterior.coords
102
+ else:
103
+ coords = shapelyShape.coords
104
+ return [{"x": i[0], "y": i[1]} for i in coords]
105
+
106
+
107
+ def convertFromWkbElement(value):
108
+ from geoalchemy2.shape import to_shape
109
+
110
+ return convertFromShape(to_shape(value))
111
+
112
+
113
+ def serialiseWkb(wkb: WKBElement) -> str:
114
+ from geoalchemy2.shape import to_shape
115
+ import shapely
116
+
117
+ return b64encode(shapely.wkb.dumps(to_shape(wkb))).decode()
118
+
119
+
120
+ def deserialiseWkb(wkbB64Data: str) -> WKBElement:
121
+ from geoalchemy2.shape import from_shape
122
+ import shapely
123
+
124
+ return from_shape(shapely.wkb.loads(b64decode(wkbB64Data)))
125
+
126
+
127
+ TYPE_MAP_RAPUI_TO_PY = {
128
+ T_FLOAT: float,
129
+ T_STR: str,
130
+ T_BYTES: bytes,
131
+ T_INT: int,
132
+ T_BOOL: bool,
133
+ T_DATETIME: datetime,
134
+ T_DICT: dict,
135
+ T_LIST: list,
136
+ T_WKB: deserialiseWkb,
137
+ }
138
+
139
+
140
+ def decimalToStr(dec):
141
+ """Return string representation of the number in scientific notation.
142
+
143
+ Captures all of the information in the underlying representation.
144
+ """
145
+
146
+ if str(dec) == "0E-10":
147
+ return "0"
148
+
149
+ sign = ["", "-"][dec._sign]
150
+ if dec._is_special:
151
+ if dec._exp == "F":
152
+ return sign + "Infinity"
153
+ elif dec._exp == "n":
154
+ return sign + "NaN" + dec._int
155
+ else: # dec._exp == 'N'
156
+ return sign + "sNaN" + dec._int
157
+
158
+ # number of digits of dec._int to left of decimal point
159
+ leftdigits = dec._exp + len(dec._int)
160
+
161
+ # no exponent required
162
+ dotplace = leftdigits
163
+
164
+ if dotplace <= 0:
165
+ intpart = "0"
166
+ fracpart = "." + "0" * (-dotplace) + dec._int
167
+ elif dotplace >= len(dec._int):
168
+ intpart = dec._int + "0" * (dotplace - len(dec._int))
169
+ fracpart = ""
170
+ else:
171
+ intpart = dec._int[:dotplace]
172
+ fracpart = "." + dec._int[dotplace:]
173
+
174
+ if leftdigits == dotplace:
175
+ exp = ""
176
+ else:
177
+ exp = "E%+d" % (leftdigits - dotplace)
178
+
179
+ s = sign + intpart + fracpart + exp
180
+ if "." in s:
181
+ s = s.rstrip("0").rstrip(".")
182
+ assert len(s)
183
+ return s
184
+
185
+
186
+ def toStr(obj) -> str:
187
+ if isinstance(obj, WKBElement):
188
+ # noinspection PyTypeChecker
189
+ return serialiseWkb(obj)
190
+
191
+ if isinstance(obj, decimal.Decimal):
192
+ return decimalToStr(obj)
193
+
194
+ if isinstance(obj, datetime):
195
+ return obj.strftime(ISO8601)
196
+
197
+ if isinstance(obj, bool):
198
+ return V_TRUE if obj else V_FALSE
199
+
200
+ if isinstance(obj, bytes):
201
+ return b64encode(obj).decode() # to make it a str
202
+
203
+ if isinstance(obj, str):
204
+ return obj
205
+
206
+ try:
207
+ return str(obj)
208
+ except:
209
+ logger.critical("Failed to convert the following to string")
210
+ logger.critical(obj)
211
+ raise
212
+
213
+
214
+ def fromStr(val: str, typeName: str):
215
+ if typeName == T_DATETIME:
216
+ # TODO: Check with ISO8601_JS_REGEXP
217
+ if len(val) == len("2017-06-06T09:40:37.097Z"):
218
+ return datetime.strptime(val, ISO8601_JS_FORMAT).replace(
219
+ tzinfo=pytz.UTC
220
+ )
221
+
222
+ if len(val) <= len("2017-06-06 09:40:37.097068"):
223
+ logger.warning(
224
+ "Parsing date time with no timezone: %s Setting TZ to UTC",
225
+ val,
226
+ )
227
+ return datetime.strptime(val, ISO8601_NOTZ).replace(tzinfo=pytz.UTC)
228
+
229
+ return datetime.strptime(val, ISO8601)
230
+
231
+ if typeName == T_BOOL:
232
+ return val == V_TRUE
233
+
234
+ if typeName == T_BYTES:
235
+ return b64decode(val)
236
+
237
+ if typeName in (T_STR):
238
+ return val
239
+
240
+ pyVal = fromRapuiType(typeName)(val)
241
+
242
+ if typeName == T_FLOAT and pyVal.is_integer():
243
+ pyVal = int(pyVal)
244
+
245
+ return pyVal
246
+
247
+
248
+ def fromRapuiType(typeName: str):
249
+ return TYPE_MAP_RAPUI_TO_PY[typeName]
250
+
251
+
252
+ def toRapuiType(value):
253
+ if value is None:
254
+ return V_NULL
255
+
256
+ from .Tuple import Tuple
257
+
258
+ if isinstance(value, Tuple):
259
+ return T_RAPUI_TUPLE
260
+
261
+ from .Payload import Payload
262
+
263
+ if isinstance(value, Payload):
264
+ return T_RAPUI_PAYLOAD
265
+
266
+ from .PayloadEnvelope import PayloadEnvelope
267
+
268
+ if isinstance(value, PayloadEnvelope):
269
+ return T_RAPUI_PAYLOAD_ENVELOPE
270
+
271
+ valueClass = value.__class__
272
+
273
+ return TYPE_MAP_PY_TO_RAPUI[valueClass]