tigerbeetle 0.16.54__py3-none-any.whl → 0.16.56__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 tigerbeetle might be problematic. Click here for more details.
- tigerbeetle/__init__.py +36 -0
- tigerbeetle/bindings.py +47 -36
- tigerbeetle/client.py +68 -34
- tigerbeetle/lib/aarch64-linux-gnu.2.27/libtb_client.so +0 -0
- tigerbeetle/lib/aarch64-linux-musl/libtb_client.so +0 -0
- tigerbeetle/lib/aarch64-macos/libtb_client.dylib +0 -0
- tigerbeetle/lib/x86_64-linux-gnu.2.27/libtb_client.so +0 -0
- tigerbeetle/lib/x86_64-linux-musl/libtb_client.so +0 -0
- tigerbeetle/lib/x86_64-macos/libtb_client.dylib +0 -0
- tigerbeetle/lib/x86_64-windows/tb_client.dll +0 -0
- tigerbeetle/lib.py +20 -18
- tigerbeetle/py.typed +0 -0
- {tigerbeetle-0.16.54.dist-info → tigerbeetle-0.16.56.dist-info}/METADATA +2 -2
- tigerbeetle-0.16.56.dist-info/RECORD +15 -0
- tigerbeetle-0.16.54.dist-info/RECORD +0 -14
- {tigerbeetle-0.16.54.dist-info → tigerbeetle-0.16.56.dist-info}/WHEEL +0 -0
tigerbeetle/__init__.py
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
1
|
from .bindings import * # noqa
|
|
2
2
|
from .client import ClientAsync, ClientSync, id, amount_max, configure_logging # noqa
|
|
3
3
|
from .lib import IntegerOverflowError, NativeError
|
|
4
|
+
|
|
5
|
+
# Explicitly declare public exports:
|
|
6
|
+
__all__ = [
|
|
7
|
+
# from .client:
|
|
8
|
+
"ClientAsync",
|
|
9
|
+
"ClientSync",
|
|
10
|
+
"id",
|
|
11
|
+
"amount_max",
|
|
12
|
+
"configure_logging",
|
|
13
|
+
# from .lib:
|
|
14
|
+
"IntegerOverflowError",
|
|
15
|
+
"NativeError",
|
|
16
|
+
# from .bindings - everything treated as public:
|
|
17
|
+
"Operation",
|
|
18
|
+
"PacketStatus",
|
|
19
|
+
"InitStatus",
|
|
20
|
+
"ClientStatus",
|
|
21
|
+
"LogLevel",
|
|
22
|
+
"RegisterLogCallbackStatus",
|
|
23
|
+
"AccountFlags",
|
|
24
|
+
"TransferFlags",
|
|
25
|
+
"AccountFilterFlags",
|
|
26
|
+
"QueryFilterFlags",
|
|
27
|
+
"CreateAccountResult",
|
|
28
|
+
"CreateTransferResult",
|
|
29
|
+
"Account",
|
|
30
|
+
"Transfer",
|
|
31
|
+
"CreateAccountsResult",
|
|
32
|
+
"CreateTransfersResult",
|
|
33
|
+
"AccountFilter",
|
|
34
|
+
"AccountBalance",
|
|
35
|
+
"QueryFilter",
|
|
36
|
+
"InitParameters",
|
|
37
|
+
"AsyncStateMachineMixin",
|
|
38
|
+
"StateMachineMixin",
|
|
39
|
+
]
|
tigerbeetle/bindings.py
CHANGED
|
@@ -6,10 +6,21 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
import ctypes
|
|
8
8
|
import enum
|
|
9
|
+
import sys
|
|
10
|
+
from dataclasses import dataclass
|
|
9
11
|
from collections.abc import Callable # noqa: TCH003
|
|
10
12
|
from typing import Any
|
|
13
|
+
if sys.version_info >= (3, 11):
|
|
14
|
+
from typing import Self
|
|
15
|
+
else:
|
|
16
|
+
from typing_extensions import Self
|
|
11
17
|
|
|
12
|
-
from .lib import c_uint128,
|
|
18
|
+
from .lib import c_uint128, tbclient, validate_uint
|
|
19
|
+
|
|
20
|
+
# Use slots=True if the version of Python is new enough (3.10+) to support it.
|
|
21
|
+
if sys.version_info >= (3, 10):
|
|
22
|
+
# mypy: ignore assignment (3.10+) and unused-ignore (pre 3.10)
|
|
23
|
+
dataclass = dataclass(slots=True) # type: ignore[assignment, unused-ignore]
|
|
13
24
|
|
|
14
25
|
|
|
15
26
|
class Operation(enum.IntEnum):
|
|
@@ -236,13 +247,13 @@ class Transfer:
|
|
|
236
247
|
@dataclass
|
|
237
248
|
class CreateAccountsResult:
|
|
238
249
|
index: int = 0
|
|
239
|
-
result: CreateAccountResult =
|
|
250
|
+
result: CreateAccountResult = CreateAccountResult.OK
|
|
240
251
|
|
|
241
252
|
|
|
242
253
|
@dataclass
|
|
243
254
|
class CreateTransfersResult:
|
|
244
255
|
index: int = 0
|
|
245
|
-
result: CreateTransferResult =
|
|
256
|
+
result: CreateTransferResult = CreateTransferResult.OK
|
|
246
257
|
|
|
247
258
|
|
|
248
259
|
@dataclass
|
|
@@ -282,7 +293,7 @@ class QueryFilter:
|
|
|
282
293
|
|
|
283
294
|
class CPacket(ctypes.Structure):
|
|
284
295
|
@classmethod
|
|
285
|
-
def from_param(cls, obj):
|
|
296
|
+
def from_param(cls, obj: Any) -> Self:
|
|
286
297
|
validate_uint(bits=32, name="data_size", number=obj.data_size)
|
|
287
298
|
validate_uint(bits=16, name="user_tag", number=obj.user_tag)
|
|
288
299
|
validate_uint(bits=8, name="operation", number=obj.operation)
|
|
@@ -309,7 +320,7 @@ CPacket._fields_ = [ # noqa: SLF001
|
|
|
309
320
|
|
|
310
321
|
class CClient(ctypes.Structure):
|
|
311
322
|
@classmethod
|
|
312
|
-
def from_param(cls, obj):
|
|
323
|
+
def from_param(cls, obj: Any) -> Self:
|
|
313
324
|
return cls(
|
|
314
325
|
opaque=obj.opaque,
|
|
315
326
|
)
|
|
@@ -321,7 +332,7 @@ CClient._fields_ = [ # noqa: SLF001
|
|
|
321
332
|
|
|
322
333
|
class CAccount(ctypes.Structure):
|
|
323
334
|
@classmethod
|
|
324
|
-
def from_param(cls, obj):
|
|
335
|
+
def from_param(cls, obj: Any) -> Self:
|
|
325
336
|
validate_uint(bits=128, name="id", number=obj.id)
|
|
326
337
|
validate_uint(bits=128, name="debits_pending", number=obj.debits_pending)
|
|
327
338
|
validate_uint(bits=128, name="debits_posted", number=obj.debits_posted)
|
|
@@ -349,7 +360,7 @@ class CAccount(ctypes.Structure):
|
|
|
349
360
|
)
|
|
350
361
|
|
|
351
362
|
|
|
352
|
-
def to_python(self):
|
|
363
|
+
def to_python(self) -> Account:
|
|
353
364
|
return Account(
|
|
354
365
|
id=self.id.to_python(),
|
|
355
366
|
debits_pending=self.debits_pending.to_python(),
|
|
@@ -384,7 +395,7 @@ CAccount._fields_ = [ # noqa: SLF001
|
|
|
384
395
|
|
|
385
396
|
class CTransfer(ctypes.Structure):
|
|
386
397
|
@classmethod
|
|
387
|
-
def from_param(cls, obj):
|
|
398
|
+
def from_param(cls, obj: Any) -> Self:
|
|
388
399
|
validate_uint(bits=128, name="id", number=obj.id)
|
|
389
400
|
validate_uint(bits=128, name="debit_account_id", number=obj.debit_account_id)
|
|
390
401
|
validate_uint(bits=128, name="credit_account_id", number=obj.credit_account_id)
|
|
@@ -414,7 +425,7 @@ class CTransfer(ctypes.Structure):
|
|
|
414
425
|
)
|
|
415
426
|
|
|
416
427
|
|
|
417
|
-
def to_python(self):
|
|
428
|
+
def to_python(self) -> Transfer:
|
|
418
429
|
return Transfer(
|
|
419
430
|
id=self.id.to_python(),
|
|
420
431
|
debit_account_id=self.debit_account_id.to_python(),
|
|
@@ -450,7 +461,7 @@ CTransfer._fields_ = [ # noqa: SLF001
|
|
|
450
461
|
|
|
451
462
|
class CCreateAccountsResult(ctypes.Structure):
|
|
452
463
|
@classmethod
|
|
453
|
-
def from_param(cls, obj):
|
|
464
|
+
def from_param(cls, obj: Any) -> Self:
|
|
454
465
|
validate_uint(bits=32, name="index", number=obj.index)
|
|
455
466
|
return cls(
|
|
456
467
|
index=obj.index,
|
|
@@ -458,7 +469,7 @@ class CCreateAccountsResult(ctypes.Structure):
|
|
|
458
469
|
)
|
|
459
470
|
|
|
460
471
|
|
|
461
|
-
def to_python(self):
|
|
472
|
+
def to_python(self) -> CreateAccountsResult:
|
|
462
473
|
return CreateAccountsResult(
|
|
463
474
|
index=self.index,
|
|
464
475
|
result=CreateAccountResult(self.result),
|
|
@@ -472,7 +483,7 @@ CCreateAccountsResult._fields_ = [ # noqa: SLF001
|
|
|
472
483
|
|
|
473
484
|
class CCreateTransfersResult(ctypes.Structure):
|
|
474
485
|
@classmethod
|
|
475
|
-
def from_param(cls, obj):
|
|
486
|
+
def from_param(cls, obj: Any) -> Self:
|
|
476
487
|
validate_uint(bits=32, name="index", number=obj.index)
|
|
477
488
|
return cls(
|
|
478
489
|
index=obj.index,
|
|
@@ -480,7 +491,7 @@ class CCreateTransfersResult(ctypes.Structure):
|
|
|
480
491
|
)
|
|
481
492
|
|
|
482
493
|
|
|
483
|
-
def to_python(self):
|
|
494
|
+
def to_python(self) -> CreateTransfersResult:
|
|
484
495
|
return CreateTransfersResult(
|
|
485
496
|
index=self.index,
|
|
486
497
|
result=CreateTransferResult(self.result),
|
|
@@ -494,7 +505,7 @@ CCreateTransfersResult._fields_ = [ # noqa: SLF001
|
|
|
494
505
|
|
|
495
506
|
class CAccountFilter(ctypes.Structure):
|
|
496
507
|
@classmethod
|
|
497
|
-
def from_param(cls, obj):
|
|
508
|
+
def from_param(cls, obj: Any) -> Self:
|
|
498
509
|
validate_uint(bits=128, name="account_id", number=obj.account_id)
|
|
499
510
|
validate_uint(bits=128, name="user_data_128", number=obj.user_data_128)
|
|
500
511
|
validate_uint(bits=64, name="user_data_64", number=obj.user_data_64)
|
|
@@ -516,7 +527,7 @@ class CAccountFilter(ctypes.Structure):
|
|
|
516
527
|
)
|
|
517
528
|
|
|
518
529
|
|
|
519
|
-
def to_python(self):
|
|
530
|
+
def to_python(self) -> AccountFilter:
|
|
520
531
|
return AccountFilter(
|
|
521
532
|
account_id=self.account_id.to_python(),
|
|
522
533
|
user_data_128=self.user_data_128.to_python(),
|
|
@@ -545,7 +556,7 @@ CAccountFilter._fields_ = [ # noqa: SLF001
|
|
|
545
556
|
|
|
546
557
|
class CAccountBalance(ctypes.Structure):
|
|
547
558
|
@classmethod
|
|
548
|
-
def from_param(cls, obj):
|
|
559
|
+
def from_param(cls, obj: Any) -> Self:
|
|
549
560
|
validate_uint(bits=128, name="debits_pending", number=obj.debits_pending)
|
|
550
561
|
validate_uint(bits=128, name="debits_posted", number=obj.debits_posted)
|
|
551
562
|
validate_uint(bits=128, name="credits_pending", number=obj.credits_pending)
|
|
@@ -560,7 +571,7 @@ class CAccountBalance(ctypes.Structure):
|
|
|
560
571
|
)
|
|
561
572
|
|
|
562
573
|
|
|
563
|
-
def to_python(self):
|
|
574
|
+
def to_python(self) -> AccountBalance:
|
|
564
575
|
return AccountBalance(
|
|
565
576
|
debits_pending=self.debits_pending.to_python(),
|
|
566
577
|
debits_posted=self.debits_posted.to_python(),
|
|
@@ -581,7 +592,7 @@ CAccountBalance._fields_ = [ # noqa: SLF001
|
|
|
581
592
|
|
|
582
593
|
class CQueryFilter(ctypes.Structure):
|
|
583
594
|
@classmethod
|
|
584
|
-
def from_param(cls, obj):
|
|
595
|
+
def from_param(cls, obj: Any) -> Self:
|
|
585
596
|
validate_uint(bits=128, name="user_data_128", number=obj.user_data_128)
|
|
586
597
|
validate_uint(bits=64, name="user_data_64", number=obj.user_data_64)
|
|
587
598
|
validate_uint(bits=32, name="user_data_32", number=obj.user_data_32)
|
|
@@ -603,7 +614,7 @@ class CQueryFilter(ctypes.Structure):
|
|
|
603
614
|
)
|
|
604
615
|
|
|
605
616
|
|
|
606
|
-
def to_python(self):
|
|
617
|
+
def to_python(self) -> QueryFilter:
|
|
607
618
|
return QueryFilter(
|
|
608
619
|
user_data_128=self.user_data_128.to_python(),
|
|
609
620
|
user_data_64=self.user_data_64,
|
|
@@ -678,13 +689,13 @@ tb_client_submit.argtypes = [ctypes.POINTER(CClient), ctypes.POINTER(CPacket)]
|
|
|
678
689
|
tb_client_register_log_callback = tbclient.tb_client_register_log_callback
|
|
679
690
|
tb_client_register_log_callback.restype = RegisterLogCallbackStatus
|
|
680
691
|
# Need to pass in None to clear - ctypes will error if argtypes is set.
|
|
681
|
-
#tb_client_register_log_callback.argtypes = [LogHandler, ctypes.c_bool]
|
|
692
|
+
# tb_client_register_log_callback.argtypes = [LogHandler, ctypes.c_bool]
|
|
682
693
|
|
|
683
694
|
|
|
684
695
|
class AsyncStateMachineMixin:
|
|
685
696
|
_submit: Callable[[Operation, Any, Any, Any], Any]
|
|
686
697
|
async def create_accounts(self, accounts: list[Account]) -> list[CreateAccountsResult]:
|
|
687
|
-
return await self._submit(
|
|
698
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
688
699
|
Operation.CREATE_ACCOUNTS,
|
|
689
700
|
accounts,
|
|
690
701
|
CAccount,
|
|
@@ -692,7 +703,7 @@ class AsyncStateMachineMixin:
|
|
|
692
703
|
)
|
|
693
704
|
|
|
694
705
|
async def create_transfers(self, transfers: list[Transfer]) -> list[CreateTransfersResult]:
|
|
695
|
-
return await self._submit(
|
|
706
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
696
707
|
Operation.CREATE_TRANSFERS,
|
|
697
708
|
transfers,
|
|
698
709
|
CTransfer,
|
|
@@ -700,7 +711,7 @@ class AsyncStateMachineMixin:
|
|
|
700
711
|
)
|
|
701
712
|
|
|
702
713
|
async def lookup_accounts(self, accounts: list[int]) -> list[Account]:
|
|
703
|
-
return await self._submit(
|
|
714
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
704
715
|
Operation.LOOKUP_ACCOUNTS,
|
|
705
716
|
accounts,
|
|
706
717
|
c_uint128,
|
|
@@ -708,7 +719,7 @@ class AsyncStateMachineMixin:
|
|
|
708
719
|
)
|
|
709
720
|
|
|
710
721
|
async def lookup_transfers(self, transfers: list[int]) -> list[Transfer]:
|
|
711
|
-
return await self._submit(
|
|
722
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
712
723
|
Operation.LOOKUP_TRANSFERS,
|
|
713
724
|
transfers,
|
|
714
725
|
c_uint128,
|
|
@@ -716,7 +727,7 @@ class AsyncStateMachineMixin:
|
|
|
716
727
|
)
|
|
717
728
|
|
|
718
729
|
async def get_account_transfers(self, filter: AccountFilter) -> list[Transfer]:
|
|
719
|
-
return await self._submit(
|
|
730
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
720
731
|
Operation.GET_ACCOUNT_TRANSFERS,
|
|
721
732
|
[filter],
|
|
722
733
|
CAccountFilter,
|
|
@@ -724,7 +735,7 @@ class AsyncStateMachineMixin:
|
|
|
724
735
|
)
|
|
725
736
|
|
|
726
737
|
async def get_account_balances(self, filter: AccountFilter) -> list[AccountBalance]:
|
|
727
|
-
return await self._submit(
|
|
738
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
728
739
|
Operation.GET_ACCOUNT_BALANCES,
|
|
729
740
|
[filter],
|
|
730
741
|
CAccountFilter,
|
|
@@ -732,7 +743,7 @@ class AsyncStateMachineMixin:
|
|
|
732
743
|
)
|
|
733
744
|
|
|
734
745
|
async def query_accounts(self, query_filter: QueryFilter) -> list[Account]:
|
|
735
|
-
return await self._submit(
|
|
746
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
736
747
|
Operation.QUERY_ACCOUNTS,
|
|
737
748
|
[query_filter],
|
|
738
749
|
CQueryFilter,
|
|
@@ -740,7 +751,7 @@ class AsyncStateMachineMixin:
|
|
|
740
751
|
)
|
|
741
752
|
|
|
742
753
|
async def query_transfers(self, query_filter: QueryFilter) -> list[Transfer]:
|
|
743
|
-
return await self._submit(
|
|
754
|
+
return await self._submit( # type: ignore[no-any-return]
|
|
744
755
|
Operation.QUERY_TRANSFERS,
|
|
745
756
|
[query_filter],
|
|
746
757
|
CQueryFilter,
|
|
@@ -752,7 +763,7 @@ class AsyncStateMachineMixin:
|
|
|
752
763
|
class StateMachineMixin:
|
|
753
764
|
_submit: Callable[[Operation, Any, Any, Any], Any]
|
|
754
765
|
def create_accounts(self, accounts: list[Account]) -> list[CreateAccountsResult]:
|
|
755
|
-
return self._submit(
|
|
766
|
+
return self._submit( # type: ignore[no-any-return]
|
|
756
767
|
Operation.CREATE_ACCOUNTS,
|
|
757
768
|
accounts,
|
|
758
769
|
CAccount,
|
|
@@ -760,7 +771,7 @@ class StateMachineMixin:
|
|
|
760
771
|
)
|
|
761
772
|
|
|
762
773
|
def create_transfers(self, transfers: list[Transfer]) -> list[CreateTransfersResult]:
|
|
763
|
-
return self._submit(
|
|
774
|
+
return self._submit( # type: ignore[no-any-return]
|
|
764
775
|
Operation.CREATE_TRANSFERS,
|
|
765
776
|
transfers,
|
|
766
777
|
CTransfer,
|
|
@@ -768,7 +779,7 @@ class StateMachineMixin:
|
|
|
768
779
|
)
|
|
769
780
|
|
|
770
781
|
def lookup_accounts(self, accounts: list[int]) -> list[Account]:
|
|
771
|
-
return self._submit(
|
|
782
|
+
return self._submit( # type: ignore[no-any-return]
|
|
772
783
|
Operation.LOOKUP_ACCOUNTS,
|
|
773
784
|
accounts,
|
|
774
785
|
c_uint128,
|
|
@@ -776,7 +787,7 @@ class StateMachineMixin:
|
|
|
776
787
|
)
|
|
777
788
|
|
|
778
789
|
def lookup_transfers(self, transfers: list[int]) -> list[Transfer]:
|
|
779
|
-
return self._submit(
|
|
790
|
+
return self._submit( # type: ignore[no-any-return]
|
|
780
791
|
Operation.LOOKUP_TRANSFERS,
|
|
781
792
|
transfers,
|
|
782
793
|
c_uint128,
|
|
@@ -784,7 +795,7 @@ class StateMachineMixin:
|
|
|
784
795
|
)
|
|
785
796
|
|
|
786
797
|
def get_account_transfers(self, filter: AccountFilter) -> list[Transfer]:
|
|
787
|
-
return self._submit(
|
|
798
|
+
return self._submit( # type: ignore[no-any-return]
|
|
788
799
|
Operation.GET_ACCOUNT_TRANSFERS,
|
|
789
800
|
[filter],
|
|
790
801
|
CAccountFilter,
|
|
@@ -792,7 +803,7 @@ class StateMachineMixin:
|
|
|
792
803
|
)
|
|
793
804
|
|
|
794
805
|
def get_account_balances(self, filter: AccountFilter) -> list[AccountBalance]:
|
|
795
|
-
return self._submit(
|
|
806
|
+
return self._submit( # type: ignore[no-any-return]
|
|
796
807
|
Operation.GET_ACCOUNT_BALANCES,
|
|
797
808
|
[filter],
|
|
798
809
|
CAccountFilter,
|
|
@@ -800,7 +811,7 @@ class StateMachineMixin:
|
|
|
800
811
|
)
|
|
801
812
|
|
|
802
813
|
def query_accounts(self, query_filter: QueryFilter) -> list[Account]:
|
|
803
|
-
return self._submit(
|
|
814
|
+
return self._submit( # type: ignore[no-any-return]
|
|
804
815
|
Operation.QUERY_ACCOUNTS,
|
|
805
816
|
[query_filter],
|
|
806
817
|
CQueryFilter,
|
|
@@ -808,7 +819,7 @@ class StateMachineMixin:
|
|
|
808
819
|
)
|
|
809
820
|
|
|
810
821
|
def query_transfers(self, query_filter: QueryFilter) -> list[Transfer]:
|
|
811
|
-
return self._submit(
|
|
822
|
+
return self._submit( # type: ignore[no-any-return]
|
|
812
823
|
Operation.QUERY_TRANSFERS,
|
|
813
824
|
[query_filter],
|
|
814
825
|
CQueryFilter,
|
tigerbeetle/client.py
CHANGED
|
@@ -4,11 +4,16 @@ import asyncio
|
|
|
4
4
|
import ctypes
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
+
import sys
|
|
7
8
|
import threading
|
|
8
9
|
import time
|
|
9
10
|
from collections.abc import Callable # noqa: TCH003
|
|
10
11
|
from dataclasses import dataclass
|
|
11
12
|
from typing import Any
|
|
13
|
+
if sys.version_info >= (3, 11):
|
|
14
|
+
from typing import Self
|
|
15
|
+
else:
|
|
16
|
+
from typing_extensions import Self
|
|
12
17
|
|
|
13
18
|
from . import bindings
|
|
14
19
|
from .lib import tb_assert, c_uint128
|
|
@@ -17,11 +22,11 @@ logger = logging.getLogger("tigerbeetle")
|
|
|
17
22
|
|
|
18
23
|
|
|
19
24
|
class AtomicInteger:
|
|
20
|
-
def __init__(self, value=0):
|
|
25
|
+
def __init__(self, value: int = 0) -> None:
|
|
21
26
|
self._value = value
|
|
22
27
|
self._lock = threading.Lock()
|
|
23
28
|
|
|
24
|
-
def increment(self):
|
|
29
|
+
def increment(self) -> int:
|
|
25
30
|
with self._lock:
|
|
26
31
|
self._value += 1
|
|
27
32
|
return self._value
|
|
@@ -45,30 +50,43 @@ class InflightPacket:
|
|
|
45
50
|
operation: bindings.Operation
|
|
46
51
|
c_event_type: Any
|
|
47
52
|
c_result_type: Any
|
|
48
|
-
on_completion: Callable | None
|
|
53
|
+
on_completion: Callable[[Self], None] | None
|
|
49
54
|
on_completion_context: CompletionContextSync | CompletionContextAsync | None
|
|
50
55
|
|
|
56
|
+
class _IDGenerator:
|
|
57
|
+
"""
|
|
58
|
+
Generator for Universally Unique and Sortable Identifiers as a 128-bit integers, based on ULIDs.
|
|
59
|
+
|
|
60
|
+
Keeps a monotonically increasing millisecond timestamp between calls to `.generate()`.
|
|
61
|
+
"""
|
|
62
|
+
def __init__(self) -> None:
|
|
63
|
+
self._time_ms_last = time.time_ns() // (1000 * 1000)
|
|
64
|
+
|
|
65
|
+
def generate(self) -> int:
|
|
66
|
+
time_ms = time.time_ns() // (1000 * 1000)
|
|
67
|
+
|
|
68
|
+
# Ensure time_ms monotonically increases.
|
|
69
|
+
if time_ms <= self._time_ms_last:
|
|
70
|
+
time_ms = self._time_ms_last
|
|
71
|
+
else:
|
|
72
|
+
self._time_ms_last = time_ms
|
|
73
|
+
|
|
74
|
+
randomness = os.urandom(10)
|
|
75
|
+
|
|
76
|
+
return int.from_bytes(
|
|
77
|
+
time_ms.to_bytes(6, "big") + randomness,
|
|
78
|
+
"big",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Module-level singleton instance.
|
|
82
|
+
_id_generator = _IDGenerator()
|
|
51
83
|
|
|
52
84
|
|
|
53
85
|
def id() -> int:
|
|
54
86
|
"""
|
|
55
87
|
Generates a Universally Unique and Sortable Identifier as a 128-bit integer. Based on ULIDs.
|
|
56
88
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Ensure time_ms monotonically increases.
|
|
60
|
-
time_ms_last = getattr(id, "_time_ms_last", 0)
|
|
61
|
-
if time_ms <= time_ms_last:
|
|
62
|
-
time_ms = time_ms_last
|
|
63
|
-
else:
|
|
64
|
-
id._time_ms_last = time_ms
|
|
65
|
-
|
|
66
|
-
randomness = os.urandom(10)
|
|
67
|
-
|
|
68
|
-
return int.from_bytes(
|
|
69
|
-
time_ms.to_bytes(6, "big") + randomness,
|
|
70
|
-
"big",
|
|
71
|
-
)
|
|
89
|
+
return _id_generator.generate()
|
|
72
90
|
|
|
73
91
|
|
|
74
92
|
amount_max = (2 ** 128) - 1
|
|
@@ -137,15 +155,15 @@ class Client:
|
|
|
137
155
|
c_event_type=c_event_type,
|
|
138
156
|
c_result_type=c_result_type)
|
|
139
157
|
|
|
140
|
-
def close(self):
|
|
158
|
+
def close(self) -> None:
|
|
141
159
|
bindings.tb_client_deinit(ctypes.byref(self._client))
|
|
142
160
|
tb_assert(self._client is not None)
|
|
143
161
|
tb_assert(len(self._inflight_packets) == 0)
|
|
144
162
|
del Client._clients[self._client_key]
|
|
145
163
|
|
|
146
164
|
@staticmethod
|
|
147
|
-
@bindings.OnCompletion
|
|
148
|
-
def _c_on_completion(completion_ctx, packet, timestamp, bytes_ptr, len_):
|
|
165
|
+
@bindings.OnCompletion # type: ignore[misc]
|
|
166
|
+
def _c_on_completion(completion_ctx: int, packet: Any, timestamp: int, bytes_ptr: Any, len_: int) -> None:
|
|
149
167
|
"""
|
|
150
168
|
Invoked in a separate thread
|
|
151
169
|
"""
|
|
@@ -156,7 +174,9 @@ class Client:
|
|
|
156
174
|
|
|
157
175
|
if packet[0].status != bindings.PacketStatus.OK.value:
|
|
158
176
|
inflight_packet.response = PacketError(repr(bindings.PacketStatus(packet[0].status)))
|
|
159
|
-
|
|
177
|
+
if inflight_packet.on_completion is None:
|
|
178
|
+
# Can't use tb_assert here, as mypy complains later that it might be None.
|
|
179
|
+
raise TypeError("inflight_packet.on_completion not set")
|
|
160
180
|
inflight_packet.on_completion(inflight_packet)
|
|
161
181
|
return
|
|
162
182
|
|
|
@@ -174,22 +194,27 @@ class Client:
|
|
|
174
194
|
|
|
175
195
|
inflight_packet.response = results
|
|
176
196
|
|
|
177
|
-
|
|
197
|
+
if inflight_packet.on_completion is None:
|
|
198
|
+
# Can't use tb_assert here, as mypy complains later that it might be None.
|
|
199
|
+
raise TypeError("inflight_packet.on_completion not set")
|
|
200
|
+
|
|
178
201
|
inflight_packet.on_completion(inflight_packet)
|
|
179
202
|
|
|
180
|
-
def __enter__(self):
|
|
203
|
+
def __enter__(self) -> Self:
|
|
181
204
|
return self
|
|
182
205
|
|
|
183
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
206
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
184
207
|
self.close()
|
|
185
208
|
|
|
186
209
|
|
|
187
210
|
class ClientSync(Client, bindings.StateMachineMixin):
|
|
188
|
-
def _on_completion(self, inflight_packet):
|
|
211
|
+
def _on_completion(self, inflight_packet: InflightPacket) -> None:
|
|
212
|
+
if not isinstance(inflight_packet.on_completion_context, CompletionContextSync):
|
|
213
|
+
raise TypeError(repr(inflight_packet.on_completion_context))
|
|
189
214
|
inflight_packet.on_completion_context.event.set()
|
|
190
215
|
|
|
191
216
|
def _submit(self, operation: bindings.Operation, operations: list[Any],
|
|
192
|
-
c_event_type: Any, c_result_type: Any):
|
|
217
|
+
c_event_type: Any, c_result_type: Any) -> Any:
|
|
193
218
|
inflight_packet = self._acquire_packet(operation, operations, c_event_type, c_result_type)
|
|
194
219
|
self._inflight_packets[inflight_packet.packet.user_data] = inflight_packet
|
|
195
220
|
|
|
@@ -212,22 +237,26 @@ class ClientSync(Client, bindings.StateMachineMixin):
|
|
|
212
237
|
|
|
213
238
|
|
|
214
239
|
class ClientAsync(Client, bindings.AsyncStateMachineMixin):
|
|
215
|
-
def _on_completion(self, inflight_packet):
|
|
240
|
+
def _on_completion(self, inflight_packet: InflightPacket) -> None:
|
|
216
241
|
"""
|
|
217
242
|
Called by Client._c_on_completion, which itself is called from a different thread. Use
|
|
218
243
|
`call_soon_threadsafe` to return to the thread of the event loop the request was invoked
|
|
219
244
|
from, so _trigger_event() can trigger the async event and allow the client to progress.
|
|
220
245
|
"""
|
|
246
|
+
if not isinstance(inflight_packet.on_completion_context, CompletionContextAsync):
|
|
247
|
+
raise TypeError(repr(inflight_packet.on_completion_context))
|
|
221
248
|
inflight_packet.on_completion_context.loop.call_soon_threadsafe(
|
|
222
249
|
self._trigger_event,
|
|
223
250
|
inflight_packet
|
|
224
251
|
)
|
|
225
252
|
|
|
226
|
-
def _trigger_event(self, inflight_packet):
|
|
253
|
+
def _trigger_event(self, inflight_packet:InflightPacket) -> None:
|
|
254
|
+
if not isinstance(inflight_packet.on_completion_context, CompletionContextAsync):
|
|
255
|
+
raise TypeError(repr(inflight_packet.on_completion_context))
|
|
227
256
|
inflight_packet.on_completion_context.event.set()
|
|
228
257
|
|
|
229
258
|
async def _submit(self, operation: bindings.Operation, operations: Any,
|
|
230
|
-
c_event_type: Any, c_result_type: Any):
|
|
259
|
+
c_event_type: Any, c_result_type: Any) -> Any:
|
|
231
260
|
inflight_packet = self._acquire_packet(operation, operations, c_event_type, c_result_type)
|
|
232
261
|
self._inflight_packets[inflight_packet.packet.user_data] = inflight_packet
|
|
233
262
|
|
|
@@ -252,8 +281,8 @@ class ClientAsync(Client, bindings.AsyncStateMachineMixin):
|
|
|
252
281
|
return inflight_packet.response
|
|
253
282
|
|
|
254
283
|
|
|
255
|
-
@bindings.LogHandler
|
|
256
|
-
def log_handler(level_zig, message_ptr, message_len):
|
|
284
|
+
@bindings.LogHandler # type: ignore[misc]
|
|
285
|
+
def log_handler(level_zig: bindings.LogLevel, message_ptr: Any, message_len: int) -> None:
|
|
257
286
|
level_python = {
|
|
258
287
|
bindings.LogLevel.ERR: logging.ERROR,
|
|
259
288
|
bindings.LogLevel.WARN: logging.WARNING,
|
|
@@ -265,10 +294,15 @@ def log_handler(level_zig, message_ptr, message_len):
|
|
|
265
294
|
tb_assert(bindings.tb_client_register_log_callback(log_handler, True) ==
|
|
266
295
|
bindings.RegisterLogCallbackStatus.SUCCESS)
|
|
267
296
|
|
|
268
|
-
|
|
297
|
+
|
|
298
|
+
def configure_logging(
|
|
299
|
+
*,
|
|
300
|
+
debug: bool,
|
|
301
|
+
handler: Callable[[bindings.LogLevel, Any, int], None] = log_handler,
|
|
302
|
+
) -> None:
|
|
269
303
|
# First disable the existing log handler, before enabling the new one.
|
|
270
304
|
tb_assert(bindings.tb_client_register_log_callback(None, debug) ==
|
|
271
305
|
bindings.RegisterLogCallbackStatus.SUCCESS)
|
|
272
306
|
|
|
273
|
-
tb_assert(bindings.tb_client_register_log_callback(
|
|
307
|
+
tb_assert(bindings.tb_client_register_log_callback(handler, debug) ==
|
|
274
308
|
bindings.RegisterLogCallbackStatus.SUCCESS)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
tigerbeetle/lib.py
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import ctypes
|
|
2
|
-
import dataclasses
|
|
3
2
|
import platform
|
|
3
|
+
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
if sys.version_info >= (3, 11):
|
|
7
|
+
from typing import Self
|
|
8
|
+
else:
|
|
9
|
+
from typing_extensions import Self
|
|
5
10
|
|
|
6
11
|
|
|
7
12
|
class NativeError(Exception):
|
|
8
13
|
pass
|
|
9
14
|
|
|
15
|
+
|
|
10
16
|
class IntegerOverflowError(ValueError):
|
|
11
17
|
pass
|
|
12
18
|
|
|
13
19
|
|
|
14
|
-
def _load_tbclient():
|
|
20
|
+
def _load_tbclient() -> ctypes.CDLL:
|
|
15
21
|
prefix = ""
|
|
16
22
|
arch = ""
|
|
17
23
|
system = ""
|
|
@@ -50,39 +56,35 @@ def _load_tbclient():
|
|
|
50
56
|
|
|
51
57
|
source_path = Path(__file__)
|
|
52
58
|
source_dir = source_path.parent
|
|
53
|
-
library_path =
|
|
59
|
+
library_path = (
|
|
60
|
+
source_dir / "lib" / f"{arch}-{system}{linux_libc}" / f"{prefix}tb_client{suffix}"
|
|
61
|
+
)
|
|
54
62
|
return ctypes.CDLL(str(library_path))
|
|
55
63
|
|
|
56
64
|
|
|
57
|
-
def validate_uint(*, bits: int, name: str, number: int):
|
|
65
|
+
def validate_uint(*, bits: int, name: str, number: int) -> None:
|
|
58
66
|
if number > 2**bits - 1:
|
|
59
67
|
raise IntegerOverflowError(f"{name}=={number} is too large to fit in {bits} bits")
|
|
60
68
|
if number < 0:
|
|
61
69
|
raise IntegerOverflowError(f"{name}=={number} cannot be negative")
|
|
62
70
|
|
|
63
71
|
|
|
64
|
-
class c_uint128(ctypes.Structure):
|
|
65
|
-
_fields_ = [("_low", ctypes.c_uint64), ("_high", ctypes.c_uint64)]
|
|
72
|
+
class c_uint128(ctypes.Structure): # noqa: N801
|
|
73
|
+
_fields_ = [("_low", ctypes.c_uint64), ("_high", ctypes.c_uint64)] # noqa: RUF012
|
|
66
74
|
|
|
67
75
|
@classmethod
|
|
68
|
-
def from_param(cls, obj):
|
|
69
|
-
return cls(_high=obj >> 64, _low=obj &
|
|
70
|
-
|
|
71
|
-
def to_python(self):
|
|
72
|
-
return self._high << 64 | self._low
|
|
76
|
+
def from_param(cls, obj: int) -> Self:
|
|
77
|
+
return cls(_high=obj >> 64, _low=obj & 0xFFFFFFFFFFFFFFFF)
|
|
73
78
|
|
|
79
|
+
def to_python(self) -> int:
|
|
80
|
+
return int(self._high << 64 | self._low)
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
try:
|
|
77
|
-
dataclass = dataclasses.dataclass(slots=True)
|
|
78
|
-
except TypeError:
|
|
79
|
-
dataclass = dataclasses.dataclass()
|
|
80
|
-
|
|
81
|
-
def tb_assert(value):
|
|
82
|
+
def tb_assert(value: Any) -> None:
|
|
82
83
|
"""
|
|
83
84
|
Python's built-in assert can be silently disabled if Python is run with -O.
|
|
84
85
|
"""
|
|
85
86
|
if not value:
|
|
86
87
|
raise AssertionError()
|
|
87
88
|
|
|
89
|
+
|
|
88
90
|
tbclient = _load_tbclient()
|
tigerbeetle/py.typed
ADDED
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigerbeetle
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.56
|
|
4
4
|
Summary: The TigerBeetle client for Python.
|
|
5
5
|
Project-URL: Homepage, https://github.com/tigerbeetle/tigerbeetle
|
|
6
6
|
Project-URL: Issues, https://github.com/tigerbeetle/tigerbeetle/issues
|
|
@@ -67,7 +67,7 @@ features of TigerBeetle.
|
|
|
67
67
|
* [Basic](/src/clients/python/samples/basic/): Create two accounts and transfer an amount between them.
|
|
68
68
|
* [Two-Phase Transfer](/src/clients/python/samples/two-phase/): Create two accounts and start a pending transfer between
|
|
69
69
|
them, then post the transfer.
|
|
70
|
-
* [Many Two-Phase Transfers](/src/clients/python/samples/two-phase-many/): Create two accounts and start a number of pending
|
|
70
|
+
* [Many Two-Phase Transfers](/src/clients/python/samples/two-phase-many/): Create two accounts and start a number of pending transfers
|
|
71
71
|
between them, posting and voiding alternating transfers.
|
|
72
72
|
## Creating a Client
|
|
73
73
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
tigerbeetle/__init__.py,sha256=L9lyq47kzyZO0uXR455LXZTXWrt7yxfs-pFs8Mfv-r4,944
|
|
2
|
+
tigerbeetle/bindings.py,sha256=NSND7UuFpnd4TRBDA9aDJLRS115QNi3m0dVksekVlZg,28155
|
|
3
|
+
tigerbeetle/client.py,sha256=QaHWXeDvLYGia1IOUArR5rNShka9y3GwveFELCcoXwY,10967
|
|
4
|
+
tigerbeetle/lib.py,sha256=1Qln-Z4ZoCHJJ43sXlJJEs7-ZY5aZJjNNu7eISooAK8,2432
|
|
5
|
+
tigerbeetle/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
tigerbeetle/lib/aarch64-linux-gnu.2.27/libtb_client.so,sha256=qPbLSTvbDH49ePtGNuxciy9QYrd_LGSsRkMeEJNNa98,692104
|
|
7
|
+
tigerbeetle/lib/aarch64-linux-musl/libtb_client.so,sha256=BZ6a2nbclS4g0HU4495b9DipBefrLEMvZgUD0Jpi4Os,690384
|
|
8
|
+
tigerbeetle/lib/aarch64-macos/libtb_client.dylib,sha256=zEAJLzDJjIBhSG2tgWww39zq7Rl-Qsf5Q4lmD6UX2M8,700224
|
|
9
|
+
tigerbeetle/lib/x86_64-linux-gnu.2.27/libtb_client.so,sha256=EgoOoAELkFc-nHgHY-AlTViVO2Sx35EqF4RbFr5lMZY,815368
|
|
10
|
+
tigerbeetle/lib/x86_64-linux-musl/libtb_client.so,sha256=zoxZYw_eH0l2GriVIGr1c8_hdNk33TyVCKhbttYnKGM,813816
|
|
11
|
+
tigerbeetle/lib/x86_64-macos/libtb_client.dylib,sha256=KQaKRbtqzsIrzBcJxejIU0b8E9EaKzXgxz_MF7BpP98,792558
|
|
12
|
+
tigerbeetle/lib/x86_64-windows/tb_client.dll,sha256=dtB6y1LLLrI9swXI98MS8kKxjuMf0rHUXBauqXhkjMc,663552
|
|
13
|
+
tigerbeetle-0.16.56.dist-info/METADATA,sha256=70k1J6tzY0I04K4xqCKjDCAB8wYI3uict2-j79Ym3xg,23268
|
|
14
|
+
tigerbeetle-0.16.56.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
tigerbeetle-0.16.56.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
tigerbeetle/__init__.py,sha256=3cmov0HPkByJw1znLKoTqIuZUEeEQMEg1fflWY3ty9A,168
|
|
2
|
-
tigerbeetle/bindings.py,sha256=syIRjAQQfoE15iGK6kxmJLToRdU2KWDY0nnu6u-t6xk,26978
|
|
3
|
-
tigerbeetle/client.py,sha256=oGg1vAFqI8UZ5awI3sshZfxUdBsroUKwgp2rUa75lFc,9255
|
|
4
|
-
tigerbeetle/lib.py,sha256=JKreDxdw_YejtH7F135_m6I6mNBSwjokaI6QicKm7cc,2425
|
|
5
|
-
tigerbeetle/lib/aarch64-linux-gnu.2.27/libtb_client.so,sha256=xNAtANcQEmaIE-UyEvHtDUOHHUPbQIKCD9wZ0yWNZAw,692104
|
|
6
|
-
tigerbeetle/lib/aarch64-linux-musl/libtb_client.so,sha256=5glgnm7FE-OO9XFhEtGncnErrUUfUw3l1MJ09RVN9-k,690384
|
|
7
|
-
tigerbeetle/lib/aarch64-macos/libtb_client.dylib,sha256=ui8UzQVF-8pKKyTcJERpoA-K-HKSkoZbL05B4nhChnw,700224
|
|
8
|
-
tigerbeetle/lib/x86_64-linux-gnu.2.27/libtb_client.so,sha256=Sei05ax48ZaZGwFrTxHZwTiV0y63izghAR928kSdwzE,815368
|
|
9
|
-
tigerbeetle/lib/x86_64-linux-musl/libtb_client.so,sha256=WZcI2WFyJj32wjF1bWYNRBXdchSyYJKsKmvJccYgAcI,813816
|
|
10
|
-
tigerbeetle/lib/x86_64-macos/libtb_client.dylib,sha256=AsRNVcxRfR8MNRk-vFol-ptwA3i6iz2scT4BAK7ykp0,792558
|
|
11
|
-
tigerbeetle/lib/x86_64-windows/tb_client.dll,sha256=Wx0pyFh73tSyT_i3oEqX1qRe2OJyUpLYedBdT1m578M,663552
|
|
12
|
-
tigerbeetle-0.16.54.dist-info/METADATA,sha256=U-ElpL8SddbwHPuNVMArgYyO2Tb5yF8k_zzg9egwFs8,23267
|
|
13
|
-
tigerbeetle-0.16.54.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
-
tigerbeetle-0.16.54.dist-info/RECORD,,
|
|
File without changes
|