eventsourcing 9.4.0a7__py3-none-any.whl → 9.4.0a8__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.
Potentially problematic release.
This version of eventsourcing might be problematic. Click here for more details.
- eventsourcing/application.py +3 -7
- eventsourcing/domain.py +346 -357
- eventsourcing/persistence.py +18 -24
- eventsourcing/popo.py +5 -1
- eventsourcing/postgres.py +41 -33
- eventsourcing/projection.py +22 -19
- eventsourcing/sqlite.py +5 -1
- eventsourcing/system.py +19 -11
- eventsourcing/tests/application.py +57 -49
- eventsourcing/tests/domain.py +8 -6
- eventsourcing/tests/persistence.py +162 -143
- eventsourcing/tests/postgres_utils.py +7 -8
- eventsourcing/utils.py +15 -10
- {eventsourcing-9.4.0a7.dist-info → eventsourcing-9.4.0a8.dist-info}/METADATA +1 -1
- eventsourcing-9.4.0a8.dist-info/RECORD +26 -0
- eventsourcing-9.4.0a7.dist-info/RECORD +0 -26
- {eventsourcing-9.4.0a7.dist-info → eventsourcing-9.4.0a8.dist-info}/AUTHORS +0 -0
- {eventsourcing-9.4.0a7.dist-info → eventsourcing-9.4.0a8.dist-info}/LICENSE +0 -0
- {eventsourcing-9.4.0a7.dist-info → eventsourcing-9.4.0a8.dist-info}/WHEEL +0 -0
|
@@ -10,10 +10,12 @@ from tempfile import NamedTemporaryFile
|
|
|
10
10
|
from threading import Event, Thread, get_ident
|
|
11
11
|
from time import sleep
|
|
12
12
|
from timeit import timeit
|
|
13
|
-
from typing import Any
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Generic, cast
|
|
14
14
|
from unittest import TestCase
|
|
15
15
|
from uuid import UUID, uuid4
|
|
16
16
|
|
|
17
|
+
from typing_extensions import TypeVar
|
|
18
|
+
|
|
17
19
|
from eventsourcing.cipher import AESCipher
|
|
18
20
|
from eventsourcing.compressor import ZlibCompressor
|
|
19
21
|
from eventsourcing.domain import DomainEvent
|
|
@@ -31,12 +33,18 @@ from eventsourcing.persistence import (
|
|
|
31
33
|
StoredEvent,
|
|
32
34
|
Tracking,
|
|
33
35
|
TrackingRecorder,
|
|
36
|
+
Transcoder,
|
|
34
37
|
Transcoding,
|
|
35
38
|
UUIDAsHex,
|
|
36
39
|
WaitInterruptedError,
|
|
37
40
|
)
|
|
38
41
|
from eventsourcing.utils import Environment, get_topic
|
|
39
42
|
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from collections.abc import Iterator
|
|
45
|
+
|
|
46
|
+
from typing_extensions import Never
|
|
47
|
+
|
|
40
48
|
|
|
41
49
|
class AggregateRecorderTestCase(TestCase, ABC):
|
|
42
50
|
INITIAL_VERSION = 1
|
|
@@ -207,12 +215,17 @@ class AggregateRecorderTestCase(TestCase, ABC):
|
|
|
207
215
|
)
|
|
208
216
|
|
|
209
217
|
|
|
210
|
-
|
|
218
|
+
_TApplicationRecorder = TypeVar(
|
|
219
|
+
"_TApplicationRecorder", bound=ApplicationRecorder, default=ApplicationRecorder
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class ApplicationRecorderTestCase(TestCase, ABC, Generic[_TApplicationRecorder]):
|
|
211
224
|
INITIAL_VERSION = 1
|
|
212
225
|
EXPECT_CONTIGUOUS_NOTIFICATION_IDS = True
|
|
213
226
|
|
|
214
227
|
@abstractmethod
|
|
215
|
-
def create_recorder(self) ->
|
|
228
|
+
def create_recorder(self) -> _TApplicationRecorder:
|
|
216
229
|
""""""
|
|
217
230
|
|
|
218
231
|
def test_insert_select(self) -> None:
|
|
@@ -535,24 +548,12 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
535
548
|
|
|
536
549
|
errors_happened = Event()
|
|
537
550
|
|
|
538
|
-
counts = {}
|
|
539
|
-
threads: dict[int, int] = {}
|
|
540
|
-
durations: dict[int, float] = {}
|
|
541
|
-
|
|
542
551
|
# Match this to the batch page size in postgres insert for max throughput.
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
552
|
+
num_events_per_job = 500
|
|
553
|
+
num_jobs = 60
|
|
554
|
+
num_workers = 4
|
|
546
555
|
|
|
547
556
|
def insert_events() -> None:
|
|
548
|
-
thread_id = get_ident()
|
|
549
|
-
if thread_id not in threads:
|
|
550
|
-
threads[thread_id] = len(threads)
|
|
551
|
-
if thread_id not in counts:
|
|
552
|
-
counts[thread_id] = 0
|
|
553
|
-
if thread_id not in durations:
|
|
554
|
-
durations[thread_id] = 0
|
|
555
|
-
|
|
556
557
|
originator_id = uuid4()
|
|
557
558
|
stored_events = [
|
|
558
559
|
StoredEvent(
|
|
@@ -561,7 +562,7 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
561
562
|
topic="topic",
|
|
562
563
|
state=b"state",
|
|
563
564
|
)
|
|
564
|
-
for i in range(
|
|
565
|
+
for i in range(num_events_per_job)
|
|
565
566
|
]
|
|
566
567
|
|
|
567
568
|
try:
|
|
@@ -571,26 +572,29 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
571
572
|
errors_happened.set()
|
|
572
573
|
tb = traceback.format_exc()
|
|
573
574
|
print(tb)
|
|
574
|
-
finally:
|
|
575
|
-
ended = datetime.now()
|
|
576
|
-
duration = (ended - started).total_seconds()
|
|
577
|
-
counts[thread_id] += 1
|
|
578
|
-
durations[thread_id] = duration
|
|
579
575
|
|
|
580
|
-
|
|
576
|
+
# Warm up.
|
|
577
|
+
with ThreadPoolExecutor(max_workers=num_workers) as executor:
|
|
578
|
+
futures = []
|
|
579
|
+
for _ in range(num_workers):
|
|
580
|
+
future = executor.submit(insert_events)
|
|
581
|
+
futures.append(future)
|
|
582
|
+
for future in futures:
|
|
583
|
+
future.result()
|
|
581
584
|
|
|
582
|
-
|
|
585
|
+
# Run.
|
|
586
|
+
with ThreadPoolExecutor(max_workers=num_workers) as executor:
|
|
587
|
+
started = datetime.now()
|
|
583
588
|
futures = []
|
|
584
589
|
for _ in range(num_jobs):
|
|
585
590
|
future = executor.submit(insert_events)
|
|
586
|
-
# future.add_done_callback(self.close_db_connection)
|
|
587
591
|
futures.append(future)
|
|
588
592
|
for future in futures:
|
|
589
593
|
future.result()
|
|
590
594
|
|
|
591
595
|
self.assertFalse(errors_happened.is_set(), "There were errors (see above)")
|
|
592
596
|
ended = datetime.now()
|
|
593
|
-
rate = num_jobs *
|
|
597
|
+
rate = num_jobs * num_events_per_job / (ended - started).total_seconds()
|
|
594
598
|
print(f"Rate: {rate:.0f} inserts per second")
|
|
595
599
|
|
|
596
600
|
def optional_test_insert_subscribe(self) -> None:
|
|
@@ -696,7 +700,7 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
696
700
|
with recorder.subscribe(gt=max_notification_id2) as subscription:
|
|
697
701
|
|
|
698
702
|
# Receive events from the subscription.
|
|
699
|
-
notifications
|
|
703
|
+
notifications = []
|
|
700
704
|
for notification in subscription:
|
|
701
705
|
notifications.append(notification)
|
|
702
706
|
if len(notifications) == 1:
|
|
@@ -752,10 +756,10 @@ class ApplicationRecorderTestCase(TestCase, ABC):
|
|
|
752
756
|
|
|
753
757
|
class TrackingRecorderTestCase(TestCase, ABC):
|
|
754
758
|
@abstractmethod
|
|
755
|
-
def create_recorder(self) ->
|
|
759
|
+
def create_recorder(self) -> TrackingRecorder:
|
|
756
760
|
""""""
|
|
757
761
|
|
|
758
|
-
def test_insert_tracking(self):
|
|
762
|
+
def test_insert_tracking(self) -> None:
|
|
759
763
|
tracking_recorder = self.create_recorder()
|
|
760
764
|
|
|
761
765
|
# Construct tracking objects.
|
|
@@ -787,7 +791,7 @@ class TrackingRecorderTestCase(TestCase, ABC):
|
|
|
787
791
|
assert tracking_recorder.has_tracking_id("upstream2", 21)
|
|
788
792
|
assert not tracking_recorder.has_tracking_id("upstream2", 22)
|
|
789
793
|
|
|
790
|
-
def test_wait(self):
|
|
794
|
+
def test_wait(self) -> None:
|
|
791
795
|
tracking_recorder = self.create_recorder()
|
|
792
796
|
tracking1 = Tracking(notification_id=21, application_name="upstream1")
|
|
793
797
|
tracking_recorder.insert_tracking(tracking=tracking1)
|
|
@@ -900,10 +904,11 @@ class ProcessRecorderTestCase(TestCase, ABC):
|
|
|
900
904
|
2,
|
|
901
905
|
)
|
|
902
906
|
|
|
903
|
-
def test_has_tracking_id(self):
|
|
907
|
+
def test_has_tracking_id(self) -> None:
|
|
904
908
|
# Construct the recorder.
|
|
905
909
|
recorder = self.create_recorder()
|
|
906
910
|
|
|
911
|
+
self.assertTrue(recorder.has_tracking_id("upstream_app", None))
|
|
907
912
|
self.assertFalse(recorder.has_tracking_id("upstream_app", 1))
|
|
908
913
|
self.assertFalse(recorder.has_tracking_id("upstream_app", 2))
|
|
909
914
|
self.assertFalse(recorder.has_tracking_id("upstream_app", 3))
|
|
@@ -975,7 +980,7 @@ class ProcessRecorderTestCase(TestCase, ABC):
|
|
|
975
980
|
class NonInterleavingNotificationIDsBaseCase(ABC, TestCase):
|
|
976
981
|
insert_num = 1000
|
|
977
982
|
|
|
978
|
-
def test(self):
|
|
983
|
+
def test(self) -> None:
|
|
979
984
|
recorder = self.create_recorder()
|
|
980
985
|
|
|
981
986
|
max_notification_id = recorder.max_notification_id()
|
|
@@ -990,7 +995,7 @@ class NonInterleavingNotificationIDsBaseCase(ABC, TestCase):
|
|
|
990
995
|
|
|
991
996
|
errors = []
|
|
992
997
|
|
|
993
|
-
def insert_stack(stack):
|
|
998
|
+
def insert_stack(stack: list[StoredEvent]) -> None:
|
|
994
999
|
try:
|
|
995
1000
|
race_started.wait()
|
|
996
1001
|
recorder.insert_events(stack)
|
|
@@ -1036,7 +1041,7 @@ class NonInterleavingNotificationIDsBaseCase(ABC, TestCase):
|
|
|
1036
1041
|
else:
|
|
1037
1042
|
self.assertGreater(min_id_for_sequence2, max_id_for_sequence1)
|
|
1038
1043
|
|
|
1039
|
-
def create_stack(self, originator_id):
|
|
1044
|
+
def create_stack(self, originator_id: UUID) -> list[StoredEvent]:
|
|
1040
1045
|
return [
|
|
1041
1046
|
StoredEvent(
|
|
1042
1047
|
originator_id=originator_id,
|
|
@@ -1052,45 +1057,52 @@ class NonInterleavingNotificationIDsBaseCase(ABC, TestCase):
|
|
|
1052
1057
|
pass
|
|
1053
1058
|
|
|
1054
1059
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1060
|
+
_TInfrastrutureFactory = TypeVar(
|
|
1061
|
+
"_TInfrastrutureFactory", bound=InfrastructureFactory[Any]
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
class InfrastructureFactoryTestCase(ABC, TestCase, Generic[_TInfrastrutureFactory]):
|
|
1066
|
+
env: Environment
|
|
1057
1067
|
|
|
1058
1068
|
@abstractmethod
|
|
1059
|
-
def expected_factory_class(self):
|
|
1069
|
+
def expected_factory_class(self) -> type[_TInfrastrutureFactory]:
|
|
1060
1070
|
pass
|
|
1061
1071
|
|
|
1062
1072
|
@abstractmethod
|
|
1063
|
-
def expected_aggregate_recorder_class(self):
|
|
1073
|
+
def expected_aggregate_recorder_class(self) -> type[AggregateRecorder]:
|
|
1064
1074
|
pass
|
|
1065
1075
|
|
|
1066
1076
|
@abstractmethod
|
|
1067
|
-
def expected_application_recorder_class(self):
|
|
1077
|
+
def expected_application_recorder_class(self) -> type[ApplicationRecorder]:
|
|
1068
1078
|
pass
|
|
1069
1079
|
|
|
1070
1080
|
@abstractmethod
|
|
1071
|
-
def expected_tracking_recorder_class(self):
|
|
1081
|
+
def expected_tracking_recorder_class(self) -> type[TrackingRecorder]:
|
|
1072
1082
|
pass
|
|
1073
1083
|
|
|
1074
1084
|
@abstractmethod
|
|
1075
|
-
def tracking_recorder_subclass(self):
|
|
1085
|
+
def tracking_recorder_subclass(self) -> type[TrackingRecorder]:
|
|
1076
1086
|
pass
|
|
1077
1087
|
|
|
1078
1088
|
@abstractmethod
|
|
1079
|
-
def expected_process_recorder_class(self):
|
|
1089
|
+
def expected_process_recorder_class(self) -> type[ProcessRecorder]:
|
|
1080
1090
|
pass
|
|
1081
1091
|
|
|
1082
1092
|
def setUp(self) -> None:
|
|
1083
|
-
self.factory =
|
|
1093
|
+
self.factory = cast(
|
|
1094
|
+
_TInfrastrutureFactory, InfrastructureFactory.construct(self.env)
|
|
1095
|
+
)
|
|
1084
1096
|
self.assertIsInstance(self.factory, self.expected_factory_class())
|
|
1085
1097
|
self.transcoder = JSONTranscoder()
|
|
1086
1098
|
self.transcoder.register(UUIDAsHex())
|
|
1087
1099
|
self.transcoder.register(DecimalAsStr())
|
|
1088
1100
|
self.transcoder.register(DatetimeAsISO())
|
|
1089
1101
|
|
|
1090
|
-
def tearDown(self):
|
|
1102
|
+
def tearDown(self) -> None:
|
|
1091
1103
|
self.factory.close()
|
|
1092
1104
|
|
|
1093
|
-
def test_createmapper(self):
|
|
1105
|
+
def test_createmapper(self) -> None:
|
|
1094
1106
|
# Want to construct:
|
|
1095
1107
|
# - application recorder
|
|
1096
1108
|
# - snapshot recorder
|
|
@@ -1126,7 +1138,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1126
1138
|
self.assertIsNone(mapper.cipher)
|
|
1127
1139
|
self.assertIsNone(mapper.compressor)
|
|
1128
1140
|
|
|
1129
|
-
def test_createmapper_with_compressor(self):
|
|
1141
|
+
def test_createmapper_with_compressor(self) -> None:
|
|
1130
1142
|
# Create mapper with compressor class as topic.
|
|
1131
1143
|
self.env[self.factory.COMPRESSOR_TOPIC] = get_topic(ZlibCompressor)
|
|
1132
1144
|
mapper = self.factory.mapper(transcoder=self.transcoder)
|
|
@@ -1141,7 +1153,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1141
1153
|
self.assertEqual(mapper.compressor, zlib)
|
|
1142
1154
|
self.assertIsNone(mapper.cipher)
|
|
1143
1155
|
|
|
1144
|
-
def test_createmapper_with_cipher(self):
|
|
1156
|
+
def test_createmapper_with_cipher(self) -> None:
|
|
1145
1157
|
# Check cipher needs a key.
|
|
1146
1158
|
self.env[self.factory.CIPHER_TOPIC] = get_topic(AESCipher)
|
|
1147
1159
|
|
|
@@ -1162,7 +1174,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1162
1174
|
|
|
1163
1175
|
def test_createmapper_with_cipher_and_compressor(
|
|
1164
1176
|
self,
|
|
1165
|
-
):
|
|
1177
|
+
) -> None:
|
|
1166
1178
|
# Create mapper with cipher and compressor.
|
|
1167
1179
|
self.env[self.factory.COMPRESSOR_TOPIC] = get_topic(ZlibCompressor)
|
|
1168
1180
|
|
|
@@ -1175,7 +1187,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1175
1187
|
self.assertIsNotNone(mapper.cipher)
|
|
1176
1188
|
self.assertIsNotNone(mapper.compressor)
|
|
1177
1189
|
|
|
1178
|
-
def test_mapper_with_wrong_cipher_key(self):
|
|
1190
|
+
def test_mapper_with_wrong_cipher_key(self) -> None:
|
|
1179
1191
|
self.env.name = "App1"
|
|
1180
1192
|
self.env[self.factory.CIPHER_TOPIC] = get_topic(AESCipher)
|
|
1181
1193
|
cipher_key1 = AESCipher.create_key(16)
|
|
@@ -1205,7 +1217,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1205
1217
|
with self.assertRaises(ValueError):
|
|
1206
1218
|
mapper2.to_domain_event(stored_event)
|
|
1207
1219
|
|
|
1208
|
-
def test_create_aggregate_recorder(self):
|
|
1220
|
+
def test_create_aggregate_recorder(self) -> None:
|
|
1209
1221
|
recorder = self.factory.aggregate_recorder()
|
|
1210
1222
|
self.assertEqual(type(recorder), self.expected_aggregate_recorder_class())
|
|
1211
1223
|
|
|
@@ -1216,7 +1228,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1216
1228
|
recorder = self.factory.aggregate_recorder()
|
|
1217
1229
|
self.assertEqual(type(recorder), self.expected_aggregate_recorder_class())
|
|
1218
1230
|
|
|
1219
|
-
def test_create_application_recorder(self):
|
|
1231
|
+
def test_create_application_recorder(self) -> None:
|
|
1220
1232
|
recorder = self.factory.application_recorder()
|
|
1221
1233
|
self.assertEqual(type(recorder), self.expected_application_recorder_class())
|
|
1222
1234
|
self.assertIsInstance(recorder, ApplicationRecorder)
|
|
@@ -1226,7 +1238,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1226
1238
|
recorder = self.factory.application_recorder()
|
|
1227
1239
|
self.assertEqual(type(recorder), self.expected_application_recorder_class())
|
|
1228
1240
|
|
|
1229
|
-
def test_create_tracking_recorder(self):
|
|
1241
|
+
def test_create_tracking_recorder(self) -> None:
|
|
1230
1242
|
recorder = self.factory.tracking_recorder()
|
|
1231
1243
|
self.assertEqual(type(recorder), self.expected_tracking_recorder_class())
|
|
1232
1244
|
self.assertIsInstance(recorder, TrackingRecorder)
|
|
@@ -1246,7 +1258,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1246
1258
|
recorder = self.factory.tracking_recorder()
|
|
1247
1259
|
self.assertEqual(type(recorder), subclass)
|
|
1248
1260
|
|
|
1249
|
-
def test_create_process_recorder(self):
|
|
1261
|
+
def test_create_process_recorder(self) -> None:
|
|
1250
1262
|
recorder = self.factory.process_recorder()
|
|
1251
1263
|
self.assertEqual(type(recorder), self.expected_process_recorder_class())
|
|
1252
1264
|
self.assertIsInstance(recorder, ProcessRecorder)
|
|
@@ -1257,7 +1269,7 @@ class InfrastructureFactoryTestCase(ABC, TestCase):
|
|
|
1257
1269
|
self.assertEqual(type(recorder), self.expected_process_recorder_class())
|
|
1258
1270
|
|
|
1259
1271
|
|
|
1260
|
-
def tmpfile_uris():
|
|
1272
|
+
def tmpfile_uris() -> Iterator[str]:
|
|
1261
1273
|
tmp_files = []
|
|
1262
1274
|
ram_disk_path = "/Volumes/RAM DISK/"
|
|
1263
1275
|
prefix = None
|
|
@@ -1273,7 +1285,7 @@ class CustomType1:
|
|
|
1273
1285
|
def __init__(self, value: UUID):
|
|
1274
1286
|
self.value = value
|
|
1275
1287
|
|
|
1276
|
-
def __eq__(self, other:
|
|
1288
|
+
def __eq__(self, other: object) -> bool:
|
|
1277
1289
|
return type(self) is type(other) and self.__dict__ == other.__dict__
|
|
1278
1290
|
|
|
1279
1291
|
|
|
@@ -1281,39 +1293,46 @@ class CustomType2:
|
|
|
1281
1293
|
def __init__(self, value: CustomType1):
|
|
1282
1294
|
self.value = value
|
|
1283
1295
|
|
|
1284
|
-
def __eq__(self, other:
|
|
1296
|
+
def __eq__(self, other: object) -> bool:
|
|
1285
1297
|
return type(self) is type(other) and self.__dict__ == other.__dict__
|
|
1286
1298
|
|
|
1287
1299
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1300
|
+
_KT = TypeVar("_KT")
|
|
1301
|
+
_VT = TypeVar("_VT")
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
class Mydict(dict[_KT, _VT]):
|
|
1305
|
+
def __repr__(self) -> str:
|
|
1290
1306
|
return f"{type(self).__name__}({super().__repr__()})"
|
|
1291
1307
|
|
|
1292
|
-
def __eq__(self, other):
|
|
1308
|
+
def __eq__(self, other: object) -> bool:
|
|
1293
1309
|
return type(self) is type(other) and super().__eq__(other)
|
|
1294
1310
|
|
|
1295
1311
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1312
|
+
_T = TypeVar("_T")
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
class MyList(list[_T]):
|
|
1316
|
+
def __repr__(self) -> str:
|
|
1298
1317
|
return f"{type(self).__name__}({super().__repr__()})"
|
|
1299
1318
|
|
|
1300
|
-
def __eq__(self, other):
|
|
1319
|
+
def __eq__(self, other: object) -> bool:
|
|
1301
1320
|
return type(self) is type(other) and super().__eq__(other)
|
|
1302
1321
|
|
|
1303
1322
|
|
|
1304
1323
|
class MyStr(str):
|
|
1305
|
-
def __repr__(self):
|
|
1324
|
+
def __repr__(self) -> str:
|
|
1306
1325
|
return f"{type(self).__name__}({super().__repr__()})"
|
|
1307
1326
|
|
|
1308
|
-
def __eq__(self, other):
|
|
1327
|
+
def __eq__(self, other: object) -> bool:
|
|
1309
1328
|
return type(self) is type(other) and super().__eq__(other)
|
|
1310
1329
|
|
|
1311
1330
|
|
|
1312
1331
|
class MyInt(int):
|
|
1313
|
-
def __repr__(self):
|
|
1332
|
+
def __repr__(self) -> str:
|
|
1314
1333
|
return f"{type(self).__name__}({super().__repr__()})"
|
|
1315
1334
|
|
|
1316
|
-
def __eq__(self, other):
|
|
1335
|
+
def __eq__(self, other: object) -> bool:
|
|
1317
1336
|
return type(self) is type(other) and super().__eq__(other)
|
|
1318
1337
|
|
|
1319
1338
|
|
|
@@ -1349,10 +1368,10 @@ class TranscoderTestCase(TestCase):
|
|
|
1349
1368
|
def setUp(self) -> None:
|
|
1350
1369
|
self.transcoder = self.construct_transcoder()
|
|
1351
1370
|
|
|
1352
|
-
def construct_transcoder(self):
|
|
1371
|
+
def construct_transcoder(self) -> Transcoder:
|
|
1353
1372
|
raise NotImplementedError
|
|
1354
1373
|
|
|
1355
|
-
def test_str(self):
|
|
1374
|
+
def test_str(self) -> None:
|
|
1356
1375
|
obj = "a"
|
|
1357
1376
|
data = self.transcoder.encode(obj)
|
|
1358
1377
|
self.assertEqual(data, b'"a"')
|
|
@@ -1384,48 +1403,48 @@ class TranscoderTestCase(TestCase):
|
|
|
1384
1403
|
obj, self.transcoder.decode(legacy_encoding_with_ensure_ascii_true)
|
|
1385
1404
|
)
|
|
1386
1405
|
|
|
1387
|
-
def test_dict(self):
|
|
1406
|
+
def test_dict(self) -> None:
|
|
1388
1407
|
# Empty dict.
|
|
1389
|
-
|
|
1390
|
-
data = self.transcoder.encode(
|
|
1408
|
+
obj1: dict[Never, Never] = {}
|
|
1409
|
+
data = self.transcoder.encode(obj1)
|
|
1391
1410
|
self.assertEqual(data, b"{}")
|
|
1392
|
-
self.assertEqual(
|
|
1411
|
+
self.assertEqual(obj1, self.transcoder.decode(data))
|
|
1393
1412
|
|
|
1394
1413
|
# dict with single key.
|
|
1395
|
-
|
|
1396
|
-
data = self.transcoder.encode(
|
|
1414
|
+
obj2 = {"a": 1}
|
|
1415
|
+
data = self.transcoder.encode(obj2)
|
|
1397
1416
|
self.assertEqual(data, b'{"a":1}')
|
|
1398
|
-
self.assertEqual(
|
|
1417
|
+
self.assertEqual(obj2, self.transcoder.decode(data))
|
|
1399
1418
|
|
|
1400
1419
|
# dict with many keys.
|
|
1401
|
-
|
|
1402
|
-
data = self.transcoder.encode(
|
|
1420
|
+
obj3 = {"a": 1, "b": 2}
|
|
1421
|
+
data = self.transcoder.encode(obj3)
|
|
1403
1422
|
self.assertEqual(data, b'{"a":1,"b":2}')
|
|
1404
|
-
self.assertEqual(
|
|
1423
|
+
self.assertEqual(obj3, self.transcoder.decode(data))
|
|
1405
1424
|
|
|
1406
1425
|
# Empty dict in dict.
|
|
1407
|
-
|
|
1408
|
-
data = self.transcoder.encode(
|
|
1426
|
+
obj4: dict[str, dict[Never, Never]] = {"a": {}}
|
|
1427
|
+
data = self.transcoder.encode(obj4)
|
|
1409
1428
|
self.assertEqual(data, b'{"a":{}}')
|
|
1410
|
-
self.assertEqual(
|
|
1429
|
+
self.assertEqual(obj4, self.transcoder.decode(data))
|
|
1411
1430
|
|
|
1412
1431
|
# Empty dicts in dict.
|
|
1413
|
-
|
|
1414
|
-
data = self.transcoder.encode(
|
|
1432
|
+
obj5: dict[str, dict[Never, Never]] = {"a": {}, "b": {}}
|
|
1433
|
+
data = self.transcoder.encode(obj5)
|
|
1415
1434
|
self.assertEqual(data, b'{"a":{},"b":{}}')
|
|
1416
|
-
self.assertEqual(
|
|
1435
|
+
self.assertEqual(obj5, self.transcoder.decode(data))
|
|
1417
1436
|
|
|
1418
1437
|
# Empty dict in dict in dict.
|
|
1419
|
-
|
|
1420
|
-
data = self.transcoder.encode(
|
|
1438
|
+
obj6: dict[str, dict[str, dict[Never, Never]]] = {"a": {"b": {}}}
|
|
1439
|
+
data = self.transcoder.encode(obj6)
|
|
1421
1440
|
self.assertEqual(data, b'{"a":{"b":{}}}')
|
|
1422
|
-
self.assertEqual(
|
|
1441
|
+
self.assertEqual(obj6, self.transcoder.decode(data))
|
|
1423
1442
|
|
|
1424
1443
|
# Int in dict in dict in dict.
|
|
1425
|
-
|
|
1426
|
-
data = self.transcoder.encode(
|
|
1444
|
+
obj7 = {"a": {"b": {"c": 1}}}
|
|
1445
|
+
data = self.transcoder.encode(obj7)
|
|
1427
1446
|
self.assertEqual(data, b'{"a":{"b":{"c":1}}}')
|
|
1428
|
-
self.assertEqual(
|
|
1447
|
+
self.assertEqual(obj7, self.transcoder.decode(data))
|
|
1429
1448
|
|
|
1430
1449
|
# TODO: Int keys?
|
|
1431
1450
|
# obj = {1: "a"}
|
|
@@ -1433,115 +1452,115 @@ class TranscoderTestCase(TestCase):
|
|
|
1433
1452
|
# self.assertEqual(data, b'{1:{"a"}')
|
|
1434
1453
|
# self.assertEqual(obj, self.transcoder.decode(data))
|
|
1435
1454
|
|
|
1436
|
-
def test_dict_with_len_2_and__data_(self):
|
|
1455
|
+
def test_dict_with_len_2_and__data_(self) -> None:
|
|
1437
1456
|
obj = {"_data_": 1, "something_else": 2}
|
|
1438
1457
|
data = self.transcoder.encode(obj)
|
|
1439
1458
|
self.assertEqual(obj, self.transcoder.decode(data))
|
|
1440
1459
|
|
|
1441
|
-
def test_dict_with_len_2_and__type_(self):
|
|
1460
|
+
def test_dict_with_len_2_and__type_(self) -> None:
|
|
1442
1461
|
obj = {"_type_": 1, "something_else": 2}
|
|
1443
1462
|
data = self.transcoder.encode(obj)
|
|
1444
1463
|
self.assertEqual(obj, self.transcoder.decode(data))
|
|
1445
1464
|
|
|
1446
|
-
def test_dict_subclass(self):
|
|
1465
|
+
def test_dict_subclass(self) -> None:
|
|
1447
1466
|
my_dict = Mydict({"a": 1})
|
|
1448
1467
|
data = self.transcoder.encode(my_dict)
|
|
1449
1468
|
self.assertEqual(b'{"_type_":"mydict","_data_":{"a":1}}', data)
|
|
1450
1469
|
copy = self.transcoder.decode(data)
|
|
1451
1470
|
self.assertEqual(my_dict, copy)
|
|
1452
1471
|
|
|
1453
|
-
def test_list_subclass(self):
|
|
1472
|
+
def test_list_subclass(self) -> None:
|
|
1454
1473
|
my_list = MyList((("a", 1),))
|
|
1455
1474
|
data = self.transcoder.encode(my_list)
|
|
1456
1475
|
copy = self.transcoder.decode(data)
|
|
1457
1476
|
self.assertEqual(my_list, copy)
|
|
1458
1477
|
|
|
1459
|
-
def test_str_subclass(self):
|
|
1478
|
+
def test_str_subclass(self) -> None:
|
|
1460
1479
|
my_str = MyStr("a")
|
|
1461
1480
|
data = self.transcoder.encode(my_str)
|
|
1462
1481
|
copy = self.transcoder.decode(data)
|
|
1463
1482
|
self.assertEqual(my_str, copy)
|
|
1464
1483
|
|
|
1465
|
-
def test_int_subclass(self):
|
|
1484
|
+
def test_int_subclass(self) -> None:
|
|
1466
1485
|
my_int = MyInt(3)
|
|
1467
1486
|
data = self.transcoder.encode(my_int)
|
|
1468
1487
|
copy = self.transcoder.decode(data)
|
|
1469
1488
|
self.assertEqual(my_int, copy)
|
|
1470
1489
|
|
|
1471
|
-
def test_tuple(self):
|
|
1490
|
+
def test_tuple(self) -> None:
|
|
1472
1491
|
# Empty tuple.
|
|
1473
|
-
|
|
1474
|
-
data = self.transcoder.encode(
|
|
1492
|
+
obj1 = ()
|
|
1493
|
+
data = self.transcoder.encode(obj1)
|
|
1475
1494
|
self.assertEqual(data, b'{"_type_":"tuple_as_list","_data_":[]}')
|
|
1476
|
-
self.assertEqual(
|
|
1495
|
+
self.assertEqual(obj1, self.transcoder.decode(data))
|
|
1477
1496
|
|
|
1478
1497
|
# Empty tuple in a tuple.
|
|
1479
|
-
|
|
1480
|
-
data = self.transcoder.encode(
|
|
1481
|
-
self.assertEqual(
|
|
1498
|
+
obj2 = ((),)
|
|
1499
|
+
data = self.transcoder.encode(obj2)
|
|
1500
|
+
self.assertEqual(obj2, self.transcoder.decode(data))
|
|
1482
1501
|
|
|
1483
1502
|
# Int in tuple in a tuple.
|
|
1484
|
-
|
|
1485
|
-
data = self.transcoder.encode(
|
|
1486
|
-
self.assertEqual(
|
|
1503
|
+
obj3 = ((1, 2),)
|
|
1504
|
+
data = self.transcoder.encode(obj3)
|
|
1505
|
+
self.assertEqual(obj3, self.transcoder.decode(data))
|
|
1487
1506
|
|
|
1488
1507
|
# Str in tuple in a tuple.
|
|
1489
|
-
|
|
1490
|
-
data = self.transcoder.encode(
|
|
1491
|
-
self.assertEqual(
|
|
1508
|
+
obj4 = (("a", "b"),)
|
|
1509
|
+
data = self.transcoder.encode(obj4)
|
|
1510
|
+
self.assertEqual(obj4, self.transcoder.decode(data))
|
|
1492
1511
|
|
|
1493
1512
|
# Int and str in tuple in a tuple.
|
|
1494
|
-
|
|
1495
|
-
data = self.transcoder.encode(
|
|
1496
|
-
self.assertEqual(
|
|
1513
|
+
obj5 = ((1, "a"),)
|
|
1514
|
+
data = self.transcoder.encode(obj5)
|
|
1515
|
+
self.assertEqual(obj5, self.transcoder.decode(data))
|
|
1497
1516
|
|
|
1498
|
-
def test_list(self):
|
|
1517
|
+
def test_list(self) -> None:
|
|
1499
1518
|
# Empty list.
|
|
1500
|
-
|
|
1501
|
-
data = self.transcoder.encode(
|
|
1502
|
-
self.assertEqual(
|
|
1519
|
+
obj1: list[Never] = []
|
|
1520
|
+
data = self.transcoder.encode(obj1)
|
|
1521
|
+
self.assertEqual(obj1, self.transcoder.decode(data))
|
|
1503
1522
|
|
|
1504
1523
|
# Empty list in a list.
|
|
1505
|
-
|
|
1506
|
-
data = self.transcoder.encode(
|
|
1507
|
-
self.assertEqual(
|
|
1524
|
+
obj2: list[list[Never]] = [[]]
|
|
1525
|
+
data = self.transcoder.encode(obj2)
|
|
1526
|
+
self.assertEqual(obj2, self.transcoder.decode(data))
|
|
1508
1527
|
|
|
1509
1528
|
# Int in list in a list.
|
|
1510
|
-
|
|
1511
|
-
data = self.transcoder.encode(
|
|
1512
|
-
self.assertEqual(
|
|
1529
|
+
obj3 = [[1, 2]]
|
|
1530
|
+
data = self.transcoder.encode(obj3)
|
|
1531
|
+
self.assertEqual(obj3, self.transcoder.decode(data))
|
|
1513
1532
|
|
|
1514
1533
|
# Str in list in a list.
|
|
1515
|
-
|
|
1516
|
-
data = self.transcoder.encode(
|
|
1517
|
-
self.assertEqual(
|
|
1534
|
+
obj4 = [["a", "b"]]
|
|
1535
|
+
data = self.transcoder.encode(obj4)
|
|
1536
|
+
self.assertEqual(obj4, self.transcoder.decode(data))
|
|
1518
1537
|
|
|
1519
1538
|
# Int and str in list in a list.
|
|
1520
|
-
|
|
1521
|
-
data = self.transcoder.encode(
|
|
1522
|
-
self.assertEqual(
|
|
1539
|
+
obj5 = [[1, "a"]]
|
|
1540
|
+
data = self.transcoder.encode(obj5)
|
|
1541
|
+
self.assertEqual(obj5, self.transcoder.decode(data))
|
|
1523
1542
|
|
|
1524
|
-
def test_mixed(self):
|
|
1525
|
-
|
|
1526
|
-
data = self.transcoder.encode(
|
|
1527
|
-
self.assertEqual(
|
|
1543
|
+
def test_mixed(self) -> None:
|
|
1544
|
+
obj1 = [(1, "a"), {"b": 2}]
|
|
1545
|
+
data = self.transcoder.encode(obj1)
|
|
1546
|
+
self.assertEqual(obj1, self.transcoder.decode(data))
|
|
1528
1547
|
|
|
1529
|
-
|
|
1530
|
-
data = self.transcoder.encode(
|
|
1531
|
-
self.assertEqual(
|
|
1548
|
+
obj2 = ([1, "a"], {"b": 2})
|
|
1549
|
+
data = self.transcoder.encode(obj2)
|
|
1550
|
+
self.assertEqual(obj2, self.transcoder.decode(data))
|
|
1532
1551
|
|
|
1533
|
-
|
|
1534
|
-
data = self.transcoder.encode(
|
|
1535
|
-
self.assertEqual(
|
|
1552
|
+
obj3 = {"a": (1, 2), "b": [3, 4]}
|
|
1553
|
+
data = self.transcoder.encode(obj3)
|
|
1554
|
+
self.assertEqual(obj3, self.transcoder.decode(data))
|
|
1536
1555
|
|
|
1537
|
-
def test_custom_type_in_dict(self):
|
|
1556
|
+
def test_custom_type_in_dict(self) -> None:
|
|
1538
1557
|
# Int in dict in dict in dict.
|
|
1539
1558
|
obj = {"a": CustomType2(CustomType1(UUID("b2723fe2c01a40d2875ea3aac6a09ff5")))}
|
|
1540
1559
|
data = self.transcoder.encode(obj)
|
|
1541
1560
|
decoded_obj = self.transcoder.decode(data)
|
|
1542
1561
|
self.assertEqual(obj, decoded_obj)
|
|
1543
1562
|
|
|
1544
|
-
def test_nested_custom_type(self):
|
|
1563
|
+
def test_nested_custom_type(self) -> None:
|
|
1545
1564
|
obj = CustomType2(CustomType1(UUID("b2723fe2c01a40d2875ea3aac6a09ff5")))
|
|
1546
1565
|
data = self.transcoder.encode(obj)
|
|
1547
1566
|
expect = (
|
|
@@ -1557,7 +1576,7 @@ class TranscoderTestCase(TestCase):
|
|
|
1557
1576
|
self.assertIsInstance(copy.value.value, UUID)
|
|
1558
1577
|
self.assertEqual(copy.value.value, obj.value.value)
|
|
1559
1578
|
|
|
1560
|
-
def test_custom_type_error(self):
|
|
1579
|
+
def test_custom_type_error(self) -> None:
|
|
1561
1580
|
# Expect a TypeError when encoding because transcoding not registered.
|
|
1562
1581
|
with self.assertRaises(TypeError) as cm:
|
|
1563
1582
|
self.transcoder.encode(MyClass())
|