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/codec.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import gzip
|
|
2
|
+
import io
|
|
3
|
+
import platform
|
|
4
|
+
import struct
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_XERIAL_V1_HEADER = (-126, b'S', b'N', b'A', b'P', b'P', b'Y', 0, 1, 1)
|
|
8
|
+
_XERIAL_V1_FORMAT = 'bccccccBii'
|
|
9
|
+
ZSTD_MAX_OUTPUT_SIZE = 1024 * 1024
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import snappy
|
|
13
|
+
except ImportError:
|
|
14
|
+
snappy = None
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import zstandard as zstd
|
|
18
|
+
except ImportError:
|
|
19
|
+
zstd = None
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import lz4.frame as lz4
|
|
23
|
+
|
|
24
|
+
def _lz4_compress(payload, **kwargs):
|
|
25
|
+
# Kafka does not support LZ4 dependent blocks
|
|
26
|
+
try:
|
|
27
|
+
# For lz4>=0.12.0
|
|
28
|
+
kwargs.pop('block_linked', None)
|
|
29
|
+
return lz4.compress(payload, block_linked=False, **kwargs)
|
|
30
|
+
except TypeError:
|
|
31
|
+
# For earlier versions of lz4
|
|
32
|
+
kwargs.pop('block_mode', None)
|
|
33
|
+
return lz4.compress(payload, block_mode=1, **kwargs)
|
|
34
|
+
|
|
35
|
+
except ImportError:
|
|
36
|
+
lz4 = None
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
import lz4f
|
|
40
|
+
except ImportError:
|
|
41
|
+
lz4f = None
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
import lz4framed
|
|
45
|
+
except ImportError:
|
|
46
|
+
lz4framed = None
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
import xxhash
|
|
50
|
+
except ImportError:
|
|
51
|
+
xxhash = None
|
|
52
|
+
|
|
53
|
+
PYPY = bool(platform.python_implementation() == 'PyPy')
|
|
54
|
+
|
|
55
|
+
def has_gzip():
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def has_snappy():
|
|
60
|
+
return snappy is not None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def has_zstd():
|
|
64
|
+
return zstd is not None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def has_lz4():
|
|
68
|
+
if lz4 is not None:
|
|
69
|
+
return True
|
|
70
|
+
if lz4f is not None:
|
|
71
|
+
return True
|
|
72
|
+
if lz4framed is not None:
|
|
73
|
+
return True
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def gzip_encode(payload, compresslevel=None):
|
|
78
|
+
if not compresslevel:
|
|
79
|
+
compresslevel = 9
|
|
80
|
+
|
|
81
|
+
buf = io.BytesIO()
|
|
82
|
+
|
|
83
|
+
# Gzip context manager introduced in python 2.7
|
|
84
|
+
# so old-fashioned way until we decide to not support 2.6
|
|
85
|
+
gzipper = gzip.GzipFile(fileobj=buf, mode="w", compresslevel=compresslevel)
|
|
86
|
+
try:
|
|
87
|
+
gzipper.write(payload)
|
|
88
|
+
finally:
|
|
89
|
+
gzipper.close()
|
|
90
|
+
|
|
91
|
+
return buf.getvalue()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def gzip_decode(payload):
|
|
95
|
+
buf = io.BytesIO(payload)
|
|
96
|
+
|
|
97
|
+
# Gzip context manager introduced in python 2.7
|
|
98
|
+
# so old-fashioned way until we decide to not support 2.6
|
|
99
|
+
gzipper = gzip.GzipFile(fileobj=buf, mode='r')
|
|
100
|
+
try:
|
|
101
|
+
return gzipper.read()
|
|
102
|
+
finally:
|
|
103
|
+
gzipper.close()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def snappy_encode(payload, xerial_compatible=True, xerial_blocksize=32*1024):
|
|
107
|
+
"""Encodes the given data with snappy compression.
|
|
108
|
+
|
|
109
|
+
If xerial_compatible is set then the stream is encoded in a fashion
|
|
110
|
+
compatible with the xerial snappy library.
|
|
111
|
+
|
|
112
|
+
The block size (xerial_blocksize) controls how frequent the blocking occurs
|
|
113
|
+
32k is the default in the xerial library.
|
|
114
|
+
|
|
115
|
+
The format winds up being:
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
+-------------+------------+--------------+------------+--------------+
|
|
119
|
+
| Header | Block1 len | Block1 data | Blockn len | Blockn data |
|
|
120
|
+
+-------------+------------+--------------+------------+--------------+
|
|
121
|
+
| 16 bytes | BE int32 | snappy bytes | BE int32 | snappy bytes |
|
|
122
|
+
+-------------+------------+--------------+------------+--------------+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
It is important to note that the blocksize is the amount of uncompressed
|
|
126
|
+
data presented to snappy at each block, whereas the blocklen is the number
|
|
127
|
+
of bytes that will be present in the stream; so the length will always be
|
|
128
|
+
<= blocksize.
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
if not has_snappy():
|
|
133
|
+
raise NotImplementedError("Snappy codec is not available")
|
|
134
|
+
|
|
135
|
+
if not xerial_compatible:
|
|
136
|
+
return snappy.compress(payload)
|
|
137
|
+
|
|
138
|
+
out = io.BytesIO()
|
|
139
|
+
for fmt, dat in zip(_XERIAL_V1_FORMAT, _XERIAL_V1_HEADER):
|
|
140
|
+
out.write(struct.pack('!' + fmt, dat))
|
|
141
|
+
|
|
142
|
+
# Chunk through buffers to avoid creating intermediate slice copies
|
|
143
|
+
if PYPY:
|
|
144
|
+
# on pypy, snappy.compress() on a sliced buffer consumes the entire
|
|
145
|
+
# buffer... likely a python-snappy bug, so just use a slice copy
|
|
146
|
+
chunker = lambda payload, i, size: payload[i:size+i]
|
|
147
|
+
|
|
148
|
+
else:
|
|
149
|
+
# snappy.compress does not like raw memoryviews, so we have to convert
|
|
150
|
+
# tobytes, which is a copy... oh well. it's the thought that counts.
|
|
151
|
+
# pylint: disable-msg=undefined-variable
|
|
152
|
+
chunker = lambda payload, i, size: memoryview(payload)[i:size+i].tobytes()
|
|
153
|
+
|
|
154
|
+
for chunk in (chunker(payload, i, xerial_blocksize)
|
|
155
|
+
for i in range(0, len(payload), xerial_blocksize)):
|
|
156
|
+
|
|
157
|
+
block = snappy.compress(chunk)
|
|
158
|
+
block_size = len(block)
|
|
159
|
+
out.write(struct.pack('!i', block_size))
|
|
160
|
+
out.write(block)
|
|
161
|
+
|
|
162
|
+
return out.getvalue()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _detect_xerial_stream(payload):
|
|
166
|
+
"""Detects if the data given might have been encoded with the blocking mode
|
|
167
|
+
of the xerial snappy library.
|
|
168
|
+
|
|
169
|
+
This mode writes a magic header of the format:
|
|
170
|
+
+--------+--------------+------------+---------+--------+
|
|
171
|
+
| Marker | Magic String | Null / Pad | Version | Compat |
|
|
172
|
+
+--------+--------------+------------+---------+--------+
|
|
173
|
+
| byte | c-string | byte | int32 | int32 |
|
|
174
|
+
+--------+--------------+------------+---------+--------+
|
|
175
|
+
| -126 | 'SNAPPY' | \0 | | |
|
|
176
|
+
+--------+--------------+------------+---------+--------+
|
|
177
|
+
|
|
178
|
+
The pad appears to be to ensure that SNAPPY is a valid cstring
|
|
179
|
+
The version is the version of this format as written by xerial,
|
|
180
|
+
in the wild this is currently 1 as such we only support v1.
|
|
181
|
+
|
|
182
|
+
Compat is there to claim the minimum supported version that
|
|
183
|
+
can read a xerial block stream, presently in the wild this is
|
|
184
|
+
1.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
if len(payload) > 16:
|
|
188
|
+
magic = struct.unpack('!' + _XERIAL_V1_FORMAT[:8], bytes(payload)[:8])
|
|
189
|
+
version, compat = struct.unpack('!' + _XERIAL_V1_FORMAT[8:], bytes(payload)[8:16])
|
|
190
|
+
# Until there is more than one way to do xerial blocking, the version + compat
|
|
191
|
+
# fields can be ignored. Also some producers (i.e., redpanda) are known to
|
|
192
|
+
# incorrectly encode these as little-endian, and that causes us to fail decoding
|
|
193
|
+
# when we otherwise would have succeeded.
|
|
194
|
+
# See https://github.com/dpkp/kafka-python/issues/2414
|
|
195
|
+
if magic == _XERIAL_V1_HEADER[:8]:
|
|
196
|
+
return True
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def snappy_decode(payload):
|
|
201
|
+
if not has_snappy():
|
|
202
|
+
raise NotImplementedError("Snappy codec is not available")
|
|
203
|
+
|
|
204
|
+
if _detect_xerial_stream(payload):
|
|
205
|
+
# TODO ? Should become a fileobj ?
|
|
206
|
+
out = io.BytesIO()
|
|
207
|
+
byt = payload[16:]
|
|
208
|
+
length = len(byt)
|
|
209
|
+
cursor = 0
|
|
210
|
+
|
|
211
|
+
while cursor < length:
|
|
212
|
+
block_size = struct.unpack_from('!i', byt[cursor:])[0]
|
|
213
|
+
# Skip the block size
|
|
214
|
+
cursor += 4
|
|
215
|
+
end = cursor + block_size
|
|
216
|
+
out.write(snappy.decompress(byt[cursor:end]))
|
|
217
|
+
cursor = end
|
|
218
|
+
|
|
219
|
+
out.seek(0)
|
|
220
|
+
return out.read()
|
|
221
|
+
else:
|
|
222
|
+
return snappy.decompress(payload)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if lz4:
|
|
226
|
+
lz4_encode = _lz4_compress # pylint: disable-msg=no-member
|
|
227
|
+
elif lz4f:
|
|
228
|
+
lz4_encode = lz4f.compressFrame # pylint: disable-msg=no-member
|
|
229
|
+
elif lz4framed:
|
|
230
|
+
lz4_encode = lz4framed.compress # pylint: disable-msg=no-member
|
|
231
|
+
else:
|
|
232
|
+
lz4_encode = None
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def lz4f_decode(payload):
|
|
236
|
+
"""Decode payload using interoperable LZ4 framing. Requires Kafka >= 0.10"""
|
|
237
|
+
# pylint: disable-msg=no-member
|
|
238
|
+
ctx = lz4f.createDecompContext()
|
|
239
|
+
data = lz4f.decompressFrame(payload, ctx)
|
|
240
|
+
lz4f.freeDecompContext(ctx)
|
|
241
|
+
|
|
242
|
+
# lz4f python module does not expose how much of the payload was
|
|
243
|
+
# actually read if the decompression was only partial.
|
|
244
|
+
if data['next'] != 0:
|
|
245
|
+
raise RuntimeError('lz4f unable to decompress full payload')
|
|
246
|
+
return data['decomp']
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
if lz4:
|
|
250
|
+
lz4_decode = lz4.decompress # pylint: disable-msg=no-member
|
|
251
|
+
elif lz4f:
|
|
252
|
+
lz4_decode = lz4f_decode
|
|
253
|
+
elif lz4framed:
|
|
254
|
+
lz4_decode = lz4framed.decompress # pylint: disable-msg=no-member
|
|
255
|
+
else:
|
|
256
|
+
lz4_decode = None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def lz4_encode_old_kafka(payload):
|
|
260
|
+
"""Encode payload for 0.8/0.9 brokers -- requires an incorrect header checksum."""
|
|
261
|
+
if xxhash is None:
|
|
262
|
+
raise RuntimeError('pip install xxhash for lz4 encoding with 0.8/0.9 brokers')
|
|
263
|
+
data = lz4_encode(payload)
|
|
264
|
+
header_size = 7
|
|
265
|
+
flg = data[4]
|
|
266
|
+
if not isinstance(flg, int):
|
|
267
|
+
flg = ord(flg)
|
|
268
|
+
|
|
269
|
+
content_size_bit = ((flg >> 3) & 1)
|
|
270
|
+
if content_size_bit:
|
|
271
|
+
# Old kafka does not accept the content-size field
|
|
272
|
+
# so we need to discard it and reset the header flag
|
|
273
|
+
flg -= 8
|
|
274
|
+
data = bytearray(data)
|
|
275
|
+
data[4] = flg
|
|
276
|
+
data = bytes(data)
|
|
277
|
+
payload = data[header_size+8:]
|
|
278
|
+
else:
|
|
279
|
+
payload = data[header_size:]
|
|
280
|
+
|
|
281
|
+
# This is the incorrect hc
|
|
282
|
+
hc = xxhash.xxh32(data[0:header_size-1]).digest()[-2:-1] # pylint: disable-msg=no-member
|
|
283
|
+
|
|
284
|
+
return b''.join([
|
|
285
|
+
data[0:header_size-1],
|
|
286
|
+
hc,
|
|
287
|
+
payload
|
|
288
|
+
])
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def lz4_decode_old_kafka(payload):
|
|
292
|
+
if xxhash is None:
|
|
293
|
+
raise RuntimeError('pip install xxhash for lz4 encoding with 0.8/0.9 brokers')
|
|
294
|
+
# Kafka's LZ4 code has a bug in its header checksum implementation
|
|
295
|
+
header_size = 7
|
|
296
|
+
if isinstance(payload[4], int):
|
|
297
|
+
flg = payload[4]
|
|
298
|
+
else:
|
|
299
|
+
flg = ord(payload[4])
|
|
300
|
+
content_size_bit = ((flg >> 3) & 1)
|
|
301
|
+
if content_size_bit:
|
|
302
|
+
header_size += 8
|
|
303
|
+
|
|
304
|
+
# This should be the correct hc
|
|
305
|
+
hc = xxhash.xxh32(payload[4:header_size-1]).digest()[-2:-1] # pylint: disable-msg=no-member
|
|
306
|
+
|
|
307
|
+
munged_payload = b''.join([
|
|
308
|
+
payload[0:header_size-1],
|
|
309
|
+
hc,
|
|
310
|
+
payload[header_size:]
|
|
311
|
+
])
|
|
312
|
+
return lz4_decode(munged_payload)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def zstd_encode(payload):
|
|
316
|
+
if not zstd:
|
|
317
|
+
raise NotImplementedError("Zstd codec is not available")
|
|
318
|
+
return zstd.ZstdCompressor().compress(payload)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def zstd_decode(payload):
|
|
322
|
+
if not zstd:
|
|
323
|
+
raise NotImplementedError("Zstd codec is not available")
|
|
324
|
+
with zstd.ZstdDecompressor().stream_reader(io.BytesIO(payload), read_across_frames=True) as reader:
|
|
325
|
+
return reader.read()
|