buz 2.19.0__py3-none-any.whl → 2.21.0rc1__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.
buz/event/event.py CHANGED
@@ -8,3 +8,6 @@ class Event(Message):
8
8
  @classmethod
9
9
  def fqn(cls) -> str:
10
10
  return f"event.{super().fqn()}"
11
+
12
+ def partition_key(self) -> str:
13
+ return str(self.id)
@@ -73,6 +73,7 @@ class AsyncBuzKafkaEventBus(AsyncEventBus):
73
73
  message=event,
74
74
  headers=headers,
75
75
  topic=topic,
76
+ partition_key=event.partition_key(),
76
77
  )
77
78
  except Exception as exc:
78
79
  raise EventNotPublishedException(event) from exc
@@ -74,6 +74,7 @@ class BuzKafkaEventBus(EventBus):
74
74
  message=event,
75
75
  headers=headers,
76
76
  topic=topic,
77
+ partition_key=event.partition_key(),
77
78
  )
78
79
  except Exception as exc:
79
80
  raise EventNotPublishedException(event) from exc
@@ -11,10 +11,12 @@ class EventToOutboxRecordTranslator:
11
11
  payload.pop("id")
12
12
  payload.pop("created_at")
13
13
  payload.pop("metadata")
14
+ partition_key = event.partition_key()
14
15
  return OutboxRecord(
15
16
  event_id=UUID(event.id),
16
17
  event_fqn=event.fqn(),
17
18
  created_at=event.parsed_created_at(),
18
19
  event_payload=payload,
19
20
  event_metadata=dict(event.metadata),
21
+ partition_key=partition_key,
20
22
  )
@@ -1,7 +1,7 @@
1
+ from dataclasses import dataclass, fields
1
2
  from datetime import datetime
2
- from typing import Optional, Any, ClassVar
3
+ from typing import Any, ClassVar
3
4
  from uuid import UUID
4
- from dataclasses import dataclass, fields
5
5
 
6
6
 
7
7
  @dataclass
@@ -12,10 +12,11 @@ class OutboxRecord: # type: ignore[misc]
12
12
  event_fqn: str
13
13
  event_payload: dict[str, Any] # type: ignore[misc]
14
14
  created_at: datetime
15
- event_metadata: Optional[dict[str, Any]] = None
16
- delivered_at: Optional[datetime] = None
15
+ event_metadata: dict[str, Any] | None = None
16
+ delivered_at: datetime | None = None
17
17
  delivery_errors: int = 0
18
- delivery_paused_at: Optional[datetime] = None
18
+ delivery_paused_at: datetime | None = None
19
+ partition_key: str | None = None
19
20
 
20
21
  def parsed_created_at(self) -> str:
21
22
  return self.created_at.strftime(self.DATE_TIME_FORMAT)
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class ConsumerGroupNotFoundException(Exception):
5
+ def __init__(self, consumer_group: str) -> None:
6
+ super().__init__(f'The consumer group "{consumer_group}" has not been found')
@@ -65,3 +65,11 @@ class KafkaAdminClient(ConnectionManager, ABC):
65
65
  new_number_of_partitions: int,
66
66
  ) -> None:
67
67
  pass
68
+
69
+ @abstractmethod
70
+ def get_consumer_group_offsets(self, *, consumer_group: str, topic: str) -> dict[int, int]:
71
+ """
72
+ Get the committed offsets for a consumer group on a specific topic.
73
+ Returns a dictionary mapping partition numbers to their committed offsets.
74
+ """
75
+ pass
@@ -12,6 +12,7 @@ from kafka.admin.new_partitions import NewPartitions
12
12
  from kafka.errors import TopicAlreadyExistsError
13
13
  from kafka.structs import TopicPartition, OffsetAndTimestamp
14
14
 
15
+ from buz.kafka.domain.exceptions.consumer_group_not_found_exception import ConsumerGroupNotFoundException
15
16
  from buz.kafka.domain.exceptions.not_all_partition_assigned_exception import NotAllPartitionAssignedException
16
17
  from buz.kafka.domain.exceptions.not_valid_partition_number_exception import NotValidPartitionNumberException
17
18
  from buz.kafka.domain.exceptions.topic_already_created_exception import KafkaTopicsAlreadyCreatedException
@@ -400,3 +401,28 @@ class KafkaPythonAdminClient(KafkaAdminClient):
400
401
  related_consumer_groups.add(consumer_group)
401
402
 
402
403
  return related_consumer_groups
404
+
405
+ def get_consumer_group_offsets(self, *, consumer_group: str, topic: str) -> dict[int, int]:
406
+ self._logger.info(f'Getting consumer group offsets for group "{consumer_group}" on topic "{topic}"')
407
+
408
+ if not self.is_topic_created(topic):
409
+ raise TopicNotFoundException(topic_name=topic)
410
+
411
+ cluster_consumer_groups = self.get_cluster_consumer_groups()
412
+ if consumer_group not in cluster_consumer_groups:
413
+ raise ConsumerGroupNotFoundException(consumer_group=consumer_group)
414
+
415
+ partitions = self.get_number_of_partitions(topic)
416
+ topic_partitions = [TopicPartition(topic=topic, partition=partition) for partition in range(partitions)]
417
+
418
+ offsets_response = self._get_kafka_admin().list_consumer_group_offsets(
419
+ consumer_group, partitions=topic_partitions
420
+ )
421
+
422
+ # Build the result dictionary, filtering out partitions with no committed offset (-1)
423
+ result: dict[int, int] = {}
424
+ for topic_partition, offset_and_metadata in offsets_response.items():
425
+ if offset_and_metadata.offset >= 0:
426
+ result[topic_partition.partition] = offset_and_metadata.offset
427
+
428
+ return result
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Generic, Optional, TypeVar, cast
3
+ from typing import Generic, TypeVar, cast
4
4
 
5
5
  from kafka import KafkaProducer as KafkaPythonLibraryProducer
6
6
  from kafka.producer.future import FutureRecordMetadata
@@ -15,7 +15,7 @@ T = TypeVar("T")
15
15
 
16
16
 
17
17
  class KafkaPythonProducer(KafkaProducer, Generic[T]):
18
- __kafka_producer: Optional[KafkaPythonLibraryProducer] = None
18
+ __kafka_producer: KafkaPythonLibraryProducer | None = None
19
19
  __SEND_TIMEOUT_SECONDS = 5
20
20
 
21
21
  def __init__(
@@ -25,7 +25,7 @@ class KafkaPythonProducer(KafkaProducer, Generic[T]):
25
25
  byte_serializer: ByteSerializer[T],
26
26
  retries: int = 0,
27
27
  retry_backoff_ms: int = 100,
28
- compression_type: Optional[KafkaSupportedCompressionType] = None,
28
+ compression_type: KafkaSupportedCompressionType | None = None,
29
29
  ) -> None:
30
30
  self.__connection_config = connection_config
31
31
  self.__byte_serializer = byte_serializer
@@ -70,8 +70,8 @@ class KafkaPythonProducer(KafkaProducer, Generic[T]):
70
70
  *,
71
71
  topic: str,
72
72
  message: T,
73
- partition_key: Optional[str] = None,
74
- headers: Optional[dict[str, str]] = None,
73
+ partition_key: str | None = None,
74
+ headers: dict[str, str] | None = None,
75
75
  ) -> None:
76
76
  serialized_headers = self.__header_serializer.serialize(headers) if headers is not None else None
77
77
  kafka_producer = self._get_kafka_producer()
@@ -82,7 +82,7 @@ class KafkaPythonProducer(KafkaProducer, Generic[T]):
82
82
  topic=topic,
83
83
  value=self.__byte_serializer.serialize(message),
84
84
  headers=serialized_headers,
85
- key=partition_key,
85
+ key=partition_key.encode("utf-8") if partition_key is not None else None,
86
86
  ),
87
87
  )
88
88
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: buz
3
- Version: 2.19.0
3
+ Version: 2.21.0rc1
4
4
  Summary: Buz is a set of light, simple and extensible implementations of event, command and query buses.
5
5
  License: MIT
6
6
  Author: Luis Pintado Lozano
@@ -36,7 +36,7 @@ buz/event/dead_letter_queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
36
36
  buz/event/dead_letter_queue/dlq_criteria.py,sha256=hxcV-BMayKTEc5suEfQZhEYkc14H7kZo_4YfDzcJTTY,290
37
37
  buz/event/dead_letter_queue/dlq_record.py,sha256=wEa9CdWkHmxHQVwoHFjWeEU6sjNOi7X8dLr1E-gVmDc,1341
38
38
  buz/event/dead_letter_queue/dlq_repository.py,sha256=8XsXSfO2OzEq4qfQ_v0E0OExintDYI1g55Qu3PtoxKI,630
39
- buz/event/event.py,sha256=x1MCBydn3qk3AkvamsAwCG-nfxR9OyP4l1UNXtnhUwU,189
39
+ buz/event/event.py,sha256=Fz23ehyHyAsS5IN3mEElzArsAefs0GNz-vLIu3bpquk,254
40
40
  buz/event/event_bus.py,sha256=QnvZD_bKsv628TZ58s5uLntLJCA9Ngir_YHOwb4_UaU,289
41
41
  buz/event/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  buz/event/exceptions/event_not_published_exception.py,sha256=gGEiRFGdKIS-VTKg8SN54vSS10WeSkgBhlO2Gpcll_0,215
@@ -46,11 +46,11 @@ buz/event/exceptions/term_signal_interruption_exception.py,sha256=RkRRF0v_K9Hg48
46
46
  buz/event/exceptions/worker_execution_exception.py,sha256=6mgztvXOCG_9VZ_Jptkk72kZtNWQ2CPuQ3TjXEWFE14,123
47
47
  buz/event/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
48
  buz/event/infrastructure/buz_kafka/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- buz/event/infrastructure/buz_kafka/async_buz_kafka_event_bus.py,sha256=SyLblUVlwWOaNfZzK7vL6Ee4m-85vZVCH0rjOgqVAww,4913
49
+ buz/event/infrastructure/buz_kafka/async_buz_kafka_event_bus.py,sha256=kxqXCf80LgQiB464jA9TENejT5FlGT7mRZsy0BsPctk,4966
50
50
  buz/event/infrastructure/buz_kafka/base_buz_aiokafka_async_consumer.py,sha256=7ZhaKaFXBpD3HVkuQMpAJvY8lfy7__1wxftLIwCmnMQ,21284
51
51
  buz/event/infrastructure/buz_kafka/buz_aiokafka_async_consumer.py,sha256=GmmuAZboDkrpNOLF8cE_F0t4I7ZnMiGsiGw4SYIvKGc,7303
52
52
  buz/event/infrastructure/buz_kafka/buz_aiokafka_multi_threaded_consumer.py,sha256=ZRLRoBRomqrXAiePSMn4gePF59AWPn6VQpQui1UVnyM,7246
53
- buz/event/infrastructure/buz_kafka/buz_kafka_event_bus.py,sha256=ymRSvcYVgbVCPgHN6rMBVBHQ5heCSwCDl6EffyqGVX8,4601
53
+ buz/event/infrastructure/buz_kafka/buz_kafka_event_bus.py,sha256=_CLD4gU7KikVTEydSMtPE3meFhlOtAx0km4cS6Q3tno,4654
54
54
  buz/event/infrastructure/buz_kafka/consume_strategy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  buz/event/infrastructure/buz_kafka/consume_strategy/consume_strategy.py,sha256=RqlXe5W2S6rH3FTr--tcxzFJTAVLb-Dhl7m6qjgNz2M,331
56
56
  buz/event/infrastructure/buz_kafka/consume_strategy/kafka_on_fail_strategy.py,sha256=elNeyTubDuhHsLlTtDA1Nqz2hZe12PUcO9kz8upPby8,136
@@ -123,14 +123,14 @@ buz/event/sync/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
123
123
  buz/event/sync/models/sync_delivery_context.py,sha256=LHjrS6gV-19NEKwtAXVmefjPd-Dsp_Ym8RZb84T3lm8,190
124
124
  buz/event/sync/sync_event_bus.py,sha256=NUtuHRsSW4f2T44qGKy4Y4r8VukSwva4fsD0TwfisX8,1931
125
125
  buz/event/transactional_outbox/__init__.py,sha256=k8ZBWCi12pWKXchHfgW_Raw4sVR8XkBLuPNW9jB9X2k,1381
126
- buz/event/transactional_outbox/event_to_outbox_record_translator.py,sha256=d20JOeKIrCcpPEV66TzWiQmYqoyZGyL7J1ys0dUfHFs,615
126
+ buz/event/transactional_outbox/event_to_outbox_record_translator.py,sha256=xFovyBqimTqGKfOubanJCmvMKVi4Vzhsx6Jh00A1T5A,702
127
127
  buz/event/transactional_outbox/fqn_to_event_mapper.py,sha256=ujcq6CfYqRJtM8f3SEEltbWN0Ru7NM5JfrbNdh4nvhQ,773
128
128
  buz/event/transactional_outbox/outbox_criteria/__init__.py,sha256=_9YjtbyYdqvDKEAwSQUyOn46Fc2pSNzTl2I7AqisEoc,594
129
129
  buz/event/transactional_outbox/outbox_criteria/deliverable_records_outbox_criteria_factory.py,sha256=vnaf6OPBAiw78RJZ8iOtaISbvuDIj4gN31aR-5k3BL8,347
130
130
  buz/event/transactional_outbox/outbox_criteria/outbox_criteria.py,sha256=HlD7tt3V-qVdPrq4H0idvyDnP7ncD0ZYQJ_XkwLNg-o,695
131
131
  buz/event/transactional_outbox/outbox_criteria/outbox_criteria_factory.py,sha256=XvqOfd7coi2veXyAvAthIBYRxS0Gpfb70nnkq9igvVM,220
132
132
  buz/event/transactional_outbox/outbox_criteria/outbox_sorting_criteria.py,sha256=-ckCLHvHlkuabI0TZ4Tw-qAQhU1hFLAgCOwuvIs_vpI,89
133
- buz/event/transactional_outbox/outbox_record.py,sha256=0lK9cG0wmP-t9JD8erxtcFFH9VoqXclRox3VIp-iSo8,1203
133
+ buz/event/transactional_outbox/outbox_record.py,sha256=PKeYJBBweFv9W76BGWOLDGS5YHjJ3gudm3qNpeqUopw,1221
134
134
  buz/event/transactional_outbox/outbox_record_finder/__init__.py,sha256=HdW8GTIaxYExlUWeXugFmkRDyHcBiOlAVwh17dpPOfQ,344
135
135
  buz/event/transactional_outbox/outbox_record_finder/outbox_record_stream_finder.py,sha256=RjCetETAggQ5arXI4Terpyvh7xfz0eXXZon2-C1gXgw,239
136
136
  buz/event/transactional_outbox/outbox_record_finder/polling_outbox_record_stream_finder.py,sha256=qdCsqPQaS6cJ7li0veA2bY17iUG7fd84MzIywG7cPHw,1078
@@ -148,6 +148,7 @@ buz/event/worker.py,sha256=BL9TXB_kyr0Avql9fIcFm3CDNnXPvZB6O6BxVwjtCdA,942
148
148
  buz/handler.py,sha256=W6jSTo5BNV9u9QKBaEMhLIa3tgQocd6oYEJf5K4EfEU,358
149
149
  buz/kafka/__init__.py,sha256=R3fcyET-SNEAvk_XlBQbHIbQVb63Qiz6lVrif3nDhNU,3435
150
150
  buz/kafka/domain/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
151
+ buz/kafka/domain/exceptions/consumer_group_not_found_exception.py,sha256=8My8lM7DZ7JARfHY1MmuW5BWOB1YadiKhnc3cSTPlaI,225
151
152
  buz/kafka/domain/exceptions/not_all_partition_assigned_exception.py,sha256=1Ky6gDh_baD6cGB0MBnjbkkLcw2zQU_kFXPpDZn56z0,400
152
153
  buz/kafka/domain/exceptions/not_valid_kafka_message_exception.py,sha256=Dn6I_-eGQnOuu5WW24oKGOdKOu4EdM8ByH3DLAbz5SY,57
153
154
  buz/kafka/domain/exceptions/not_valid_partition_number_exception.py,sha256=YZyGbblHk6ON9sBtjRQTDa-nC88i4oe14_VSO8vSTm0,337
@@ -168,7 +169,7 @@ buz/kafka/domain/models/kafka_supported_sasl_mechanisms.py,sha256=ASyDaFgseQRcUJ
168
169
  buz/kafka/domain/models/kafka_supported_security_protocols.py,sha256=ffY2-9sOj4XIkJTSQVkqeOb4KnuqEYXISDarfDN8r9Q,161
169
170
  buz/kafka/domain/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
170
171
  buz/kafka/domain/services/async_kafka_producer.py,sha256=gSq3WwEVux_gp3EKDAMN1WsM027uklB58E-WnKpyhPs,533
171
- buz/kafka/domain/services/kafka_admin_client.py,sha256=Kh_w-qWEY8rsrlYjnJT1FrJLVZrO3l2LeRAWFyc_nOg,1558
172
+ buz/kafka/domain/services/kafka_admin_client.py,sha256=gjLs7BZwRjBd6qEoL2bNUdpHGPkh51dw1-kmC6I2YHw,1871
172
173
  buz/kafka/domain/services/kafka_admin_test_client.py,sha256=91l_vFIo1yhJLQQCC_OmeXZ5F429zP7Hx5g4FNllpfE,1625
173
174
  buz/kafka/domain/services/kafka_producer.py,sha256=8bLTV328orrPHcARzkc6no4vyJzrArVtCsjmSRXDjos,506
174
175
  buz/kafka/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -198,9 +199,9 @@ buz/kafka/infrastructure/interfaces/async_connection_manager.py,sha256=JbaLu5UVV
198
199
  buz/kafka/infrastructure/interfaces/connection_manager.py,sha256=EWnvShJHOg8QYe6a3ma0urjKjmVMDBi7q8T2cv_i_MQ,200
199
200
  buz/kafka/infrastructure/kafka_python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
200
201
  buz/kafka/infrastructure/kafka_python/exception/consumer_interrupted_exception.py,sha256=fqhgV7HILdVdv-p1CsOIaaESKY2ZXBtRGYbrVSdPLg0,164
201
- buz/kafka/infrastructure/kafka_python/kafka_python_admin_client.py,sha256=_zTOY3ihiXESUlDj0SECEvixt9MMny0xBGzFPix0ZYM,16241
202
+ buz/kafka/infrastructure/kafka_python/kafka_python_admin_client.py,sha256=FRO_zmGdOkiu5wFYmO7v-hLASxR0XT45uDqjHgmg_F4,17544
202
203
  buz/kafka/infrastructure/kafka_python/kafka_python_admin_test_client.py,sha256=5xP23dQ7FDuy7dIWNw39C3bMVmaUj9ZQhEEJISRv9ec,2986
203
- buz/kafka/infrastructure/kafka_python/kafka_python_producer.py,sha256=DkqqLSSXHBf4SXXf-IZwwLhxWrGE95Jg4MO_3RDsikU,3594
204
+ buz/kafka/infrastructure/kafka_python/kafka_python_producer.py,sha256=McH_YNQcte7HzNRvS_4bgaz1ng7v4ESoi4mUkrQFLYw,3627
204
205
  buz/kafka/infrastructure/kafka_python/translators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
205
206
  buz/kafka/infrastructure/kafka_python/translators/consumer_initial_offset_position_translator.py,sha256=hJ48_eyMcnbFL_Y5TOiMbGXrQSryuKk9CvP59MdqNOY,620
206
207
  buz/kafka/infrastructure/serializers/byte_serializer.py,sha256=T83sLdX9V5Oh1mzjRwHi_1DsTFI7KefFj7kmnz7JVy4,207
@@ -262,7 +263,7 @@ buz/serializer/message_to_json_bytes_serializer.py,sha256=RGZJ64t4t4Pz2FCASZZCv-
262
263
  buz/wrapper/__init__.py,sha256=GnRdJFcncn-qp0hzDG9dBHLmTJSbHFVjE_yr-MdW_n4,77
263
264
  buz/wrapper/async_to_sync.py,sha256=OfK-vrVUhuN-LLLvekLdMbQYtH0ue5lfbvuasj6ovMI,698
264
265
  buz/wrapper/event_loop.py,sha256=pfBJ1g-8A2a3YgW8Gf9Fg0kkewoh3-wgTy2KIFDyfHk,266
265
- buz-2.19.0.dist-info/LICENSE,sha256=jcLgcIIVaBqaZNwe0kzGWSU99YgwMcI0IGv142wkYSM,1062
266
- buz-2.19.0.dist-info/METADATA,sha256=WP5d5tDJHfipqBV92aBMidSml18fol-PT4bHHorjDLY,12580
267
- buz-2.19.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
268
- buz-2.19.0.dist-info/RECORD,,
266
+ buz-2.21.0rc1.dist-info/LICENSE,sha256=jcLgcIIVaBqaZNwe0kzGWSU99YgwMcI0IGv142wkYSM,1062
267
+ buz-2.21.0rc1.dist-info/METADATA,sha256=hCqsbi3drdgM_i8DpYKf_8E96KLmj5x0OvZyYKMEHS4,12583
268
+ buz-2.21.0rc1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
269
+ buz-2.21.0rc1.dist-info/RECORD,,
File without changes