buz 2.24.0__py3-none-any.whl → 2.26.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.
@@ -0,0 +1,35 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Collection
3
+
4
+ from buz.event import Event
5
+ from buz.event.event_bus_publish_result import EventBusPublishResult
6
+ from buz.kafka.infrastructure.interfaces.async_connection_manager import AsyncConnectionManager
7
+
8
+
9
+ class AsyncEventBusWithPublishResult(AsyncConnectionManager, ABC):
10
+ """
11
+ Event bus with publish result tracking capabilities.
12
+
13
+ Unlike AsyncEventBus which returns None, this event bus returns
14
+ EventBusPublishResult to report which events succeeded and which failed.
15
+
16
+ This enables:
17
+ - Partial success tracking
18
+ - Fail-fast behavior with precise tracking
19
+ - Application-level retry control
20
+ - Better observability
21
+ """
22
+
23
+ @abstractmethod
24
+ async def publish(self, event: Event) -> None:
25
+ pass
26
+
27
+ @abstractmethod
28
+ async def bulk_publish(self, events: Collection[Event]) -> EventBusPublishResult:
29
+ """
30
+ Publish multiple events and return detailed results.
31
+
32
+ Returns:
33
+ EventBusPublishResult containing published_events and failed_events
34
+ """
35
+ pass
buz/event/event.py CHANGED
@@ -1,6 +1,8 @@
1
1
  from dataclasses import dataclass
2
2
 
3
- from buz import Message
3
+ from buz.message import Message, MessageId
4
+
5
+ EventId = MessageId
4
6
 
5
7
 
6
8
  @dataclass(frozen=True)
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+ from typing import Collection
3
+
4
+ from buz.event import Event
5
+
6
+
7
+ @dataclass
8
+ class EventBusPublishResult:
9
+ published_events: Collection[Event]
10
+ failed_events: Collection[Event]
@@ -3,21 +3,24 @@ from typing import Collection, Optional
3
3
 
4
4
  from buz.event import Event
5
5
  from buz.event.async_event_bus import AsyncEventBus
6
- from buz.event.exceptions.event_not_published_exception import EventNotPublishedException
7
- from buz.event.infrastructure.buz_kafka.exceptions.kafka_event_bus_config_not_valid_exception import (
8
- KafkaEventBusConfigNotValidException,
9
- )
6
+ from buz.event.infrastructure.buz_kafka.base_async_buz_kafka_event_bus import BaseAsyncBuzKafkaEventBus
10
7
  from buz.event.infrastructure.buz_kafka.publish_strategy.publish_strategy import KafkaPublishStrategy
11
8
  from buz.event.middleware.async_publish_middleware import AsyncPublishMiddleware
12
- from buz.event.middleware.async_publish_middleware_chain_resolver import AsyncPublishMiddlewareChainResolver
13
- from buz.kafka.domain.exceptions.topic_already_created_exception import KafkaTopicsAlreadyCreatedException
14
9
  from buz.kafka.domain.models.auto_create_topic_configuration import AutoCreateTopicConfiguration
15
- from buz.kafka.domain.models.create_kafka_topic import CreateKafkaTopic
16
10
  from buz.kafka.domain.services.async_kafka_producer import AsyncKafkaProducer
17
11
  from buz.kafka.domain.services.kafka_admin_client import KafkaAdminClient
12
+ from buz.kafka.infrastructure.serializers.partitiion_key_generator import PartitionKeySerializer
18
13
 
19
14
 
20
- class AsyncBuzKafkaEventBus(AsyncEventBus):
15
+ class AsyncBuzKafkaEventBus(BaseAsyncBuzKafkaEventBus, AsyncEventBus):
16
+ """
17
+ Async Kafka event bus with simple behavior (consistent with sync version).
18
+
19
+ - Returns None from bulk_publish
20
+ - Raises exception on failure
21
+ - No retry logic
22
+ """
23
+
21
24
  def __init__(
22
25
  self,
23
26
  *,
@@ -27,82 +30,19 @@ class AsyncBuzKafkaEventBus(AsyncEventBus):
27
30
  kafka_admin_client: Optional[KafkaAdminClient] = None,
28
31
  publish_middlewares: Optional[list[AsyncPublishMiddleware]] = None,
29
32
  auto_create_topic_configuration: Optional[AutoCreateTopicConfiguration] = None,
33
+ partition_key_generator: Optional[PartitionKeySerializer] = None,
30
34
  ):
31
- self.__publish_middleware_chain_resolver = AsyncPublishMiddlewareChainResolver(publish_middlewares or [])
32
- self.__publish_strategy = publish_strategy
33
- self.__producer = producer
34
- self.__topics_checked: dict[str, bool] = {}
35
- self.__kafka_admin_client = kafka_admin_client
36
- self.__auto_create_topic_configuration = auto_create_topic_configuration
37
- self.__logger = logger
38
- self.__check_kafka_admin_client_is_needed()
39
-
40
- def __check_kafka_admin_client_is_needed(self) -> None:
41
- if self.__kafka_admin_client is None and self.__auto_create_topic_configuration is not None:
42
- raise KafkaEventBusConfigNotValidException(
43
- "A KafkaAdminClient is needed to create topics when 'auto_create_topic_configuration' is set."
44
- )
45
-
46
- async def publish(self, event: Event) -> None:
47
- await self.__publish_middleware_chain_resolver.resolve(event, self.__perform_publish)
48
-
49
- async def __perform_publish(self, event: Event) -> None:
50
- try:
51
- topic = self.__publish_strategy.get_topic(event)
52
-
53
- if self.__auto_create_topic_configuration is not None and self.__is_topic_created(topic) is False:
54
- try:
55
- self.__logger.info(f"Creating missing topic: {topic}..")
56
- self.__get_kafka_admin_client().create_topics(
57
- topics=[
58
- CreateKafkaTopic(
59
- name=topic,
60
- partitions=self.__auto_create_topic_configuration.partitions,
61
- replication_factor=self.__auto_create_topic_configuration.replication_factor,
62
- configs=self.__auto_create_topic_configuration.configs,
63
- )
64
- ]
65
- )
66
- self.__logger.info(f"Created missing topic: {topic}")
67
- self.__topics_checked[topic] = True
68
- except KafkaTopicsAlreadyCreatedException:
69
- pass
70
-
71
- headers = self.__get_event_headers(event)
72
- await self.__producer.produce(
73
- message=event,
74
- headers=headers,
75
- topic=topic,
76
- partition_key=event.partition_key(),
77
- )
78
- except Exception as exc:
79
- raise EventNotPublishedException(event) from exc
80
-
81
- def __get_kafka_admin_client(self) -> KafkaAdminClient:
82
- if self.__kafka_admin_client is None:
83
- raise KafkaEventBusConfigNotValidException("KafkaAdminClient is not set.")
84
- return self.__kafka_admin_client
85
-
86
- def __is_topic_created(self, topic: str) -> bool:
87
- is_topic_created = self.__topics_checked.get(topic, None)
88
-
89
- if is_topic_created is not None:
90
- return is_topic_created
91
-
92
- is_topic_created = self.__get_kafka_admin_client().is_topic_created(topic)
93
- self.__topics_checked[topic] = is_topic_created
94
-
95
- return is_topic_created
35
+ super().__init__(
36
+ publish_strategy=publish_strategy,
37
+ producer=producer,
38
+ logger=logger,
39
+ kafka_admin_client=kafka_admin_client,
40
+ publish_middlewares=publish_middlewares,
41
+ auto_create_topic_configuration=auto_create_topic_configuration,
42
+ partition_key_generator=partition_key_generator,
43
+ )
96
44
 
97
45
  async def bulk_publish(self, events: Collection[Event]) -> None:
46
+ """Publish all events sequentially. Raises exception on first failure."""
98
47
  for event in events:
99
48
  await self.publish(event)
100
-
101
- def __get_event_headers(self, event: Event) -> dict:
102
- return {"id": event.id}
103
-
104
- async def connect(self) -> None:
105
- await self.__producer.connect()
106
-
107
- async def disconnect(self) -> None:
108
- await self.__producer.disconnect()
@@ -0,0 +1,80 @@
1
+ from logging import Logger
2
+ from typing import Collection, Optional
3
+
4
+ from buz.event import Event
5
+ from buz.event.async_event_bus_with_publish_result import AsyncEventBusWithPublishResult
6
+ from buz.event.event_bus_publish_result import EventBusPublishResult
7
+ from buz.event.infrastructure.buz_kafka.base_async_buz_kafka_event_bus import BaseAsyncBuzKafkaEventBus
8
+ from buz.event.infrastructure.buz_kafka.publish_strategy.publish_strategy import KafkaPublishStrategy
9
+ from buz.event.middleware.async_publish_middleware import AsyncPublishMiddleware
10
+ from buz.kafka.domain.models.auto_create_topic_configuration import AutoCreateTopicConfiguration
11
+ from buz.kafka.domain.services.async_kafka_producer import AsyncKafkaProducer
12
+ from buz.kafka.domain.services.kafka_admin_client import KafkaAdminClient
13
+ from buz.kafka.infrastructure.serializers.partitiion_key_generator import PartitionKeySerializer
14
+
15
+
16
+ class AsyncBuzKafkaEventBusWithPublishResult(BaseAsyncBuzKafkaEventBus, AsyncEventBusWithPublishResult):
17
+ """
18
+ Kafka event bus with publish result tracking and fail-fast behavior.
19
+
20
+ Features:
21
+ - Returns EventBusPublishResult with published/failed events
22
+ - Fail-fast behavior (stops on first failure)
23
+ - Partial success tracking for external state management
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ *,
29
+ publish_strategy: KafkaPublishStrategy,
30
+ producer: AsyncKafkaProducer,
31
+ logger: Logger,
32
+ kafka_admin_client: Optional[KafkaAdminClient] = None,
33
+ publish_middlewares: Optional[list[AsyncPublishMiddleware]] = None,
34
+ auto_create_topic_configuration: Optional[AutoCreateTopicConfiguration] = None,
35
+ partition_key_generator: Optional[PartitionKeySerializer] = None,
36
+ ):
37
+ super().__init__(
38
+ publish_strategy=publish_strategy,
39
+ producer=producer,
40
+ logger=logger,
41
+ kafka_admin_client=kafka_admin_client,
42
+ publish_middlewares=publish_middlewares,
43
+ auto_create_topic_configuration=auto_create_topic_configuration,
44
+ partition_key_generator=partition_key_generator,
45
+ )
46
+
47
+ async def bulk_publish(self, events: Collection[Event]) -> EventBusPublishResult:
48
+ """
49
+ Publish events one by one with fail-fast behavior.
50
+
51
+ Publishes events sequentially. If any event fails, stops immediately and returns the result with:
52
+ - published_events: All events successfully published before the failure
53
+ - failed_events: The first event that failed (only one event)
54
+
55
+ This allows the caller to track which events were sent and which were not,
56
+ enabling proper state management in external systems.
57
+
58
+ Note: Kafka automatically handles partition assignment based on partition_key.
59
+ Events with the same partition_key will go to the same partition and maintain order.
60
+
61
+ Returns:
62
+ EventBusPublishResult with published_events and failed_events
63
+ """
64
+ published_events: list[Event] = []
65
+ failed_events: list[Event] = []
66
+
67
+ # FAIL-FAST: Stop on first failure
68
+ for event in events:
69
+ try:
70
+ await self.publish(event)
71
+ published_events.append(event)
72
+ except Exception as exc:
73
+ self._logger.error(f"Failed to publish event {event.id}. Error: {exc}")
74
+ failed_events.append(event)
75
+ break
76
+
77
+ return EventBusPublishResult(
78
+ published_events=published_events,
79
+ failed_events=failed_events,
80
+ )
@@ -0,0 +1,118 @@
1
+ from logging import Logger
2
+ from typing import Optional
3
+
4
+ from buz.event import Event
5
+ from buz.event.exceptions.event_not_published_exception import EventNotPublishedException
6
+ from buz.event.infrastructure.buz_kafka.exceptions.kafka_event_bus_config_not_valid_exception import (
7
+ KafkaEventBusConfigNotValidException,
8
+ )
9
+ from buz.event.infrastructure.buz_kafka.publish_strategy.publish_strategy import KafkaPublishStrategy
10
+ from buz.event.middleware.async_publish_middleware import AsyncPublishMiddleware
11
+ from buz.event.middleware.async_publish_middleware_chain_resolver import AsyncPublishMiddlewareChainResolver
12
+ from buz.kafka.domain.exceptions.topic_already_created_exception import KafkaTopicsAlreadyCreatedException
13
+ from buz.kafka.domain.models.auto_create_topic_configuration import AutoCreateTopicConfiguration
14
+ from buz.kafka.domain.models.create_kafka_topic import CreateKafkaTopic
15
+ from buz.kafka.domain.services.async_kafka_producer import AsyncKafkaProducer
16
+ from buz.kafka.domain.services.kafka_admin_client import KafkaAdminClient
17
+ from buz.kafka.infrastructure.serializers.implementations.cdc_partition_key_serializer import CDCPartitionKeySerializer
18
+ from buz.kafka.infrastructure.serializers.partitiion_key_generator import PartitionKeySerializer
19
+
20
+
21
+ class BaseAsyncBuzKafkaEventBus:
22
+ """
23
+ Base class for async Kafka event buses with common functionality.
24
+
25
+ Provides:
26
+ - Event publishing with middleware support
27
+ - Topic auto-creation
28
+ - Partition key generation
29
+ - Connection management
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ *,
35
+ publish_strategy: KafkaPublishStrategy,
36
+ producer: AsyncKafkaProducer,
37
+ logger: Logger,
38
+ kafka_admin_client: Optional[KafkaAdminClient] = None,
39
+ publish_middlewares: Optional[list[AsyncPublishMiddleware]] = None,
40
+ auto_create_topic_configuration: Optional[AutoCreateTopicConfiguration] = None,
41
+ partition_key_generator: Optional[PartitionKeySerializer] = None,
42
+ ):
43
+ self._publish_middleware_chain_resolver = AsyncPublishMiddlewareChainResolver(publish_middlewares or [])
44
+ self._publish_strategy = publish_strategy
45
+ self._producer = producer
46
+ self._topics_checked: dict[str, bool] = {}
47
+ self._kafka_admin_client = kafka_admin_client
48
+ self._auto_create_topic_configuration = auto_create_topic_configuration
49
+ self._logger = logger
50
+ self._partition_key_generator: PartitionKeySerializer = partition_key_generator or CDCPartitionKeySerializer()
51
+ self._check_kafka_admin_client_is_needed()
52
+
53
+ def _check_kafka_admin_client_is_needed(self) -> None:
54
+ if self._kafka_admin_client is None and self._auto_create_topic_configuration is not None:
55
+ raise KafkaEventBusConfigNotValidException(
56
+ "A KafkaAdminClient is needed to create topics when 'auto_create_topic_configuration' is set."
57
+ )
58
+
59
+ async def publish(self, event: Event) -> None:
60
+ """Publish a single event through the middleware chain."""
61
+ await self._publish_middleware_chain_resolver.resolve(event, self._perform_publish)
62
+
63
+ async def _perform_publish(self, event: Event) -> None:
64
+ try:
65
+ topic = self._publish_strategy.get_topic(event)
66
+
67
+ if self._auto_create_topic_configuration is not None and self._is_topic_created(topic) is False:
68
+ try:
69
+ self._logger.info(f"Creating missing topic: {topic}..")
70
+ self._get_kafka_admin_client().create_topics(
71
+ topics=[
72
+ CreateKafkaTopic(
73
+ name=topic,
74
+ partitions=self._auto_create_topic_configuration.partitions,
75
+ replication_factor=self._auto_create_topic_configuration.replication_factor,
76
+ configs=self._auto_create_topic_configuration.configs,
77
+ )
78
+ ]
79
+ )
80
+ self._logger.info(f"Created missing topic: {topic}")
81
+ self._topics_checked[topic] = True
82
+ except KafkaTopicsAlreadyCreatedException:
83
+ pass
84
+
85
+ headers = self._get_event_headers(event)
86
+ await self._producer.produce(
87
+ message=event,
88
+ headers=headers,
89
+ topic=topic,
90
+ partition_key=self._partition_key_generator.generate_key(event),
91
+ )
92
+ except Exception as exc:
93
+ raise EventNotPublishedException(event) from exc
94
+
95
+ def _get_kafka_admin_client(self) -> KafkaAdminClient:
96
+ if self._kafka_admin_client is None:
97
+ raise KafkaEventBusConfigNotValidException("KafkaAdminClient is not set.")
98
+ return self._kafka_admin_client
99
+
100
+ def _is_topic_created(self, topic: str) -> bool:
101
+ is_topic_created = self._topics_checked.get(topic, None)
102
+
103
+ if is_topic_created is not None:
104
+ return is_topic_created
105
+
106
+ is_topic_created = self._get_kafka_admin_client().is_topic_created(topic)
107
+ self._topics_checked[topic] = is_topic_created
108
+
109
+ return is_topic_created
110
+
111
+ def _get_event_headers(self, event: Event) -> dict:
112
+ return {"id": event.id}
113
+
114
+ async def connect(self) -> None:
115
+ await self._producer.connect()
116
+
117
+ async def disconnect(self) -> None:
118
+ await self._producer.disconnect()
@@ -1,10 +1,12 @@
1
1
  from buz.event.middleware.base_async_consume_middleware import BaseAsyncConsumeMiddleware
2
+ from buz.event.middleware.base_async_publish_middleware import BaseAsyncPublishMiddleware
2
3
  from buz.event.middleware.publish_middleware import PublishMiddleware, PublishCallable
3
4
  from buz.event.middleware.base_publish_middleware import BasePublishMiddleware
4
5
  from buz.event.middleware.publish_middleware_chain_resolver import PublishMiddlewareChainResolver
5
6
  from buz.event.middleware.consume_middleware import ConsumeMiddleware, ConsumeCallable
6
7
  from buz.event.middleware.base_consume_middleware import BaseConsumeMiddleware
7
8
  from buz.event.middleware.consume_middleware_chain_resolver import ConsumeMiddlewareChainResolver
9
+ from buz.event.middleware.async_publish_middleware import AsyncPublishMiddleware, AsyncPublishCallable
8
10
 
9
11
  __all__ = [
10
12
  "PublishMiddleware",
@@ -16,4 +18,7 @@ __all__ = [
16
18
  "BaseConsumeMiddleware",
17
19
  "ConsumeMiddlewareChainResolver",
18
20
  "BaseAsyncConsumeMiddleware",
21
+ "BaseAsyncPublishMiddleware",
22
+ "AsyncPublishMiddleware",
23
+ "AsyncPublishCallable",
19
24
  ]
@@ -0,0 +1,27 @@
1
+ from abc import abstractmethod
2
+
3
+ from buz.event import Event
4
+ from buz.event.middleware.async_publish_middleware import AsyncPublishCallable, AsyncPublishMiddleware
5
+
6
+
7
+ class BaseAsyncPublishMiddleware(AsyncPublishMiddleware):
8
+ """
9
+ Base class for async publish middlewares with before/after hooks.
10
+ Provides a template pattern for middleware that need to execute logic
11
+ before and after event publishing.
12
+ """
13
+
14
+ async def on_publish(self, event: Event, publish: AsyncPublishCallable) -> None:
15
+ await self._before_on_publish(event)
16
+ await publish(event)
17
+ await self._after_on_publish(event)
18
+
19
+ @abstractmethod
20
+ async def _before_on_publish(self, event: Event) -> None:
21
+ """Hook executed before publishing the event."""
22
+ pass
23
+
24
+ @abstractmethod
25
+ async def _after_on_publish(self, event: Event) -> None:
26
+ """Hook executed after publishing the event."""
27
+ pass
buz/kafka/__init__.py CHANGED
@@ -22,10 +22,10 @@ from buz.kafka.infrastructure.kafka_python.kafka_python_producer import KafkaPyt
22
22
  from buz.kafka.infrastructure.serializers.byte_serializer import ByteSerializer
23
23
  from buz.kafka.infrastructure.serializers.implementations.json_byte_serializer import JSONByteSerializer
24
24
  from buz.kafka.domain.models.kafka_supported_compression_type import KafkaSupportedCompressionType
25
+
25
26
  from buz.event.infrastructure.buz_kafka.exceptions.kafka_event_bus_config_not_valid_exception import (
26
27
  KafkaEventBusConfigNotValidException,
27
28
  )
28
- from buz.event.infrastructure.buz_kafka.async_buz_kafka_event_bus import AsyncBuzKafkaEventBus
29
29
  from buz.event.infrastructure.buz_kafka.buz_kafka_event_bus import BuzKafkaEventBus
30
30
 
31
31
 
@@ -46,7 +46,6 @@ __all__ = [
46
46
  "ConsumerInitialOffsetPosition",
47
47
  "KafkaSupportedCompressionType",
48
48
  "KafkaEventBusConfigNotValidException",
49
- "AsyncBuzKafkaEventBus",
50
49
  "BuzKafkaEventBus",
51
50
  "AutoCreateTopicConfiguration",
52
51
  "NotAllPartitionAssignedException",
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from abc import abstractmethod, ABC
4
4
  from datetime import datetime
5
- from typing import Sequence, Any
5
+ from typing import Sequence, Any, Optional
6
6
 
7
7
  from buz.kafka.domain.models.create_kafka_topic import CreateKafkaTopic
8
8
  from buz.kafka.infrastructure.interfaces.connection_manager import ConnectionManager
@@ -63,6 +63,7 @@ class KafkaAdminClient(ConnectionManager, ABC):
63
63
  *,
64
64
  topic: str,
65
65
  new_number_of_partitions: int,
66
+ consumer_groups_to_ignore: Optional[set[str]] = None,
66
67
  ) -> None:
67
68
  pass
68
69
 
@@ -52,6 +52,7 @@ class AIOKafkaProducer(AsyncKafkaProducer, Generic[T]):
52
52
  self.__kafka_producer = NativeAIOKafkaProducer(
53
53
  client_id=self.__connection_config.client_id,
54
54
  bootstrap_servers=",".join(self.__connection_config.bootstrap_servers),
55
+ security_protocol=self.__connection_config.credentials.security_protocol.value,
55
56
  sasl_mechanism=sasl_mechanism,
56
57
  ssl_context=ssl_context,
57
58
  sasl_plain_username=self.__connection_config.credentials.user,
@@ -91,8 +92,3 @@ class AIOKafkaProducer(AsyncKafkaProducer, Generic[T]):
91
92
  headers=serialized_headers,
92
93
  key=partition_key.encode("utf-8") if partition_key else None,
93
94
  )
94
-
95
- async def close(self) -> None:
96
- if self.__kafka_producer is not None:
97
- await self.__kafka_producer.stop()
98
- self.__kafka_producer = None
@@ -271,6 +271,7 @@ class KafkaPythonAdminClient(KafkaAdminClient):
271
271
  *,
272
272
  topic: str,
273
273
  new_number_of_partitions: int,
274
+ consumer_groups_to_ignore: Optional[set[str]] = None,
274
275
  ) -> None:
275
276
  self._logger.info(
276
277
  f'Increasing topic "{topic}" partitions: Verifying the new number of partitions "{new_number_of_partitions}"'
@@ -290,6 +291,10 @@ class KafkaPythonAdminClient(KafkaAdminClient):
290
291
  self._logger.info(f'Increasing topic "{topic}" partitions: Extracting related consumer groups')
291
292
  related_consumer_groups = self.__get_consumer_groups_related_to_a_topic(topic_partitions)
292
293
 
294
+ if consumer_groups_to_ignore:
295
+ consumer_groups_to_ignore_set = set[str](consumer_groups_to_ignore)
296
+ related_consumer_groups = related_consumer_groups - consumer_groups_to_ignore_set
297
+
293
298
  self._logger.info(
294
299
  f'Increasing topic "{topic}" partitions: The following consumer groups will be updated:"{related_consumer_groups}"'
295
300
  )
@@ -79,8 +79,7 @@ class KafkaPythonAdminTestClient(KafkaPythonAdminClient, KafkaAdminTestClient):
79
79
  )
80
80
 
81
81
  consumer.close()
82
-
83
- topic_records = records[0] if len(records) > 0 else []
82
+ topic_records = list(sum(records, []))
84
83
 
85
84
  return topic_records
86
85
 
buz/message.py CHANGED
@@ -3,7 +3,7 @@ from dataclasses import field, dataclass
3
3
  from datetime import datetime
4
4
  from inspect import signature, Parameter
5
5
  from types import MappingProxyType
6
- from typing import Any, ClassVar, get_origin, get_args, Union
6
+ from typing import Any, ClassVar, NewType, get_origin, get_args, Union
7
7
  from typing_extensions import Self
8
8
 
9
9
  from uuid_utils.compat import uuid7
@@ -11,6 +11,9 @@ from uuid_utils.compat import uuid7
11
11
  from buz.metadata import Metadata
12
12
 
13
13
 
14
+ MessageId = NewType("MessageId", str)
15
+
16
+
14
17
  @dataclass(frozen=True)
15
18
  class Message(ABC):
16
19
  DATE_TIME_FORMAT: ClassVar[str] = "%Y-%m-%d %H:%M:%S.%f"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: buz
3
- Version: 2.24.0
3
+ Version: 2.26.0
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
@@ -27,6 +27,7 @@ buz/command/synchronous/synced_async/synced_async_command_bus.py,sha256=8tvD1zR8
27
27
  buz/event/__init__.py,sha256=ey3c3fY85XpcWFlmIlbpanJfxv1BZI42Ia1njAtjcEs,588
28
28
  buz/event/async_consumer.py,sha256=k6v_WqQ8A8vWJzO_sMcjU75mroA_Il9D-rE-E-pu_lM,200
29
29
  buz/event/async_event_bus.py,sha256=l627YtPplBprVO0Ccepgt4hkwtMJyI8uaqx6TzCQ9Lw,430
30
+ buz/event/async_event_bus_with_publish_result.py,sha256=_pPhjkVcu9lYfAu6Xysz8Us4TmH2FpGphlpnN7cGsHM,1098
30
31
  buz/event/async_subscriber.py,sha256=FNW5qtPlImcw2i7Ic1VcSpeai0RyPFAx4yRUn_NmpwQ,357
31
32
  buz/event/async_worker.py,sha256=OR7g6cYWOWTh9DbfAfWwS6U6bZ1CDzScJHfH52PYj_k,881
32
33
  buz/event/base_async_subscriber.py,sha256=66qxsFgBSSc4-dG0YsjkvYFFKOOIDA9nP8NuZ7kN2tw,362
@@ -36,8 +37,9 @@ buz/event/dead_letter_queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
36
37
  buz/event/dead_letter_queue/dlq_criteria.py,sha256=hxcV-BMayKTEc5suEfQZhEYkc14H7kZo_4YfDzcJTTY,290
37
38
  buz/event/dead_letter_queue/dlq_record.py,sha256=wEa9CdWkHmxHQVwoHFjWeEU6sjNOi7X8dLr1E-gVmDc,1341
38
39
  buz/event/dead_letter_queue/dlq_repository.py,sha256=8XsXSfO2OzEq4qfQ_v0E0OExintDYI1g55Qu3PtoxKI,630
39
- buz/event/event.py,sha256=Fz23ehyHyAsS5IN3mEElzArsAefs0GNz-vLIu3bpquk,254
40
+ buz/event/event.py,sha256=zs1cL-zu6lkLFKVn8_DPqH8R0JiQaPqubz-x9p_VUHg,294
40
41
  buz/event/event_bus.py,sha256=QnvZD_bKsv628TZ58s5uLntLJCA9Ngir_YHOwb4_UaU,289
42
+ buz/event/event_bus_publish_result.py,sha256=ca13xIPND2_wIcUk16enyNUpRLEH-yOushhqz9Td7cg,212
41
43
  buz/event/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
44
  buz/event/exceptions/event_not_published_exception.py,sha256=gGEiRFGdKIS-VTKg8SN54vSS10WeSkgBhlO2Gpcll_0,215
43
45
  buz/event/exceptions/event_restore_exception.py,sha256=dYHp5i1E-VCUYYhOAVYR-eJfZ3CqPpR9gm1bZ1EFXfE,245
@@ -46,7 +48,9 @@ buz/event/exceptions/term_signal_interruption_exception.py,sha256=RkRRF0v_K9Hg48
46
48
  buz/event/exceptions/worker_execution_exception.py,sha256=6mgztvXOCG_9VZ_Jptkk72kZtNWQ2CPuQ3TjXEWFE14,123
47
49
  buz/event/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
50
  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=kxqXCf80LgQiB464jA9TENejT5FlGT7mRZsy0BsPctk,4966
51
+ buz/event/infrastructure/buz_kafka/async_buz_kafka_event_bus.py,sha256=UMWkG8XzFo4np5YayWqFzc212rcFpfmeCoad2rRJZDo,2106
52
+ buz/event/infrastructure/buz_kafka/async_buz_kafka_event_bus_with_publish_result.py,sha256=hWyQW5oaxtPd4ts4AeAhBZiYnAgaef4SEzqMuV4GNg8,3556
53
+ buz/event/infrastructure/buz_kafka/base_async_buz_kafka_event_bus.py,sha256=XPmfQ4qBkEcyrA_NDlQDXshN27NRCyV2KgPFCTUbM7s,5457
50
54
  buz/event/infrastructure/buz_kafka/base_buz_aiokafka_async_consumer.py,sha256=7ZhaKaFXBpD3HVkuQMpAJvY8lfy7__1wxftLIwCmnMQ,21284
51
55
  buz/event/infrastructure/buz_kafka/buz_aiokafka_async_consumer.py,sha256=GmmuAZboDkrpNOLF8cE_F0t4I7ZnMiGsiGw4SYIvKGc,7303
52
56
  buz/event/infrastructure/buz_kafka/buz_aiokafka_multi_threaded_consumer.py,sha256=ZRLRoBRomqrXAiePSMn4gePF59AWPn6VQpQui1UVnyM,7246
@@ -89,12 +93,13 @@ buz/event/infrastructure/models/process_context.py,sha256=rPdirnYF_G_3Rdqj2us24G
89
93
  buz/event/infrastructure/queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
94
  buz/event/meta_base_subscriber.py,sha256=cqB-iDtN4iuAVBZs2UwrrrAVB2t-u1VvzZw1aSClkwg,817
91
95
  buz/event/meta_subscriber.py,sha256=yH2_2OGqionoC86a3xc4K9kewiuNJs5UtiXdRAMViNM,451
92
- buz/event/middleware/__init__.py,sha256=vbmskMeXurTSgwXqPsRQBydHhNAYnbEoqFc1pqemI7Y,897
96
+ buz/event/middleware/__init__.py,sha256=IlPWwkWQmo0QuW_tQgyQ3fyVFo8cRGr7SuwKZSWZBEA,1182
93
97
  buz/event/middleware/async_consume_middleware.py,sha256=n_EsDMNFgOIu7UVPGRC0iaxCIfGL-p_3xo5Avq43zYA,640
94
98
  buz/event/middleware/async_consume_middleware_chain_resolver.py,sha256=dIiyob5TYavjcD5QL31_Yqya9x3ujWtL7cUZK4bEVvk,1459
95
99
  buz/event/middleware/async_publish_middleware.py,sha256=JIxbRx7HVf_Q1iEziN_5RKGVJ-Oen_f1c3OL9QLmoxE,358
96
100
  buz/event/middleware/async_publish_middleware_chain_resolver.py,sha256=Hqj8CRZXJD6h9KuJaKl88iToOFN7BijoatoDo66En8w,1016
97
101
  buz/event/middleware/base_async_consume_middleware.py,sha256=9IAY-c57PlUq93rAUB3aVDXBVCNH_UO57xHOTiYeWvw,1167
102
+ buz/event/middleware/base_async_publish_middleware.py,sha256=b9dCJk8mszSNSZdh154NdMqBJUmd24Mr8zVU73KeA3Q,930
98
103
  buz/event/middleware/base_consume_middleware.py,sha256=GQxuAjHFVs_nKZXvYjBn9h4EZyUFHz1xdwl6qW5TVo8,912
99
104
  buz/event/middleware/base_publish_middleware.py,sha256=vtM8oA4LZjbZn4omPy-cIAUxQQwL-_Xb4ScU85DwjMU,531
100
105
  buz/event/middleware/consume_middleware.py,sha256=MEzfXfV1hMI0TKHU9f4zqWZsNykgu02X2lr9ZM1WLaw,511
@@ -146,7 +151,7 @@ buz/event/transactional_outbox/transactional_outbox_event_bus.py,sha256=Z9YTfixQ
146
151
  buz/event/transactional_outbox/transactional_outbox_worker.py,sha256=x6kf-Oc4oYKu9S4MTcCqd3VqPNURScTReYJ3Ahx4rKA,2221
147
152
  buz/event/worker.py,sha256=BL9TXB_kyr0Avql9fIcFm3CDNnXPvZB6O6BxVwjtCdA,942
148
153
  buz/handler.py,sha256=W6jSTo5BNV9u9QKBaEMhLIa3tgQocd6oYEJf5K4EfEU,358
149
- buz/kafka/__init__.py,sha256=R3fcyET-SNEAvk_XlBQbHIbQVb63Qiz6lVrif3nDhNU,3435
154
+ buz/kafka/__init__.py,sha256=eaxgfmtoq3_q_kYpKQYEryNj9ApZbuD78RR3psPdrcQ,3312
150
155
  buz/kafka/domain/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
151
156
  buz/kafka/domain/exceptions/consumer_group_not_found_exception.py,sha256=8My8lM7DZ7JARfHY1MmuW5BWOB1YadiKhnc3cSTPlaI,225
152
157
  buz/kafka/domain/exceptions/not_all_partition_assigned_exception.py,sha256=1Ky6gDh_baD6cGB0MBnjbkkLcw2zQU_kFXPpDZn56z0,400
@@ -169,13 +174,13 @@ buz/kafka/domain/models/kafka_supported_sasl_mechanisms.py,sha256=ASyDaFgseQRcUJ
169
174
  buz/kafka/domain/models/kafka_supported_security_protocols.py,sha256=ffY2-9sOj4XIkJTSQVkqeOb4KnuqEYXISDarfDN8r9Q,161
170
175
  buz/kafka/domain/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
176
  buz/kafka/domain/services/async_kafka_producer.py,sha256=gSq3WwEVux_gp3EKDAMN1WsM027uklB58E-WnKpyhPs,533
172
- buz/kafka/domain/services/kafka_admin_client.py,sha256=6WJzMPTYSKTUq0Dtxj1rzGAWK-GAYGolW_LFDB_faVU,2042
177
+ buz/kafka/domain/services/kafka_admin_client.py,sha256=Q89JY0Yjp2KqhXmP7IFZkoDXT8A9CsNqPxTy0oywsdI,2114
173
178
  buz/kafka/domain/services/kafka_admin_test_client.py,sha256=91l_vFIo1yhJLQQCC_OmeXZ5F429zP7Hx5g4FNllpfE,1625
174
179
  buz/kafka/domain/services/kafka_producer.py,sha256=8bLTV328orrPHcARzkc6no4vyJzrArVtCsjmSRXDjos,506
175
180
  buz/kafka/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
176
181
  buz/kafka/infrastructure/aiokafka/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
177
182
  buz/kafka/infrastructure/aiokafka/aiokafka_consumer.py,sha256=3-73e2CJhSjHONsCH_kZB1x5EwhnzLG2cCKGmkU4q0s,10008
178
- buz/kafka/infrastructure/aiokafka/aiokafka_producer.py,sha256=LteHKIHpT6MKplwmwsPYMsd2GWNJCzus65XDHCIdoN8,3823
183
+ buz/kafka/infrastructure/aiokafka/aiokafka_producer.py,sha256=u1mpXjFzYz5DK5C8FbZZoWNX5ARn5_CROiDp7nyvGJM,3745
179
184
  buz/kafka/infrastructure/aiokafka/rebalance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
180
185
  buz/kafka/infrastructure/aiokafka/rebalance/kafka_callback_rebalancer.py,sha256=0IMc_TH4cHjIQiBWOFei_yMx6GbhO58CS-daKzffly4,2259
181
186
  buz/kafka/infrastructure/aiokafka/translators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -199,8 +204,8 @@ buz/kafka/infrastructure/interfaces/async_connection_manager.py,sha256=JbaLu5UVV
199
204
  buz/kafka/infrastructure/interfaces/connection_manager.py,sha256=EWnvShJHOg8QYe6a3ma0urjKjmVMDBi7q8T2cv_i_MQ,200
200
205
  buz/kafka/infrastructure/kafka_python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
206
  buz/kafka/infrastructure/kafka_python/exception/consumer_interrupted_exception.py,sha256=fqhgV7HILdVdv-p1CsOIaaESKY2ZXBtRGYbrVSdPLg0,164
202
- buz/kafka/infrastructure/kafka_python/kafka_python_admin_client.py,sha256=ikNTLUp3CrLgctvQDEcYTCjz8eRBTffg6cQFP8n0MNY,17571
203
- buz/kafka/infrastructure/kafka_python/kafka_python_admin_test_client.py,sha256=5xP23dQ7FDuy7dIWNw39C3bMVmaUj9ZQhEEJISRv9ec,2986
207
+ buz/kafka/infrastructure/kafka_python/kafka_python_admin_client.py,sha256=rvyjRmafKJZL68TxRpT0lENNU1dpQXU0Rv8E8M2rEzc,17846
208
+ buz/kafka/infrastructure/kafka_python/kafka_python_admin_test_client.py,sha256=vbklV4pPsx2-lTLaaEplw1dk7uj44V4GIFVlC5jJsvM,2969
204
209
  buz/kafka/infrastructure/kafka_python/kafka_python_producer.py,sha256=McH_YNQcte7HzNRvS_4bgaz1ng7v4ESoi4mUkrQFLYw,3627
205
210
  buz/kafka/infrastructure/kafka_python/translators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
206
211
  buz/kafka/infrastructure/kafka_python/translators/consumer_initial_offset_position_translator.py,sha256=hJ48_eyMcnbFL_Y5TOiMbGXrQSryuKk9CvP59MdqNOY,620
@@ -223,7 +228,7 @@ buz/locator/sync/__init__.py,sha256=OljGqPXBPuxHzWcn-tQ0P-ObLRHbhoCmmXhsSIgVxJM,
223
228
  buz/locator/sync/handler_already_registered_exception.py,sha256=7DAnkGD97OIC9gc7604dTMBtVUl8KereUvTMicK7FD0,245
224
229
  buz/locator/sync/handler_not_registered_exception.py,sha256=cYKtKET1fHXaMvbBFJNLtvNF6-yp8pixUV62_CdTS54,234
225
230
  buz/locator/sync/instance_locator.py,sha256=HGQ6uRIwrTruhax2i8L6clfJUywgHCHC3k2rwYqjApo,2076
226
- buz/message.py,sha256=d9CMiVSlQVX7xXHi60NSHwqd9cLFoulKIlmlBqLqzaA,2779
231
+ buz/message.py,sha256=kBJvA1eKpdHQJiIhxlvDA1sXRpMe8Z8Ex8ceQb-gTXo,2828
227
232
  buz/metadata.py,sha256=F0-zp_6Tplr2L3euZtuZR3jZ1qb2mW0X99I5ZvQE9Yg,250
228
233
  buz/middleware/__init__.py,sha256=ndg9oGa4p8zOYC3i0nikfmSe3Ny5ug9RBwb9vZ7YhKM,176
229
234
  buz/middleware/middleware.py,sha256=XNSlfSTyBAfHzvuFrjM0IOVtz35lP_f-xWbkfdMYltA,54
@@ -265,7 +270,7 @@ buz/serializer/message_to_json_bytes_serializer.py,sha256=RGZJ64t4t4Pz2FCASZZCv-
265
270
  buz/wrapper/__init__.py,sha256=GnRdJFcncn-qp0hzDG9dBHLmTJSbHFVjE_yr-MdW_n4,77
266
271
  buz/wrapper/async_to_sync.py,sha256=OfK-vrVUhuN-LLLvekLdMbQYtH0ue5lfbvuasj6ovMI,698
267
272
  buz/wrapper/event_loop.py,sha256=pfBJ1g-8A2a3YgW8Gf9Fg0kkewoh3-wgTy2KIFDyfHk,266
268
- buz-2.24.0.dist-info/LICENSE,sha256=jcLgcIIVaBqaZNwe0kzGWSU99YgwMcI0IGv142wkYSM,1062
269
- buz-2.24.0.dist-info/METADATA,sha256=rE4uNgOa5TqPQI5T9PUEtMSM_r2ecmyGp-4TyR14qj4,12580
270
- buz-2.24.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
271
- buz-2.24.0.dist-info/RECORD,,
273
+ buz-2.26.0.dist-info/LICENSE,sha256=jcLgcIIVaBqaZNwe0kzGWSU99YgwMcI0IGv142wkYSM,1062
274
+ buz-2.26.0.dist-info/METADATA,sha256=j6esCho8yx3WMGYuCrI9lrVKNxrNiBAh_FjRhDaZlyE,12580
275
+ buz-2.26.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
276
+ buz-2.26.0.dist-info/RECORD,,
File without changes
File without changes