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
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from .api import Request, Response
|
|
2
|
+
from .types import Array, Int16, Schema, String
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SaslHandshakeResponse_v0(Response):
|
|
6
|
+
API_KEY = 17
|
|
7
|
+
API_VERSION = 0
|
|
8
|
+
SCHEMA = Schema(
|
|
9
|
+
('error_code', Int16),
|
|
10
|
+
('mechanisms', Array(String('utf-8')))
|
|
11
|
+
)
|
|
12
|
+
ALIASES = {
|
|
13
|
+
'enabled_mechanisms': 'mechanisms',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SaslHandshakeResponse_v1(Response):
|
|
18
|
+
API_KEY = 17
|
|
19
|
+
API_VERSION = 1
|
|
20
|
+
SCHEMA = SaslHandshakeResponse_v0.SCHEMA
|
|
21
|
+
ALIASES = SaslHandshakeResponse_v0.ALIASES
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SaslHandshakeRequest_v0(Request):
|
|
25
|
+
API_KEY = 17
|
|
26
|
+
API_VERSION = 0
|
|
27
|
+
SCHEMA = Schema(
|
|
28
|
+
('mechanism', String('utf-8'))
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SaslHandshakeRequest_v1(Request):
|
|
33
|
+
API_KEY = 17
|
|
34
|
+
API_VERSION = 1
|
|
35
|
+
SCHEMA = SaslHandshakeRequest_v0.SCHEMA
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
SaslHandshakeRequest = [SaslHandshakeRequest_v0, SaslHandshakeRequest_v1]
|
|
39
|
+
SaslHandshakeResponse = [SaslHandshakeResponse_v0, SaslHandshakeResponse_v1]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from abc import ABC, abstractproperty
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
|
|
4
|
+
from .abstract import AbstractType
|
|
5
|
+
from .types import Schema, TaggedFields
|
|
6
|
+
|
|
7
|
+
from kafka.util import WeakMethod
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Struct(ABC):
|
|
11
|
+
|
|
12
|
+
@abstractproperty
|
|
13
|
+
def SCHEMA(self):
|
|
14
|
+
"""An instance of Schema() representing the structure"""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
ALIASES = {} # for compatibility with new protocol defs from json
|
|
18
|
+
def __getattr__(self, name):
|
|
19
|
+
if name in self.ALIASES:
|
|
20
|
+
return getattr(self, self.ALIASES[name])
|
|
21
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
22
|
+
|
|
23
|
+
def __setattr__(self, name, value):
|
|
24
|
+
if name in self.ALIASES:
|
|
25
|
+
name = self.ALIASES[name]
|
|
26
|
+
return super().__setattr__(name, value)
|
|
27
|
+
|
|
28
|
+
def __init__(self, *args, **kwargs):
|
|
29
|
+
if self.SCHEMA.has_tagged_fields():
|
|
30
|
+
# Dont require TaggedFields value in *args
|
|
31
|
+
if len(args) == len(self.SCHEMA) - 1:
|
|
32
|
+
args = (*args, {})
|
|
33
|
+
elif len(args) == len(self.SCHEMA) and args[-1] is None:
|
|
34
|
+
args = (*args[:-1], {})
|
|
35
|
+
if len(args) == len(self.SCHEMA):
|
|
36
|
+
for i, name in enumerate(self.SCHEMA.names):
|
|
37
|
+
setattr(self, name, args[i])
|
|
38
|
+
elif len(args) > 0:
|
|
39
|
+
raise ValueError('Args must be empty or mirror schema')
|
|
40
|
+
else:
|
|
41
|
+
if self.SCHEMA.has_tagged_fields():
|
|
42
|
+
if kwargs.get('tags') is None:
|
|
43
|
+
kwargs['tags'] = {}
|
|
44
|
+
for name in self.ALIASES:
|
|
45
|
+
if name in kwargs:
|
|
46
|
+
kwargs[self.ALIASES[name]] = kwargs.pop(name)
|
|
47
|
+
for name in self.SCHEMA.names:
|
|
48
|
+
setattr(self, name, kwargs.pop(name, None))
|
|
49
|
+
if kwargs:
|
|
50
|
+
raise ValueError('Keyword(s) not in schema %s: %s'
|
|
51
|
+
% (list(self.SCHEMA.names),
|
|
52
|
+
', '.join(kwargs.keys())))
|
|
53
|
+
|
|
54
|
+
def encode(self):
|
|
55
|
+
return self.SCHEMA.encode(
|
|
56
|
+
[getattr(self, name) for name in self.SCHEMA.names]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def decode(cls, data):
|
|
61
|
+
if isinstance(data, bytes):
|
|
62
|
+
data = BytesIO(data)
|
|
63
|
+
return cls(*cls.SCHEMA.decode(data))
|
|
64
|
+
|
|
65
|
+
def get_item(self, name):
|
|
66
|
+
if name not in self.SCHEMA.names:
|
|
67
|
+
raise KeyError("%s is not in the schema" % name)
|
|
68
|
+
return getattr(self, name)
|
|
69
|
+
|
|
70
|
+
def __repr__(self):
|
|
71
|
+
key_vals = []
|
|
72
|
+
for name, field in zip(self.SCHEMA.names, self.SCHEMA.fields):
|
|
73
|
+
key_vals.append('%s=%s' % (name, field.repr(getattr(self, name))))
|
|
74
|
+
return self.__class__.__name__ + '(' + ', '.join(key_vals) + ')'
|
|
75
|
+
|
|
76
|
+
def __hash__(self):
|
|
77
|
+
return hash(self.encode())
|
|
78
|
+
|
|
79
|
+
def __eq__(self, other):
|
|
80
|
+
if not isinstance(other, Struct):
|
|
81
|
+
return False
|
|
82
|
+
if self.SCHEMA != other.SCHEMA:
|
|
83
|
+
return False
|
|
84
|
+
for attr in self.SCHEMA.names:
|
|
85
|
+
if getattr(self, attr) != getattr(other, attr):
|
|
86
|
+
return False
|
|
87
|
+
return True
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from .api import Request, Response
|
|
2
|
+
from .types import Array, Int16, Int32, Int64, Schema, String
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TxnOffsetCommitResponse_v0(Response):
|
|
6
|
+
API_KEY = 28
|
|
7
|
+
API_VERSION = 0
|
|
8
|
+
SCHEMA = Schema(
|
|
9
|
+
('throttle_time_ms', Int32),
|
|
10
|
+
('topics', Array(
|
|
11
|
+
('name', String('utf-8')),
|
|
12
|
+
('partitions', Array(
|
|
13
|
+
('partition_index', Int32),
|
|
14
|
+
('error_code', Int16))))))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TxnOffsetCommitResponse_v1(Response):
|
|
18
|
+
API_KEY = 28
|
|
19
|
+
API_VERSION = 1
|
|
20
|
+
SCHEMA = TxnOffsetCommitResponse_v0.SCHEMA
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TxnOffsetCommitResponse_v2(Response):
|
|
24
|
+
API_KEY = 28
|
|
25
|
+
API_VERSION = 2
|
|
26
|
+
SCHEMA = TxnOffsetCommitResponse_v1.SCHEMA
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TxnOffsetCommitRequest_v0(Request):
|
|
30
|
+
API_KEY = 28
|
|
31
|
+
API_VERSION = 0
|
|
32
|
+
SCHEMA = Schema(
|
|
33
|
+
('transactional_id', String('utf-8')),
|
|
34
|
+
('group_id', String('utf-8')),
|
|
35
|
+
('producer_id', Int64),
|
|
36
|
+
('producer_epoch', Int16),
|
|
37
|
+
('topics', Array(
|
|
38
|
+
('name', String('utf-8')),
|
|
39
|
+
('partitions', Array(
|
|
40
|
+
('partition_index', Int32),
|
|
41
|
+
('committed_offset', Int64),
|
|
42
|
+
('committed_metadata', String('utf-8')))))))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TxnOffsetCommitRequest_v1(Request):
|
|
46
|
+
API_KEY = 28
|
|
47
|
+
API_VERSION = 1
|
|
48
|
+
SCHEMA = TxnOffsetCommitRequest_v0.SCHEMA
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TxnOffsetCommitRequest_v2(Request):
|
|
52
|
+
API_KEY = 28
|
|
53
|
+
API_VERSION = 2
|
|
54
|
+
SCHEMA = Schema(
|
|
55
|
+
('transactional_id', String('utf-8')),
|
|
56
|
+
('group_id', String('utf-8')),
|
|
57
|
+
('producer_id', Int64),
|
|
58
|
+
('producer_epoch', Int16),
|
|
59
|
+
('topics', Array(
|
|
60
|
+
('name', String('utf-8')),
|
|
61
|
+
('partitions', Array(
|
|
62
|
+
('partition_index', Int32),
|
|
63
|
+
('committed_offset', Int64),
|
|
64
|
+
('committed_leader_epoch', Int32),
|
|
65
|
+
('committed_metadata', String('utf-8')))))))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
TxnOffsetCommitRequest = [
|
|
69
|
+
TxnOffsetCommitRequest_v0, TxnOffsetCommitRequest_v1, TxnOffsetCommitRequest_v2,
|
|
70
|
+
]
|
|
71
|
+
TxnOffsetCommitResponse = [
|
|
72
|
+
TxnOffsetCommitResponse_v0, TxnOffsetCommitResponse_v1, TxnOffsetCommitResponse_v2,
|
|
73
|
+
]
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
from struct import error
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from .abstract import AbstractType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _pack(f, value):
|
|
9
|
+
try:
|
|
10
|
+
return f(value)
|
|
11
|
+
except error as e:
|
|
12
|
+
try:
|
|
13
|
+
fmt = f.__self__.format
|
|
14
|
+
except AttributeError:
|
|
15
|
+
fmt = 'unknown'
|
|
16
|
+
raise ValueError("Error encountered when attempting to convert value: "
|
|
17
|
+
"{!r} to struct format: '{}', hit error: {}"
|
|
18
|
+
.format(value, fmt, e))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _unpack(f, data):
|
|
22
|
+
try:
|
|
23
|
+
(value,) = f(data)
|
|
24
|
+
return value
|
|
25
|
+
except error as e:
|
|
26
|
+
try:
|
|
27
|
+
fmt = f.__self__.format
|
|
28
|
+
except AttributeError:
|
|
29
|
+
fmt = 'unknown'
|
|
30
|
+
raise ValueError("Error encountered when attempting to convert value: "
|
|
31
|
+
"{!r} to struct format: '{}', hit error: {}"
|
|
32
|
+
.format(data, fmt, e))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Int8(AbstractType):
|
|
36
|
+
_pack = struct.Struct('>b').pack
|
|
37
|
+
_unpack = struct.Struct('>b').unpack
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def encode(cls, value):
|
|
41
|
+
return _pack(cls._pack, value)
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def decode(cls, data):
|
|
45
|
+
return _unpack(cls._unpack, data.read(1))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Int16(AbstractType):
|
|
49
|
+
_pack = struct.Struct('>h').pack
|
|
50
|
+
_unpack = struct.Struct('>h').unpack
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def encode(cls, value):
|
|
54
|
+
return _pack(cls._pack, value)
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def decode(cls, data):
|
|
58
|
+
return _unpack(cls._unpack, data.read(2))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Int32(AbstractType):
|
|
62
|
+
_pack = struct.Struct('>i').pack
|
|
63
|
+
_unpack = struct.Struct('>i').unpack
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def encode(cls, value):
|
|
67
|
+
return _pack(cls._pack, value)
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def decode(cls, data):
|
|
71
|
+
return _unpack(cls._unpack, data.read(4))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class Int64(AbstractType):
|
|
75
|
+
_pack = struct.Struct('>q').pack
|
|
76
|
+
_unpack = struct.Struct('>q').unpack
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def encode(cls, value):
|
|
80
|
+
return _pack(cls._pack, value)
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def decode(cls, data):
|
|
84
|
+
return _unpack(cls._unpack, data.read(8))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class Float64(AbstractType):
|
|
88
|
+
_pack = struct.Struct('>d').pack
|
|
89
|
+
_unpack = struct.Struct('>d').unpack
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def encode(cls, value):
|
|
93
|
+
return _pack(cls._pack, value)
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def decode(cls, data):
|
|
97
|
+
return _unpack(cls._unpack, data.read(8))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class UUID(AbstractType):
|
|
101
|
+
ZERO_UUID = uuid.UUID(int=0)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def encode(cls, value):
|
|
105
|
+
if value is None:
|
|
106
|
+
value = cls.ZERO_UUID
|
|
107
|
+
if isinstance(value, uuid.UUID):
|
|
108
|
+
return value.bytes
|
|
109
|
+
return uuid.UUID(value).bytes
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def decode(cls, data):
|
|
113
|
+
val = uuid.UUID(bytes=data.read(16))
|
|
114
|
+
if val == cls.ZERO_UUID:
|
|
115
|
+
return None
|
|
116
|
+
return val
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class String(AbstractType):
|
|
120
|
+
def __init__(self, encoding='utf-8'):
|
|
121
|
+
self.encoding = encoding
|
|
122
|
+
|
|
123
|
+
def encode(self, value):
|
|
124
|
+
if value is None:
|
|
125
|
+
return Int16.encode(-1)
|
|
126
|
+
value = str(value).encode(self.encoding)
|
|
127
|
+
return Int16.encode(len(value)) + value
|
|
128
|
+
|
|
129
|
+
def decode(self, data):
|
|
130
|
+
length = Int16.decode(data)
|
|
131
|
+
if length < 0:
|
|
132
|
+
return None
|
|
133
|
+
value = data.read(length)
|
|
134
|
+
if len(value) != length:
|
|
135
|
+
raise ValueError('Buffer underrun decoding string')
|
|
136
|
+
return value.decode(self.encoding)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class Bytes(AbstractType):
|
|
140
|
+
@classmethod
|
|
141
|
+
def encode(cls, value):
|
|
142
|
+
if value is None:
|
|
143
|
+
return Int32.encode(-1)
|
|
144
|
+
elif not isinstance(value, bytes):
|
|
145
|
+
value = value.encode()
|
|
146
|
+
return Int32.encode(len(value)) + value
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def decode(cls, data):
|
|
150
|
+
length = Int32.decode(data)
|
|
151
|
+
if length < 0:
|
|
152
|
+
return None
|
|
153
|
+
value = data.read(length)
|
|
154
|
+
if len(value) != length:
|
|
155
|
+
raise ValueError('Buffer underrun decoding Bytes')
|
|
156
|
+
return value
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def repr(cls, value):
|
|
160
|
+
return repr(value[:100] + b'...' if value is not None and len(value) > 100 else value)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class Boolean(AbstractType):
|
|
164
|
+
_pack = struct.Struct('>?').pack
|
|
165
|
+
_unpack = struct.Struct('>?').unpack
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def encode(cls, value):
|
|
169
|
+
return _pack(cls._pack, value)
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def decode(cls, data):
|
|
173
|
+
return _unpack(cls._unpack, data.read(1))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class Schema(AbstractType):
|
|
177
|
+
def __init__(self, *fields):
|
|
178
|
+
if fields:
|
|
179
|
+
self.names, self.fields = zip(*fields)
|
|
180
|
+
else:
|
|
181
|
+
self.names, self.fields = (), ()
|
|
182
|
+
|
|
183
|
+
def has_tagged_fields(self):
|
|
184
|
+
return len(self.fields) and self.fields[-1] is TaggedFields
|
|
185
|
+
|
|
186
|
+
def encode(self, item):
|
|
187
|
+
# Add empty tags to item if missing
|
|
188
|
+
if self.has_tagged_fields() and len(item) == len(self.fields) - 1:
|
|
189
|
+
item = [*item, {}]
|
|
190
|
+
if len(item) != len(self.fields):
|
|
191
|
+
raise ValueError('Item field count does not match Schema')
|
|
192
|
+
return b''.join([
|
|
193
|
+
field.encode(item[i])
|
|
194
|
+
for i, field in enumerate(self.fields)
|
|
195
|
+
])
|
|
196
|
+
|
|
197
|
+
def decode(self, data):
|
|
198
|
+
decoded = [field.decode(data) for field in self.fields]
|
|
199
|
+
# Drop empty tags from tuple decoding
|
|
200
|
+
if self.has_tagged_fields() and decoded[-1] == {}:
|
|
201
|
+
decoded.pop()
|
|
202
|
+
return tuple(decoded)
|
|
203
|
+
|
|
204
|
+
def __len__(self):
|
|
205
|
+
return len(self.fields)
|
|
206
|
+
|
|
207
|
+
def repr(self, value):
|
|
208
|
+
key_vals = []
|
|
209
|
+
try:
|
|
210
|
+
for i in range(len(self)):
|
|
211
|
+
try:
|
|
212
|
+
field_val = getattr(value, self.names[i])
|
|
213
|
+
except AttributeError:
|
|
214
|
+
field_val = value[i]
|
|
215
|
+
key_vals.append('%s=%s' % (self.names[i], self.fields[i].repr(field_val)))
|
|
216
|
+
return '(' + ', '.join(key_vals) + ')'
|
|
217
|
+
except Exception:
|
|
218
|
+
return repr(value)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class Array(AbstractType):
|
|
222
|
+
def __init__(self, *array_of):
|
|
223
|
+
if len(array_of) > 1:
|
|
224
|
+
self.array_of = Schema(*array_of)
|
|
225
|
+
elif len(array_of) == 1 and (isinstance(array_of[0], AbstractType) or
|
|
226
|
+
issubclass(array_of[0], AbstractType)):
|
|
227
|
+
self.array_of = array_of[0]
|
|
228
|
+
else:
|
|
229
|
+
raise ValueError('Array instantiated with no array_of type')
|
|
230
|
+
|
|
231
|
+
def encode(self, items):
|
|
232
|
+
if items is None:
|
|
233
|
+
return Int32.encode(-1)
|
|
234
|
+
encoded_items = [self.array_of.encode(item) for item in items]
|
|
235
|
+
return b''.join(
|
|
236
|
+
[Int32.encode(len(encoded_items))] +
|
|
237
|
+
encoded_items
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def decode(self, data):
|
|
241
|
+
length = Int32.decode(data)
|
|
242
|
+
if length == -1:
|
|
243
|
+
return None
|
|
244
|
+
return [self.array_of.decode(data) for _ in range(length)]
|
|
245
|
+
|
|
246
|
+
def repr(self, list_of_items):
|
|
247
|
+
if list_of_items is None:
|
|
248
|
+
return 'NULL'
|
|
249
|
+
return '[' + ', '.join([self.array_of.repr(item) for item in list_of_items]) + ']'
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class UnsignedVarInt32(AbstractType):
|
|
253
|
+
@classmethod
|
|
254
|
+
def decode(cls, data):
|
|
255
|
+
value = VarInt32.decode(data)
|
|
256
|
+
return (value << 1) ^ (value >> 31)
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
def encode(cls, value):
|
|
260
|
+
return VarInt32.encode((value >> 1) ^ -(value & 1))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class VarInt32(AbstractType):
|
|
264
|
+
@classmethod
|
|
265
|
+
def decode(cls, data):
|
|
266
|
+
value, i = 0, 0
|
|
267
|
+
while True:
|
|
268
|
+
b, = struct.unpack('B', data.read(1))
|
|
269
|
+
if not (b & 0x80):
|
|
270
|
+
break
|
|
271
|
+
value |= (b & 0x7f) << i
|
|
272
|
+
i += 7
|
|
273
|
+
if i > 28:
|
|
274
|
+
raise ValueError('Invalid value {}'.format(value))
|
|
275
|
+
value |= b << i
|
|
276
|
+
return (value >> 1) ^ -(value & 1)
|
|
277
|
+
|
|
278
|
+
@classmethod
|
|
279
|
+
def encode(cls, value):
|
|
280
|
+
# bring it in line with the java binary repr
|
|
281
|
+
value = (value << 1) ^ (value >> 31)
|
|
282
|
+
value &= 0xffffffff
|
|
283
|
+
ret = b''
|
|
284
|
+
while (value & 0xffffff80) != 0:
|
|
285
|
+
b = (value & 0x7f) | 0x80
|
|
286
|
+
ret += struct.pack('B', b)
|
|
287
|
+
value >>= 7
|
|
288
|
+
ret += struct.pack('B', value)
|
|
289
|
+
return ret
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class VarInt64(AbstractType):
|
|
293
|
+
@classmethod
|
|
294
|
+
def decode(cls, data):
|
|
295
|
+
value, i = 0, 0
|
|
296
|
+
while True:
|
|
297
|
+
b, = struct.unpack('B', data.read(1))
|
|
298
|
+
if not (b & 0x80):
|
|
299
|
+
break
|
|
300
|
+
value |= (b & 0x7f) << i
|
|
301
|
+
i += 7
|
|
302
|
+
if i > 63:
|
|
303
|
+
raise ValueError('Invalid value {}'.format(value))
|
|
304
|
+
value |= b << i
|
|
305
|
+
return (value >> 1) ^ -(value & 1)
|
|
306
|
+
|
|
307
|
+
@classmethod
|
|
308
|
+
def encode(cls, value):
|
|
309
|
+
# bring it in line with the java binary repr
|
|
310
|
+
value = (value << 1) ^ (value >> 63)
|
|
311
|
+
value &= 0xffffffffffffffff
|
|
312
|
+
ret = b''
|
|
313
|
+
while (value & 0xffffffffffffff80) != 0:
|
|
314
|
+
b = (value & 0x7f) | 0x80
|
|
315
|
+
ret += struct.pack('B', b)
|
|
316
|
+
value >>= 7
|
|
317
|
+
ret += struct.pack('B', value)
|
|
318
|
+
return ret
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class CompactString(String):
|
|
322
|
+
def decode(self, data):
|
|
323
|
+
length = UnsignedVarInt32.decode(data) - 1
|
|
324
|
+
if length < 0:
|
|
325
|
+
return None
|
|
326
|
+
value = data.read(length)
|
|
327
|
+
if len(value) != length:
|
|
328
|
+
raise ValueError('Buffer underrun decoding string')
|
|
329
|
+
return value.decode(self.encoding)
|
|
330
|
+
|
|
331
|
+
def encode(self, value):
|
|
332
|
+
if value is None:
|
|
333
|
+
return UnsignedVarInt32.encode(0)
|
|
334
|
+
value = str(value).encode(self.encoding)
|
|
335
|
+
return UnsignedVarInt32.encode(len(value) + 1) + value
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class TaggedFields(AbstractType):
|
|
339
|
+
@classmethod
|
|
340
|
+
def decode(cls, data):
|
|
341
|
+
num_fields = UnsignedVarInt32.decode(data)
|
|
342
|
+
ret = {}
|
|
343
|
+
if not num_fields:
|
|
344
|
+
return ret
|
|
345
|
+
prev_tag = -1
|
|
346
|
+
for i in range(num_fields):
|
|
347
|
+
tag = UnsignedVarInt32.decode(data)
|
|
348
|
+
if tag <= prev_tag:
|
|
349
|
+
raise ValueError('Invalid or out-of-order tag {}'.format(tag))
|
|
350
|
+
prev_tag = tag
|
|
351
|
+
size = UnsignedVarInt32.decode(data)
|
|
352
|
+
val = data.read(size)
|
|
353
|
+
ret[tag] = val
|
|
354
|
+
return ret
|
|
355
|
+
|
|
356
|
+
@classmethod
|
|
357
|
+
def encode(cls, value):
|
|
358
|
+
if value is None:
|
|
359
|
+
value = {}
|
|
360
|
+
ret = UnsignedVarInt32.encode(len(value))
|
|
361
|
+
for k, v in value.items():
|
|
362
|
+
# do we allow for other data types ?? It could get complicated really fast
|
|
363
|
+
assert isinstance(v, bytes), 'Value {} is not a byte array'.format(v)
|
|
364
|
+
assert isinstance(k, int) and k >= 0, 'Key {} is not a non-negative integer'.format(k)
|
|
365
|
+
ret += UnsignedVarInt32.encode(k)
|
|
366
|
+
ret += UnsignedVarInt32.encode(len(v))
|
|
367
|
+
ret += v
|
|
368
|
+
return ret
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class CompactBytes(AbstractType):
|
|
372
|
+
@classmethod
|
|
373
|
+
def decode(cls, data):
|
|
374
|
+
length = UnsignedVarInt32.decode(data) - 1
|
|
375
|
+
if length < 0:
|
|
376
|
+
return None
|
|
377
|
+
value = data.read(length)
|
|
378
|
+
if len(value) != length:
|
|
379
|
+
raise ValueError('Buffer underrun decoding Bytes')
|
|
380
|
+
return value
|
|
381
|
+
|
|
382
|
+
@classmethod
|
|
383
|
+
def encode(cls, value):
|
|
384
|
+
if value is None:
|
|
385
|
+
return UnsignedVarInt32.encode(0)
|
|
386
|
+
else:
|
|
387
|
+
return UnsignedVarInt32.encode(len(value) + 1) + value
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class CompactArray(Array):
|
|
391
|
+
def encode(self, items):
|
|
392
|
+
if items is None:
|
|
393
|
+
return UnsignedVarInt32.encode(0)
|
|
394
|
+
return b''.join(
|
|
395
|
+
[UnsignedVarInt32.encode(len(items) + 1)] +
|
|
396
|
+
[self.array_of.encode(item) for item in items]
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
def decode(self, data):
|
|
400
|
+
length = UnsignedVarInt32.decode(data) - 1
|
|
401
|
+
if length == -1:
|
|
402
|
+
return None
|
|
403
|
+
return [self.array_of.decode(data) for _ in range(length)]
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class BitField(AbstractType):
|
|
407
|
+
@classmethod
|
|
408
|
+
def decode(cls, data):
|
|
409
|
+
vals = cls.from_32_bit_field(Int32.decode(data))
|
|
410
|
+
if vals == {31}:
|
|
411
|
+
vals = None
|
|
412
|
+
return vals
|
|
413
|
+
|
|
414
|
+
@classmethod
|
|
415
|
+
def encode(cls, vals):
|
|
416
|
+
if vals is None:
|
|
417
|
+
vals = {31}
|
|
418
|
+
# to_32_bit_field returns unsigned val, so we need to
|
|
419
|
+
# encode >I to avoid crash if/when byte 31 is set
|
|
420
|
+
# (note that decode as signed still works fine)
|
|
421
|
+
return struct.Struct('>I').pack(cls.to_32_bit_field(vals))
|
|
422
|
+
|
|
423
|
+
@classmethod
|
|
424
|
+
def to_32_bit_field(cls, vals):
|
|
425
|
+
value = 0
|
|
426
|
+
for b in vals:
|
|
427
|
+
assert 0 <= b < 32
|
|
428
|
+
value |= 1 << b
|
|
429
|
+
return value
|
|
430
|
+
|
|
431
|
+
@classmethod
|
|
432
|
+
def from_32_bit_field(cls, value):
|
|
433
|
+
result = set()
|
|
434
|
+
count = 0
|
|
435
|
+
while value != 0:
|
|
436
|
+
if (value & 1) != 0:
|
|
437
|
+
result.add(count)
|
|
438
|
+
count += 1
|
|
439
|
+
value = (value & 0xFFFFFFFF) >> 1
|
|
440
|
+
return result
|