kafka-python 2.2.2__tar.gz → 2.2.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 (168) hide show
  1. {kafka_python-2.2.2 → kafka_python-2.2.3}/CHANGES.md +6 -0
  2. {kafka_python-2.2.2 → kafka_python-2.2.3}/PKG-INFO +1 -1
  3. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/cluster.py +24 -1
  4. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/conn.py +1 -27
  5. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/consumer/fetcher.py +38 -27
  6. kafka_python-2.2.3/kafka/version.py +1 -0
  7. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka_python.egg-info/PKG-INFO +1 -1
  8. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_cluster.py +60 -1
  9. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_conn.py +1 -49
  10. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_fetcher.py +1 -0
  11. kafka_python-2.2.2/kafka/version.py +0 -1
  12. {kafka_python-2.2.2 → kafka_python-2.2.3}/AUTHORS.md +0 -0
  13. {kafka_python-2.2.2 → kafka_python-2.2.3}/LICENSE +0 -0
  14. {kafka_python-2.2.2 → kafka_python-2.2.3}/MANIFEST.in +0 -0
  15. {kafka_python-2.2.2 → kafka_python-2.2.3}/README.rst +0 -0
  16. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/__init__.py +0 -0
  17. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/admin/__init__.py +0 -0
  18. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/admin/acl_resource.py +0 -0
  19. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/admin/client.py +0 -0
  20. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/admin/config_resource.py +0 -0
  21. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/admin/new_partitions.py +0 -0
  22. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/admin/new_topic.py +0 -0
  23. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/benchmarks/__init__.py +0 -0
  24. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/benchmarks/consumer_performance.py +0 -0
  25. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/benchmarks/load_example.py +0 -0
  26. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/benchmarks/producer_performance.py +0 -0
  27. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/benchmarks/record_batch_compose.py +0 -0
  28. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/benchmarks/record_batch_read.py +0 -0
  29. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/benchmarks/varint_speed.py +0 -0
  30. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/client_async.py +0 -0
  31. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/codec.py +0 -0
  32. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/consumer/__init__.py +0 -0
  33. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/consumer/group.py +0 -0
  34. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/consumer/subscription_state.py +0 -0
  35. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/__init__.py +0 -0
  36. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/__init__.py +0 -0
  37. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/abstract.py +0 -0
  38. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/range.py +0 -0
  39. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/roundrobin.py +0 -0
  40. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/sticky/__init__.py +0 -0
  41. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/sticky/partition_movements.py +0 -0
  42. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/sticky/sorted_set.py +0 -0
  43. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/assignors/sticky/sticky_assignor.py +0 -0
  44. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/base.py +0 -0
  45. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/consumer.py +0 -0
  46. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/heartbeat.py +0 -0
  47. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/coordinator/protocol.py +0 -0
  48. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/errors.py +0 -0
  49. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/future.py +0 -0
  50. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/__init__.py +0 -0
  51. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/compound_stat.py +0 -0
  52. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/dict_reporter.py +0 -0
  53. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/kafka_metric.py +0 -0
  54. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/measurable.py +0 -0
  55. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/measurable_stat.py +0 -0
  56. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/metric_config.py +0 -0
  57. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/metric_name.py +0 -0
  58. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/metrics.py +0 -0
  59. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/metrics_reporter.py +0 -0
  60. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/quota.py +0 -0
  61. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stat.py +0 -0
  62. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/__init__.py +0 -0
  63. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/avg.py +0 -0
  64. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/count.py +0 -0
  65. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/histogram.py +0 -0
  66. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/max_stat.py +0 -0
  67. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/min_stat.py +0 -0
  68. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/percentile.py +0 -0
  69. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/percentiles.py +0 -0
  70. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/rate.py +0 -0
  71. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/sampled_stat.py +0 -0
  72. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/sensor.py +0 -0
  73. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/metrics/stats/total.py +0 -0
  74. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/partitioner/__init__.py +0 -0
  75. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/partitioner/default.py +0 -0
  76. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/producer/__init__.py +0 -0
  77. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/producer/future.py +0 -0
  78. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/producer/kafka.py +0 -0
  79. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/producer/record_accumulator.py +0 -0
  80. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/producer/sender.py +0 -0
  81. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/producer/transaction_manager.py +0 -0
  82. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/__init__.py +0 -0
  83. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/abstract.py +0 -0
  84. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/add_offsets_to_txn.py +0 -0
  85. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/add_partitions_to_txn.py +0 -0
  86. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/admin.py +0 -0
  87. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/api.py +0 -0
  88. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/api_versions.py +0 -0
  89. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/broker_api_versions.py +0 -0
  90. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/commit.py +0 -0
  91. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/end_txn.py +0 -0
  92. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/fetch.py +0 -0
  93. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/find_coordinator.py +0 -0
  94. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/frame.py +0 -0
  95. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/group.py +0 -0
  96. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/init_producer_id.py +0 -0
  97. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/list_offsets.py +0 -0
  98. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/message.py +0 -0
  99. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/metadata.py +0 -0
  100. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/offset_for_leader_epoch.py +0 -0
  101. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/parser.py +0 -0
  102. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/pickle.py +0 -0
  103. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/produce.py +0 -0
  104. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/sasl_authenticate.py +0 -0
  105. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/sasl_handshake.py +0 -0
  106. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/struct.py +0 -0
  107. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/txn_offset_commit.py +0 -0
  108. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/protocol/types.py +0 -0
  109. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/record/__init__.py +0 -0
  110. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/record/_crc32c.py +0 -0
  111. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/record/abc.py +0 -0
  112. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/record/default_records.py +0 -0
  113. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/record/legacy_records.py +0 -0
  114. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/record/memory_records.py +0 -0
  115. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/record/util.py +0 -0
  116. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/__init__.py +0 -0
  117. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/abc.py +0 -0
  118. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/gssapi.py +0 -0
  119. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/msk.py +0 -0
  120. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/oauth.py +0 -0
  121. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/plain.py +0 -0
  122. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/scram.py +0 -0
  123. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/sasl/sspi.py +0 -0
  124. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/serializer/__init__.py +0 -0
  125. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/serializer/abstract.py +0 -0
  126. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/socks5_wrapper.py +0 -0
  127. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/structs.py +0 -0
  128. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/util.py +0 -0
  129. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/vendor/__init__.py +0 -0
  130. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/vendor/enum34.py +0 -0
  131. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/vendor/selectors34.py +0 -0
  132. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/vendor/six.py +0 -0
  133. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka/vendor/socketpair.py +0 -0
  134. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka_python.egg-info/SOURCES.txt +0 -0
  135. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka_python.egg-info/dependency_links.txt +0 -0
  136. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka_python.egg-info/requires.txt +0 -0
  137. {kafka_python-2.2.2 → kafka_python-2.2.3}/kafka_python.egg-info/top_level.txt +0 -0
  138. {kafka_python-2.2.2 → kafka_python-2.2.3}/pyproject.toml +0 -0
  139. {kafka_python-2.2.2 → kafka_python-2.2.3}/setup.cfg +0 -0
  140. {kafka_python-2.2.2 → kafka_python-2.2.3}/setup.py +0 -0
  141. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/__init__.py +0 -0
  142. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/conftest.py +0 -0
  143. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/fixtures.py +0 -0
  144. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/test_admin_integration.py +0 -0
  145. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/test_consumer_group.py +0 -0
  146. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/test_consumer_integration.py +0 -0
  147. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/test_producer_integration.py +0 -0
  148. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/integration/test_sasl_integration.py +0 -0
  149. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_acl_comparisons.py +0 -0
  150. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_admin.py +0 -0
  151. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_api_object_implementation.py +0 -0
  152. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_assignors.py +0 -0
  153. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_client_async.py +0 -0
  154. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_codec.py +0 -0
  155. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_consumer.py +0 -0
  156. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_coordinator.py +0 -0
  157. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_metrics.py +0 -0
  158. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_object_conversion.py +0 -0
  159. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_package.py +0 -0
  160. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_partition_movements.py +0 -0
  161. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_partitioner.py +0 -0
  162. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_producer.py +0 -0
  163. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_protocol.py +0 -0
  164. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_record_accumulator.py +0 -0
  165. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_sender.py +0 -0
  166. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_subscription_state.py +0 -0
  167. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/test_util.py +0 -0
  168. {kafka_python-2.2.2 → kafka_python-2.2.3}/test/testutil.py +0 -0
@@ -1,3 +1,9 @@
1
+ # 2.2.3 (May 1, 2025)
2
+
3
+ Fixes
4
+ * Ignore leading SECURITY_PROTOCOL:// in bootstrap_servers (#2608)
5
+ * Only create fetch requests for ready nodes (#2607)
6
+
1
7
  # 2.2.2 (Apr 30, 2025)
2
8
 
3
9
  Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kafka-python
3
- Version: 2.2.2
3
+ Version: 2.2.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
@@ -3,13 +3,15 @@ from __future__ import absolute_import
3
3
  import collections
4
4
  import copy
5
5
  import logging
6
+ import random
7
+ import re
6
8
  import threading
7
9
  import time
8
10
 
9
11
  from kafka.vendor import six
10
12
 
11
13
  from kafka import errors as Errors
12
- from kafka.conn import collect_hosts
14
+ from kafka.conn import get_ip_port_afi
13
15
  from kafka.future import Future
14
16
  from kafka.structs import BrokerMetadata, PartitionMetadata, TopicPartition
15
17
 
@@ -422,3 +424,24 @@ class ClusterMetadata(object):
422
424
  def __str__(self):
423
425
  return 'ClusterMetadata(brokers: %d, topics: %d, coordinators: %d)' % \
424
426
  (len(self._brokers), len(self._partitions), len(self._coordinators))
427
+
428
+
429
+ def collect_hosts(hosts, randomize=True):
430
+ """
431
+ Collects a comma-separated set of hosts (host:port) and optionally
432
+ randomize the returned list.
433
+ """
434
+
435
+ if isinstance(hosts, six.string_types):
436
+ hosts = hosts.strip().split(',')
437
+
438
+ result = []
439
+ for host_port in hosts:
440
+ # ignore leading SECURITY_PROTOCOL:// to mimic java client
441
+ host_port = re.sub('^.*://', '', host_port)
442
+ host, port, afi = get_ip_port_afi(host_port)
443
+ result.append((host, port, afi))
444
+
445
+ if randomize:
446
+ random.shuffle(result)
447
+ return result
@@ -4,7 +4,7 @@ import copy
4
4
  import errno
5
5
  import io
6
6
  import logging
7
- from random import shuffle, uniform
7
+ from random import uniform
8
8
 
9
9
  # selectors in stdlib as of py3.4
10
10
  try:
@@ -1496,32 +1496,6 @@ def get_ip_port_afi(host_and_port_str):
1496
1496
  return host, port, af
1497
1497
 
1498
1498
 
1499
- def collect_hosts(hosts, randomize=True):
1500
- """
1501
- Collects a comma-separated set of hosts (host:port) and optionally
1502
- randomize the returned list.
1503
- """
1504
-
1505
- if isinstance(hosts, six.string_types):
1506
- hosts = hosts.strip().split(',')
1507
-
1508
- result = []
1509
- afi = socket.AF_INET
1510
- for host_port in hosts:
1511
-
1512
- host, port, afi = get_ip_port_afi(host_port)
1513
-
1514
- if port < 0:
1515
- port = DEFAULT_KAFKA_PORT
1516
-
1517
- result.append((host, port, afi))
1518
-
1519
- if randomize:
1520
- shuffle(result)
1521
-
1522
- return result
1523
-
1524
-
1525
1499
  def is_inet_4_or_6(gai):
1526
1500
  """Given a getaddrinfo struct, return True iff ipv4 or ipv6"""
1527
1501
  return gai[0] in (socket.AF_INET, socket.AF_INET6)
@@ -153,6 +153,7 @@ class Fetcher(six.Iterator):
153
153
  future = self._client.send(node_id, request, wakeup=False)
154
154
  future.add_callback(self._handle_fetch_response, node_id, fetch_offsets, time.time())
155
155
  future.add_errback(self._handle_fetch_error, node_id)
156
+ future.add_both(self._clear_pending_fetch_request, node_id)
156
157
  futures.append(future)
157
158
  self._fetch_futures.extend(futures)
158
159
  self._clean_done_fetch_futures()
@@ -643,36 +644,42 @@ class Fetcher(six.Iterator):
643
644
  log.debug("Skipping fetch for partition %s because node %s is throttled",
644
645
  partition, node_id)
645
646
 
647
+ elif not self._client.ready(node_id):
648
+ # Until we support send request queues, any attempt to send to a not-ready node will be
649
+ # immediately failed with NodeNotReadyError.
650
+ log.debug("Skipping fetch for partition %s because connection to leader node is not ready yet")
651
+
646
652
  elif node_id in self._nodes_with_pending_fetch_requests:
647
653
  log.debug("Skipping fetch for partition %s because there is a pending fetch request to node %s",
648
654
  partition, node_id)
649
- continue
650
655
 
651
- if version < 5:
652
- partition_info = (
653
- partition.partition,
654
- position.offset,
655
- self.config['max_partition_fetch_bytes']
656
- )
657
- elif version <= 8:
658
- partition_info = (
659
- partition.partition,
660
- position.offset,
661
- -1, # log_start_offset is used internally by brokers / replicas only
662
- self.config['max_partition_fetch_bytes'],
663
- )
664
656
  else:
665
- partition_info = (
666
- partition.partition,
667
- position.leader_epoch,
668
- position.offset,
669
- -1, # log_start_offset is used internally by brokers / replicas only
670
- self.config['max_partition_fetch_bytes'],
671
- )
672
-
673
- fetchable[node_id][partition] = partition_info
674
- log.debug("Adding fetch request for partition %s at offset %d",
675
- partition, position.offset)
657
+ # Leader is connected and does not have a pending fetch request
658
+ if version < 5:
659
+ partition_info = (
660
+ partition.partition,
661
+ position.offset,
662
+ self.config['max_partition_fetch_bytes']
663
+ )
664
+ elif version <= 8:
665
+ partition_info = (
666
+ partition.partition,
667
+ position.offset,
668
+ -1, # log_start_offset is used internally by brokers / replicas only
669
+ self.config['max_partition_fetch_bytes'],
670
+ )
671
+ else:
672
+ partition_info = (
673
+ partition.partition,
674
+ position.leader_epoch,
675
+ position.offset,
676
+ -1, # log_start_offset is used internally by brokers / replicas only
677
+ self.config['max_partition_fetch_bytes'],
678
+ )
679
+
680
+ fetchable[node_id][partition] = partition_info
681
+ log.debug("Adding fetch request for partition %s at offset %d",
682
+ partition, position.offset)
676
683
 
677
684
  requests = {}
678
685
  for node_id, next_partitions in six.iteritems(fetchable):
@@ -761,14 +768,18 @@ class Fetcher(six.Iterator):
761
768
 
762
769
  if self._sensors:
763
770
  self._sensors.fetch_latency.record((time.time() - send_time) * 1000)
764
- self._nodes_with_pending_fetch_requests.remove(node_id)
765
771
 
766
772
  def _handle_fetch_error(self, node_id, exception):
767
773
  level = logging.INFO if isinstance(exception, Errors.Cancelled) else logging.ERROR
768
774
  log.log(level, 'Fetch to node %s failed: %s', node_id, exception)
769
775
  if node_id in self._session_handlers:
770
776
  self._session_handlers[node_id].handle_error(exception)
771
- self._nodes_with_pending_fetch_requests.remove(node_id)
777
+
778
+ def _clear_pending_fetch_request(self, node_id, _):
779
+ try:
780
+ self._nodes_with_pending_fetch_requests.remove(node_id)
781
+ except KeyError:
782
+ pass
772
783
 
773
784
  def _parse_fetched_data(self, completed_fetch):
774
785
  tp = completed_fetch.topic_partition
@@ -0,0 +1 @@
1
+ __version__ = '2.2.3'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kafka-python
3
- Version: 2.2.2
3
+ Version: 2.2.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
@@ -1,7 +1,9 @@
1
1
  # pylint: skip-file
2
2
  from __future__ import absolute_import
3
3
 
4
- from kafka.cluster import ClusterMetadata
4
+ import socket
5
+
6
+ from kafka.cluster import ClusterMetadata, collect_hosts
5
7
  from kafka.protocol.metadata import MetadataResponse
6
8
 
7
9
 
@@ -132,3 +134,60 @@ def test_metadata_v7():
132
134
  assert cluster.cluster_id == 'cluster-foo'
133
135
  assert cluster._partitions['topic-1'][0].offline_replicas == [12]
134
136
  assert cluster._partitions['topic-1'][0].leader_epoch == 0
137
+
138
+
139
+ def test_collect_hosts__happy_path():
140
+ hosts = "127.0.0.1:1234,127.0.0.1"
141
+ results = collect_hosts(hosts)
142
+ assert set(results) == set([
143
+ ('127.0.0.1', 1234, socket.AF_INET),
144
+ ('127.0.0.1', 9092, socket.AF_INET),
145
+ ])
146
+
147
+
148
+ def test_collect_hosts__ipv6():
149
+ hosts = "[localhost]:1234,[2001:1000:2000::1],[2001:1000:2000::1]:1234"
150
+ results = collect_hosts(hosts)
151
+ assert set(results) == set([
152
+ ('localhost', 1234, socket.AF_INET6),
153
+ ('2001:1000:2000::1', 9092, socket.AF_INET6),
154
+ ('2001:1000:2000::1', 1234, socket.AF_INET6),
155
+ ])
156
+
157
+
158
+ def test_collect_hosts__string_list():
159
+ hosts = [
160
+ 'localhost:1234',
161
+ 'localhost',
162
+ '[localhost]',
163
+ '2001::1',
164
+ '[2001::1]',
165
+ '[2001::1]:1234',
166
+ ]
167
+ results = collect_hosts(hosts)
168
+ assert set(results) == set([
169
+ ('localhost', 1234, socket.AF_UNSPEC),
170
+ ('localhost', 9092, socket.AF_UNSPEC),
171
+ ('localhost', 9092, socket.AF_INET6),
172
+ ('2001::1', 9092, socket.AF_INET6),
173
+ ('2001::1', 9092, socket.AF_INET6),
174
+ ('2001::1', 1234, socket.AF_INET6),
175
+ ])
176
+
177
+
178
+ def test_collect_hosts__with_spaces():
179
+ hosts = "localhost:1234, localhost"
180
+ results = collect_hosts(hosts)
181
+ assert set(results) == set([
182
+ ('localhost', 1234, socket.AF_UNSPEC),
183
+ ('localhost', 9092, socket.AF_UNSPEC),
184
+ ])
185
+
186
+
187
+ def test_collect_hosts__protocol():
188
+ hosts = "SASL_SSL://foo.bar:1234,SASL_SSL://fizz.buzz:5678"
189
+ results = collect_hosts(hosts)
190
+ assert set(results) == set([
191
+ ('foo.bar', 1234, socket.AF_UNSPEC),
192
+ ('fizz.buzz', 5678, socket.AF_UNSPEC),
193
+ ])
@@ -10,7 +10,7 @@ except ImportError:
10
10
  import mock
11
11
  import pytest
12
12
 
13
- from kafka.conn import BrokerConnection, ConnectionStates, collect_hosts
13
+ from kafka.conn import BrokerConnection, ConnectionStates
14
14
  from kafka.future import Future
15
15
  from kafka.protocol.api import RequestHeader
16
16
  from kafka.protocol.group import HeartbeatResponse
@@ -280,54 +280,6 @@ def test_close(conn):
280
280
  pass # TODO
281
281
 
282
282
 
283
- def test_collect_hosts__happy_path():
284
- hosts = "127.0.0.1:1234,127.0.0.1"
285
- results = collect_hosts(hosts)
286
- assert set(results) == set([
287
- ('127.0.0.1', 1234, socket.AF_INET),
288
- ('127.0.0.1', 9092, socket.AF_INET),
289
- ])
290
-
291
-
292
- def test_collect_hosts__ipv6():
293
- hosts = "[localhost]:1234,[2001:1000:2000::1],[2001:1000:2000::1]:1234"
294
- results = collect_hosts(hosts)
295
- assert set(results) == set([
296
- ('localhost', 1234, socket.AF_INET6),
297
- ('2001:1000:2000::1', 9092, socket.AF_INET6),
298
- ('2001:1000:2000::1', 1234, socket.AF_INET6),
299
- ])
300
-
301
-
302
- def test_collect_hosts__string_list():
303
- hosts = [
304
- 'localhost:1234',
305
- 'localhost',
306
- '[localhost]',
307
- '2001::1',
308
- '[2001::1]',
309
- '[2001::1]:1234',
310
- ]
311
- results = collect_hosts(hosts)
312
- assert set(results) == set([
313
- ('localhost', 1234, socket.AF_UNSPEC),
314
- ('localhost', 9092, socket.AF_UNSPEC),
315
- ('localhost', 9092, socket.AF_INET6),
316
- ('2001::1', 9092, socket.AF_INET6),
317
- ('2001::1', 9092, socket.AF_INET6),
318
- ('2001::1', 1234, socket.AF_INET6),
319
- ])
320
-
321
-
322
- def test_collect_hosts__with_spaces():
323
- hosts = "localhost:1234, localhost"
324
- results = collect_hosts(hosts)
325
- assert set(results) == set([
326
- ('localhost', 1234, socket.AF_UNSPEC),
327
- ('localhost', 9092, socket.AF_UNSPEC),
328
- ])
329
-
330
-
331
283
  def test_lookup_on_connect():
332
284
  hostname = 'example.org'
333
285
  port = 9092
@@ -103,6 +103,7 @@ def test_create_fetch_requests(fetcher, mocker, api_version, fetch_version):
103
103
  fetcher._client._api_versions = BROKER_API_VERSIONS[api_version]
104
104
  mocker.patch.object(fetcher._client.cluster, "leader_for_partition", return_value=0)
105
105
  mocker.patch.object(fetcher._client.cluster, "leader_epoch_for_partition", return_value=0)
106
+ mocker.patch.object(fetcher._client, "ready", return_value=True)
106
107
  by_node = fetcher._create_fetch_requests()
107
108
  requests_and_offsets = by_node.values()
108
109
  assert set([r.API_VERSION for (r, _offsets) in requests_and_offsets]) == set([fetch_version])
@@ -1 +0,0 @@
1
- __version__ = '2.2.2'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes