kafka-python 2.0.4__tar.gz → 2.0.5__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 (138) hide show
  1. {kafka_python-2.0.4 → kafka_python-2.0.5}/CHANGES.md +26 -0
  2. {kafka_python-2.0.4 → kafka_python-2.0.5}/PKG-INFO +2 -2
  3. {kafka_python-2.0.4 → kafka_python-2.0.5}/README.rst +1 -1
  4. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/admin/client.py +30 -18
  5. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/client_async.py +2 -16
  6. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/conn.py +2 -2
  7. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/base.py +4 -3
  8. kafka_python-2.0.5/kafka/version.py +1 -0
  9. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka_python.egg-info/PKG-INFO +2 -2
  10. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_admin_integration.py +6 -3
  11. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_consumer_integration.py +1 -1
  12. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_sasl_integration.py +13 -7
  13. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/testutil.py +2 -2
  14. kafka_python-2.0.4/kafka/version.py +0 -1
  15. {kafka_python-2.0.4 → kafka_python-2.0.5}/AUTHORS.md +0 -0
  16. {kafka_python-2.0.4 → kafka_python-2.0.5}/LICENSE +0 -0
  17. {kafka_python-2.0.4 → kafka_python-2.0.5}/MANIFEST.in +0 -0
  18. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/__init__.py +0 -0
  19. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/admin/__init__.py +0 -0
  20. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/admin/acl_resource.py +0 -0
  21. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/admin/config_resource.py +0 -0
  22. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/admin/new_partitions.py +0 -0
  23. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/admin/new_topic.py +0 -0
  24. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/cluster.py +0 -0
  25. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/codec.py +0 -0
  26. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/consumer/__init__.py +0 -0
  27. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/consumer/fetcher.py +0 -0
  28. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/consumer/group.py +0 -0
  29. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/consumer/subscription_state.py +0 -0
  30. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/__init__.py +0 -0
  31. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/__init__.py +0 -0
  32. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/abstract.py +0 -0
  33. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/range.py +0 -0
  34. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/roundrobin.py +0 -0
  35. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/sticky/__init__.py +0 -0
  36. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
  37. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
  38. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
  39. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/consumer.py +0 -0
  40. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/heartbeat.py +0 -0
  41. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/coordinator/protocol.py +0 -0
  42. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/errors.py +0 -0
  43. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/future.py +0 -0
  44. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/__init__.py +0 -0
  45. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/compound_stat.py +0 -0
  46. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/dict_reporter.py +0 -0
  47. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/kafka_metric.py +0 -0
  48. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/measurable.py +0 -0
  49. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/measurable_stat.py +0 -0
  50. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/metric_config.py +0 -0
  51. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/metric_name.py +0 -0
  52. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/metrics.py +0 -0
  53. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/metrics_reporter.py +0 -0
  54. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/quota.py +0 -0
  55. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stat.py +0 -0
  56. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/__init__.py +0 -0
  57. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/avg.py +0 -0
  58. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/count.py +0 -0
  59. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/histogram.py +0 -0
  60. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/max_stat.py +0 -0
  61. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/min_stat.py +0 -0
  62. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/percentile.py +0 -0
  63. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/percentiles.py +0 -0
  64. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/rate.py +0 -0
  65. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/sampled_stat.py +0 -0
  66. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/sensor.py +0 -0
  67. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/metrics/stats/total.py +0 -0
  68. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/oauth/__init__.py +0 -0
  69. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/oauth/abstract.py +0 -0
  70. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/partitioner/__init__.py +0 -0
  71. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/partitioner/default.py +0 -0
  72. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/producer/__init__.py +0 -0
  73. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/producer/buffer.py +0 -0
  74. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/producer/future.py +0 -0
  75. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/producer/kafka.py +0 -0
  76. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/producer/record_accumulator.py +0 -0
  77. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/producer/sender.py +0 -0
  78. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/__init__.py +0 -0
  79. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/abstract.py +0 -0
  80. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/admin.py +0 -0
  81. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/api.py +0 -0
  82. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/commit.py +0 -0
  83. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/fetch.py +0 -0
  84. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/frame.py +0 -0
  85. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/group.py +0 -0
  86. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/message.py +0 -0
  87. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/metadata.py +0 -0
  88. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/offset.py +0 -0
  89. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/parser.py +0 -0
  90. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/pickle.py +0 -0
  91. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/produce.py +0 -0
  92. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/struct.py +0 -0
  93. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/protocol/types.py +0 -0
  94. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/record/__init__.py +0 -0
  95. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/record/_crc32c.py +0 -0
  96. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/record/abc.py +0 -0
  97. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/record/default_records.py +0 -0
  98. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/record/legacy_records.py +0 -0
  99. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/record/memory_records.py +0 -0
  100. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/record/util.py +0 -0
  101. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/scram.py +0 -0
  102. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/serializer/__init__.py +0 -0
  103. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/serializer/abstract.py +0 -0
  104. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/structs.py +0 -0
  105. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/util.py +0 -0
  106. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/vendor/__init__.py +0 -0
  107. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/vendor/enum34.py +0 -0
  108. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/vendor/selectors34.py +0 -0
  109. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/vendor/six.py +0 -0
  110. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka/vendor/socketpair.py +0 -0
  111. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka_python.egg-info/SOURCES.txt +0 -0
  112. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka_python.egg-info/dependency_links.txt +0 -0
  113. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka_python.egg-info/requires.txt +0 -0
  114. {kafka_python-2.0.4 → kafka_python-2.0.5}/kafka_python.egg-info/top_level.txt +0 -0
  115. {kafka_python-2.0.4 → kafka_python-2.0.5}/pyproject.toml +0 -0
  116. {kafka_python-2.0.4 → kafka_python-2.0.5}/setup.cfg +0 -0
  117. {kafka_python-2.0.4 → kafka_python-2.0.5}/setup.py +0 -0
  118. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_acl_comparisons.py +0 -0
  119. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_admin.py +0 -0
  120. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_api_object_implementation.py +0 -0
  121. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_assignors.py +0 -0
  122. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_client_async.py +0 -0
  123. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_cluster.py +0 -0
  124. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_codec.py +0 -0
  125. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_conn.py +0 -0
  126. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_consumer.py +0 -0
  127. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_consumer_group.py +0 -0
  128. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_coordinator.py +0 -0
  129. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_fetcher.py +0 -0
  130. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_metrics.py +0 -0
  131. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_object_conversion.py +0 -0
  132. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_package.py +0 -0
  133. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_partition_movements.py +0 -0
  134. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_partitioner.py +0 -0
  135. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_producer.py +0 -0
  136. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_protocol.py +0 -0
  137. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_sender.py +0 -0
  138. {kafka_python-2.0.4 → kafka_python-2.0.5}/test/test_subscription_state.py +0 -0
@@ -1,3 +1,29 @@
1
+ # 2.0.5 (Feb 25, 2025)
2
+
3
+ Networking
4
+ * Remove unused client bootstrap backoff code
5
+ * 200ms timeout for client.poll in ensure_active_group and admin client
6
+
7
+ Fixes
8
+ * Admin client: check_version only if needed, use node_id kwarg for controller
9
+ * Check for -1 controller_id in admin client
10
+ * Only acquire coordinator lock in heartbeat thread close if not self thread
11
+
12
+ Testing
13
+ * Also sleep when waiting for consumers in test_describe_consumer_group_exists
14
+ * Refactor sasl_integration test_client - wait for node ready; use send future
15
+ * Add timeout to test_kafka_consumer
16
+ * Add error str to assert_message_count checks
17
+ * Retry on error in test fixture create_topic_via_metadata
18
+ * Fixup variable interpolation in test fixture error
19
+
20
+ Documentation
21
+ * Update compatibility docs
22
+ * Include client_id in BrokerConnection __str__ output
23
+
24
+ Project Maintenance
25
+ * Add make targets `servers/*/api_versions` and `servers/*/messages`
26
+
1
27
  # 2.0.4 (Feb 21, 2025)
2
28
 
3
29
  Networking
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kafka-python
3
- Version: 2.0.4
3
+ Version: 2.0.5
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
@@ -41,7 +41,7 @@ Requires-Dist: pytest-mock; extra == "testing"
41
41
  Kafka Python client
42
42
  ------------------------
43
43
 
44
- .. image:: https://img.shields.io/badge/kafka-2.6%2C%202.5%2C%202.4%2C%202.3%2C%202.2%2C%202.1%2C%202.0%2C%201.1%2C%201.0%2C%200.11%2C%200.10%2C%200.9%2C%200.8-brightgreen.svg
44
+ .. image:: https://img.shields.io/badge/kafka-3.9--0.8-brightgreen.svg
45
45
  :target: https://kafka-python.readthedocs.io/en/master/compatibility.html
46
46
  .. image:: https://img.shields.io/pypi/pyversions/kafka-python.svg
47
47
  :target: https://pypi.python.org/pypi/kafka-python
@@ -1,7 +1,7 @@
1
1
  Kafka Python client
2
2
  ------------------------
3
3
 
4
- .. image:: https://img.shields.io/badge/kafka-2.6%2C%202.5%2C%202.4%2C%202.3%2C%202.2%2C%202.1%2C%202.0%2C%201.1%2C%201.0%2C%200.11%2C%200.10%2C%200.9%2C%200.8-brightgreen.svg
4
+ .. image:: https://img.shields.io/badge/kafka-3.9--0.8-brightgreen.svg
5
5
  :target: https://kafka-python.readthedocs.io/en/master/compatibility.html
6
6
  .. image:: https://img.shields.io/pypi/pyversions/kafka-python.svg
7
7
  :target: https://pypi.python.org/pypi/kafka-python
@@ -1,9 +1,10 @@
1
- from __future__ import absolute_import
1
+ from __future__ import absolute_import, division
2
2
 
3
3
  from collections import defaultdict
4
4
  import copy
5
5
  import logging
6
6
  import socket
7
+ import time
7
8
 
8
9
  from . import ConfigResourceType
9
10
  from kafka.vendor import six
@@ -212,11 +213,13 @@ class KafkaAdminClient(object):
212
213
  metric_group_prefix='admin',
213
214
  **self.config
214
215
  )
215
- self._client.check_version(timeout=(self.config['api_version_auto_timeout_ms'] / 1000))
216
216
 
217
217
  # Get auto-discovered version from client if necessary
218
218
  if self.config['api_version'] is None:
219
219
  self.config['api_version'] = self._client.config['api_version']
220
+ else:
221
+ # need to run check_version for get_api_versions()
222
+ self._client.check_version(timeout=(self.config['api_version_auto_timeout_ms'] / 1000))
220
223
 
221
224
  self._closed = False
222
225
  self._refresh_controller_id()
@@ -273,24 +276,33 @@ class KafkaAdminClient(object):
273
276
  """
274
277
  return timeout_ms or self.config['request_timeout_ms']
275
278
 
276
- def _refresh_controller_id(self):
279
+ def _refresh_controller_id(self, timeout_ms=30000):
277
280
  """Determine the Kafka cluster controller."""
278
281
  version = self._matching_api_version(MetadataRequest)
279
282
  if 1 <= version <= 6:
280
- request = MetadataRequest[version]()
281
- future = self._send_request_to_node(self._client.least_loaded_node(), request)
282
-
283
- self._wait_for_futures([future])
284
-
285
- response = future.value
286
- controller_id = response.controller_id
287
- # verify the controller is new enough to support our requests
288
- controller_version = self._client.check_version(controller_id, timeout=(self.config['api_version_auto_timeout_ms'] / 1000))
289
- if controller_version < (0, 10, 0):
290
- raise IncompatibleBrokerVersion(
291
- "The controller appears to be running Kafka {}. KafkaAdminClient requires brokers >= 0.10.0.0."
292
- .format(controller_version))
293
- self._controller_id = controller_id
283
+ timeout_at = time.time() + timeout_ms / 1000
284
+ while time.time() < timeout_at:
285
+ request = MetadataRequest[version]()
286
+ future = self._send_request_to_node(self._client.least_loaded_node(), request)
287
+
288
+ self._wait_for_futures([future])
289
+
290
+ response = future.value
291
+ controller_id = response.controller_id
292
+ if controller_id == -1:
293
+ log.warning("Controller ID not available, got -1")
294
+ time.sleep(1)
295
+ continue
296
+ # verify the controller is new enough to support our requests
297
+ controller_version = self._client.check_version(node_id=controller_id, timeout=(self.config['api_version_auto_timeout_ms'] / 1000))
298
+ if controller_version < (0, 10, 0):
299
+ raise IncompatibleBrokerVersion(
300
+ "The controller appears to be running Kafka {}. KafkaAdminClient requires brokers >= 0.10.0.0."
301
+ .format(controller_version))
302
+ self._controller_id = controller_id
303
+ return
304
+ else:
305
+ raise Errors.NodeNotAvailableError('controller')
294
306
  else:
295
307
  raise UnrecognizedBrokerVersion(
296
308
  "Kafka Admin interface cannot determine the controller using MetadataRequest_v{}."
@@ -390,7 +402,7 @@ class KafkaAdminClient(object):
390
402
  while not self._client.ready(node_id):
391
403
  # poll until the connection to broker is ready, otherwise send()
392
404
  # will fail with NodeNotReadyError
393
- self._client.poll()
405
+ self._client.poll(timeout_ms=200)
394
406
  return self._client.send(node_id, request, wakeup)
395
407
 
396
408
  def _send_request_to_controller(self, request):
@@ -216,6 +216,8 @@ class KafkaClient(object):
216
216
  self._connecting = set()
217
217
  self._sending = set()
218
218
  self._refresh_on_disconnects = True
219
+
220
+ # Not currently used, but data is collected internally
219
221
  self._last_bootstrap = 0
220
222
  self._bootstrap_fails = 0
221
223
 
@@ -233,8 +235,6 @@ class KafkaClient(object):
233
235
  self.config['metric_group_prefix'],
234
236
  weakref.proxy(self._conns))
235
237
 
236
- self._num_bootstrap_hosts = len(collect_hosts(self.config['bootstrap_servers']))
237
-
238
238
  # Check Broker Version if not set explicitly
239
239
  if self.config['api_version'] is None:
240
240
  check_timeout = self.config['api_version_auto_timeout_ms'] / 1000
@@ -259,20 +259,6 @@ class KafkaClient(object):
259
259
  self._wake_r = None
260
260
  self._wake_w = None
261
261
 
262
- def _can_bootstrap(self):
263
- effective_failures = self._bootstrap_fails // self._num_bootstrap_hosts
264
- backoff_factor = 2 ** effective_failures
265
- backoff_ms = min(self.config['reconnect_backoff_ms'] * backoff_factor,
266
- self.config['reconnect_backoff_max_ms'])
267
-
268
- backoff_ms *= random.uniform(0.8, 1.2)
269
-
270
- next_at = self._last_bootstrap + backoff_ms / 1000.0
271
- now = time.time()
272
- if next_at > now:
273
- return False
274
- return True
275
-
276
262
  def _can_connect(self, node_id):
277
263
  if node_id not in self._conns:
278
264
  if self.cluster.broker_metadata(node_id):
@@ -1315,8 +1315,8 @@ class BrokerConnection(object):
1315
1315
  return version
1316
1316
 
1317
1317
  def __str__(self):
1318
- return "<BrokerConnection node_id=%s host=%s:%d %s [%s %s]>" % (
1319
- self.node_id, self.host, self.port, self.state,
1318
+ return "<BrokerConnection client_id=%s, node_id=%s host=%s:%d %s [%s %s]>" % (
1319
+ self.config['client_id'], self.node_id, self.host, self.port, self.state,
1320
1320
  AFI_NAMES[self._sock_afi], self._sock_addr)
1321
1321
 
1322
1322
 
@@ -371,7 +371,7 @@ class BaseCoordinator(object):
371
371
  while not self.coordinator_unknown():
372
372
  if not self._client.in_flight_request_count(self.coordinator_id):
373
373
  break
374
- self._client.poll()
374
+ self._client.poll(timeout_ms=200)
375
375
  else:
376
376
  continue
377
377
 
@@ -923,8 +923,6 @@ class HeartbeatThread(threading.Thread):
923
923
  if self.closed:
924
924
  return
925
925
  self.closed = True
926
- with self.coordinator._lock:
927
- self.coordinator._lock.notify()
928
926
 
929
927
  # Generally this should not happen - close() is triggered
930
928
  # by the coordinator. But in some cases GC may close the coordinator
@@ -932,6 +930,9 @@ class HeartbeatThread(threading.Thread):
932
930
  if threading.current_thread() == self:
933
931
  return
934
932
 
933
+ with self.coordinator._lock:
934
+ self.coordinator._lock.notify()
935
+
935
936
  if self.is_alive():
936
937
  self.join(self.coordinator.config['heartbeat_interval_ms'] / 1000)
937
938
  if self.is_alive():
@@ -0,0 +1 @@
1
+ __version__ = '2.0.5'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kafka-python
3
- Version: 2.0.4
3
+ Version: 2.0.5
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
@@ -41,7 +41,7 @@ Requires-Dist: pytest-mock; extra == "testing"
41
41
  Kafka Python client
42
42
  ------------------------
43
43
 
44
- .. image:: https://img.shields.io/badge/kafka-2.6%2C%202.5%2C%202.4%2C%202.3%2C%202.2%2C%202.1%2C%202.0%2C%201.1%2C%201.0%2C%200.11%2C%200.10%2C%200.9%2C%200.8-brightgreen.svg
44
+ .. image:: https://img.shields.io/badge/kafka-3.9--0.8-brightgreen.svg
45
45
  :target: https://kafka-python.readthedocs.io/en/master/compatibility.html
46
46
  .. image:: https://img.shields.io/pypi/pyversions/kafka-python.svg
47
47
  :target: https://pypi.python.org/pypi/kafka-python
@@ -168,7 +168,7 @@ def test_describe_consumer_group_exists(kafka_admin_client, kafka_consumer_facto
168
168
  stop[i] = Event()
169
169
  consumers[i] = kafka_consumer_factory(group_id=group_id)
170
170
  while not stop[i].is_set():
171
- consumers[i].poll(20)
171
+ consumers[i].poll(timeout_ms=200)
172
172
  consumers[i].close()
173
173
  consumers[i] = None
174
174
  stop[i] = None
@@ -183,6 +183,7 @@ def test_describe_consumer_group_exists(kafka_admin_client, kafka_consumer_facto
183
183
  try:
184
184
  timeout = time() + 35
185
185
  while True:
186
+ info('Checking consumers...')
186
187
  for c in range(num_consumers):
187
188
 
188
189
  # Verify all consumers have been created
@@ -212,9 +213,9 @@ def test_describe_consumer_group_exists(kafka_admin_client, kafka_consumer_facto
212
213
 
213
214
  if not rejoining and is_same_generation:
214
215
  break
215
- else:
216
- sleep(1)
217
216
  assert time() < timeout, "timeout waiting for assignments"
217
+ info('sleeping...')
218
+ sleep(1)
218
219
 
219
220
  info('Group stabilized; verifying assignment')
220
221
  output = kafka_admin_client.describe_consumer_groups(group_id_list)
@@ -236,6 +237,8 @@ def test_describe_consumer_group_exists(kafka_admin_client, kafka_consumer_facto
236
237
  for c in range(num_consumers):
237
238
  info('Stopping consumer %s', c)
238
239
  stop[c].set()
240
+ for c in range(num_consumers):
241
+ info('Waiting for consumer thread %s', c)
239
242
  threads[c].join()
240
243
  threads[c] = None
241
244
 
@@ -27,7 +27,7 @@ def test_kafka_version_infer(kafka_consumer_factory):
27
27
  @pytest.mark.skipif(not env_kafka_version(), reason="No KAFKA_VERSION set")
28
28
  def test_kafka_consumer(kafka_consumer_factory, send_messages):
29
29
  """Test KafkaConsumer"""
30
- consumer = kafka_consumer_factory(auto_offset_reset='earliest')
30
+ consumer = kafka_consumer_factory(auto_offset_reset='earliest', consumer_timeout_ms=2000)
31
31
  send_messages(range(0, 100), partition=0)
32
32
  send_messages(range(0, 100), partition=1)
33
33
  cnt = 0
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import uuid
3
+ import time
3
4
 
4
5
  import pytest
5
6
 
@@ -69,12 +70,17 @@ def test_client(request, sasl_kafka):
69
70
 
70
71
  client, = sasl_kafka.get_clients(1)
71
72
  request = MetadataRequest_v1(None)
72
- client.send(0, request)
73
- for _ in range(10):
74
- result = client.poll(timeout_ms=10000)
75
- if len(result) > 0:
76
- break
77
- else:
73
+ timeout_at = time.time() + 1
74
+ while not client.is_ready(0):
75
+ client.maybe_connect(0)
76
+ client.poll(timeout_ms=100)
77
+ if time.time() > timeout_at:
78
+ raise RuntimeError("Couldn't connect to node 0")
79
+ future = client.send(0, request)
80
+ client.poll(future=future, timeout_ms=10000)
81
+ if not future.is_done:
78
82
  raise RuntimeError("Couldn't fetch topic response from Broker.")
79
- result = result[0]
83
+ elif future.failed():
84
+ raise future.exception
85
+ result = future.value
80
86
  assert topic_name in [t[1] for t in result.topics]
@@ -28,12 +28,12 @@ def env_kafka_version():
28
28
  def assert_message_count(messages, num_messages):
29
29
  """Check that we received the expected number of messages with no duplicates."""
30
30
  # Make sure we got them all
31
- assert len(messages) == num_messages
31
+ assert len(messages) == num_messages, 'Expected %d messages, got %d' % (num_messages, len(messages))
32
32
  # Make sure there are no duplicates
33
33
  # Note: Currently duplicates are identified only using key/value. Other attributes like topic, partition, headers,
34
34
  # timestamp, etc are ignored... this could be changed if necessary, but will be more tolerant of dupes.
35
35
  unique_messages = {(m.key, m.value) for m in messages}
36
- assert len(unique_messages) == num_messages
36
+ assert len(unique_messages) == num_messages, 'Expected %d unique messages, got %d' % (num_messages, len(unique_messages))
37
37
 
38
38
 
39
39
  class Timer(object):
@@ -1 +0,0 @@
1
- __version__ = '2.0.4'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes