kafka-python 2.1.1__tar.gz → 2.1.3__tar.gz

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 (152) hide show
  1. {kafka_python-2.1.1 → kafka_python-2.1.3}/CHANGES.md +41 -0
  2. {kafka_python-2.1.1 → kafka_python-2.1.3}/PKG-INFO +3 -2
  3. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/admin/client.py +1 -1
  4. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/client_async.py +8 -1
  5. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/cluster.py +1 -0
  6. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/conn.py +21 -4
  7. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/consumer/fetcher.py +209 -252
  8. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/consumer/group.py +27 -19
  9. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/consumer/subscription_state.py +69 -63
  10. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/base.py +11 -9
  11. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/consumer.py +12 -5
  12. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/compound_stat.py +2 -2
  13. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/measurable_stat.py +2 -1
  14. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/metrics_reporter.py +3 -2
  15. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stat.py +3 -2
  16. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/sampled_stat.py +2 -2
  17. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/producer/kafka.py +5 -0
  18. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/abstract.py +3 -2
  19. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/admin.py +2 -1
  20. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/api.py +12 -9
  21. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/api_versions.py +45 -1
  22. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/broker_api_versions.py +2 -0
  23. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/parser.py +7 -6
  24. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/record/abc.py +22 -4
  25. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/record/default_records.py +17 -9
  26. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/record/legacy_records.py +33 -10
  27. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/record/memory_records.py +6 -2
  28. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/abc.py +3 -2
  29. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/util.py +24 -0
  30. kafka_python-2.1.3/kafka/version.py +1 -0
  31. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka_python.egg-info/PKG-INFO +3 -2
  32. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka_python.egg-info/SOURCES.txt +1 -0
  33. {kafka_python-2.1.1 → kafka_python-2.1.3}/pyproject.toml +1 -0
  34. kafka_python-2.1.3/test/test_consumer.py +52 -0
  35. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_consumer_group.py +7 -6
  36. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_consumer_integration.py +2 -2
  37. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_coordinator.py +1 -0
  38. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_fetcher.py +47 -51
  39. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_producer.py +1 -1
  40. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_sasl_integration.py +1 -1
  41. kafka_python-2.1.3/test/test_subscription_state.py +57 -0
  42. kafka_python-2.1.1/test/test_subscription_state.py → kafka_python-2.1.3/test/test_util.py +2 -3
  43. kafka_python-2.1.1/kafka/version.py +0 -1
  44. kafka_python-2.1.1/test/test_consumer.py +0 -26
  45. {kafka_python-2.1.1 → kafka_python-2.1.3}/AUTHORS.md +0 -0
  46. {kafka_python-2.1.1 → kafka_python-2.1.3}/LICENSE +0 -0
  47. {kafka_python-2.1.1 → kafka_python-2.1.3}/MANIFEST.in +0 -0
  48. {kafka_python-2.1.1 → kafka_python-2.1.3}/README.rst +0 -0
  49. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/__init__.py +0 -0
  50. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/admin/__init__.py +0 -0
  51. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/admin/acl_resource.py +0 -0
  52. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/admin/config_resource.py +0 -0
  53. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/admin/new_partitions.py +0 -0
  54. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/admin/new_topic.py +0 -0
  55. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/codec.py +0 -0
  56. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/consumer/__init__.py +0 -0
  57. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/__init__.py +0 -0
  58. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/__init__.py +0 -0
  59. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/abstract.py +0 -0
  60. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/range.py +0 -0
  61. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/roundrobin.py +0 -0
  62. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/sticky/__init__.py +0 -0
  63. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
  64. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
  65. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
  66. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/heartbeat.py +0 -0
  67. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/coordinator/protocol.py +0 -0
  68. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/errors.py +0 -0
  69. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/future.py +0 -0
  70. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/__init__.py +0 -0
  71. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/dict_reporter.py +0 -0
  72. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/kafka_metric.py +0 -0
  73. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/measurable.py +0 -0
  74. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/metric_config.py +0 -0
  75. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/metric_name.py +0 -0
  76. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/metrics.py +0 -0
  77. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/quota.py +0 -0
  78. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/__init__.py +0 -0
  79. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/avg.py +0 -0
  80. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/count.py +0 -0
  81. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/histogram.py +0 -0
  82. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/max_stat.py +0 -0
  83. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/min_stat.py +0 -0
  84. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/percentile.py +0 -0
  85. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/percentiles.py +0 -0
  86. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/rate.py +0 -0
  87. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/sensor.py +0 -0
  88. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/metrics/stats/total.py +0 -0
  89. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/partitioner/__init__.py +0 -0
  90. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/partitioner/default.py +0 -0
  91. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/producer/__init__.py +0 -0
  92. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/producer/buffer.py +0 -0
  93. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/producer/future.py +0 -0
  94. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/producer/record_accumulator.py +0 -0
  95. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/producer/sender.py +0 -0
  96. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/__init__.py +0 -0
  97. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/commit.py +0 -0
  98. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/fetch.py +0 -0
  99. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/find_coordinator.py +0 -0
  100. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/frame.py +0 -0
  101. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/group.py +0 -0
  102. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/list_offsets.py +0 -0
  103. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/message.py +0 -0
  104. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/metadata.py +0 -0
  105. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/offset_for_leader_epoch.py +0 -0
  106. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/pickle.py +0 -0
  107. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/produce.py +0 -0
  108. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/sasl_authenticate.py +0 -0
  109. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/sasl_handshake.py +0 -0
  110. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/struct.py +0 -0
  111. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/protocol/types.py +0 -0
  112. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/record/__init__.py +0 -0
  113. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/record/_crc32c.py +0 -0
  114. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/record/util.py +0 -0
  115. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/__init__.py +0 -0
  116. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/gssapi.py +0 -0
  117. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/msk.py +0 -0
  118. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/oauth.py +0 -0
  119. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/plain.py +0 -0
  120. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/scram.py +0 -0
  121. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/sasl/sspi.py +0 -0
  122. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/serializer/__init__.py +0 -0
  123. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/serializer/abstract.py +0 -0
  124. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/socks5_wrapper.py +0 -0
  125. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/structs.py +0 -0
  126. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/vendor/__init__.py +0 -0
  127. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/vendor/enum34.py +0 -0
  128. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/vendor/selectors34.py +0 -0
  129. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/vendor/six.py +0 -0
  130. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka/vendor/socketpair.py +0 -0
  131. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka_python.egg-info/dependency_links.txt +0 -0
  132. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka_python.egg-info/requires.txt +0 -0
  133. {kafka_python-2.1.1 → kafka_python-2.1.3}/kafka_python.egg-info/top_level.txt +0 -0
  134. {kafka_python-2.1.1 → kafka_python-2.1.3}/setup.cfg +0 -0
  135. {kafka_python-2.1.1 → kafka_python-2.1.3}/setup.py +0 -0
  136. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_acl_comparisons.py +0 -0
  137. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_admin.py +0 -0
  138. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_admin_integration.py +0 -0
  139. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_api_object_implementation.py +0 -0
  140. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_assignors.py +0 -0
  141. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_client_async.py +0 -0
  142. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_cluster.py +0 -0
  143. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_codec.py +0 -0
  144. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_conn.py +0 -0
  145. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_metrics.py +0 -0
  146. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_object_conversion.py +0 -0
  147. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_package.py +0 -0
  148. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_partition_movements.py +0 -0
  149. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_partitioner.py +0 -0
  150. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_protocol.py +0 -0
  151. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/test_sender.py +0 -0
  152. {kafka_python-2.1.1 → kafka_python-2.1.3}/test/testutil.py +0 -0
@@ -1,3 +1,44 @@
1
+ # 2.1.3 (Mar 25, 2025)
2
+
3
+ Fixes
4
+ * Fix crash when switching to closest compatible api_version in KafkaClient (#2567)
5
+ * Fix maximum version to send an OffsetFetchRequest in KafkaAdminClient (#2563)
6
+ * Return empty set from consumer.partitions_for_topic when topic not found (#2556)
7
+
8
+ Improvements
9
+ * KIP-511: Use ApiVersions v4 on initial connect w/ client_software_name + version (#2558)
10
+ * KIP-74: Manage assigned partition order in consumer (#2562)
11
+ * KIP-70: Auto-commit offsets on consumer.unsubscribe(), defer assignment changes to rejoin (#2560)
12
+ * Use SubscriptionType to track topics/pattern/user assignment (#2565)
13
+ * Add optional timeout_ms kwarg to consumer.close() (#2564)
14
+ * Move ensure_valid_topic_name to kafka.util; use in client and producer (#2561)
15
+
16
+ Testing
17
+ * Support KRaft / 4.0 brokers in tests (#2559)
18
+ * Test older pythons against 4.0 broker
19
+
20
+ Compatibility
21
+ * Add python 3.13 to compatibility list
22
+
23
+ # 2.1.2 (Mar 17, 2025)
24
+
25
+ Fixes
26
+ * Simplify consumer.poll send fetches logic
27
+ * Fix crc validation in consumer / fetcher
28
+ * Lazy `_unpack_records` in PartitionRecords to fix premature fetch offset advance in consumer.poll() (#2555)
29
+ * Debug log fetch records return; separate offsets update log
30
+ * Fix Fetcher retriable error handling (#2554)
31
+ * Use six.add_metaclass for py2/py3 compatible abc (#2551)
32
+
33
+ Improvements
34
+ * Add FetchMetrics class; move topic_fetch_metrics inside aggregator
35
+ * DefaultRecordsBatchBuilder: support empty batch
36
+ * MemoryRecordsBuilder: support arbitrary offset, skipping offsets
37
+ * Add record.validate_crc() for v0/v1 crc checks
38
+ * Remove fetcher message_generator / iterator interface
39
+ * Add size_in_bytes to ABCRecordBatch and implement for Legacy and Default
40
+ * Add magic property to ABCRecord and implement for LegacyRecord
41
+
1
42
  # 2.1.1 (Mar 16, 2025)
2
43
 
3
44
  Fixes
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: kafka-python
3
- Version: 2.1.1
3
+ Version: 2.1.3
4
4
  Summary: Pure Python client for Apache Kafka
5
5
  Author-email: Dana Powers <dana.powers@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dpkp/kafka-python
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.9
21
21
  Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
24
25
  Classifier: Programming Language :: Python :: Implementation :: CPython
25
26
  Classifier: Programming Language :: Python :: Implementation :: PyPy
26
27
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -1496,7 +1496,7 @@ class KafkaAdminClient(object):
1496
1496
  A message future
1497
1497
  """
1498
1498
  version = self._client.api_version(OffsetFetchRequest, max_version=5)
1499
- if version <= 3:
1499
+ if version <= 5:
1500
1500
  if partitions is None:
1501
1501
  if version <= 1:
1502
1502
  raise ValueError(
@@ -27,7 +27,7 @@ from kafka.metrics.stats import Avg, Count, Rate
27
27
  from kafka.metrics.stats.rate import TimeUnit
28
28
  from kafka.protocol.broker_api_versions import BROKER_API_VERSIONS
29
29
  from kafka.protocol.metadata import MetadataRequest
30
- from kafka.util import Dict, WeakMethod
30
+ from kafka.util import Dict, WeakMethod, ensure_valid_topic_name
31
31
  # Although this looks unused, it actually monkey-patches socket.socketpair()
32
32
  # and should be left in as long as we're using socket.socketpair() in this file
33
33
  from kafka.vendor import socketpair # noqa: F401
@@ -276,6 +276,7 @@ class KafkaClient(object):
276
276
  if compatible_version:
277
277
  log.warning('Configured api_version %s not supported; using %s',
278
278
  self.config['api_version'], compatible_version)
279
+ self.config['api_version'] = compatible_version
279
280
  self._api_versions = BROKER_API_VERSIONS[compatible_version]
280
281
  else:
281
282
  raise Errors.UnrecognizedBrokerVersion(self.config['api_version'])
@@ -909,7 +910,13 @@ class KafkaClient(object):
909
910
 
910
911
  Returns:
911
912
  Future: resolves after metadata request/response
913
+
914
+ Raises:
915
+ TypeError: if topic is not a string
916
+ ValueError: if topic is invalid: must be chars (a-zA-Z0-9._-), and less than 250 length
912
917
  """
918
+ ensure_valid_topic_name(topic)
919
+
913
920
  if topic in self._topics:
914
921
  return Future().success(set(self._topics))
915
922
 
@@ -112,6 +112,7 @@ class ClusterMetadata(object):
112
112
 
113
113
  Returns:
114
114
  set: {partition (int), ...}
115
+ None if topic not found.
115
116
  """
116
117
  if topic not in self._partitions:
117
118
  return None
@@ -101,6 +101,10 @@ class BrokerConnection(object):
101
101
  server-side log entries that correspond to this client. Also
102
102
  submitted to GroupCoordinator for logging with respect to
103
103
  consumer group administration. Default: 'kafka-python-{version}'
104
+ client_software_name (str): Sent to kafka broker for KIP-511.
105
+ Default: 'kafka-python'
106
+ client_software_version (str): Sent to kafka broker for KIP-511.
107
+ Default: The kafka-python version (via kafka.version).
104
108
  reconnect_backoff_ms (int): The amount of time in milliseconds to
105
109
  wait before attempting to reconnect to a given host.
106
110
  Default: 50.
@@ -191,6 +195,8 @@ class BrokerConnection(object):
191
195
 
192
196
  DEFAULT_CONFIG = {
193
197
  'client_id': 'kafka-python-' + __version__,
198
+ 'client_software_name': 'kafka-python',
199
+ 'client_software_version': __version__,
194
200
  'node_id': 0,
195
201
  'request_timeout_ms': 30000,
196
202
  'reconnect_backoff_ms': 50,
@@ -242,7 +248,7 @@ class BrokerConnection(object):
242
248
  self._api_versions = None
243
249
  self._api_version = None
244
250
  self._check_version_idx = None
245
- self._api_versions_idx = 2
251
+ self._api_versions_idx = 4 # version of ApiVersionsRequest to try on first connect
246
252
  self._throttle_time = None
247
253
  self._socks5_proxy = None
248
254
 
@@ -538,7 +544,14 @@ class BrokerConnection(object):
538
544
  log.debug('%s: Using pre-configured api_version %s for ApiVersions', self, self._api_version)
539
545
  return True
540
546
  elif self._check_version_idx is None:
541
- request = ApiVersionsRequest[self._api_versions_idx]()
547
+ version = self._api_versions_idx
548
+ if version >= 3:
549
+ request = ApiVersionsRequest[version](
550
+ client_software_name=self.config['client_software_name'],
551
+ client_software_version=self.config['client_software_version'],
552
+ _tagged_fields={})
553
+ else:
554
+ request = ApiVersionsRequest[version]()
542
555
  future = Future()
543
556
  response = self._send(request, blocking=True, request_timeout_ms=(self.config['api_version_auto_timeout_ms'] * 0.8))
544
557
  response.add_callback(self._handle_api_versions_response, future)
@@ -573,11 +586,15 @@ class BrokerConnection(object):
573
586
 
574
587
  def _handle_api_versions_response(self, future, response):
575
588
  error_type = Errors.for_code(response.error_code)
576
- # if error_type i UNSUPPORTED_VERSION: retry w/ latest version from response
577
589
  if error_type is not Errors.NoError:
578
590
  future.failure(error_type())
579
591
  if error_type is Errors.UnsupportedVersionError:
580
592
  self._api_versions_idx -= 1
593
+ for api_key, min_version, max_version, *rest in response.api_versions:
594
+ # If broker provides a lower max_version, skip to that
595
+ if api_key == response.API_KEY:
596
+ self._api_versions_idx = min(self._api_versions_idx, max_version)
597
+ break
581
598
  if self._api_versions_idx >= 0:
582
599
  self._api_versions_future = None
583
600
  self.state = ConnectionStates.API_VERSIONS_SEND
@@ -587,7 +604,7 @@ class BrokerConnection(object):
587
604
  return
588
605
  self._api_versions = dict([
589
606
  (api_key, (min_version, max_version))
590
- for api_key, min_version, max_version in response.api_versions
607
+ for api_key, min_version, max_version, *rest in response.api_versions
591
608
  ])
592
609
  self._api_version = self._infer_broker_version_from_api_versions(self._api_versions)
593
610
  log.info('Broker version identified as %s', '.'.join(map(str, self._api_version)))