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.
Files changed (373) hide show
  1. kafka/__init__.py +34 -0
  2. kafka/__main__.py +5 -0
  3. kafka/admin/__init__.py +29 -0
  4. kafka/admin/__main__.py +5 -0
  5. kafka/admin/_acls.py +355 -0
  6. kafka/admin/_cluster.py +359 -0
  7. kafka/admin/_configs.py +479 -0
  8. kafka/admin/_groups.py +754 -0
  9. kafka/admin/_partitions.py +595 -0
  10. kafka/admin/_topics.py +281 -0
  11. kafka/admin/_transactions.py +450 -0
  12. kafka/admin/_users.py +194 -0
  13. kafka/admin/client.py +373 -0
  14. kafka/benchmarks/__init__.py +0 -0
  15. kafka/benchmarks/consumer_performance.py +138 -0
  16. kafka/benchmarks/load_example.py +109 -0
  17. kafka/benchmarks/producer_encode_path.py +201 -0
  18. kafka/benchmarks/producer_performance.py +161 -0
  19. kafka/benchmarks/profile_protocol.py +138 -0
  20. kafka/benchmarks/protocol_old_vs_new.py +447 -0
  21. kafka/benchmarks/record_batch_compose.py +77 -0
  22. kafka/benchmarks/record_batch_read.py +82 -0
  23. kafka/benchmarks/varint_speed.py +426 -0
  24. kafka/cli/__init__.py +36 -0
  25. kafka/cli/admin/__init__.py +117 -0
  26. kafka/cli/admin/acls/__init__.py +9 -0
  27. kafka/cli/admin/acls/common.py +76 -0
  28. kafka/cli/admin/acls/create.py +19 -0
  29. kafka/cli/admin/acls/delete.py +23 -0
  30. kafka/cli/admin/acls/describe.py +16 -0
  31. kafka/cli/admin/cluster/__init__.py +14 -0
  32. kafka/cli/admin/cluster/describe.py +11 -0
  33. kafka/cli/admin/cluster/describe_quorum.py +11 -0
  34. kafka/cli/admin/cluster/features.py +52 -0
  35. kafka/cli/admin/cluster/log_dirs.py +43 -0
  36. kafka/cli/admin/cluster/versions.py +33 -0
  37. kafka/cli/admin/configs/__init__.py +10 -0
  38. kafka/cli/admin/configs/alter.py +43 -0
  39. kafka/cli/admin/configs/common.py +17 -0
  40. kafka/cli/admin/configs/describe.py +30 -0
  41. kafka/cli/admin/configs/list.py +16 -0
  42. kafka/cli/admin/configs/reset.py +20 -0
  43. kafka/cli/admin/groups/__init__.py +16 -0
  44. kafka/cli/admin/groups/alter_offsets.py +30 -0
  45. kafka/cli/admin/groups/delete.py +11 -0
  46. kafka/cli/admin/groups/delete_offsets.py +29 -0
  47. kafka/cli/admin/groups/describe.py +11 -0
  48. kafka/cli/admin/groups/list.py +28 -0
  49. kafka/cli/admin/groups/list_offsets.py +29 -0
  50. kafka/cli/admin/groups/remove_members.py +40 -0
  51. kafka/cli/admin/groups/reset_offsets.py +139 -0
  52. kafka/cli/admin/partitions/__init__.py +21 -0
  53. kafka/cli/admin/partitions/alter_reassignments.py +37 -0
  54. kafka/cli/admin/partitions/create.py +27 -0
  55. kafka/cli/admin/partitions/delete_records.py +31 -0
  56. kafka/cli/admin/partitions/describe.py +36 -0
  57. kafka/cli/admin/partitions/elect_leaders.py +53 -0
  58. kafka/cli/admin/partitions/list_offsets.py +88 -0
  59. kafka/cli/admin/partitions/list_reassignments.py +35 -0
  60. kafka/cli/admin/topics/__init__.py +10 -0
  61. kafka/cli/admin/topics/create.py +13 -0
  62. kafka/cli/admin/topics/delete.py +19 -0
  63. kafka/cli/admin/topics/describe.py +18 -0
  64. kafka/cli/admin/topics/list.py +11 -0
  65. kafka/cli/admin/transactions/__init__.py +17 -0
  66. kafka/cli/admin/transactions/abort.py +38 -0
  67. kafka/cli/admin/transactions/describe.py +24 -0
  68. kafka/cli/admin/transactions/describe_producers.py +29 -0
  69. kafka/cli/admin/transactions/find_hanging.py +26 -0
  70. kafka/cli/admin/transactions/list.py +37 -0
  71. kafka/cli/admin/users/__init__.py +8 -0
  72. kafka/cli/admin/users/alter_user_scram_credentials.py +34 -0
  73. kafka/cli/admin/users/describe_user_scram_credentials.py +15 -0
  74. kafka/cli/common.py +95 -0
  75. kafka/cli/consumer/__init__.py +63 -0
  76. kafka/cli/producer/__init__.py +57 -0
  77. kafka/cluster.py +824 -0
  78. kafka/codec.py +325 -0
  79. kafka/consumer/__init__.py +5 -0
  80. kafka/consumer/__main__.py +5 -0
  81. kafka/consumer/fetcher.py +2012 -0
  82. kafka/consumer/group.py +1347 -0
  83. kafka/consumer/subscription_state.py +897 -0
  84. kafka/coordinator/__init__.py +0 -0
  85. kafka/coordinator/assignors/__init__.py +0 -0
  86. kafka/coordinator/assignors/abstract.py +90 -0
  87. kafka/coordinator/assignors/cooperative_sticky.py +167 -0
  88. kafka/coordinator/assignors/range.py +81 -0
  89. kafka/coordinator/assignors/roundrobin.py +101 -0
  90. kafka/coordinator/assignors/sticky/StickyAssignorUserData.json +37 -0
  91. kafka/coordinator/assignors/sticky/__init__.py +0 -0
  92. kafka/coordinator/assignors/sticky/partition_movements.py +149 -0
  93. kafka/coordinator/assignors/sticky/sorted_set.py +63 -0
  94. kafka/coordinator/assignors/sticky/sticky_assignor.py +665 -0
  95. kafka/coordinator/assignors/sticky/user_data.py +8 -0
  96. kafka/coordinator/base.py +1215 -0
  97. kafka/coordinator/consumer.py +1224 -0
  98. kafka/coordinator/heartbeat.py +82 -0
  99. kafka/coordinator/subscription.py +34 -0
  100. kafka/errors.py +1004 -0
  101. kafka/future.py +166 -0
  102. kafka/metrics/__init__.py +13 -0
  103. kafka/metrics/compound_stat.py +33 -0
  104. kafka/metrics/dict_reporter.py +81 -0
  105. kafka/metrics/kafka_metric.py +36 -0
  106. kafka/metrics/measurable.py +27 -0
  107. kafka/metrics/measurable_stat.py +13 -0
  108. kafka/metrics/metric_config.py +33 -0
  109. kafka/metrics/metric_name.py +105 -0
  110. kafka/metrics/metrics.py +261 -0
  111. kafka/metrics/metrics_reporter.py +53 -0
  112. kafka/metrics/quota.py +41 -0
  113. kafka/metrics/stat.py +19 -0
  114. kafka/metrics/stats/__init__.py +15 -0
  115. kafka/metrics/stats/avg.py +24 -0
  116. kafka/metrics/stats/count.py +17 -0
  117. kafka/metrics/stats/histogram.py +99 -0
  118. kafka/metrics/stats/max_stat.py +17 -0
  119. kafka/metrics/stats/min_stat.py +19 -0
  120. kafka/metrics/stats/percentile.py +14 -0
  121. kafka/metrics/stats/percentiles.py +75 -0
  122. kafka/metrics/stats/rate.py +118 -0
  123. kafka/metrics/stats/sampled_stat.py +99 -0
  124. kafka/metrics/stats/sensor.py +136 -0
  125. kafka/metrics/stats/total.py +15 -0
  126. kafka/net/__init__.py +19 -0
  127. kafka/net/compat.py +165 -0
  128. kafka/net/connection.py +593 -0
  129. kafka/net/http_connect.py +144 -0
  130. kafka/net/inet.py +122 -0
  131. kafka/net/manager.py +451 -0
  132. kafka/net/metrics.py +149 -0
  133. kafka/net/sasl/__init__.py +32 -0
  134. kafka/net/sasl/abc.py +28 -0
  135. kafka/net/sasl/gssapi.py +95 -0
  136. kafka/net/sasl/msk.py +245 -0
  137. kafka/net/sasl/oauth.py +98 -0
  138. kafka/net/sasl/plain.py +42 -0
  139. kafka/net/sasl/scram.py +135 -0
  140. kafka/net/sasl/sspi.py +111 -0
  141. kafka/net/selector.py +644 -0
  142. kafka/net/socks5.py +262 -0
  143. kafka/net/transport.py +415 -0
  144. kafka/net/wakeup_notifier.py +72 -0
  145. kafka/partitioner/__init__.py +8 -0
  146. kafka/partitioner/abc.py +8 -0
  147. kafka/partitioner/default.py +89 -0
  148. kafka/partitioner/sticky.py +109 -0
  149. kafka/producer/__init__.py +5 -0
  150. kafka/producer/__main__.py +5 -0
  151. kafka/producer/future.py +101 -0
  152. kafka/producer/kafka.py +1123 -0
  153. kafka/producer/producer_batch.py +192 -0
  154. kafka/producer/record_accumulator.py +647 -0
  155. kafka/producer/sender.py +884 -0
  156. kafka/producer/transaction_manager.py +1326 -0
  157. kafka/protocol/__init__.py +0 -0
  158. kafka/protocol/admin/__init__.py +29 -0
  159. kafka/protocol/admin/acl.py +83 -0
  160. kafka/protocol/admin/acl.pyi +375 -0
  161. kafka/protocol/admin/client_quotas.py +14 -0
  162. kafka/protocol/admin/client_quotas.pyi +265 -0
  163. kafka/protocol/admin/cluster.py +31 -0
  164. kafka/protocol/admin/cluster.pyi +620 -0
  165. kafka/protocol/admin/configs.py +22 -0
  166. kafka/protocol/admin/configs.pyi +437 -0
  167. kafka/protocol/admin/groups.py +24 -0
  168. kafka/protocol/admin/groups.pyi +261 -0
  169. kafka/protocol/admin/topics.py +53 -0
  170. kafka/protocol/admin/topics.pyi +982 -0
  171. kafka/protocol/admin/transactions.py +18 -0
  172. kafka/protocol/admin/transactions.pyi +311 -0
  173. kafka/protocol/admin/users.py +14 -0
  174. kafka/protocol/admin/users.pyi +223 -0
  175. kafka/protocol/api_data.py +125 -0
  176. kafka/protocol/api_header.py +55 -0
  177. kafka/protocol/api_key.py +97 -0
  178. kafka/protocol/api_message.py +277 -0
  179. kafka/protocol/broker_version_data.py +246 -0
  180. kafka/protocol/consumer/__init__.py +13 -0
  181. kafka/protocol/consumer/fetch.py +16 -0
  182. kafka/protocol/consumer/fetch.pyi +298 -0
  183. kafka/protocol/consumer/group.py +38 -0
  184. kafka/protocol/consumer/group.pyi +824 -0
  185. kafka/protocol/consumer/metadata.py +30 -0
  186. kafka/protocol/consumer/metadata.pyi +89 -0
  187. kafka/protocol/consumer/offsets.py +75 -0
  188. kafka/protocol/consumer/offsets.pyi +288 -0
  189. kafka/protocol/data_container.py +166 -0
  190. kafka/protocol/frame.py +30 -0
  191. kafka/protocol/generate_stubs.py +468 -0
  192. kafka/protocol/metadata/__init__.py +10 -0
  193. kafka/protocol/metadata/api_versions.py +41 -0
  194. kafka/protocol/metadata/api_versions.pyi +128 -0
  195. kafka/protocol/metadata/find_coordinator.py +19 -0
  196. kafka/protocol/metadata/find_coordinator.pyi +105 -0
  197. kafka/protocol/metadata/metadata.py +34 -0
  198. kafka/protocol/metadata/metadata.pyi +160 -0
  199. kafka/protocol/old/__init__.py +0 -0
  200. kafka/protocol/old/abstract.py +17 -0
  201. kafka/protocol/old/add_offsets_to_txn.py +54 -0
  202. kafka/protocol/old/add_partitions_to_txn.py +71 -0
  203. kafka/protocol/old/admin.py +1086 -0
  204. kafka/protocol/old/api.py +205 -0
  205. kafka/protocol/old/api_versions.py +133 -0
  206. kafka/protocol/old/commit.py +355 -0
  207. kafka/protocol/old/consumer_protocol.py +36 -0
  208. kafka/protocol/old/end_txn.py +53 -0
  209. kafka/protocol/old/fetch.py +408 -0
  210. kafka/protocol/old/find_coordinator.py +72 -0
  211. kafka/protocol/old/group.py +451 -0
  212. kafka/protocol/old/init_producer_id.py +42 -0
  213. kafka/protocol/old/list_offsets.py +186 -0
  214. kafka/protocol/old/metadata.py +290 -0
  215. kafka/protocol/old/offset_for_leader_epoch.py +133 -0
  216. kafka/protocol/old/produce.py +247 -0
  217. kafka/protocol/old/sasl_authenticate.py +38 -0
  218. kafka/protocol/old/sasl_handshake.py +39 -0
  219. kafka/protocol/old/struct.py +87 -0
  220. kafka/protocol/old/txn_offset_commit.py +73 -0
  221. kafka/protocol/old/types.py +440 -0
  222. kafka/protocol/parser.py +191 -0
  223. kafka/protocol/producer/__init__.py +7 -0
  224. kafka/protocol/producer/produce.py +17 -0
  225. kafka/protocol/producer/produce.pyi +197 -0
  226. kafka/protocol/producer/transaction.py +30 -0
  227. kafka/protocol/producer/transaction.pyi +663 -0
  228. kafka/protocol/sasl.py +52 -0
  229. kafka/protocol/sasl.pyi +126 -0
  230. kafka/protocol/schemas/__init__.py +7 -0
  231. kafka/protocol/schemas/fields/__init__.py +7 -0
  232. kafka/protocol/schemas/fields/array.py +127 -0
  233. kafka/protocol/schemas/fields/base.py +156 -0
  234. kafka/protocol/schemas/fields/codecs/__init__.py +12 -0
  235. kafka/protocol/schemas/fields/codecs/encode_buffer.py +82 -0
  236. kafka/protocol/schemas/fields/codecs/tagged_fields.py +109 -0
  237. kafka/protocol/schemas/fields/codecs/types.py +505 -0
  238. kafka/protocol/schemas/fields/codegen.py +40 -0
  239. kafka/protocol/schemas/fields/simple.py +127 -0
  240. kafka/protocol/schemas/fields/struct.py +357 -0
  241. kafka/protocol/schemas/fields/struct_array.py +142 -0
  242. kafka/protocol/schemas/load_json.py +42 -0
  243. kafka/protocol/schemas/resources/AddOffsetsToTxnRequest.json +40 -0
  244. kafka/protocol/schemas/resources/AddOffsetsToTxnResponse.json +35 -0
  245. kafka/protocol/schemas/resources/AddPartitionsToTxnRequest.json +65 -0
  246. kafka/protocol/schemas/resources/AddPartitionsToTxnResponse.json +60 -0
  247. kafka/protocol/schemas/resources/AlterClientQuotasRequest.json +47 -0
  248. kafka/protocol/schemas/resources/AlterClientQuotasResponse.json +41 -0
  249. kafka/protocol/schemas/resources/AlterConfigsRequest.json +43 -0
  250. kafka/protocol/schemas/resources/AlterConfigsResponse.json +39 -0
  251. kafka/protocol/schemas/resources/AlterPartitionReassignmentsRequest.json +42 -0
  252. kafka/protocol/schemas/resources/AlterPartitionReassignmentsResponse.json +47 -0
  253. kafka/protocol/schemas/resources/AlterReplicaLogDirsRequest.json +41 -0
  254. kafka/protocol/schemas/resources/AlterReplicaLogDirsResponse.json +41 -0
  255. kafka/protocol/schemas/resources/AlterUserScramCredentialsRequest.json +45 -0
  256. kafka/protocol/schemas/resources/AlterUserScramCredentialsResponse.json +35 -0
  257. kafka/protocol/schemas/resources/ApiVersionsRequest.json +34 -0
  258. kafka/protocol/schemas/resources/ApiVersionsResponse.json +79 -0
  259. kafka/protocol/schemas/resources/ConsumerProtocolAssignment.json +42 -0
  260. kafka/protocol/schemas/resources/ConsumerProtocolSubscription.json +49 -0
  261. kafka/protocol/schemas/resources/CreateAclsRequest.json +46 -0
  262. kafka/protocol/schemas/resources/CreateAclsResponse.json +37 -0
  263. kafka/protocol/schemas/resources/CreatePartitionsRequest.json +47 -0
  264. kafka/protocol/schemas/resources/CreatePartitionsResponse.json +41 -0
  265. kafka/protocol/schemas/resources/CreateTopicsRequest.json +65 -0
  266. kafka/protocol/schemas/resources/CreateTopicsResponse.json +72 -0
  267. kafka/protocol/schemas/resources/DeleteAclsRequest.json +46 -0
  268. kafka/protocol/schemas/resources/DeleteAclsResponse.json +59 -0
  269. kafka/protocol/schemas/resources/DeleteGroupsRequest.json +30 -0
  270. kafka/protocol/schemas/resources/DeleteGroupsResponse.json +36 -0
  271. kafka/protocol/schemas/resources/DeleteRecordsRequest.json +42 -0
  272. kafka/protocol/schemas/resources/DeleteRecordsResponse.json +43 -0
  273. kafka/protocol/schemas/resources/DeleteTopicsRequest.json +43 -0
  274. kafka/protocol/schemas/resources/DeleteTopicsResponse.json +52 -0
  275. kafka/protocol/schemas/resources/DescribeAclsRequest.json +43 -0
  276. kafka/protocol/schemas/resources/DescribeAclsResponse.json +55 -0
  277. kafka/protocol/schemas/resources/DescribeClientQuotasRequest.json +37 -0
  278. kafka/protocol/schemas/resources/DescribeClientQuotasResponse.json +47 -0
  279. kafka/protocol/schemas/resources/DescribeClusterRequest.json +35 -0
  280. kafka/protocol/schemas/resources/DescribeClusterResponse.json +56 -0
  281. kafka/protocol/schemas/resources/DescribeConfigsRequest.json +42 -0
  282. kafka/protocol/schemas/resources/DescribeConfigsResponse.json +69 -0
  283. kafka/protocol/schemas/resources/DescribeGroupsRequest.json +38 -0
  284. kafka/protocol/schemas/resources/DescribeGroupsResponse.json +74 -0
  285. kafka/protocol/schemas/resources/DescribeLogDirsRequest.json +38 -0
  286. kafka/protocol/schemas/resources/DescribeLogDirsResponse.json +65 -0
  287. kafka/protocol/schemas/resources/DescribeProducersRequest.json +32 -0
  288. kafka/protocol/schemas/resources/DescribeProducersResponse.json +55 -0
  289. kafka/protocol/schemas/resources/DescribeQuorumRequest.json +39 -0
  290. kafka/protocol/schemas/resources/DescribeQuorumResponse.json +82 -0
  291. kafka/protocol/schemas/resources/DescribeTopicPartitionsRequest.json +40 -0
  292. kafka/protocol/schemas/resources/DescribeTopicPartitionsResponse.json +66 -0
  293. kafka/protocol/schemas/resources/DescribeTransactionsRequest.json +27 -0
  294. kafka/protocol/schemas/resources/DescribeTransactionsResponse.json +52 -0
  295. kafka/protocol/schemas/resources/DescribeUserScramCredentialsRequest.json +30 -0
  296. kafka/protocol/schemas/resources/DescribeUserScramCredentialsResponse.json +45 -0
  297. kafka/protocol/schemas/resources/ElectLeadersRequest.json +41 -0
  298. kafka/protocol/schemas/resources/ElectLeadersResponse.json +45 -0
  299. kafka/protocol/schemas/resources/EndTxnRequest.json +43 -0
  300. kafka/protocol/schemas/resources/EndTxnResponse.json +41 -0
  301. kafka/protocol/schemas/resources/FetchRequest.json +125 -0
  302. kafka/protocol/schemas/resources/FetchResponse.json +124 -0
  303. kafka/protocol/schemas/resources/FindCoordinatorRequest.json +43 -0
  304. kafka/protocol/schemas/resources/FindCoordinatorResponse.json +58 -0
  305. kafka/protocol/schemas/resources/HeartbeatRequest.json +39 -0
  306. kafka/protocol/schemas/resources/HeartbeatResponse.json +35 -0
  307. kafka/protocol/schemas/resources/IncrementalAlterConfigsRequest.json +44 -0
  308. kafka/protocol/schemas/resources/IncrementalAlterConfigsResponse.json +38 -0
  309. kafka/protocol/schemas/resources/InitProducerIdRequest.json +50 -0
  310. kafka/protocol/schemas/resources/InitProducerIdResponse.json +47 -0
  311. kafka/protocol/schemas/resources/JoinGroupRequest.json +63 -0
  312. kafka/protocol/schemas/resources/JoinGroupResponse.json +69 -0
  313. kafka/protocol/schemas/resources/LeaveGroupRequest.json +47 -0
  314. kafka/protocol/schemas/resources/LeaveGroupResponse.json +47 -0
  315. kafka/protocol/schemas/resources/ListConfigResourcesRequest.json +31 -0
  316. kafka/protocol/schemas/resources/ListConfigResourcesResponse.json +37 -0
  317. kafka/protocol/schemas/resources/ListGroupsRequest.json +36 -0
  318. kafka/protocol/schemas/resources/ListGroupsResponse.json +49 -0
  319. kafka/protocol/schemas/resources/ListOffsetsRequest.json +72 -0
  320. kafka/protocol/schemas/resources/ListOffsetsResponse.json +71 -0
  321. kafka/protocol/schemas/resources/ListPartitionReassignmentsRequest.json +34 -0
  322. kafka/protocol/schemas/resources/ListPartitionReassignmentsResponse.json +46 -0
  323. kafka/protocol/schemas/resources/ListTransactionsRequest.json +40 -0
  324. kafka/protocol/schemas/resources/ListTransactionsResponse.json +42 -0
  325. kafka/protocol/schemas/resources/MetadataRequest.json +56 -0
  326. kafka/protocol/schemas/resources/MetadataResponse.json +101 -0
  327. kafka/protocol/schemas/resources/OffsetCommitRequest.json +76 -0
  328. kafka/protocol/schemas/resources/OffsetCommitResponse.json +71 -0
  329. kafka/protocol/schemas/resources/OffsetDeleteRequest.json +39 -0
  330. kafka/protocol/schemas/resources/OffsetDeleteResponse.json +42 -0
  331. kafka/protocol/schemas/resources/OffsetFetchRequest.json +76 -0
  332. kafka/protocol/schemas/resources/OffsetFetchResponse.json +107 -0
  333. kafka/protocol/schemas/resources/OffsetForLeaderEpochRequest.json +52 -0
  334. kafka/protocol/schemas/resources/OffsetForLeaderEpochResponse.json +51 -0
  335. kafka/protocol/schemas/resources/ProduceRequest.json +73 -0
  336. kafka/protocol/schemas/resources/ProduceResponse.json +96 -0
  337. kafka/protocol/schemas/resources/RequestHeader.json +44 -0
  338. kafka/protocol/schemas/resources/ResponseHeader.json +26 -0
  339. kafka/protocol/schemas/resources/SaslAuthenticateRequest.json +29 -0
  340. kafka/protocol/schemas/resources/SaslAuthenticateResponse.json +34 -0
  341. kafka/protocol/schemas/resources/SaslHandshakeRequest.json +31 -0
  342. kafka/protocol/schemas/resources/SaslHandshakeResponse.json +32 -0
  343. kafka/protocol/schemas/resources/SyncGroupRequest.json +56 -0
  344. kafka/protocol/schemas/resources/SyncGroupResponse.json +46 -0
  345. kafka/protocol/schemas/resources/TxnOffsetCommitRequest.json +68 -0
  346. kafka/protocol/schemas/resources/TxnOffsetCommitResponse.json +47 -0
  347. kafka/protocol/schemas/resources/UpdateFeaturesRequest.json +43 -0
  348. kafka/protocol/schemas/resources/UpdateFeaturesResponse.json +39 -0
  349. kafka/protocol/schemas/resources/WriteTxnMarkersRequest.json +49 -0
  350. kafka/protocol/schemas/resources/WriteTxnMarkersResponse.json +45 -0
  351. kafka/protocol/schemas/resources/__init__.py +0 -0
  352. kafka/record/__init__.py +3 -0
  353. kafka/record/_crc32c.py +161 -0
  354. kafka/record/abc.py +144 -0
  355. kafka/record/default_records.py +782 -0
  356. kafka/record/legacy_records.py +587 -0
  357. kafka/record/memory_records.py +255 -0
  358. kafka/record/util.py +135 -0
  359. kafka/serializer/__init__.py +4 -0
  360. kafka/serializer/abstract.py +20 -0
  361. kafka/serializer/default.py +16 -0
  362. kafka/serializer/json.py +17 -0
  363. kafka/serializer/wrapper.py +21 -0
  364. kafka/structs.py +69 -0
  365. kafka/util.py +159 -0
  366. kafka/vendor/__init__.py +0 -0
  367. kafka/version.py +1 -0
  368. kafka_python-3.0.0.dist-info/METADATA +319 -0
  369. kafka_python-3.0.0.dist-info/RECORD +373 -0
  370. kafka_python-3.0.0.dist-info/WHEEL +5 -0
  371. kafka_python-3.0.0.dist-info/entry_points.txt +2 -0
  372. kafka_python-3.0.0.dist-info/licenses/LICENSE +202 -0
  373. kafka_python-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,139 @@
1
+ import re
2
+ from collections import defaultdict
3
+ from datetime import datetime, timedelta, timezone
4
+
5
+ from kafka.admin import OffsetSpec, OffsetTimestamp
6
+ from kafka.structs import TopicPartition
7
+
8
+
9
+ class ResetGroupOffsets:
10
+ COMMAND = 'reset-offsets'
11
+ HELP = 'Reset committed offsets for a consumer group'
12
+
13
+ @classmethod
14
+ def add_arguments(cls, parser):
15
+ parser.add_argument('-g', '--group-id', type=str, required=True)
16
+ parser.add_argument(
17
+ '-p', '--partition', type=str, action='append',
18
+ dest='partitions', default=[],
19
+ help='TOPIC:PARTITION pair (repeatable). Scopes the reset to these '
20
+ 'partitions. If omitted, the reset applies to every partition '
21
+ 'currently committed by the group.')
22
+ mode = parser.add_mutually_exclusive_group(required=True)
23
+ mode.add_argument(
24
+ '-s', '--spec', type=str,
25
+ help='Spec may be one of earliest, latest, max-timestamp, '
26
+ 'earliest-local, latest-tiered, or a millisecond timestamp.')
27
+ mode.add_argument(
28
+ '--to-offset', type=int, dest='to_offset',
29
+ help='Reset all in-scope partitions to this explicit offset '
30
+ '(clamped to [earliest, latest]).')
31
+ mode.add_argument(
32
+ '--shift-by', type=int, dest='shift_by',
33
+ help='Shift each in-scope committed offset by N positions (may be '
34
+ 'negative); clamped to [earliest, latest]. Requires a current '
35
+ 'commit for each in-scope partition.')
36
+ mode.add_argument(
37
+ '--by-duration', type=str, dest='by_duration',
38
+ help='Reset to the offset at (now - DURATION). DURATION is ISO-8601, '
39
+ 'e.g. P7D, PT1H, PT30M.')
40
+ mode.add_argument(
41
+ '--to-datetime', type=str, dest='to_datetime',
42
+ help='Reset to the offset at the given ISO-8601 datetime (UTC assumed '
43
+ 'if no tz offset is provided).')
44
+ mode.add_argument(
45
+ '--to-current', action='store_true', dest='to_current',
46
+ help='Re-commit the current committed offsets, clamped to '
47
+ '[earliest, latest]. Useful to heal out-of-range offsets. '
48
+ 'Requires a current commit for each in-scope partition.')
49
+
50
+ @classmethod
51
+ def command(cls, client, args):
52
+ group = client.describe_groups([args.group_id])
53
+ state = group[args.group_id]['group_state']
54
+ if state not in ('Empty', 'Dead'):
55
+ raise RuntimeError(f'Group {args.group_id} is {state}, expecting Empty or Dead!')
56
+
57
+ targets = cls._build_targets(client, args)
58
+ result = client.reset_group_offsets(args.group_id, targets)
59
+ output = defaultdict(dict)
60
+ for tp, res in result.items():
61
+ res['error'] = res['error'].__name__
62
+ output[tp.topic][tp.partition] = res
63
+ return dict(output)
64
+
65
+ @classmethod
66
+ def _build_targets(cls, client, args):
67
+ explicit_scope = [cls._parse_tp(p) for p in args.partitions] if args.partitions else None
68
+
69
+ if args.spec is not None:
70
+ scope = explicit_scope if explicit_scope is not None else list(client.list_group_offsets(args.group_id))
71
+ spec = cls._parse_spec(args.spec)
72
+ return {tp: spec for tp in scope}
73
+
74
+ if args.to_offset is not None:
75
+ scope = explicit_scope if explicit_scope is not None else list(client.list_group_offsets(args.group_id))
76
+ return {tp: args.to_offset for tp in scope}
77
+
78
+ if args.by_duration:
79
+ delta = cls._parse_duration(args.by_duration)
80
+ ts = OffsetTimestamp(int((datetime.now(timezone.utc) - delta).timestamp() * 1000))
81
+ scope = explicit_scope if explicit_scope is not None else list(client.list_group_offsets(args.group_id))
82
+ return {tp: ts for tp in scope}
83
+
84
+ if args.to_datetime:
85
+ dt = cls._parse_datetime(args.to_datetime)
86
+ ts = OffsetTimestamp(int(dt.timestamp() * 1000))
87
+ scope = explicit_scope if explicit_scope is not None else list(client.list_group_offsets(args.group_id))
88
+ return {tp: ts for tp in scope}
89
+
90
+ current = client.list_group_offsets(args.group_id)
91
+ scope = explicit_scope if explicit_scope is not None else list(current)
92
+ missing = [tp for tp in scope if tp not in current]
93
+ if missing:
94
+ raise ValueError(
95
+ f'No committed offset for {missing}; --shift-by / --to-current '
96
+ 'require a current commit per in-scope partition')
97
+
98
+ if args.shift_by is not None:
99
+ return {tp: current[tp].offset + args.shift_by for tp in scope}
100
+ if args.to_current:
101
+ return {tp: current[tp].offset for tp in scope}
102
+ raise ValueError('No reset mode selected')
103
+
104
+ @classmethod
105
+ def _parse_tp(cls, entry):
106
+ topic, partition = entry.rsplit(':', 1)
107
+ return TopicPartition(topic, int(partition))
108
+
109
+ @classmethod
110
+ def _parse_spec(cls, spec_str):
111
+ try:
112
+ return OffsetSpec.build_from(spec_str)
113
+ except ValueError:
114
+ return OffsetTimestamp(int(spec_str))
115
+
116
+ @classmethod
117
+ def _parse_duration(cls, duration):
118
+ m = re.match(
119
+ r'^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$',
120
+ duration)
121
+ if not m or not any(m.groups()):
122
+ raise ValueError(f'Invalid ISO-8601 duration: {duration}')
123
+ days, hours, mins, secs = m.groups()
124
+ return timedelta(
125
+ days=int(days) if days else 0,
126
+ hours=int(hours) if hours else 0,
127
+ minutes=int(mins) if mins else 0,
128
+ seconds=float(secs) if secs else 0,
129
+ )
130
+
131
+ @classmethod
132
+ def _parse_datetime(cls, dt_str):
133
+ try:
134
+ dt = datetime.fromisoformat(dt_str)
135
+ except ValueError:
136
+ raise ValueError(f'Invalid ISO-8601 datetime: {dt_str}')
137
+ if dt.tzinfo is None:
138
+ dt = dt.replace(tzinfo=timezone.utc)
139
+ return dt
@@ -0,0 +1,21 @@
1
+ from .alter_reassignments import AlterPartitionReassignments
2
+ from .create import CreatePartitions
3
+ from .delete_records import DeleteRecords
4
+ from .describe import DescribeTopicPartitions
5
+ from .elect_leaders import ElectLeaders
6
+ from .list_offsets import ListPartitionOffsets
7
+ from .list_reassignments import ListPartitionReassignments
8
+
9
+
10
+ class PartitionsCommandGroup:
11
+ GROUP = 'partitions'
12
+ HELP = 'Manage Kafka Partitions'
13
+ COMMANDS = [
14
+ CreatePartitions,
15
+ DescribeTopicPartitions,
16
+ ListPartitionOffsets,
17
+ ListPartitionReassignments,
18
+ AlterPartitionReassignments,
19
+ DeleteRecords,
20
+ ElectLeaders,
21
+ ]
@@ -0,0 +1,37 @@
1
+ from kafka.structs import TopicPartition
2
+
3
+
4
+ class AlterPartitionReassignments:
5
+ COMMAND = 'alter-reassignments'
6
+ HELP = 'Alter replica assignments for partitions'
7
+
8
+ @classmethod
9
+ def add_arguments(cls, parser):
10
+ parser.add_argument(
11
+ '-r', '--reassign', type=str, action='append',
12
+ dest='reassignments', default=[], required=True,
13
+ help='TOPIC:PARTITION=BROKER_ID[,BROKER_ID...] to set a new '
14
+ 'replica set, or TOPIC:PARTITION=cancel to cancel an '
15
+ 'in-progress reassignment for that partition. Repeatable.')
16
+ parser.add_argument(
17
+ '--timeout-ms', type=int, default=None,
18
+ help='Request timeout in milliseconds')
19
+
20
+ @classmethod
21
+ def command(cls, client, args):
22
+ reassignments = {}
23
+ for spec in args.reassignments:
24
+ tp_str, replicas_str = spec.rsplit('=', 1)
25
+ topic, partition = tp_str.rsplit(':', 1)
26
+ tp = TopicPartition(topic, int(partition))
27
+ if replicas_str.lower() == 'cancel':
28
+ reassignments[tp] = None
29
+ else:
30
+ reassignments[tp] = [int(b) for b in replicas_str.split(',') if b]
31
+ results = client.alter_partition_reassignments(
32
+ reassignments,
33
+ timeout_ms=args.timeout_ms)
34
+ return {
35
+ '%s:%d' % (tp.topic, tp.partition): (err.__name__ if err else None)
36
+ for tp, err in results.items()
37
+ }
@@ -0,0 +1,27 @@
1
+ class CreatePartitions:
2
+ COMMAND = 'create'
3
+ HELP = 'Create additional partitions for existing topics'
4
+
5
+ @classmethod
6
+ def add_arguments(cls, parser):
7
+ parser.add_argument(
8
+ '-p', '--topic-partitions', type=str, action='append',
9
+ dest='topic_partitions', default=[], required=True,
10
+ help='TOPIC:TOTAL_PARTITION_COUNT pair (repeatable)')
11
+ parser.add_argument(
12
+ '--timeout-ms', type=int, default=None,
13
+ help='Request timeout in milliseconds')
14
+ parser.add_argument(
15
+ '--validate-only', action='store_true',
16
+ help='Validate the request without actually creating partitions')
17
+
18
+ @classmethod
19
+ def command(cls, client, args):
20
+ topic_partitions = {}
21
+ for spec in args.topic_partitions:
22
+ topic, count = spec.rsplit(':', 1)
23
+ topic_partitions[topic] = int(count)
24
+ return client.create_partitions(
25
+ topic_partitions,
26
+ timeout_ms=args.timeout_ms,
27
+ validate_only=args.validate_only)
@@ -0,0 +1,31 @@
1
+ from kafka.structs import TopicPartition
2
+
3
+
4
+ class DeleteRecords:
5
+ COMMAND = 'delete-records'
6
+ HELP = 'Delete records from partitions up to a given offset'
7
+
8
+ @classmethod
9
+ def add_arguments(cls, parser):
10
+ parser.add_argument(
11
+ '-r', '--record', type=str, action='append',
12
+ dest='records', default=[], required=True,
13
+ help='TOPIC:PARTITION:OFFSET triple (repeatable). '
14
+ 'Use -1 as OFFSET to delete up to the current high-water mark.')
15
+ parser.add_argument(
16
+ '--timeout-ms', type=int, default=None,
17
+ help='Request timeout in milliseconds')
18
+ parser.add_argument(
19
+ '--partition-leader-id', type=int, default=None,
20
+ help='Send all delete requests to this broker id, skipping metadata lookup')
21
+
22
+ @classmethod
23
+ def command(cls, client, args):
24
+ records_to_delete = {}
25
+ for spec in args.records:
26
+ topic, partition, offset = spec.rsplit(':', 2)
27
+ records_to_delete[TopicPartition(topic, int(partition))] = int(offset)
28
+ return client.delete_records(
29
+ records_to_delete,
30
+ timeout_ms=args.timeout_ms,
31
+ partition_leader_id=args.partition_leader_id)
@@ -0,0 +1,36 @@
1
+ class DescribeTopicPartitions:
2
+ COMMAND = 'describe'
3
+ HELP = 'Describe topic partitions with pagination (KIP-966, broker >=3.9)'
4
+
5
+ @classmethod
6
+ def add_arguments(cls, parser):
7
+ parser.add_argument(
8
+ '-t', '--topic', type=str, action='append',
9
+ dest='topics', default=[], required=True,
10
+ help='Topic to describe (repeatable)')
11
+ parser.add_argument(
12
+ '--response-partition-limit', type=int, default=2000,
13
+ help='Maximum number of partitions to include in the response '
14
+ '(default: 2000)')
15
+ parser.add_argument(
16
+ '--cursor-topic', type=str, default=None,
17
+ help='Topic name to start pagination from')
18
+ parser.add_argument(
19
+ '--cursor-partition', type=int, default=None,
20
+ help='Partition index to start pagination from')
21
+
22
+ @classmethod
23
+ def command(cls, client, args):
24
+ cursor = None
25
+ if args.cursor_topic is not None or args.cursor_partition is not None:
26
+ if args.cursor_topic is None or args.cursor_partition is None:
27
+ raise ValueError(
28
+ '--cursor-topic and --cursor-partition must be used together')
29
+ cursor = {
30
+ 'topic_name': args.cursor_topic,
31
+ 'partition_index': args.cursor_partition,
32
+ }
33
+ return client.describe_topic_partitions(
34
+ args.topics,
35
+ response_partition_limit=args.response_partition_limit,
36
+ cursor=cursor)
@@ -0,0 +1,53 @@
1
+ from collections import defaultdict
2
+
3
+
4
+ _ELECTION_TYPES = {'preferred': 0, 'unclean': 1}
5
+
6
+
7
+ class ElectLeaders:
8
+ COMMAND = 'elect-leaders'
9
+ HELP = 'Trigger leader election for partitions'
10
+
11
+ @classmethod
12
+ def add_arguments(cls, parser):
13
+ parser.add_argument(
14
+ '--election-type', type=str, default='preferred',
15
+ choices=sorted(_ELECTION_TYPES),
16
+ help='Election type (default: preferred)')
17
+ parser.add_argument(
18
+ '-p', '--topic-partition', type=str, action='append',
19
+ dest='topic_partitions', default=[],
20
+ help='TOPIC:PARTITION pair (repeatable). Omit to elect leaders for '
21
+ 'all partitions of all topics.')
22
+ parser.add_argument(
23
+ '-t', '--topic', type=str, action='append',
24
+ dest='topics', default=[],
25
+ help='Elect leaders for all partitions of TOPIC (repeatable). '
26
+ 'Mutually exclusive with --topic-partition.')
27
+ parser.add_argument(
28
+ '--timeout-ms', type=int, default=None,
29
+ help='Request timeout in milliseconds')
30
+ parser.add_argument(
31
+ '--no-raise-errors', dest='raise_errors', action='store_false',
32
+ help='Do not raise on partition-level errors; return the response instead')
33
+
34
+ @classmethod
35
+ def command(cls, client, args):
36
+ if args.topic_partitions and args.topics:
37
+ raise ValueError(
38
+ '--topic-partition and --topic are mutually exclusive')
39
+ if args.topic_partitions:
40
+ topic2partitions = defaultdict(list)
41
+ for spec in args.topic_partitions:
42
+ topic, partition = spec.rsplit(':', 1)
43
+ topic2partitions[topic].append(int(partition))
44
+ topic_partitions = dict(topic2partitions)
45
+ elif args.topics:
46
+ topic_partitions = args.topics
47
+ else:
48
+ topic_partitions = None
49
+ return client.elect_leaders(
50
+ _ELECTION_TYPES[args.election_type],
51
+ topic_partitions=topic_partitions,
52
+ timeout_ms=args.timeout_ms,
53
+ raise_errors=args.raise_errors)
@@ -0,0 +1,88 @@
1
+ from collections import defaultdict
2
+
3
+ from kafka.protocol.consumer import OffsetSpec
4
+ from kafka.structs import TopicPartition
5
+
6
+
7
+ class ListPartitionOffsets:
8
+ COMMAND = 'list-offsets'
9
+ HELP = 'List offsets for partitions by spec (earliest/latest/timestamp)'
10
+
11
+ @classmethod
12
+ def add_arguments(cls, parser):
13
+ parser.add_argument(
14
+ '-t', '--topic', type=str)
15
+ parser.add_argument(
16
+ '-s', '--spec', type=str,
17
+ help='Spec may be one of earliest, latest, max-timestamp, earliest-local, '
18
+ 'latest-tiered, or a millisecond timestamp.')
19
+ parser.add_argument(
20
+ '-p', '--partition', type=str, action='append',
21
+ dest='partitions', default=[],
22
+ help='TOPIC:PARTITION:SPEC triple (repeatable). PARTITION may be a '
23
+ 'single partition, a closed range (0-2), an open range (1-), or '
24
+ 'a single wildcard "*" for all partitions. SPEC may be one of '
25
+ 'earliest, latest, max-timestamp, earliest-local, latest-tiered, '
26
+ 'or a millisecond timestamp.')
27
+
28
+ @classmethod
29
+ def command(cls, client, args):
30
+ tp_offsets = cls._parse_partition_specs(client, args)
31
+ output = defaultdict(dict)
32
+ result = client.list_partition_offsets(tp_offsets)
33
+ for tp, info in result.items():
34
+ output[tp.topic][tp.partition] = {
35
+ 'offset': info.offset,
36
+ 'timestamp': info.timestamp,
37
+ 'leader_epoch': info.leader_epoch,
38
+ 'spec': tp_offsets[tp]
39
+ }
40
+ return dict(output)
41
+
42
+ @classmethod
43
+ def _parse_partition_specs(cls, client, args):
44
+ if args.partitions:
45
+ if args.topic or args.spec:
46
+ raise ValueError("Either --partition or (--topic and --spec) is supported, but not both.")
47
+ partitions = args.partitions
48
+ else:
49
+ if not args.topic or not args.spec:
50
+ raise ValueError("Both --topic and --spec must be provided.")
51
+ partitions = [f'{args.topic}:*:{args.spec}']
52
+ tp_offsets = {}
53
+ for entry in partitions:
54
+ topic, partition, spec_str = entry.rsplit(':', 2)
55
+ spec = OffsetSpec.build_from(spec_str)
56
+ for tp in cls._parse_tp(client, topic, partition):
57
+ if tp in tp_offsets:
58
+ # Passing multiple specs for a single partition results in an InvalidRequestError
59
+ raise ValueError('Only one spec allowed per partition')
60
+ tp_offsets[tp] = spec
61
+ return tp_offsets
62
+
63
+ @classmethod
64
+ def _parse_tp(cls, client, topic, partition, cache={}):
65
+ try:
66
+ return [TopicPartition(topic, int(partition))]
67
+ except ValueError:
68
+ pass
69
+ if not partition == '*' and '-' not in partition:
70
+ raise ValueError(f'Unrecognized partition: {partition}')
71
+
72
+ if topic not in cache:
73
+ cache[topic] = sorted([p['partition_index'] for p in client.describe_topics([topic])[0]['partitions']])
74
+
75
+ if partition == '*':
76
+ return [TopicPartition(topic, p)
77
+ for p in cache[topic]]
78
+
79
+ elif '-' in partition:
80
+ start, end = partition.split('-')
81
+ if not start and not end:
82
+ raise ValueError(f'Unrecognized partition: {partition}')
83
+ if not start:
84
+ start = cache[topic][0]
85
+ if not end:
86
+ end = cache[topic][-1]
87
+ return [TopicPartition(topic, p)
88
+ for p in range(int(start), int(end) + 1)]
@@ -0,0 +1,35 @@
1
+ from collections import defaultdict
2
+
3
+ from kafka.structs import TopicPartition
4
+
5
+
6
+ class ListPartitionReassignments:
7
+ COMMAND = 'list-reassignments'
8
+ HELP = 'List the current ongoing partition reassignments'
9
+
10
+ @classmethod
11
+ def add_arguments(cls, parser):
12
+ parser.add_argument(
13
+ '-p', '--topic-partition', type=str, action='append',
14
+ dest='topic_partitions', default=[],
15
+ help='TOPIC:PARTITION pair (repeatable). Omit to list '
16
+ 'reassignments for all partitions.')
17
+ parser.add_argument(
18
+ '--timeout-ms', type=int, default=None,
19
+ help='Request timeout in milliseconds')
20
+
21
+ @classmethod
22
+ def command(cls, client, args):
23
+ if args.topic_partitions:
24
+ topic2partitions = defaultdict(list)
25
+ for spec in args.topic_partitions:
26
+ topic, partition = spec.rsplit(':', 1)
27
+ topic2partitions[topic].append(int(partition))
28
+ topic_partitions = dict(topic2partitions)
29
+ else:
30
+ topic_partitions = None
31
+ result = client.list_partition_reassignments(
32
+ topic_partitions, timeout_ms=args.timeout_ms)
33
+ # TopicPartition keys don't render cleanly in --format json; normalize
34
+ # to "topic:partition" strings for CLI output.
35
+ return {'%s:%d' % (tp.topic, tp.partition): v for tp, v in result.items()}
@@ -0,0 +1,10 @@
1
+ from .create import CreateTopic
2
+ from .delete import DeleteTopic
3
+ from .describe import DescribeTopics
4
+ from .list import ListTopics
5
+
6
+
7
+ class TopicsCommandGroup:
8
+ GROUP = 'topics'
9
+ HELP = 'Manage Kafka Topics'
10
+ COMMANDS = [ListTopics, DescribeTopics, CreateTopic, DeleteTopic]
@@ -0,0 +1,13 @@
1
+ class CreateTopic:
2
+ COMMAND = 'create'
3
+ HELP = 'Create a Kafka Topic'
4
+
5
+ @classmethod
6
+ def add_arguments(cls, parser):
7
+ parser.add_argument('-t', '--topic', type=str, required=True)
8
+ parser.add_argument('--num-partitions', type=int, default=-1)
9
+ parser.add_argument('--replication-factor', type=int, default=-1)
10
+
11
+ @classmethod
12
+ def command(cls, client, args):
13
+ return client.create_topics({args.topic: {'num_partitions': args.num_partitions, 'replication_factor': args.replication_factor}})
@@ -0,0 +1,19 @@
1
+ import uuid
2
+
3
+
4
+ class DeleteTopic:
5
+ COMMAND = 'delete'
6
+ HELP = 'Delete Kafka Topic'
7
+
8
+ @classmethod
9
+ def add_arguments(cls, parser):
10
+ parser.add_argument('-t', '--topic', type=str, action='append', dest='topics', default=[], help='topic name')
11
+ parser.add_argument('--id', type=str, action='append', dest='topic_ids', default=[], help='topic UUID')
12
+
13
+ @classmethod
14
+ def command(cls, client, args):
15
+ if not args.topics and not args.topic_ids:
16
+ raise ValueError('At least one topic or topic_id is required!')
17
+ topic_ids = [uuid.UUID(topic_id) for topic_id in args.topic_ids]
18
+ topic_names = args.topics
19
+ return client.delete_topics(topic_names + topic_ids)
@@ -0,0 +1,18 @@
1
+ import uuid
2
+
3
+
4
+ class DescribeTopics:
5
+ COMMAND = 'describe'
6
+ HELP = 'Describe Kafka Topics'
7
+
8
+ @classmethod
9
+ def add_arguments(cls, parser):
10
+ parser.add_argument('-t', '--topic', type=str, action='append', dest='topics', default=[], help='topic name')
11
+ parser.add_argument('--id', type=str, action='append', dest='topic_ids', default=[],
12
+ help='topic UUID (requires broker >= 2.8, KIP-516)')
13
+
14
+ @classmethod
15
+ def command(cls, client, args):
16
+ topic_ids = [uuid.UUID(topic_id) for topic_id in args.topic_ids]
17
+ topics = args.topics + topic_ids
18
+ return client.describe_topics(topics or None)
@@ -0,0 +1,11 @@
1
+ class ListTopics:
2
+ COMMAND = 'list'
3
+ HELP = 'List Kafka Topics'
4
+
5
+ @classmethod
6
+ def add_arguments(cls, parser):
7
+ pass
8
+
9
+ @classmethod
10
+ def command(cls, client, args):
11
+ return client.list_topics()
@@ -0,0 +1,17 @@
1
+ from .abort import AbortTransaction
2
+ from .describe import DescribeTransactions
3
+ from .describe_producers import DescribeProducers
4
+ from .find_hanging import FindHangingTransactions
5
+ from .list import ListTransactions
6
+
7
+
8
+ class TransactionsCommandGroup:
9
+ GROUP = 'transactions'
10
+ HELP = 'Inspect and recover from hanging transactions (KIP-664)'
11
+ COMMANDS = [
12
+ ListTransactions,
13
+ DescribeTransactions,
14
+ DescribeProducers,
15
+ FindHangingTransactions,
16
+ AbortTransaction,
17
+ ]
@@ -0,0 +1,38 @@
1
+ from kafka.admin import AbortTransactionSpec
2
+ from kafka.structs import TopicPartition
3
+
4
+
5
+ class AbortTransaction:
6
+ COMMAND = 'abort'
7
+ HELP = 'Administratively abort an open transaction on a partition'
8
+
9
+ @classmethod
10
+ def add_arguments(cls, parser):
11
+ parser.add_argument(
12
+ '-t', '--topic', type=str, required=True,
13
+ help='Topic name.')
14
+ parser.add_argument(
15
+ '-p', '--partition', type=int, required=True,
16
+ help='Partition index.')
17
+ parser.add_argument(
18
+ '--producer-id', type=int, required=True,
19
+ help='Producer id of the hanging transaction.')
20
+ parser.add_argument(
21
+ '--producer-epoch', type=int, required=True,
22
+ help='Producer epoch of the hanging transaction.')
23
+ parser.add_argument(
24
+ '--coordinator-epoch', type=int, default=-1,
25
+ help='Coordinator epoch (default: -1, the admin sentinel).')
26
+
27
+ @classmethod
28
+ def command(cls, client, args):
29
+ spec = AbortTransactionSpec(
30
+ topic_partition=TopicPartition(args.topic, args.partition),
31
+ producer_id=args.producer_id,
32
+ producer_epoch=args.producer_epoch,
33
+ coordinator_epoch=args.coordinator_epoch,
34
+ )
35
+ client.abort_transaction(spec)
36
+ return {'aborted': {'topic': args.topic, 'partition': args.partition,
37
+ 'producer_id': args.producer_id,
38
+ 'producer_epoch': args.producer_epoch}}
@@ -0,0 +1,24 @@
1
+ class DescribeTransactions:
2
+ COMMAND = 'describe'
3
+ HELP = 'Describe one or more transactional ids (broker >= 3.0)'
4
+
5
+ @classmethod
6
+ def add_arguments(cls, parser):
7
+ parser.add_argument(
8
+ '--transactional-id', type=str, action='append',
9
+ dest='transactional_ids', required=True,
10
+ help='Transactional id to describe (repeatable).')
11
+
12
+ @classmethod
13
+ def command(cls, client, args):
14
+ results = client.describe_transactions(args.transactional_ids)
15
+ output = {}
16
+ for txn_id, desc in results.items():
17
+ out = desc._asdict()
18
+ out['state'] = desc.state.value
19
+ out['topic_partitions'] = sorted(
20
+ [{'topic': tp.topic, 'partition': tp.partition}
21
+ for tp in desc.topic_partitions],
22
+ key=lambda p: (p['topic'], p['partition']))
23
+ output[txn_id] = out
24
+ return output
@@ -0,0 +1,29 @@
1
+ from kafka.structs import TopicPartition
2
+
3
+
4
+ class DescribeProducers:
5
+ COMMAND = 'describe-producers'
6
+ HELP = 'Describe active producers on a partition (broker >= 2.8)'
7
+
8
+ @classmethod
9
+ def add_arguments(cls, parser):
10
+ parser.add_argument(
11
+ '-t', '--topic', type=str, required=True,
12
+ help='Topic name.')
13
+ parser.add_argument(
14
+ '-p', '--partition', type=int, action='append',
15
+ dest='partitions', required=True,
16
+ help='Partition index (repeatable).')
17
+ parser.add_argument(
18
+ '--broker-id', type=int, default=None,
19
+ help='Send to this replica instead of the partition leader.')
20
+
21
+ @classmethod
22
+ def command(cls, client, args):
23
+ tps = [TopicPartition(args.topic, p) for p in args.partitions]
24
+ results = client.describe_producers(tps, broker_id=args.broker_id)
25
+ return {
26
+ f'{tp.topic}:{tp.partition}': {
27
+ 'active_producers': [p._asdict() for p in state.active_producers],
28
+ } for tp, state in results.items()
29
+ }