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,595 @@
1
+ """Partition management mixin for KafkaAdminClient.
2
+
3
+ Also defines NewPartitions data class.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import logging
9
+ from collections import defaultdict
10
+ from typing import TYPE_CHECKING
11
+
12
+ import kafka.errors as Errors
13
+ from kafka.errors import UnknownTopicOrPartitionError
14
+ from kafka.protocol.admin import (
15
+ AlterPartitionReassignmentsRequest,
16
+ CreatePartitionsRequest,
17
+ DeleteRecordsRequest,
18
+ DescribeTopicPartitionsRequest,
19
+ ElectLeadersRequest,
20
+ ElectionType,
21
+ ListPartitionReassignmentsRequest,
22
+ )
23
+ from kafka.protocol.consumer import (
24
+ ListOffsetsRequest, IsolationLevel, OffsetSpec, OffsetTimestamp,
25
+ )
26
+ from kafka.structs import TopicPartition, OffsetAndTimestamp
27
+ from kafka.util import Timer
28
+
29
+
30
+ if TYPE_CHECKING:
31
+ from kafka.net.manager import KafkaConnectionManager
32
+
33
+ log = logging.getLogger(__name__)
34
+
35
+
36
+ class PartitionAdminMixin:
37
+ """Mixin providing partition and record management methods."""
38
+ _manager: KafkaConnectionManager
39
+ config: dict
40
+
41
+ @staticmethod
42
+ def _process_create_partitions_input(topic_partitions):
43
+ _Topic = CreatePartitionsRequest.CreatePartitionsTopic
44
+ _Assignment = CreatePartitionsRequest.CreatePartitionsTopic.CreatePartitionsAssignment
45
+ topics = []
46
+ for topic, count in topic_partitions.items():
47
+ if isinstance(count, int):
48
+ topics.append(_Topic(name=topic, count=count))
49
+ elif isinstance(count, dict):
50
+ topics.append(
51
+ _Topic(
52
+ name=topic,
53
+ count=count['count'],
54
+ assignments=[_Assignment(broker_ids=broker_ids)
55
+ for broker_ids in count['assignments']]))
56
+ else:
57
+ topics.append(
58
+ _Topic(
59
+ name=topic,
60
+ count=count.total_count,
61
+ assignments=[_Assignment(broker_ids=broker_ids)
62
+ for broker_ids in count.new_assignments]))
63
+ return topics
64
+
65
+ def create_partitions(self, topic_partitions, timeout_ms=None, validate_only=False, raise_errors=True):
66
+ """Create additional partitions for an existing topic.
67
+
68
+ Arguments:
69
+ topic_partitions: A dict of topic name strings to total partition count (int),
70
+ or a dict of {topic_name: {count: int, assignments: [[broker_ids]]}}
71
+ if manual assignment is desired.
72
+ dict of {topic_name: NewPartitions} is deprecated.
73
+
74
+ Keyword Arguments:
75
+ timeout_ms (numeric, optional): Milliseconds to wait for new partitions to be
76
+ created before the broker returns.
77
+ validate_only (bool, optional): If True, don't actually create new partitions.
78
+ Default: False
79
+ raise_errors (bool, optional): Whether to raise errors as exceptions. Default True.
80
+
81
+ Returns:
82
+ Appropriate version of CreatePartitionsResponse class.
83
+ """
84
+ timeout_ms = self._validate_timeout(timeout_ms)
85
+ request = CreatePartitionsRequest(
86
+ topics=self._process_create_partitions_input(topic_partitions),
87
+ timeout_ms=timeout_ms,
88
+ validate_only=validate_only)
89
+
90
+ def response_errors(r):
91
+ for result in r.results:
92
+ yield Errors.for_code(result.error_code)
93
+ return self._manager.run(self._send_request_to_controller, request, response_errors, raise_errors)
94
+
95
+ async def _async_get_leader_for_partitions(self, partitions):
96
+ """Finds ID of the leader node for every given topic partition."""
97
+ partitions = set(partitions)
98
+ topics = set(tp.topic for tp in partitions)
99
+
100
+ metadata = await self._get_cluster_metadata(topics)
101
+
102
+ leader2partitions = defaultdict(set)
103
+ valid_partitions = set()
104
+ for topic in metadata.get("topics", ()):
105
+ for partition in topic.get("partitions", ()):
106
+ t2p = TopicPartition(topic=topic["name"], partition=partition["partition_index"])
107
+ if t2p in partitions:
108
+ leader2partitions[partition["leader_id"]].add(t2p)
109
+ valid_partitions.add(t2p)
110
+
111
+ if partitions != valid_partitions:
112
+ unknown = partitions - valid_partitions
113
+ raise UnknownTopicOrPartitionError(
114
+ "The following partitions are not known: %s"
115
+ % ", ".join(str(x) for x in unknown)
116
+ )
117
+ return leader2partitions
118
+
119
+ async def _async_delete_records(self, records_to_delete, timeout_ms=None, partition_leader_id=None):
120
+ timeout_ms = self._validate_timeout(timeout_ms)
121
+ timer = Timer(timeout_ms)
122
+ backoff_secs = self.config['retry_backoff_ms'] / 1000
123
+
124
+ pending = set(records_to_delete)
125
+ partition2result = {}
126
+ partition2error = {}
127
+
128
+ while pending:
129
+ if partition_leader_id is None:
130
+ leader2partitions = await self._async_get_leader_for_partitions(pending)
131
+ else:
132
+ leader2partitions = {partition_leader_id: set(pending)}
133
+
134
+ responses = []
135
+ for leader, partitions in leader2partitions.items():
136
+ topic2partitions = defaultdict(list)
137
+ for partition in partitions:
138
+ topic2partitions[partition.topic].append(partition)
139
+
140
+ request = DeleteRecordsRequest(
141
+ topics=[
142
+ (topic, [(tp.partition, records_to_delete[tp]) for tp in parts])
143
+ for topic, parts in topic2partitions.items()
144
+ ],
145
+ timeout_ms=int(timer.timeout_ms) if timer.timeout_ms is not None else timeout_ms,
146
+ )
147
+ response = await self._manager.send(request, node_id=leader)
148
+ responses.append(response.to_dict())
149
+
150
+ retry_partitions = set()
151
+ for response in responses:
152
+ for topic in response["topics"]:
153
+ for partition in topic["partitions"]:
154
+ tp = TopicPartition(topic["name"], partition["partition_index"])
155
+ err_code = partition["error_code"]
156
+ if (err_code == Errors.NotLeaderForPartitionError.errno
157
+ and partition_leader_id is None
158
+ and not timer.expired):
159
+ retry_partitions.add(tp)
160
+ continue
161
+ partition2result[tp] = partition
162
+ pending.discard(tp)
163
+ if err_code != 0:
164
+ partition2error[tp] = err_code
165
+
166
+ if not retry_partitions:
167
+ break
168
+
169
+ log.debug(
170
+ 'delete_records: NotLeaderForPartitionError on %d partition(s); '
171
+ 'refreshing metadata and retrying', len(retry_partitions))
172
+ pending = retry_partitions
173
+ await self._net.sleep(min(backoff_secs, max(0.0, timer.timeout_secs or 0.0)))
174
+
175
+ if partition2error:
176
+ if len(partition2error) == 1:
177
+ key, error = next(iter(partition2error.items()))
178
+ raise Errors.for_code(error)(
179
+ "Error deleting records from topic %s partition %s" % (key.topic, key.partition)
180
+ )
181
+ else:
182
+ raise Errors.BrokerResponseError(
183
+ "The following errors occured when trying to delete records: " +
184
+ ", ".join(
185
+ "%s(partition=%d): %s" %
186
+ (partition.topic, partition.partition, Errors.for_code(error).__name__)
187
+ for partition, error in partition2error.items()
188
+ )
189
+ )
190
+
191
+ return partition2result
192
+
193
+ def delete_records(self, records_to_delete, timeout_ms=None, partition_leader_id=None):
194
+ """Delete records whose offset is smaller than the given offset of the corresponding partition.
195
+
196
+ Partitions whose response is :class:`~kafka.errors.NotLeaderForPartitionError`
197
+ are retried with refreshed metadata, bounded by ``timeout_ms`` (or the
198
+ admin client's ``request_timeout_ms`` when ``None``). When
199
+ ``partition_leader_id`` is supplied no retry is attempted; the caller
200
+ is asserting routing and any error is reported as-is.
201
+
202
+ Arguments:
203
+ records_to_delete ({TopicPartition: int}): The earliest available offsets for the
204
+ given partitions.
205
+
206
+ Keyword Arguments:
207
+ timeout_ms (numeric, optional): Timeout in milliseconds. Also caps
208
+ the total time spent retrying NotLeaderForPartitionError.
209
+ partition_leader_id (node_id / int, optional): If specified, all deletion requests
210
+ will be sent to this node.
211
+
212
+ Returns:
213
+ dict {topicPartition -> metadata}
214
+ """
215
+ return self._manager.run(self._async_delete_records, records_to_delete, timeout_ms, partition_leader_id)
216
+
217
+ def _get_all_topic_partitions(self, topics=None):
218
+ return [
219
+ (
220
+ topic['name'],
221
+ [p['partition_index'] for p in topic['partitions']]
222
+ )
223
+ for topic in self.describe_topics(topics)
224
+ ]
225
+
226
+ def _get_topic_partitions(self, topic_partitions):
227
+ if isinstance(topic_partitions, dict):
228
+ return topic_partitions.items()
229
+ else:
230
+ return self._get_all_topic_partitions(topic_partitions)
231
+
232
+ def elect_leaders(self, election_type, topic_partitions=None, timeout_ms=None, raise_errors=True):
233
+ """Trigger leader election for the specified topic partitions.
234
+
235
+ Arguments:
236
+ election_type: Type of election to attempt. 0 for Preferred, 1 for Unclean
237
+
238
+ Keyword Arguments:
239
+ topic_partitions (dict, list, optional):
240
+ Either: dict of {topic_name: [partition ids]}.
241
+ Or: list of [topic_name], and election will run on all partitions for topic.
242
+ Or: None, and election runs against all topics / all partitions.
243
+ Default: None
244
+ timeout_ms (num, optional): Milliseconds to wait for the leader election process.
245
+ raise_errors (bool, optional): Whether to raise errors as exceptions. Default True.
246
+
247
+ Returns:
248
+ Appropriate version of ElectLeadersResponse class.
249
+ """
250
+ timeout_ms = self._validate_timeout(timeout_ms)
251
+ request = ElectLeadersRequest(
252
+ election_type=ElectionType(election_type),
253
+ topic_partitions=self._get_topic_partitions(topic_partitions),
254
+ timeout_ms=timeout_ms,
255
+ )
256
+ def response_errors(r):
257
+ if r.API_VERSION >= 1:
258
+ yield Errors.for_code(r.error_code)
259
+ for result in r.replica_election_results:
260
+ for partition in result.partition_result:
261
+ yield Errors.for_code(partition.error_code)
262
+ ignore_errors = (Errors.ElectionNotNeededError,)
263
+ return self._manager.run(self._send_request_to_controller, request, response_errors, raise_errors, ignore_errors)
264
+
265
+ @staticmethod
266
+ def _process_alter_partition_reassignments_input(reassignments):
267
+ _Topic = AlterPartitionReassignmentsRequest.ReassignableTopic
268
+ _Partition = _Topic.ReassignablePartition
269
+ topic2partitions = defaultdict(list)
270
+ for tp, replicas in reassignments.items():
271
+ if replicas is not None:
272
+ replicas = list(replicas)
273
+ if not replicas:
274
+ raise ValueError(
275
+ "Replica list for %s must be non-empty; "
276
+ "use None to cancel a reassignment." % (tp,))
277
+ elif not all(isinstance(item, int) for item in replicas):
278
+ raise ValueError(
279
+ "Replica list for %s must be int broker_ids." % (tp,))
280
+ topic2partitions[tp.topic].append(_Partition(
281
+ partition_index=tp.partition,
282
+ replicas=replicas,
283
+ ))
284
+ return [_Topic(name=topic, partitions=parts) for topic, parts in topic2partitions.items()]
285
+
286
+ def alter_partition_reassignments(self, reassignments, timeout_ms=None):
287
+ """Alter the replica sets for the given partitions.
288
+
289
+ Arguments:
290
+ reassignments (dict): A dict mapping
291
+ :class:`~kafka.TopicPartition` to a list of broker IDs
292
+ for the new replica set, or ``None`` to cancel a
293
+ pending reassignment for that partition.
294
+
295
+ Keyword Arguments:
296
+ timeout_ms (numeric, optional): The time in ms to wait for
297
+ the request to complete.
298
+
299
+ Raises: top-level failures that prevents processing request.
300
+ Does not raise partition-specific errors.
301
+
302
+ Returns:
303
+ dict: A dict mapping each :class:`~kafka.TopicPartition`
304
+ that the broker acknowledged to the error class for that
305
+ partition, or ``None`` if the reassignment was accepted.
306
+ Partitions the broker did not report on are absent from the
307
+ dict.
308
+ """
309
+ timeout_ms = self._validate_timeout(timeout_ms)
310
+
311
+ request = AlterPartitionReassignmentsRequest(
312
+ timeout_ms=timeout_ms,
313
+ topics=self._process_alter_partition_reassignments_input(reassignments),
314
+ )
315
+
316
+ def top_level_error(r):
317
+ yield Errors.for_code(r.error_code)
318
+ response = self._manager.run(
319
+ self._send_request_to_controller, request, top_level_error)
320
+
321
+ results = {}
322
+ for topic in response.responses:
323
+ for partition in topic.partitions:
324
+ tp = TopicPartition(topic.name, partition.partition_index)
325
+ err = Errors.for_code(partition.error_code)
326
+ results[tp] = err if err is not Errors.NoError else None
327
+ return results
328
+
329
+ async def _async_list_partition_reassignments(self, topic_partitions=None, timeout_ms=None):
330
+ timeout_ms = self._validate_timeout(timeout_ms)
331
+
332
+ if topic_partitions is None:
333
+ topics_field = None
334
+ else:
335
+ _Topic = ListPartitionReassignmentsRequest.ListPartitionReassignmentsTopics
336
+ if isinstance(topic_partitions, dict):
337
+ topics_field = [
338
+ _Topic(name=topic, partition_indexes=list(partitions))
339
+ for topic, partitions in topic_partitions.items()
340
+ ]
341
+ else:
342
+ topic2partitions = defaultdict(list)
343
+ for tp in topic_partitions:
344
+ topic2partitions[tp.topic].append(tp.partition)
345
+ topics_field = [
346
+ _Topic(name=topic, partition_indexes=partitions)
347
+ for topic, partitions in topic2partitions.items()
348
+ ]
349
+
350
+ request = ListPartitionReassignmentsRequest(
351
+ timeout_ms=timeout_ms,
352
+ topics=topics_field,
353
+ )
354
+
355
+ def top_level_error(r):
356
+ yield Errors.for_code(r.error_code)
357
+ response = await self._send_request_to_controller(request, top_level_error)
358
+
359
+ ret = {}
360
+ for topic in response.topics:
361
+ for partition in topic.partitions:
362
+ ret[TopicPartition(topic.name, partition.partition_index)] = {
363
+ 'replicas': list(partition.replicas),
364
+ 'adding_replicas': list(partition.adding_replicas),
365
+ 'removing_replicas': list(partition.removing_replicas),
366
+ }
367
+ return ret
368
+
369
+ def list_partition_reassignments(self, topic_partitions=None, timeout_ms=None):
370
+ """List the current ongoing partition reassignments.
371
+
372
+ Arguments:
373
+ topic_partitions (dict, list, optional):
374
+ Either: a dict of ``{topic_name: [partition_ids]}``,
375
+ or a list of :class:`~kafka.TopicPartition`,
376
+ or ``None`` to list ongoing reassignments for all partitions.
377
+ Default: None.
378
+
379
+ Keyword Arguments:
380
+ timeout_ms (numeric, optional): The time in ms to wait for the
381
+ request to complete.
382
+
383
+ Returns:
384
+ dict: A dict mapping :class:`~kafka.TopicPartition` to a dict
385
+ with keys ``'replicas'``, ``'adding_replicas'``, and
386
+ ``'removing_replicas'`` (each a list of broker IDs).
387
+ """
388
+ return self._manager.run(
389
+ self._async_list_partition_reassignments, topic_partitions, timeout_ms)
390
+
391
+ async def _async_describe_topic_partitions(self, topics, response_partition_limit, cursor):
392
+ _Topic = DescribeTopicPartitionsRequest.TopicRequest
393
+ _Cursor = DescribeTopicPartitionsRequest.Cursor
394
+
395
+ if cursor is not None:
396
+ cursor_field = _Cursor(
397
+ topic_name=cursor['topic_name'],
398
+ partition_index=cursor['partition_index'],
399
+ )
400
+ else:
401
+ cursor_field = None
402
+
403
+ request = DescribeTopicPartitionsRequest(
404
+ topics=[_Topic(name=t) for t in topics],
405
+ response_partition_limit=response_partition_limit,
406
+ cursor=cursor_field,
407
+ )
408
+ response = await self._manager.send(request)
409
+
410
+ result = []
411
+ for topic in response.topics:
412
+ topic_dict = {
413
+ 'error_code': topic.error_code,
414
+ 'name': topic.name,
415
+ 'topic_id': topic.topic_id,
416
+ 'is_internal': topic.is_internal,
417
+ 'partitions': [
418
+ {
419
+ 'error_code': p.error_code,
420
+ 'partition_index': p.partition_index,
421
+ 'leader_id': p.leader_id,
422
+ 'leader_epoch': p.leader_epoch,
423
+ 'replica_nodes': list(p.replica_nodes),
424
+ 'isr_nodes': list(p.isr_nodes),
425
+ 'eligible_leader_replicas': list(p.eligible_leader_replicas) if p.eligible_leader_replicas else None,
426
+ 'last_known_elr': list(p.last_known_elr) if p.last_known_elr else None,
427
+ 'offline_replicas': list(p.offline_replicas),
428
+ }
429
+ for p in topic.partitions
430
+ ],
431
+ 'topic_authorized_operations': topic.topic_authorized_operations,
432
+ }
433
+ result.append(topic_dict)
434
+
435
+ next_cursor = None
436
+ if response.next_cursor is not None:
437
+ next_cursor = {
438
+ 'topic_name': response.next_cursor.topic_name,
439
+ 'partition_index': response.next_cursor.partition_index,
440
+ }
441
+
442
+ return {'topics': result, 'next_cursor': next_cursor}
443
+
444
+ def describe_topic_partitions(self, topics, response_partition_limit=2000, cursor=None):
445
+ """Describe topics with fine-grained partition-level control (KIP-966).
446
+
447
+ Unlike :meth:`describe_topics`, this uses the DescribeTopicPartitions
448
+ API (apiKey 75, broker 3.7+) which supports pagination via a cursor
449
+ and partition-level ELR (Eligible Leader Replicas) information.
450
+
451
+ Arguments:
452
+ topics ([str]): A list of topic names.
453
+
454
+ Keyword Arguments:
455
+ response_partition_limit (int, optional): Maximum number of
456
+ partitions to include in the response. Default: 2000.
457
+ cursor (dict, optional): Dict with ``'topic_name'`` and
458
+ ``'partition_index'`` keys to start pagination from. Default:
459
+ None.
460
+
461
+ Returns:
462
+ dict: ``{'topics': [...], 'next_cursor': None | {...}}``.
463
+ ``topics`` is a list of dicts (one per topic) with keys
464
+ ``error_code``, ``name``, ``topic_id``, ``is_internal``,
465
+ ``partitions``, and ``topic_authorized_operations``.
466
+ ``next_cursor`` is None if pagination is complete, otherwise a
467
+ dict with the next page's ``topic_name`` and ``partition_index``.
468
+ """
469
+ return self._manager.run(
470
+ self._async_describe_topic_partitions, topics, response_partition_limit, cursor)
471
+
472
+ # -- List partition offsets --------------------------------------------
473
+
474
+ @staticmethod
475
+ def _list_partition_offsets_request(partition_timestamps, isolation_level_int):
476
+ min_version = max(ListOffsetsRequest.min_version_for_isolation_level(isolation_level_int), 0)
477
+ _Topic = ListOffsetsRequest.ListOffsetsTopic
478
+ _Partition = _Topic.ListOffsetsPartition
479
+ topic2partitions = defaultdict(list)
480
+ for tp, ts in partition_timestamps.items():
481
+ if not isinstance(ts, (int, OffsetSpec)):
482
+ raise TypeError(f'Unsupported ts type {type(ts)}, expected int or OffsetSpec')
483
+ elif int(ts) < 0:
484
+ min_version = max(ListOffsetsRequest.min_version_for_timestamp(ts), min_version)
485
+ topic2partitions[tp.topic].append(
486
+ _Partition(partition_index=tp.partition, timestamp=ts))
487
+ return ListOffsetsRequest(
488
+ replica_id=-1,
489
+ isolation_level=isolation_level_int,
490
+ topics=[_Topic(name=name, partitions=parts)
491
+ for name, parts in topic2partitions.items()],
492
+ min_version=min_version,
493
+ )
494
+
495
+ @staticmethod
496
+ def _list_partition_offsets_process_response(response):
497
+ results = {}
498
+ for topic in response.topics:
499
+ for partition in topic.partitions:
500
+ tp = TopicPartition(topic.name, partition.partition_index)
501
+ err = Errors.for_code(partition.error_code)
502
+ if err is not Errors.NoError:
503
+ raise err(
504
+ "ListOffsetsRequest failed for %s: %s" % (tp, err.__name__))
505
+ leader_epoch = getattr(partition, 'leader_epoch', -1)
506
+ results[tp] = OffsetAndTimestamp(
507
+ offset=partition.offset,
508
+ timestamp=partition.timestamp,
509
+ leader_epoch=leader_epoch if leader_epoch >= 0 else None,
510
+ )
511
+ return results
512
+
513
+ async def _async_list_partition_offsets(self, topic_partition_specs, isolation_level=IsolationLevel.READ_UNCOMMITTED, timeout_ms=None):
514
+ isolation_level = IsolationLevel.build_from(isolation_level)
515
+ timer = Timer(self._validate_timeout(timeout_ms))
516
+ backoff_secs = self.config['retry_backoff_ms'] / 1000
517
+ results = {}
518
+ topic_partitions = set(topic_partition_specs.keys())
519
+ while topic_partitions:
520
+ leader2partitions = await self._async_get_leader_for_partitions(topic_partitions)
521
+ for leader, partitions in leader2partitions.items():
522
+ request = self._list_partition_offsets_request(
523
+ {tp: spec for tp, spec in topic_partition_specs.items() if tp in partitions},
524
+ isolation_level.value)
525
+ try:
526
+ response = await self._manager.send(request, node_id=leader)
527
+ results.update(self._list_partition_offsets_process_response(response))
528
+ topic_partitions -= partitions
529
+ except Errors.NotLeaderForPartitionError:
530
+ continue
531
+ if topic_partitions:
532
+ if timer.expired:
533
+ raise Errors.NotLeaderForPartitionError(
534
+ 'list_partition_offsets timed out retrying NotLeaderForPartitionError '
535
+ 'for partitions: %s' % sorted(topic_partitions))
536
+ log.debug(
537
+ 'list_partition_offsets: NotLeaderForPartitionError on %d partition(s); '
538
+ 'refreshing metadata and retrying', len(topic_partitions))
539
+ await self._net.sleep(min(backoff_secs, max(0.0, timer.timeout_secs or 0.0)))
540
+ return results
541
+
542
+ def list_partition_offsets(self, topic_partition_specs, isolation_level=IsolationLevel.READ_UNCOMMITTED, timeout_ms=None):
543
+ """Look up offsets for the given partitions by spec.
544
+
545
+ Partitions are routed to their respective leader brokers via cluster
546
+ metadata; one ``ListOffsetsRequest`` is sent per leader. Partitions
547
+ that return :class:`~kafka.errors.NotLeaderForPartitionError` are
548
+ retried with refreshed metadata, bounded by ``timeout_ms`` (or the
549
+ admin client's ``request_timeout_ms`` when ``None``).
550
+
551
+ Arguments:
552
+ topic_partition_specs: dict mapping :class:`~kafka.TopicPartition` to
553
+ :class:`OffsetSpec` (or a raw integer timestamp /
554
+ wire-level sentinel).
555
+
556
+ Keyword Arguments:
557
+ isolation_level (IsolationLevel, optional): Requires broker support
558
+ for ListOffsets v2+. Default: IsolationLevel.READ_UNCOMMITTED.
559
+ timeout_ms (int, optional): Maximum time to spend retrying
560
+ NotLeaderForPartitionError. Default: ``request_timeout_ms``.
561
+
562
+ Returns:
563
+ dict: A dict mapping :class:`~kafka.TopicPartition` to
564
+ :class:`~kafka.structs.OffsetAndTimestamp`
565
+
566
+ Raises:
567
+ KafkaError: If any partition response carries an error code.
568
+ NotLeaderForPartitionError: If NotLeaderForPartitionError retries
569
+ do not converge within ``timeout_ms``.
570
+ UnknownTopicOrPartitionError: If a requested partition is not
571
+ known to the cluster.
572
+ UnsupportedVersionError: If the broker does not support a version
573
+ of ListOffsetsRequest compatible with the requested specs.
574
+ """
575
+ return self._manager.run(
576
+ self._async_list_partition_offsets, topic_partition_specs, isolation_level, timeout_ms)
577
+
578
+
579
+ class NewPartitions:
580
+ """DEPRECATED: A class for new partition creation on existing topics.
581
+
582
+ Note that the length of new_assignments, if specified, must be the
583
+ difference between the new total number of partitions and the existing
584
+ number of partitions.
585
+
586
+ Arguments:
587
+ total_count (int): the total number of partitions that should exist
588
+ on the topic
589
+ new_assignments ([[int]]): an array of arrays of replica assignments
590
+ for new partitions. If not set, broker assigns replicas per an
591
+ internal algorithm.
592
+ """
593
+ def __init__(self, total_count, new_assignments=None):
594
+ self.total_count = total_count
595
+ self.new_assignments = new_assignments