kafka-python 3.0.0__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.
- kafka/__init__.py +34 -0
- kafka/__main__.py +5 -0
- kafka/admin/__init__.py +29 -0
- kafka/admin/__main__.py +5 -0
- kafka/admin/_acls.py +355 -0
- kafka/admin/_cluster.py +359 -0
- kafka/admin/_configs.py +479 -0
- kafka/admin/_groups.py +754 -0
- kafka/admin/_partitions.py +595 -0
- kafka/admin/_topics.py +281 -0
- kafka/admin/_transactions.py +450 -0
- kafka/admin/_users.py +194 -0
- kafka/admin/client.py +373 -0
- kafka/benchmarks/__init__.py +0 -0
- kafka/benchmarks/consumer_performance.py +138 -0
- kafka/benchmarks/load_example.py +109 -0
- kafka/benchmarks/producer_encode_path.py +201 -0
- kafka/benchmarks/producer_performance.py +161 -0
- kafka/benchmarks/profile_protocol.py +138 -0
- kafka/benchmarks/protocol_old_vs_new.py +447 -0
- kafka/benchmarks/record_batch_compose.py +77 -0
- kafka/benchmarks/record_batch_read.py +82 -0
- kafka/benchmarks/varint_speed.py +426 -0
- kafka/cli/__init__.py +36 -0
- kafka/cli/admin/__init__.py +117 -0
- kafka/cli/admin/acls/__init__.py +9 -0
- kafka/cli/admin/acls/common.py +76 -0
- kafka/cli/admin/acls/create.py +19 -0
- kafka/cli/admin/acls/delete.py +23 -0
- kafka/cli/admin/acls/describe.py +16 -0
- kafka/cli/admin/cluster/__init__.py +14 -0
- kafka/cli/admin/cluster/describe.py +11 -0
- kafka/cli/admin/cluster/describe_quorum.py +11 -0
- kafka/cli/admin/cluster/features.py +52 -0
- kafka/cli/admin/cluster/log_dirs.py +43 -0
- kafka/cli/admin/cluster/versions.py +33 -0
- kafka/cli/admin/configs/__init__.py +10 -0
- kafka/cli/admin/configs/alter.py +43 -0
- kafka/cli/admin/configs/common.py +17 -0
- kafka/cli/admin/configs/describe.py +30 -0
- kafka/cli/admin/configs/list.py +16 -0
- kafka/cli/admin/configs/reset.py +20 -0
- kafka/cli/admin/groups/__init__.py +16 -0
- kafka/cli/admin/groups/alter_offsets.py +30 -0
- kafka/cli/admin/groups/delete.py +11 -0
- kafka/cli/admin/groups/delete_offsets.py +29 -0
- kafka/cli/admin/groups/describe.py +11 -0
- kafka/cli/admin/groups/list.py +28 -0
- kafka/cli/admin/groups/list_offsets.py +29 -0
- kafka/cli/admin/groups/remove_members.py +40 -0
- kafka/cli/admin/groups/reset_offsets.py +139 -0
- kafka/cli/admin/partitions/__init__.py +21 -0
- kafka/cli/admin/partitions/alter_reassignments.py +37 -0
- kafka/cli/admin/partitions/create.py +27 -0
- kafka/cli/admin/partitions/delete_records.py +31 -0
- kafka/cli/admin/partitions/describe.py +36 -0
- kafka/cli/admin/partitions/elect_leaders.py +53 -0
- kafka/cli/admin/partitions/list_offsets.py +88 -0
- kafka/cli/admin/partitions/list_reassignments.py +35 -0
- kafka/cli/admin/topics/__init__.py +10 -0
- kafka/cli/admin/topics/create.py +13 -0
- kafka/cli/admin/topics/delete.py +19 -0
- kafka/cli/admin/topics/describe.py +18 -0
- kafka/cli/admin/topics/list.py +11 -0
- kafka/cli/admin/transactions/__init__.py +17 -0
- kafka/cli/admin/transactions/abort.py +38 -0
- kafka/cli/admin/transactions/describe.py +24 -0
- kafka/cli/admin/transactions/describe_producers.py +29 -0
- kafka/cli/admin/transactions/find_hanging.py +26 -0
- kafka/cli/admin/transactions/list.py +37 -0
- kafka/cli/admin/users/__init__.py +8 -0
- kafka/cli/admin/users/alter_user_scram_credentials.py +34 -0
- kafka/cli/admin/users/describe_user_scram_credentials.py +15 -0
- kafka/cli/common.py +95 -0
- kafka/cli/consumer/__init__.py +63 -0
- kafka/cli/producer/__init__.py +57 -0
- kafka/cluster.py +824 -0
- kafka/codec.py +325 -0
- kafka/consumer/__init__.py +5 -0
- kafka/consumer/__main__.py +5 -0
- kafka/consumer/fetcher.py +2012 -0
- kafka/consumer/group.py +1347 -0
- kafka/consumer/subscription_state.py +897 -0
- kafka/coordinator/__init__.py +0 -0
- kafka/coordinator/assignors/__init__.py +0 -0
- kafka/coordinator/assignors/abstract.py +90 -0
- kafka/coordinator/assignors/cooperative_sticky.py +167 -0
- kafka/coordinator/assignors/range.py +81 -0
- kafka/coordinator/assignors/roundrobin.py +101 -0
- kafka/coordinator/assignors/sticky/StickyAssignorUserData.json +37 -0
- kafka/coordinator/assignors/sticky/__init__.py +0 -0
- kafka/coordinator/assignors/sticky/partition_movements.py +149 -0
- kafka/coordinator/assignors/sticky/sorted_set.py +63 -0
- kafka/coordinator/assignors/sticky/sticky_assignor.py +665 -0
- kafka/coordinator/assignors/sticky/user_data.py +8 -0
- kafka/coordinator/base.py +1215 -0
- kafka/coordinator/consumer.py +1224 -0
- kafka/coordinator/heartbeat.py +82 -0
- kafka/coordinator/subscription.py +34 -0
- kafka/errors.py +1004 -0
- kafka/future.py +166 -0
- kafka/metrics/__init__.py +13 -0
- kafka/metrics/compound_stat.py +33 -0
- kafka/metrics/dict_reporter.py +81 -0
- kafka/metrics/kafka_metric.py +36 -0
- kafka/metrics/measurable.py +27 -0
- kafka/metrics/measurable_stat.py +13 -0
- kafka/metrics/metric_config.py +33 -0
- kafka/metrics/metric_name.py +105 -0
- kafka/metrics/metrics.py +261 -0
- kafka/metrics/metrics_reporter.py +53 -0
- kafka/metrics/quota.py +41 -0
- kafka/metrics/stat.py +19 -0
- kafka/metrics/stats/__init__.py +15 -0
- kafka/metrics/stats/avg.py +24 -0
- kafka/metrics/stats/count.py +17 -0
- kafka/metrics/stats/histogram.py +99 -0
- kafka/metrics/stats/max_stat.py +17 -0
- kafka/metrics/stats/min_stat.py +19 -0
- kafka/metrics/stats/percentile.py +14 -0
- kafka/metrics/stats/percentiles.py +75 -0
- kafka/metrics/stats/rate.py +118 -0
- kafka/metrics/stats/sampled_stat.py +99 -0
- kafka/metrics/stats/sensor.py +136 -0
- kafka/metrics/stats/total.py +15 -0
- kafka/net/__init__.py +19 -0
- kafka/net/compat.py +165 -0
- kafka/net/connection.py +593 -0
- kafka/net/http_connect.py +144 -0
- kafka/net/inet.py +122 -0
- kafka/net/manager.py +451 -0
- kafka/net/metrics.py +149 -0
- kafka/net/sasl/__init__.py +32 -0
- kafka/net/sasl/abc.py +28 -0
- kafka/net/sasl/gssapi.py +95 -0
- kafka/net/sasl/msk.py +245 -0
- kafka/net/sasl/oauth.py +98 -0
- kafka/net/sasl/plain.py +42 -0
- kafka/net/sasl/scram.py +135 -0
- kafka/net/sasl/sspi.py +111 -0
- kafka/net/selector.py +644 -0
- kafka/net/socks5.py +262 -0
- kafka/net/transport.py +415 -0
- kafka/net/wakeup_notifier.py +72 -0
- kafka/partitioner/__init__.py +8 -0
- kafka/partitioner/abc.py +8 -0
- kafka/partitioner/default.py +89 -0
- kafka/partitioner/sticky.py +109 -0
- kafka/producer/__init__.py +5 -0
- kafka/producer/__main__.py +5 -0
- kafka/producer/future.py +101 -0
- kafka/producer/kafka.py +1123 -0
- kafka/producer/producer_batch.py +192 -0
- kafka/producer/record_accumulator.py +647 -0
- kafka/producer/sender.py +884 -0
- kafka/producer/transaction_manager.py +1326 -0
- kafka/protocol/__init__.py +0 -0
- kafka/protocol/admin/__init__.py +29 -0
- kafka/protocol/admin/acl.py +83 -0
- kafka/protocol/admin/acl.pyi +375 -0
- kafka/protocol/admin/client_quotas.py +14 -0
- kafka/protocol/admin/client_quotas.pyi +265 -0
- kafka/protocol/admin/cluster.py +31 -0
- kafka/protocol/admin/cluster.pyi +620 -0
- kafka/protocol/admin/configs.py +22 -0
- kafka/protocol/admin/configs.pyi +437 -0
- kafka/protocol/admin/groups.py +24 -0
- kafka/protocol/admin/groups.pyi +261 -0
- kafka/protocol/admin/topics.py +53 -0
- kafka/protocol/admin/topics.pyi +982 -0
- kafka/protocol/admin/transactions.py +18 -0
- kafka/protocol/admin/transactions.pyi +311 -0
- kafka/protocol/admin/users.py +14 -0
- kafka/protocol/admin/users.pyi +223 -0
- kafka/protocol/api_data.py +125 -0
- kafka/protocol/api_header.py +55 -0
- kafka/protocol/api_key.py +97 -0
- kafka/protocol/api_message.py +277 -0
- kafka/protocol/broker_version_data.py +246 -0
- kafka/protocol/consumer/__init__.py +13 -0
- kafka/protocol/consumer/fetch.py +16 -0
- kafka/protocol/consumer/fetch.pyi +298 -0
- kafka/protocol/consumer/group.py +38 -0
- kafka/protocol/consumer/group.pyi +824 -0
- kafka/protocol/consumer/metadata.py +30 -0
- kafka/protocol/consumer/metadata.pyi +89 -0
- kafka/protocol/consumer/offsets.py +75 -0
- kafka/protocol/consumer/offsets.pyi +288 -0
- kafka/protocol/data_container.py +166 -0
- kafka/protocol/frame.py +30 -0
- kafka/protocol/generate_stubs.py +468 -0
- kafka/protocol/metadata/__init__.py +10 -0
- kafka/protocol/metadata/api_versions.py +41 -0
- kafka/protocol/metadata/api_versions.pyi +128 -0
- kafka/protocol/metadata/find_coordinator.py +19 -0
- kafka/protocol/metadata/find_coordinator.pyi +105 -0
- kafka/protocol/metadata/metadata.py +34 -0
- kafka/protocol/metadata/metadata.pyi +160 -0
- kafka/protocol/old/__init__.py +0 -0
- kafka/protocol/old/abstract.py +17 -0
- kafka/protocol/old/add_offsets_to_txn.py +54 -0
- kafka/protocol/old/add_partitions_to_txn.py +71 -0
- kafka/protocol/old/admin.py +1086 -0
- kafka/protocol/old/api.py +205 -0
- kafka/protocol/old/api_versions.py +133 -0
- kafka/protocol/old/commit.py +355 -0
- kafka/protocol/old/consumer_protocol.py +36 -0
- kafka/protocol/old/end_txn.py +53 -0
- kafka/protocol/old/fetch.py +408 -0
- kafka/protocol/old/find_coordinator.py +72 -0
- kafka/protocol/old/group.py +451 -0
- kafka/protocol/old/init_producer_id.py +42 -0
- kafka/protocol/old/list_offsets.py +186 -0
- kafka/protocol/old/metadata.py +290 -0
- kafka/protocol/old/offset_for_leader_epoch.py +133 -0
- kafka/protocol/old/produce.py +247 -0
- kafka/protocol/old/sasl_authenticate.py +38 -0
- kafka/protocol/old/sasl_handshake.py +39 -0
- kafka/protocol/old/struct.py +87 -0
- kafka/protocol/old/txn_offset_commit.py +73 -0
- kafka/protocol/old/types.py +440 -0
- kafka/protocol/parser.py +191 -0
- kafka/protocol/producer/__init__.py +7 -0
- kafka/protocol/producer/produce.py +17 -0
- kafka/protocol/producer/produce.pyi +197 -0
- kafka/protocol/producer/transaction.py +30 -0
- kafka/protocol/producer/transaction.pyi +663 -0
- kafka/protocol/sasl.py +52 -0
- kafka/protocol/sasl.pyi +126 -0
- kafka/protocol/schemas/__init__.py +7 -0
- kafka/protocol/schemas/fields/__init__.py +7 -0
- kafka/protocol/schemas/fields/array.py +127 -0
- kafka/protocol/schemas/fields/base.py +156 -0
- kafka/protocol/schemas/fields/codecs/__init__.py +12 -0
- kafka/protocol/schemas/fields/codecs/encode_buffer.py +82 -0
- kafka/protocol/schemas/fields/codecs/tagged_fields.py +109 -0
- kafka/protocol/schemas/fields/codecs/types.py +505 -0
- kafka/protocol/schemas/fields/codegen.py +40 -0
- kafka/protocol/schemas/fields/simple.py +127 -0
- kafka/protocol/schemas/fields/struct.py +357 -0
- kafka/protocol/schemas/fields/struct_array.py +142 -0
- kafka/protocol/schemas/load_json.py +42 -0
- kafka/protocol/schemas/resources/AddOffsetsToTxnRequest.json +40 -0
- kafka/protocol/schemas/resources/AddOffsetsToTxnResponse.json +35 -0
- kafka/protocol/schemas/resources/AddPartitionsToTxnRequest.json +65 -0
- kafka/protocol/schemas/resources/AddPartitionsToTxnResponse.json +60 -0
- kafka/protocol/schemas/resources/AlterClientQuotasRequest.json +47 -0
- kafka/protocol/schemas/resources/AlterClientQuotasResponse.json +41 -0
- kafka/protocol/schemas/resources/AlterConfigsRequest.json +43 -0
- kafka/protocol/schemas/resources/AlterConfigsResponse.json +39 -0
- kafka/protocol/schemas/resources/AlterPartitionReassignmentsRequest.json +42 -0
- kafka/protocol/schemas/resources/AlterPartitionReassignmentsResponse.json +47 -0
- kafka/protocol/schemas/resources/AlterReplicaLogDirsRequest.json +41 -0
- kafka/protocol/schemas/resources/AlterReplicaLogDirsResponse.json +41 -0
- kafka/protocol/schemas/resources/AlterUserScramCredentialsRequest.json +45 -0
- kafka/protocol/schemas/resources/AlterUserScramCredentialsResponse.json +35 -0
- kafka/protocol/schemas/resources/ApiVersionsRequest.json +34 -0
- kafka/protocol/schemas/resources/ApiVersionsResponse.json +79 -0
- kafka/protocol/schemas/resources/ConsumerProtocolAssignment.json +42 -0
- kafka/protocol/schemas/resources/ConsumerProtocolSubscription.json +49 -0
- kafka/protocol/schemas/resources/CreateAclsRequest.json +46 -0
- kafka/protocol/schemas/resources/CreateAclsResponse.json +37 -0
- kafka/protocol/schemas/resources/CreatePartitionsRequest.json +47 -0
- kafka/protocol/schemas/resources/CreatePartitionsResponse.json +41 -0
- kafka/protocol/schemas/resources/CreateTopicsRequest.json +65 -0
- kafka/protocol/schemas/resources/CreateTopicsResponse.json +72 -0
- kafka/protocol/schemas/resources/DeleteAclsRequest.json +46 -0
- kafka/protocol/schemas/resources/DeleteAclsResponse.json +59 -0
- kafka/protocol/schemas/resources/DeleteGroupsRequest.json +30 -0
- kafka/protocol/schemas/resources/DeleteGroupsResponse.json +36 -0
- kafka/protocol/schemas/resources/DeleteRecordsRequest.json +42 -0
- kafka/protocol/schemas/resources/DeleteRecordsResponse.json +43 -0
- kafka/protocol/schemas/resources/DeleteTopicsRequest.json +43 -0
- kafka/protocol/schemas/resources/DeleteTopicsResponse.json +52 -0
- kafka/protocol/schemas/resources/DescribeAclsRequest.json +43 -0
- kafka/protocol/schemas/resources/DescribeAclsResponse.json +55 -0
- kafka/protocol/schemas/resources/DescribeClientQuotasRequest.json +37 -0
- kafka/protocol/schemas/resources/DescribeClientQuotasResponse.json +47 -0
- kafka/protocol/schemas/resources/DescribeClusterRequest.json +35 -0
- kafka/protocol/schemas/resources/DescribeClusterResponse.json +56 -0
- kafka/protocol/schemas/resources/DescribeConfigsRequest.json +42 -0
- kafka/protocol/schemas/resources/DescribeConfigsResponse.json +69 -0
- kafka/protocol/schemas/resources/DescribeGroupsRequest.json +38 -0
- kafka/protocol/schemas/resources/DescribeGroupsResponse.json +74 -0
- kafka/protocol/schemas/resources/DescribeLogDirsRequest.json +38 -0
- kafka/protocol/schemas/resources/DescribeLogDirsResponse.json +65 -0
- kafka/protocol/schemas/resources/DescribeProducersRequest.json +32 -0
- kafka/protocol/schemas/resources/DescribeProducersResponse.json +55 -0
- kafka/protocol/schemas/resources/DescribeQuorumRequest.json +39 -0
- kafka/protocol/schemas/resources/DescribeQuorumResponse.json +82 -0
- kafka/protocol/schemas/resources/DescribeTopicPartitionsRequest.json +40 -0
- kafka/protocol/schemas/resources/DescribeTopicPartitionsResponse.json +66 -0
- kafka/protocol/schemas/resources/DescribeTransactionsRequest.json +27 -0
- kafka/protocol/schemas/resources/DescribeTransactionsResponse.json +52 -0
- kafka/protocol/schemas/resources/DescribeUserScramCredentialsRequest.json +30 -0
- kafka/protocol/schemas/resources/DescribeUserScramCredentialsResponse.json +45 -0
- kafka/protocol/schemas/resources/ElectLeadersRequest.json +41 -0
- kafka/protocol/schemas/resources/ElectLeadersResponse.json +45 -0
- kafka/protocol/schemas/resources/EndTxnRequest.json +43 -0
- kafka/protocol/schemas/resources/EndTxnResponse.json +41 -0
- kafka/protocol/schemas/resources/FetchRequest.json +125 -0
- kafka/protocol/schemas/resources/FetchResponse.json +124 -0
- kafka/protocol/schemas/resources/FindCoordinatorRequest.json +43 -0
- kafka/protocol/schemas/resources/FindCoordinatorResponse.json +58 -0
- kafka/protocol/schemas/resources/HeartbeatRequest.json +39 -0
- kafka/protocol/schemas/resources/HeartbeatResponse.json +35 -0
- kafka/protocol/schemas/resources/IncrementalAlterConfigsRequest.json +44 -0
- kafka/protocol/schemas/resources/IncrementalAlterConfigsResponse.json +38 -0
- kafka/protocol/schemas/resources/InitProducerIdRequest.json +50 -0
- kafka/protocol/schemas/resources/InitProducerIdResponse.json +47 -0
- kafka/protocol/schemas/resources/JoinGroupRequest.json +63 -0
- kafka/protocol/schemas/resources/JoinGroupResponse.json +69 -0
- kafka/protocol/schemas/resources/LeaveGroupRequest.json +47 -0
- kafka/protocol/schemas/resources/LeaveGroupResponse.json +47 -0
- kafka/protocol/schemas/resources/ListConfigResourcesRequest.json +31 -0
- kafka/protocol/schemas/resources/ListConfigResourcesResponse.json +37 -0
- kafka/protocol/schemas/resources/ListGroupsRequest.json +36 -0
- kafka/protocol/schemas/resources/ListGroupsResponse.json +49 -0
- kafka/protocol/schemas/resources/ListOffsetsRequest.json +72 -0
- kafka/protocol/schemas/resources/ListOffsetsResponse.json +71 -0
- kafka/protocol/schemas/resources/ListPartitionReassignmentsRequest.json +34 -0
- kafka/protocol/schemas/resources/ListPartitionReassignmentsResponse.json +46 -0
- kafka/protocol/schemas/resources/ListTransactionsRequest.json +40 -0
- kafka/protocol/schemas/resources/ListTransactionsResponse.json +42 -0
- kafka/protocol/schemas/resources/MetadataRequest.json +56 -0
- kafka/protocol/schemas/resources/MetadataResponse.json +101 -0
- kafka/protocol/schemas/resources/OffsetCommitRequest.json +76 -0
- kafka/protocol/schemas/resources/OffsetCommitResponse.json +71 -0
- kafka/protocol/schemas/resources/OffsetDeleteRequest.json +39 -0
- kafka/protocol/schemas/resources/OffsetDeleteResponse.json +42 -0
- kafka/protocol/schemas/resources/OffsetFetchRequest.json +76 -0
- kafka/protocol/schemas/resources/OffsetFetchResponse.json +107 -0
- kafka/protocol/schemas/resources/OffsetForLeaderEpochRequest.json +52 -0
- kafka/protocol/schemas/resources/OffsetForLeaderEpochResponse.json +51 -0
- kafka/protocol/schemas/resources/ProduceRequest.json +73 -0
- kafka/protocol/schemas/resources/ProduceResponse.json +96 -0
- kafka/protocol/schemas/resources/RequestHeader.json +44 -0
- kafka/protocol/schemas/resources/ResponseHeader.json +26 -0
- kafka/protocol/schemas/resources/SaslAuthenticateRequest.json +29 -0
- kafka/protocol/schemas/resources/SaslAuthenticateResponse.json +34 -0
- kafka/protocol/schemas/resources/SaslHandshakeRequest.json +31 -0
- kafka/protocol/schemas/resources/SaslHandshakeResponse.json +32 -0
- kafka/protocol/schemas/resources/SyncGroupRequest.json +56 -0
- kafka/protocol/schemas/resources/SyncGroupResponse.json +46 -0
- kafka/protocol/schemas/resources/TxnOffsetCommitRequest.json +68 -0
- kafka/protocol/schemas/resources/TxnOffsetCommitResponse.json +47 -0
- kafka/protocol/schemas/resources/UpdateFeaturesRequest.json +43 -0
- kafka/protocol/schemas/resources/UpdateFeaturesResponse.json +39 -0
- kafka/protocol/schemas/resources/WriteTxnMarkersRequest.json +49 -0
- kafka/protocol/schemas/resources/WriteTxnMarkersResponse.json +45 -0
- kafka/protocol/schemas/resources/__init__.py +0 -0
- kafka/record/__init__.py +3 -0
- kafka/record/_crc32c.py +161 -0
- kafka/record/abc.py +144 -0
- kafka/record/default_records.py +782 -0
- kafka/record/legacy_records.py +587 -0
- kafka/record/memory_records.py +255 -0
- kafka/record/util.py +135 -0
- kafka/serializer/__init__.py +4 -0
- kafka/serializer/abstract.py +20 -0
- kafka/serializer/default.py +16 -0
- kafka/serializer/json.py +17 -0
- kafka/serializer/wrapper.py +21 -0
- kafka/structs.py +69 -0
- kafka/util.py +159 -0
- kafka/vendor/__init__.py +0 -0
- kafka/version.py +1 -0
- kafka_python-3.0.0.dist-info/METADATA +319 -0
- kafka_python-3.0.0.dist-info/RECORD +373 -0
- kafka_python-3.0.0.dist-info/WHEEL +5 -0
- kafka_python-3.0.0.dist-info/entry_points.txt +2 -0
- kafka_python-3.0.0.dist-info/licenses/LICENSE +202 -0
- kafka_python-3.0.0.dist-info/top_level.txt +1 -0
kafka/protocol/sasl.pyi
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Generated by generate_stubs.py (Python 3.14)
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Any, Self
|
|
4
|
+
|
|
5
|
+
from kafka.protocol.api_message import ApiMessage
|
|
6
|
+
from kafka.protocol.api_data import ApiData
|
|
7
|
+
|
|
8
|
+
__all__ = ['SaslHandshakeRequest', 'SaslHandshakeResponse', 'SaslAuthenticateRequest', 'SaslAuthenticateResponse', 'SaslBytesRequest', 'SaslBytesResponse']
|
|
9
|
+
|
|
10
|
+
class SaslHandshakeRequest(ApiMessage):
|
|
11
|
+
mechanism: str
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
*args: Any,
|
|
15
|
+
mechanism: str = ...,
|
|
16
|
+
version: int | None = None,
|
|
17
|
+
**kwargs: Any,
|
|
18
|
+
) -> None: ...
|
|
19
|
+
@property
|
|
20
|
+
def version(self) -> int | None: ...
|
|
21
|
+
def to_dict(self, meta: bool = False, json: bool = True) -> dict: ...
|
|
22
|
+
name: str
|
|
23
|
+
type: str
|
|
24
|
+
API_KEY: int
|
|
25
|
+
API_VERSION: int
|
|
26
|
+
valid_versions: tuple[int, int]
|
|
27
|
+
min_version: int
|
|
28
|
+
max_version: int
|
|
29
|
+
@property
|
|
30
|
+
def header(self) -> Any: ...
|
|
31
|
+
@classmethod
|
|
32
|
+
def is_request(cls) -> bool: ...
|
|
33
|
+
def expect_response(self) -> bool: ...
|
|
34
|
+
def with_header(self, correlation_id: int = 0, client_id: str = "kafka-python") -> None: ...
|
|
35
|
+
|
|
36
|
+
class SaslHandshakeResponse(ApiMessage):
|
|
37
|
+
error_code: int
|
|
38
|
+
mechanisms: list[str]
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
*args: Any,
|
|
42
|
+
error_code: int = ...,
|
|
43
|
+
mechanisms: list[str] = ...,
|
|
44
|
+
version: int | None = None,
|
|
45
|
+
**kwargs: Any,
|
|
46
|
+
) -> None: ...
|
|
47
|
+
@property
|
|
48
|
+
def version(self) -> int | None: ...
|
|
49
|
+
def to_dict(self, meta: bool = False, json: bool = True) -> dict: ...
|
|
50
|
+
name: str
|
|
51
|
+
type: str
|
|
52
|
+
API_KEY: int
|
|
53
|
+
API_VERSION: int
|
|
54
|
+
valid_versions: tuple[int, int]
|
|
55
|
+
min_version: int
|
|
56
|
+
max_version: int
|
|
57
|
+
@property
|
|
58
|
+
def header(self) -> Any: ...
|
|
59
|
+
@classmethod
|
|
60
|
+
def is_request(cls) -> bool: ...
|
|
61
|
+
def expect_response(self) -> bool: ...
|
|
62
|
+
def with_header(self, correlation_id: int = 0, client_id: str = "kafka-python") -> None: ...
|
|
63
|
+
|
|
64
|
+
class SaslAuthenticateRequest(ApiMessage):
|
|
65
|
+
auth_bytes: bytes | ApiData
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
*args: Any,
|
|
69
|
+
auth_bytes: bytes | ApiData = ...,
|
|
70
|
+
version: int | None = None,
|
|
71
|
+
**kwargs: Any,
|
|
72
|
+
) -> None: ...
|
|
73
|
+
@property
|
|
74
|
+
def version(self) -> int | None: ...
|
|
75
|
+
def to_dict(self, meta: bool = False, json: bool = True) -> dict: ...
|
|
76
|
+
name: str
|
|
77
|
+
type: str
|
|
78
|
+
API_KEY: int
|
|
79
|
+
API_VERSION: int
|
|
80
|
+
valid_versions: tuple[int, int]
|
|
81
|
+
min_version: int
|
|
82
|
+
max_version: int
|
|
83
|
+
@property
|
|
84
|
+
def header(self) -> Any: ...
|
|
85
|
+
@classmethod
|
|
86
|
+
def is_request(cls) -> bool: ...
|
|
87
|
+
def expect_response(self) -> bool: ...
|
|
88
|
+
def with_header(self, correlation_id: int = 0, client_id: str = "kafka-python") -> None: ...
|
|
89
|
+
|
|
90
|
+
class SaslAuthenticateResponse(ApiMessage):
|
|
91
|
+
error_code: int
|
|
92
|
+
error_message: str | None
|
|
93
|
+
auth_bytes: bytes | ApiData
|
|
94
|
+
session_lifetime_ms: int
|
|
95
|
+
def __init__(
|
|
96
|
+
self,
|
|
97
|
+
*args: Any,
|
|
98
|
+
error_code: int = ...,
|
|
99
|
+
error_message: str | None = ...,
|
|
100
|
+
auth_bytes: bytes | ApiData = ...,
|
|
101
|
+
session_lifetime_ms: int = ...,
|
|
102
|
+
version: int | None = None,
|
|
103
|
+
**kwargs: Any,
|
|
104
|
+
) -> None: ...
|
|
105
|
+
@property
|
|
106
|
+
def version(self) -> int | None: ...
|
|
107
|
+
def to_dict(self, meta: bool = False, json: bool = True) -> dict: ...
|
|
108
|
+
name: str
|
|
109
|
+
type: str
|
|
110
|
+
API_KEY: int
|
|
111
|
+
API_VERSION: int
|
|
112
|
+
valid_versions: tuple[int, int]
|
|
113
|
+
min_version: int
|
|
114
|
+
max_version: int
|
|
115
|
+
@property
|
|
116
|
+
def header(self) -> Any: ...
|
|
117
|
+
@classmethod
|
|
118
|
+
def is_request(cls) -> bool: ...
|
|
119
|
+
def expect_response(self) -> bool: ...
|
|
120
|
+
def with_header(self, correlation_id: int = 0, client_id: str = "kafka-python") -> None: ...
|
|
121
|
+
|
|
122
|
+
class SaslBytesRequest:
|
|
123
|
+
API_VERSION: int
|
|
124
|
+
|
|
125
|
+
class SaslBytesResponse:
|
|
126
|
+
...
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from .base import BaseField
|
|
2
|
+
from .simple import SimpleField
|
|
3
|
+
from .codecs import (
|
|
4
|
+
UnsignedVarInt32, Int32,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ArrayField(BaseField):
|
|
9
|
+
@classmethod
|
|
10
|
+
def parse_inner_type(cls, json):
|
|
11
|
+
if 'fields' in json:
|
|
12
|
+
return
|
|
13
|
+
type_str = cls.parse_array_type(json)
|
|
14
|
+
if type_str is not None:
|
|
15
|
+
inner_json = {**json, 'type': type_str}
|
|
16
|
+
return SimpleField.parse_json(inner_json)
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def parse_array_type(cls, json):
|
|
20
|
+
if json['type'].startswith('[]'):
|
|
21
|
+
type_str = json['type'][2:]
|
|
22
|
+
assert not type_str.startswith('[]'), 'Unexpected double-array type: %s' % json['type']
|
|
23
|
+
return type_str
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def parse_json(cls, json):
|
|
27
|
+
inner_type = cls.parse_inner_type(json)
|
|
28
|
+
if inner_type is not None:
|
|
29
|
+
return cls(json, array_of=inner_type)
|
|
30
|
+
|
|
31
|
+
def __init__(self, json, array_of=None):
|
|
32
|
+
if array_of is None:
|
|
33
|
+
array_of = self.parse_inner_type(json)
|
|
34
|
+
assert array_of is not None, 'json does not contain a (simple) Array!'
|
|
35
|
+
super().__init__(json)
|
|
36
|
+
self.array_of = array_of # SimpleField
|
|
37
|
+
|
|
38
|
+
def is_array(self):
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
def _calculate_default(self, default):
|
|
42
|
+
if default == 'null':
|
|
43
|
+
return None
|
|
44
|
+
elif not default:
|
|
45
|
+
return []
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError('Invalid default for field %s. The only valid default is empty or null.' % self._name)
|
|
48
|
+
|
|
49
|
+
def encode(self, items, version=None, compact=False, tagged=False):
|
|
50
|
+
if compact:
|
|
51
|
+
size = UnsignedVarInt32.encode(len(items) + 1 if items is not None else 0)
|
|
52
|
+
else:
|
|
53
|
+
size = Int32.encode(len(items) if items is not None else -1)
|
|
54
|
+
if items is None:
|
|
55
|
+
return size
|
|
56
|
+
fields = [self.array_of.encode(item, version=version, compact=compact, tagged=tagged)
|
|
57
|
+
for item in items]
|
|
58
|
+
return b''.join([size] + fields)
|
|
59
|
+
|
|
60
|
+
def encode_into(self, items, out, version=None, compact=False, tagged=False):
|
|
61
|
+
if compact:
|
|
62
|
+
UnsignedVarInt32.encode_into(out, len(items) + 1 if items is not None else 0)
|
|
63
|
+
else:
|
|
64
|
+
Int32.encode_into(out, len(items) if items is not None else -1)
|
|
65
|
+
if items is None:
|
|
66
|
+
return
|
|
67
|
+
encode_into = self.array_of.encode_into
|
|
68
|
+
for item in items:
|
|
69
|
+
encode_into(item, out, version=version, compact=compact, tagged=tagged)
|
|
70
|
+
|
|
71
|
+
def emit_encode_into(self, ctx, val_expr, indent, version=None, compact=False, tagged=False):
|
|
72
|
+
if compact:
|
|
73
|
+
an = ctx.next_var('an')
|
|
74
|
+
ctx.emit(indent, '%s = len(%s) + 1 if %s is not None else 0' % (an, val_expr, val_expr))
|
|
75
|
+
UnsignedVarInt32.emit_encode_into(ctx, an, indent)
|
|
76
|
+
ctx.emit(indent, 'if %s is not None:' % val_expr)
|
|
77
|
+
else:
|
|
78
|
+
ctx.emit(indent, 'if %s is None:' % val_expr)
|
|
79
|
+
ctx.emit(indent, " pack_into('>i', buf, pos, -1)")
|
|
80
|
+
ctx.emit(indent, ' pos += 4')
|
|
81
|
+
ctx.emit(indent, 'else:')
|
|
82
|
+
ctx.emit(indent, " pack_into('>i', buf, pos, len(%s))" % val_expr)
|
|
83
|
+
ctx.emit(indent, ' pos += 4')
|
|
84
|
+
guard = indent + ' '
|
|
85
|
+
item_var = ctx.next_var('ai')
|
|
86
|
+
ctx.emit(guard, 'for %s in %s:' % (item_var, val_expr))
|
|
87
|
+
self.array_of.emit_encode_into(ctx, item_var, guard + ' ',
|
|
88
|
+
version=version, compact=compact, tagged=tagged)
|
|
89
|
+
|
|
90
|
+
def emit_decode_from(self, ctx, var_name, indent, version=None, compact=False, tagged=False):
|
|
91
|
+
n = ctx.next_var('n')
|
|
92
|
+
if compact:
|
|
93
|
+
UnsignedVarInt32.emit_decode_from(ctx, n, indent)
|
|
94
|
+
ctx.emit(indent, '%s -= 1' % n)
|
|
95
|
+
else:
|
|
96
|
+
ctx.emit(indent, '%s = unpack_from(">i", data, pos)[0]' % n)
|
|
97
|
+
ctx.emit(indent, 'pos += 4')
|
|
98
|
+
ctx.emit(indent, 'if %s == -1:' % n)
|
|
99
|
+
ctx.emit(indent, ' %s = None' % var_name)
|
|
100
|
+
ctx.emit(indent, 'else:')
|
|
101
|
+
inner_indent = indent + ' '
|
|
102
|
+
ctx.emit(inner_indent, '%s = []' % var_name)
|
|
103
|
+
idx = ctx.next_var('idx')
|
|
104
|
+
item = ctx.next_var('item')
|
|
105
|
+
ctx.emit(inner_indent, 'for %s in range(%s):' % (idx, n))
|
|
106
|
+
self.array_of.emit_decode_from(ctx, item, inner_indent + ' ',
|
|
107
|
+
version=version, compact=compact, tagged=tagged)
|
|
108
|
+
ctx.emit(inner_indent, ' %s.append(%s)' % (var_name, item))
|
|
109
|
+
|
|
110
|
+
def decode(self, data, version=None, compact=False, tagged=False):
|
|
111
|
+
if compact:
|
|
112
|
+
size = UnsignedVarInt32.decode(data)
|
|
113
|
+
size -= 1
|
|
114
|
+
else:
|
|
115
|
+
size = Int32.decode(data)
|
|
116
|
+
if size == -1:
|
|
117
|
+
return None
|
|
118
|
+
return [self.array_of.decode(data, version=version, compact=compact, tagged=tagged)
|
|
119
|
+
for _ in range(size)]
|
|
120
|
+
|
|
121
|
+
def to_json(self, val):
|
|
122
|
+
if val is None:
|
|
123
|
+
return None
|
|
124
|
+
return [self.array_of.to_json(i) for i in val]
|
|
125
|
+
|
|
126
|
+
def __repr__(self):
|
|
127
|
+
return 'ArrayField(%s)' % self._json
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BaseField:
|
|
5
|
+
FIELD_TYPES = []
|
|
6
|
+
|
|
7
|
+
def __init_subclass__(cls, **kw):
|
|
8
|
+
cls.FIELD_TYPES.append(cls)
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def parse_json_fields(cls, json):
|
|
12
|
+
return tuple(map(BaseField.parse_json, json.get('fields', []))) or None # Note: DFS Field construction
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def parse_json(cls, json):
|
|
16
|
+
if 'type' not in json:
|
|
17
|
+
raise ValueError('No type found in json')
|
|
18
|
+
type_str = json['type']
|
|
19
|
+
for field_type in cls.FIELD_TYPES:
|
|
20
|
+
maybe_type = field_type.parse_json(json)
|
|
21
|
+
if maybe_type is not None:
|
|
22
|
+
return maybe_type
|
|
23
|
+
else:
|
|
24
|
+
raise ValueError('Unable to parse field type: %s' % type_str)
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def underscore_name(cls, name):
|
|
28
|
+
return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def parse_versions(cls, versions):
|
|
32
|
+
if versions is None or versions.strip() == '':
|
|
33
|
+
return None
|
|
34
|
+
elif versions.strip() == 'none':
|
|
35
|
+
return (-1, -1)
|
|
36
|
+
elif versions[-1] == '+':
|
|
37
|
+
return (int(versions[:-1]), 32767)
|
|
38
|
+
elif versions.find('-') == -1:
|
|
39
|
+
return (int(versions), int(versions))
|
|
40
|
+
else:
|
|
41
|
+
return tuple(map(int, versions.split('-')))
|
|
42
|
+
|
|
43
|
+
def __init__(self, json):
|
|
44
|
+
self._json = json
|
|
45
|
+
self._name = json['name']
|
|
46
|
+
self.name = self.underscore_name(self._name)
|
|
47
|
+
# versions tells when to include the field in encode / decode
|
|
48
|
+
# 'validVersions' is included for top-level request/response structs
|
|
49
|
+
# otherwise this should always be 'versions'
|
|
50
|
+
for versions_key in ('versions', 'validVersions'):
|
|
51
|
+
if versions_key in json:
|
|
52
|
+
self._versions = self.parse_versions(json[versions_key])
|
|
53
|
+
if self._versions is not None:
|
|
54
|
+
break
|
|
55
|
+
else:
|
|
56
|
+
raise ValueError('Unable to find versions in json!')
|
|
57
|
+
# If the field is tagged, it should have 'tag' and 'taggedVersions'
|
|
58
|
+
self._tag = json.get('tag')
|
|
59
|
+
self._tagged_versions = self.parse_versions(json.get('taggedVersions'))
|
|
60
|
+
# flexibleVersions is used to override 'compact' string/bytes encoding for a specific field
|
|
61
|
+
# currently only used in RequestHeader client_id
|
|
62
|
+
self._flexible_versions = self.parse_versions(json.get('flexibleVersions'))
|
|
63
|
+
self._nullable_versions = self.parse_versions(json.get('nullableVersions'))
|
|
64
|
+
self._type_str = json['type']
|
|
65
|
+
if 'fields' in json:
|
|
66
|
+
self._fields = self.parse_json_fields(json)
|
|
67
|
+
else:
|
|
68
|
+
self._fields = None
|
|
69
|
+
self._ignorable = json.get('ignorable')
|
|
70
|
+
self._entity_type = json.get('entityType')
|
|
71
|
+
self._about = json.get('about', '')
|
|
72
|
+
self._zero_copy = json.get('zeroCopy') # ?
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def type_str(self):
|
|
76
|
+
return self._type_str
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def tag(self):
|
|
80
|
+
return self._tag
|
|
81
|
+
|
|
82
|
+
# Override in subclasses
|
|
83
|
+
def is_array(self):
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
def is_struct(self):
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
def is_struct_array(self):
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
def is_batchable(self):
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
def has_data_class(self):
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
def __call__(self, *args, **kwargs):
|
|
99
|
+
raise NotImplementedError
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def fields(self):
|
|
103
|
+
return self._fields
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def min_version(self):
|
|
107
|
+
return self._versions[0]
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def max_version(self):
|
|
111
|
+
return self._versions[1]
|
|
112
|
+
|
|
113
|
+
def for_version_q(self, version):
|
|
114
|
+
return self._versions[0] <= version <= self._versions[1]
|
|
115
|
+
|
|
116
|
+
def nullable_for_version_q(self, version):
|
|
117
|
+
if self._nullable_versions is None:
|
|
118
|
+
return False
|
|
119
|
+
return self._nullable_versions[0] <= version <= self._nullable_versions[1]
|
|
120
|
+
|
|
121
|
+
def tagged_field_q(self, version):
|
|
122
|
+
if self._tag is None or self._tagged_versions is None:
|
|
123
|
+
return False
|
|
124
|
+
elif not self._tagged_versions[0] <= version <= self._tagged_versions[1]:
|
|
125
|
+
return False
|
|
126
|
+
else:
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
def _calculate_default(self, default):
|
|
130
|
+
return default
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def default(self):
|
|
134
|
+
try:
|
|
135
|
+
return self._default # pylint: disable=E1101
|
|
136
|
+
except AttributeError:
|
|
137
|
+
self._default = self._calculate_default(self._json.get('default', ''))
|
|
138
|
+
return self._default
|
|
139
|
+
|
|
140
|
+
def encode(self, value, version=None, compact=False, tagged=False):
|
|
141
|
+
raise NotImplementedError
|
|
142
|
+
|
|
143
|
+
def decode(self, data, version=None, compact=False, tagged=False):
|
|
144
|
+
raise NotImplementedError
|
|
145
|
+
|
|
146
|
+
def __repr__(self):
|
|
147
|
+
return 'BaseField(%s)' % self._json
|
|
148
|
+
|
|
149
|
+
def __eq__(self, other):
|
|
150
|
+
if self.__class__ != other.__class__:
|
|
151
|
+
return False
|
|
152
|
+
if self.name != other.name:
|
|
153
|
+
return False
|
|
154
|
+
if self.tag != other.tag:
|
|
155
|
+
return False
|
|
156
|
+
return True
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .encode_buffer import EncodeBuffer, EncodeBufferPool
|
|
2
|
+
from .tagged_fields import TaggedFields
|
|
3
|
+
from .types import (
|
|
4
|
+
BitField, Boolean, UUID, Bytes, String,
|
|
5
|
+
Int8, Int16, Int32, Int64, UnsignedInt16, UnsignedVarInt32, Float64,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'BitField', 'Boolean', 'UUID', 'Bytes', 'String',
|
|
10
|
+
'Int8', 'Int16', 'Int32', 'Int64', 'UnsignedInt16', 'UnsignedVarInt32', 'Float64',
|
|
11
|
+
'TaggedFields', 'EncodeBuffer',
|
|
12
|
+
]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EncodeBuffer:
|
|
5
|
+
"""Growable buffer for encode_into operations."""
|
|
6
|
+
__slots__ = ('buf', 'pos')
|
|
7
|
+
|
|
8
|
+
def __init__(self, size=65536):
|
|
9
|
+
self.buf = bytearray(size)
|
|
10
|
+
self.pos = 0
|
|
11
|
+
|
|
12
|
+
def reset(self):
|
|
13
|
+
"""Reset position to 0 for reuse. The buffer retains its current size."""
|
|
14
|
+
self.pos = 0
|
|
15
|
+
|
|
16
|
+
def ensure(self, needed):
|
|
17
|
+
if self.pos + needed > len(self.buf):
|
|
18
|
+
new_size = max(len(self.buf) * 2, self.pos + needed)
|
|
19
|
+
new_buf = bytearray(new_size)
|
|
20
|
+
new_buf[:self.pos] = self.buf[:self.pos]
|
|
21
|
+
self.buf = new_buf
|
|
22
|
+
|
|
23
|
+
def result(self):
|
|
24
|
+
# Return a bytearray slice (one copy) rather than bytes(self.buf[:pos])
|
|
25
|
+
# (two copies - the slice creates a bytearray, then bytes() copies
|
|
26
|
+
# again). Downstream consumers (protocol codec slice assignment,
|
|
27
|
+
# socket.send) accept bytearray transparently.
|
|
28
|
+
return self.buf[:self.pos]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class EncodeBufferPool:
|
|
32
|
+
"""Thread-local pool of reusable EncodeBuffer objects.
|
|
33
|
+
|
|
34
|
+
Each thread gets its own buffer that grows to match the largest message
|
|
35
|
+
encoded on that thread and stays that size - avoiding repeated allocation
|
|
36
|
+
of large bytearrays.
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
with EncodeBufferPool.acquire() as out:
|
|
40
|
+
fast_encode(item, out)
|
|
41
|
+
return out.result()
|
|
42
|
+
"""
|
|
43
|
+
_local = threading.local()
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def acquire(cls):
|
|
47
|
+
"""Return a context manager that provides a reset EncodeBuffer."""
|
|
48
|
+
return _PooledBuffer(cls)
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def _get(cls):
|
|
52
|
+
buf = getattr(cls._local, 'buf', None)
|
|
53
|
+
if buf is None:
|
|
54
|
+
buf = EncodeBuffer()
|
|
55
|
+
cls._local.buf = buf
|
|
56
|
+
buf.reset()
|
|
57
|
+
return buf
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def _release(cls, buf):
|
|
61
|
+
# Nothing to do - the buffer stays on the thread-local.
|
|
62
|
+
# Future: could cap max size to prevent memory leaks from
|
|
63
|
+
# one-off large messages.
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class _PooledBuffer:
|
|
68
|
+
"""Context manager for EncodeBufferPool.acquire()."""
|
|
69
|
+
__slots__ = ('_pool', '_buf')
|
|
70
|
+
|
|
71
|
+
def __init__(self, pool):
|
|
72
|
+
self._pool = pool
|
|
73
|
+
self._buf = None
|
|
74
|
+
|
|
75
|
+
def __enter__(self):
|
|
76
|
+
self._buf = self._pool._get()
|
|
77
|
+
return self._buf
|
|
78
|
+
|
|
79
|
+
def __exit__(self, *exc):
|
|
80
|
+
self._pool._release(self._buf)
|
|
81
|
+
self._buf = None
|
|
82
|
+
return False
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from .types import UnsignedVarInt32
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _uvarint_size(value):
|
|
5
|
+
"""Return the number of bytes needed to encode value as an unsigned varint."""
|
|
6
|
+
if value < 0x80:
|
|
7
|
+
return 1
|
|
8
|
+
elif value < 0x4000:
|
|
9
|
+
return 2
|
|
10
|
+
elif value < 0x200000:
|
|
11
|
+
return 3
|
|
12
|
+
elif value < 0x10000000:
|
|
13
|
+
return 4
|
|
14
|
+
else:
|
|
15
|
+
return 5
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TaggedFields:
|
|
19
|
+
def __init__(self, fields):
|
|
20
|
+
self._fields = list(fields) # listify to clean filter() inputs
|
|
21
|
+
self._tags = {field.tag: field for field in self._fields}
|
|
22
|
+
self._names = {field.name: field for field in self._fields}
|
|
23
|
+
|
|
24
|
+
def encode(self, item, version=None):
|
|
25
|
+
if isinstance(item, dict):
|
|
26
|
+
tags = [(self._names[name].tag, val)
|
|
27
|
+
for name, val in item.items()
|
|
28
|
+
if name in self._names
|
|
29
|
+
and val != self._names[name].default]
|
|
30
|
+
else:
|
|
31
|
+
tags = [(self._names[name].tag, getattr(item, name))
|
|
32
|
+
for name in self._names
|
|
33
|
+
if hasattr(item, name)
|
|
34
|
+
and getattr(item, name) != self._names[name].default]
|
|
35
|
+
ret = [UnsignedVarInt32.encode(len(tags))]
|
|
36
|
+
for tag, val in tags:
|
|
37
|
+
ret.append(UnsignedVarInt32.encode(tag))
|
|
38
|
+
# struct tags have an empty nested tagged fields (tagged=None)
|
|
39
|
+
encoded_val = self._tags[tag].encode(val, version=version, compact=True, tagged=None)
|
|
40
|
+
ret.append(UnsignedVarInt32.encode(len(encoded_val)))
|
|
41
|
+
ret.append(encoded_val)
|
|
42
|
+
return b''.join(ret)
|
|
43
|
+
|
|
44
|
+
def encode_into(self, item, out, version=None):
|
|
45
|
+
if isinstance(item, dict):
|
|
46
|
+
tags = [(self._names[name].tag, name)
|
|
47
|
+
for name, val in item.items()
|
|
48
|
+
if name in self._names
|
|
49
|
+
and val != self._names[name].default]
|
|
50
|
+
else:
|
|
51
|
+
tags = [(self._names[name].tag, name)
|
|
52
|
+
for name in self._names
|
|
53
|
+
if hasattr(item, name)
|
|
54
|
+
and getattr(item, name) != self._names[name].default]
|
|
55
|
+
UnsignedVarInt32.encode_into(out, len(tags))
|
|
56
|
+
for tag, name in tags:
|
|
57
|
+
field = self._tags[tag]
|
|
58
|
+
val = item.get(name) if isinstance(item, dict) else getattr(item, name)
|
|
59
|
+
UnsignedVarInt32.encode_into(out, tag)
|
|
60
|
+
# Encode value into buffer after reserving space for size prefix.
|
|
61
|
+
# Strategy: assume size fits in 1 byte (< 128), encode value,
|
|
62
|
+
# then fix up if the size varint is larger.
|
|
63
|
+
size_pos = out.pos
|
|
64
|
+
out.pos += 1 # reserve 1 byte for size
|
|
65
|
+
val_start = out.pos
|
|
66
|
+
# struct tags have an empty nested tagged fields (tagged=None)
|
|
67
|
+
field.encode_into(val, out, version=version, compact=True, tagged=None)
|
|
68
|
+
val_size = out.pos - val_start
|
|
69
|
+
actual_size_len = _uvarint_size(val_size)
|
|
70
|
+
if actual_size_len == 1:
|
|
71
|
+
# Common case: size fits in 1 byte, just write it
|
|
72
|
+
out.buf[size_pos] = val_size
|
|
73
|
+
else:
|
|
74
|
+
# Rare case: size needs more bytes. Shift the value data forward.
|
|
75
|
+
extra = actual_size_len - 1
|
|
76
|
+
out.ensure(extra)
|
|
77
|
+
# Move value bytes forward to make room
|
|
78
|
+
out.buf[val_start + extra:out.pos + extra] = out.buf[val_start:out.pos]
|
|
79
|
+
out.pos += extra
|
|
80
|
+
# Write the multi-byte size at size_pos
|
|
81
|
+
saved_pos = out.pos
|
|
82
|
+
out.pos = size_pos
|
|
83
|
+
UnsignedVarInt32.encode_into(out, val_size)
|
|
84
|
+
out.pos = saved_pos
|
|
85
|
+
|
|
86
|
+
def decode(self, data, version=None):
|
|
87
|
+
num_fields = UnsignedVarInt32.decode(data)
|
|
88
|
+
ret = {}
|
|
89
|
+
for i in range(num_fields):
|
|
90
|
+
tag = UnsignedVarInt32.decode(data)
|
|
91
|
+
size = UnsignedVarInt32.decode(data)
|
|
92
|
+
if tag in self._tags:
|
|
93
|
+
field = self._tags[tag]
|
|
94
|
+
# struct tags have an empty nested tagged fields (tagged=None)
|
|
95
|
+
ret[field.name] = field.decode(data, version=version, compact=True, tagged=None)
|
|
96
|
+
else:
|
|
97
|
+
ret['_%d' % tag] = data.read(size)
|
|
98
|
+
return ret
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def decode_empty(cls, data):
|
|
102
|
+
assert UnsignedVarInt32.decode(data) == 0
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def encode_empty(cls):
|
|
106
|
+
return UnsignedVarInt32.encode(0)
|
|
107
|
+
|
|
108
|
+
def __repr__(self):
|
|
109
|
+
return 'TaggedFields(%s)' % list(self._names.keys())
|