From e2563c18ffea1233f29453e85b60b5eb166a4032 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 00:32:36 +0000 Subject: [PATCH] Deployed 2ab1605 to 0.5 with MkDocs 1.6.1 and mike 2.1.3 --- 0.5/contributing/index.html | 11 ++++-- 0.5/index.html | 4 +- 0.5/release-notes/index.html | 4 +- 0.5/search/search_index.json | 2 +- 0.5/sitemap.xml | 44 ++++++++++----------- 0.5/sitemap.xml.gz | Bin 345 -> 345 bytes 0.5/tutorials/emitters/asyncio/index.html | 6 +-- 0.5/tutorials/emitters/celery/index.html | 6 +-- 0.5/tutorials/emitters/executor/index.html | 6 +-- 0.5/tutorials/emitters/fastapi/index.html | 6 +-- 0.5/tutorials/emitters/index.html | 6 +-- 0.5/tutorials/emitters/rq/index.html | 6 +-- 0.5/tutorials/event-linker/index.html | 6 +-- 0.5/tutorials/event/index.html | 6 +-- 14 files changed, 42 insertions(+), 71 deletions(-) diff --git a/0.5/contributing/index.html b/0.5/contributing/index.html index d12617d..fcc05b9 100644 --- a/0.5/contributing/index.html +++ b/0.5/contributing/index.html @@ -1681,7 +1681,7 @@

Pre-Submission Testing and Valida If you want to test for specific Python versions, you can do so by specifying the desired versions in the command, as follows:

-
+
hatch run +py=3.10 tests:all
@@ -1695,8 +1695,11 @@ 

Pre-Submission Testing and Valida
hatch run +py=3.12 tests:all
 

+
+
hatch run +py=3.13 tests:all
+

Troubleshooting Hatch Environment Errors

@@ -1706,12 +1709,12 @@

Pre-Submission Testing and Valida issue with the Hatch environment cache. To resolve potential cache-related issues, you can remove the environment and clear its cache by running:

-
hatch env remove [ENV_NAME]
+
hatch env remove [ENV_NAME]
 

Alternatively, you can remove all environments and their cache by running the following command:

-
hatch env prune
+
hatch env prune
 

Code of Conduct

@@ -1748,7 +1751,7 @@

Thanks in Advance! - 2024-04-09 + 2024-10-15 diff --git a/0.5/index.html b/0.5/index.html index 0cb1e10..6e1a145 100644 --- a/0.5/index.html +++ b/0.5/index.html @@ -1492,8 +1492,8 @@

Pyventus

Supported Python versions
- - Code style: black + + Code style: black

diff --git a/0.5/release-notes/index.html b/0.5/release-notes/index.html index 09ce34b..1af97d8 100644 --- a/0.5/release-notes/index.html +++ b/0.5/release-notes/index.html @@ -1762,7 +1762,7 @@

Breaking ChangesInitial Implementation - 2024-04-09 + 2024-10-16 diff --git a/0.5/search/search_index.json b/0.5/search/search_index.json index 1559697..171e2de 100644 --- a/0.5/search/search_index.json +++ b/0.5/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Pyventus","text":"

Documentation: https://mdapena.github.io/pyventus

Source Code: https://github.com/mdapena/pyventus

\u2003\u2003Pyventus is a powerful Python package for event-driven programming. It offers a comprehensive suite of tools to easily define, emit, and orchestrate events. With Pyventus, you can build scalable, extensible, and loosely-coupled event-driven applications.

"},{"location":"#key-features","title":"Key Features","text":"

Pyventus offers several key features, such as:

  • Sync and Async Support \u2500 Whether your code is synchronous or asynchronous, Pyventus allows you to define event handlers as either sync or async callbacks and emit events from both scopes.
  • Customization \u2500 Whether you choose official emitters or custom ones, Pyventus allows you to customize the behavior and capabilities of the event emitters to perfectly align with your unique requirements.
  • An Intuitive API \u2500 Pyventus provides a user-friendly API for defining events, emitters, and handlers. Its design simplifies the process of working with events, enabling you to organize your code around discrete events and their associated actions.
  • Runtime Flexibility \u2500 Pyventus' runtime flexibility allows you to switch seamlessly between different built-in or custom event emitter implementations on the fly, providing a dynamic and adaptable environment for event-driven programming.
  • Reliable Event Handling \u2500 Pyventus allows you to define handlers to customize how events are processed upon completion. Attach success and failure logic to take targeted actions based on the outcome of each event execution.
  • Scalability and Maintainability \u2500 By adopting an event-driven approach with Pyventus, you can create scalable and maintainable code thanks to the loose coupling between its components that enables extensibility and modularity.
  • Comprehensive Documentation \u2500 Pyventus provides a comprehensive documentation suite that includes API references, usage examples, and tutorials to effectively leverage all the features and capabilities of the package.
"},{"location":"#quick-start","title":"Quick Start","text":"

\u2003\u2003Pyventus is published as a Python package and can be installed using pip, ideally in a virtual environment for proper dependency isolation. To get started, open up a terminal and install Pyventus with the following command:

pip install pyventus\n

\u2003\u2003Pyventus by default relies on the Python standard library and requires Python 3.10 or higher with no additional dependencies. However, this package also includes alternative integrations to access additional features such as Redis Queue, Celery, and FastAPI. For more information on this matter, please refer to the Optional Dependencies section.

"},{"location":"#a-simple-example","title":"A Simple Example","text":"

\u2003\u2003Experience the power of Pyventus through a simple Hello, World! example that illustrates the core concepts and basic usage of the package. By following this example, you\u2019ll learn how to subscribe to events and emit them within your application.

Hello, World! Example
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\ndef handle_greet_event():\n    print(\"Hello, World!\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\")\n
You can also work with async functions and contexts... Hello, World! Example (Async version)
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\nasync def handle_greet_event():\n    print(\"Hello, World!\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\")\n

\u2003\u2003As we can see from the Hello, World! example, Pyventus follows a simple and intuitive workflow for defining and emitting events. Let's recap the essential steps involved:

  1. Importing Necessary Modules: We first imported the required modules from Pyventus, which encompassed the EventLinker class, the EventEmitter interface, and the AsyncIOEventEmitter implementation.
  2. Linking Events to Callbacks: Next, we used the @EventLinker.on() decorator to define and link the string event GreetEvent to the function handle_greet_event(), which will print 'Hello, World!' to the console whenever the GreetEvent is emitted.
  3. Instantiating an Event Emitter: After that, and in order to trigger our event, we needed to create an instance of the event emitter class. While AsyncIOEventEmitter was utilized, any built-in or custom implementation could be employed.
  4. Triggering the Event: Finally, by using the emit() method of the event emitter instance, we were able to trigger the GreetEvent, which resulted in the execution of the handle_greet_event() callback.

\u2003\u2003Having gained a clear understanding of the workflow showcased in the Hello, World! example, you are now well-equipped to explore more intricate event-driven scenarios and fully harness the capabilities of Pyventus in your own projects. For a deep dive into the package's functionalities, you can refer to the Pyventus Tutorials or API.

"},{"location":"#a-practical-example","title":"A Practical Example","text":"

\u2003\u2003To showcase Pyventus' event-driven capabilities in a real-world scenario, we will explore a practical example of implementing a voltage sensor using an event-driven architecture (crucial for such scenarios). The purpose of this example is to create an efficient voltage sensor that can seamlessly handle real-time data and respond appropriately to specific voltage conditions.

Example \u2500 Monitoring Voltage Levels Across Devices (Context)

\u2003\u2003A common aspect found in many systems is the need to monitor and respond to changes in sensor data. Whether it's pressure sensors, temperature sensors, or other types, capturing and reacting to sensor data is crucial for effective system operation. In our practical example, we will focus on a specific scenario: building a sensor system that monitors voltage levels across devices. The goal of our voltage sensor is to detect potential issues, such as low or high voltage conditions, and respond appropriately in real-time.

\u2003\u2003To accomplish our goal, we will define a VoltageSensor class to read voltage levels and emit events based on predefined thresholds. We will create event handlers to respond to these events, performing actions such as activating eco-mode for low voltage or implementing high-voltage protection. Additionally, a shared event handler will provide general notifications for out-of-range voltage situations. The code example below illustrates the implementation of this system.

Voltage Sensor System with Pyventus (Practical Example)
import asyncio\nimport random\n\nfrom pyventus import EventEmitter, EventLinker, AsyncIOEventEmitter\n\n\nclass VoltageSensor:\n\n    def __init__(self, name: str, low: float, high: float, event_emitter: EventEmitter) -> None:\n        # Initialize the VoltageSensor object with the provided parameters\n        self._name: str = name\n        self._low: float = low\n        self._high: float = high\n        self._event_emitter: EventEmitter = event_emitter\n\n    async def __call__(self) -> None:\n        # Start voltage readings for the sensor\n        print(f\"Starting voltage readings for: {self._name}\")\n        print(f\"Low: {self._low:.3g}v | High: {self._high:.3g}v\\n-----------\\n\")\n\n        while True:\n            # Simulate sensor readings\n            voltage: float = random.uniform(0, 5)\n            print(\"\\tSensor Reading:\", \"\\033[32m\", f\"{voltage:.3g}v\", \"\\033[0m\")\n\n            # Emit events based on voltage readings\n            if voltage < self._low:\n                self._event_emitter.emit(\"LowVoltageEvent\", sensor=self._name, voltage=voltage)\n            elif voltage > self._high:\n                self._event_emitter.emit(\"HighVoltageEvent\", sensor=self._name, voltage=voltage)\n\n            await asyncio.sleep(1)\n\n\n@EventLinker.on(\"LowVoltageEvent\")\ndef handle_low_voltage_event(sensor: str, voltage: float):\n    print(f\"\ud83e\udeab Turning on eco-mode for '{sensor}'. ({voltage:.3g}v)\\n\")\n    # Perform action for low voltage...\n\n\n@EventLinker.on(\"HighVoltageEvent\")\nasync def handle_high_voltage_event(sensor: str, voltage: float):\n    print(f\"\u26a1 Starting high-voltage protection for '{sensor}'. ({voltage:.3g}v)\\n\")\n    # Perform action for high voltage...\n\n\n@EventLinker.on(\"LowVoltageEvent\", \"HighVoltageEvent\")\ndef handle_voltage_event(sensor: str, voltage: float):\n    print(f\"\\033[31m\\nSensor '{sensor}' out of range.\\033[0m (Voltage: {voltage:.3g})\")\n    # Perform notification for out of range voltage...\n\n\nasync def main():\n    # Initialize the sensor and run the sensor readings\n    sensor = VoltageSensor(name=\"PressureSensor\", low=0.5, high=3.9, event_emitter=AsyncIOEventEmitter())\n    await asyncio.gather(sensor(), )  # Add new sensors inside the 'gather' for multi-device monitoring\n\n\nasyncio.run(main())\n

\u2003\u2003As we can see from this practical example, Pyventus enables us to easily build an event-driven system for voltage sensors that is flexible, efficient, and highly responsive. With its intuitive API and support for both synchronous and asynchronous operations, we were able to effectively monitor voltage levels, detect anomalies, and trigger appropriate actions in real-time.

"},{"location":"#support-for-synchronous-and-asynchronous-code","title":"Support for Synchronous and Asynchronous Code","text":"

\u2003\u2003Pyventus is designed from the ground up to seamlessly support both synchronous and asynchronous programming models. Its unified sync/async API allows you to define event callbacks and emit events across sync and async contexts.

"},{"location":"#subscribing-event-handlers-with-sync-and-async-callbacks","title":"Subscribing Event Handlers with Sync and Async Callbacks","text":"
@EventLinker.on(\"MyEvent\")\ndef sync_event_callback():\n    pass  # Synchronous event handling\n\n\n@EventLinker.on(\"MyEvent\")\nasync def async_event_callback():\n    pass  # Asynchronous event handling\n
You can optimize the execution of your callbacks based on their workload...

\u2003\u2003By default, event handlers in Pyventus are executed concurrently during an event emission, running their sync and async callbacks as defined. However, if you have a sync callback that involves I/O or non-CPU bound operations, you can enable the force_async parameter to offload it to a thread pool, ensuring optimal performance and responsiveness. The force_async parameter utilizes the asyncio.to_thread() function to execute sync callbacks asynchronously.

@EventLinker.on(\"BlockingIO\", force_async=True)\ndef blocking_io():\n    print(f\"start blocking_io at {time.strftime('%X')}\")\n    # Note that time.sleep() can be replaced with any blocking\n    # IO-bound operation, such as file operations.\n    time.sleep(1)\n    print(f\"blocking_io complete at {time.strftime('%X')}\")\n
"},{"location":"#emitting-events-from-sync-and-async-contexts","title":"Emitting Events from Sync and Async Contexts","text":"
# Emitting an event within a sync function\ndef sync_function(event_emitter: EventEmitter):\n    event_emitter.emit(\"MyEvent\")\n\n\n# Emitting an event within an async function\nasync def async_function(event_emitter: EventEmitter):\n    event_emitter.emit(\"MyEvent\")\n
Event propagation within different contexts...

\u2003\u2003While Pyventus provides a base EventEmitter class with a unified sync/async API, the specific propagation behavior when emitting events may vary depending on the concrete EventEmitter used. For example, the AsyncIOEventEmitter implementation leverages the AsyncIO event loop to schedule callbacks added from asynchronous contexts without blocking. But alternative emitters could structure propagation differently to suit their needs.

"},{"location":"#runtime-flexibility-and-customization","title":"Runtime Flexibility and Customization","text":"

\u2003\u2003At its core, Pyventus utilizes a modular event emitter design that allows you to switch seamlessly between different built-in or custom event emitter implementations on the fly. Whether you opt for official emitters or decide to create your custom ones, Pyventus allows you to tailor the behavior and capabilities of the event emitters to perfectly align with your unique requirements.

"},{"location":"#swapping-event-emitter-implementations-at-runtime","title":"Swapping Event Emitter Implementations at Runtime","text":"

\u2003\u2003By leveraging the principle of dependency inversion and using the base EventEmitter as a dependency, you can change the concrete implementation on the fly. Let's demonstrate this using the AsyncIO Event Emitter and the Executor Event Emitter:

Event Emitter Runtime Flexibility Example
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter, ExecutorEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\ndef handle_greet_event(name: str = \"World\"):\n    print(f\"Hello, {name}!\")\n\n\nif __name__ == \"__main__\":\n    def main(event_emitter: EventEmitter) -> None:\n        event_emitter.emit(\"GreetEvent\", name=type(event_emitter).__name__)\n\n\n    main(event_emitter=AsyncIOEventEmitter())\n    with ExecutorEventEmitter() as executor_event_emitter:\n        main(event_emitter=executor_event_emitter)\n
"},{"location":"#defining-custom-event-emitters","title":"Defining Custom Event Emitters","text":"

\u2003\u2003To illustrate Pyventus' customization capabilities, we will define and implement a custom event emitter class for the FastAPI framework. This class will efficiently handle the execution of event emissions through its background tasks workflow.

Custom Event Emitter Example
from fastapi import BackgroundTasks\n\nfrom pyventus import EventEmitter, EventLinker\n\n\nclass FastAPIEventEmitter(EventEmitter):\n    \"\"\"A custom event emitter that uses the FastAPI background tasks.\"\"\"\n\n    def __init__(self, background_tasks: BackgroundTasks):\n        super().__init__(event_linker=EventLinker, debug=False)\n        self._background_tasks = background_tasks\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        self._background_tasks.add_task(event_emission)  # Process the event emission as a background task\n
Official FastAPIEventEmitter Integration.

In case you're interested in integrating Pyventus with FastAPI, you can refer to the official Pyventus FastAPI Event Emitter implementation.

"},{"location":"#event-objects-and-global-events","title":"Event Objects and Global Events","text":"

\u2003\u2003In addition to string events, Pyventus also supports Event Objects, which provide a structured way to define events and encapsulate relevant data payloads.

Event Object Example
@dataclass  # Define a Python dataclass representing the event and its payload.\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n\n\n@EventLinker.on(OrderCreatedEvent)  # Subscribe event handlers to the event.\ndef handle_order_created_event(event: OrderCreatedEvent):\n    # Pyventus will automatically pass the Event Object \n    # as the first positional argument.\n    print(f\"Event Object: {event}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\n    event=OrderCreatedEvent(  # Emit an instance of the event!\n        order_id=6452879,\n        payload={},\n    ),\n)\n

\u2003\u2003Furthermore, Pyventus provides support for Global Events, which are particularly useful for implementing cross-cutting concerns such as logging, monitoring, or analytics. By subscribing event handlers to ... or Ellipsis, you can capture all events that may occur within that EventLinker context.

Global Event Example
@EventLinker.on(...)\ndef handle_any_event(*args, **kwargs):\n    print(f\"Perform logging...\\nArgs: {args}\\tKwargs: {kwargs}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\", name=\"Pyventus\")\n
"},{"location":"#success-and-error-handling","title":"Success and Error Handling","text":"

\u2003\u2003With Pyventus, you can customize how events are handled upon completion, whether they succeed or encounter errors. This customization is achieved by using either the EventLinker's on() or once() decorator within a with statement block. Inside this block, you can define not only the event callbacks but also the overall workflow of the event. Now, let\u2019s explore this simple yet powerful Pythonic syntax of Pyventus through an example.

Success and Error Handling Example
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n# Create an event linker for the \"DivisionEvent\"\nwith EventLinker.on(\"DivisionEvent\") as linker:\n    @linker.on_event\n    def divide(a: float, b: float) -> float:\n        return a / b\n\n    @linker.on_success\n    def handle_success(result: float) -> None:\n        print(f\"Division result: {result:.3g}\")\n\n    @linker.on_failure\n    def handle_failure(e: Exception) -> None:\n        print(f\"Oops, something went wrong: {e}\")\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()  # Create an event emitter\nevent_emitter.emit(\"DivisionEvent\", a=1, b=0)  # Example: Division by zero\nevent_emitter.emit(\"DivisionEvent\", a=1, b=2)  # Example: Valid division\n

\u2003\u2003As we have seen from the example, Pyventus offers a reliable and Pythonic solution for customizing event handling. By utilizing the EventLinker and its decorators within a with statement block, we were able to define the DivisionEvent and specify the callbacks for division, success, and failure cases.

"},{"location":"#continuous-evolution","title":"Continuous Evolution","text":"

\u2003\u2003Pyventus continuously adapts to support developers across technological and programming domains. Its aim is to remain at the forefront of event-driven design. Future development may introduce new official event emitters, expanding compatibility with different technologies through seamless integration.

\u2003\u2003Current default emitters provide reliable out-of-the-box capabilities for common use cases. They efficiently handle core event operations and lay the foundation for building event-driven applications.

Driving Innovation Through Collaboration

\u2003\u2003Pyventus is an open source project that welcomes community involvement. If you wish to contribute additional event emitters, improvements, or bug fixes, please check the Contributing section for guidelines on collaborating. Together, we can further the possibilities of event-driven development.

"},{"location":"#license","title":"License","text":"

\u2003\u2003Pyventus is distributed as open source software and is released under the MIT License. You can view the full text of the license in the LICENSE file located in the Pyventus repository.

"},{"location":"contributing/","title":"Contributing","text":""},{"location":"contributing/#contribution-guidelines","title":"Contribution Guidelines","text":"

Thank you for being interested in contributing to Pyventus! Your involvement is greatly appreciated \u2764\ufe0f

"},{"location":"contributing/#getting-started","title":"Getting Started","text":"

\u2003\u2003Before creating an issue or pull request, please make sure to check if a similar discussion already exists. We encourage you to actively participate by engaging in existing issues.

"},{"location":"contributing/#reporting-issues","title":"Reporting Issues","text":"

\u2003\u2003If you have any questions, bug reports, or feature requests, please open a new issue or discussion. When reporting issues, be sure to provide clear steps to reproduce the problem. For security vulnerabilities, please refer to our security policy.

"},{"location":"contributing/#submitting-changes","title":"Submitting Changes","text":"

\u2003\u2003We greatly appreciate your contributions and want to ensure they align with the project's goals and quality standards. Unless your proposed change is trivial, such as fixing a typo or tweaking documentation, we recommend creating an issue or discussion to talk about the proposed change before submitting a pull request. This allows us to provide feedback, clarify requirements, and ensure your efforts are focused in the right direction. To make a contribution, please follow these steps:

  1. Fork the repository and create a new branch.
  2. Implement your changes in the branch.
  3. Ensure that formatting, linting, and tests pass.
  4. Whenever possible, include tests to cover the lines of code you added or modified.
  5. Commit your changes and submit a pull request with a clear, detailed message.

\u2003\u2003We'll review your pull request to ensure it meets our quality standards before merging it into the main codebase. Please feel free to ask any questions along the way!

"},{"location":"contributing/#development-setup","title":"Development Setup","text":"

\u2003\u2003We recommend developing in a virtual environment to isolate project dependencies. To set up your development environment, follow these steps:

  1. Create a virtual environment:

    python -m venv venv\n
  2. Activate the virtual environment:

    Linux, macOSWindows PowerShellWindows Bash
    source ./venv/bin/activate\n
    .\\venv\\Scripts\\Activate.ps1\n
    source ./venv/Scripts/activate\n
  3. Install development dependencies:

    pip install -e .[dev]\n
"},{"location":"contributing/#running-the-tests","title":"Running the Tests","text":"

During development, you have two options to run the test suite:

ManualUsing Hatch
pytest -v\n
hatch run tests:test\n

Validating New Event Emitters

When implementing new event emitters, it is crucial to ensure their seamless integration with other event emitters and the entire package. To achieve this, we kindly request that you utilize the provided test suite specifically designed for testing new event emitters.

"},{"location":"contributing/#checking-types","title":"Checking Types","text":"

You can use the mypy tool to check the static typing of your code. Simply run the following command:

ManualUsing Hatch
mypy\n
hatch run tests:typing\n
"},{"location":"contributing/#code-coverage","title":"Code Coverage","text":"

To check the code coverage of your changes, run the following command:

ManualUsing Hatch
coverage run -m pytest -v\n
hatch run tests:cov\n
"},{"location":"contributing/#pyventus-documentation","title":"Pyventus Documentation","text":"

\u2003\u2003The documentation for our project is written in Markdown and built using Material for MkDocs. Additionally, the API documentation is generated from the docstrings using mkdocstrings. To begin working on the documentation in a development environment, simply execute the following command:

ManualUsing Hatch
mkdocs serve --dev-addr localhost:8000\n
hatch run docs:serve\n
"},{"location":"contributing/#project-structure-and-conventions","title":"Project Structure and Conventions","text":"

\u2003\u2003This project follows the src-layout convention for Python packages. This convention improves code organization, facilitates easy testing and usage, and allows developers to install the package in editable mode. By adhering to this convention, we can validate the package thoroughly in a realistic environment, leading to a higher quality and user-friendly product.

"},{"location":"contributing/#code-standards","title":"Code Standards","text":"

We strive for a high-quality and maintainable codebase. To achieve this, we have established the following code standards:

  • PEP-8 Compliance \u2500 Please follow the guidelines outlined in PEP-8 for consistent code formatting. Adhering to these standards ensures readability and maintainability across our codebase.
  • Black Formatter \u2500 We recommend using the Black code formatter to ensure consistent style and formatting. By automatically enforcing a standard style, the Black formatter saves you time and effort in manual formatting.
  • Meaningful Naming \u2500 Use descriptive and meaningful names for variables, functions, and classes. Clear and intuitive naming enhances code comprehension, making it easier for everyone to understand and work with the code.
  • Modularity and Reusability \u2500 Encourage the development of modular and reusable code. Breaking down complex tasks into smaller, self-contained components promotes maintainability, reduces complexity, and allows for scalability and extensibility.
  • Optimization and Efficiency \u2500 Strive for efficient code by considering algorithmic complexity and optimizing where necessary. Writing code that is both correct and performant ensures responsive and scalable applications.
"},{"location":"contributing/#documentation-style","title":"Documentation Style","text":"

\u2003\u2003Clear and comprehensive documentation facilitates collaboration and understanding. When contributing to this project, please ensure that you document the following items using properly formatted docstrings:

  • Modules.
  • Class definitions.
  • Function definitions.
  • Module-level variables.

\u2003\u2003Pyventus uses Sphinx docstrings formatted according to PEP 257 guidelines. For more examples and detailed guidance on using Sphinx-style docstrings, we encourage you to consult the official Sphinx documentation.

"},{"location":"contributing/#pre-submission-testing-and-validation","title":"Pre-Submission Testing and Validation","text":"

\u2003\u2003Before submitting your pull request, it is crucial to ensure that your changes pass all the necessary checks. To do so, simply run the following command:

hatch run tests:all\n

\u2003\u2003The above command will trigger the Hatch project manager to initiate the comprehensive testing process across all supported Python versions. It will run tests, perform typing checks, ensure code formatting, and measure code coverage. This ensures that your changes meet the required quality standards.

Testing for Individual Python Versions

If you want to test for specific Python versions, you can do so by specifying the desired versions in the command, as follows:

Python 3.10Python 3.11Python 3.12
hatch run +py=3.10 tests:all\n
hatch run +py=3.11 tests:all\n
hatch run +py=3.12 tests:all\n

Troubleshooting Hatch Environment Errors

If commands run successfully when executed manually but produce unexpected errors or misbehavior when run within a Hatch environment, even though the dependencies are declared correctly, this could indicate an issue with the Hatch environment cache. To resolve potential cache-related issues, you can remove the environment and clear its cache by running:

hatch env remove [ENV_NAME]\n

Alternatively, you can remove all environments and their cache by running the following command:

hatch env prune\n
"},{"location":"contributing/#code-of-conduct","title":"Code of Conduct","text":"

\u2003\u2003This project and everyone participating in it is governed by the Pyventus Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior.

"},{"location":"contributing/#thanks-in-advance","title":"Thanks in Advance!","text":"

\u2003\u2003Thank you for considering contributing to this project. Your contributions are valuable and greatly appreciated. If you have any questions or need further clarification, please don't hesitate to reach out. We look forward to collaborating with you to enhance this project!

"},{"location":"getting-started/","title":"Getting Started","text":"

\u2003\u2003Welcome to the Getting Started section! This guide will help you install and configure Pyventus in your project. For more detailed information on how to use this package, you can refer to the Pyventus Tutorials or API Reference.

"},{"location":"getting-started/#requirements","title":"Requirements","text":"

\u2003\u2003By default, Pyventus' core functionalities and default event emitter implementations, such as the AsyncIO Event Emitter, and the Executor Event Emitter, only require Python 3.10+ with no additional dependencies. However, these requirements may expand if you opt to use alternative built-in event emitter implementations.

"},{"location":"getting-started/#installation","title":"Installation","text":"

\u2003\u2003Pyventus is published as a Python package and can be installed using pip, ideally in a virtual environment for proper dependency isolation. To get started, open up a terminal and install Pyventus with the following command:

pip install pyventus\n
"},{"location":"getting-started/#optional-dependencies","title":"Optional Dependencies","text":"

\u2003\u2003 While Pyventus primarily relies on the Python standard library, it also supports optional dependencies to access additional features, as shown below:

"},{"location":"getting-started/#supported-library-integrations","title":"Supported Library Integrations","text":"
  • Celery \u2500 Pyventus integrates with Celery using the CeleryEventEmitter, enabling event emissions to be executed on Celery worker nodes to improve task processing. To install Pyventus with Celery support, use the following command:
    pip install pyventus[celery] (1)\n
    1. Optional Package Dependencies \u2003\u2003This package includes some optional dependencies. For more information, please visit the Celery bundles documentation.

      These optional dependencies can be installed as described in their individual documentation. For example:

      pip install celery[...]\n

  • Redis Queue (RQ) \u2500 Pyventus integrates with Redis Queue (RQ) using the RQEventEmitter, allowing event emissions to run as background jobs through RQ's asynchronous workers. To install Pyventus with RQ support, use the following command:
    pip install pyventus[rq]\n
  • FastAPI \u2500 Pyventus integrates with the FastAPI framework using the FastAPIEventEmitter, enabling event-driven architectures to be built directly into FastAPI applications. The emitter leverages FastAPI's background tasks to asynchronously process event emissions without blocking responses. To install Pyventus with FastAPI integration, use the following command:
    pip install pyventus[fastapi] (1)\n
    1. Optional Package Dependencies \u2003\u2003This package includes some optional dependencies. For more information, please visit the FastAPI optional dependencies.

      These optional dependencies can be installed as described in their individual documentation. For example:

      pip install fastapi[...]\n

You can install all of them with:

pip install pyventus[all]\n

"},{"location":"getting-started/#supported-framework-integrations","title":"Supported Framework Integrations","text":""},{"location":"release-notes/","title":"Release Notes","text":""},{"location":"release-notes/#0.5.0","title":"v0.5.0 April 9, 2024","text":""},{"location":"release-notes/#breaking-changes","title":"Breaking Changes","text":"
  • Removed the base Event class due to improved event semantics and unnecessary redundancy.
  • Renamed the get_event_registry() method of EventLinker to get_registry().
  • Renamed the __event_registry inner property of EventLinker to __registry.
  • Renamed the get_events_by_handler() method of EventLinker to get_event_handlers_by_events().
  • Renamed the get_handlers_by_events() method of EventLinker to get_event_handlers_by_events().
  • Renamed the protected method _executor_callback() of the ExecutorEventEmitter to _callback().
  • Renamed the task name of CeleryEventEmitter from _executor to pyventus_executor to avoid collisions with other task names.
"},{"location":"release-notes/#added","title":"Added","text":"
  • Added __slots__ to EventLinkageWrapper class for more efficient memory usage.
  • Extended support for subscription and emission of any dataclass object, removing the limitation of only Event subclasses.
  • Added the force_async parameter to the EventHandler class and EventLinker subscription methods to be able to optimize the execution of sync callbacks based on their workload.
  • Introduced a new event semantic where the Python ... (Ellipsis) is now used to refer to all events on a subscription, like the onAny() method but with a Pythonic syntax.
  • Added the mkdocs-material social cards plugin, which provides a preview of the documentation content when shared on social media platforms.
"},{"location":"release-notes/#changed","title":"Changed","text":"
  • Standardized the order of static methods, class methods, and instance methods for improved readability.
  • Applied Python best practices to optimize the methods within the EventLinker and EventEmitter classes.
  • Improved validation of variable instances in the event emitters, EventLinker, and EventHandler.
  • Updated and improved the test suite to ensure accurate validation and consistency.
  • Enabled creation date for the mkdocs git-revision-date-localized plugin.
  • Replaced the mkdocs git-authors plugin with the git-committers plugin.
  • Updated and improved the package description.
  • Updated the tutorial section to incorporate recent changes.
  • Enhanced the documentation index page and README file with new examples and better descriptions to showcase the unique features of Pyventus.
"},{"location":"release-notes/#removed","title":"Removed","text":"
  • Removed the default value of the once flag in the EventHandler class.
"},{"location":"release-notes/#fixed","title":"Fixed","text":"
  • Fixed and standardized all package docstrings and code comments for consistency and clarity.
  • Addressed minor errors and details in the documentation.
"},{"location":"release-notes/#0.4.1","title":"v0.4.1 January 30, 2024","text":""},{"location":"release-notes/#changed_1","title":"Changed","text":"
  • Optimized the size of the source distribution (sdist) build by including only essential files and directories, such as the /src and /tests directories, as well as the following files: .gitignore, pyproject.toml, CITATION.cff, README, and LICENSE.
  • Refactored documentation dependencies into an optional dependency called docs.
  • Updated the deploy-docs.yml GitHub workflow to leverage the new optional dependency docs.
  • Updated the EventEmission class with the @final decorator from the typing module, indicating that it is meant for internal use only and should not be subclassed.
"},{"location":"release-notes/#fixed_1","title":"Fixed","text":"
  • Addressed minor errors and details in the documentation.
"},{"location":"release-notes/#0.4.0","title":"v0.4.0 January 6, 2024","text":""},{"location":"release-notes/#added_1","title":"Added","text":"
  • Added FastAPIEventEmitter implementation to facilitate seamless integration with the FastAPI framework.
  • Added tests for FastAPIEventEmitter to validate its behavior and ensure proper operation.
  • Added documentation for FastAPIEventEmitter, including tutorials and API references.
  • Integrated the Coveralls.io workflow to generate coverage badge and reports.
  • Included coverage badges on the main documentation page and the readme file.
  • Introduced permalinks within the documentation for easy navigation.
"},{"location":"release-notes/#changed_2","title":"Changed","text":"
  • Updated pyproject.toml with the new optional dependency for FastAPI integration.
"},{"location":"release-notes/#fixed_2","title":"Fixed","text":"
  • Addressed minor errors in the Pyventus documentation to improve accuracy and clarity.
"},{"location":"release-notes/#0.3.0","title":"v0.3.0 December 29, 2023","text":""},{"location":"release-notes/#breaking-changes_1","title":"Breaking Changes","text":"
  • Introduced EventEmission object to encapsulate the processing of event emissions. This changes the _execute() method of EventEmitter but provides a cleaner, more scalable, and efficient approach.
  • Renamed all debug flags from debug_mode to debug for enhanced clarity and consistency.
  • Renamed EventEmitter's _execute() method to _process() to better reflect its purpose of processing event emissions.
"},{"location":"release-notes/#added_2","title":"Added","text":"
  • Added CeleryEventEmitter implementation to leverage the Celery distributed task queue for event handling.
  • Added tests for CeleryEventEmitter to validate its behavior and ensure proper operation.
  • Added documentation for CeleryEventEmitter, including tutorials and API references.
"},{"location":"release-notes/#changed_3","title":"Changed","text":"
  • Restructured the documentation for event emitters tutorials and API references to improve organization and clarity.
  • Updated the contributing.md page to include the Troubleshooting Hatch Environment Errors section.
  • Updated the EventEmitter API documentation to include the EventEmission class reference.
  • Updated pyproject.toml with the new optional dependency for Celery integration.
  • Updated mypy ignore flags to properly silence specific false positive error codes.
"},{"location":"release-notes/#fixed_3","title":"Fixed","text":"
  • Addressed minor errors in the Pyventus documentation.
"},{"location":"release-notes/#0.2.1","title":"v0.2.1 December 17, 2023","text":""},{"location":"release-notes/#changed_4","title":"Changed","text":"
  • Updated docstring links throughout the package to refer to the official documentation.
  • Updated the RQEventEmitter API Reference and Tutorials docs to reflect the new optional import.
"},{"location":"release-notes/#fixed_4","title":"Fixed","text":"
  • Resolved the issue where the RQEventEmitter class was automatically imported in the main package, requiring the installation of its optional dependency to use any of the package's core functionalities. It is now fully optional.
  • Fixed issues with invalid links in the documentation.
"},{"location":"release-notes/#0.2.0","title":"v0.2.0 December 16, 2023","text":""},{"location":"release-notes/#added_3","title":"Added","text":"
  • Introduced the publish to PyPI workflow, automating the uploading of package builds when new releases are created.
  • Added the mkdocs-git-authors plugin to display git authors of a markdown page in the documentation.
  • Added badges to the main page of the documentation as well as the readme file.
  • Added a code of conduct for the project, using the Contributor Covenant v2.1.
  • Included a CITATION.cff file to facilitate academic citations.
"},{"location":"release-notes/#changed_5","title":"Changed","text":"
  • Renamed the tests.yml workflow to run-tests.yml.
  • Updated the deploy-docs.yml workflow with the mkdocs-git-authors plugin dependency.
  • Modified the mkdocs.yml config file by adding the site_url and site_author properties.
  • Updated the pyproject.toml file with the mkdocs-git-authors plugin dependency and python package keywords.
"},{"location":"release-notes/#fixed_5","title":"Fixed","text":"
  • Fixed the python version in the deploy-docs.yml workflow.
  • Resolved issues with relative links in the documentation.
"},{"location":"release-notes/#0.1.0","title":"v0.1.0 December 15, 2023","text":""},{"location":"release-notes/#initial-implementation","title":"Initial Implementation","text":"

\u2003\u2003This release introduces Pyventus v0.1.0, a modern and robust Python package for event-driven programming. Pyventus provides developers with a comprehensive suite of tools and utilities to define, emit, and orchestrate events. It empowers developers to build scalable, extensible, and loosely-coupled event-driven applications.

  • Implementation Details: The first implementation includes all the core functionalities of the package, encompassing events, event linkers, event emitters, event handlers, and more.
  • Testing and Coverage: This release includes a test suite that verifies the correctness of the package implementation. It also integrates code coverage, achieving 100% test coverage. The tests are configured to run automatically via GitHub Actions on both push and pull requests to the master branch.
  • Formatter and Lint Configuration: A formatter and lint configuration have been added to the project. This ensures consistent code style, maintainability, and adherence to the established coding standards defined in the project documentation.
  • Documentation: Additionally, this release includes comprehensive documentation for the package. The documentation covers the main page, a detailed getting started guide, tutorials, API reference, and release notes.

"},{"location":"api/","title":"API Reference","text":"

\u2003\u2003Welcome to the Pyventus API Reference, a comprehensive guide that provides detailed information about the classes, functions, parameters, attributes, and other components available in Pyventus.

\u2003\u2003In the API Reference, you will find detailed documentation for each component, including clear explanations, parameter details, return values, and usage examples. You can navigate through the reference using the search functionality or by browsing the different sections and categories to find the specific information you need.

Let's explore the Pyventus API Reference!

"},{"location":"api/event-handler/","title":"EventHandler class","text":"

A class that encapsulates event callbacks and provides a mechanism for executing them when the event occurs. This class manages both asynchronous and synchronous execution and handles event completion in both success and error scenarios.

Notes:

  • The __call__ method of the EventHandler class is an asynchronous method that returns a Coroutine. It should never be treated as a synchronous function.

  • This class is not intended to be subclassed or manually created. It is used internally to encapsulate the callbacks associated with an event and manage their execution.

  • The event handler can be invoked by calling the instance as a function and passing the required arguments.

Read more in the Pyventus docs for Event Handler.

Source code in pyventus/handlers/event_handler.py
@final\nclass EventHandler:\n    \"\"\"\n    A class that encapsulates event callbacks and provides a mechanism for executing them\n    when the event occurs. This class manages both asynchronous and synchronous execution\n    and handles event completion in both success and error scenarios.\n\n    **Notes:**\n\n    -   The `__call__` method of the `EventHandler` class is an asynchronous method\n        that returns a `Coroutine`. It should never be treated as a synchronous function.\n\n    -   This class is not intended to be subclassed or manually created. It is used\n        internally to encapsulate the callbacks associated with an event and manage\n        their execution.\n\n    -   The event handler can be invoked by calling the instance as a function and\n        passing the required arguments.\n\n    ---\n    Read more in the\n    [Pyventus docs for Event Handler](https://mdapena.github.io/pyventus/tutorials/event-linker/#event-handlers).\n    \"\"\"\n\n    @staticmethod\n    def get_callback_name(\n        callback: EventCallbackType | SuccessCallbackType | FailureCallbackType | None,  # type: ignore[type-arg]\n    ) -> str:\n        \"\"\"\n        Retrieves the name of the provided callback.\n        :param callback: The callback object.\n        :return: The name of the callback as a string.\n        \"\"\"\n        if callback is not None and hasattr(callback, \"__name__\"):\n            return callback.__name__\n        elif callback is not None and hasattr(callback, \"__class__\"):\n            return type(callback).__name__\n        else:\n            return \"None\"\n\n    @staticmethod\n    def validate_callback(\n        callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n    ) -> None:\n        \"\"\"\n        Validates whether the provided callback is a valid callable object.\n        :param callback: The callback to be validated.\n        :return: None\n        :raises PyventusException: If the callback is not a callable object.\n        \"\"\"\n        if not callable(callback):\n            raise PyventusException(\n                f\"'{callback.__name__ if hasattr(callback, '__name__') else callback}' is not a callable object.\"\n            )\n\n    @staticmethod\n    def is_async(\n        callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n    ) -> bool:\n        \"\"\"\n        Checks whether the provided callback is an asynchronous function or method.\n        :param callback: The callback to be checked.\n        :return: `True` if the callback is an asynchronous function or method, `False` otherwise.\n        :raises PyventusException: If the callback is not a callable or a string.\n        \"\"\"\n        if ismethod(callback) or isfunction(callback) or isbuiltin(callback):\n            return iscoroutinefunction(callback)\n        elif not isclass(callback) and hasattr(callback, \"__call__\"):  # A callable class instance\n            return iscoroutinefunction(callback.__call__)\n        else:\n            raise PyventusException(\"Expected a callable or a string, but got: {0}\".format(callback))\n\n    # Event handler attributes\n    __slots__ = (\n        \"_once\",\n        \"_force_async\",\n        \"_event_callback\",\n        \"_success_callback\",\n        \"_failure_callback\",\n        \"_is_event_callback_async\",\n        \"_is_success_callback_async\",\n        \"_is_failure_callback_async\",\n        \"_timestamp\",\n    )\n\n    @property\n    def once(self) -> bool:\n        \"\"\"\n        Determines if the event handler is a one-time subscription.\n        :return: A boolean value indicating if the event handler is\n            a one-time subscription.\n        \"\"\"\n        return self._once\n\n    @property\n    def force_async(self) -> bool:\n        \"\"\"\n        Determines whether all callbacks are forced to run asynchronously.\n        :return: A boolean value indicating if all callbacks are forced to run\n            asynchronously. If `True`, synchronous callbacks will be converted to\n            run asynchronously in a thread pool, using the `asyncio.to_thread`\n            function. If `False`, callbacks will run synchronously or\n            asynchronously as defined.\n        \"\"\"\n        return self._force_async\n\n    @property\n    def timestamp(self) -> datetime:\n        \"\"\"\n        Retrieves the timestamp when the event handler was created.\n        :return: The timestamp when the event handler was created.\n        \"\"\"\n        return self._timestamp\n\n    def __init__(\n        self,\n        once: bool,\n        force_async: bool,\n        event_callback: EventCallbackType,  # type: ignore[type-arg]\n        success_callback: SuccessCallbackType | None = None,\n        failure_callback: FailureCallbackType | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `EventHandler`.\n        :param once: Specifies if the event handler is a one-time subscription.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :param event_callback: The callback to be executed when the event occurs.\n        :param success_callback: The callback to be executed when the event execution\n            completes successfully. Default is `None`.\n        :param failure_callback: The callback to be executed when the event execution\n            fails. Default is `None`.\n        :raises PyventusException: If the provided callbacks are invalid.\n        \"\"\"\n        # Validate callbacks\n        EventHandler.validate_callback(callback=event_callback)\n\n        if success_callback is not None:\n            EventHandler.validate_callback(callback=success_callback)\n\n        if failure_callback is not None:\n            EventHandler.validate_callback(callback=failure_callback)\n\n        # Validate flags\n        if not isinstance(once, bool):\n            raise PyventusException(\"The 'once' argument must be a boolean value.\")\n        if not isinstance(force_async, bool):\n            raise PyventusException(\"The 'force_async' argument must be a boolean value.\")\n\n        # Set the event handler flags\n        self._once: bool = once\n        self._force_async: bool = force_async\n\n        # Set the event handler callbacks\n        self._event_callback: EventCallbackType = event_callback  # type: ignore[type-arg]\n        self._success_callback: SuccessCallbackType | None = success_callback\n        self._failure_callback: FailureCallbackType | None = failure_callback\n\n        # Set the event handler callbacks flags\n        self._is_event_callback_async: bool = EventHandler.is_async(event_callback)\n        self._is_success_callback_async: bool | None = (\n            EventHandler.is_async(success_callback) if success_callback else None\n        )\n        self._is_failure_callback_async: bool | None = (\n            EventHandler.is_async(failure_callback) if failure_callback else None\n        )\n\n        # Set the event handler timestamp\n        self._timestamp: datetime = datetime.now()\n\n    async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> None:\n        \"\"\"\n        Executes the event flow by invoking the associated callbacks.\n        :param args: Positional arguments to be passed to the event callback.\n        :param kwargs: Keyword arguments to be passed to the event callback.\n        :return: Coroutine\n        \"\"\"\n        # Event callback results\n        results: Any | None = None\n\n        try:\n            # Invoke the event callback.\n            if self._is_event_callback_async:\n                results = await self._event_callback(*args, **kwargs)\n            elif self._force_async:\n                results = await to_thread(self._event_callback, *args, **kwargs)\n            else:\n                results = self._event_callback(*args, **kwargs)\n        except Exception as exception:\n            # Log the exception with error level\n            StdOutLogger.error(name=f\"{self.__class__.__name__}\", action=\"Exception:\", msg=f\"{exception}\")\n\n            # Invoke the failure callback and pass the exception.\n            if self._failure_callback:\n                if self._is_failure_callback_async:\n                    await self._failure_callback(exception)\n                elif self._force_async:\n                    await to_thread(self._failure_callback, exception)\n                else:\n                    self._failure_callback(exception)\n        else:\n            # Invoke the success callback and pass the results, if any.\n            if self._success_callback:\n                if self._is_success_callback_async:\n                    if results is None:\n                        await self._success_callback()\n                    else:\n                        await self._success_callback(results)\n                elif self._force_async:\n                    if results is None:\n                        await to_thread(self._success_callback)\n                    else:\n                        await to_thread(self._success_callback, results)\n                else:\n                    if results is None:\n                        self._success_callback()\n                    else:\n                        self._success_callback(results)\n\n    def __str__(self) -> str:\n        \"\"\"\n        Returns a formatted string representation of the event handler.\n        :return: A string representation of the event handler.\n        \"\"\"\n        return \"\".join(\n            [\n                f\"Event Callback: `{EventHandler.get_callback_name(callback=self._event_callback)}\",\n                \"` (Async) | \" if self._is_event_callback_async else \"` (Sync) | \",\n                (\n                    \"Success Callback: `\".join(\n                        [\n                            EventHandler.get_callback_name(callback=self._success_callback),\n                            \"` (Async) | \" if self._is_success_callback_async else \"` (Sync) | \",\n                        ]\n                    )\n                    if self._success_callback\n                    else \"\"\n                ),\n                (\n                    \"Failure Callback: `\".join(\n                        [\n                            EventHandler.get_callback_name(callback=self._failure_callback),\n                            \"` (Async) | \" if self._is_failure_callback_async else \"` (Sync) | \",\n                        ]\n                    )\n                    if self._failure_callback\n                    else \"\"\n                ),\n                f\"Once: {self.once} | \",\n                f\"Force Async: {self.force_async} | \",\n                f\"Timestamp: {self.timestamp.strftime('%Y-%m-%d %I:%M:%S %p')}\",\n            ]\n        )\n

"},{"location":"api/event-handler/#pyventus.EventHandler-attributes","title":"Attributes","text":""},{"location":"api/event-handler/#pyventus.EventHandler.once","title":"once property","text":"
once: bool\n

Determines if the event handler is a one-time subscription.

RETURNS DESCRIPTION bool

A boolean value indicating if the event handler is a one-time subscription.

"},{"location":"api/event-handler/#pyventus.EventHandler.force_async","title":"force_async property","text":"
force_async: bool\n

Determines whether all callbacks are forced to run asynchronously.

RETURNS DESCRIPTION bool

A boolean value indicating if all callbacks are forced to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

"},{"location":"api/event-handler/#pyventus.EventHandler.timestamp","title":"timestamp property","text":"
timestamp: datetime\n

Retrieves the timestamp when the event handler was created.

RETURNS DESCRIPTION datetime

The timestamp when the event handler was created.

"},{"location":"api/event-handler/#pyventus.EventHandler-functions","title":"Functions","text":""},{"location":"api/event-handler/#pyventus.EventHandler.get_callback_name","title":"get_callback_name staticmethod","text":"
get_callback_name(callback: EventCallbackType | SuccessCallbackType | FailureCallbackType | None) -> str\n

Retrieves the name of the provided callback.

PARAMETER DESCRIPTION callback

The callback object.

TYPE: EventCallbackType | SuccessCallbackType | FailureCallbackType | None

RETURNS DESCRIPTION str

The name of the callback as a string.

Source code in pyventus/handlers/event_handler.py
@staticmethod\ndef get_callback_name(\n    callback: EventCallbackType | SuccessCallbackType | FailureCallbackType | None,  # type: ignore[type-arg]\n) -> str:\n    \"\"\"\n    Retrieves the name of the provided callback.\n    :param callback: The callback object.\n    :return: The name of the callback as a string.\n    \"\"\"\n    if callback is not None and hasattr(callback, \"__name__\"):\n        return callback.__name__\n    elif callback is not None and hasattr(callback, \"__class__\"):\n        return type(callback).__name__\n    else:\n        return \"None\"\n
"},{"location":"api/event-handler/#pyventus.EventHandler.validate_callback","title":"validate_callback staticmethod","text":"
validate_callback(callback: EventCallbackType | SuccessCallbackType | FailureCallbackType) -> None\n

Validates whether the provided callback is a valid callable object.

PARAMETER DESCRIPTION callback

The callback to be validated.

TYPE: EventCallbackType | SuccessCallbackType | FailureCallbackType

RETURNS DESCRIPTION None

None

RAISES DESCRIPTION PyventusException

If the callback is not a callable object.

Source code in pyventus/handlers/event_handler.py
@staticmethod\ndef validate_callback(\n    callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n) -> None:\n    \"\"\"\n    Validates whether the provided callback is a valid callable object.\n    :param callback: The callback to be validated.\n    :return: None\n    :raises PyventusException: If the callback is not a callable object.\n    \"\"\"\n    if not callable(callback):\n        raise PyventusException(\n            f\"'{callback.__name__ if hasattr(callback, '__name__') else callback}' is not a callable object.\"\n        )\n
"},{"location":"api/event-handler/#pyventus.EventHandler.is_async","title":"is_async staticmethod","text":"
is_async(callback: EventCallbackType | SuccessCallbackType | FailureCallbackType) -> bool\n

Checks whether the provided callback is an asynchronous function or method.

PARAMETER DESCRIPTION callback

The callback to be checked.

TYPE: EventCallbackType | SuccessCallbackType | FailureCallbackType

RETURNS DESCRIPTION bool

True if the callback is an asynchronous function or method, False otherwise.

RAISES DESCRIPTION PyventusException

If the callback is not a callable or a string.

Source code in pyventus/handlers/event_handler.py
@staticmethod\ndef is_async(\n    callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n) -> bool:\n    \"\"\"\n    Checks whether the provided callback is an asynchronous function or method.\n    :param callback: The callback to be checked.\n    :return: `True` if the callback is an asynchronous function or method, `False` otherwise.\n    :raises PyventusException: If the callback is not a callable or a string.\n    \"\"\"\n    if ismethod(callback) or isfunction(callback) or isbuiltin(callback):\n        return iscoroutinefunction(callback)\n    elif not isclass(callback) and hasattr(callback, \"__call__\"):  # A callable class instance\n        return iscoroutinefunction(callback.__call__)\n    else:\n        raise PyventusException(\"Expected a callable or a string, but got: {0}\".format(callback))\n
"},{"location":"api/event-handler/#pyventus.EventHandler.__init__","title":"__init__","text":"
__init__(once: bool, force_async: bool, event_callback: EventCallbackType, success_callback: SuccessCallbackType | None = None, failure_callback: FailureCallbackType | None = None) -> None\n

Initialize an instance of EventHandler.

PARAMETER DESCRIPTION once

Specifies if the event handler is a one-time subscription.

TYPE: bool

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool

event_callback

The callback to be executed when the event occurs.

TYPE: EventCallbackType

success_callback

The callback to be executed when the event execution completes successfully. Default is None.

TYPE: SuccessCallbackType | None DEFAULT: None

failure_callback

The callback to be executed when the event execution fails. Default is None.

TYPE: FailureCallbackType | None DEFAULT: None

RAISES DESCRIPTION PyventusException

If the provided callbacks are invalid.

Source code in pyventus/handlers/event_handler.py
def __init__(\n    self,\n    once: bool,\n    force_async: bool,\n    event_callback: EventCallbackType,  # type: ignore[type-arg]\n    success_callback: SuccessCallbackType | None = None,\n    failure_callback: FailureCallbackType | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `EventHandler`.\n    :param once: Specifies if the event handler is a one-time subscription.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :param event_callback: The callback to be executed when the event occurs.\n    :param success_callback: The callback to be executed when the event execution\n        completes successfully. Default is `None`.\n    :param failure_callback: The callback to be executed when the event execution\n        fails. Default is `None`.\n    :raises PyventusException: If the provided callbacks are invalid.\n    \"\"\"\n    # Validate callbacks\n    EventHandler.validate_callback(callback=event_callback)\n\n    if success_callback is not None:\n        EventHandler.validate_callback(callback=success_callback)\n\n    if failure_callback is not None:\n        EventHandler.validate_callback(callback=failure_callback)\n\n    # Validate flags\n    if not isinstance(once, bool):\n        raise PyventusException(\"The 'once' argument must be a boolean value.\")\n    if not isinstance(force_async, bool):\n        raise PyventusException(\"The 'force_async' argument must be a boolean value.\")\n\n    # Set the event handler flags\n    self._once: bool = once\n    self._force_async: bool = force_async\n\n    # Set the event handler callbacks\n    self._event_callback: EventCallbackType = event_callback  # type: ignore[type-arg]\n    self._success_callback: SuccessCallbackType | None = success_callback\n    self._failure_callback: FailureCallbackType | None = failure_callback\n\n    # Set the event handler callbacks flags\n    self._is_event_callback_async: bool = EventHandler.is_async(event_callback)\n    self._is_success_callback_async: bool | None = (\n        EventHandler.is_async(success_callback) if success_callback else None\n    )\n    self._is_failure_callback_async: bool | None = (\n        EventHandler.is_async(failure_callback) if failure_callback else None\n    )\n\n    # Set the event handler timestamp\n    self._timestamp: datetime = datetime.now()\n
"},{"location":"api/event-handler/#pyventus.EventHandler.__call__","title":"__call__ async","text":"
__call__(*args: args, **kwargs: kwargs) -> None\n

Executes the event flow by invoking the associated callbacks.

PARAMETER DESCRIPTION args

Positional arguments to be passed to the event callback.

TYPE: args DEFAULT: ()

kwargs

Keyword arguments to be passed to the event callback.

TYPE: kwargs DEFAULT: {}

RETURNS DESCRIPTION None

Coroutine

Source code in pyventus/handlers/event_handler.py
async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> None:\n    \"\"\"\n    Executes the event flow by invoking the associated callbacks.\n    :param args: Positional arguments to be passed to the event callback.\n    :param kwargs: Keyword arguments to be passed to the event callback.\n    :return: Coroutine\n    \"\"\"\n    # Event callback results\n    results: Any | None = None\n\n    try:\n        # Invoke the event callback.\n        if self._is_event_callback_async:\n            results = await self._event_callback(*args, **kwargs)\n        elif self._force_async:\n            results = await to_thread(self._event_callback, *args, **kwargs)\n        else:\n            results = self._event_callback(*args, **kwargs)\n    except Exception as exception:\n        # Log the exception with error level\n        StdOutLogger.error(name=f\"{self.__class__.__name__}\", action=\"Exception:\", msg=f\"{exception}\")\n\n        # Invoke the failure callback and pass the exception.\n        if self._failure_callback:\n            if self._is_failure_callback_async:\n                await self._failure_callback(exception)\n            elif self._force_async:\n                await to_thread(self._failure_callback, exception)\n            else:\n                self._failure_callback(exception)\n    else:\n        # Invoke the success callback and pass the results, if any.\n        if self._success_callback:\n            if self._is_success_callback_async:\n                if results is None:\n                    await self._success_callback()\n                else:\n                    await self._success_callback(results)\n            elif self._force_async:\n                if results is None:\n                    await to_thread(self._success_callback)\n                else:\n                    await to_thread(self._success_callback, results)\n            else:\n                if results is None:\n                    self._success_callback()\n                else:\n                    self._success_callback(results)\n
"},{"location":"api/event-linker/","title":"EventLinker class","text":"

A base class that acts as a global registry for events and callbacks linkage. It provides a centralized mechanism for managing event subscriptions, unsubscriptions, and retrieval of events and their associated event handlers.

Notes:

  • The EventLinker class can be subclassed to create specific namespaces or contexts for managing events and event handlers separately. By subclassing the EventLinker, users can organize event subscriptions and handlers within different scopes, providing modularity and flexibility in event management. Subclassing also allows users to configure settings of the EventLinker to suit their specific use cases.

  • The EventLinker has been implemented with thread safety in mind. All of its methods synchronize access to prevent race conditions when managing events and event handlers across multiple threads. This ensures that concurrent operations on the EventLinker are properly synchronized, avoiding data inconsistencies and race conditions.

Read more in the Pyventus docs for Event Linker.

Source code in pyventus/linkers/event_linker.py
class EventLinker:\n    \"\"\"\n    A base class that acts as a global registry for events and callbacks linkage. It provides\n    a centralized mechanism for managing event subscriptions, unsubscriptions, and retrieval\n    of events and their associated event handlers.\n\n    **Notes:**\n\n    -   The `EventLinker` class can be subclassed to create specific namespaces or contexts\n        for managing events and event handlers separately. By subclassing the `EventLinker`,\n        users can organize event subscriptions and handlers within different scopes, providing\n        modularity and flexibility in event management. Subclassing also allows users to\n        configure settings of the `EventLinker` to suit their specific use cases.\n\n    -   The `EventLinker` has been implemented with *thread safety* in mind. All of its methods\n        synchronize access to prevent race conditions when managing events and event handlers\n        across multiple threads. This ensures that concurrent operations on the `EventLinker`\n        are properly synchronized, avoiding data inconsistencies and race conditions.\n\n    ---\n    Read more in the\n    [Pyventus docs for Event Linker](https://mdapena.github.io/pyventus/tutorials/event-linker/).\n    \"\"\"\n\n    @final\n    class EventLinkageWrapper:\n        \"\"\"\n        A class that serves as a wrapper for event linking operations, providing a simplified\n        interface for subscribing events with their corresponding callbacks.\n\n        **Notes:**\n\n        -   This class can be used as either a decorator or a context manager. When used as a\n            decorator, it automatically subscribes the decorated callback to the provided events.\n            When used as a context manager with the `with` statement, it allows multiple callbacks\n            to be associated with the provided events within the context block.\n\n        -   This class is not intended to be subclassed or manually created.\n            The `EventLinkageWrapper` is used internally as a wrapper for event\n            linking operations.\n        \"\"\"\n\n        # Event linkage wrapper attributes\n        __slots__ = (\n            \"_event_linker\",\n            \"_events\",\n            \"_once\",\n            \"_force_async\",\n            \"_event_callback\",\n            \"_success_callback\",\n            \"_failure_callback\",\n        )\n\n        @property\n        def on_event(self) -> Callable[[EventCallbackType], EventCallbackType]:  # type: ignore[type-arg]\n            \"\"\"\n            Decorator that sets the main callback for the event. This callback\n            will be invoked when the associated event occurs.\n            :return: The decorated callback.\n            \"\"\"\n\n            def _wrapper(callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n                self._event_callback = callback\n                return callback\n\n            return _wrapper\n\n        @property\n        def on_success(self) -> Callable[[SuccessCallbackType], SuccessCallbackType]:\n            \"\"\"\n            Decorator that sets the success callback. This callback will be\n            invoked when the event execution completes successfully.\n            :return: The decorated callback.\n            \"\"\"\n\n            def _wrapper(callback: SuccessCallbackType) -> SuccessCallbackType:\n                self._success_callback = callback\n                return callback\n\n            return _wrapper\n\n        @property\n        def on_failure(self) -> Callable[[FailureCallbackType], FailureCallbackType]:\n            \"\"\"\n            Decorator that sets the failure callback. This callback\n            will be invoked when the event execution fails.\n            :return: The decorated callback.\n            \"\"\"\n\n            def _wrapper(callback: FailureCallbackType) -> FailureCallbackType:\n                self._failure_callback = callback\n                return callback\n\n            return _wrapper\n\n        def __init__(\n            self,\n            *events: SubscribableEventType,\n            event_linker: Type[\"EventLinker\"],\n            force_async: bool,\n            once: bool,\n        ) -> None:\n            \"\"\"\n            Initialize an instance of `EventLinkageWrapper`.\n            :param events: The events to subscribe/link to.\n            :param event_linker: The event linker instance used for subscription.\n            :param force_async: Determines whether to force all callbacks to run asynchronously.\n            :param once: Specifies if the callback is a one-time subscription.\n            \"\"\"\n            self._event_linker: Type[EventLinker] = event_linker\n            self._events: Tuple[SubscribableEventType, ...] = events\n\n            self._once: bool = once\n            self._force_async: bool = force_async\n            self._event_callback: EventCallbackType | None = None  # type: ignore[type-arg, no-redef, assignment]\n            self._success_callback: SuccessCallbackType | None = None  # type: ignore[no-redef, assignment]\n            self._failure_callback: FailureCallbackType | None = None  # type: ignore[no-redef, assignment]\n\n        def __call__(self, callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n            \"\"\"\n            Subscribes the provided events to the decorated callback.\n            :param callback: The callback to associate with the events.\n            :return: The decorated callback.\n            \"\"\"\n            self._event_callback = callback\n            self._event_linker.subscribe(\n                *self._events,\n                event_callback=self._event_callback,\n                success_callback=None,\n                failure_callback=None,\n                force_async=self._force_async,\n                once=self._once,\n            )\n            del self\n            return callback\n\n        def __enter__(self) -> \"EventLinker.EventLinkageWrapper\":\n            \"\"\"\n            Enters the linkage context block, allowing multiple\n            callbacks to be associated with the events.\n            :return: The context manager object\n            \"\"\"\n            return self\n\n        def __exit__(\n            self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n        ) -> None:\n            \"\"\"\n            Exits the linkage context block, subscribing the provided callbacks within\n            the context to the specified events. Performs any necessary cleanup.\n            :param exc_type: The type of the exception raised, if any.\n            :param exc_val: The exception object raised, if any.\n            :param exc_tb: The traceback information, if any.\n            :return: None\n            \"\"\"\n            self._event_linker.subscribe(\n                *self._events,\n                event_callback=self._event_callback,\n                success_callback=self._success_callback,\n                failure_callback=self._failure_callback,\n                force_async=self._force_async,\n                once=self._once,\n            )\n            del self\n\n    __registry: Dict[str, List[EventHandler]] = {}\n    \"\"\" \n    A dictionary that serves as a container for storing events and their associated event \n    handlers. The keys represent registered event names, and the values are lists of event\n    handler objects associated with each event.\n    \"\"\"\n\n    __max_event_handlers: int | None = None\n    \"\"\"The maximum number of `EventHandlers` allowed per event, or `None` if there is no limit.\"\"\"\n\n    __default_success_callback: SuccessCallbackType | None = None\n    \"\"\" \n    Represents the default success callback function that will be assigned to event handlers in \n    the absence of a specific success callback. This callback will be executed upon successful \n    completion of the event execution in each event handler.\n    \"\"\"\n\n    __default_failure_callback: FailureCallbackType | None = None\n    \"\"\"\n    Represents the default failure callback function that will be assigned to event handlers in \n    the absence of a specific failure callback. This callback will be executed when the event \n    execution fails in each event handler.\n    \"\"\"\n\n    __thread_lock: Lock = Lock()\n    \"\"\"\n    A `threading.Lock` object used for thread synchronization when accessing and modifying the \n    event registry to ensure thread safety. It prevents multiple threads from accessing and \n    modifying the registry simultaneously.\n    \"\"\"\n\n    __logger: Logger = Logger(name=\"EventLinker\", debug=bool(gettrace() is not None))\n    \"\"\"\n    The logger used to debug and log information within the `EventLinker` class. The debug mode\n    of the logger depends on the execution environment and the value returned by the `gettrace()`\n    function. The debug mode can also be influenced by subclassing and overridden in subclasses.\n    \"\"\"\n\n    def __init_subclass__(\n        cls,\n        max_event_handlers: int | None = None,\n        default_success_callback: SuccessCallbackType | None = None,\n        default_failure_callback: FailureCallbackType | None = None,\n        debug: bool | None = None,\n        **kwargs: Any,\n    ) -> None:\n        \"\"\"\n        Initialize a subclass of `EventLinker`.\n\n        By default, this method sets up the main registry and thread lock object, but\n        it can also be used to configure specific settings of the `EventLinker` subclass.\n\n        :param max_event_handlers: The maximum number of event handlers allowed per event,\n            or `None` if there is no limit.\n        :param default_success_callback: The default callback to assign as the success\n            callback in the event handlers when no specific success callback is provided.\n        :param default_failure_callback: The default callback to assign as the failure\n            callback in the event handlers when no specific failure callback is provided.\n        :param debug: Specifies the debug mode for the subclass logger. If `None`,\n            it is determined based on the execution environment.\n        :param kwargs: The keyword arguments to pass to the superclass\n            `__init_subclass__` method.\n        :raises PyventusException: If `max_event_handlers` is less than 1 or\n            if the provided callbacks are invalid.\n        :return: None\n        \"\"\"\n        # Call the parent class' __init_subclass__ method\n        super().__init_subclass__(**kwargs)\n\n        # Initialize the main registry\n        cls.__registry = {}\n\n        # Create a lock object for thread synchronization\n        cls.__thread_lock = Lock()\n\n        # Validate the max_event_handlers argument\n        if max_event_handlers is not None and max_event_handlers < 1:\n            raise PyventusException(\"The 'max_event_handlers' argument must be greater than or equal to 1.\")\n\n        # Set the maximum number of event handlers per event\n        cls.__max_event_handlers = max_event_handlers\n\n        # Validate the default success callback, if any\n        if default_success_callback is not None:\n            EventHandler.validate_callback(callback=default_success_callback)\n\n        # Set the default success callback\n        cls.__default_success_callback = default_success_callback\n\n        # Validate the default failure callback, if any\n        if default_failure_callback is not None:\n            EventHandler.validate_callback(callback=default_failure_callback)\n\n        # Set the default failure callback\n        cls.__default_failure_callback = default_failure_callback\n\n        # Validate the debug argument\n        if debug is not None and not isinstance(debug, bool):\n            raise PyventusException(\"The 'debug' argument must be a boolean value.\")\n\n        # Set up the logger\n        cls.__logger = Logger(\n            name=cls.__name__,\n            debug=debug if debug is not None else bool(gettrace() is not None),\n        )\n\n    @classmethod\n    def _get_logger(cls) -> Logger:\n        \"\"\"\n        Retrieve the class-level logger instance.\n        :return: The class-level logger instance used to debug and log\n            information within the `EventLinker` class.\n        \"\"\"\n        return cls.__logger\n\n    @classmethod\n    def get_event_name(cls, event: SubscribableEventType) -> str:\n        \"\"\"\n        Determines the name of the event.\n        :param event: The event to obtain the name for.\n        :return: A string that represents the event name.\n        :raises PyventusException: If the `event` argument is invalid\n            or if the event is not supported.\n        \"\"\"\n        # Validate the event argument\n        if event is None:\n            raise PyventusException(\"The 'event' argument cannot be None.\")\n\n        if event is Ellipsis:\n            # If the event is Ellipsis, return its type name\n            return type(event).__name__\n        elif isinstance(event, str):\n            if not event:\n                raise PyventusException(\"String events cannot be empty.\")\n            # If the event is a non-empty string, return it as the event name\n            return event\n        elif isinstance(event, type):\n            if not is_dataclass(event) and not issubclass(event, Exception):\n                raise PyventusException(\"Type events must be either a dataclass or an exception.\")\n            # If the event is either a dataclass type or an exception type, return its type name\n            return event.__name__\n        else:\n            # If the event is not supported, raise an exception\n            raise PyventusException(\"Unsupported event\")\n\n    @classmethod\n    def get_max_event_handlers(cls) -> int | None:\n        \"\"\"\n        Retrieve the maximum number of event handlers allowed per event.\n        :return: The maximum number of event handlers or `None` if there is no limit.\n        \"\"\"\n        return cls.__max_event_handlers\n\n    @classmethod\n    def get_default_success_callback(cls) -> SuccessCallbackType | None:\n        \"\"\"\n        Retrieve the default callback to be assigned as the success callback\n        in the event handlers when no specific success callback is provided.\n        :return: The default success callback or `None` if not set.\n        \"\"\"\n        return cls.__default_success_callback\n\n    @classmethod\n    def get_default_failure_callback(cls) -> FailureCallbackType | None:\n        \"\"\"\n        Retrieve the default callback to be assigned as the failure callback\n        in the event handlers when no specific failure callback is provided.\n        :return: The default failure callback or `None` if not set.\n        \"\"\"\n        return cls.__default_failure_callback\n\n    @classmethod\n    def get_registry(cls) -> Mapping[str, List[EventHandler]]:\n        \"\"\"\n        Retrieve the main registry mapping.\n        :return: A mapping of event names to event handlers.\n        \"\"\"\n        with cls.__thread_lock:\n            return {event_name: list(event_handlers) for event_name, event_handlers in cls.__registry.items()}\n\n    @classmethod\n    def get_events(cls) -> List[str]:\n        \"\"\"\n        Retrieve a list of all the registered events.\n        :return: A list of event names.\n        \"\"\"\n        with cls.__thread_lock:\n            return list(cls.__registry.keys())\n\n    @classmethod\n    def get_event_handlers(cls) -> List[EventHandler]:\n        \"\"\"\n        Retrieve a list of non-duplicated event handlers\n        that have been registered across all events.\n        :return: A list of event handlers.\n        \"\"\"\n        with cls.__thread_lock:\n            return list(\n                {event_handler for event_handlers in cls.__registry.values() for event_handler in event_handlers}\n            )\n\n    @classmethod\n    def get_events_by_event_handler(cls, event_handler: EventHandler) -> List[str]:\n        \"\"\"\n        Retrieve a list of event names associated with the provided event handler.\n        :param event_handler: The handler to retrieve the associated events for.\n        :return: A list of event names.\n        :raise PyventusException: If the `event_handler` argument is `None` or invalid.\n        \"\"\"\n        # Validate the event_handler argument\n        if event_handler is None:\n            raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n        if not isinstance(event_handler, EventHandler):\n            raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n        with cls.__thread_lock:\n            return [\n                event_name for event_name, event_handlers in cls.__registry.items() if event_handler in event_handlers\n            ]\n\n    @classmethod\n    def get_event_handlers_by_events(cls, *events: SubscribableEventType) -> List[EventHandler]:\n        \"\"\"\n        Retrieve a list of non-duplicated event handlers associated with the provided events.\n        :param events: Events to retrieve the event handlers for.\n        :return: A list of event handlers.\n        :raise PyventusException: If the `events` argument is `None`, empty or unsupported.\n        \"\"\"\n        # Validate the events argument\n        if events is None or len(events) <= 0:\n            raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n        # Retrieve all unique event names\n        event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n        with cls.__thread_lock:\n            return list(\n                {event_handler for event_name in event_names for event_handler in cls.__registry.get(event_name, [])}\n            )\n\n    @classmethod\n    def once(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n        \"\"\"\n        Decorator that allows you to conveniently subscribe callbacks to the provided events\n        for a single invocation.\n\n        This method can be used as either a decorator or a context manager. When used as a\n        decorator, it automatically subscribes the decorated callback to the provided events.\n        When used as a context manager with the `with` statement, it allows multiple callbacks\n        to be associated with the provided events within the context block.\n\n        :param events: The events to subscribe to.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :return: The decorator that wraps the callback.\n        \"\"\"\n        return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=True)\n\n    @classmethod\n    def on(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n        \"\"\"\n        Decorator that allows you to conveniently subscribe callbacks to the provided events.\n\n        This method can be used as either a decorator or a context manager. When used as a\n        decorator, it automatically subscribes the decorated callback to the provided events.\n        When used as a context manager with the `with` statement, it allows multiple callbacks\n        to be associated with the provided events within the context block.\n\n        :param events: The events to subscribe to.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :return: The decorator that wraps the callback.\n        \"\"\"\n        return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=False)\n\n    @classmethod\n    def subscribe(\n        cls,\n        *events: SubscribableEventType,\n        event_callback: EventCallbackType,  # type: ignore[type-arg]\n        success_callback: SuccessCallbackType | None = None,\n        failure_callback: FailureCallbackType | None = None,\n        force_async: bool = False,\n        once: bool = False,\n    ) -> EventHandler:\n        \"\"\"\n        Subscribes callbacks to the provided events.\n        :param events: The events to subscribe to.\n        :param event_callback: The callback to be executed when the event occurs.\n        :param success_callback: The callback to be executed when the event execution completes\n            successfully.\n        :param failure_callback: The callback to be executed when the event execution fails.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :param once: Specifies if the event handler is a one-time subscription.\n        :return: The event handler object associated with the given events.\n        \"\"\"\n        # Validate the events argument\n        if events is None or len(events) <= 0:\n            raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n        # Retrieve all unique event names\n        event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # Check if the maximum number of handlers property is set\n            if cls.__max_event_handlers is not None:\n                # For each event name, check if the maximum number of handlers for the event has been exceeded\n                for event_name in event_names:\n                    if len(cls.__registry.get(event_name, [])) >= cls.__max_event_handlers:\n                        raise PyventusException(\n                            f\"The event '{event_name}' has exceeded the maximum number of handlers allowed. The \"\n                            f\"'{EventHandler.get_callback_name(callback=event_callback)}'\"\n                            f\" callback cannot be subscribed.\"\n                        )\n\n            # Create a new event handler\n            event_handler: EventHandler = EventHandler(\n                event_callback=event_callback,\n                success_callback=success_callback if success_callback else cls.__default_success_callback,\n                failure_callback=failure_callback if failure_callback else cls.__default_failure_callback,\n                force_async=force_async,\n                once=once,\n            )\n\n            # For each event name, register the event handler\n            for event_name in event_names:\n                # If the event name is not present in the main registry, create a new empty list for it\n                if event_name not in cls.__registry:\n                    cls.__registry[event_name] = []\n\n                # Append the event handler to the list of handlers for the event\n                cls.__registry[event_name].append(event_handler)\n\n                # Log the subscription if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(\n                        action=\"Subscribed:\",\n                        msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                    )\n\n        # Return the new event handler\n        return event_handler\n\n    @classmethod\n    def unsubscribe(cls, *events: SubscribableEventType, event_handler: EventHandler) -> bool:\n        \"\"\"\n        Unsubscribes an event handler from the provided events. If there are no more\n        handlers for a particular event, that event is also removed from the registry.\n        :param events: The events to unsubscribe from.\n        :param event_handler: The event handler to unsubscribe.\n        :return: `True` if the event handler associated with the events was found and\n            removed, `False` otherwise.\n        :raises PyventusException: If the `events` argument is `None`, empty, unsupported,\n            or if the `event_handler` argument is `None`, invalid.\n        \"\"\"\n        # Validate the events argument\n        if events is None or len(events) <= 0:\n            raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n        # Validate the event_handler argument\n        if event_handler is None:\n            raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n        if not isinstance(event_handler, EventHandler):\n            raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n        # Retrieve all unique event names\n        event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n        # A flag indicating whether the event handler was successfully removed\n        deleted: bool = False\n\n        # Obtain the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # For each event name, check and remove the event handler if found\n            for event_name in event_names:\n                # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n                event_handlers = cls.__registry.get(event_name, [])\n\n                # Check if the event handler is present in the list of handlers for the event\n                if event_handler in event_handlers:\n                    # Remove the event handler from the list of handlers\n                    event_handlers.remove(event_handler)\n                    deleted = True\n\n                    # If there are no more handlers for the event, remove the event name from the registry\n                    if not event_handlers:\n                        cls.__registry.pop(event_name)\n\n                    # Log the unsubscription if debug is enabled\n                    if cls.__logger.debug_enabled:  # pragma: no cover\n                        cls.__logger.debug(\n                            action=\"Unsubscribed:\",\n                            msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                        )\n\n        # Return the flag indicating whether the event handler was deleted\n        return deleted\n\n    @classmethod\n    def remove_event_handler(cls, event_handler: EventHandler) -> bool:\n        \"\"\"\n        Removes an event handler from all subscribed events. If there are no more\n        handlers for a particular event, that event is also removed from the registry.\n        :param event_handler: The event handler to remove.\n        :return: `True` if the event handler was found and removed, `False` otherwise.\n        :raises PyventusException: If the `event_handler` argument is `None` or invalid.\n        \"\"\"\n        # Validate the event_handler argument\n        if event_handler is None:\n            raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n        if not isinstance(event_handler, EventHandler):\n            raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n        # A flag indicating if the event handler gets removed\n        deleted: bool = False\n\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # Iterate through each event and its associated handlers in the main registry\n            for event_name in list(cls.__registry.keys()):\n                # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n                event_handlers = cls.__registry.get(event_name, [])\n\n                # Check if the event handler is present in the list of handlers for the event\n                if event_handler in event_handlers:\n                    # Remove the event handler from the list of handlers\n                    event_handlers.remove(event_handler)\n                    deleted = True\n\n                    # If there are no more handlers for the event, remove the event from the registry\n                    if not event_handlers:\n                        cls.__registry.pop(event_name)\n\n                    # Log the removal of the event handler if debug is enabled\n                    if cls.__logger.debug_enabled:  # pragma: no cover\n                        cls.__logger.debug(\n                            action=\"Handler Removed:\",\n                            msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                        )\n\n        # Return the flag indicating if the event handler was found and deleted\n        return deleted\n\n    @classmethod\n    def remove_event(cls, event: SubscribableEventType) -> bool:\n        \"\"\"\n        Removes an event from the registry, including all its event handler subscriptions.\n        :param event: The event to remove.\n        :return: `True` if the event was found and removed, `False` otherwise.\n        \"\"\"\n        # Get the event name\n        event_name: str = cls.get_event_name(event=event)\n\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # Check if the event name is present in the main registry\n            if event_name in cls.__registry:\n                # Remove the event from the registry\n                cls.__registry.pop(event_name)\n\n                # Log the removal of the event if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(action=\"Event Removed:\", msg=f\"{event_name}\")\n\n                # Return True to indicate successful removal\n                return True\n\n        return False\n\n    @classmethod\n    def remove_all(cls) -> bool:\n        \"\"\"\n        Removes all events and their associated event handlers from the registry.\n        :return: `True` if the events were found and removed, `False` otherwise.\n        \"\"\"\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            if cls.__registry:\n                # Clear the main registry\n                cls.__registry.clear()\n\n                # Log a debug message if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(msg=\"All events and handlers were successfully removed.\")\n\n                return True\n            else:\n                # Log a debug message if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(msg=\"The event registry is already empty.\")\n\n                return False\n

"},{"location":"api/event-linker/#pyventus.EventLinker-classes","title":"Classes","text":""},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper","title":"EventLinkageWrapper","text":"

A class that serves as a wrapper for event linking operations, providing a simplified interface for subscribing events with their corresponding callbacks.

Notes:

  • This class can be used as either a decorator or a context manager. When used as a decorator, it automatically subscribes the decorated callback to the provided events. When used as a context manager with the with statement, it allows multiple callbacks to be associated with the provided events within the context block.

  • This class is not intended to be subclassed or manually created. The EventLinkageWrapper is used internally as a wrapper for event linking operations.

Source code in pyventus/linkers/event_linker.py
@final\nclass EventLinkageWrapper:\n    \"\"\"\n    A class that serves as a wrapper for event linking operations, providing a simplified\n    interface for subscribing events with their corresponding callbacks.\n\n    **Notes:**\n\n    -   This class can be used as either a decorator or a context manager. When used as a\n        decorator, it automatically subscribes the decorated callback to the provided events.\n        When used as a context manager with the `with` statement, it allows multiple callbacks\n        to be associated with the provided events within the context block.\n\n    -   This class is not intended to be subclassed or manually created.\n        The `EventLinkageWrapper` is used internally as a wrapper for event\n        linking operations.\n    \"\"\"\n\n    # Event linkage wrapper attributes\n    __slots__ = (\n        \"_event_linker\",\n        \"_events\",\n        \"_once\",\n        \"_force_async\",\n        \"_event_callback\",\n        \"_success_callback\",\n        \"_failure_callback\",\n    )\n\n    @property\n    def on_event(self) -> Callable[[EventCallbackType], EventCallbackType]:  # type: ignore[type-arg]\n        \"\"\"\n        Decorator that sets the main callback for the event. This callback\n        will be invoked when the associated event occurs.\n        :return: The decorated callback.\n        \"\"\"\n\n        def _wrapper(callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n            self._event_callback = callback\n            return callback\n\n        return _wrapper\n\n    @property\n    def on_success(self) -> Callable[[SuccessCallbackType], SuccessCallbackType]:\n        \"\"\"\n        Decorator that sets the success callback. This callback will be\n        invoked when the event execution completes successfully.\n        :return: The decorated callback.\n        \"\"\"\n\n        def _wrapper(callback: SuccessCallbackType) -> SuccessCallbackType:\n            self._success_callback = callback\n            return callback\n\n        return _wrapper\n\n    @property\n    def on_failure(self) -> Callable[[FailureCallbackType], FailureCallbackType]:\n        \"\"\"\n        Decorator that sets the failure callback. This callback\n        will be invoked when the event execution fails.\n        :return: The decorated callback.\n        \"\"\"\n\n        def _wrapper(callback: FailureCallbackType) -> FailureCallbackType:\n            self._failure_callback = callback\n            return callback\n\n        return _wrapper\n\n    def __init__(\n        self,\n        *events: SubscribableEventType,\n        event_linker: Type[\"EventLinker\"],\n        force_async: bool,\n        once: bool,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `EventLinkageWrapper`.\n        :param events: The events to subscribe/link to.\n        :param event_linker: The event linker instance used for subscription.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n        :param once: Specifies if the callback is a one-time subscription.\n        \"\"\"\n        self._event_linker: Type[EventLinker] = event_linker\n        self._events: Tuple[SubscribableEventType, ...] = events\n\n        self._once: bool = once\n        self._force_async: bool = force_async\n        self._event_callback: EventCallbackType | None = None  # type: ignore[type-arg, no-redef, assignment]\n        self._success_callback: SuccessCallbackType | None = None  # type: ignore[no-redef, assignment]\n        self._failure_callback: FailureCallbackType | None = None  # type: ignore[no-redef, assignment]\n\n    def __call__(self, callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n        \"\"\"\n        Subscribes the provided events to the decorated callback.\n        :param callback: The callback to associate with the events.\n        :return: The decorated callback.\n        \"\"\"\n        self._event_callback = callback\n        self._event_linker.subscribe(\n            *self._events,\n            event_callback=self._event_callback,\n            success_callback=None,\n            failure_callback=None,\n            force_async=self._force_async,\n            once=self._once,\n        )\n        del self\n        return callback\n\n    def __enter__(self) -> \"EventLinker.EventLinkageWrapper\":\n        \"\"\"\n        Enters the linkage context block, allowing multiple\n        callbacks to be associated with the events.\n        :return: The context manager object\n        \"\"\"\n        return self\n\n    def __exit__(\n        self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n    ) -> None:\n        \"\"\"\n        Exits the linkage context block, subscribing the provided callbacks within\n        the context to the specified events. Performs any necessary cleanup.\n        :param exc_type: The type of the exception raised, if any.\n        :param exc_val: The exception object raised, if any.\n        :param exc_tb: The traceback information, if any.\n        :return: None\n        \"\"\"\n        self._event_linker.subscribe(\n            *self._events,\n            event_callback=self._event_callback,\n            success_callback=self._success_callback,\n            failure_callback=self._failure_callback,\n            force_async=self._force_async,\n            once=self._once,\n        )\n        del self\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper-attributes","title":"Attributes","text":""},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.on_event","title":"on_event property","text":"
on_event: Callable[[EventCallbackType], EventCallbackType]\n

Decorator that sets the main callback for the event. This callback will be invoked when the associated event occurs.

RETURNS DESCRIPTION Callable[[EventCallbackType], EventCallbackType]

The decorated callback.

"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.on_success","title":"on_success property","text":"
on_success: Callable[[SuccessCallbackType], SuccessCallbackType]\n

Decorator that sets the success callback. This callback will be invoked when the event execution completes successfully.

RETURNS DESCRIPTION Callable[[SuccessCallbackType], SuccessCallbackType]

The decorated callback.

"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.on_failure","title":"on_failure property","text":"
on_failure: Callable[[FailureCallbackType], FailureCallbackType]\n

Decorator that sets the failure callback. This callback will be invoked when the event execution fails.

RETURNS DESCRIPTION Callable[[FailureCallbackType], FailureCallbackType]

The decorated callback.

"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper-functions","title":"Functions","text":""},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__init__","title":"__init__","text":"
__init__(*events: SubscribableEventType, event_linker: Type[EventLinker], force_async: bool, once: bool) -> None\n

Initialize an instance of EventLinkageWrapper.

PARAMETER DESCRIPTION events

The events to subscribe/link to.

TYPE: SubscribableEventType DEFAULT: ()

event_linker

The event linker instance used for subscription.

TYPE: Type[EventLinker]

force_async

Determines whether to force all callbacks to run asynchronously.

TYPE: bool

once

Specifies if the callback is a one-time subscription.

TYPE: bool

Source code in pyventus/linkers/event_linker.py
def __init__(\n    self,\n    *events: SubscribableEventType,\n    event_linker: Type[\"EventLinker\"],\n    force_async: bool,\n    once: bool,\n) -> None:\n    \"\"\"\n    Initialize an instance of `EventLinkageWrapper`.\n    :param events: The events to subscribe/link to.\n    :param event_linker: The event linker instance used for subscription.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n    :param once: Specifies if the callback is a one-time subscription.\n    \"\"\"\n    self._event_linker: Type[EventLinker] = event_linker\n    self._events: Tuple[SubscribableEventType, ...] = events\n\n    self._once: bool = once\n    self._force_async: bool = force_async\n    self._event_callback: EventCallbackType | None = None  # type: ignore[type-arg, no-redef, assignment]\n    self._success_callback: SuccessCallbackType | None = None  # type: ignore[no-redef, assignment]\n    self._failure_callback: FailureCallbackType | None = None  # type: ignore[no-redef, assignment]\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__call__","title":"__call__","text":"
__call__(callback: EventCallbackType) -> EventCallbackType\n

Subscribes the provided events to the decorated callback.

PARAMETER DESCRIPTION callback

The callback to associate with the events.

TYPE: EventCallbackType

RETURNS DESCRIPTION EventCallbackType

The decorated callback.

Source code in pyventus/linkers/event_linker.py
def __call__(self, callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n    \"\"\"\n    Subscribes the provided events to the decorated callback.\n    :param callback: The callback to associate with the events.\n    :return: The decorated callback.\n    \"\"\"\n    self._event_callback = callback\n    self._event_linker.subscribe(\n        *self._events,\n        event_callback=self._event_callback,\n        success_callback=None,\n        failure_callback=None,\n        force_async=self._force_async,\n        once=self._once,\n    )\n    del self\n    return callback\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__enter__","title":"__enter__","text":"
__enter__() -> EventLinkageWrapper\n

Enters the linkage context block, allowing multiple callbacks to be associated with the events.

RETURNS DESCRIPTION EventLinkageWrapper

The context manager object

Source code in pyventus/linkers/event_linker.py
def __enter__(self) -> \"EventLinker.EventLinkageWrapper\":\n    \"\"\"\n    Enters the linkage context block, allowing multiple\n    callbacks to be associated with the events.\n    :return: The context manager object\n    \"\"\"\n    return self\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__exit__","title":"__exit__","text":"
__exit__(exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) -> None\n

Exits the linkage context block, subscribing the provided callbacks within the context to the specified events. Performs any necessary cleanup.

PARAMETER DESCRIPTION exc_type

The type of the exception raised, if any.

TYPE: Type[BaseException] | None

exc_val

The exception object raised, if any.

TYPE: BaseException | None

exc_tb

The traceback information, if any.

TYPE: TracebackType | None

RETURNS DESCRIPTION None

None

Source code in pyventus/linkers/event_linker.py
def __exit__(\n    self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n) -> None:\n    \"\"\"\n    Exits the linkage context block, subscribing the provided callbacks within\n    the context to the specified events. Performs any necessary cleanup.\n    :param exc_type: The type of the exception raised, if any.\n    :param exc_val: The exception object raised, if any.\n    :param exc_tb: The traceback information, if any.\n    :return: None\n    \"\"\"\n    self._event_linker.subscribe(\n        *self._events,\n        event_callback=self._event_callback,\n        success_callback=self._success_callback,\n        failure_callback=self._failure_callback,\n        force_async=self._force_async,\n        once=self._once,\n    )\n    del self\n
"},{"location":"api/event-linker/#pyventus.EventLinker-functions","title":"Functions","text":""},{"location":"api/event-linker/#pyventus.EventLinker.__init_subclass__","title":"__init_subclass__","text":"
__init_subclass__(max_event_handlers: int | None = None, default_success_callback: SuccessCallbackType | None = None, default_failure_callback: FailureCallbackType | None = None, debug: bool | None = None, **kwargs: Any) -> None\n

Initialize a subclass of EventLinker.

By default, this method sets up the main registry and thread lock object, but it can also be used to configure specific settings of the EventLinker subclass.

PARAMETER DESCRIPTION max_event_handlers

The maximum number of event handlers allowed per event, or None if there is no limit.

TYPE: int | None DEFAULT: None

default_success_callback

The default callback to assign as the success callback in the event handlers when no specific success callback is provided.

TYPE: SuccessCallbackType | None DEFAULT: None

default_failure_callback

The default callback to assign as the failure callback in the event handlers when no specific failure callback is provided.

TYPE: FailureCallbackType | None DEFAULT: None

debug

Specifies the debug mode for the subclass logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

kwargs

The keyword arguments to pass to the superclass __init_subclass__ method.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

RAISES DESCRIPTION PyventusException

If max_event_handlers is less than 1 or if the provided callbacks are invalid.

Source code in pyventus/linkers/event_linker.py
def __init_subclass__(\n    cls,\n    max_event_handlers: int | None = None,\n    default_success_callback: SuccessCallbackType | None = None,\n    default_failure_callback: FailureCallbackType | None = None,\n    debug: bool | None = None,\n    **kwargs: Any,\n) -> None:\n    \"\"\"\n    Initialize a subclass of `EventLinker`.\n\n    By default, this method sets up the main registry and thread lock object, but\n    it can also be used to configure specific settings of the `EventLinker` subclass.\n\n    :param max_event_handlers: The maximum number of event handlers allowed per event,\n        or `None` if there is no limit.\n    :param default_success_callback: The default callback to assign as the success\n        callback in the event handlers when no specific success callback is provided.\n    :param default_failure_callback: The default callback to assign as the failure\n        callback in the event handlers when no specific failure callback is provided.\n    :param debug: Specifies the debug mode for the subclass logger. If `None`,\n        it is determined based on the execution environment.\n    :param kwargs: The keyword arguments to pass to the superclass\n        `__init_subclass__` method.\n    :raises PyventusException: If `max_event_handlers` is less than 1 or\n        if the provided callbacks are invalid.\n    :return: None\n    \"\"\"\n    # Call the parent class' __init_subclass__ method\n    super().__init_subclass__(**kwargs)\n\n    # Initialize the main registry\n    cls.__registry = {}\n\n    # Create a lock object for thread synchronization\n    cls.__thread_lock = Lock()\n\n    # Validate the max_event_handlers argument\n    if max_event_handlers is not None and max_event_handlers < 1:\n        raise PyventusException(\"The 'max_event_handlers' argument must be greater than or equal to 1.\")\n\n    # Set the maximum number of event handlers per event\n    cls.__max_event_handlers = max_event_handlers\n\n    # Validate the default success callback, if any\n    if default_success_callback is not None:\n        EventHandler.validate_callback(callback=default_success_callback)\n\n    # Set the default success callback\n    cls.__default_success_callback = default_success_callback\n\n    # Validate the default failure callback, if any\n    if default_failure_callback is not None:\n        EventHandler.validate_callback(callback=default_failure_callback)\n\n    # Set the default failure callback\n    cls.__default_failure_callback = default_failure_callback\n\n    # Validate the debug argument\n    if debug is not None and not isinstance(debug, bool):\n        raise PyventusException(\"The 'debug' argument must be a boolean value.\")\n\n    # Set up the logger\n    cls.__logger = Logger(\n        name=cls.__name__,\n        debug=debug if debug is not None else bool(gettrace() is not None),\n    )\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_event_name","title":"get_event_name classmethod","text":"
get_event_name(event: SubscribableEventType) -> str\n

Determines the name of the event.

PARAMETER DESCRIPTION event

The event to obtain the name for.

TYPE: SubscribableEventType

RETURNS DESCRIPTION str

A string that represents the event name.

RAISES DESCRIPTION PyventusException

If the event argument is invalid or if the event is not supported.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_event_name(cls, event: SubscribableEventType) -> str:\n    \"\"\"\n    Determines the name of the event.\n    :param event: The event to obtain the name for.\n    :return: A string that represents the event name.\n    :raises PyventusException: If the `event` argument is invalid\n        or if the event is not supported.\n    \"\"\"\n    # Validate the event argument\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    if event is Ellipsis:\n        # If the event is Ellipsis, return its type name\n        return type(event).__name__\n    elif isinstance(event, str):\n        if not event:\n            raise PyventusException(\"String events cannot be empty.\")\n        # If the event is a non-empty string, return it as the event name\n        return event\n    elif isinstance(event, type):\n        if not is_dataclass(event) and not issubclass(event, Exception):\n            raise PyventusException(\"Type events must be either a dataclass or an exception.\")\n        # If the event is either a dataclass type or an exception type, return its type name\n        return event.__name__\n    else:\n        # If the event is not supported, raise an exception\n        raise PyventusException(\"Unsupported event\")\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_max_event_handlers","title":"get_max_event_handlers classmethod","text":"
get_max_event_handlers() -> int | None\n

Retrieve the maximum number of event handlers allowed per event.

RETURNS DESCRIPTION int | None

The maximum number of event handlers or None if there is no limit.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_max_event_handlers(cls) -> int | None:\n    \"\"\"\n    Retrieve the maximum number of event handlers allowed per event.\n    :return: The maximum number of event handlers or `None` if there is no limit.\n    \"\"\"\n    return cls.__max_event_handlers\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_default_success_callback","title":"get_default_success_callback classmethod","text":"
get_default_success_callback() -> SuccessCallbackType | None\n

Retrieve the default callback to be assigned as the success callback in the event handlers when no specific success callback is provided.

RETURNS DESCRIPTION SuccessCallbackType | None

The default success callback or None if not set.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_default_success_callback(cls) -> SuccessCallbackType | None:\n    \"\"\"\n    Retrieve the default callback to be assigned as the success callback\n    in the event handlers when no specific success callback is provided.\n    :return: The default success callback or `None` if not set.\n    \"\"\"\n    return cls.__default_success_callback\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_default_failure_callback","title":"get_default_failure_callback classmethod","text":"
get_default_failure_callback() -> FailureCallbackType | None\n

Retrieve the default callback to be assigned as the failure callback in the event handlers when no specific failure callback is provided.

RETURNS DESCRIPTION FailureCallbackType | None

The default failure callback or None if not set.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_default_failure_callback(cls) -> FailureCallbackType | None:\n    \"\"\"\n    Retrieve the default callback to be assigned as the failure callback\n    in the event handlers when no specific failure callback is provided.\n    :return: The default failure callback or `None` if not set.\n    \"\"\"\n    return cls.__default_failure_callback\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_registry","title":"get_registry classmethod","text":"
get_registry() -> Mapping[str, List[EventHandler]]\n

Retrieve the main registry mapping.

RETURNS DESCRIPTION Mapping[str, List[EventHandler]]

A mapping of event names to event handlers.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_registry(cls) -> Mapping[str, List[EventHandler]]:\n    \"\"\"\n    Retrieve the main registry mapping.\n    :return: A mapping of event names to event handlers.\n    \"\"\"\n    with cls.__thread_lock:\n        return {event_name: list(event_handlers) for event_name, event_handlers in cls.__registry.items()}\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_events","title":"get_events classmethod","text":"
get_events() -> List[str]\n

Retrieve a list of all the registered events.

RETURNS DESCRIPTION List[str]

A list of event names.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_events(cls) -> List[str]:\n    \"\"\"\n    Retrieve a list of all the registered events.\n    :return: A list of event names.\n    \"\"\"\n    with cls.__thread_lock:\n        return list(cls.__registry.keys())\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_event_handlers","title":"get_event_handlers classmethod","text":"
get_event_handlers() -> List[EventHandler]\n

Retrieve a list of non-duplicated event handlers that have been registered across all events.

RETURNS DESCRIPTION List[EventHandler]

A list of event handlers.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_event_handlers(cls) -> List[EventHandler]:\n    \"\"\"\n    Retrieve a list of non-duplicated event handlers\n    that have been registered across all events.\n    :return: A list of event handlers.\n    \"\"\"\n    with cls.__thread_lock:\n        return list(\n            {event_handler for event_handlers in cls.__registry.values() for event_handler in event_handlers}\n        )\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_events_by_event_handler","title":"get_events_by_event_handler classmethod","text":"
get_events_by_event_handler(event_handler: EventHandler) -> List[str]\n

Retrieve a list of event names associated with the provided event handler.

PARAMETER DESCRIPTION event_handler

The handler to retrieve the associated events for.

TYPE: EventHandler

RETURNS DESCRIPTION List[str]

A list of event names.

RAISES DESCRIPTION PyventusException

If the event_handler argument is None or invalid.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_events_by_event_handler(cls, event_handler: EventHandler) -> List[str]:\n    \"\"\"\n    Retrieve a list of event names associated with the provided event handler.\n    :param event_handler: The handler to retrieve the associated events for.\n    :return: A list of event names.\n    :raise PyventusException: If the `event_handler` argument is `None` or invalid.\n    \"\"\"\n    # Validate the event_handler argument\n    if event_handler is None:\n        raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n    if not isinstance(event_handler, EventHandler):\n        raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n    with cls.__thread_lock:\n        return [\n            event_name for event_name, event_handlers in cls.__registry.items() if event_handler in event_handlers\n        ]\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_event_handlers_by_events","title":"get_event_handlers_by_events classmethod","text":"
get_event_handlers_by_events(*events: SubscribableEventType) -> List[EventHandler]\n

Retrieve a list of non-duplicated event handlers associated with the provided events.

PARAMETER DESCRIPTION events

Events to retrieve the event handlers for.

TYPE: SubscribableEventType DEFAULT: ()

RETURNS DESCRIPTION List[EventHandler]

A list of event handlers.

RAISES DESCRIPTION PyventusException

If the events argument is None, empty or unsupported.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_event_handlers_by_events(cls, *events: SubscribableEventType) -> List[EventHandler]:\n    \"\"\"\n    Retrieve a list of non-duplicated event handlers associated with the provided events.\n    :param events: Events to retrieve the event handlers for.\n    :return: A list of event handlers.\n    :raise PyventusException: If the `events` argument is `None`, empty or unsupported.\n    \"\"\"\n    # Validate the events argument\n    if events is None or len(events) <= 0:\n        raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n    # Retrieve all unique event names\n    event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n    with cls.__thread_lock:\n        return list(\n            {event_handler for event_name in event_names for event_handler in cls.__registry.get(event_name, [])}\n        )\n
"},{"location":"api/event-linker/#pyventus.EventLinker.once","title":"once classmethod","text":"
once(*events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper\n

Decorator that allows you to conveniently subscribe callbacks to the provided events for a single invocation.

This method can be used as either a decorator or a context manager. When used as a decorator, it automatically subscribes the decorated callback to the provided events. When used as a context manager with the with statement, it allows multiple callbacks to be associated with the provided events within the context block.

PARAMETER DESCRIPTION events

The events to subscribe to.

TYPE: SubscribableEventType DEFAULT: ()

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION EventLinkageWrapper

The decorator that wraps the callback.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef once(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n    \"\"\"\n    Decorator that allows you to conveniently subscribe callbacks to the provided events\n    for a single invocation.\n\n    This method can be used as either a decorator or a context manager. When used as a\n    decorator, it automatically subscribes the decorated callback to the provided events.\n    When used as a context manager with the `with` statement, it allows multiple callbacks\n    to be associated with the provided events within the context block.\n\n    :param events: The events to subscribe to.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :return: The decorator that wraps the callback.\n    \"\"\"\n    return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=True)\n
"},{"location":"api/event-linker/#pyventus.EventLinker.on","title":"on classmethod","text":"
on(*events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper\n

Decorator that allows you to conveniently subscribe callbacks to the provided events.

This method can be used as either a decorator or a context manager. When used as a decorator, it automatically subscribes the decorated callback to the provided events. When used as a context manager with the with statement, it allows multiple callbacks to be associated with the provided events within the context block.

PARAMETER DESCRIPTION events

The events to subscribe to.

TYPE: SubscribableEventType DEFAULT: ()

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION EventLinkageWrapper

The decorator that wraps the callback.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef on(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n    \"\"\"\n    Decorator that allows you to conveniently subscribe callbacks to the provided events.\n\n    This method can be used as either a decorator or a context manager. When used as a\n    decorator, it automatically subscribes the decorated callback to the provided events.\n    When used as a context manager with the `with` statement, it allows multiple callbacks\n    to be associated with the provided events within the context block.\n\n    :param events: The events to subscribe to.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :return: The decorator that wraps the callback.\n    \"\"\"\n    return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=False)\n
"},{"location":"api/event-linker/#pyventus.EventLinker.subscribe","title":"subscribe classmethod","text":"
subscribe(*events: SubscribableEventType, event_callback: EventCallbackType, success_callback: SuccessCallbackType | None = None, failure_callback: FailureCallbackType | None = None, force_async: bool = False, once: bool = False) -> EventHandler\n

Subscribes callbacks to the provided events.

PARAMETER DESCRIPTION events

The events to subscribe to.

TYPE: SubscribableEventType DEFAULT: ()

event_callback

The callback to be executed when the event occurs.

TYPE: EventCallbackType

success_callback

The callback to be executed when the event execution completes successfully.

TYPE: SuccessCallbackType | None DEFAULT: None

failure_callback

The callback to be executed when the event execution fails.

TYPE: FailureCallbackType | None DEFAULT: None

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool DEFAULT: False

once

Specifies if the event handler is a one-time subscription.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION EventHandler

The event handler object associated with the given events.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef subscribe(\n    cls,\n    *events: SubscribableEventType,\n    event_callback: EventCallbackType,  # type: ignore[type-arg]\n    success_callback: SuccessCallbackType | None = None,\n    failure_callback: FailureCallbackType | None = None,\n    force_async: bool = False,\n    once: bool = False,\n) -> EventHandler:\n    \"\"\"\n    Subscribes callbacks to the provided events.\n    :param events: The events to subscribe to.\n    :param event_callback: The callback to be executed when the event occurs.\n    :param success_callback: The callback to be executed when the event execution completes\n        successfully.\n    :param failure_callback: The callback to be executed when the event execution fails.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :param once: Specifies if the event handler is a one-time subscription.\n    :return: The event handler object associated with the given events.\n    \"\"\"\n    # Validate the events argument\n    if events is None or len(events) <= 0:\n        raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n    # Retrieve all unique event names\n    event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # Check if the maximum number of handlers property is set\n        if cls.__max_event_handlers is not None:\n            # For each event name, check if the maximum number of handlers for the event has been exceeded\n            for event_name in event_names:\n                if len(cls.__registry.get(event_name, [])) >= cls.__max_event_handlers:\n                    raise PyventusException(\n                        f\"The event '{event_name}' has exceeded the maximum number of handlers allowed. The \"\n                        f\"'{EventHandler.get_callback_name(callback=event_callback)}'\"\n                        f\" callback cannot be subscribed.\"\n                    )\n\n        # Create a new event handler\n        event_handler: EventHandler = EventHandler(\n            event_callback=event_callback,\n            success_callback=success_callback if success_callback else cls.__default_success_callback,\n            failure_callback=failure_callback if failure_callback else cls.__default_failure_callback,\n            force_async=force_async,\n            once=once,\n        )\n\n        # For each event name, register the event handler\n        for event_name in event_names:\n            # If the event name is not present in the main registry, create a new empty list for it\n            if event_name not in cls.__registry:\n                cls.__registry[event_name] = []\n\n            # Append the event handler to the list of handlers for the event\n            cls.__registry[event_name].append(event_handler)\n\n            # Log the subscription if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(\n                    action=\"Subscribed:\",\n                    msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                )\n\n    # Return the new event handler\n    return event_handler\n
"},{"location":"api/event-linker/#pyventus.EventLinker.unsubscribe","title":"unsubscribe classmethod","text":"
unsubscribe(*events: SubscribableEventType, event_handler: EventHandler) -> bool\n

Unsubscribes an event handler from the provided events. If there are no more handlers for a particular event, that event is also removed from the registry.

PARAMETER DESCRIPTION events

The events to unsubscribe from.

TYPE: SubscribableEventType DEFAULT: ()

event_handler

The event handler to unsubscribe.

TYPE: EventHandler

RETURNS DESCRIPTION bool

True if the event handler associated with the events was found and removed, False otherwise.

RAISES DESCRIPTION PyventusException

If the events argument is None, empty, unsupported, or if the event_handler argument is None, invalid.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef unsubscribe(cls, *events: SubscribableEventType, event_handler: EventHandler) -> bool:\n    \"\"\"\n    Unsubscribes an event handler from the provided events. If there are no more\n    handlers for a particular event, that event is also removed from the registry.\n    :param events: The events to unsubscribe from.\n    :param event_handler: The event handler to unsubscribe.\n    :return: `True` if the event handler associated with the events was found and\n        removed, `False` otherwise.\n    :raises PyventusException: If the `events` argument is `None`, empty, unsupported,\n        or if the `event_handler` argument is `None`, invalid.\n    \"\"\"\n    # Validate the events argument\n    if events is None or len(events) <= 0:\n        raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n    # Validate the event_handler argument\n    if event_handler is None:\n        raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n    if not isinstance(event_handler, EventHandler):\n        raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n    # Retrieve all unique event names\n    event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n    # A flag indicating whether the event handler was successfully removed\n    deleted: bool = False\n\n    # Obtain the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # For each event name, check and remove the event handler if found\n        for event_name in event_names:\n            # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n            event_handlers = cls.__registry.get(event_name, [])\n\n            # Check if the event handler is present in the list of handlers for the event\n            if event_handler in event_handlers:\n                # Remove the event handler from the list of handlers\n                event_handlers.remove(event_handler)\n                deleted = True\n\n                # If there are no more handlers for the event, remove the event name from the registry\n                if not event_handlers:\n                    cls.__registry.pop(event_name)\n\n                # Log the unsubscription if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(\n                        action=\"Unsubscribed:\",\n                        msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                    )\n\n    # Return the flag indicating whether the event handler was deleted\n    return deleted\n
"},{"location":"api/event-linker/#pyventus.EventLinker.remove_event_handler","title":"remove_event_handler classmethod","text":"
remove_event_handler(event_handler: EventHandler) -> bool\n

Removes an event handler from all subscribed events. If there are no more handlers for a particular event, that event is also removed from the registry.

PARAMETER DESCRIPTION event_handler

The event handler to remove.

TYPE: EventHandler

RETURNS DESCRIPTION bool

True if the event handler was found and removed, False otherwise.

RAISES DESCRIPTION PyventusException

If the event_handler argument is None or invalid.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef remove_event_handler(cls, event_handler: EventHandler) -> bool:\n    \"\"\"\n    Removes an event handler from all subscribed events. If there are no more\n    handlers for a particular event, that event is also removed from the registry.\n    :param event_handler: The event handler to remove.\n    :return: `True` if the event handler was found and removed, `False` otherwise.\n    :raises PyventusException: If the `event_handler` argument is `None` or invalid.\n    \"\"\"\n    # Validate the event_handler argument\n    if event_handler is None:\n        raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n    if not isinstance(event_handler, EventHandler):\n        raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n    # A flag indicating if the event handler gets removed\n    deleted: bool = False\n\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # Iterate through each event and its associated handlers in the main registry\n        for event_name in list(cls.__registry.keys()):\n            # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n            event_handlers = cls.__registry.get(event_name, [])\n\n            # Check if the event handler is present in the list of handlers for the event\n            if event_handler in event_handlers:\n                # Remove the event handler from the list of handlers\n                event_handlers.remove(event_handler)\n                deleted = True\n\n                # If there are no more handlers for the event, remove the event from the registry\n                if not event_handlers:\n                    cls.__registry.pop(event_name)\n\n                # Log the removal of the event handler if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(\n                        action=\"Handler Removed:\",\n                        msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                    )\n\n    # Return the flag indicating if the event handler was found and deleted\n    return deleted\n
"},{"location":"api/event-linker/#pyventus.EventLinker.remove_event","title":"remove_event classmethod","text":"
remove_event(event: SubscribableEventType) -> bool\n

Removes an event from the registry, including all its event handler subscriptions.

PARAMETER DESCRIPTION event

The event to remove.

TYPE: SubscribableEventType

RETURNS DESCRIPTION bool

True if the event was found and removed, False otherwise.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef remove_event(cls, event: SubscribableEventType) -> bool:\n    \"\"\"\n    Removes an event from the registry, including all its event handler subscriptions.\n    :param event: The event to remove.\n    :return: `True` if the event was found and removed, `False` otherwise.\n    \"\"\"\n    # Get the event name\n    event_name: str = cls.get_event_name(event=event)\n\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # Check if the event name is present in the main registry\n        if event_name in cls.__registry:\n            # Remove the event from the registry\n            cls.__registry.pop(event_name)\n\n            # Log the removal of the event if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(action=\"Event Removed:\", msg=f\"{event_name}\")\n\n            # Return True to indicate successful removal\n            return True\n\n    return False\n
"},{"location":"api/event-linker/#pyventus.EventLinker.remove_all","title":"remove_all classmethod","text":"
remove_all() -> bool\n

Removes all events and their associated event handlers from the registry.

RETURNS DESCRIPTION bool

True if the events were found and removed, False otherwise.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef remove_all(cls) -> bool:\n    \"\"\"\n    Removes all events and their associated event handlers from the registry.\n    :return: `True` if the events were found and removed, `False` otherwise.\n    \"\"\"\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        if cls.__registry:\n            # Clear the main registry\n            cls.__registry.clear()\n\n            # Log a debug message if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(msg=\"All events and handlers were successfully removed.\")\n\n            return True\n        else:\n            # Log a debug message if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(msg=\"The event registry is already empty.\")\n\n            return False\n
"},{"location":"api/emitters/","title":"EventEmitter class","text":"

Bases: ABC

An abstract base class for event emitters.

Notes:

  • This class defines a common interface for emitting events. It serves as a foundation for implementing custom event emitters with specific dispatch strategies. It is designed to handle string-named events with positional and keyword arguments, as well as instances of dataclass objects and Exceptions objects.

  • The main goal of this class is to decouple the event emission process from the underlying implementation. This loose coupling promotes flexibility, adaptability, and adheres to the Open-Closed principle, allowing custom event emitters to be implemented without affecting existing consumers.

Read more in the Pyventus docs for Event Emitter.

Source code in pyventus/emitters/event_emitter.py
class EventEmitter(ABC):\n    \"\"\"\n    An abstract base class for event emitters.\n\n    **Notes:**\n\n    -   This class defines a common interface for emitting events. It serves as a foundation for\n        implementing custom event emitters with specific dispatch strategies. It is designed to\n        handle `string-named` events with positional and keyword arguments, as well as instances\n        of `dataclass` objects and `Exceptions` objects.\n\n    -   The main goal of this class is to decouple the event emission process from the underlying\n        implementation. This loose coupling promotes flexibility, adaptability, and adheres to the\n        Open-Closed principle, allowing custom event emitters to be implemented without affecting\n        existing consumers.\n\n    ---\n    Read more in the\n    [Pyventus docs for Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/).\n    \"\"\"\n\n    @final\n    class EventEmission:\n        \"\"\"\n        Represents an event emission that has been triggered but whose propagation is not\n        yet complete. It provides a self-contained context for executing the event emission,\n        encapsulating both the event data and the associated event handlers.\n\n        This class acts as an isolated unit of work to asynchronously propagate the emission\n        of an event. When an event occurs, the `EventEmitter` class creates an `EventEmission`\n        instance, which is then processed by the `_process()` method to handle the event\n        propagation.\n        \"\"\"\n\n        # Event emission attributes\n        __slots__ = (\"_id\", \"_timestamp\", \"_debug\", \"_event\", \"_event_handlers\", \"_event_args\", \"_event_kwargs\")\n\n        @property\n        def id(self) -> str:\n            \"\"\"\n            Gets the unique identifier of the event emission.\n            :return: The unique identifier of the event emission.\n            \"\"\"\n            return self._id\n\n        @property\n        def timestamp(self) -> datetime:\n            \"\"\"\n            Gets the timestamp when the event emission was created.\n            :return: The timestamp when the event emission was created.\n            \"\"\"\n            return self._timestamp\n\n        @property\n        def event(self) -> str:\n            \"\"\"\n            Gets the name of the event being emitted.\n            :return: The name of the event.\n            \"\"\"\n            return self._event\n\n        def __init__(\n            self,\n            event: str,\n            event_handlers: List[EventHandler],\n            event_args: Tuple[Any, ...],\n            event_kwargs: Dict[str, Any],\n            debug: bool,\n        ) -> None:\n            \"\"\"\n            Initialize an instance of `EventEmission`.\n            :param event: The name of the event being emitted.\n            :param event_handlers: List of event handlers associated with the event.\n            :param event_args: Positional arguments to be passed to the event handlers.\n            :param event_kwargs: Keyword arguments to be passed to the event handlers.\n            :param debug: Indicates if debug mode is enabled.\n            :raises PyventusException: If `event_handlers` or `event` is empty.\n            \"\"\"\n            if not event_handlers:  # pragma: no cover\n                raise PyventusException(\"The 'event_handlers' argument cannot be empty.\")\n\n            if not event:  # pragma: no cover\n                raise PyventusException(\"The 'event' argument cannot be empty.\")\n\n            # Set the event emission metadata\n            self._id: str = str(uuid4())\n            self._timestamp: datetime = datetime.now()\n            self._debug: bool = debug\n\n            # Set the event emission properties\n            self._event: str = event\n            self._event_handlers: Tuple[EventHandler, ...] = tuple(event_handlers)\n            self._event_args: Tuple[Any, ...] = event_args\n            self._event_kwargs: Dict[str, Any] = event_kwargs\n\n        async def __call__(self) -> None:\n            \"\"\"\n            Execute the event handlers concurrently.\n            :return: None\n            \"\"\"\n            # Log the event execution if debug is enabled\n            if self._debug:  # pragma: no cover\n                StdOutLogger.debug(name=self.__class__.__name__, action=\"Running:\", msg=str(self))\n\n            # Execute the event handlers concurrently\n            await gather(\n                *[event_handler(*self._event_args, **self._event_kwargs) for event_handler in self._event_handlers],\n                return_exceptions=True,\n            )\n\n        def __str__(self) -> str:\n            \"\"\"\n            Returns a formatted string representation of the event emission.\n            :return: The formatted string representation of the event emission.\n            \"\"\"\n            return (\n                f\"ID: {self.id} | Timestamp: {self.timestamp.strftime('%Y-%m-%d %I:%M:%S %p')} | \"\n                f\"Event: {self.event} | Handlers: {len(self._event_handlers)}\"\n            )\n\n    def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n        \"\"\"\n        Initialize an instance of `EventEmitter`.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the subclass logger. If `None`,\n            it is determined based on the execution environment.\n        :raises PyventusException: If the `event_linker` argument is None.\n        \"\"\"\n        # Validate the event linker argument\n        if event_linker is None:\n            raise PyventusException(\"The 'event_linker' argument cannot be None.\")\n        if not issubclass(event_linker, EventLinker):\n            raise PyventusException(\"The 'event_linker' argument must be a subtype of the EventLinker class.\")\n\n        # Set the event_linker value\n        self._event_linker: Type[EventLinker] = event_linker\n        \"\"\"\n        Specifies the type of event linker to use for associating events with their \n        respective event handlers.\n        \"\"\"\n\n        self._logger: Logger = Logger(\n            name=self.__class__.__name__,\n            debug=debug if debug is not None else bool(gettrace() is not None),\n        )\n        \"\"\"\n        An instance of the logger used for logging events and debugging information.\n        \"\"\"\n\n    def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n        \"\"\"\n        Emits an event and triggers its associated event handlers.\n\n        **Notes:**\n\n        -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n            to the event handler as the first positional argument, even if you pass additional `*args`\n            or `**kwargs`.\n        -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n            they will also be triggered each time an event or exception is emitted.\n\n        :param event: The event to be emitted. It can be `str`, a `dataclass`\n            object, or an `Exception` object.\n        :param args: Positional arguments to be passed to the event handlers.\n        :param kwargs: Keyword arguments to be passed to the event handlers.\n        :return: None\n        \"\"\"\n        # Raise an exception if the event is None\n        if event is None:\n            raise PyventusException(\"The 'event' argument cannot be None.\")\n\n        # Raise an exception if the event is a type\n        if isinstance(event, type):\n            raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n        # Determine the event name\n        event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n        # Retrieve the event handlers associated with the event\n        event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n        # Sort the event handlers by timestamp\n        event_handlers.sort(key=lambda handler: handler.timestamp)\n\n        # Initialize the list of event handlers to be executed\n        pending_event_handlers: List[EventHandler] = []\n\n        # Iterate through each event handler\n        for event_handler in event_handlers:\n            # Check if the event handler is a one-time subscription\n            if event_handler.once:\n                # If the event handler is a one-time subscription, we attempt to remove it.\n                if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                    # If the removal is successful, it indicates that the handler has not\n                    # been processed before, so we add it to the pending list.\n                    pending_event_handlers.append(event_handler)\n            else:\n                pending_event_handlers.append(event_handler)\n\n        # Check if the pending list of event handlers is not empty\n        if len(pending_event_handlers) > 0:\n            # Create a new EventEmission instance\n            event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n                event=event_name,\n                event_handlers=pending_event_handlers,\n                event_args=args if isinstance(event, str) else (event, *args),\n                event_kwargs=kwargs,\n                debug=self._logger.debug_enabled,\n            )\n\n            # Log the event emission when debug is enabled\n            if self._logger.debug_enabled:  # pragma: no cover\n                self._logger.debug(\n                    action=\"Emitting Event:\",\n                    msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n                )\n\n            # Delegate the event emission processing to subclasses\n            self._process(event_emission)\n\n        # Log a debug message if there are no event handlers subscribed to the event\n        elif self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n            )\n\n    @abstractmethod\n    def _process(self, event_emission: EventEmission) -> None:\n        \"\"\"\n        Process the event emission execution. Subclasses must implement\n        this method to define the specific processing logic.\n\n        :param event_emission: The event emission to be processed.\n        :return: None\n        \"\"\"\n        pass\n

"},{"location":"api/emitters/#pyventus.EventEmitter-classes","title":"Classes","text":""},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission","title":"EventEmission","text":"

Represents an event emission that has been triggered but whose propagation is not yet complete. It provides a self-contained context for executing the event emission, encapsulating both the event data and the associated event handlers.

This class acts as an isolated unit of work to asynchronously propagate the emission of an event. When an event occurs, the EventEmitter class creates an EventEmission instance, which is then processed by the _process() method to handle the event propagation.

Source code in pyventus/emitters/event_emitter.py
@final\nclass EventEmission:\n    \"\"\"\n    Represents an event emission that has been triggered but whose propagation is not\n    yet complete. It provides a self-contained context for executing the event emission,\n    encapsulating both the event data and the associated event handlers.\n\n    This class acts as an isolated unit of work to asynchronously propagate the emission\n    of an event. When an event occurs, the `EventEmitter` class creates an `EventEmission`\n    instance, which is then processed by the `_process()` method to handle the event\n    propagation.\n    \"\"\"\n\n    # Event emission attributes\n    __slots__ = (\"_id\", \"_timestamp\", \"_debug\", \"_event\", \"_event_handlers\", \"_event_args\", \"_event_kwargs\")\n\n    @property\n    def id(self) -> str:\n        \"\"\"\n        Gets the unique identifier of the event emission.\n        :return: The unique identifier of the event emission.\n        \"\"\"\n        return self._id\n\n    @property\n    def timestamp(self) -> datetime:\n        \"\"\"\n        Gets the timestamp when the event emission was created.\n        :return: The timestamp when the event emission was created.\n        \"\"\"\n        return self._timestamp\n\n    @property\n    def event(self) -> str:\n        \"\"\"\n        Gets the name of the event being emitted.\n        :return: The name of the event.\n        \"\"\"\n        return self._event\n\n    def __init__(\n        self,\n        event: str,\n        event_handlers: List[EventHandler],\n        event_args: Tuple[Any, ...],\n        event_kwargs: Dict[str, Any],\n        debug: bool,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `EventEmission`.\n        :param event: The name of the event being emitted.\n        :param event_handlers: List of event handlers associated with the event.\n        :param event_args: Positional arguments to be passed to the event handlers.\n        :param event_kwargs: Keyword arguments to be passed to the event handlers.\n        :param debug: Indicates if debug mode is enabled.\n        :raises PyventusException: If `event_handlers` or `event` is empty.\n        \"\"\"\n        if not event_handlers:  # pragma: no cover\n            raise PyventusException(\"The 'event_handlers' argument cannot be empty.\")\n\n        if not event:  # pragma: no cover\n            raise PyventusException(\"The 'event' argument cannot be empty.\")\n\n        # Set the event emission metadata\n        self._id: str = str(uuid4())\n        self._timestamp: datetime = datetime.now()\n        self._debug: bool = debug\n\n        # Set the event emission properties\n        self._event: str = event\n        self._event_handlers: Tuple[EventHandler, ...] = tuple(event_handlers)\n        self._event_args: Tuple[Any, ...] = event_args\n        self._event_kwargs: Dict[str, Any] = event_kwargs\n\n    async def __call__(self) -> None:\n        \"\"\"\n        Execute the event handlers concurrently.\n        :return: None\n        \"\"\"\n        # Log the event execution if debug is enabled\n        if self._debug:  # pragma: no cover\n            StdOutLogger.debug(name=self.__class__.__name__, action=\"Running:\", msg=str(self))\n\n        # Execute the event handlers concurrently\n        await gather(\n            *[event_handler(*self._event_args, **self._event_kwargs) for event_handler in self._event_handlers],\n            return_exceptions=True,\n        )\n\n    def __str__(self) -> str:\n        \"\"\"\n        Returns a formatted string representation of the event emission.\n        :return: The formatted string representation of the event emission.\n        \"\"\"\n        return (\n            f\"ID: {self.id} | Timestamp: {self.timestamp.strftime('%Y-%m-%d %I:%M:%S %p')} | \"\n            f\"Event: {self.event} | Handlers: {len(self._event_handlers)}\"\n        )\n
"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission-attributes","title":"Attributes","text":""},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.id","title":"id property","text":"
id: str\n

Gets the unique identifier of the event emission.

RETURNS DESCRIPTION str

The unique identifier of the event emission.

"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.timestamp","title":"timestamp property","text":"
timestamp: datetime\n

Gets the timestamp when the event emission was created.

RETURNS DESCRIPTION datetime

The timestamp when the event emission was created.

"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.event","title":"event property","text":"
event: str\n

Gets the name of the event being emitted.

RETURNS DESCRIPTION str

The name of the event.

"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission-functions","title":"Functions","text":""},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.__init__","title":"__init__","text":"
__init__(event: str, event_handlers: List[EventHandler], event_args: Tuple[Any, ...], event_kwargs: Dict[str, Any], debug: bool) -> None\n

Initialize an instance of EventEmission.

PARAMETER DESCRIPTION event

The name of the event being emitted.

TYPE: str

event_handlers

List of event handlers associated with the event.

TYPE: List[EventHandler]

event_args

Positional arguments to be passed to the event handlers.

TYPE: Tuple[Any, ...]

event_kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Dict[str, Any]

debug

Indicates if debug mode is enabled.

TYPE: bool

RAISES DESCRIPTION PyventusException

If event_handlers or event is empty.

Source code in pyventus/emitters/event_emitter.py
def __init__(\n    self,\n    event: str,\n    event_handlers: List[EventHandler],\n    event_args: Tuple[Any, ...],\n    event_kwargs: Dict[str, Any],\n    debug: bool,\n) -> None:\n    \"\"\"\n    Initialize an instance of `EventEmission`.\n    :param event: The name of the event being emitted.\n    :param event_handlers: List of event handlers associated with the event.\n    :param event_args: Positional arguments to be passed to the event handlers.\n    :param event_kwargs: Keyword arguments to be passed to the event handlers.\n    :param debug: Indicates if debug mode is enabled.\n    :raises PyventusException: If `event_handlers` or `event` is empty.\n    \"\"\"\n    if not event_handlers:  # pragma: no cover\n        raise PyventusException(\"The 'event_handlers' argument cannot be empty.\")\n\n    if not event:  # pragma: no cover\n        raise PyventusException(\"The 'event' argument cannot be empty.\")\n\n    # Set the event emission metadata\n    self._id: str = str(uuid4())\n    self._timestamp: datetime = datetime.now()\n    self._debug: bool = debug\n\n    # Set the event emission properties\n    self._event: str = event\n    self._event_handlers: Tuple[EventHandler, ...] = tuple(event_handlers)\n    self._event_args: Tuple[Any, ...] = event_args\n    self._event_kwargs: Dict[str, Any] = event_kwargs\n
"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.__call__","title":"__call__ async","text":"
__call__() -> None\n

Execute the event handlers concurrently.

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
async def __call__(self) -> None:\n    \"\"\"\n    Execute the event handlers concurrently.\n    :return: None\n    \"\"\"\n    # Log the event execution if debug is enabled\n    if self._debug:  # pragma: no cover\n        StdOutLogger.debug(name=self.__class__.__name__, action=\"Running:\", msg=str(self))\n\n    # Execute the event handlers concurrently\n    await gather(\n        *[event_handler(*self._event_args, **self._event_kwargs) for event_handler in self._event_handlers],\n        return_exceptions=True,\n    )\n
"},{"location":"api/emitters/#pyventus.EventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/#pyventus.EventEmitter.__init__","title":"__init__","text":"
__init__(event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of EventEmitter.

PARAMETER DESCRIPTION event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the subclass logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

RAISES DESCRIPTION PyventusException

If the event_linker argument is None.

Source code in pyventus/emitters/event_emitter.py
def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n    \"\"\"\n    Initialize an instance of `EventEmitter`.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the subclass logger. If `None`,\n        it is determined based on the execution environment.\n    :raises PyventusException: If the `event_linker` argument is None.\n    \"\"\"\n    # Validate the event linker argument\n    if event_linker is None:\n        raise PyventusException(\"The 'event_linker' argument cannot be None.\")\n    if not issubclass(event_linker, EventLinker):\n        raise PyventusException(\"The 'event_linker' argument must be a subtype of the EventLinker class.\")\n\n    # Set the event_linker value\n    self._event_linker: Type[EventLinker] = event_linker\n    \"\"\"\n    Specifies the type of event linker to use for associating events with their \n    respective event handlers.\n    \"\"\"\n\n    self._logger: Logger = Logger(\n        name=self.__class__.__name__,\n        debug=debug if debug is not None else bool(gettrace() is not None),\n    )\n    \"\"\"\n    An instance of the logger used for logging events and debugging information.\n    \"\"\"\n
"},{"location":"api/emitters/#pyventus.EventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/#pyventus.EventEmitter._process","title":"_process abstractmethod","text":"
_process(event_emission: EventEmission) -> None\n

Process the event emission execution. Subclasses must implement this method to define the specific processing logic.

PARAMETER DESCRIPTION event_emission

The event emission to be processed.

TYPE: EventEmission

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
@abstractmethod\ndef _process(self, event_emission: EventEmission) -> None:\n    \"\"\"\n    Process the event emission execution. Subclasses must implement\n    this method to define the specific processing logic.\n\n    :param event_emission: The event emission to be processed.\n    :return: None\n    \"\"\"\n    pass\n
"},{"location":"api/emitters/asyncio/","title":"AsyncIOEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the AsyncIO framework to handle the execution of event emissions.

Notes:

  • When used in an asynchronous context where an event loop is already running, the event emission is scheduled and processed on that existing loop. If the event loop is closed before all callbacks complete, any remaining scheduled callbacks will be canceled.

  • When used in a synchronous context where no event loop is active, a new event loop is started and subsequently closed by the asyncio.run() method. Within this loop, the event emission is executed. The loop then waits for all scheduled tasks to finish before closing.

Read more in the Pyventus docs for AsyncIO Event Emitter.

Source code in pyventus/emitters/asyncio/asyncio_event_emitter.py
class AsyncIOEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the AsyncIO framework to handle\n    the execution of event emissions.\n\n    **Notes:**\n\n    -   When used in an asynchronous context where an event loop is already running,\n        the event emission is scheduled and processed on that existing loop. If the\n        event loop is closed before all callbacks complete, any remaining scheduled\n        callbacks will be canceled.\n\n    -   When used in a synchronous context where no event loop is active, a new event\n        loop is started and subsequently closed by the `asyncio.run()` method. Within\n        this loop, the event emission is executed. The loop then waits for all\n        scheduled tasks to finish before closing.\n\n    ---\n    Read more in the\n    [Pyventus docs for AsyncIO Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/asyncio/).\n    \"\"\"\n\n    @property\n    def __is_loop_running(self) -> bool:\n        \"\"\"\n        Check if there is currently an active AsyncIO event loop.\n        :return: `True` if an event loop is running, `False` otherwise.\n        \"\"\"\n        try:\n            asyncio.get_running_loop()\n            return True\n        except RuntimeError:\n            return False\n\n    def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n        \"\"\"\n        Initialize an instance of `AsyncIOEventEmitter`.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Initialize the set of background futures\n        self._background_futures: Set[Future] = set()  # type: ignore[type-arg]\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Check if there is an active event loop\n        is_loop_running: bool = self.__is_loop_running\n\n        # Log the execution context, if debug mode is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(action=f\"Context:\", msg=f\"{'Async' if is_loop_running else 'Sync'}\")\n\n        if is_loop_running:\n            # Schedule the event emission in the running loop as a future\n            future = asyncio.ensure_future(event_emission())\n\n            # Remove the Future from the set of background futures after completion\n            future.add_done_callback(self._background_futures.remove)\n\n            # Add the Future to the set of background futures\n            self._background_futures.add(future)\n        else:\n            # Run the event emission in a blocking manner\n            asyncio.run(event_emission())\n

"},{"location":"api/emitters/asyncio/#pyventus.AsyncIOEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/asyncio/#pyventus.AsyncIOEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/asyncio/#pyventus.AsyncIOEventEmitter.__init__","title":"__init__","text":"
__init__(event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of AsyncIOEventEmitter.

PARAMETER DESCRIPTION event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/asyncio/asyncio_event_emitter.py
def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n    \"\"\"\n    Initialize an instance of `AsyncIOEventEmitter`.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Initialize the set of background futures\n    self._background_futures: Set[Future] = set()  # type: ignore[type-arg]\n
"},{"location":"api/emitters/celery/","title":"CeleryEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the Celery distributed system to handle the execution of event emissions.

Notes:

  • This class uses a Celery Queue instance to enqueue event emissions, which are subsequently executed by Celery workers. This approach provides a scalable and distributed method for handling the execution of event emissions.

Read more in the Pyventus docs for Celery Event Emitter.

Source code in pyventus/emitters/celery/celery_event_emitter.py
class CeleryEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the Celery distributed system\n    to handle the execution of event emissions.\n\n    **Notes:**\n\n    -   This class uses a Celery Queue instance to enqueue event emissions, which are\n        subsequently executed by Celery workers. This approach provides a scalable\n        and distributed method for handling the execution of event emissions.\n\n    ---\n    Read more in the\n    [Pyventus docs for Celery Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/celery/).\n    \"\"\"\n\n    class Queue:\n        \"\"\"A Celery event emitter queue used for enqueuing event emissions.\"\"\"\n\n        class Serializer:\n            \"\"\"An event emitter object serializer for Celery queues.\"\"\"\n\n            @staticmethod\n            def dumps(obj: EventEmitter.EventEmission) -> Any:\n                \"\"\"\n                Serializes the event emission object.\n                :param obj: The event emission object to be serialized.\n                :return: The serialized representation of the event emission object.\n                \"\"\"\n                return dumps(obj)  # pragma: no cover\n\n            @staticmethod\n            def loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n                \"\"\"\n                Deserializes the task payload back to the event emission object.\n                :param serialized_obj: The serialized object to be loaded.\n                :return: The deserialized event emission object.\n                \"\"\"\n                return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n\n        class _Payload(NamedTuple):\n            \"\"\"The Celery event emitter queue payload.\"\"\"\n\n            serialized_obj: bytes\n            \"\"\"Serialized event emission object.\"\"\"\n\n            obj_hash: bytes | None\n            \"\"\"Hash of the serialized event emission object.\"\"\"\n\n            @classmethod\n            def from_json(cls, **kwargs: Any) -> \"CeleryEventEmitter.Queue._Payload\":\n                \"\"\"\n                Creates a Payload instance from a JSON-compatible dictionary.\n                :param kwargs: JSON-compatible dictionary representing the payload.\n                :return: Payload instance.\n                :raises ValueError: If the JSON data is missing fields or contains extra keys.\n                \"\"\"\n                # Get the field names of the named tuple\n                tuple_fields: tuple[str, ...] = CeleryEventEmitter.Queue._Payload._fields\n\n                # Check if all expected fields are present\n                if not set(tuple_fields).issubset(kwargs.keys()):\n                    raise PyventusException(\"Missing fields in JSON data.\")\n\n                # Check for extra keys in the JSON data\n                extra_keys = set(kwargs.keys()) - set(tuple_fields)\n                if extra_keys:\n                    raise PyventusException(\"Extra keys in JSON data: {}\".format(extra_keys))\n\n                # Create the named tuple using the JSON data\n                return cls(**kwargs)\n\n            def to_json(self) -> Dict[str, Any]:\n                \"\"\"\n                Converts the payload to a JSON-compatible dictionary.\n                :return: JSON-compatible dictionary representing the payload.\n                \"\"\"\n                return self._asdict()\n\n        def __init__(\n            self,\n            celery: Celery,\n            name: str | None = None,\n            secret: str | None = None,\n            serializer: Type[Serializer] = Serializer,\n        ) -> None:\n            \"\"\"\n            Initialize an instance of `CeleryEventEmitter.Queue`.\n            :param celery: The Celery object used to enqueue and process event emissions.\n            :param name: The name of the queue where the event emission will be enqueued.\n                Default is None (task_default_queue).\n            :param secret: The secret key used for message authentication and integrity validation.\n                This key is used for hashing the event emission object and verifying its integrity.\n            :param serializer: The serializer class used for serializing and deserializing event\n                emission objects.\n            :raises PyventusException: If the Celery object is None, or the secret key is not None\n                and empty, or if the content type 'application/x-python-serialize' is not accepted.\n            \"\"\"\n            if celery is None:\n                raise PyventusException(\"The 'celery' argument cannot be None.\")\n            if not isinstance(celery, Celery):\n                raise PyventusException(\"The 'celery' argument must be an instance of the Celery class.\")\n\n            if secret is not None and len(secret) == 0:\n                raise PyventusException(\"The 'secret' argument cannot be empty.\")\n\n            if \"application/x-python-serialize\" not in celery.conf.accept_content:\n                raise PyventusException(\n                    \"Unsupported content type. \"\n                    \"'application/x-python-serialize' is not in the list of accepted content types.\"\n                )\n\n            # Set the Celery queue properties\n            self._celery: Celery = celery\n            self._name: str = self._celery.conf.task_default_queue if name is None else name\n            self._secret: bytes | None = secret.encode(\"utf-8\") if secret else None\n            self._digestmod: str | Callable[[], Any] | ModuleType = sha256  # The digest algorithm used for hashing\n            self._serializer: Type[CeleryEventEmitter.Queue.Serializer] = serializer\n\n            # Register the event processor method as a Celery task\n            self._celery.task(self._executor, name=f\"pyventus{self._executor.__name__}\", queue=self._name)\n\n        def enqueue(self, event_emission: EventEmitter.EventEmission) -> None:\n            \"\"\"\n            Enqueues an event emission object for asynchronous processing in Celery.\n\n            This method takes an `EventEmission` object and enqueues it for asynchronous\n            execution by Celery workers. If a secret key is provided during initialization,\n            the event emission object is first serialized, and its hash is calculated using\n            the secret key. This hash is used to verify the integrity of the event emission\n            object during execution.\n\n            :param event_emission: The event emission object to be enqueued for asynchronous execution.\n            :return: None\n            \"\"\"\n            # Serialize the event emission object\n            serialized_obj: Any = self._serializer.dumps(event_emission)\n\n            # Calculate the hash of the serialized object if a secret key was provided\n            obj_hash: Any | None = None\n            if self._secret:  # pragma: no cover\n                obj_hash = hmac.new(key=self._secret, msg=serialized_obj, digestmod=self._digestmod).digest()\n\n            # Create a payload with the serialized event emission instance and its hash\n            payload = CeleryEventEmitter.Queue._Payload(\n                serialized_obj=serialized_obj,\n                obj_hash=obj_hash,\n            )\n\n            # Send the event emission object to Celery for asynchronous execution\n            self._celery.send_task(\n                f\"pyventus{self._executor.__name__}\",\n                kwargs=payload.to_json(),\n                queue=self._name,\n            )\n\n        def _executor(self, **kwargs: Any) -> None:\n            \"\"\"\n            Process the enqueued event emission object.\n\n            This method serves as the Celery task responsible\n            for processing the enqueued event emission.\n\n            :param kwargs: The JSON-compatible dictionary representing the payload.\n            :return: None\n            \"\"\"\n            # Create a Payload instance from the JSON data\n            payload = CeleryEventEmitter.Queue._Payload.from_json(**kwargs)\n\n            # Check payload\n            if self._secret:  # pragma: no cover\n                if not payload.obj_hash:\n                    raise PyventusException(\"Invalid payload structure.\")\n\n                # Verify the integrity of the payload\n                obj_hash: bytes = hmac.new(\n                    key=self._secret, msg=payload.serialized_obj, digestmod=self._digestmod\n                ).digest()\n\n                # Compare the calculated hash with the provided payload hash\n                if not hmac.compare_digest(payload.obj_hash, obj_hash):\n                    raise PyventusException(\"Payload integrity verification failed.\")\n            elif payload.obj_hash:  # pragma: no cover\n                raise PyventusException(\"Unexpected payload structure.\")\n\n            # Deserialize the event emission object\n            event_emission = self._serializer.loads(payload.serialized_obj)\n\n            # Check if the deserialized event emission object is valid\n            if event_emission is None:  # pragma: no cover\n                raise PyventusException(\"Failed to deserialize the event emission object.\")\n\n            # Run the event emission\n            asyncio.run(event_emission())\n\n    def __init__(self, queue: Queue, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n        \"\"\"\n        Initialize an instance of `CeleryEventEmitter`.\n        :param queue: The queue used for enqueuing event emissions in the Celery event emitter.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Validate the queue argument\n        if queue is None:\n            raise PyventusException(\"The 'queue' argument cannot be None\")\n        if not isinstance(queue, CeleryEventEmitter.Queue):\n            raise PyventusException(\"The 'queue' argument must be an instance of the CeleryEventEmitter.Queue class.\")\n\n        # Store the queue object\n        self._queue: CeleryEventEmitter.Queue = queue\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Add the event emission object to the Celery queue for asynchronous execution\n        self._queue.enqueue(event_emission=event_emission)\n

"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter-classes","title":"Classes","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue","title":"Queue","text":"

A Celery event emitter queue used for enqueuing event emissions.

Source code in pyventus/emitters/celery/celery_event_emitter.py
class Queue:\n    \"\"\"A Celery event emitter queue used for enqueuing event emissions.\"\"\"\n\n    class Serializer:\n        \"\"\"An event emitter object serializer for Celery queues.\"\"\"\n\n        @staticmethod\n        def dumps(obj: EventEmitter.EventEmission) -> Any:\n            \"\"\"\n            Serializes the event emission object.\n            :param obj: The event emission object to be serialized.\n            :return: The serialized representation of the event emission object.\n            \"\"\"\n            return dumps(obj)  # pragma: no cover\n\n        @staticmethod\n        def loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n            \"\"\"\n            Deserializes the task payload back to the event emission object.\n            :param serialized_obj: The serialized object to be loaded.\n            :return: The deserialized event emission object.\n            \"\"\"\n            return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n\n    class _Payload(NamedTuple):\n        \"\"\"The Celery event emitter queue payload.\"\"\"\n\n        serialized_obj: bytes\n        \"\"\"Serialized event emission object.\"\"\"\n\n        obj_hash: bytes | None\n        \"\"\"Hash of the serialized event emission object.\"\"\"\n\n        @classmethod\n        def from_json(cls, **kwargs: Any) -> \"CeleryEventEmitter.Queue._Payload\":\n            \"\"\"\n            Creates a Payload instance from a JSON-compatible dictionary.\n            :param kwargs: JSON-compatible dictionary representing the payload.\n            :return: Payload instance.\n            :raises ValueError: If the JSON data is missing fields or contains extra keys.\n            \"\"\"\n            # Get the field names of the named tuple\n            tuple_fields: tuple[str, ...] = CeleryEventEmitter.Queue._Payload._fields\n\n            # Check if all expected fields are present\n            if not set(tuple_fields).issubset(kwargs.keys()):\n                raise PyventusException(\"Missing fields in JSON data.\")\n\n            # Check for extra keys in the JSON data\n            extra_keys = set(kwargs.keys()) - set(tuple_fields)\n            if extra_keys:\n                raise PyventusException(\"Extra keys in JSON data: {}\".format(extra_keys))\n\n            # Create the named tuple using the JSON data\n            return cls(**kwargs)\n\n        def to_json(self) -> Dict[str, Any]:\n            \"\"\"\n            Converts the payload to a JSON-compatible dictionary.\n            :return: JSON-compatible dictionary representing the payload.\n            \"\"\"\n            return self._asdict()\n\n    def __init__(\n        self,\n        celery: Celery,\n        name: str | None = None,\n        secret: str | None = None,\n        serializer: Type[Serializer] = Serializer,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `CeleryEventEmitter.Queue`.\n        :param celery: The Celery object used to enqueue and process event emissions.\n        :param name: The name of the queue where the event emission will be enqueued.\n            Default is None (task_default_queue).\n        :param secret: The secret key used for message authentication and integrity validation.\n            This key is used for hashing the event emission object and verifying its integrity.\n        :param serializer: The serializer class used for serializing and deserializing event\n            emission objects.\n        :raises PyventusException: If the Celery object is None, or the secret key is not None\n            and empty, or if the content type 'application/x-python-serialize' is not accepted.\n        \"\"\"\n        if celery is None:\n            raise PyventusException(\"The 'celery' argument cannot be None.\")\n        if not isinstance(celery, Celery):\n            raise PyventusException(\"The 'celery' argument must be an instance of the Celery class.\")\n\n        if secret is not None and len(secret) == 0:\n            raise PyventusException(\"The 'secret' argument cannot be empty.\")\n\n        if \"application/x-python-serialize\" not in celery.conf.accept_content:\n            raise PyventusException(\n                \"Unsupported content type. \"\n                \"'application/x-python-serialize' is not in the list of accepted content types.\"\n            )\n\n        # Set the Celery queue properties\n        self._celery: Celery = celery\n        self._name: str = self._celery.conf.task_default_queue if name is None else name\n        self._secret: bytes | None = secret.encode(\"utf-8\") if secret else None\n        self._digestmod: str | Callable[[], Any] | ModuleType = sha256  # The digest algorithm used for hashing\n        self._serializer: Type[CeleryEventEmitter.Queue.Serializer] = serializer\n\n        # Register the event processor method as a Celery task\n        self._celery.task(self._executor, name=f\"pyventus{self._executor.__name__}\", queue=self._name)\n\n    def enqueue(self, event_emission: EventEmitter.EventEmission) -> None:\n        \"\"\"\n        Enqueues an event emission object for asynchronous processing in Celery.\n\n        This method takes an `EventEmission` object and enqueues it for asynchronous\n        execution by Celery workers. If a secret key is provided during initialization,\n        the event emission object is first serialized, and its hash is calculated using\n        the secret key. This hash is used to verify the integrity of the event emission\n        object during execution.\n\n        :param event_emission: The event emission object to be enqueued for asynchronous execution.\n        :return: None\n        \"\"\"\n        # Serialize the event emission object\n        serialized_obj: Any = self._serializer.dumps(event_emission)\n\n        # Calculate the hash of the serialized object if a secret key was provided\n        obj_hash: Any | None = None\n        if self._secret:  # pragma: no cover\n            obj_hash = hmac.new(key=self._secret, msg=serialized_obj, digestmod=self._digestmod).digest()\n\n        # Create a payload with the serialized event emission instance and its hash\n        payload = CeleryEventEmitter.Queue._Payload(\n            serialized_obj=serialized_obj,\n            obj_hash=obj_hash,\n        )\n\n        # Send the event emission object to Celery for asynchronous execution\n        self._celery.send_task(\n            f\"pyventus{self._executor.__name__}\",\n            kwargs=payload.to_json(),\n            queue=self._name,\n        )\n\n    def _executor(self, **kwargs: Any) -> None:\n        \"\"\"\n        Process the enqueued event emission object.\n\n        This method serves as the Celery task responsible\n        for processing the enqueued event emission.\n\n        :param kwargs: The JSON-compatible dictionary representing the payload.\n        :return: None\n        \"\"\"\n        # Create a Payload instance from the JSON data\n        payload = CeleryEventEmitter.Queue._Payload.from_json(**kwargs)\n\n        # Check payload\n        if self._secret:  # pragma: no cover\n            if not payload.obj_hash:\n                raise PyventusException(\"Invalid payload structure.\")\n\n            # Verify the integrity of the payload\n            obj_hash: bytes = hmac.new(\n                key=self._secret, msg=payload.serialized_obj, digestmod=self._digestmod\n            ).digest()\n\n            # Compare the calculated hash with the provided payload hash\n            if not hmac.compare_digest(payload.obj_hash, obj_hash):\n                raise PyventusException(\"Payload integrity verification failed.\")\n        elif payload.obj_hash:  # pragma: no cover\n            raise PyventusException(\"Unexpected payload structure.\")\n\n        # Deserialize the event emission object\n        event_emission = self._serializer.loads(payload.serialized_obj)\n\n        # Check if the deserialized event emission object is valid\n        if event_emission is None:  # pragma: no cover\n            raise PyventusException(\"Failed to deserialize the event emission object.\")\n\n        # Run the event emission\n        asyncio.run(event_emission())\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue-classes","title":"Classes","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.Serializer","title":"Serializer","text":"

An event emitter object serializer for Celery queues.

Source code in pyventus/emitters/celery/celery_event_emitter.py
class Serializer:\n    \"\"\"An event emitter object serializer for Celery queues.\"\"\"\n\n    @staticmethod\n    def dumps(obj: EventEmitter.EventEmission) -> Any:\n        \"\"\"\n        Serializes the event emission object.\n        :param obj: The event emission object to be serialized.\n        :return: The serialized representation of the event emission object.\n        \"\"\"\n        return dumps(obj)  # pragma: no cover\n\n    @staticmethod\n    def loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n        \"\"\"\n        Deserializes the task payload back to the event emission object.\n        :param serialized_obj: The serialized object to be loaded.\n        :return: The deserialized event emission object.\n        \"\"\"\n        return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.Serializer-functions","title":"Functions","text":"dumps staticmethod \u00b6
dumps(obj: EventEmission) -> Any\n

Serializes the event emission object.

PARAMETER DESCRIPTION obj

The event emission object to be serialized.

TYPE: EventEmission

RETURNS DESCRIPTION Any

The serialized representation of the event emission object.

Source code in pyventus/emitters/celery/celery_event_emitter.py
@staticmethod\ndef dumps(obj: EventEmitter.EventEmission) -> Any:\n    \"\"\"\n    Serializes the event emission object.\n    :param obj: The event emission object to be serialized.\n    :return: The serialized representation of the event emission object.\n    \"\"\"\n    return dumps(obj)  # pragma: no cover\n
loads staticmethod \u00b6
loads(serialized_obj: Any) -> EventEmission\n

Deserializes the task payload back to the event emission object.

PARAMETER DESCRIPTION serialized_obj

The serialized object to be loaded.

TYPE: Any

RETURNS DESCRIPTION EventEmission

The deserialized event emission object.

Source code in pyventus/emitters/celery/celery_event_emitter.py
@staticmethod\ndef loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n    \"\"\"\n    Deserializes the task payload back to the event emission object.\n    :param serialized_obj: The serialized object to be loaded.\n    :return: The deserialized event emission object.\n    \"\"\"\n    return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue-functions","title":"Functions","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.__init__","title":"__init__","text":"
__init__(celery: Celery, name: str | None = None, secret: str | None = None, serializer: Type[Serializer] = Serializer) -> None\n

Initialize an instance of CeleryEventEmitter.Queue.

PARAMETER DESCRIPTION celery

The Celery object used to enqueue and process event emissions.

TYPE: Celery

name

The name of the queue where the event emission will be enqueued. Default is None (task_default_queue).

TYPE: str | None DEFAULT: None

secret

The secret key used for message authentication and integrity validation. This key is used for hashing the event emission object and verifying its integrity.

TYPE: str | None DEFAULT: None

serializer

The serializer class used for serializing and deserializing event emission objects.

TYPE: Type[Serializer] DEFAULT: Serializer

RAISES DESCRIPTION PyventusException

If the Celery object is None, or the secret key is not None and empty, or if the content type 'application/x-python-serialize' is not accepted.

Source code in pyventus/emitters/celery/celery_event_emitter.py
def __init__(\n    self,\n    celery: Celery,\n    name: str | None = None,\n    secret: str | None = None,\n    serializer: Type[Serializer] = Serializer,\n) -> None:\n    \"\"\"\n    Initialize an instance of `CeleryEventEmitter.Queue`.\n    :param celery: The Celery object used to enqueue and process event emissions.\n    :param name: The name of the queue where the event emission will be enqueued.\n        Default is None (task_default_queue).\n    :param secret: The secret key used for message authentication and integrity validation.\n        This key is used for hashing the event emission object and verifying its integrity.\n    :param serializer: The serializer class used for serializing and deserializing event\n        emission objects.\n    :raises PyventusException: If the Celery object is None, or the secret key is not None\n        and empty, or if the content type 'application/x-python-serialize' is not accepted.\n    \"\"\"\n    if celery is None:\n        raise PyventusException(\"The 'celery' argument cannot be None.\")\n    if not isinstance(celery, Celery):\n        raise PyventusException(\"The 'celery' argument must be an instance of the Celery class.\")\n\n    if secret is not None and len(secret) == 0:\n        raise PyventusException(\"The 'secret' argument cannot be empty.\")\n\n    if \"application/x-python-serialize\" not in celery.conf.accept_content:\n        raise PyventusException(\n            \"Unsupported content type. \"\n            \"'application/x-python-serialize' is not in the list of accepted content types.\"\n        )\n\n    # Set the Celery queue properties\n    self._celery: Celery = celery\n    self._name: str = self._celery.conf.task_default_queue if name is None else name\n    self._secret: bytes | None = secret.encode(\"utf-8\") if secret else None\n    self._digestmod: str | Callable[[], Any] | ModuleType = sha256  # The digest algorithm used for hashing\n    self._serializer: Type[CeleryEventEmitter.Queue.Serializer] = serializer\n\n    # Register the event processor method as a Celery task\n    self._celery.task(self._executor, name=f\"pyventus{self._executor.__name__}\", queue=self._name)\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.enqueue","title":"enqueue","text":"
enqueue(event_emission: EventEmission) -> None\n

Enqueues an event emission object for asynchronous processing in Celery.

This method takes an EventEmission object and enqueues it for asynchronous execution by Celery workers. If a secret key is provided during initialization, the event emission object is first serialized, and its hash is calculated using the secret key. This hash is used to verify the integrity of the event emission object during execution.

PARAMETER DESCRIPTION event_emission

The event emission object to be enqueued for asynchronous execution.

TYPE: EventEmission

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/celery/celery_event_emitter.py
def enqueue(self, event_emission: EventEmitter.EventEmission) -> None:\n    \"\"\"\n    Enqueues an event emission object for asynchronous processing in Celery.\n\n    This method takes an `EventEmission` object and enqueues it for asynchronous\n    execution by Celery workers. If a secret key is provided during initialization,\n    the event emission object is first serialized, and its hash is calculated using\n    the secret key. This hash is used to verify the integrity of the event emission\n    object during execution.\n\n    :param event_emission: The event emission object to be enqueued for asynchronous execution.\n    :return: None\n    \"\"\"\n    # Serialize the event emission object\n    serialized_obj: Any = self._serializer.dumps(event_emission)\n\n    # Calculate the hash of the serialized object if a secret key was provided\n    obj_hash: Any | None = None\n    if self._secret:  # pragma: no cover\n        obj_hash = hmac.new(key=self._secret, msg=serialized_obj, digestmod=self._digestmod).digest()\n\n    # Create a payload with the serialized event emission instance and its hash\n    payload = CeleryEventEmitter.Queue._Payload(\n        serialized_obj=serialized_obj,\n        obj_hash=obj_hash,\n    )\n\n    # Send the event emission object to Celery for asynchronous execution\n    self._celery.send_task(\n        f\"pyventus{self._executor.__name__}\",\n        kwargs=payload.to_json(),\n        queue=self._name,\n    )\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.__init__","title":"__init__","text":"
__init__(queue: Queue, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of CeleryEventEmitter.

PARAMETER DESCRIPTION queue

The queue used for enqueuing event emissions in the Celery event emitter.

TYPE: Queue

event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/celery/celery_event_emitter.py
def __init__(self, queue: Queue, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n    \"\"\"\n    Initialize an instance of `CeleryEventEmitter`.\n    :param queue: The queue used for enqueuing event emissions in the Celery event emitter.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Validate the queue argument\n    if queue is None:\n        raise PyventusException(\"The 'queue' argument cannot be None\")\n    if not isinstance(queue, CeleryEventEmitter.Queue):\n        raise PyventusException(\"The 'queue' argument must be an instance of the CeleryEventEmitter.Queue class.\")\n\n    # Store the queue object\n    self._queue: CeleryEventEmitter.Queue = queue\n
"},{"location":"api/emitters/executor/","title":"ExecutorEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the concurrent.futures Executor base class to handle the execution of event emissions. It can work with either ThreadPoolExecutor for thread-based execution or ProcessPoolExecutor for process-based execution.

Notes:

  • When using this event emitter, it is important to properly manage the underlying Executor. Once you have finished emitting events, call the shutdown() method to signal the executor to free any resources for pending futures. You can avoid the need to call this method explicitly by using the with statement, which will automatically shut down the Executor (waiting as if Executor.shutdown() were called with wait set to True).

Read more in the Pyventus docs for Executor Event Emitter.

Source code in pyventus/emitters/executor/executor_event_emitter.py
class ExecutorEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the `concurrent.futures` Executor base class to\n    handle the execution of event emissions. It can work with either `ThreadPoolExecutor`\n    for thread-based execution or `ProcessPoolExecutor` for process-based execution.\n\n    **Notes:**\n\n    -   When using this event emitter, it is important to properly manage the underlying `Executor`.\n        Once you have finished emitting events, call the `shutdown()` method to signal the executor to\n        free any resources for pending futures. You can avoid the need to call this method explicitly\n        by using the `with` statement, which will automatically shut down the `Executor` (waiting as\n        if `Executor.shutdown()` were called with `wait` set to `True`).\n\n    ---\n    Read more in the\n    [Pyventus docs for Executor Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/executor/).\n    \"\"\"\n\n    @staticmethod\n    def _callback(event_emission: EventEmitter.EventEmission) -> None:\n        \"\"\"\n        This method is used as the callback function for the executor\n        to process the event emission.\n        :param event_emission: The event emission to be executed.\n        :return: None\n        \"\"\"\n        asyncio.run(event_emission())\n\n    def __init__(\n        self,\n        executor: Executor = ThreadPoolExecutor(),\n        event_linker: Type[EventLinker] = EventLinker,\n        debug: bool | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `ExecutorEventEmitter`.\n        :param executor: The executor object used to handle the execution of event\n            emissions. Defaults to `ThreadPoolExecutor()`.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Validate the executor argument\n        if executor is None:\n            raise PyventusException(\"The 'executor' argument cannot be None.\")\n        if not isinstance(executor, Executor):\n            raise PyventusException(\"The 'executor' argument must be an instance of the Executor class.\")\n\n        # Set the executor object reference\n        self._executor: Executor = executor\n\n    def __enter__(self) -> \"ExecutorEventEmitter\":\n        \"\"\"\n        Returns the current instance of `ExecutorEventEmitter` for context management.\n        :return: The current instance of `ExecutorEventEmitter`.\n        \"\"\"\n        return self\n\n    def __exit__(\n        self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n    ) -> None:\n        \"\"\"\n        Cleans up the executor resources when exiting the context.\n        :param exc_type: The exception type, if any.\n        :param exc_val: The exception value, if any.\n        :param exc_tb: The traceback information, if any.\n        :return: None\n        \"\"\"\n        self.shutdown(wait=True)\n\n    def shutdown(self, wait: bool = True, cancel_futures: bool = False) -> None:\n        \"\"\"\n        Shuts down the executor and frees any resources it is using.\n        :param wait: A boolean indicating whether to wait for the currently pending futures\n            to complete before shutting down.\n        :param cancel_futures: A boolean indicating whether to cancel any pending futures.\n        :return: None\n        \"\"\"\n        self._executor.shutdown(wait=wait, cancel_futures=cancel_futures)\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Submit the event emission to the executor\n        self._executor.submit(ExecutorEventEmitter._callback, event_emission)\n

"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.__init__","title":"__init__","text":"
__init__(executor: Executor = ThreadPoolExecutor(), event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of ExecutorEventEmitter.

PARAMETER DESCRIPTION executor

The executor object used to handle the execution of event emissions. Defaults to ThreadPoolExecutor().

TYPE: Executor DEFAULT: ThreadPoolExecutor()

event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/executor/executor_event_emitter.py
def __init__(\n    self,\n    executor: Executor = ThreadPoolExecutor(),\n    event_linker: Type[EventLinker] = EventLinker,\n    debug: bool | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `ExecutorEventEmitter`.\n    :param executor: The executor object used to handle the execution of event\n        emissions. Defaults to `ThreadPoolExecutor()`.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Validate the executor argument\n    if executor is None:\n        raise PyventusException(\"The 'executor' argument cannot be None.\")\n    if not isinstance(executor, Executor):\n        raise PyventusException(\"The 'executor' argument must be an instance of the Executor class.\")\n\n    # Set the executor object reference\n    self._executor: Executor = executor\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.__enter__","title":"__enter__","text":"
__enter__() -> ExecutorEventEmitter\n

Returns the current instance of ExecutorEventEmitter for context management.

RETURNS DESCRIPTION ExecutorEventEmitter

The current instance of ExecutorEventEmitter.

Source code in pyventus/emitters/executor/executor_event_emitter.py
def __enter__(self) -> \"ExecutorEventEmitter\":\n    \"\"\"\n    Returns the current instance of `ExecutorEventEmitter` for context management.\n    :return: The current instance of `ExecutorEventEmitter`.\n    \"\"\"\n    return self\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.__exit__","title":"__exit__","text":"
__exit__(exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) -> None\n

Cleans up the executor resources when exiting the context.

PARAMETER DESCRIPTION exc_type

The exception type, if any.

TYPE: Type[BaseException] | None

exc_val

The exception value, if any.

TYPE: BaseException | None

exc_tb

The traceback information, if any.

TYPE: TracebackType | None

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/executor/executor_event_emitter.py
def __exit__(\n    self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n) -> None:\n    \"\"\"\n    Cleans up the executor resources when exiting the context.\n    :param exc_type: The exception type, if any.\n    :param exc_val: The exception value, if any.\n    :param exc_tb: The traceback information, if any.\n    :return: None\n    \"\"\"\n    self.shutdown(wait=True)\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.shutdown","title":"shutdown","text":"
shutdown(wait: bool = True, cancel_futures: bool = False) -> None\n

Shuts down the executor and frees any resources it is using.

PARAMETER DESCRIPTION wait

A boolean indicating whether to wait for the currently pending futures to complete before shutting down.

TYPE: bool DEFAULT: True

cancel_futures

A boolean indicating whether to cancel any pending futures.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/executor/executor_event_emitter.py
def shutdown(self, wait: bool = True, cancel_futures: bool = False) -> None:\n    \"\"\"\n    Shuts down the executor and frees any resources it is using.\n    :param wait: A boolean indicating whether to wait for the currently pending futures\n        to complete before shutting down.\n    :param cancel_futures: A boolean indicating whether to cancel any pending futures.\n    :return: None\n    \"\"\"\n    self._executor.shutdown(wait=wait, cancel_futures=cancel_futures)\n
"},{"location":"api/emitters/fastapi/","title":"FastAPIEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes FastAPI's BackgroundTasks system to handle the execution of event emissions.

Notes:

  • It provides a convenient way to incorporate event-driven functionality into FastAPI apps.
  • This class offers a powerful mechanism for implementing asynchronous and decoupled operations in FastAPI, such as asynchronously sending emails in an event-driven manner.

Read more in the Pyventus docs for FastAPI Event Emitter.

Source code in pyventus/emitters/fastapi/fastapi_event_emitter.py
class FastAPIEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes FastAPI's BackgroundTasks system\n    to handle the execution of event emissions.\n\n    **Notes:**\n\n    -   It provides a convenient way to incorporate event-driven functionality\n        into FastAPI apps.\n    -   This class offers a powerful mechanism for implementing asynchronous\n        and decoupled operations in FastAPI, such as asynchronously sending\n        emails in an event-driven manner.\n\n    ---\n    Read more in the\n    [Pyventus docs for FastAPI Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/fastapi/).\n    \"\"\"\n\n    @classmethod\n    def options(\n        cls, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None\n    ) -> Callable[[BackgroundTasks], \"FastAPIEventEmitter\"]:\n        \"\"\"\n        Returns a decorator that allows you to configure the `FastAPIEventEmitter` class\n        when using FastAPI's `Depends` method.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        :return: A decorator that can be used with the `Depends` method.\n        \"\"\"\n\n        def wrapper(background_tasks: BackgroundTasks) -> \"FastAPIEventEmitter\":\n            \"\"\"\n            A decorator wrapper function that configures the `FastAPIEventEmitter` class with\n            the provided options.\n            :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n                the execution of event emissions.\n            :return: An instance of `FastAPIEventEmitter` configured with the specified options.\n            \"\"\"\n            return cls(background_tasks=background_tasks, event_linker=event_linker, debug=debug)\n\n        return wrapper\n\n    def __init__(\n        self,\n        background_tasks: BackgroundTasks,\n        event_linker: Type[EventLinker] = EventLinker,\n        debug: bool | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `FastAPIEventEmitter`.\n        :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n            the execution of event emissions.\n        :param event_linker: Specifies the type of event linker to use for associating\n            events with their respective event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Check if the provided background_tasks object is valid\n        if background_tasks is None:\n            raise PyventusException(\"The 'background_tasks' argument cannot be None.\")\n        if not isinstance(background_tasks, BackgroundTasks):\n            raise PyventusException(\"The 'background_tasks' argument must be an instance of the BackgroundTasks class.\")\n\n        # Set the background tasks\n        self._background_tasks: BackgroundTasks = background_tasks\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Submit the event emission to the background tasks\n        self._background_tasks.add_task(event_emission)\n

"},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter.options","title":"options classmethod","text":"
options(event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> Callable[[BackgroundTasks], FastAPIEventEmitter]\n

Returns a decorator that allows you to configure the FastAPIEventEmitter class when using FastAPI's Depends method.

PARAMETER DESCRIPTION event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

RETURNS DESCRIPTION Callable[[BackgroundTasks], FastAPIEventEmitter]

A decorator that can be used with the Depends method.

Source code in pyventus/emitters/fastapi/fastapi_event_emitter.py
@classmethod\ndef options(\n    cls, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None\n) -> Callable[[BackgroundTasks], \"FastAPIEventEmitter\"]:\n    \"\"\"\n    Returns a decorator that allows you to configure the `FastAPIEventEmitter` class\n    when using FastAPI's `Depends` method.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    :return: A decorator that can be used with the `Depends` method.\n    \"\"\"\n\n    def wrapper(background_tasks: BackgroundTasks) -> \"FastAPIEventEmitter\":\n        \"\"\"\n        A decorator wrapper function that configures the `FastAPIEventEmitter` class with\n        the provided options.\n        :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n            the execution of event emissions.\n        :return: An instance of `FastAPIEventEmitter` configured with the specified options.\n        \"\"\"\n        return cls(background_tasks=background_tasks, event_linker=event_linker, debug=debug)\n\n    return wrapper\n
"},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter.__init__","title":"__init__","text":"
__init__(background_tasks: BackgroundTasks, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of FastAPIEventEmitter.

PARAMETER DESCRIPTION background_tasks

The FastAPI BackgroundTasks object used to handle the execution of event emissions.

TYPE: BackgroundTasks

event_linker

Specifies the type of event linker to use for associating events with their respective event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/fastapi/fastapi_event_emitter.py
def __init__(\n    self,\n    background_tasks: BackgroundTasks,\n    event_linker: Type[EventLinker] = EventLinker,\n    debug: bool | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `FastAPIEventEmitter`.\n    :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n        the execution of event emissions.\n    :param event_linker: Specifies the type of event linker to use for associating\n        events with their respective event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Check if the provided background_tasks object is valid\n    if background_tasks is None:\n        raise PyventusException(\"The 'background_tasks' argument cannot be None.\")\n    if not isinstance(background_tasks, BackgroundTasks):\n        raise PyventusException(\"The 'background_tasks' argument must be an instance of the BackgroundTasks class.\")\n\n    # Set the background tasks\n    self._background_tasks: BackgroundTasks = background_tasks\n
"},{"location":"api/emitters/rq/","title":"RQEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the Redis Queue system to handle the execution of event emissions.

Notes:

  • This class uses a Redis Queue instance to enqueue event emissions, which are subsequently executed by Redis Queue workers. This approach provides a scalable and distributed method for handling the execution of event emissions.

Read more in the Pyventus docs for RQ Event Emitter.

Source code in pyventus/emitters/rq/rq_event_emitter.py
class RQEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the Redis Queue system to handle the\n    execution of event emissions.\n\n    **Notes:**\n\n    -   This class uses a Redis Queue instance to enqueue event emissions, which are\n        subsequently executed by Redis Queue workers. This approach provides a scalable\n        and distributed method for handling the execution of event emissions.\n\n    ---\n    Read more in the\n    [Pyventus docs for RQ Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/rq/).\n    \"\"\"\n\n    def __init__(\n        self,\n        queue: Queue,\n        options: Dict[str, Any] | None = None,\n        event_linker: Type[EventLinker] = EventLinker,\n        debug: bool | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `RQEventEmitter`.\n        :param queue: The Redis queue for enqueuing event handlers.\n        :param options: Additional options for the RQ package enqueueing method.\n            Defaults to an empty dictionary.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Validate the queue argument\n        if queue is None:\n            raise PyventusException(\"The 'queue' argument cannot be None.\")\n        if not isinstance(queue, Queue):\n            raise PyventusException(\"The 'queue' argument must be an instance of the Queue class.\")\n\n        # Store the Redis queue and RQ options\n        self._queue: Queue = queue\n        self._options: Dict[str, Any] = options if options is not None else {}\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Add the event emission to the Redis Queue\n        self._queue.enqueue(event_emission, **self._options)\n

"},{"location":"api/emitters/rq/#pyventus.emitters.rq.RQEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/rq/#pyventus.emitters.rq.RQEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/rq/#pyventus.emitters.rq.RQEventEmitter.__init__","title":"__init__","text":"
__init__(queue: Queue, options: Dict[str, Any] | None = None, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of RQEventEmitter.

PARAMETER DESCRIPTION queue

The Redis queue for enqueuing event handlers.

TYPE: Queue

options

Additional options for the RQ package enqueueing method. Defaults to an empty dictionary.

TYPE: Dict[str, Any] | None DEFAULT: None

event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/rq/rq_event_emitter.py
def __init__(\n    self,\n    queue: Queue,\n    options: Dict[str, Any] | None = None,\n    event_linker: Type[EventLinker] = EventLinker,\n    debug: bool | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `RQEventEmitter`.\n    :param queue: The Redis queue for enqueuing event handlers.\n    :param options: Additional options for the RQ package enqueueing method.\n        Defaults to an empty dictionary.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Validate the queue argument\n    if queue is None:\n        raise PyventusException(\"The 'queue' argument cannot be None.\")\n    if not isinstance(queue, Queue):\n        raise PyventusException(\"The 'queue' argument must be an instance of the Queue class.\")\n\n    # Store the Redis queue and RQ options\n    self._queue: Queue = queue\n    self._options: Dict[str, Any] = options if options is not None else {}\n
"},{"location":"tutorials/","title":"Tutorials","text":"

\u2003\u2003Welcome to the Tutorials section, where you can learn the key concepts and features of Pyventus through step-by-step examples. These tutorials are designed to be user-friendly, covering a range of topics from the basics to more advanced concepts.

\u2003\u2003By following these tutorials, you'll gain a solid understanding of Pyventus' core abstractions and how to effectively apply them when building event-driven applications. The tutorials are organized in a progressive manner, allowing you to gradually enhance your knowledge and skills.

Let's kickstart your Pyventus experience!

"},{"location":"tutorials/event-linker/","title":"The EventLinker Registry","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003Events are essential for building reactive applications with Pyventus. However, we need a way to connect events to the code that should run in response. This is where the EventLinker comes in.

"},{"location":"tutorials/event-linker/#what-is-the-eventlinker","title":"What is the EventLinker?","text":"

\u2003\u2003The EventLinker is a central class that acts as a registry for linking events to their associated event handlers. It keeps track of which events have event handlers assigned to them, so when an event occurs it knows which code needs to run.

\u2003\u2003The EventLinker can also be subclassed to create separate linking registries, allowing you to define different namespaces or contexts for events and event handlers.

"},{"location":"tutorials/event-linker/#benefits-of-using-the-eventlinker","title":"Benefits of Using The EventLinker","text":"

Using the EventLinker class offers several advantages:

  • Clear separation of concerns - The emitter class focuses only on emitting events, while the linker handles all the subscription logic. This makes both classes cleaner and easier to understand, as well as allowing them to be modified independently as needed.
  • Centralized logic - Having global registries means there is only one place to go to see which events have event handlers. This simplifies management of the overall event system.
  • Flexibility - You can change the event emitter instance at runtime without the need to reconfigure all connections.
"},{"location":"tutorials/event-linker/#event-handlers","title":"Event Handlers","text":"

\u2003\u2003Before proceeding, we should first understand the concept behind event handlers and what they do. An event handler is a callable object designed to respond to a specific event. When an event occurs, such as a button click, mouse movement, or a timer expiration, these objects are executed concurrently in response.

\u2003\u2003The EventHandler class encapsulates the event callbacks and provides a standardized mechanism for executing them when the event occurs. It handles the asynchronous and synchronous execution of event callbacks, as well as the completion workflow for success and error handling.

"},{"location":"tutorials/event-linker/#subscribing-event-handlers","title":"Subscribing Event Handlers","text":"

\u2003\u2003The EventLinker makes it easy to subscribe event handlers to respond to events. Let's explore the different approaches to subscribing event handlers.

"},{"location":"tutorials/event-linker/#subscription-basics","title":"Subscription Basics","text":"

\u2003\u2003Pyventus supports two types of subscriptions to handle callback registration: regular subscriptions and one-time subscriptions. Each subscription, regardless of type, will create a separate EventHandler instance independently. So subscribing the same callback multiple times to an event will cause it to be invoked multiple times.

"},{"location":"tutorials/event-linker/#regular-subscriptions","title":"Regular Subscriptions","text":"

\u2003\u2003Regular subscriptions trigger the event handler each time the subscribed event(s) occur, and the handler remains subscribed until explicitly unsubscribed. Below we will explore how to subscribe regular event handlers.

"},{"location":"tutorials/event-linker/#using-decorators","title":"Using decorators","text":"

Decorators provide a clean Python syntax for subscribing handlers.

Sync contextAsync context
# Subscribe to one event\n@EventLinker.on(\"StringEvent\")\ndef event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.on(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\ndef event_callback2():\n    print(\"Event received!\")\n
# Subscribe to one event\n@EventLinker.on(\"StringEvent\")\nasync def event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.on(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\nasync def event_callback2():\n    print(\"Event received!\")\n
"},{"location":"tutorials/event-linker/#using-the-subscribe-method","title":"Using the subscribe() method","text":"

You can also subscribe event handlers by calling the subscribe() method.

Sync contextAsync context
def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\"StringEvent\", event_callback=event_callback)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", event_callback=event_callback)\n
async def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\"StringEvent\", event_callback=event_callback)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", event_callback=event_callback)\n
"},{"location":"tutorials/event-linker/#one-time-subscriptions","title":"One-time Subscriptions","text":"

\u2003\u2003One-time subscriptions trigger the event handler only once, then automatically unsubscribe it. One-time handlers are useful for tasks that should only run once.

Behavior with Multiple Events

When subscribing a one-time handler to multiple events, if one event fires it will automatically unsubscribe the event handler from all other events.

"},{"location":"tutorials/event-linker/#using-decorators_1","title":"Using decorators","text":"

In order to perform a one-time subscription using decorators we use the once() method:

Sync contextAsync context
# Subscribe to one event\n@EventLinker.once(\"StringEvent\")\ndef event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.once(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\ndef event_callback2():\n    print(\"Event received!\")\n
# Subscribe to one event\n@EventLinker.once(\"StringEvent\")\nasync def event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.once(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\nasync def event_callback2():\n    print(\"Event received!\")\n
"},{"location":"tutorials/event-linker/#using-the-subscribe-method_1","title":"Using the subscribe() method","text":"

Alternatively, you can also use the subscribe() method to do a one-time subscription too:

Sync contextAsync context
def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\n    \"StringEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\n    \"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n
async def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\n    \"StringEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\n    \"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n
"},{"location":"tutorials/event-linker/#success-and-error-handling","title":"Success and Error Handling","text":"

\u2003\u2003In the previous sections, we discussed the process of subscribing callback functions to handle events using Pyventus' event linker. However, there may be times when we need more control over the exact workflow.

\u2003\u2003In this section, we'll show you how to define custom logic to process events upon completion using success and failure handlers. It's important to have reliable control over the asynchronous flow to build robust apps.

"},{"location":"tutorials/event-linker/#response-logic-for-regular-subscriptions","title":"Response Logic For Regular Subscriptions","text":"Using DecoratorsUsing the subscribe() method
with EventLinker.on(\"DivisionEvent\") as linker:  # (1)!\n\n    @linker.on_event\n    def handle_division(a: float, b: float) -> float:\n        return a / b\n\n    @linker.on_success\n    def handle_success(result: float) -> None:\n        print(f\"Division result: {result:.3g}\")\n\n    @linker.on_failure\n    def handle_failure(e: Exception) -> None:\n        print(f\"Oops, something went wrong: {e}\")\n
  1. When the EventLinker.on method is used as a context manager via the with statement, it allows multiple callbacks to be associated with events within the linkage context block, defining the event workflow.
def handle_division(a: float, b: float) -> float:\n    return a / b\n\ndef handle_success(result: float) -> None:\n    print(f\"Division result: {result:.3g}\")\n\ndef handle_failure(e: Exception) -> None:\n    print(f\"Oops, something went wrong: {e}\")\n\n\nEventLinker.subscribe(\n    \"DivisionEvent\", \n    event_callback=handle_division, \n    success_callback=handle_success, \n    failure_callback=handle_failure,\n)\n
"},{"location":"tutorials/event-linker/#response-logic-for-one-time-subscriptions","title":"Response Logic For One-time Subscriptions","text":"Using DecoratorsUsing the subscribe() method
with EventLinker.once(\"DivisionEvent\") as linker:  # (1)!\n\n    @linker.on_event\n    def handle_division(a: float, b: float) -> float:\n        return a / b\n\n    @linker.on_success\n    def handle_success(result: float) -> None:\n        print(f\"Division result: {result:.3g}\")\n\n    @linker.on_failure\n    def handle_failure(e: Exception) -> None:\n        print(f\"Oops, something went wrong: {e}\")\n
  1. When the EventLinker.once method is used as a context manager via the with statement, it allows multiple callbacks to be associated with events within the linkage context block, defining the event workflow.
def handle_division(a: float, b: float) -> float:\n    return a / b\n\ndef handle_success(result: float) -> None:\n    print(f\"Division result: {result:.3g}\")\n\ndef handle_failure(e: Exception) -> None:\n    print(f\"Oops, something went wrong: {e}\")\n\n\nEventLinker.subscribe(\n    \"DivisionEvent\", \n    event_callback=handle_division, \n    success_callback=handle_success, \n    failure_callback=handle_failure,\n    once=True,\n)\n
"},{"location":"tutorials/event-linker/#optimizing-callbacks-execution","title":"Optimizing Callbacks Execution","text":"

\u2003\u2003By default, event handlers in Pyventus are executed concurrently during an event emission, running their sync and async callbacks as defined. However, if you have a sync callback that involves I/O or non-CPU bound operations, you can enable the force_async parameter to offload it to a thread pool, ensuring optimal performance and responsiveness. The force_async parameter utilizes the asyncio.to_thread() function to execute sync callbacks asynchronously.

Using DecoratorsUsing the subscribe() method
# You can also set this when using the `with` \n# statement and the `once()` decorator\n@EventLinker.on(\"BlockingIO\", force_async=True)\ndef blocking_io():\n    print(f\"start blocking_io at {time.strftime('%X')}\")\n    # Note that time.sleep() can be replaced with any blocking\n    # IO-bound operation, such as file operations.\n    time.sleep(1)\n    print(f\"blocking_io complete at {time.strftime('%X')}\")\n
def blocking_io():\n    print(f\"start blocking_io at {time.strftime('%X')}\")\n    # Note that time.sleep() can be replaced with any blocking\n    # IO-bound operation, such as file operations.\n    time.sleep(1)\n    print(f\"blocking_io complete at {time.strftime('%X')}\")\n\nEventLinker.subscribe(\n    \"BlockingIO\",\n    event_callback=blocking_io,\n    force_async=True,\n)\n
"},{"location":"tutorials/event-linker/#unsubscribing-event-handlers","title":"Unsubscribing Event Handlers","text":"

\u2003\u2003Removing event handlers is an important part of working with events. Let's look at different approaches to properly teardown event subscriptions:

"},{"location":"tutorials/event-linker/#removing-an-event-handler-from-an-event","title":"Removing an Event Handler from an Event","text":"

To remove a single event handler from a specific event:

def event_callback():\n    print(\"Event received!\")\n\n\nevent_handler = EventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", event_callback=event_callback)\n\nEventLinker.unsubscribe(\"StringEvent\", event_handler=event_handler)  # (1)!\n
  1. Removes the event_handler from just the StringEvent
"},{"location":"tutorials/event-linker/#removing-event-handlers-from-all-events","title":"Removing Event Handlers from All Events","text":"

To remove an event handler from all subscribed events:

def event_callback():\n    print(\"Event received!\")\n\n\nevent_handler = EventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", event_callback=event_callback)\n\nEventLinker.remove_event_handler(event_handler=event_handler)  # (1)!\n
  1. Removes the event_handler from the StringEvent and AnotherEvent
"},{"location":"tutorials/event-linker/#removing-an-event-and-its-event-handlers","title":"Removing an Event and its Event Handlers","text":"

To delete an event and all associated handlers:

@EventLinker.on(\"StringEvent\")\ndef event_callback1():\n    print(\"Event received!\")\n\n\n@EventLinker.on(\"StringEvent\", \"AnotherEvent\")\ndef event_callback2():\n    print(\"Event received!\")\n\n\nEventLinker.remove_event(event=\"StringEvent\")  # (1)!\n
  1. Removes all event handlers associated with the StringEvent
"},{"location":"tutorials/event-linker/#clearing-all-events","title":"Clearing All Events","text":"

To remove all events and their handlers:

@EventLinker.on(\"StringEvent\", ...)\ndef event_callback1():\n    pass\n\n\n@EventLinker.on(...)\ndef event_callback2():\n    pass\n\n\nEventLinker.remove_all() \n
"},{"location":"tutorials/event-linker/#custom-event-linkers","title":"Custom Event Linkers","text":"

\u2003\u2003The EventLinker class in Pyventus was designed to support subclassing to allow you to define separate linking registries or namespaces for your events and handlers, as well as configure the EventLinker behavior. This approach provides a powerful way to customize how events are linked within your applications. Some key reasons for using custom linkers include:

  • Organizing events into logical domains/contexts.
  • Isolating events to only be accessible within certain scopes.
  • Configuring linker-specific options like max handlers per event.

To define a custom linker, subclass the EventLinker, as shown below:

class UserEventLinker(EventLinker, max_event_handlers=10):\n    \"\"\" EventLinker for User's events only \"\"\"\n    pass  # Additional logic can be added here if needed...\n\n\n@UserEventLinker.on(\"PasswordResetEvent\")\nasync def handle_password_reset_event(email: str):\n    print(\"PasswordResetEvent received!\")\n\n\n@UserEventLinker.on(\"EmailVerifiedEvent\")\nasync def handle_email_verified_event(email: str):\n    print(\"EmailVerifiedEvent received!\")\n
"},{"location":"tutorials/event-linker/#debug-mode","title":"Debug Mode","text":"

\u2003\u2003The EventLinker also offers a debug mode feature which helps you understand how event subscriptions and unsubscriptions are happening during runtime.

"},{"location":"tutorials/event-linker/#global-debug-mode","title":"Global Debug Mode","text":"

\u2003\u2003By default, Pyventus leverages the Python's global debug tracing feature. Simply run your code in an IDE's debugger mode to activate the global debug mode tracing.

"},{"location":"tutorials/event-linker/#namespace-debug-flag","title":"Namespace Debug Flag","text":"

\u2003\u2003Alternatively, if you want to enable or disable the debug mode specifically for a certain EventLinker namespace, you can use the debug flag that is available in the subclass configurations. Setting the debug flag to True enables debug mode for that namespace, while setting it to False disables debug mode. Here's an example:

Debug Flag OnDebug Flag Off
class CustomEventLinker(EventLinker, debug=True):\n    pass  # Additional logic can be added here if needed...\n
class CustomEventLinker(EventLinker, debug=False):\n    pass  # Additional logic can be added here if needed...\n
"},{"location":"tutorials/event-linker/#recap","title":"Recap","text":"

In this tutorial we covered:

  • The standardized mechanism for executing event callbacks (Event Handlers).
  • Subscribing regular and one-time handlers with decorators and the subscribe() method.
  • Unsubscribing a single event handler, all handlers, an event, or clearing all.
  • Success and error handling by defining custom logic for event completion.
  • Techniques for optimizing synchronous callback execution.
  • Custom Event Linkers to separate event namespaces.
  • Debug mode to trace subscriptions

We learned the core EventLinker concepts of:

  • Use the EventLinker to link events to code responses.
  • Subscribe/unsubscribe as needed using various approaches.

"},{"location":"tutorials/event/","title":"Exploring Event Types","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003In this first tutorial, you'll learn about defining and handling events in Pyventus. Whether you're new to event-driven programming or just getting started with the package, this guide will explain the key concepts.

"},{"location":"tutorials/event/#what-are-events","title":"What are Events?","text":"

\u2003\u2003In event-driven programming, events refer to specific occurrences or incidents that happen within the program or system. These events play an important role in Pyventus by enabling different parts of a program to communicate and react to changes. Pyventus provides a powerful event system that supports various event types, allowing developers to effectively define and handle events.

"},{"location":"tutorials/event/#string-events","title":"String Events","text":"

\u2003\u2003We'll begin with Pyventus' basic string event type. These provide an easy way to define and handle events using event names as strings. This makes them straightforward to work with and are suited for simpler applications requiring a minimal approach.

"},{"location":"tutorials/event/#passing-data","title":"Passing Data","text":"

\u2003\u2003When subscribing to a String Event, event callbacks can define parameters like regular functions. This allows flexibility in passing different data types like strings, integers, etc. The event emitters forward any arguments emitted with the string event to handlers using *args and **kwargs, ensuring they receive the same data payload.

"},{"location":"tutorials/event/#usage","title":"Usage","text":"

Let's look at some code examples of defining and handling String Events:

@EventLinker.on(\"StringEvent\")\ndef event_handler(param1: str, param2: str, **kwargs):\n    print(\"Parameters:\", param1, param2)\n    print(\"**kwargs:\", kwargs)\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"StringEvent\", \"value1\", param2=\"value2\", key1=\"value3\", key2=\"value4\")\n

You can also use the subscribe() method to define and link string-named events:

def event_handler():\n    pass\n\nEventLinker.subscribe(\"StringEvent\", event_callback=event_handler)\n
"},{"location":"tutorials/event/#event-objects","title":"Event Objects","text":"

\u2003\u2003Let's move on to Event Objects in Pyventus. They provide a structured way to define events and encapsulate relevant data payloads. Some benefits include better organization, maintainability, and validation support.

"},{"location":"tutorials/event/#defining-event-objects","title":"Defining Event Objects","text":"

To create an Event Object:

  1. Define a Python dataclass.
  2. Declare fields for the event's payload within the class.

For example:

@dataclass\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n
"},{"location":"tutorials/event/#adding-validation","title":"Adding Validation","text":"

\u2003\u2003You can also ensure valid data before propagation by adding validation logic to the Event class using the dataclass' __post_init__() method:

@dataclass\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n\n    def __post_init__(self):\n        if not isinstance(self.order_id, int):\n            raise ValueError(\"Error: 'order_id' must be a valid int number!\")\n
"},{"location":"tutorials/event/#usage_1","title":"Usage","text":"

Here's an example demonstrating subscription and emission:

@dataclass  # Define a Python dataclass.\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n\n\n@EventLinker.on(OrderCreatedEvent)  # Subscribe event handlers to the event.\ndef handle_order_created_event(event: OrderCreatedEvent):\n    # Pyventus will automatically pass the Event Object \n    # as the first positional argument.\n    print(f\"Event Object: {event}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit( # (1)!\n    event=OrderCreatedEvent(  # Emit an instance of the event!\n        order_id=6452879,\n        payload={},\n    ),\n)\n
  1. You can also emit the Event object as a positional argument:
    event_emitter.emit( \n    OrderCreatedEvent(\n        order_id=6452879,\n        payload={},\n    ),\n)  \n
    As well as pass extra *args and **kwargs too:
    event_emitter.emit( \n    OrderCreatedEvent(\n        order_id=6452879,\n        payload={},\n    ),\n    \"param1\",\n    param2=\"param2\",\n)  \n

Event Object's Behavior

By default, Pyventus retrieves the event name from the event class and automatically passes the instance of the Event Object as the first positional argument to the event callback, even if you provide additional *args or **kwargs.

"},{"location":"tutorials/event/#benefits","title":"Benefits","text":"
  • Natural emission of event payloads: Emitting an event object is a simple and intuitive process. Once an event object is created, it can be sent using the event emitter, providing a natural and straightforward approach to event emission. Since the event object carries the relevant data, the event emitter ensures that the same data is received by the event handlers.
  • Structured and organized event definitions: Event objects provide a structured and encapsulated representation of events, enabling better organization and management of events throughout the system.
  • Custom data validation: Event objects can include custom validation logic to ensure the validity of the encapsulated data before propagation.
  • Auto-completion when handling events: Event objects benefit from autocompletion integration provided by code editors and IDEs.
"},{"location":"tutorials/event/#exception-events","title":"Exception Events","text":"

\u2003\u2003In addition to normal events, Pyventus allows exceptions to be treated as first-class events. This enables propagating and handling errors in an event-driven manner. If you're interested in incorporating error handling in event emission, you can check out Success and Error Handling.

"},{"location":"tutorials/event/#usage_2","title":"Usage","text":"

Let's look at some code examples that demonstrates the usage of event exceptions:

@EventLinker.on(ValueError)\ndef handle_validation_error(exc: ValueError):\n    print(f\"Validation failed for; '{exc}'\")\n\n\ntry:\n    raise ValueError(\"`username`, already in use.\")\nexcept ValueError as e:\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(e)\n
You can also work with custom exceptions...
class UserValidationError(ValueError):\n    def __init__(self, error: str = \"Validation Error\"):\n        super().__init__(error)\n        self.error: str = error\n\n\n@EventLinker.on(UserValidationError)\ndef handle_validation_error(exc: UserValidationError):\n    print(f\"Validation failed for; '{exc.error}'\")\n\n\ntry:\n    raise UserValidationError(\"`username`, already in use.\")\nexcept UserValidationError as e:\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(e)\n
"},{"location":"tutorials/event/#benefits_1","title":"Benefits","text":"

\u2003\u2003By treating exceptions as first-class events, Pyventus provides a unified approach to handling errors in an event-driven manner. This approach leverages the existing event-driven infrastructure, promotes code reuse, and enables flexible and powerful error handling strategies.

"},{"location":"tutorials/event/#global-events","title":"Global Events","text":"

\u2003\u2003In addition to individual events, Pyventus provides support for Global Events within the context of an EventLinker. This feature allows you to register handlers that respond to event occurrences across a specific namespace, regardless of where they happen in your code. Global Events are particularly useful for implementing cross-cutting concerns such as logging, monitoring, or analytics. By subscribing event handlers to ... or Ellipsis, you can capture all events that may occur within that EventLinker context.

@EventLinker.on(...)\ndef handle_any_event(*args, **kwargs):  #(1)!\n    print(f\"Perform logging...\\nArgs: {args}\\tKwargs: {kwargs}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\", name=\"Pyventus\")\n
  1. This handler will be triggered by any event type that occurs.
"},{"location":"tutorials/event/#recap","title":"Recap","text":"

\u2003\u2003In summary, we've covered the different types of events supported by Pyventus - string named events, event objects, and exception events. String events provide a simple way to define and handle basic events using names as strings. Event objects offer a more structured approach with encapsulated payloads and validation capabilities. And exception events allow treating errors as first-class events.

\u2003\u2003Additionally, Pyventus provides support for Global Events within an EventLinker context. Global Events allow registering handlers across a namespace to respond to events anywhere in the code. This feature is useful for implementing cross-cutting concerns like logging, monitoring, or analytics.

Using Different Emitters and Linkers

The EventEmitter and EventLinker used in the code examples can be easily replaced with any custom or built-in Pyventus implementation of your choice. For more information on available options, consult the official documentation.

"},{"location":"tutorials/emitters/","title":"Master the Event Emitter","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003In the previous tutorial, we learned how to link events with their event handlers using the EventLinker. Now, let's dive into the process of dispatching events and triggering the associated callbacks. This tutorial will introduce you to the EventEmitter class, which plays a crucial role in this event-driven system.

"},{"location":"tutorials/emitters/#what-is-an-event-emitter","title":"What is an Event Emitter?","text":"

\u2003\u2003In event-driven programming, an Event Emitter is a variation of the Observer pattern that allows you to easily manage and handle events in a decoupled manner. The Observer pattern provides a way for objects to subscribe to and receive notifications from a subject when its state changes. Similarly, an Event Emitter enables objects/functions to subscribe to and receive notifications when specific events occur.

"},{"location":"tutorials/emitters/#pyventus-event-emitter","title":"Pyventus Event Emitter","text":"

\u2003\u2003In Pyventus, the Event Emitter concept remains largely the same, but with a few unique features of its own. The Pyventus Event Emitter focuses only on emitting events, while the association logic is handled by the Event Linker class. This separation of concerns makes both classes cleaner and easier to understand, as well as allowing them to be modified independently as needed. Furthermore, it offers the flexibility to change the event emitter instance at runtime without the need to reconfigure all connections.

\u2003\u2003So, what exactly is the Pyventus EventEmitter? It is an abstract base class that provides a common interface for emitting events and notifying registered callbacks when those events occur. It serves as the foundation for implementing custom event emitters with specific dispatch strategies.

"},{"location":"tutorials/emitters/#purpose-of-pyventus-event-emitter","title":"Purpose of Pyventus Event Emitter","text":"

\u2003\u2003The main goal of the EventEmitter base class is to decouple the event emission process from the underlying implementation. This decoupling promotes flexibility, adaptability, and adheres to the Open-Closed principle, allowing the implementation of custom event emitters without impacting existing consumers.

\u2003\u2003The EventEmitter presents a unified API with two key methods: emit() and _process(). These methods can be used in both synchronous and asynchronous contexts to emit events and handle their emission. The emit() method is used to invoke an event, while the _process() method is an abstract method responsible for processing the execution of the emitted event.

"},{"location":"tutorials/emitters/#built-in-event-emitters","title":"Built-in Event Emitters","text":"

\u2003\u2003Pyventus includes several built-in event emitters by default. For instance, the AsyncIOEventEmitter leverages the AsyncIO framework to handle the execution of event emissions, while the RQEventEmitter utilizes Redis Queue pub/sub system with workers to manage the execution of event emissions.

Driving Innovation Through Collaboration

Pyventus is an open source project that welcomes community involvement. If you wish to contribute additional event emitters, improvements, or bug fixes, please check the Contributing section for guidelines on collaborating. Together, we can further the possibilities of event-driven development.

"},{"location":"tutorials/emitters/#custom-event-emitters","title":"Custom Event Emitters","text":"

\u2003\u2003Pyventus provides a powerful abstraction layer for creating custom event emitters, allowing you to tailor their behavior and capabilities to suit your specific needs. In this section, we will guide you through the process of creating a custom event emitter specifically designed for the FastAPI framework.

\u2003\u2003The objective is to leverage FastAPI's BackgroundTasks feature to efficiently process the execution of event emissions within your FastAPI application. Before we jump into the implementation details, make sure you have FastAPI properly installed and set up in your development environment.

"},{"location":"tutorials/emitters/#defining-and-implementing-the-custom-event-emitter-class","title":"Defining and Implementing the Custom Event Emitter Class","text":"

\u2003\u2003To create the custom event emitter for FastAPI, we'll define a class called FastAPIEventEmitter. This class will extend the base EventEmitter class and implement the abstract _process() method using the FastAPI's background tasks to handle the event emission properly.

from fastapi import BackgroundTasks\n\nfrom pyventus import EventEmitter, EventLinker\n\n\nclass FastAPIEventEmitter(EventEmitter):\n    \"\"\"A custom event emitter that uses the FastAPI background tasks.\"\"\"\n\n    def __init__(self, background_tasks: BackgroundTasks):\n        super().__init__(event_linker=EventLinker, debug=False)\n        self._background_tasks = background_tasks  # (1)!\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        self._background_tasks.add_task(event_emission)  # (2)!\n
  1. Stores the FastAPI background tasks object.
  2. Executes the event handler callbacks as background tasks.

Once the custom event emitter is defined, you can integrate it into your code as follows:

@EventLinker.on(\"ConsolePrintEvent\")\nasync def handle_console_print_event(message: str):\n    await sleep(randint(0, 3))  # (1)!\n    print(message)\n\n\napp = FastAPI()\n\n\n@app.get(\"/print\")\nasync def console_print_endpoint(background_tasks: BackgroundTasks):\n    \"\"\" FastAPI endpoint that triggers the console_print event. \"\"\"\n\n    def app_service(event_emitter: EventEmitter) -> None:\n        event_emitter.emit(\n            event=\"ConsolePrintEvent\", \n            message=f\"\\n{type(event_emitter).__name__}\",\n        )\n\n    fastapi_event_emitter = FastAPIEventEmitter(background_tasks)\n    app_service(event_emitter=fastapi_event_emitter)\n\n    return {\"message\": \"Console print triggered!\"}\n
  1. Simulate a random delay.
To test the custom event emitter integration follow these steps...

Run the server with:

uvicorn main:app --reload\n

Open your browser at http://127.0.0.1:8000/print. You will see the JSON response as:

{ \"message\": \"Console print triggered!\" }\n

And also you are going see the outputs of the event emitter in the console logs as:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\nINFO:     Started reloader process [28720]\nINFO:     Started server process [28722]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\nINFO:     127.0.0.1:52391 - \"GET /print HTTP/1.1\" 200 OK\n\nFastAPIEventEmitter\n

Official FastAPIEventEmitter Integration

In case you're interested in integrating Pyventus with FastAPI, you can refer to the official Pyventus FastAPI Event Emitter implementation.

"},{"location":"tutorials/emitters/#runtime-flexibility","title":"Runtime Flexibility","text":"

\u2003\u2003Another key feature of the Pyventus EventEmitter is the decoupling of event dispatching from the underlying implementation that processes the event handlers. This, combined with the EventLinker, allows you to change the event emitter at runtime without reconfiguring all the connections or any complex logic. We can use the base EventEmitter as a dependency and then change the concrete instance to suit your needs. Let's demonstrate this using the AsyncIOEventEmitter and ExecutorEventEmitter:

from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter, ExecutorEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\ndef handle_greet_event(name: str = \"World\"):\n    print(f\"Hello, {name}!\")\n\n\nif __name__ == \"__main__\":\n    def main(event_emitter: EventEmitter) -> None:\n        event_emitter.emit(\"GreetEvent\", name=type(event_emitter).__name__)\n\n\n    main(event_emitter=AsyncIOEventEmitter())\n    with ExecutorEventEmitter() as executor_event_emitter:\n        main(event_emitter=executor_event_emitter)\n

\u2003\u2003In the example above, we defined a helper function handle_greet_event that accepts an EventEmitter instance as a parameter. This allows us to dynamically switch between the AsyncIOEventEmitter and the ExecutorEventEmitter depending on our requirements. This flexibility enables us to adapt the event emitter implementation at runtime without modifying the core application logic.

"},{"location":"tutorials/emitters/#using-custom-event-linkers","title":"Using Custom Event Linkers","text":"

\u2003\u2003By default, event emitters come with the base EventLinker registry assigned to the event_linker property. However, you have the flexibility to specify the EventLinker class that will be used by the EventEmitter. To configure this option, simply manually set the EventLinker class in the constructor.

from pyventus import EventEmitter, EventLinker, AsyncIOEventEmitter\n\n\nclass UserEventLinker(EventLinker, max_event_handlers=10):\n    \"\"\" EventLinker for User's events only \"\"\"\n    pass  # Additional logic can be added here if needed...\n\n\n@UserEventLinker.once(\"PasswordResetEvent\")\nasync def handle_users_password_reset_event(email: str):\n    print(\"User's PasswordResetEvent received!\")\n\n\n@EventLinker.once(\"PasswordResetEvent\")\nasync def handle_any_password_reset_event(email: str):\n    print(\"Any PasswordResetEvent received!\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter(event_linker=UserEventLinker)\nevent_emitter.emit(\"PasswordResetEvent\", \"example@email.com\")\n

\u2003\u2003In the example above, we have assigned a custom event linker to the specific class of the EventEmitter. When we emit the PasswordResetEvent, we can see that only the handle_users_password_reset_event(), which was registered within the UserEventLinker namespace, gets triggered and removed. The handle_any_password_reset_event() callback, registered in a different EventLinker context, does not get triggered.

"},{"location":"tutorials/emitters/#debug-mode","title":"Debug Mode","text":"

\u2003\u2003Pyventus' EventEmitter offers a useful debug mode feature to help you understand the flow of events and troubleshoot your event-driven application. You can enable debug mode in the EventEmitter using the following options:

"},{"location":"tutorials/emitters/#global-debug-mode","title":"Global Debug Mode","text":"

\u2003\u2003By default, Pyventus makes use of Python's global debug tracing feature. To activate the global debug mode, simply run your code in an IDE's debugger mode. This allows you to observe the execution of events and trace their paths.

"},{"location":"tutorials/emitters/#instance-debug-flag","title":"Instance Debug Flag","text":"

\u2003\u2003Alternatively, if you want to enable or disable debug mode for a specific EventEmitter instance, you can use the debug flag provided by the concrete implementation. Setting the debug flag to True enables debug mode for that instance, while setting it to False disables debug mode. Here's an example:

Debug flag OnDebug flag Off
# Enable debug mode for a specific EventEmitter instance\nevent_emitter: EventEmitter = AsyncIOEventEmitter(debug=True)\nevent_emitter.emit(\"Hello\", \"Pyventus\")\n
# Disable debug mode for a specific EventEmitter instance\nevent_emitter: EventEmitter = AsyncIOEventEmitter(debug=False)\nevent_emitter.emit(\"Hello\", \"Pyventus\")\n
"},{"location":"tutorials/emitters/#best-practices","title":"Best Practices","text":"

\u2003\u2003To fully leverage the power of the EventEmitter, it is recommended to use the base EventEmitter as a dependency instead of any concrete implementation. This allows you to easily switch between different event emitter implementations at runtime, providing flexibility and adaptability to your code.

"},{"location":"tutorials/emitters/#recap","title":"Recap","text":"

\u2003\u2003In this tutorial, we learned about the EventEmitter component and its role in dispatching events and triggering associated callbacks. We explored the base EventEmitter class, its unified async/sync API, and the process of creating custom event emitters. We also covered the usage of custom event linkers, best practices for using the EventEmitter, and the debug mode options provided by Pyventus.

"},{"location":"tutorials/emitters/asyncio/","title":"AsyncIO Event Emitter","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003Now that we've covered the base EventEmitter interface, let's examine one of its official implementations: the AsyncIOEventEmitter.

"},{"location":"tutorials/emitters/asyncio/#what-is-it","title":"What is it?","text":"

\u2003\u2003The AsyncIOEventEmitter is a class that inherits from EventEmitter and uses the AsyncIO framework to handle the execution of event emissions.

"},{"location":"tutorials/emitters/asyncio/#how-it-works","title":"How it works","text":"

\u2003\u2003The AsyncIOEventEmitter handles the event emission differently depending on whether it is operating in a synchronous or asynchronous execution context. In synchronous contexts, it will automatically start an event loop to run handlers concurrently. In asynchronous contexts, it leverages any existing event loop. Let's explore the AsyncIOEventEmitter's behavior in more detail:

"},{"location":"tutorials/emitters/asyncio/#sync-context","title":"Sync context","text":"

\u2003\u2003When running without an existing AsyncIO event loop, the AsyncIOEventEmitter automatically starts a new loop using asyncio.run(). Within this loop, it executes the event emission. The loop then waits for all scheduled tasks to finish before closing. This preserves synchronous execution while still gaining the benefits of the concurrent execution.

"},{"location":"tutorials/emitters/asyncio/#async-context","title":"Async context","text":"

\u2003\u2003In an asynchronous context where an event loop is already running, the event emission is scheduled and processed on that existing loop.

AsyncIO Event Loop Behavior

If the event loop is closed before all callbacks complete, any remaining scheduled tasks will be canceled.

"},{"location":"tutorials/emitters/asyncio/#usage","title":"Usage","text":"

\u2003\u2003Using the AsyncIOEventEmitter is straightforward. To get started, simply create a new instance of the class and call its emit() methods, as shown below:

Sync contextAsync context Usage of the AsyncIOEventEmitter
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"Sync event callback!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"Async event callback!\")\n\n\ndef main():\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(\"StringEvent\")\n\n\nmain()\n
Usage of the AsyncIOEventEmitter
import asyncio\nfrom pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"Sync event callback!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"Async event callback!\")\n\n\nasync def main():\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(\"StringEvent\")\n    await asyncio.sleep(0.5) # (1)!\n\nasyncio.run(main())\n
  1. By awaiting the asyncio.sleep(0.5), we ensure the existing event loop continues running long enough for all scheduled tasks to finish processing before concluding. Without waiting, closing the loop prematurely could cause unfinished tasks to be canceled.
"},{"location":"tutorials/emitters/asyncio/#recap","title":"Recap","text":"

We've explored the AsyncIOEventEmitter class in depth:

  • The AsyncIOEventEmitter inherits from the EventEmitter class
  • To use it, instantiate the class and call methods like emit()

\u2003\u2003By understanding these concepts, you can effectively utilize the AsyncIOEventEmitter to emit events in both synchronous and asynchronous contexts, benefiting from the concurrency features provided by the AsyncIO framework.

"},{"location":"tutorials/emitters/celery/","title":"Celery Event Emitter","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003The CeleryEventEmitter provides a powerful way to build event-driven applications that can handle high volumes of work in a scalable and asynchronous manner.

"},{"location":"tutorials/emitters/celery/#what-is-it","title":"What is it?","text":"

\u2003\u2003The CeleryEventEmitter is a concrete implementation of the EventEmitter that leverages the Celery distributed task queue system for event handling. It provides the capability to enqueue and process event emissions in a scalable and asynchronous manner using Celery. This makes the CeleryEventEmitter particularly useful for handling resource-intensive tasks.

"},{"location":"tutorials/emitters/celery/#how-it-works","title":"How it works","text":"

\u2003\u2003The CeleryEventEmitter seamlessly handles the emission and processing of events by utilizing the Celery package. Here's a breakdown of how it functions:

  1. Event emission: When an event is triggered, an object is created and submitted as a task to the Celery queue.
  2. Task queue: The Celery broker stores the task in its queue, where it can be retrieved by workers.
  3. Worker processing: Idle Celery workers pull tasks from the queue and execute the event emissions asynchronously in parallel.
"},{"location":"tutorials/emitters/celery/#usage","title":"Usage","text":"

To start using the CeleryEventEmitter, follow these steps:

  1. Install Celery: Before proceeding, make sure you have installed the Celery optional dependency.
  2. Define event handlers: Let's start with the definition of the event handlers. It is important to note that these functions cannot reside in the main module. Therefore, we need to create another module where all our event handlers can be placed. For this example, let's create a file called event_handlers.py and add the handlers to be processed. event_handlers.py
    import asyncio\nimport time\n\nfrom pyventus.linkers import EventLinker\n\n\n@EventLinker.on(\"StringEvent\")\nasync def slow_async_event_callback():\n    print(\"Starting the async slow process...\")\n    await asyncio.sleep(5)\n    print(\"Finishing the async slow process!\")\n\n\n@EventLinker.on(\"StringEvent\")\ndef slow_sync_event_callback():\n    print(\"Starting the sync slow process...\")\n    time.sleep(5)\n    print(\"Finishing the sync slow process!\")\n
  3. Celery worker: Once you have defined the event handlers, the next step is to configure the Celery workers to process event emissions within a distributed task queue system. To accomplish this, create a file called worker.py and include the following worker configuration. These workers will actively listen to a message broker like RabbitMQ or Redis and process incoming tasks. For more advanced configurations, refer to the official Celery documentation. Serialization Security

    \u2003\u2003It's important to set the content type in the Celery app to application/x-python-serialize. This allows the event emission object to be serialized and deserialized when tasks are processed. The CeleryEventEmitter queue can authenticate and validate any serialized payloads through hashing methods and a secret key. Moreover, a custom serializer can be implemented if the default does not meet the specific needs of your project.

    worker.py
    from celery import Celery\nfrom pyventus.emitters.celery import CeleryEventEmitter\n\n# To ensure Python recognizes the existence of the event handlers, we need to import them.\nfrom event_handlers import slow_sync_event_callback, slow_async_event_callback\n\n# Using Redis as a broker for example purpose. For the Redis support 'pip install celery[redis]'\napp: Celery = Celery(\"worker\", broker=\"redis://default:redispw@localhost:6379\")\n\n# Optional configuration, see the Celery app user guide.\napp.conf.update(result_expires=3600)\n\n# Set the accepted content type to \"application/x-python-serialize\" in the Celery app.\napp.conf.accept_content = [\"application/json\", \"application/x-python-serialize\"]\n\n# Create the celery event emitter queue.\ncelery_event_emitter_queue = CeleryEventEmitter.Queue(celery=app, secret=\"secret-key\")\n\nif __name__ == \"__main__\":\n    app.start()\n
  4. Launching Celery Workers: With the previous configuration and setup complete, we can now launch the Celery worker processes. There are a few differences depending on your operating system:
    • For Linux/macOS:
      celery -A worker worker -l INFO\n
    • For Windows:
      celery -A worker worker -l INFO --pool=solo\n
    • Specifying a Queue:
      celery -A worker worker -l INFO -Q [queue-name]\n
  5. Emitting events: To emit events, we will create a main.py file where we instantiate the CeleryEventEmitter and trigger our first event. main.py
    from pyventus import EventEmitter\nfrom pyventus.emitters.celery import CeleryEventEmitter\n\nfrom worker import celery_event_emitter_queue\n\nif __name__ == \"__main__\":\n    event_emitter: EventEmitter = CeleryEventEmitter(queue=celery_event_emitter_queue)\n    event_emitter.emit(\"StringEvent\")\n
"},{"location":"tutorials/emitters/celery/#recap","title":"Recap","text":"

\u2003\u2003We've explored how the CeleryEventEmitter provides an asynchronous and scalable solution for processing events. Here are the key points:

  • Events are emitted and serialized into tasks submitted to the Celery queue.
  • Celery workers then asynchronously process the queued event emissions independently and in parallel.
  • This distributed approach provides scalable event handling under any workload.

\u2003\u2003In summary, the CeleryEventEmitter leverages Celery's distributed task queue architecture to efficiently scale event-driven applications through asynchronous parallel processing of event emissions. This elastic approach allows applications to handle increasing workloads in a scalable manner.

"},{"location":"tutorials/emitters/executor/","title":"Executor Event Emitter","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003The ExecutorEventEmitter leverages Python's concurrent.futures module to asynchronously execute event emissions across threads or processes. This approach helps optimize performance for applications with I/O-intensive or CPU-bound tasks by utilizing all available CPU resources.

"},{"location":"tutorials/emitters/executor/#what-is-it","title":"What is it?","text":"

\u2003\u2003The ExecutorEventEmitter inherits from the base EventEmitter class and uses an Executor interface to asynchronously run event emissions in either threads or processes. This flexibility in execution models allows you to choose the optimal approach based on your specific application requirements.

"},{"location":"tutorials/emitters/executor/#how-it-works","title":"How it Works","text":"

\u2003\u2003This class utilizes the concurrent.futures Executor interface to handle asynchronous execution of event handlers. It can work with either ThreadPoolExecutor for thread-based execution or ProcessPoolExecutor for process-based execution. When an event is emitted, its execution is submitted to the executor to run asynchronously in either threads (ThreadPoolExecutor) or processes (ProcessPoolExecutor).

ProcessPoolExecutor

\u2003\u2003The ProcessPoolExecutor utilizes Python's multiprocessing module to run event emissions in separate processes instead of threads. This sidesteps the Global Interpreter Lock to enable true parallel execution. However, only pickleable objects can be executed and returned.

"},{"location":"tutorials/emitters/executor/#usage","title":"Usage","text":"

\u2003\u2003Using the ExecutorEventEmitter is straightforward. To get started, simply create a new instance of the class, pass the desired executor concrete instance and call its emit() methods.

Executor Management

\u2003\u2003It is important to properly manage the underlying Executor when using this event emitter. Once finished emitting events, call the shutdown() method to signal the executor to free any resources for pending futures or use the with statement, which will shut down the Executor automatically.

"},{"location":"tutorials/emitters/executor/#threadpoolexecutor-example","title":"ThreadPoolExecutor Example","text":"Using the with statementUsing the shutdown() method
import asyncio\nimport time\nfrom concurrent.futures import ThreadPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\n\nif __name__ == \"__main__\":\n    with ExecutorEventEmitter() as event_emitter: #(1)!\n        event_emitter.emit(\"StringEvent\")\n        event_emitter.emit(\"StringEvent\")\n
  1. The ExecutorEventEmitter uses the ThreadPoolExecutor by default, but you can customize it by providing your own instance.
import asyncio\nimport time\nfrom concurrent.futures import ThreadPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\nif __name__ == \"__main__\":\n    event_emitter = ExecutorEventEmitter()\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.shutdown(wait=True)\n
"},{"location":"tutorials/emitters/executor/#processpoolexecutor-example","title":"ProcessPoolExecutor Example","text":"Using the with statementUsing the shutdown() method
import asyncio\nimport time\nfrom concurrent.futures import ProcessPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\n\nif __name__ == \"__main__\":\n    with ExecutorEventEmitter(executor=ProcessPoolExecutor()) as event_emitter:\n        event_emitter.emit(\"StringEvent\")\n        event_emitter.emit(\"StringEvent\")\n
import asyncio\nimport time\nfrom concurrent.futures import ProcessPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\nif __name__ == \"__main__\":\n    event_emitter = ExecutorEventEmitter(executor=ProcessPoolExecutor())\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.shutdown(wait=True)\n
"},{"location":"tutorials/emitters/executor/#recap","title":"Recap","text":"

\u2003\u2003By learning how this event emitter leverages executors for concurrent/parallel execution, you can optimize your applications to take full advantage of multicore systems through balanced workload distribution. Proper use of this approach can significantly improve performance.

"},{"location":"tutorials/emitters/fastapi/","title":"FastAPI Event Emitter","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003The FastAPIEventEmitter provides a powerful way to build reactive FastAPI applications using an event-driven architecture. It leverages FastAPI's asynchronous BackgroundTasks to handle events outside the request-response cycle.

"},{"location":"tutorials/emitters/fastapi/#what-is-it","title":"What is it?","text":"

\u2003\u2003The FastAPIEventEmitter is a concrete implementation of the EventEmitter class that utilizes FastAPI's BackgroundTasks for event handling. It provides a convenient way to incorporate event-driven functionality into FastAPI applications, allowing you to implement tasks such as sending emails in a decoupled and asynchronous manner.

"},{"location":"tutorials/emitters/fastapi/#how-it-works","title":"How it Works","text":"

\u2003\u2003The FastAPIEventEmitter handles the emission and processing of events by utilizing the FastAPI's background tasks queue. When an event is emitted, its execution is scheduled into the FastAPI's background tasks to run asynchronously after the response is sent.

"},{"location":"tutorials/emitters/fastapi/#usage","title":"Usage","text":"

To start using the FastAPIEventEmitter, follow these steps:

  1. Install Dependencies: Ensure FastAPI and Pyventus are installed.

  2. Dependency injection and usage: The FastAPIEventEmitter integrates fully with FastAPI and can be used in routes or elsewhere via dependency injection. As an example, we'll create a simple FastAPI app to simulate a non-blocking email notification. Create a main.py file and add the following code:

    Without optionsWith options main.py
    import time\nfrom typing import Dict\n\nfrom fastapi import FastAPI, Depends\n\nfrom pyventus import EventLinker\nfrom pyventus.emitters.fastapi import FastAPIEventEmitter\n\n\n@EventLinker.on(\"SendEmail\")\ndef event_callback(email: str):\n    print(f\"Sending email to: {email}\")\n    time.sleep(2)\n    print(\"Email sent successfully!\")\n\n\napp = FastAPI()\n\n@app.get(\"/\")\nasync def send_email(\n    event_emitter: FastAPIEventEmitter = Depends(FastAPIEventEmitter),\n) -> Dict[str, str]:\n    event_emitter.emit(\"SendEmail\", \"email@pyventus.com\")\n    return {\"message\": \"Email sent!\"}\n
    main.py
    import time\nfrom typing import Dict\n\nfrom fastapi import FastAPI, Depends\n\nfrom pyventus import EventLinker\nfrom pyventus.emitters.fastapi import FastAPIEventEmitter\n\n\n@EventLinker.on(\"SendEmail\")\ndef event_callback(email: str):\n    print(f\"Sending email to: {email}\")\n    time.sleep(2)\n    print(\"Email sent successfully!\")\n\n\napp = FastAPI()\n\n@app.get(\"/\")\nasync def send_email(\n    event_emitter: FastAPIEventEmitter = Depends(FastAPIEventEmitter.options(debug=False)),\n) -> Dict[str, str]:\n    event_emitter.emit(\"SendEmail\", \"email@pyventus.com\")\n    return {\"message\": \"Email sent!\"}\n
  3. Run the server: Start the app with:

    uvicorn main:app --reload\n

    Open your browser at http://127.0.0.1:8000/. You will see the JSON response as:

    { \"message\": \"Email sent!\" }\n

    You'll also be able to see the outputs of the functions in the console logs as follows:

    INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\nINFO:     Started reloader process [28720]\nINFO:     Started server process [28722]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\nINFO:     127.0.0.1:57926 - \"GET / HTTP/1.1\" 200 OK\n\nSending email to: email@pyventus.com\nEmail sent successfully!\n
"},{"location":"tutorials/emitters/fastapi/#recap","title":"Recap","text":"

\u2003\u2003As we have seen, the FastAPIEventEmitter allows building reactive FastAPI apps using an event-driven architecture. By leveraging background tasks, events can be emitted from routes and processed independently without blocking responses. This delivers asynchronous and non-blocking behavior for tasks like emails, jobs, streams and more. The emitter integrates seamlessly with FastAPI via dependency injection.

"},{"location":"tutorials/emitters/rq/","title":"RQ Event Emitter","text":"

\ud83c\udfd7\ufe0f Work in Progress

This page is a work in progress.

\u2003\u2003In the previous sections, we explored different event emitters, such as AsyncIOEventEmitter and ExecutorEventEmitter. Now, let's dive into the RQEventEmitter, a powerful tool for handling events that involve intensive asynchronous background tasks.

"},{"location":"tutorials/emitters/rq/#what-is-it","title":"What is it?","text":"

\u2003\u2003The RQEventEmitter is a concrete implementation of the EventEmitter that takes advantage of the Redis Queue pub/sub and worker system to execute event emissions.

\u2003\u2003This event emitter is particularly useful when dealing with events that require resource-intensive tasks like model optimization or video processing. By leveraging RQ workers, it enables non-blocking execution and enhances performance.

"},{"location":"tutorials/emitters/rq/#how-it-works","title":"How it Works","text":"

\u2003\u2003The RQEventEmitter seamlessly handles the emission and processing of events by utilizing the RQ package. Here's how it works:

  1. Event emission: When an event is emitted, all associated event handlers are bundled into an EventEmission object, which is then enqueued into the Redis Queue system.
  2. Workers processing: The enqueued event emission object is asynchronously processed by available Python RQ workers, enabling efficient parallel execution.
"},{"location":"tutorials/emitters/rq/#usage","title":"Usage","text":"

To start using the RQEventEmitter, follow these steps:

  1. Install Python RQ: Before proceeding, make sure you have installed the Redis Queue (RQ) optional dependency.
  2. Python RQ worker configuration: The Python RQ workers act as processors for event emission objects. They listen to the Redis Queue pub/sub channel and process tasks when enqueued. To configure the workers, create a file named worker.py and include the worker configuration code. You can refer to the official RQ documentation for more advanced configurations. worker.py
    from multiprocessing import Process\nfrom typing import List\n\nfrom redis import Redis\nfrom rq import Queue, SimpleWorker\nfrom rq.timeouts import TimerDeathPenalty\n\n# Creates a new Redis connection with the given URL\nredis_conn = Redis.from_url(\"redis://default:redispw@localhost:6379\")\ndefault_queue: Queue = Queue(name=\"default\", connection=redis_conn)\n\n\ndef worker_process() -> None:\n    \"\"\"Creates a new Worker instance and starts the work loop.\"\"\"\n\n    class WindowsSimpleWorker(SimpleWorker):\n        \"\"\"\n        A class that inherits from SimpleWorker and is used to\n        create a new worker instance in a Windows based system.\n        \"\"\"\n        death_penalty_class = TimerDeathPenalty\n\n    worker = WindowsSimpleWorker(\n        connection=redis_conn,\n        queues=[default_queue]\n    )\n    worker.work()\n\n\nif __name__ == \"__main__\":\n    # Test connection\n    redis_conn.ping()\n\n    # Set the number of workers. For auto-assignment\n    # use: multiprocessing.cpu_count()\n    num_workers = 1  # Default 1\n\n    # Workers list\n    worker_processes: List[Process] = []\n\n    # Creates and starts new\n    # Processes for each worker\n    for _ in range(num_workers):\n        p = Process(target=worker_process)\n        worker_processes.append(p)\n        p.start()\n\n    # Join every worker process\n    for process in worker_processes:\n        process.join()\n
  3. Define event handlers: After defining the worker file, let's focus on the event handlers. According to the RQ documentation, these functions should not reside in the main module. Therefore, we need to create another module where all our event handlers can be placed. For this example, let's create a file called event_handlers.py and add the handlers to be processed. event_handlers.py
    import asyncio\nimport time\n\nfrom pyventus.linkers import EventLinker\n\n\n@EventLinker.on(\"StringEvent\")\nasync def slow_async_event_callback():\n    print(\"Starting the async slow process...\")\n    await asyncio.sleep(5)\n    print(\"Finishing the async slow process!\")\n\n\n@EventLinker.on(\"StringEvent\")\ndef slow_sync_event_callback():\n    print(\"Starting the sync slow process...\")\n    time.sleep(5)\n    print(\"Finishing the sync slow process!\")\n
  4. Emitting events: Once the previous steps have been completed, the RQ workers can be started by running the worker.py script. Following that, a main.py file should be created to instantiate an RQEventEmitter and configure the Queue where the event emission objects will be enqueued. For full details on the configuration options, please refer to the RQ website and documentation on the enqueue method settings. At this point, we are ready to emit our first event using the RQEventEmitter. main.py
    from redis import Redis\nfrom rq import Queue\n\nfrom pyventus import EventEmitter\nfrom pyventus.emitters.rq import RQEventEmitter\n\n# To ensure Python recognizes the existence of the event handlers, we need to import them.\nfrom event_handlers import slow_sync_event_callback, slow_async_event_callback\n\nredis_conn = Redis.from_url(\"redis://default:redispw@localhost:6379\")\ndefault_queue: Queue = Queue(name=\"default\", connection=redis_conn)\n\nif __name__ == \"__main__\":\n    event_emitter: EventEmitter = RQEventEmitter(queue=default_queue)\n    event_emitter.emit(\"StringEvent\")\n
"},{"location":"tutorials/emitters/rq/#recap","title":"Recap","text":"

\u2003\u2003We've seen how the RQEventEmitter provides an asynchronous approach to event handling using Redis Queues and RQ workers. The main points are:

  • It leverages existing Redis Queue infrastructure for asynchronous task processing.
  • Event emissions are enqueued in Redis, and workers independently process them.
  • This distributed model scales efficiently regardless of workload volume.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Pyventus","text":"

Documentation: https://mdapena.github.io/pyventus

Source Code: https://github.com/mdapena/pyventus

\u2003\u2003Pyventus is a powerful Python package for event-driven programming. It offers a comprehensive suite of tools to easily define, emit, and orchestrate events. With Pyventus, you can build scalable, extensible, and loosely-coupled event-driven applications.

"},{"location":"#key-features","title":"Key Features","text":"

Pyventus offers several key features, such as:

  • Sync and Async Support \u2500 Whether your code is synchronous or asynchronous, Pyventus allows you to define event handlers as either sync or async callbacks and emit events from both scopes.
  • Customization \u2500 Whether you choose official emitters or custom ones, Pyventus allows you to customize the behavior and capabilities of the event emitters to perfectly align with your unique requirements.
  • An Intuitive API \u2500 Pyventus provides a user-friendly API for defining events, emitters, and handlers. Its design simplifies the process of working with events, enabling you to organize your code around discrete events and their associated actions.
  • Runtime Flexibility \u2500 Pyventus' runtime flexibility allows you to switch seamlessly between different built-in or custom event emitter implementations on the fly, providing a dynamic and adaptable environment for event-driven programming.
  • Reliable Event Handling \u2500 Pyventus allows you to define handlers to customize how events are processed upon completion. Attach success and failure logic to take targeted actions based on the outcome of each event execution.
  • Scalability and Maintainability \u2500 By adopting an event-driven approach with Pyventus, you can create scalable and maintainable code thanks to the loose coupling between its components that enables extensibility and modularity.
  • Comprehensive Documentation \u2500 Pyventus provides a comprehensive documentation suite that includes API references, usage examples, and tutorials to effectively leverage all the features and capabilities of the package.
"},{"location":"#quick-start","title":"Quick Start","text":"

\u2003\u2003Pyventus is published as a Python package and can be installed using pip, ideally in a virtual environment for proper dependency isolation. To get started, open up a terminal and install Pyventus with the following command:

pip install pyventus\n

\u2003\u2003Pyventus by default relies on the Python standard library and requires Python 3.10 or higher with no additional dependencies. However, this package also includes alternative integrations to access additional features such as Redis Queue, Celery, and FastAPI. For more information on this matter, please refer to the Optional Dependencies section.

"},{"location":"#a-simple-example","title":"A Simple Example","text":"

\u2003\u2003Experience the power of Pyventus through a simple Hello, World! example that illustrates the core concepts and basic usage of the package. By following this example, you\u2019ll learn how to subscribe to events and emit them within your application.

Hello, World! Example
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\ndef handle_greet_event():\n    print(\"Hello, World!\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\")\n
You can also work with async functions and contexts... Hello, World! Example (Async version)
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\nasync def handle_greet_event():\n    print(\"Hello, World!\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\")\n

\u2003\u2003As we can see from the Hello, World! example, Pyventus follows a simple and intuitive workflow for defining and emitting events. Let's recap the essential steps involved:

  1. Importing Necessary Modules: We first imported the required modules from Pyventus, which encompassed the EventLinker class, the EventEmitter interface, and the AsyncIOEventEmitter implementation.
  2. Linking Events to Callbacks: Next, we used the @EventLinker.on() decorator to define and link the string event GreetEvent to the function handle_greet_event(), which will print 'Hello, World!' to the console whenever the GreetEvent is emitted.
  3. Instantiating an Event Emitter: After that, and in order to trigger our event, we needed to create an instance of the event emitter class. While AsyncIOEventEmitter was utilized, any built-in or custom implementation could be employed.
  4. Triggering the Event: Finally, by using the emit() method of the event emitter instance, we were able to trigger the GreetEvent, which resulted in the execution of the handle_greet_event() callback.

\u2003\u2003Having gained a clear understanding of the workflow showcased in the Hello, World! example, you are now well-equipped to explore more intricate event-driven scenarios and fully harness the capabilities of Pyventus in your own projects. For a deep dive into the package's functionalities, you can refer to the Pyventus Tutorials or API.

"},{"location":"#a-practical-example","title":"A Practical Example","text":"

\u2003\u2003To showcase Pyventus' event-driven capabilities in a real-world scenario, we will explore a practical example of implementing a voltage sensor using an event-driven architecture (crucial for such scenarios). The purpose of this example is to create an efficient voltage sensor that can seamlessly handle real-time data and respond appropriately to specific voltage conditions.

Example \u2500 Monitoring Voltage Levels Across Devices (Context)

\u2003\u2003A common aspect found in many systems is the need to monitor and respond to changes in sensor data. Whether it's pressure sensors, temperature sensors, or other types, capturing and reacting to sensor data is crucial for effective system operation. In our practical example, we will focus on a specific scenario: building a sensor system that monitors voltage levels across devices. The goal of our voltage sensor is to detect potential issues, such as low or high voltage conditions, and respond appropriately in real-time.

\u2003\u2003To accomplish our goal, we will define a VoltageSensor class to read voltage levels and emit events based on predefined thresholds. We will create event handlers to respond to these events, performing actions such as activating eco-mode for low voltage or implementing high-voltage protection. Additionally, a shared event handler will provide general notifications for out-of-range voltage situations. The code example below illustrates the implementation of this system.

Voltage Sensor System with Pyventus (Practical Example)
import asyncio\nimport random\n\nfrom pyventus import EventEmitter, EventLinker, AsyncIOEventEmitter\n\n\nclass VoltageSensor:\n\n    def __init__(self, name: str, low: float, high: float, event_emitter: EventEmitter) -> None:\n        # Initialize the VoltageSensor object with the provided parameters\n        self._name: str = name\n        self._low: float = low\n        self._high: float = high\n        self._event_emitter: EventEmitter = event_emitter\n\n    async def __call__(self) -> None:\n        # Start voltage readings for the sensor\n        print(f\"Starting voltage readings for: {self._name}\")\n        print(f\"Low: {self._low:.3g}v | High: {self._high:.3g}v\\n-----------\\n\")\n\n        while True:\n            # Simulate sensor readings\n            voltage: float = random.uniform(0, 5)\n            print(\"\\tSensor Reading:\", \"\\033[32m\", f\"{voltage:.3g}v\", \"\\033[0m\")\n\n            # Emit events based on voltage readings\n            if voltage < self._low:\n                self._event_emitter.emit(\"LowVoltageEvent\", sensor=self._name, voltage=voltage)\n            elif voltage > self._high:\n                self._event_emitter.emit(\"HighVoltageEvent\", sensor=self._name, voltage=voltage)\n\n            await asyncio.sleep(1)\n\n\n@EventLinker.on(\"LowVoltageEvent\")\ndef handle_low_voltage_event(sensor: str, voltage: float):\n    print(f\"\ud83e\udeab Turning on eco-mode for '{sensor}'. ({voltage:.3g}v)\\n\")\n    # Perform action for low voltage...\n\n\n@EventLinker.on(\"HighVoltageEvent\")\nasync def handle_high_voltage_event(sensor: str, voltage: float):\n    print(f\"\u26a1 Starting high-voltage protection for '{sensor}'. ({voltage:.3g}v)\\n\")\n    # Perform action for high voltage...\n\n\n@EventLinker.on(\"LowVoltageEvent\", \"HighVoltageEvent\")\ndef handle_voltage_event(sensor: str, voltage: float):\n    print(f\"\\033[31m\\nSensor '{sensor}' out of range.\\033[0m (Voltage: {voltage:.3g})\")\n    # Perform notification for out of range voltage...\n\n\nasync def main():\n    # Initialize the sensor and run the sensor readings\n    sensor = VoltageSensor(name=\"PressureSensor\", low=0.5, high=3.9, event_emitter=AsyncIOEventEmitter())\n    await asyncio.gather(sensor(), )  # Add new sensors inside the 'gather' for multi-device monitoring\n\n\nasyncio.run(main())\n

\u2003\u2003As we can see from this practical example, Pyventus enables us to easily build an event-driven system for voltage sensors that is flexible, efficient, and highly responsive. With its intuitive API and support for both synchronous and asynchronous operations, we were able to effectively monitor voltage levels, detect anomalies, and trigger appropriate actions in real-time.

"},{"location":"#support-for-synchronous-and-asynchronous-code","title":"Support for Synchronous and Asynchronous Code","text":"

\u2003\u2003Pyventus is designed from the ground up to seamlessly support both synchronous and asynchronous programming models. Its unified sync/async API allows you to define event callbacks and emit events across sync and async contexts.

"},{"location":"#subscribing-event-handlers-with-sync-and-async-callbacks","title":"Subscribing Event Handlers with Sync and Async Callbacks","text":"
@EventLinker.on(\"MyEvent\")\ndef sync_event_callback():\n    pass  # Synchronous event handling\n\n\n@EventLinker.on(\"MyEvent\")\nasync def async_event_callback():\n    pass  # Asynchronous event handling\n
You can optimize the execution of your callbacks based on their workload...

\u2003\u2003By default, event handlers in Pyventus are executed concurrently during an event emission, running their sync and async callbacks as defined. However, if you have a sync callback that involves I/O or non-CPU bound operations, you can enable the force_async parameter to offload it to a thread pool, ensuring optimal performance and responsiveness. The force_async parameter utilizes the asyncio.to_thread() function to execute sync callbacks asynchronously.

@EventLinker.on(\"BlockingIO\", force_async=True)\ndef blocking_io():\n    print(f\"start blocking_io at {time.strftime('%X')}\")\n    # Note that time.sleep() can be replaced with any blocking\n    # IO-bound operation, such as file operations.\n    time.sleep(1)\n    print(f\"blocking_io complete at {time.strftime('%X')}\")\n
"},{"location":"#emitting-events-from-sync-and-async-contexts","title":"Emitting Events from Sync and Async Contexts","text":"
# Emitting an event within a sync function\ndef sync_function(event_emitter: EventEmitter):\n    event_emitter.emit(\"MyEvent\")\n\n\n# Emitting an event within an async function\nasync def async_function(event_emitter: EventEmitter):\n    event_emitter.emit(\"MyEvent\")\n
Event propagation within different contexts...

\u2003\u2003While Pyventus provides a base EventEmitter class with a unified sync/async API, the specific propagation behavior when emitting events may vary depending on the concrete EventEmitter used. For example, the AsyncIOEventEmitter implementation leverages the AsyncIO event loop to schedule callbacks added from asynchronous contexts without blocking. But alternative emitters could structure propagation differently to suit their needs.

"},{"location":"#runtime-flexibility-and-customization","title":"Runtime Flexibility and Customization","text":"

\u2003\u2003At its core, Pyventus utilizes a modular event emitter design that allows you to switch seamlessly between different built-in or custom event emitter implementations on the fly. Whether you opt for official emitters or decide to create your custom ones, Pyventus allows you to tailor the behavior and capabilities of the event emitters to perfectly align with your unique requirements.

"},{"location":"#swapping-event-emitter-implementations-at-runtime","title":"Swapping Event Emitter Implementations at Runtime","text":"

\u2003\u2003By leveraging the principle of dependency inversion and using the base EventEmitter as a dependency, you can change the concrete implementation on the fly. Let's demonstrate this using the AsyncIO Event Emitter and the Executor Event Emitter:

Event Emitter Runtime Flexibility Example
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter, ExecutorEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\ndef handle_greet_event(name: str = \"World\"):\n    print(f\"Hello, {name}!\")\n\n\nif __name__ == \"__main__\":\n    def main(event_emitter: EventEmitter) -> None:\n        event_emitter.emit(\"GreetEvent\", name=type(event_emitter).__name__)\n\n\n    main(event_emitter=AsyncIOEventEmitter())\n    with ExecutorEventEmitter() as executor_event_emitter:\n        main(event_emitter=executor_event_emitter)\n
"},{"location":"#defining-custom-event-emitters","title":"Defining Custom Event Emitters","text":"

\u2003\u2003To illustrate Pyventus' customization capabilities, we will define and implement a custom event emitter class for the FastAPI framework. This class will efficiently handle the execution of event emissions through its background tasks workflow.

Custom Event Emitter Example
from fastapi import BackgroundTasks\n\nfrom pyventus import EventEmitter, EventLinker\n\n\nclass FastAPIEventEmitter(EventEmitter):\n    \"\"\"A custom event emitter that uses the FastAPI background tasks.\"\"\"\n\n    def __init__(self, background_tasks: BackgroundTasks):\n        super().__init__(event_linker=EventLinker, debug=False)\n        self._background_tasks = background_tasks\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        self._background_tasks.add_task(event_emission)  # Process the event emission as a background task\n
Official FastAPIEventEmitter Integration.

In case you're interested in integrating Pyventus with FastAPI, you can refer to the official Pyventus FastAPI Event Emitter implementation.

"},{"location":"#event-objects-and-global-events","title":"Event Objects and Global Events","text":"

\u2003\u2003In addition to string events, Pyventus also supports Event Objects, which provide a structured way to define events and encapsulate relevant data payloads.

Event Object Example
@dataclass  # Define a Python dataclass representing the event and its payload.\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n\n\n@EventLinker.on(OrderCreatedEvent)  # Subscribe event handlers to the event.\ndef handle_order_created_event(event: OrderCreatedEvent):\n    # Pyventus will automatically pass the Event Object \n    # as the first positional argument.\n    print(f\"Event Object: {event}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\n    event=OrderCreatedEvent(  # Emit an instance of the event!\n        order_id=6452879,\n        payload={},\n    ),\n)\n

\u2003\u2003Furthermore, Pyventus provides support for Global Events, which are particularly useful for implementing cross-cutting concerns such as logging, monitoring, or analytics. By subscribing event handlers to ... or Ellipsis, you can capture all events that may occur within that EventLinker context.

Global Event Example
@EventLinker.on(...)\ndef handle_any_event(*args, **kwargs):\n    print(f\"Perform logging...\\nArgs: {args}\\tKwargs: {kwargs}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\", name=\"Pyventus\")\n
"},{"location":"#success-and-error-handling","title":"Success and Error Handling","text":"

\u2003\u2003With Pyventus, you can customize how events are handled upon completion, whether they succeed or encounter errors. This customization is achieved by using either the EventLinker's on() or once() decorator within a with statement block. Inside this block, you can define not only the event callbacks but also the overall workflow of the event. Now, let\u2019s explore this simple yet powerful Pythonic syntax of Pyventus through an example.

Success and Error Handling Example
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n# Create an event linker for the \"DivisionEvent\"\nwith EventLinker.on(\"DivisionEvent\") as linker:\n    @linker.on_event\n    def divide(a: float, b: float) -> float:\n        return a / b\n\n    @linker.on_success\n    def handle_success(result: float) -> None:\n        print(f\"Division result: {result:.3g}\")\n\n    @linker.on_failure\n    def handle_failure(e: Exception) -> None:\n        print(f\"Oops, something went wrong: {e}\")\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()  # Create an event emitter\nevent_emitter.emit(\"DivisionEvent\", a=1, b=0)  # Example: Division by zero\nevent_emitter.emit(\"DivisionEvent\", a=1, b=2)  # Example: Valid division\n

\u2003\u2003As we have seen from the example, Pyventus offers a reliable and Pythonic solution for customizing event handling. By utilizing the EventLinker and its decorators within a with statement block, we were able to define the DivisionEvent and specify the callbacks for division, success, and failure cases.

"},{"location":"#continuous-evolution","title":"Continuous Evolution","text":"

\u2003\u2003Pyventus continuously adapts to support developers across technological and programming domains. Its aim is to remain at the forefront of event-driven design. Future development may introduce new official event emitters, expanding compatibility with different technologies through seamless integration.

\u2003\u2003Current default emitters provide reliable out-of-the-box capabilities for common use cases. They efficiently handle core event operations and lay the foundation for building event-driven applications.

Driving Innovation Through Collaboration

\u2003\u2003Pyventus is an open source project that welcomes community involvement. If you wish to contribute additional event emitters, improvements, or bug fixes, please check the Contributing section for guidelines on collaborating. Together, we can further the possibilities of event-driven development.

"},{"location":"#license","title":"License","text":"

\u2003\u2003Pyventus is distributed as open source software and is released under the MIT License. You can view the full text of the license in the LICENSE file located in the Pyventus repository.

"},{"location":"contributing/","title":"Contributing","text":""},{"location":"contributing/#contribution-guidelines","title":"Contribution Guidelines","text":"

Thank you for being interested in contributing to Pyventus! Your involvement is greatly appreciated \u2764\ufe0f

"},{"location":"contributing/#getting-started","title":"Getting Started","text":"

\u2003\u2003Before creating an issue or pull request, please make sure to check if a similar discussion already exists. We encourage you to actively participate by engaging in existing issues.

"},{"location":"contributing/#reporting-issues","title":"Reporting Issues","text":"

\u2003\u2003If you have any questions, bug reports, or feature requests, please open a new issue or discussion. When reporting issues, be sure to provide clear steps to reproduce the problem. For security vulnerabilities, please refer to our security policy.

"},{"location":"contributing/#submitting-changes","title":"Submitting Changes","text":"

\u2003\u2003We greatly appreciate your contributions and want to ensure they align with the project's goals and quality standards. Unless your proposed change is trivial, such as fixing a typo or tweaking documentation, we recommend creating an issue or discussion to talk about the proposed change before submitting a pull request. This allows us to provide feedback, clarify requirements, and ensure your efforts are focused in the right direction. To make a contribution, please follow these steps:

  1. Fork the repository and create a new branch.
  2. Implement your changes in the branch.
  3. Ensure that formatting, linting, and tests pass.
  4. Whenever possible, include tests to cover the lines of code you added or modified.
  5. Commit your changes and submit a pull request with a clear, detailed message.

\u2003\u2003We'll review your pull request to ensure it meets our quality standards before merging it into the main codebase. Please feel free to ask any questions along the way!

"},{"location":"contributing/#development-setup","title":"Development Setup","text":"

\u2003\u2003We recommend developing in a virtual environment to isolate project dependencies. To set up your development environment, follow these steps:

  1. Create a virtual environment:

    python -m venv venv\n
  2. Activate the virtual environment:

    Linux, macOSWindows PowerShellWindows Bash
    source ./venv/bin/activate\n
    .\\venv\\Scripts\\Activate.ps1\n
    source ./venv/Scripts/activate\n
  3. Install development dependencies:

    pip install -e .[dev]\n
"},{"location":"contributing/#running-the-tests","title":"Running the Tests","text":"

During development, you have two options to run the test suite:

ManualUsing Hatch
pytest -v\n
hatch run tests:test\n

Validating New Event Emitters

When implementing new event emitters, it is crucial to ensure their seamless integration with other event emitters and the entire package. To achieve this, we kindly request that you utilize the provided test suite specifically designed for testing new event emitters.

"},{"location":"contributing/#checking-types","title":"Checking Types","text":"

You can use the mypy tool to check the static typing of your code. Simply run the following command:

ManualUsing Hatch
mypy\n
hatch run tests:typing\n
"},{"location":"contributing/#code-coverage","title":"Code Coverage","text":"

To check the code coverage of your changes, run the following command:

ManualUsing Hatch
coverage run -m pytest -v\n
hatch run tests:cov\n
"},{"location":"contributing/#pyventus-documentation","title":"Pyventus Documentation","text":"

\u2003\u2003The documentation for our project is written in Markdown and built using Material for MkDocs. Additionally, the API documentation is generated from the docstrings using mkdocstrings. To begin working on the documentation in a development environment, simply execute the following command:

ManualUsing Hatch
mkdocs serve --dev-addr localhost:8000\n
hatch run docs:serve\n
"},{"location":"contributing/#project-structure-and-conventions","title":"Project Structure and Conventions","text":"

\u2003\u2003This project follows the src-layout convention for Python packages. This convention improves code organization, facilitates easy testing and usage, and allows developers to install the package in editable mode. By adhering to this convention, we can validate the package thoroughly in a realistic environment, leading to a higher quality and user-friendly product.

"},{"location":"contributing/#code-standards","title":"Code Standards","text":"

We strive for a high-quality and maintainable codebase. To achieve this, we have established the following code standards:

  • PEP-8 Compliance \u2500 Please follow the guidelines outlined in PEP-8 for consistent code formatting. Adhering to these standards ensures readability and maintainability across our codebase.
  • Black Formatter \u2500 We recommend using the Black code formatter to ensure consistent style and formatting. By automatically enforcing a standard style, the Black formatter saves you time and effort in manual formatting.
  • Meaningful Naming \u2500 Use descriptive and meaningful names for variables, functions, and classes. Clear and intuitive naming enhances code comprehension, making it easier for everyone to understand and work with the code.
  • Modularity and Reusability \u2500 Encourage the development of modular and reusable code. Breaking down complex tasks into smaller, self-contained components promotes maintainability, reduces complexity, and allows for scalability and extensibility.
  • Optimization and Efficiency \u2500 Strive for efficient code by considering algorithmic complexity and optimizing where necessary. Writing code that is both correct and performant ensures responsive and scalable applications.
"},{"location":"contributing/#documentation-style","title":"Documentation Style","text":"

\u2003\u2003Clear and comprehensive documentation facilitates collaboration and understanding. When contributing to this project, please ensure that you document the following items using properly formatted docstrings:

  • Modules.
  • Class definitions.
  • Function definitions.
  • Module-level variables.

\u2003\u2003Pyventus uses Sphinx docstrings formatted according to PEP 257 guidelines. For more examples and detailed guidance on using Sphinx-style docstrings, we encourage you to consult the official Sphinx documentation.

"},{"location":"contributing/#pre-submission-testing-and-validation","title":"Pre-Submission Testing and Validation","text":"

\u2003\u2003Before submitting your pull request, it is crucial to ensure that your changes pass all the necessary checks. To do so, simply run the following command:

hatch run tests:all\n

\u2003\u2003The above command will trigger the Hatch project manager to initiate the comprehensive testing process across all supported Python versions. It will run tests, perform typing checks, ensure code formatting, and measure code coverage. This ensures that your changes meet the required quality standards.

Testing for Individual Python Versions

If you want to test for specific Python versions, you can do so by specifying the desired versions in the command, as follows:

Python 3.10Python 3.11Python 3.12Python 3.13
hatch run +py=3.10 tests:all\n
hatch run +py=3.11 tests:all\n
hatch run +py=3.12 tests:all\n
hatch run +py=3.13 tests:all\n

Troubleshooting Hatch Environment Errors

If commands run successfully when executed manually but produce unexpected errors or misbehavior when run within a Hatch environment, even though the dependencies are declared correctly, this could indicate an issue with the Hatch environment cache. To resolve potential cache-related issues, you can remove the environment and clear its cache by running:

hatch env remove [ENV_NAME]\n

Alternatively, you can remove all environments and their cache by running the following command:

hatch env prune\n
"},{"location":"contributing/#code-of-conduct","title":"Code of Conduct","text":"

\u2003\u2003This project and everyone participating in it is governed by the Pyventus Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior.

"},{"location":"contributing/#thanks-in-advance","title":"Thanks in Advance!","text":"

\u2003\u2003Thank you for considering contributing to this project. Your contributions are valuable and greatly appreciated. If you have any questions or need further clarification, please don't hesitate to reach out. We look forward to collaborating with you to enhance this project!

"},{"location":"getting-started/","title":"Getting Started","text":"

\u2003\u2003Welcome to the Getting Started section! This guide will help you install and configure Pyventus in your project. For more detailed information on how to use this package, you can refer to the Pyventus Tutorials or API Reference.

"},{"location":"getting-started/#requirements","title":"Requirements","text":"

\u2003\u2003By default, Pyventus' core functionalities and default event emitter implementations, such as the AsyncIO Event Emitter, and the Executor Event Emitter, only require Python 3.10+ with no additional dependencies. However, these requirements may expand if you opt to use alternative built-in event emitter implementations.

"},{"location":"getting-started/#installation","title":"Installation","text":"

\u2003\u2003Pyventus is published as a Python package and can be installed using pip, ideally in a virtual environment for proper dependency isolation. To get started, open up a terminal and install Pyventus with the following command:

pip install pyventus\n
"},{"location":"getting-started/#optional-dependencies","title":"Optional Dependencies","text":"

\u2003\u2003 While Pyventus primarily relies on the Python standard library, it also supports optional dependencies to access additional features, as shown below:

"},{"location":"getting-started/#supported-library-integrations","title":"Supported Library Integrations","text":"
  • Celery \u2500 Pyventus integrates with Celery using the CeleryEventEmitter, enabling event emissions to be executed on Celery worker nodes to improve task processing. To install Pyventus with Celery support, use the following command:
    pip install pyventus[celery] (1)\n
    1. Optional Package Dependencies \u2003\u2003This package includes some optional dependencies. For more information, please visit the Celery bundles documentation.

      These optional dependencies can be installed as described in their individual documentation. For example:

      pip install celery[...]\n

  • Redis Queue (RQ) \u2500 Pyventus integrates with Redis Queue (RQ) using the RQEventEmitter, allowing event emissions to run as background jobs through RQ's asynchronous workers. To install Pyventus with RQ support, use the following command:
    pip install pyventus[rq]\n
  • FastAPI \u2500 Pyventus integrates with the FastAPI framework using the FastAPIEventEmitter, enabling event-driven architectures to be built directly into FastAPI applications. The emitter leverages FastAPI's background tasks to asynchronously process event emissions without blocking responses. To install Pyventus with FastAPI integration, use the following command:
    pip install pyventus[fastapi] (1)\n
    1. Optional Package Dependencies \u2003\u2003This package includes some optional dependencies. For more information, please visit the FastAPI optional dependencies.

      These optional dependencies can be installed as described in their individual documentation. For example:

      pip install fastapi[...]\n

You can install all of them with:

pip install pyventus[all]\n

"},{"location":"getting-started/#supported-framework-integrations","title":"Supported Framework Integrations","text":""},{"location":"release-notes/","title":"Release Notes","text":""},{"location":"release-notes/#0.5.0","title":"v0.5.0 April 9, 2024","text":""},{"location":"release-notes/#breaking-changes","title":"Breaking Changes","text":"
  • Removed the base Event class due to improved event semantics and unnecessary redundancy.
  • Renamed the get_event_registry() method of EventLinker to get_registry().
  • Renamed the __event_registry inner property of EventLinker to __registry.
  • Renamed the get_events_by_handler() method of EventLinker to get_events_by_event_handler().
  • Renamed the get_handlers_by_events() method of EventLinker to get_event_handlers_by_events().
  • Renamed the protected method _executor_callback() of the ExecutorEventEmitter to _callback().
  • Renamed the task name of CeleryEventEmitter from _executor to pyventus_executor to avoid collisions with other task names.
"},{"location":"release-notes/#added","title":"Added","text":"
  • Added __slots__ to EventLinkageWrapper class for more efficient memory usage.
  • Extended support for subscription and emission of any dataclass object, removing the limitation of only Event subclasses.
  • Added the force_async parameter to the EventHandler class and EventLinker subscription methods to be able to optimize the execution of sync callbacks based on their workload.
  • Introduced a new event semantic where the Python ... (Ellipsis) is now used to refer to all events on a subscription, like the onAny() method but with a Pythonic syntax.
  • Added the mkdocs-material social cards plugin, which provides a preview of the documentation content when shared on social media platforms.
"},{"location":"release-notes/#changed","title":"Changed","text":"
  • Standardized the order of static methods, class methods, and instance methods for improved readability.
  • Applied Python best practices to optimize the methods within the EventLinker and EventEmitter classes.
  • Improved validation of variable instances in the event emitters, EventLinker, and EventHandler.
  • Updated and improved the test suite to ensure accurate validation and consistency.
  • Enabled creation date for the mkdocs git-revision-date-localized plugin.
  • Replaced the mkdocs git-authors plugin with the git-committers plugin.
  • Updated and improved the package description.
  • Updated the tutorial section to incorporate recent changes.
  • Enhanced the documentation index page and README file with new examples and better descriptions to showcase the unique features of Pyventus.
"},{"location":"release-notes/#removed","title":"Removed","text":"
  • Removed the default value of the once flag in the EventHandler class.
"},{"location":"release-notes/#fixed","title":"Fixed","text":"
  • Fixed and standardized all package docstrings and code comments for consistency and clarity.
  • Addressed minor errors and details in the documentation.
"},{"location":"release-notes/#0.4.1","title":"v0.4.1 January 30, 2024","text":""},{"location":"release-notes/#changed_1","title":"Changed","text":"
  • Optimized the size of the source distribution (sdist) build by including only essential files and directories, such as the /src and /tests directories, as well as the following files: .gitignore, pyproject.toml, CITATION.cff, README, and LICENSE.
  • Refactored documentation dependencies into an optional dependency called docs.
  • Updated the deploy-docs.yml GitHub workflow to leverage the new optional dependency docs.
  • Updated the EventEmission class with the @final decorator from the typing module, indicating that it is meant for internal use only and should not be subclassed.
"},{"location":"release-notes/#fixed_1","title":"Fixed","text":"
  • Addressed minor errors and details in the documentation.
"},{"location":"release-notes/#0.4.0","title":"v0.4.0 January 6, 2024","text":""},{"location":"release-notes/#added_1","title":"Added","text":"
  • Added FastAPIEventEmitter implementation to facilitate seamless integration with the FastAPI framework.
  • Added tests for FastAPIEventEmitter to validate its behavior and ensure proper operation.
  • Added documentation for FastAPIEventEmitter, including tutorials and API references.
  • Integrated the Coveralls.io workflow to generate coverage badge and reports.
  • Included coverage badges on the main documentation page and the readme file.
  • Introduced permalinks within the documentation for easy navigation.
"},{"location":"release-notes/#changed_2","title":"Changed","text":"
  • Updated pyproject.toml with the new optional dependency for FastAPI integration.
"},{"location":"release-notes/#fixed_2","title":"Fixed","text":"
  • Addressed minor errors in the Pyventus documentation to improve accuracy and clarity.
"},{"location":"release-notes/#0.3.0","title":"v0.3.0 December 29, 2023","text":""},{"location":"release-notes/#breaking-changes_1","title":"Breaking Changes","text":"
  • Introduced EventEmission object to encapsulate the processing of event emissions. This changes the _execute() method of EventEmitter but provides a cleaner, more scalable, and efficient approach.
  • Renamed all debug flags from debug_mode to debug for enhanced clarity and consistency.
  • Renamed EventEmitter's _execute() method to _process() to better reflect its purpose of processing event emissions.
"},{"location":"release-notes/#added_2","title":"Added","text":"
  • Added CeleryEventEmitter implementation to leverage the Celery distributed task queue for event handling.
  • Added tests for CeleryEventEmitter to validate its behavior and ensure proper operation.
  • Added documentation for CeleryEventEmitter, including tutorials and API references.
"},{"location":"release-notes/#changed_3","title":"Changed","text":"
  • Restructured the documentation for event emitters tutorials and API references to improve organization and clarity.
  • Updated the contributing.md page to include the Troubleshooting Hatch Environment Errors section.
  • Updated the EventEmitter API documentation to include the EventEmission class reference.
  • Updated pyproject.toml with the new optional dependency for Celery integration.
  • Updated mypy ignore flags to properly silence specific false positive error codes.
"},{"location":"release-notes/#fixed_3","title":"Fixed","text":"
  • Addressed minor errors in the Pyventus documentation.
"},{"location":"release-notes/#0.2.1","title":"v0.2.1 December 17, 2023","text":""},{"location":"release-notes/#changed_4","title":"Changed","text":"
  • Updated docstring links throughout the package to refer to the official documentation.
  • Updated the RQEventEmitter API Reference and Tutorials docs to reflect the new optional import.
"},{"location":"release-notes/#fixed_4","title":"Fixed","text":"
  • Resolved the issue where the RQEventEmitter class was automatically imported in the main package, requiring the installation of its optional dependency to use any of the package's core functionalities. It is now fully optional.
  • Fixed issues with invalid links in the documentation.
"},{"location":"release-notes/#0.2.0","title":"v0.2.0 December 16, 2023","text":""},{"location":"release-notes/#added_3","title":"Added","text":"
  • Introduced the publish to PyPI workflow, automating the uploading of package builds when new releases are created.
  • Added the mkdocs-git-authors plugin to display git authors of a markdown page in the documentation.
  • Added badges to the main page of the documentation as well as the readme file.
  • Added a code of conduct for the project, using the Contributor Covenant v2.1.
  • Included a CITATION.cff file to facilitate academic citations.
"},{"location":"release-notes/#changed_5","title":"Changed","text":"
  • Renamed the tests.yml workflow to run-tests.yml.
  • Updated the deploy-docs.yml workflow with the mkdocs-git-authors plugin dependency.
  • Modified the mkdocs.yml config file by adding the site_url and site_author properties.
  • Updated the pyproject.toml file with the mkdocs-git-authors plugin dependency and python package keywords.
"},{"location":"release-notes/#fixed_5","title":"Fixed","text":"
  • Fixed the python version in the deploy-docs.yml workflow.
  • Resolved issues with relative links in the documentation.
"},{"location":"release-notes/#0.1.0","title":"v0.1.0 December 15, 2023","text":""},{"location":"release-notes/#initial-implementation","title":"Initial Implementation","text":"

\u2003\u2003This release introduces Pyventus v0.1.0, a modern and robust Python package for event-driven programming. Pyventus provides developers with a comprehensive suite of tools and utilities to define, emit, and orchestrate events. It empowers developers to build scalable, extensible, and loosely-coupled event-driven applications.

  • Implementation Details: The first implementation includes all the core functionalities of the package, encompassing events, event linkers, event emitters, event handlers, and more.
  • Testing and Coverage: This release includes a test suite that verifies the correctness of the package implementation. It also integrates code coverage, achieving 100% test coverage. The tests are configured to run automatically via GitHub Actions on both push and pull requests to the master branch.
  • Formatter and Lint Configuration: A formatter and lint configuration have been added to the project. This ensures consistent code style, maintainability, and adherence to the established coding standards defined in the project documentation.
  • Documentation: Additionally, this release includes comprehensive documentation for the package. The documentation covers the main page, a detailed getting started guide, tutorials, API reference, and release notes.

"},{"location":"api/","title":"API Reference","text":"

\u2003\u2003Welcome to the Pyventus API Reference, a comprehensive guide that provides detailed information about the classes, functions, parameters, attributes, and other components available in Pyventus.

\u2003\u2003In the API Reference, you will find detailed documentation for each component, including clear explanations, parameter details, return values, and usage examples. You can navigate through the reference using the search functionality or by browsing the different sections and categories to find the specific information you need.

Let's explore the Pyventus API Reference!

"},{"location":"api/event-handler/","title":"EventHandler class","text":"

A class that encapsulates event callbacks and provides a mechanism for executing them when the event occurs. This class manages both asynchronous and synchronous execution and handles event completion in both success and error scenarios.

Notes:

  • The __call__ method of the EventHandler class is an asynchronous method that returns a Coroutine. It should never be treated as a synchronous function.

  • This class is not intended to be subclassed or manually created. It is used internally to encapsulate the callbacks associated with an event and manage their execution.

  • The event handler can be invoked by calling the instance as a function and passing the required arguments.

Read more in the Pyventus docs for Event Handler.

Source code in pyventus/handlers/event_handler.py
@final\nclass EventHandler:\n    \"\"\"\n    A class that encapsulates event callbacks and provides a mechanism for executing them\n    when the event occurs. This class manages both asynchronous and synchronous execution\n    and handles event completion in both success and error scenarios.\n\n    **Notes:**\n\n    -   The `__call__` method of the `EventHandler` class is an asynchronous method\n        that returns a `Coroutine`. It should never be treated as a synchronous function.\n\n    -   This class is not intended to be subclassed or manually created. It is used\n        internally to encapsulate the callbacks associated with an event and manage\n        their execution.\n\n    -   The event handler can be invoked by calling the instance as a function and\n        passing the required arguments.\n\n    ---\n    Read more in the\n    [Pyventus docs for Event Handler](https://mdapena.github.io/pyventus/tutorials/event-linker/#event-handlers).\n    \"\"\"\n\n    @staticmethod\n    def get_callback_name(\n        callback: EventCallbackType | SuccessCallbackType | FailureCallbackType | None,  # type: ignore[type-arg]\n    ) -> str:\n        \"\"\"\n        Retrieves the name of the provided callback.\n        :param callback: The callback object.\n        :return: The name of the callback as a string.\n        \"\"\"\n        if callback is not None and hasattr(callback, \"__name__\"):\n            return callback.__name__\n        elif callback is not None and hasattr(callback, \"__class__\"):\n            return type(callback).__name__\n        else:\n            return \"None\"\n\n    @staticmethod\n    def validate_callback(\n        callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n    ) -> None:\n        \"\"\"\n        Validates whether the provided callback is a valid callable object.\n        :param callback: The callback to be validated.\n        :return: None\n        :raises PyventusException: If the callback is not a callable object.\n        \"\"\"\n        if not callable(callback):\n            raise PyventusException(\n                f\"'{callback.__name__ if hasattr(callback, '__name__') else callback}' is not a callable object.\"\n            )\n\n    @staticmethod\n    def is_async(\n        callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n    ) -> bool:\n        \"\"\"\n        Checks whether the provided callback is an asynchronous function or method.\n        :param callback: The callback to be checked.\n        :return: `True` if the callback is an asynchronous function or method, `False` otherwise.\n        :raises PyventusException: If the callback is not a callable or a string.\n        \"\"\"\n        if ismethod(callback) or isfunction(callback) or isbuiltin(callback):\n            return iscoroutinefunction(callback)\n        elif not isclass(callback) and hasattr(callback, \"__call__\"):  # A callable class instance\n            return iscoroutinefunction(callback.__call__)\n        else:\n            raise PyventusException(\"Expected a callable or a string, but got: {0}\".format(callback))\n\n    # Event handler attributes\n    __slots__ = (\n        \"_once\",\n        \"_force_async\",\n        \"_event_callback\",\n        \"_success_callback\",\n        \"_failure_callback\",\n        \"_is_event_callback_async\",\n        \"_is_success_callback_async\",\n        \"_is_failure_callback_async\",\n        \"_timestamp\",\n    )\n\n    @property\n    def once(self) -> bool:\n        \"\"\"\n        Determines if the event handler is a one-time subscription.\n        :return: A boolean value indicating if the event handler is\n            a one-time subscription.\n        \"\"\"\n        return self._once\n\n    @property\n    def force_async(self) -> bool:\n        \"\"\"\n        Determines whether all callbacks are forced to run asynchronously.\n        :return: A boolean value indicating if all callbacks are forced to run\n            asynchronously. If `True`, synchronous callbacks will be converted to\n            run asynchronously in a thread pool, using the `asyncio.to_thread`\n            function. If `False`, callbacks will run synchronously or\n            asynchronously as defined.\n        \"\"\"\n        return self._force_async\n\n    @property\n    def timestamp(self) -> datetime:\n        \"\"\"\n        Retrieves the timestamp when the event handler was created.\n        :return: The timestamp when the event handler was created.\n        \"\"\"\n        return self._timestamp\n\n    def __init__(\n        self,\n        once: bool,\n        force_async: bool,\n        event_callback: EventCallbackType,  # type: ignore[type-arg]\n        success_callback: SuccessCallbackType | None = None,\n        failure_callback: FailureCallbackType | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `EventHandler`.\n        :param once: Specifies if the event handler is a one-time subscription.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :param event_callback: The callback to be executed when the event occurs.\n        :param success_callback: The callback to be executed when the event execution\n            completes successfully. Default is `None`.\n        :param failure_callback: The callback to be executed when the event execution\n            fails. Default is `None`.\n        :raises PyventusException: If the provided callbacks are invalid.\n        \"\"\"\n        # Validate callbacks\n        EventHandler.validate_callback(callback=event_callback)\n\n        if success_callback is not None:\n            EventHandler.validate_callback(callback=success_callback)\n\n        if failure_callback is not None:\n            EventHandler.validate_callback(callback=failure_callback)\n\n        # Validate flags\n        if not isinstance(once, bool):\n            raise PyventusException(\"The 'once' argument must be a boolean value.\")\n        if not isinstance(force_async, bool):\n            raise PyventusException(\"The 'force_async' argument must be a boolean value.\")\n\n        # Set the event handler flags\n        self._once: bool = once\n        self._force_async: bool = force_async\n\n        # Set the event handler callbacks\n        self._event_callback: EventCallbackType = event_callback  # type: ignore[type-arg]\n        self._success_callback: SuccessCallbackType | None = success_callback\n        self._failure_callback: FailureCallbackType | None = failure_callback\n\n        # Set the event handler callbacks flags\n        self._is_event_callback_async: bool = EventHandler.is_async(event_callback)\n        self._is_success_callback_async: bool | None = (\n            EventHandler.is_async(success_callback) if success_callback else None\n        )\n        self._is_failure_callback_async: bool | None = (\n            EventHandler.is_async(failure_callback) if failure_callback else None\n        )\n\n        # Set the event handler timestamp\n        self._timestamp: datetime = datetime.now()\n\n    async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> None:\n        \"\"\"\n        Executes the event flow by invoking the associated callbacks.\n        :param args: Positional arguments to be passed to the event callback.\n        :param kwargs: Keyword arguments to be passed to the event callback.\n        :return: Coroutine\n        \"\"\"\n        # Event callback results\n        results: Any | None = None\n\n        try:\n            # Invoke the event callback.\n            if self._is_event_callback_async:\n                results = await self._event_callback(*args, **kwargs)\n            elif self._force_async:\n                results = await to_thread(self._event_callback, *args, **kwargs)\n            else:\n                results = self._event_callback(*args, **kwargs)\n        except Exception as exception:\n            # Log the exception with error level\n            StdOutLogger.error(name=f\"{self.__class__.__name__}\", action=\"Exception:\", msg=f\"{exception}\")\n\n            # Invoke the failure callback and pass the exception.\n            if self._failure_callback:\n                if self._is_failure_callback_async:\n                    await self._failure_callback(exception)\n                elif self._force_async:\n                    await to_thread(self._failure_callback, exception)\n                else:\n                    self._failure_callback(exception)\n        else:\n            # Invoke the success callback and pass the results, if any.\n            if self._success_callback:\n                if self._is_success_callback_async:\n                    if results is None:\n                        await self._success_callback()\n                    else:\n                        await self._success_callback(results)\n                elif self._force_async:\n                    if results is None:\n                        await to_thread(self._success_callback)\n                    else:\n                        await to_thread(self._success_callback, results)\n                else:\n                    if results is None:\n                        self._success_callback()\n                    else:\n                        self._success_callback(results)\n\n    def __str__(self) -> str:\n        \"\"\"\n        Returns a formatted string representation of the event handler.\n        :return: A string representation of the event handler.\n        \"\"\"\n        return \"\".join(\n            [\n                f\"Event Callback: `{EventHandler.get_callback_name(callback=self._event_callback)}\",\n                \"` (Async) | \" if self._is_event_callback_async else \"` (Sync) | \",\n                (\n                    \"Success Callback: `\".join(\n                        [\n                            EventHandler.get_callback_name(callback=self._success_callback),\n                            \"` (Async) | \" if self._is_success_callback_async else \"` (Sync) | \",\n                        ]\n                    )\n                    if self._success_callback\n                    else \"\"\n                ),\n                (\n                    \"Failure Callback: `\".join(\n                        [\n                            EventHandler.get_callback_name(callback=self._failure_callback),\n                            \"` (Async) | \" if self._is_failure_callback_async else \"` (Sync) | \",\n                        ]\n                    )\n                    if self._failure_callback\n                    else \"\"\n                ),\n                f\"Once: {self.once} | \",\n                f\"Force Async: {self.force_async} | \",\n                f\"Timestamp: {self.timestamp.strftime('%Y-%m-%d %I:%M:%S %p')}\",\n            ]\n        )\n

"},{"location":"api/event-handler/#pyventus.EventHandler-attributes","title":"Attributes","text":""},{"location":"api/event-handler/#pyventus.EventHandler.once","title":"once property","text":"
once: bool\n

Determines if the event handler is a one-time subscription.

RETURNS DESCRIPTION bool

A boolean value indicating if the event handler is a one-time subscription.

"},{"location":"api/event-handler/#pyventus.EventHandler.force_async","title":"force_async property","text":"
force_async: bool\n

Determines whether all callbacks are forced to run asynchronously.

RETURNS DESCRIPTION bool

A boolean value indicating if all callbacks are forced to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

"},{"location":"api/event-handler/#pyventus.EventHandler.timestamp","title":"timestamp property","text":"
timestamp: datetime\n

Retrieves the timestamp when the event handler was created.

RETURNS DESCRIPTION datetime

The timestamp when the event handler was created.

"},{"location":"api/event-handler/#pyventus.EventHandler-functions","title":"Functions","text":""},{"location":"api/event-handler/#pyventus.EventHandler.get_callback_name","title":"get_callback_name staticmethod","text":"
get_callback_name(callback: EventCallbackType | SuccessCallbackType | FailureCallbackType | None) -> str\n

Retrieves the name of the provided callback.

PARAMETER DESCRIPTION callback

The callback object.

TYPE: EventCallbackType | SuccessCallbackType | FailureCallbackType | None

RETURNS DESCRIPTION str

The name of the callback as a string.

Source code in pyventus/handlers/event_handler.py
@staticmethod\ndef get_callback_name(\n    callback: EventCallbackType | SuccessCallbackType | FailureCallbackType | None,  # type: ignore[type-arg]\n) -> str:\n    \"\"\"\n    Retrieves the name of the provided callback.\n    :param callback: The callback object.\n    :return: The name of the callback as a string.\n    \"\"\"\n    if callback is not None and hasattr(callback, \"__name__\"):\n        return callback.__name__\n    elif callback is not None and hasattr(callback, \"__class__\"):\n        return type(callback).__name__\n    else:\n        return \"None\"\n
"},{"location":"api/event-handler/#pyventus.EventHandler.validate_callback","title":"validate_callback staticmethod","text":"
validate_callback(callback: EventCallbackType | SuccessCallbackType | FailureCallbackType) -> None\n

Validates whether the provided callback is a valid callable object.

PARAMETER DESCRIPTION callback

The callback to be validated.

TYPE: EventCallbackType | SuccessCallbackType | FailureCallbackType

RETURNS DESCRIPTION None

None

RAISES DESCRIPTION PyventusException

If the callback is not a callable object.

Source code in pyventus/handlers/event_handler.py
@staticmethod\ndef validate_callback(\n    callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n) -> None:\n    \"\"\"\n    Validates whether the provided callback is a valid callable object.\n    :param callback: The callback to be validated.\n    :return: None\n    :raises PyventusException: If the callback is not a callable object.\n    \"\"\"\n    if not callable(callback):\n        raise PyventusException(\n            f\"'{callback.__name__ if hasattr(callback, '__name__') else callback}' is not a callable object.\"\n        )\n
"},{"location":"api/event-handler/#pyventus.EventHandler.is_async","title":"is_async staticmethod","text":"
is_async(callback: EventCallbackType | SuccessCallbackType | FailureCallbackType) -> bool\n

Checks whether the provided callback is an asynchronous function or method.

PARAMETER DESCRIPTION callback

The callback to be checked.

TYPE: EventCallbackType | SuccessCallbackType | FailureCallbackType

RETURNS DESCRIPTION bool

True if the callback is an asynchronous function or method, False otherwise.

RAISES DESCRIPTION PyventusException

If the callback is not a callable or a string.

Source code in pyventus/handlers/event_handler.py
@staticmethod\ndef is_async(\n    callback: EventCallbackType | SuccessCallbackType | FailureCallbackType,  # type: ignore[type-arg]\n) -> bool:\n    \"\"\"\n    Checks whether the provided callback is an asynchronous function or method.\n    :param callback: The callback to be checked.\n    :return: `True` if the callback is an asynchronous function or method, `False` otherwise.\n    :raises PyventusException: If the callback is not a callable or a string.\n    \"\"\"\n    if ismethod(callback) or isfunction(callback) or isbuiltin(callback):\n        return iscoroutinefunction(callback)\n    elif not isclass(callback) and hasattr(callback, \"__call__\"):  # A callable class instance\n        return iscoroutinefunction(callback.__call__)\n    else:\n        raise PyventusException(\"Expected a callable or a string, but got: {0}\".format(callback))\n
"},{"location":"api/event-handler/#pyventus.EventHandler.__init__","title":"__init__","text":"
__init__(once: bool, force_async: bool, event_callback: EventCallbackType, success_callback: SuccessCallbackType | None = None, failure_callback: FailureCallbackType | None = None) -> None\n

Initialize an instance of EventHandler.

PARAMETER DESCRIPTION once

Specifies if the event handler is a one-time subscription.

TYPE: bool

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool

event_callback

The callback to be executed when the event occurs.

TYPE: EventCallbackType

success_callback

The callback to be executed when the event execution completes successfully. Default is None.

TYPE: SuccessCallbackType | None DEFAULT: None

failure_callback

The callback to be executed when the event execution fails. Default is None.

TYPE: FailureCallbackType | None DEFAULT: None

RAISES DESCRIPTION PyventusException

If the provided callbacks are invalid.

Source code in pyventus/handlers/event_handler.py
def __init__(\n    self,\n    once: bool,\n    force_async: bool,\n    event_callback: EventCallbackType,  # type: ignore[type-arg]\n    success_callback: SuccessCallbackType | None = None,\n    failure_callback: FailureCallbackType | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `EventHandler`.\n    :param once: Specifies if the event handler is a one-time subscription.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :param event_callback: The callback to be executed when the event occurs.\n    :param success_callback: The callback to be executed when the event execution\n        completes successfully. Default is `None`.\n    :param failure_callback: The callback to be executed when the event execution\n        fails. Default is `None`.\n    :raises PyventusException: If the provided callbacks are invalid.\n    \"\"\"\n    # Validate callbacks\n    EventHandler.validate_callback(callback=event_callback)\n\n    if success_callback is not None:\n        EventHandler.validate_callback(callback=success_callback)\n\n    if failure_callback is not None:\n        EventHandler.validate_callback(callback=failure_callback)\n\n    # Validate flags\n    if not isinstance(once, bool):\n        raise PyventusException(\"The 'once' argument must be a boolean value.\")\n    if not isinstance(force_async, bool):\n        raise PyventusException(\"The 'force_async' argument must be a boolean value.\")\n\n    # Set the event handler flags\n    self._once: bool = once\n    self._force_async: bool = force_async\n\n    # Set the event handler callbacks\n    self._event_callback: EventCallbackType = event_callback  # type: ignore[type-arg]\n    self._success_callback: SuccessCallbackType | None = success_callback\n    self._failure_callback: FailureCallbackType | None = failure_callback\n\n    # Set the event handler callbacks flags\n    self._is_event_callback_async: bool = EventHandler.is_async(event_callback)\n    self._is_success_callback_async: bool | None = (\n        EventHandler.is_async(success_callback) if success_callback else None\n    )\n    self._is_failure_callback_async: bool | None = (\n        EventHandler.is_async(failure_callback) if failure_callback else None\n    )\n\n    # Set the event handler timestamp\n    self._timestamp: datetime = datetime.now()\n
"},{"location":"api/event-handler/#pyventus.EventHandler.__call__","title":"__call__ async","text":"
__call__(*args: args, **kwargs: kwargs) -> None\n

Executes the event flow by invoking the associated callbacks.

PARAMETER DESCRIPTION args

Positional arguments to be passed to the event callback.

TYPE: args DEFAULT: ()

kwargs

Keyword arguments to be passed to the event callback.

TYPE: kwargs DEFAULT: {}

RETURNS DESCRIPTION None

Coroutine

Source code in pyventus/handlers/event_handler.py
async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> None:\n    \"\"\"\n    Executes the event flow by invoking the associated callbacks.\n    :param args: Positional arguments to be passed to the event callback.\n    :param kwargs: Keyword arguments to be passed to the event callback.\n    :return: Coroutine\n    \"\"\"\n    # Event callback results\n    results: Any | None = None\n\n    try:\n        # Invoke the event callback.\n        if self._is_event_callback_async:\n            results = await self._event_callback(*args, **kwargs)\n        elif self._force_async:\n            results = await to_thread(self._event_callback, *args, **kwargs)\n        else:\n            results = self._event_callback(*args, **kwargs)\n    except Exception as exception:\n        # Log the exception with error level\n        StdOutLogger.error(name=f\"{self.__class__.__name__}\", action=\"Exception:\", msg=f\"{exception}\")\n\n        # Invoke the failure callback and pass the exception.\n        if self._failure_callback:\n            if self._is_failure_callback_async:\n                await self._failure_callback(exception)\n            elif self._force_async:\n                await to_thread(self._failure_callback, exception)\n            else:\n                self._failure_callback(exception)\n    else:\n        # Invoke the success callback and pass the results, if any.\n        if self._success_callback:\n            if self._is_success_callback_async:\n                if results is None:\n                    await self._success_callback()\n                else:\n                    await self._success_callback(results)\n            elif self._force_async:\n                if results is None:\n                    await to_thread(self._success_callback)\n                else:\n                    await to_thread(self._success_callback, results)\n            else:\n                if results is None:\n                    self._success_callback()\n                else:\n                    self._success_callback(results)\n
"},{"location":"api/event-linker/","title":"EventLinker class","text":"

A base class that acts as a global registry for events and callbacks linkage. It provides a centralized mechanism for managing event subscriptions, unsubscriptions, and retrieval of events and their associated event handlers.

Notes:

  • The EventLinker class can be subclassed to create specific namespaces or contexts for managing events and event handlers separately. By subclassing the EventLinker, users can organize event subscriptions and handlers within different scopes, providing modularity and flexibility in event management. Subclassing also allows users to configure settings of the EventLinker to suit their specific use cases.

  • The EventLinker has been implemented with thread safety in mind. All of its methods synchronize access to prevent race conditions when managing events and event handlers across multiple threads. This ensures that concurrent operations on the EventLinker are properly synchronized, avoiding data inconsistencies and race conditions.

Read more in the Pyventus docs for Event Linker.

Source code in pyventus/linkers/event_linker.py
class EventLinker:\n    \"\"\"\n    A base class that acts as a global registry for events and callbacks linkage. It provides\n    a centralized mechanism for managing event subscriptions, unsubscriptions, and retrieval\n    of events and their associated event handlers.\n\n    **Notes:**\n\n    -   The `EventLinker` class can be subclassed to create specific namespaces or contexts\n        for managing events and event handlers separately. By subclassing the `EventLinker`,\n        users can organize event subscriptions and handlers within different scopes, providing\n        modularity and flexibility in event management. Subclassing also allows users to\n        configure settings of the `EventLinker` to suit their specific use cases.\n\n    -   The `EventLinker` has been implemented with *thread safety* in mind. All of its methods\n        synchronize access to prevent race conditions when managing events and event handlers\n        across multiple threads. This ensures that concurrent operations on the `EventLinker`\n        are properly synchronized, avoiding data inconsistencies and race conditions.\n\n    ---\n    Read more in the\n    [Pyventus docs for Event Linker](https://mdapena.github.io/pyventus/tutorials/event-linker/).\n    \"\"\"\n\n    @final\n    class EventLinkageWrapper:\n        \"\"\"\n        A class that serves as a wrapper for event linking operations, providing a simplified\n        interface for subscribing events with their corresponding callbacks.\n\n        **Notes:**\n\n        -   This class can be used as either a decorator or a context manager. When used as a\n            decorator, it automatically subscribes the decorated callback to the provided events.\n            When used as a context manager with the `with` statement, it allows multiple callbacks\n            to be associated with the provided events within the context block.\n\n        -   This class is not intended to be subclassed or manually created.\n            The `EventLinkageWrapper` is used internally as a wrapper for event\n            linking operations.\n        \"\"\"\n\n        # Event linkage wrapper attributes\n        __slots__ = (\n            \"_event_linker\",\n            \"_events\",\n            \"_once\",\n            \"_force_async\",\n            \"_event_callback\",\n            \"_success_callback\",\n            \"_failure_callback\",\n        )\n\n        @property\n        def on_event(self) -> Callable[[EventCallbackType], EventCallbackType]:  # type: ignore[type-arg]\n            \"\"\"\n            Decorator that sets the main callback for the event. This callback\n            will be invoked when the associated event occurs.\n            :return: The decorated callback.\n            \"\"\"\n\n            def _wrapper(callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n                self._event_callback = callback\n                return callback\n\n            return _wrapper\n\n        @property\n        def on_success(self) -> Callable[[SuccessCallbackType], SuccessCallbackType]:\n            \"\"\"\n            Decorator that sets the success callback. This callback will be\n            invoked when the event execution completes successfully.\n            :return: The decorated callback.\n            \"\"\"\n\n            def _wrapper(callback: SuccessCallbackType) -> SuccessCallbackType:\n                self._success_callback = callback\n                return callback\n\n            return _wrapper\n\n        @property\n        def on_failure(self) -> Callable[[FailureCallbackType], FailureCallbackType]:\n            \"\"\"\n            Decorator that sets the failure callback. This callback\n            will be invoked when the event execution fails.\n            :return: The decorated callback.\n            \"\"\"\n\n            def _wrapper(callback: FailureCallbackType) -> FailureCallbackType:\n                self._failure_callback = callback\n                return callback\n\n            return _wrapper\n\n        def __init__(\n            self,\n            *events: SubscribableEventType,\n            event_linker: Type[\"EventLinker\"],\n            force_async: bool,\n            once: bool,\n        ) -> None:\n            \"\"\"\n            Initialize an instance of `EventLinkageWrapper`.\n            :param events: The events to subscribe/link to.\n            :param event_linker: The event linker instance used for subscription.\n            :param force_async: Determines whether to force all callbacks to run asynchronously.\n            :param once: Specifies if the callback is a one-time subscription.\n            \"\"\"\n            self._event_linker: Type[EventLinker] = event_linker\n            self._events: Tuple[SubscribableEventType, ...] = events\n\n            self._once: bool = once\n            self._force_async: bool = force_async\n            self._event_callback: EventCallbackType | None = None  # type: ignore[type-arg, no-redef, assignment]\n            self._success_callback: SuccessCallbackType | None = None  # type: ignore[no-redef, assignment]\n            self._failure_callback: FailureCallbackType | None = None  # type: ignore[no-redef, assignment]\n\n        def __call__(self, callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n            \"\"\"\n            Subscribes the provided events to the decorated callback.\n            :param callback: The callback to associate with the events.\n            :return: The decorated callback.\n            \"\"\"\n            self._event_callback = callback\n            self._event_linker.subscribe(\n                *self._events,\n                event_callback=self._event_callback,\n                success_callback=None,\n                failure_callback=None,\n                force_async=self._force_async,\n                once=self._once,\n            )\n            del self\n            return callback\n\n        def __enter__(self) -> \"EventLinker.EventLinkageWrapper\":\n            \"\"\"\n            Enters the linkage context block, allowing multiple\n            callbacks to be associated with the events.\n            :return: The context manager object\n            \"\"\"\n            return self\n\n        def __exit__(\n            self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n        ) -> None:\n            \"\"\"\n            Exits the linkage context block, subscribing the provided callbacks within\n            the context to the specified events. Performs any necessary cleanup.\n            :param exc_type: The type of the exception raised, if any.\n            :param exc_val: The exception object raised, if any.\n            :param exc_tb: The traceback information, if any.\n            :return: None\n            \"\"\"\n            self._event_linker.subscribe(\n                *self._events,\n                event_callback=self._event_callback,\n                success_callback=self._success_callback,\n                failure_callback=self._failure_callback,\n                force_async=self._force_async,\n                once=self._once,\n            )\n            del self\n\n    __registry: Dict[str, List[EventHandler]] = {}\n    \"\"\" \n    A dictionary that serves as a container for storing events and their associated event \n    handlers. The keys represent registered event names, and the values are lists of event\n    handler objects associated with each event.\n    \"\"\"\n\n    __max_event_handlers: int | None = None\n    \"\"\"The maximum number of `EventHandlers` allowed per event, or `None` if there is no limit.\"\"\"\n\n    __default_success_callback: SuccessCallbackType | None = None\n    \"\"\" \n    Represents the default success callback function that will be assigned to event handlers in \n    the absence of a specific success callback. This callback will be executed upon successful \n    completion of the event execution in each event handler.\n    \"\"\"\n\n    __default_failure_callback: FailureCallbackType | None = None\n    \"\"\"\n    Represents the default failure callback function that will be assigned to event handlers in \n    the absence of a specific failure callback. This callback will be executed when the event \n    execution fails in each event handler.\n    \"\"\"\n\n    __thread_lock: Lock = Lock()\n    \"\"\"\n    A `threading.Lock` object used for thread synchronization when accessing and modifying the \n    event registry to ensure thread safety. It prevents multiple threads from accessing and \n    modifying the registry simultaneously.\n    \"\"\"\n\n    __logger: Logger = Logger(name=\"EventLinker\", debug=bool(gettrace() is not None))\n    \"\"\"\n    The logger used to debug and log information within the `EventLinker` class. The debug mode\n    of the logger depends on the execution environment and the value returned by the `gettrace()`\n    function. The debug mode can also be influenced by subclassing and overridden in subclasses.\n    \"\"\"\n\n    def __init_subclass__(\n        cls,\n        max_event_handlers: int | None = None,\n        default_success_callback: SuccessCallbackType | None = None,\n        default_failure_callback: FailureCallbackType | None = None,\n        debug: bool | None = None,\n        **kwargs: Any,\n    ) -> None:\n        \"\"\"\n        Initialize a subclass of `EventLinker`.\n\n        By default, this method sets up the main registry and thread lock object, but\n        it can also be used to configure specific settings of the `EventLinker` subclass.\n\n        :param max_event_handlers: The maximum number of event handlers allowed per event,\n            or `None` if there is no limit.\n        :param default_success_callback: The default callback to assign as the success\n            callback in the event handlers when no specific success callback is provided.\n        :param default_failure_callback: The default callback to assign as the failure\n            callback in the event handlers when no specific failure callback is provided.\n        :param debug: Specifies the debug mode for the subclass logger. If `None`,\n            it is determined based on the execution environment.\n        :param kwargs: The keyword arguments to pass to the superclass\n            `__init_subclass__` method.\n        :raises PyventusException: If `max_event_handlers` is less than 1 or\n            if the provided callbacks are invalid.\n        :return: None\n        \"\"\"\n        # Call the parent class' __init_subclass__ method\n        super().__init_subclass__(**kwargs)\n\n        # Initialize the main registry\n        cls.__registry = {}\n\n        # Create a lock object for thread synchronization\n        cls.__thread_lock = Lock()\n\n        # Validate the max_event_handlers argument\n        if max_event_handlers is not None and max_event_handlers < 1:\n            raise PyventusException(\"The 'max_event_handlers' argument must be greater than or equal to 1.\")\n\n        # Set the maximum number of event handlers per event\n        cls.__max_event_handlers = max_event_handlers\n\n        # Validate the default success callback, if any\n        if default_success_callback is not None:\n            EventHandler.validate_callback(callback=default_success_callback)\n\n        # Set the default success callback\n        cls.__default_success_callback = default_success_callback\n\n        # Validate the default failure callback, if any\n        if default_failure_callback is not None:\n            EventHandler.validate_callback(callback=default_failure_callback)\n\n        # Set the default failure callback\n        cls.__default_failure_callback = default_failure_callback\n\n        # Validate the debug argument\n        if debug is not None and not isinstance(debug, bool):\n            raise PyventusException(\"The 'debug' argument must be a boolean value.\")\n\n        # Set up the logger\n        cls.__logger = Logger(\n            name=cls.__name__,\n            debug=debug if debug is not None else bool(gettrace() is not None),\n        )\n\n    @classmethod\n    def _get_logger(cls) -> Logger:\n        \"\"\"\n        Retrieve the class-level logger instance.\n        :return: The class-level logger instance used to debug and log\n            information within the `EventLinker` class.\n        \"\"\"\n        return cls.__logger\n\n    @classmethod\n    def get_event_name(cls, event: SubscribableEventType) -> str:\n        \"\"\"\n        Determines the name of the event.\n        :param event: The event to obtain the name for.\n        :return: A string that represents the event name.\n        :raises PyventusException: If the `event` argument is invalid\n            or if the event is not supported.\n        \"\"\"\n        # Validate the event argument\n        if event is None:\n            raise PyventusException(\"The 'event' argument cannot be None.\")\n\n        if event is Ellipsis:\n            # If the event is Ellipsis, return its type name\n            return type(event).__name__\n        elif isinstance(event, str):\n            if not event:\n                raise PyventusException(\"String events cannot be empty.\")\n            # If the event is a non-empty string, return it as the event name\n            return event\n        elif isinstance(event, type):\n            if not is_dataclass(event) and not issubclass(event, Exception):\n                raise PyventusException(\"Type events must be either a dataclass or an exception.\")\n            # If the event is either a dataclass type or an exception type, return its type name\n            return event.__name__\n        else:\n            # If the event is not supported, raise an exception\n            raise PyventusException(\"Unsupported event\")\n\n    @classmethod\n    def get_max_event_handlers(cls) -> int | None:\n        \"\"\"\n        Retrieve the maximum number of event handlers allowed per event.\n        :return: The maximum number of event handlers or `None` if there is no limit.\n        \"\"\"\n        return cls.__max_event_handlers\n\n    @classmethod\n    def get_default_success_callback(cls) -> SuccessCallbackType | None:\n        \"\"\"\n        Retrieve the default callback to be assigned as the success callback\n        in the event handlers when no specific success callback is provided.\n        :return: The default success callback or `None` if not set.\n        \"\"\"\n        return cls.__default_success_callback\n\n    @classmethod\n    def get_default_failure_callback(cls) -> FailureCallbackType | None:\n        \"\"\"\n        Retrieve the default callback to be assigned as the failure callback\n        in the event handlers when no specific failure callback is provided.\n        :return: The default failure callback or `None` if not set.\n        \"\"\"\n        return cls.__default_failure_callback\n\n    @classmethod\n    def get_registry(cls) -> Mapping[str, List[EventHandler]]:\n        \"\"\"\n        Retrieve the main registry mapping.\n        :return: A mapping of event names to event handlers.\n        \"\"\"\n        with cls.__thread_lock:\n            return {event_name: list(event_handlers) for event_name, event_handlers in cls.__registry.items()}\n\n    @classmethod\n    def get_events(cls) -> List[str]:\n        \"\"\"\n        Retrieve a list of all the registered events.\n        :return: A list of event names.\n        \"\"\"\n        with cls.__thread_lock:\n            return list(cls.__registry.keys())\n\n    @classmethod\n    def get_event_handlers(cls) -> List[EventHandler]:\n        \"\"\"\n        Retrieve a list of non-duplicated event handlers\n        that have been registered across all events.\n        :return: A list of event handlers.\n        \"\"\"\n        with cls.__thread_lock:\n            return list(\n                {event_handler for event_handlers in cls.__registry.values() for event_handler in event_handlers}\n            )\n\n    @classmethod\n    def get_events_by_event_handler(cls, event_handler: EventHandler) -> List[str]:\n        \"\"\"\n        Retrieve a list of event names associated with the provided event handler.\n        :param event_handler: The handler to retrieve the associated events for.\n        :return: A list of event names.\n        :raise PyventusException: If the `event_handler` argument is `None` or invalid.\n        \"\"\"\n        # Validate the event_handler argument\n        if event_handler is None:\n            raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n        if not isinstance(event_handler, EventHandler):\n            raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n        with cls.__thread_lock:\n            return [\n                event_name for event_name, event_handlers in cls.__registry.items() if event_handler in event_handlers\n            ]\n\n    @classmethod\n    def get_event_handlers_by_events(cls, *events: SubscribableEventType) -> List[EventHandler]:\n        \"\"\"\n        Retrieve a list of non-duplicated event handlers associated with the provided events.\n        :param events: Events to retrieve the event handlers for.\n        :return: A list of event handlers.\n        :raise PyventusException: If the `events` argument is `None`, empty or unsupported.\n        \"\"\"\n        # Validate the events argument\n        if events is None or len(events) <= 0:\n            raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n        # Retrieve all unique event names\n        event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n        with cls.__thread_lock:\n            return list(\n                {event_handler for event_name in event_names for event_handler in cls.__registry.get(event_name, [])}\n            )\n\n    @classmethod\n    def once(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n        \"\"\"\n        Decorator that allows you to conveniently subscribe callbacks to the provided events\n        for a single invocation.\n\n        This method can be used as either a decorator or a context manager. When used as a\n        decorator, it automatically subscribes the decorated callback to the provided events.\n        When used as a context manager with the `with` statement, it allows multiple callbacks\n        to be associated with the provided events within the context block.\n\n        :param events: The events to subscribe to.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :return: The decorator that wraps the callback.\n        \"\"\"\n        return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=True)\n\n    @classmethod\n    def on(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n        \"\"\"\n        Decorator that allows you to conveniently subscribe callbacks to the provided events.\n\n        This method can be used as either a decorator or a context manager. When used as a\n        decorator, it automatically subscribes the decorated callback to the provided events.\n        When used as a context manager with the `with` statement, it allows multiple callbacks\n        to be associated with the provided events within the context block.\n\n        :param events: The events to subscribe to.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :return: The decorator that wraps the callback.\n        \"\"\"\n        return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=False)\n\n    @classmethod\n    def subscribe(\n        cls,\n        *events: SubscribableEventType,\n        event_callback: EventCallbackType,  # type: ignore[type-arg]\n        success_callback: SuccessCallbackType | None = None,\n        failure_callback: FailureCallbackType | None = None,\n        force_async: bool = False,\n        once: bool = False,\n    ) -> EventHandler:\n        \"\"\"\n        Subscribes callbacks to the provided events.\n        :param events: The events to subscribe to.\n        :param event_callback: The callback to be executed when the event occurs.\n        :param success_callback: The callback to be executed when the event execution completes\n            successfully.\n        :param failure_callback: The callback to be executed when the event execution fails.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n            If `True`, synchronous callbacks will be converted to run asynchronously in a\n            thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n            will run synchronously or asynchronously as defined.\n        :param once: Specifies if the event handler is a one-time subscription.\n        :return: The event handler object associated with the given events.\n        \"\"\"\n        # Validate the events argument\n        if events is None or len(events) <= 0:\n            raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n        # Retrieve all unique event names\n        event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # Check if the maximum number of handlers property is set\n            if cls.__max_event_handlers is not None:\n                # For each event name, check if the maximum number of handlers for the event has been exceeded\n                for event_name in event_names:\n                    if len(cls.__registry.get(event_name, [])) >= cls.__max_event_handlers:\n                        raise PyventusException(\n                            f\"The event '{event_name}' has exceeded the maximum number of handlers allowed. The \"\n                            f\"'{EventHandler.get_callback_name(callback=event_callback)}'\"\n                            f\" callback cannot be subscribed.\"\n                        )\n\n            # Create a new event handler\n            event_handler: EventHandler = EventHandler(\n                event_callback=event_callback,\n                success_callback=success_callback if success_callback else cls.__default_success_callback,\n                failure_callback=failure_callback if failure_callback else cls.__default_failure_callback,\n                force_async=force_async,\n                once=once,\n            )\n\n            # For each event name, register the event handler\n            for event_name in event_names:\n                # If the event name is not present in the main registry, create a new empty list for it\n                if event_name not in cls.__registry:\n                    cls.__registry[event_name] = []\n\n                # Append the event handler to the list of handlers for the event\n                cls.__registry[event_name].append(event_handler)\n\n                # Log the subscription if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(\n                        action=\"Subscribed:\",\n                        msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                    )\n\n        # Return the new event handler\n        return event_handler\n\n    @classmethod\n    def unsubscribe(cls, *events: SubscribableEventType, event_handler: EventHandler) -> bool:\n        \"\"\"\n        Unsubscribes an event handler from the provided events. If there are no more\n        handlers for a particular event, that event is also removed from the registry.\n        :param events: The events to unsubscribe from.\n        :param event_handler: The event handler to unsubscribe.\n        :return: `True` if the event handler associated with the events was found and\n            removed, `False` otherwise.\n        :raises PyventusException: If the `events` argument is `None`, empty, unsupported,\n            or if the `event_handler` argument is `None`, invalid.\n        \"\"\"\n        # Validate the events argument\n        if events is None or len(events) <= 0:\n            raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n        # Validate the event_handler argument\n        if event_handler is None:\n            raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n        if not isinstance(event_handler, EventHandler):\n            raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n        # Retrieve all unique event names\n        event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n        # A flag indicating whether the event handler was successfully removed\n        deleted: bool = False\n\n        # Obtain the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # For each event name, check and remove the event handler if found\n            for event_name in event_names:\n                # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n                event_handlers = cls.__registry.get(event_name, [])\n\n                # Check if the event handler is present in the list of handlers for the event\n                if event_handler in event_handlers:\n                    # Remove the event handler from the list of handlers\n                    event_handlers.remove(event_handler)\n                    deleted = True\n\n                    # If there are no more handlers for the event, remove the event name from the registry\n                    if not event_handlers:\n                        cls.__registry.pop(event_name)\n\n                    # Log the unsubscription if debug is enabled\n                    if cls.__logger.debug_enabled:  # pragma: no cover\n                        cls.__logger.debug(\n                            action=\"Unsubscribed:\",\n                            msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                        )\n\n        # Return the flag indicating whether the event handler was deleted\n        return deleted\n\n    @classmethod\n    def remove_event_handler(cls, event_handler: EventHandler) -> bool:\n        \"\"\"\n        Removes an event handler from all subscribed events. If there are no more\n        handlers for a particular event, that event is also removed from the registry.\n        :param event_handler: The event handler to remove.\n        :return: `True` if the event handler was found and removed, `False` otherwise.\n        :raises PyventusException: If the `event_handler` argument is `None` or invalid.\n        \"\"\"\n        # Validate the event_handler argument\n        if event_handler is None:\n            raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n        if not isinstance(event_handler, EventHandler):\n            raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n        # A flag indicating if the event handler gets removed\n        deleted: bool = False\n\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # Iterate through each event and its associated handlers in the main registry\n            for event_name in list(cls.__registry.keys()):\n                # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n                event_handlers = cls.__registry.get(event_name, [])\n\n                # Check if the event handler is present in the list of handlers for the event\n                if event_handler in event_handlers:\n                    # Remove the event handler from the list of handlers\n                    event_handlers.remove(event_handler)\n                    deleted = True\n\n                    # If there are no more handlers for the event, remove the event from the registry\n                    if not event_handlers:\n                        cls.__registry.pop(event_name)\n\n                    # Log the removal of the event handler if debug is enabled\n                    if cls.__logger.debug_enabled:  # pragma: no cover\n                        cls.__logger.debug(\n                            action=\"Handler Removed:\",\n                            msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                        )\n\n        # Return the flag indicating if the event handler was found and deleted\n        return deleted\n\n    @classmethod\n    def remove_event(cls, event: SubscribableEventType) -> bool:\n        \"\"\"\n        Removes an event from the registry, including all its event handler subscriptions.\n        :param event: The event to remove.\n        :return: `True` if the event was found and removed, `False` otherwise.\n        \"\"\"\n        # Get the event name\n        event_name: str = cls.get_event_name(event=event)\n\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            # Check if the event name is present in the main registry\n            if event_name in cls.__registry:\n                # Remove the event from the registry\n                cls.__registry.pop(event_name)\n\n                # Log the removal of the event if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(action=\"Event Removed:\", msg=f\"{event_name}\")\n\n                # Return True to indicate successful removal\n                return True\n\n        return False\n\n    @classmethod\n    def remove_all(cls) -> bool:\n        \"\"\"\n        Removes all events and their associated event handlers from the registry.\n        :return: `True` if the events were found and removed, `False` otherwise.\n        \"\"\"\n        # Acquire the lock to ensure exclusive access to the main registry\n        with cls.__thread_lock:\n            if cls.__registry:\n                # Clear the main registry\n                cls.__registry.clear()\n\n                # Log a debug message if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(msg=\"All events and handlers were successfully removed.\")\n\n                return True\n            else:\n                # Log a debug message if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(msg=\"The event registry is already empty.\")\n\n                return False\n

"},{"location":"api/event-linker/#pyventus.EventLinker-classes","title":"Classes","text":""},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper","title":"EventLinkageWrapper","text":"

A class that serves as a wrapper for event linking operations, providing a simplified interface for subscribing events with their corresponding callbacks.

Notes:

  • This class can be used as either a decorator or a context manager. When used as a decorator, it automatically subscribes the decorated callback to the provided events. When used as a context manager with the with statement, it allows multiple callbacks to be associated with the provided events within the context block.

  • This class is not intended to be subclassed or manually created. The EventLinkageWrapper is used internally as a wrapper for event linking operations.

Source code in pyventus/linkers/event_linker.py
@final\nclass EventLinkageWrapper:\n    \"\"\"\n    A class that serves as a wrapper for event linking operations, providing a simplified\n    interface for subscribing events with their corresponding callbacks.\n\n    **Notes:**\n\n    -   This class can be used as either a decorator or a context manager. When used as a\n        decorator, it automatically subscribes the decorated callback to the provided events.\n        When used as a context manager with the `with` statement, it allows multiple callbacks\n        to be associated with the provided events within the context block.\n\n    -   This class is not intended to be subclassed or manually created.\n        The `EventLinkageWrapper` is used internally as a wrapper for event\n        linking operations.\n    \"\"\"\n\n    # Event linkage wrapper attributes\n    __slots__ = (\n        \"_event_linker\",\n        \"_events\",\n        \"_once\",\n        \"_force_async\",\n        \"_event_callback\",\n        \"_success_callback\",\n        \"_failure_callback\",\n    )\n\n    @property\n    def on_event(self) -> Callable[[EventCallbackType], EventCallbackType]:  # type: ignore[type-arg]\n        \"\"\"\n        Decorator that sets the main callback for the event. This callback\n        will be invoked when the associated event occurs.\n        :return: The decorated callback.\n        \"\"\"\n\n        def _wrapper(callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n            self._event_callback = callback\n            return callback\n\n        return _wrapper\n\n    @property\n    def on_success(self) -> Callable[[SuccessCallbackType], SuccessCallbackType]:\n        \"\"\"\n        Decorator that sets the success callback. This callback will be\n        invoked when the event execution completes successfully.\n        :return: The decorated callback.\n        \"\"\"\n\n        def _wrapper(callback: SuccessCallbackType) -> SuccessCallbackType:\n            self._success_callback = callback\n            return callback\n\n        return _wrapper\n\n    @property\n    def on_failure(self) -> Callable[[FailureCallbackType], FailureCallbackType]:\n        \"\"\"\n        Decorator that sets the failure callback. This callback\n        will be invoked when the event execution fails.\n        :return: The decorated callback.\n        \"\"\"\n\n        def _wrapper(callback: FailureCallbackType) -> FailureCallbackType:\n            self._failure_callback = callback\n            return callback\n\n        return _wrapper\n\n    def __init__(\n        self,\n        *events: SubscribableEventType,\n        event_linker: Type[\"EventLinker\"],\n        force_async: bool,\n        once: bool,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `EventLinkageWrapper`.\n        :param events: The events to subscribe/link to.\n        :param event_linker: The event linker instance used for subscription.\n        :param force_async: Determines whether to force all callbacks to run asynchronously.\n        :param once: Specifies if the callback is a one-time subscription.\n        \"\"\"\n        self._event_linker: Type[EventLinker] = event_linker\n        self._events: Tuple[SubscribableEventType, ...] = events\n\n        self._once: bool = once\n        self._force_async: bool = force_async\n        self._event_callback: EventCallbackType | None = None  # type: ignore[type-arg, no-redef, assignment]\n        self._success_callback: SuccessCallbackType | None = None  # type: ignore[no-redef, assignment]\n        self._failure_callback: FailureCallbackType | None = None  # type: ignore[no-redef, assignment]\n\n    def __call__(self, callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n        \"\"\"\n        Subscribes the provided events to the decorated callback.\n        :param callback: The callback to associate with the events.\n        :return: The decorated callback.\n        \"\"\"\n        self._event_callback = callback\n        self._event_linker.subscribe(\n            *self._events,\n            event_callback=self._event_callback,\n            success_callback=None,\n            failure_callback=None,\n            force_async=self._force_async,\n            once=self._once,\n        )\n        del self\n        return callback\n\n    def __enter__(self) -> \"EventLinker.EventLinkageWrapper\":\n        \"\"\"\n        Enters the linkage context block, allowing multiple\n        callbacks to be associated with the events.\n        :return: The context manager object\n        \"\"\"\n        return self\n\n    def __exit__(\n        self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n    ) -> None:\n        \"\"\"\n        Exits the linkage context block, subscribing the provided callbacks within\n        the context to the specified events. Performs any necessary cleanup.\n        :param exc_type: The type of the exception raised, if any.\n        :param exc_val: The exception object raised, if any.\n        :param exc_tb: The traceback information, if any.\n        :return: None\n        \"\"\"\n        self._event_linker.subscribe(\n            *self._events,\n            event_callback=self._event_callback,\n            success_callback=self._success_callback,\n            failure_callback=self._failure_callback,\n            force_async=self._force_async,\n            once=self._once,\n        )\n        del self\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper-attributes","title":"Attributes","text":""},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.on_event","title":"on_event property","text":"
on_event: Callable[[EventCallbackType], EventCallbackType]\n

Decorator that sets the main callback for the event. This callback will be invoked when the associated event occurs.

RETURNS DESCRIPTION Callable[[EventCallbackType], EventCallbackType]

The decorated callback.

"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.on_success","title":"on_success property","text":"
on_success: Callable[[SuccessCallbackType], SuccessCallbackType]\n

Decorator that sets the success callback. This callback will be invoked when the event execution completes successfully.

RETURNS DESCRIPTION Callable[[SuccessCallbackType], SuccessCallbackType]

The decorated callback.

"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.on_failure","title":"on_failure property","text":"
on_failure: Callable[[FailureCallbackType], FailureCallbackType]\n

Decorator that sets the failure callback. This callback will be invoked when the event execution fails.

RETURNS DESCRIPTION Callable[[FailureCallbackType], FailureCallbackType]

The decorated callback.

"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper-functions","title":"Functions","text":""},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__init__","title":"__init__","text":"
__init__(*events: SubscribableEventType, event_linker: Type[EventLinker], force_async: bool, once: bool) -> None\n

Initialize an instance of EventLinkageWrapper.

PARAMETER DESCRIPTION events

The events to subscribe/link to.

TYPE: SubscribableEventType DEFAULT: ()

event_linker

The event linker instance used for subscription.

TYPE: Type[EventLinker]

force_async

Determines whether to force all callbacks to run asynchronously.

TYPE: bool

once

Specifies if the callback is a one-time subscription.

TYPE: bool

Source code in pyventus/linkers/event_linker.py
def __init__(\n    self,\n    *events: SubscribableEventType,\n    event_linker: Type[\"EventLinker\"],\n    force_async: bool,\n    once: bool,\n) -> None:\n    \"\"\"\n    Initialize an instance of `EventLinkageWrapper`.\n    :param events: The events to subscribe/link to.\n    :param event_linker: The event linker instance used for subscription.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n    :param once: Specifies if the callback is a one-time subscription.\n    \"\"\"\n    self._event_linker: Type[EventLinker] = event_linker\n    self._events: Tuple[SubscribableEventType, ...] = events\n\n    self._once: bool = once\n    self._force_async: bool = force_async\n    self._event_callback: EventCallbackType | None = None  # type: ignore[type-arg, no-redef, assignment]\n    self._success_callback: SuccessCallbackType | None = None  # type: ignore[no-redef, assignment]\n    self._failure_callback: FailureCallbackType | None = None  # type: ignore[no-redef, assignment]\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__call__","title":"__call__","text":"
__call__(callback: EventCallbackType) -> EventCallbackType\n

Subscribes the provided events to the decorated callback.

PARAMETER DESCRIPTION callback

The callback to associate with the events.

TYPE: EventCallbackType

RETURNS DESCRIPTION EventCallbackType

The decorated callback.

Source code in pyventus/linkers/event_linker.py
def __call__(self, callback: EventCallbackType) -> EventCallbackType:  # type: ignore[type-arg]\n    \"\"\"\n    Subscribes the provided events to the decorated callback.\n    :param callback: The callback to associate with the events.\n    :return: The decorated callback.\n    \"\"\"\n    self._event_callback = callback\n    self._event_linker.subscribe(\n        *self._events,\n        event_callback=self._event_callback,\n        success_callback=None,\n        failure_callback=None,\n        force_async=self._force_async,\n        once=self._once,\n    )\n    del self\n    return callback\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__enter__","title":"__enter__","text":"
__enter__() -> EventLinkageWrapper\n

Enters the linkage context block, allowing multiple callbacks to be associated with the events.

RETURNS DESCRIPTION EventLinkageWrapper

The context manager object

Source code in pyventus/linkers/event_linker.py
def __enter__(self) -> \"EventLinker.EventLinkageWrapper\":\n    \"\"\"\n    Enters the linkage context block, allowing multiple\n    callbacks to be associated with the events.\n    :return: The context manager object\n    \"\"\"\n    return self\n
"},{"location":"api/event-linker/#pyventus.EventLinker.EventLinkageWrapper.__exit__","title":"__exit__","text":"
__exit__(exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) -> None\n

Exits the linkage context block, subscribing the provided callbacks within the context to the specified events. Performs any necessary cleanup.

PARAMETER DESCRIPTION exc_type

The type of the exception raised, if any.

TYPE: Type[BaseException] | None

exc_val

The exception object raised, if any.

TYPE: BaseException | None

exc_tb

The traceback information, if any.

TYPE: TracebackType | None

RETURNS DESCRIPTION None

None

Source code in pyventus/linkers/event_linker.py
def __exit__(\n    self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n) -> None:\n    \"\"\"\n    Exits the linkage context block, subscribing the provided callbacks within\n    the context to the specified events. Performs any necessary cleanup.\n    :param exc_type: The type of the exception raised, if any.\n    :param exc_val: The exception object raised, if any.\n    :param exc_tb: The traceback information, if any.\n    :return: None\n    \"\"\"\n    self._event_linker.subscribe(\n        *self._events,\n        event_callback=self._event_callback,\n        success_callback=self._success_callback,\n        failure_callback=self._failure_callback,\n        force_async=self._force_async,\n        once=self._once,\n    )\n    del self\n
"},{"location":"api/event-linker/#pyventus.EventLinker-functions","title":"Functions","text":""},{"location":"api/event-linker/#pyventus.EventLinker.__init_subclass__","title":"__init_subclass__","text":"
__init_subclass__(max_event_handlers: int | None = None, default_success_callback: SuccessCallbackType | None = None, default_failure_callback: FailureCallbackType | None = None, debug: bool | None = None, **kwargs: Any) -> None\n

Initialize a subclass of EventLinker.

By default, this method sets up the main registry and thread lock object, but it can also be used to configure specific settings of the EventLinker subclass.

PARAMETER DESCRIPTION max_event_handlers

The maximum number of event handlers allowed per event, or None if there is no limit.

TYPE: int | None DEFAULT: None

default_success_callback

The default callback to assign as the success callback in the event handlers when no specific success callback is provided.

TYPE: SuccessCallbackType | None DEFAULT: None

default_failure_callback

The default callback to assign as the failure callback in the event handlers when no specific failure callback is provided.

TYPE: FailureCallbackType | None DEFAULT: None

debug

Specifies the debug mode for the subclass logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

kwargs

The keyword arguments to pass to the superclass __init_subclass__ method.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

RAISES DESCRIPTION PyventusException

If max_event_handlers is less than 1 or if the provided callbacks are invalid.

Source code in pyventus/linkers/event_linker.py
def __init_subclass__(\n    cls,\n    max_event_handlers: int | None = None,\n    default_success_callback: SuccessCallbackType | None = None,\n    default_failure_callback: FailureCallbackType | None = None,\n    debug: bool | None = None,\n    **kwargs: Any,\n) -> None:\n    \"\"\"\n    Initialize a subclass of `EventLinker`.\n\n    By default, this method sets up the main registry and thread lock object, but\n    it can also be used to configure specific settings of the `EventLinker` subclass.\n\n    :param max_event_handlers: The maximum number of event handlers allowed per event,\n        or `None` if there is no limit.\n    :param default_success_callback: The default callback to assign as the success\n        callback in the event handlers when no specific success callback is provided.\n    :param default_failure_callback: The default callback to assign as the failure\n        callback in the event handlers when no specific failure callback is provided.\n    :param debug: Specifies the debug mode for the subclass logger. If `None`,\n        it is determined based on the execution environment.\n    :param kwargs: The keyword arguments to pass to the superclass\n        `__init_subclass__` method.\n    :raises PyventusException: If `max_event_handlers` is less than 1 or\n        if the provided callbacks are invalid.\n    :return: None\n    \"\"\"\n    # Call the parent class' __init_subclass__ method\n    super().__init_subclass__(**kwargs)\n\n    # Initialize the main registry\n    cls.__registry = {}\n\n    # Create a lock object for thread synchronization\n    cls.__thread_lock = Lock()\n\n    # Validate the max_event_handlers argument\n    if max_event_handlers is not None and max_event_handlers < 1:\n        raise PyventusException(\"The 'max_event_handlers' argument must be greater than or equal to 1.\")\n\n    # Set the maximum number of event handlers per event\n    cls.__max_event_handlers = max_event_handlers\n\n    # Validate the default success callback, if any\n    if default_success_callback is not None:\n        EventHandler.validate_callback(callback=default_success_callback)\n\n    # Set the default success callback\n    cls.__default_success_callback = default_success_callback\n\n    # Validate the default failure callback, if any\n    if default_failure_callback is not None:\n        EventHandler.validate_callback(callback=default_failure_callback)\n\n    # Set the default failure callback\n    cls.__default_failure_callback = default_failure_callback\n\n    # Validate the debug argument\n    if debug is not None and not isinstance(debug, bool):\n        raise PyventusException(\"The 'debug' argument must be a boolean value.\")\n\n    # Set up the logger\n    cls.__logger = Logger(\n        name=cls.__name__,\n        debug=debug if debug is not None else bool(gettrace() is not None),\n    )\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_event_name","title":"get_event_name classmethod","text":"
get_event_name(event: SubscribableEventType) -> str\n

Determines the name of the event.

PARAMETER DESCRIPTION event

The event to obtain the name for.

TYPE: SubscribableEventType

RETURNS DESCRIPTION str

A string that represents the event name.

RAISES DESCRIPTION PyventusException

If the event argument is invalid or if the event is not supported.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_event_name(cls, event: SubscribableEventType) -> str:\n    \"\"\"\n    Determines the name of the event.\n    :param event: The event to obtain the name for.\n    :return: A string that represents the event name.\n    :raises PyventusException: If the `event` argument is invalid\n        or if the event is not supported.\n    \"\"\"\n    # Validate the event argument\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    if event is Ellipsis:\n        # If the event is Ellipsis, return its type name\n        return type(event).__name__\n    elif isinstance(event, str):\n        if not event:\n            raise PyventusException(\"String events cannot be empty.\")\n        # If the event is a non-empty string, return it as the event name\n        return event\n    elif isinstance(event, type):\n        if not is_dataclass(event) and not issubclass(event, Exception):\n            raise PyventusException(\"Type events must be either a dataclass or an exception.\")\n        # If the event is either a dataclass type or an exception type, return its type name\n        return event.__name__\n    else:\n        # If the event is not supported, raise an exception\n        raise PyventusException(\"Unsupported event\")\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_max_event_handlers","title":"get_max_event_handlers classmethod","text":"
get_max_event_handlers() -> int | None\n

Retrieve the maximum number of event handlers allowed per event.

RETURNS DESCRIPTION int | None

The maximum number of event handlers or None if there is no limit.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_max_event_handlers(cls) -> int | None:\n    \"\"\"\n    Retrieve the maximum number of event handlers allowed per event.\n    :return: The maximum number of event handlers or `None` if there is no limit.\n    \"\"\"\n    return cls.__max_event_handlers\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_default_success_callback","title":"get_default_success_callback classmethod","text":"
get_default_success_callback() -> SuccessCallbackType | None\n

Retrieve the default callback to be assigned as the success callback in the event handlers when no specific success callback is provided.

RETURNS DESCRIPTION SuccessCallbackType | None

The default success callback or None if not set.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_default_success_callback(cls) -> SuccessCallbackType | None:\n    \"\"\"\n    Retrieve the default callback to be assigned as the success callback\n    in the event handlers when no specific success callback is provided.\n    :return: The default success callback or `None` if not set.\n    \"\"\"\n    return cls.__default_success_callback\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_default_failure_callback","title":"get_default_failure_callback classmethod","text":"
get_default_failure_callback() -> FailureCallbackType | None\n

Retrieve the default callback to be assigned as the failure callback in the event handlers when no specific failure callback is provided.

RETURNS DESCRIPTION FailureCallbackType | None

The default failure callback or None if not set.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_default_failure_callback(cls) -> FailureCallbackType | None:\n    \"\"\"\n    Retrieve the default callback to be assigned as the failure callback\n    in the event handlers when no specific failure callback is provided.\n    :return: The default failure callback or `None` if not set.\n    \"\"\"\n    return cls.__default_failure_callback\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_registry","title":"get_registry classmethod","text":"
get_registry() -> Mapping[str, List[EventHandler]]\n

Retrieve the main registry mapping.

RETURNS DESCRIPTION Mapping[str, List[EventHandler]]

A mapping of event names to event handlers.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_registry(cls) -> Mapping[str, List[EventHandler]]:\n    \"\"\"\n    Retrieve the main registry mapping.\n    :return: A mapping of event names to event handlers.\n    \"\"\"\n    with cls.__thread_lock:\n        return {event_name: list(event_handlers) for event_name, event_handlers in cls.__registry.items()}\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_events","title":"get_events classmethod","text":"
get_events() -> List[str]\n

Retrieve a list of all the registered events.

RETURNS DESCRIPTION List[str]

A list of event names.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_events(cls) -> List[str]:\n    \"\"\"\n    Retrieve a list of all the registered events.\n    :return: A list of event names.\n    \"\"\"\n    with cls.__thread_lock:\n        return list(cls.__registry.keys())\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_event_handlers","title":"get_event_handlers classmethod","text":"
get_event_handlers() -> List[EventHandler]\n

Retrieve a list of non-duplicated event handlers that have been registered across all events.

RETURNS DESCRIPTION List[EventHandler]

A list of event handlers.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_event_handlers(cls) -> List[EventHandler]:\n    \"\"\"\n    Retrieve a list of non-duplicated event handlers\n    that have been registered across all events.\n    :return: A list of event handlers.\n    \"\"\"\n    with cls.__thread_lock:\n        return list(\n            {event_handler for event_handlers in cls.__registry.values() for event_handler in event_handlers}\n        )\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_events_by_event_handler","title":"get_events_by_event_handler classmethod","text":"
get_events_by_event_handler(event_handler: EventHandler) -> List[str]\n

Retrieve a list of event names associated with the provided event handler.

PARAMETER DESCRIPTION event_handler

The handler to retrieve the associated events for.

TYPE: EventHandler

RETURNS DESCRIPTION List[str]

A list of event names.

RAISES DESCRIPTION PyventusException

If the event_handler argument is None or invalid.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_events_by_event_handler(cls, event_handler: EventHandler) -> List[str]:\n    \"\"\"\n    Retrieve a list of event names associated with the provided event handler.\n    :param event_handler: The handler to retrieve the associated events for.\n    :return: A list of event names.\n    :raise PyventusException: If the `event_handler` argument is `None` or invalid.\n    \"\"\"\n    # Validate the event_handler argument\n    if event_handler is None:\n        raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n    if not isinstance(event_handler, EventHandler):\n        raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n    with cls.__thread_lock:\n        return [\n            event_name for event_name, event_handlers in cls.__registry.items() if event_handler in event_handlers\n        ]\n
"},{"location":"api/event-linker/#pyventus.EventLinker.get_event_handlers_by_events","title":"get_event_handlers_by_events classmethod","text":"
get_event_handlers_by_events(*events: SubscribableEventType) -> List[EventHandler]\n

Retrieve a list of non-duplicated event handlers associated with the provided events.

PARAMETER DESCRIPTION events

Events to retrieve the event handlers for.

TYPE: SubscribableEventType DEFAULT: ()

RETURNS DESCRIPTION List[EventHandler]

A list of event handlers.

RAISES DESCRIPTION PyventusException

If the events argument is None, empty or unsupported.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef get_event_handlers_by_events(cls, *events: SubscribableEventType) -> List[EventHandler]:\n    \"\"\"\n    Retrieve a list of non-duplicated event handlers associated with the provided events.\n    :param events: Events to retrieve the event handlers for.\n    :return: A list of event handlers.\n    :raise PyventusException: If the `events` argument is `None`, empty or unsupported.\n    \"\"\"\n    # Validate the events argument\n    if events is None or len(events) <= 0:\n        raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n    # Retrieve all unique event names\n    event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n    with cls.__thread_lock:\n        return list(\n            {event_handler for event_name in event_names for event_handler in cls.__registry.get(event_name, [])}\n        )\n
"},{"location":"api/event-linker/#pyventus.EventLinker.once","title":"once classmethod","text":"
once(*events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper\n

Decorator that allows you to conveniently subscribe callbacks to the provided events for a single invocation.

This method can be used as either a decorator or a context manager. When used as a decorator, it automatically subscribes the decorated callback to the provided events. When used as a context manager with the with statement, it allows multiple callbacks to be associated with the provided events within the context block.

PARAMETER DESCRIPTION events

The events to subscribe to.

TYPE: SubscribableEventType DEFAULT: ()

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION EventLinkageWrapper

The decorator that wraps the callback.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef once(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n    \"\"\"\n    Decorator that allows you to conveniently subscribe callbacks to the provided events\n    for a single invocation.\n\n    This method can be used as either a decorator or a context manager. When used as a\n    decorator, it automatically subscribes the decorated callback to the provided events.\n    When used as a context manager with the `with` statement, it allows multiple callbacks\n    to be associated with the provided events within the context block.\n\n    :param events: The events to subscribe to.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :return: The decorator that wraps the callback.\n    \"\"\"\n    return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=True)\n
"},{"location":"api/event-linker/#pyventus.EventLinker.on","title":"on classmethod","text":"
on(*events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper\n

Decorator that allows you to conveniently subscribe callbacks to the provided events.

This method can be used as either a decorator or a context manager. When used as a decorator, it automatically subscribes the decorated callback to the provided events. When used as a context manager with the with statement, it allows multiple callbacks to be associated with the provided events within the context block.

PARAMETER DESCRIPTION events

The events to subscribe to.

TYPE: SubscribableEventType DEFAULT: ()

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION EventLinkageWrapper

The decorator that wraps the callback.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef on(cls, *events: SubscribableEventType, force_async: bool = False) -> EventLinkageWrapper:\n    \"\"\"\n    Decorator that allows you to conveniently subscribe callbacks to the provided events.\n\n    This method can be used as either a decorator or a context manager. When used as a\n    decorator, it automatically subscribes the decorated callback to the provided events.\n    When used as a context manager with the `with` statement, it allows multiple callbacks\n    to be associated with the provided events within the context block.\n\n    :param events: The events to subscribe to.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :return: The decorator that wraps the callback.\n    \"\"\"\n    return EventLinker.EventLinkageWrapper(*events, event_linker=cls, force_async=force_async, once=False)\n
"},{"location":"api/event-linker/#pyventus.EventLinker.subscribe","title":"subscribe classmethod","text":"
subscribe(*events: SubscribableEventType, event_callback: EventCallbackType, success_callback: SuccessCallbackType | None = None, failure_callback: FailureCallbackType | None = None, force_async: bool = False, once: bool = False) -> EventHandler\n

Subscribes callbacks to the provided events.

PARAMETER DESCRIPTION events

The events to subscribe to.

TYPE: SubscribableEventType DEFAULT: ()

event_callback

The callback to be executed when the event occurs.

TYPE: EventCallbackType

success_callback

The callback to be executed when the event execution completes successfully.

TYPE: SuccessCallbackType | None DEFAULT: None

failure_callback

The callback to be executed when the event execution fails.

TYPE: FailureCallbackType | None DEFAULT: None

force_async

Determines whether to force all callbacks to run asynchronously. If True, synchronous callbacks will be converted to run asynchronously in a thread pool, using the asyncio.to_thread function. If False, callbacks will run synchronously or asynchronously as defined.

TYPE: bool DEFAULT: False

once

Specifies if the event handler is a one-time subscription.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION EventHandler

The event handler object associated with the given events.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef subscribe(\n    cls,\n    *events: SubscribableEventType,\n    event_callback: EventCallbackType,  # type: ignore[type-arg]\n    success_callback: SuccessCallbackType | None = None,\n    failure_callback: FailureCallbackType | None = None,\n    force_async: bool = False,\n    once: bool = False,\n) -> EventHandler:\n    \"\"\"\n    Subscribes callbacks to the provided events.\n    :param events: The events to subscribe to.\n    :param event_callback: The callback to be executed when the event occurs.\n    :param success_callback: The callback to be executed when the event execution completes\n        successfully.\n    :param failure_callback: The callback to be executed when the event execution fails.\n    :param force_async: Determines whether to force all callbacks to run asynchronously.\n        If `True`, synchronous callbacks will be converted to run asynchronously in a\n        thread pool, using the `asyncio.to_thread` function. If `False`, callbacks\n        will run synchronously or asynchronously as defined.\n    :param once: Specifies if the event handler is a one-time subscription.\n    :return: The event handler object associated with the given events.\n    \"\"\"\n    # Validate the events argument\n    if events is None or len(events) <= 0:\n        raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n    # Retrieve all unique event names\n    event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # Check if the maximum number of handlers property is set\n        if cls.__max_event_handlers is not None:\n            # For each event name, check if the maximum number of handlers for the event has been exceeded\n            for event_name in event_names:\n                if len(cls.__registry.get(event_name, [])) >= cls.__max_event_handlers:\n                    raise PyventusException(\n                        f\"The event '{event_name}' has exceeded the maximum number of handlers allowed. The \"\n                        f\"'{EventHandler.get_callback_name(callback=event_callback)}'\"\n                        f\" callback cannot be subscribed.\"\n                    )\n\n        # Create a new event handler\n        event_handler: EventHandler = EventHandler(\n            event_callback=event_callback,\n            success_callback=success_callback if success_callback else cls.__default_success_callback,\n            failure_callback=failure_callback if failure_callback else cls.__default_failure_callback,\n            force_async=force_async,\n            once=once,\n        )\n\n        # For each event name, register the event handler\n        for event_name in event_names:\n            # If the event name is not present in the main registry, create a new empty list for it\n            if event_name not in cls.__registry:\n                cls.__registry[event_name] = []\n\n            # Append the event handler to the list of handlers for the event\n            cls.__registry[event_name].append(event_handler)\n\n            # Log the subscription if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(\n                    action=\"Subscribed:\",\n                    msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                )\n\n    # Return the new event handler\n    return event_handler\n
"},{"location":"api/event-linker/#pyventus.EventLinker.unsubscribe","title":"unsubscribe classmethod","text":"
unsubscribe(*events: SubscribableEventType, event_handler: EventHandler) -> bool\n

Unsubscribes an event handler from the provided events. If there are no more handlers for a particular event, that event is also removed from the registry.

PARAMETER DESCRIPTION events

The events to unsubscribe from.

TYPE: SubscribableEventType DEFAULT: ()

event_handler

The event handler to unsubscribe.

TYPE: EventHandler

RETURNS DESCRIPTION bool

True if the event handler associated with the events was found and removed, False otherwise.

RAISES DESCRIPTION PyventusException

If the events argument is None, empty, unsupported, or if the event_handler argument is None, invalid.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef unsubscribe(cls, *events: SubscribableEventType, event_handler: EventHandler) -> bool:\n    \"\"\"\n    Unsubscribes an event handler from the provided events. If there are no more\n    handlers for a particular event, that event is also removed from the registry.\n    :param events: The events to unsubscribe from.\n    :param event_handler: The event handler to unsubscribe.\n    :return: `True` if the event handler associated with the events was found and\n        removed, `False` otherwise.\n    :raises PyventusException: If the `events` argument is `None`, empty, unsupported,\n        or if the `event_handler` argument is `None`, invalid.\n    \"\"\"\n    # Validate the events argument\n    if events is None or len(events) <= 0:\n        raise PyventusException(\"The 'events' argument cannot be None or empty.\")\n\n    # Validate the event_handler argument\n    if event_handler is None:\n        raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n    if not isinstance(event_handler, EventHandler):\n        raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n    # Retrieve all unique event names\n    event_names: Set[str] = {cls.get_event_name(event=event) for event in events}\n\n    # A flag indicating whether the event handler was successfully removed\n    deleted: bool = False\n\n    # Obtain the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # For each event name, check and remove the event handler if found\n        for event_name in event_names:\n            # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n            event_handlers = cls.__registry.get(event_name, [])\n\n            # Check if the event handler is present in the list of handlers for the event\n            if event_handler in event_handlers:\n                # Remove the event handler from the list of handlers\n                event_handlers.remove(event_handler)\n                deleted = True\n\n                # If there are no more handlers for the event, remove the event name from the registry\n                if not event_handlers:\n                    cls.__registry.pop(event_name)\n\n                # Log the unsubscription if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(\n                        action=\"Unsubscribed:\",\n                        msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                    )\n\n    # Return the flag indicating whether the event handler was deleted\n    return deleted\n
"},{"location":"api/event-linker/#pyventus.EventLinker.remove_event_handler","title":"remove_event_handler classmethod","text":"
remove_event_handler(event_handler: EventHandler) -> bool\n

Removes an event handler from all subscribed events. If there are no more handlers for a particular event, that event is also removed from the registry.

PARAMETER DESCRIPTION event_handler

The event handler to remove.

TYPE: EventHandler

RETURNS DESCRIPTION bool

True if the event handler was found and removed, False otherwise.

RAISES DESCRIPTION PyventusException

If the event_handler argument is None or invalid.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef remove_event_handler(cls, event_handler: EventHandler) -> bool:\n    \"\"\"\n    Removes an event handler from all subscribed events. If there are no more\n    handlers for a particular event, that event is also removed from the registry.\n    :param event_handler: The event handler to remove.\n    :return: `True` if the event handler was found and removed, `False` otherwise.\n    :raises PyventusException: If the `event_handler` argument is `None` or invalid.\n    \"\"\"\n    # Validate the event_handler argument\n    if event_handler is None:\n        raise PyventusException(\"The 'event_handler' argument cannot be None.\")\n    if not isinstance(event_handler, EventHandler):\n        raise PyventusException(\"The 'event_handler' argument must be an instance of the EventHandler class.\")\n\n    # A flag indicating if the event handler gets removed\n    deleted: bool = False\n\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # Iterate through each event and its associated handlers in the main registry\n        for event_name in list(cls.__registry.keys()):\n            # Get the list of event handlers for the event name, or an empty list if it doesn't exist\n            event_handlers = cls.__registry.get(event_name, [])\n\n            # Check if the event handler is present in the list of handlers for the event\n            if event_handler in event_handlers:\n                # Remove the event handler from the list of handlers\n                event_handlers.remove(event_handler)\n                deleted = True\n\n                # If there are no more handlers for the event, remove the event from the registry\n                if not event_handlers:\n                    cls.__registry.pop(event_name)\n\n                # Log the removal of the event handler if debug is enabled\n                if cls.__logger.debug_enabled:  # pragma: no cover\n                    cls.__logger.debug(\n                        action=\"Handler Removed:\",\n                        msg=f\"{event_handler} {StdOutColors.PURPLE}Event:{StdOutColors.DEFAULT} {event_name}\",\n                    )\n\n    # Return the flag indicating if the event handler was found and deleted\n    return deleted\n
"},{"location":"api/event-linker/#pyventus.EventLinker.remove_event","title":"remove_event classmethod","text":"
remove_event(event: SubscribableEventType) -> bool\n

Removes an event from the registry, including all its event handler subscriptions.

PARAMETER DESCRIPTION event

The event to remove.

TYPE: SubscribableEventType

RETURNS DESCRIPTION bool

True if the event was found and removed, False otherwise.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef remove_event(cls, event: SubscribableEventType) -> bool:\n    \"\"\"\n    Removes an event from the registry, including all its event handler subscriptions.\n    :param event: The event to remove.\n    :return: `True` if the event was found and removed, `False` otherwise.\n    \"\"\"\n    # Get the event name\n    event_name: str = cls.get_event_name(event=event)\n\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        # Check if the event name is present in the main registry\n        if event_name in cls.__registry:\n            # Remove the event from the registry\n            cls.__registry.pop(event_name)\n\n            # Log the removal of the event if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(action=\"Event Removed:\", msg=f\"{event_name}\")\n\n            # Return True to indicate successful removal\n            return True\n\n    return False\n
"},{"location":"api/event-linker/#pyventus.EventLinker.remove_all","title":"remove_all classmethod","text":"
remove_all() -> bool\n

Removes all events and their associated event handlers from the registry.

RETURNS DESCRIPTION bool

True if the events were found and removed, False otherwise.

Source code in pyventus/linkers/event_linker.py
@classmethod\ndef remove_all(cls) -> bool:\n    \"\"\"\n    Removes all events and their associated event handlers from the registry.\n    :return: `True` if the events were found and removed, `False` otherwise.\n    \"\"\"\n    # Acquire the lock to ensure exclusive access to the main registry\n    with cls.__thread_lock:\n        if cls.__registry:\n            # Clear the main registry\n            cls.__registry.clear()\n\n            # Log a debug message if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(msg=\"All events and handlers were successfully removed.\")\n\n            return True\n        else:\n            # Log a debug message if debug is enabled\n            if cls.__logger.debug_enabled:  # pragma: no cover\n                cls.__logger.debug(msg=\"The event registry is already empty.\")\n\n            return False\n
"},{"location":"api/emitters/","title":"EventEmitter class","text":"

Bases: ABC

An abstract base class for event emitters.

Notes:

  • This class defines a common interface for emitting events. It serves as a foundation for implementing custom event emitters with specific dispatch strategies. It is designed to handle string-named events with positional and keyword arguments, as well as instances of dataclass objects and Exceptions objects.

  • The main goal of this class is to decouple the event emission process from the underlying implementation. This loose coupling promotes flexibility, adaptability, and adheres to the Open-Closed principle, allowing custom event emitters to be implemented without affecting existing consumers.

Read more in the Pyventus docs for Event Emitter.

Source code in pyventus/emitters/event_emitter.py
class EventEmitter(ABC):\n    \"\"\"\n    An abstract base class for event emitters.\n\n    **Notes:**\n\n    -   This class defines a common interface for emitting events. It serves as a foundation for\n        implementing custom event emitters with specific dispatch strategies. It is designed to\n        handle `string-named` events with positional and keyword arguments, as well as instances\n        of `dataclass` objects and `Exceptions` objects.\n\n    -   The main goal of this class is to decouple the event emission process from the underlying\n        implementation. This loose coupling promotes flexibility, adaptability, and adheres to the\n        Open-Closed principle, allowing custom event emitters to be implemented without affecting\n        existing consumers.\n\n    ---\n    Read more in the\n    [Pyventus docs for Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/).\n    \"\"\"\n\n    @final\n    class EventEmission:\n        \"\"\"\n        Represents an event emission that has been triggered but whose propagation is not\n        yet complete. It provides a self-contained context for executing the event emission,\n        encapsulating both the event data and the associated event handlers.\n\n        This class acts as an isolated unit of work to asynchronously propagate the emission\n        of an event. When an event occurs, the `EventEmitter` class creates an `EventEmission`\n        instance, which is then processed by the `_process()` method to handle the event\n        propagation.\n        \"\"\"\n\n        # Event emission attributes\n        __slots__ = (\"_id\", \"_timestamp\", \"_debug\", \"_event\", \"_event_handlers\", \"_event_args\", \"_event_kwargs\")\n\n        @property\n        def id(self) -> str:\n            \"\"\"\n            Gets the unique identifier of the event emission.\n            :return: The unique identifier of the event emission.\n            \"\"\"\n            return self._id\n\n        @property\n        def timestamp(self) -> datetime:\n            \"\"\"\n            Gets the timestamp when the event emission was created.\n            :return: The timestamp when the event emission was created.\n            \"\"\"\n            return self._timestamp\n\n        @property\n        def event(self) -> str:\n            \"\"\"\n            Gets the name of the event being emitted.\n            :return: The name of the event.\n            \"\"\"\n            return self._event\n\n        def __init__(\n            self,\n            event: str,\n            event_handlers: List[EventHandler],\n            event_args: Tuple[Any, ...],\n            event_kwargs: Dict[str, Any],\n            debug: bool,\n        ) -> None:\n            \"\"\"\n            Initialize an instance of `EventEmission`.\n            :param event: The name of the event being emitted.\n            :param event_handlers: List of event handlers associated with the event.\n            :param event_args: Positional arguments to be passed to the event handlers.\n            :param event_kwargs: Keyword arguments to be passed to the event handlers.\n            :param debug: Indicates if debug mode is enabled.\n            :raises PyventusException: If `event_handlers` or `event` is empty.\n            \"\"\"\n            if not event_handlers:  # pragma: no cover\n                raise PyventusException(\"The 'event_handlers' argument cannot be empty.\")\n\n            if not event:  # pragma: no cover\n                raise PyventusException(\"The 'event' argument cannot be empty.\")\n\n            # Set the event emission metadata\n            self._id: str = str(uuid4())\n            self._timestamp: datetime = datetime.now()\n            self._debug: bool = debug\n\n            # Set the event emission properties\n            self._event: str = event\n            self._event_handlers: Tuple[EventHandler, ...] = tuple(event_handlers)\n            self._event_args: Tuple[Any, ...] = event_args\n            self._event_kwargs: Dict[str, Any] = event_kwargs\n\n        async def __call__(self) -> None:\n            \"\"\"\n            Execute the event handlers concurrently.\n            :return: None\n            \"\"\"\n            # Log the event execution if debug is enabled\n            if self._debug:  # pragma: no cover\n                StdOutLogger.debug(name=self.__class__.__name__, action=\"Running:\", msg=str(self))\n\n            # Execute the event handlers concurrently\n            await gather(\n                *[event_handler(*self._event_args, **self._event_kwargs) for event_handler in self._event_handlers],\n                return_exceptions=True,\n            )\n\n        def __str__(self) -> str:\n            \"\"\"\n            Returns a formatted string representation of the event emission.\n            :return: The formatted string representation of the event emission.\n            \"\"\"\n            return (\n                f\"ID: {self.id} | Timestamp: {self.timestamp.strftime('%Y-%m-%d %I:%M:%S %p')} | \"\n                f\"Event: {self.event} | Handlers: {len(self._event_handlers)}\"\n            )\n\n    def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n        \"\"\"\n        Initialize an instance of `EventEmitter`.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the subclass logger. If `None`,\n            it is determined based on the execution environment.\n        :raises PyventusException: If the `event_linker` argument is None.\n        \"\"\"\n        # Validate the event linker argument\n        if event_linker is None:\n            raise PyventusException(\"The 'event_linker' argument cannot be None.\")\n        if not issubclass(event_linker, EventLinker):\n            raise PyventusException(\"The 'event_linker' argument must be a subtype of the EventLinker class.\")\n\n        # Set the event_linker value\n        self._event_linker: Type[EventLinker] = event_linker\n        \"\"\"\n        Specifies the type of event linker to use for associating events with their \n        respective event handlers.\n        \"\"\"\n\n        self._logger: Logger = Logger(\n            name=self.__class__.__name__,\n            debug=debug if debug is not None else bool(gettrace() is not None),\n        )\n        \"\"\"\n        An instance of the logger used for logging events and debugging information.\n        \"\"\"\n\n    def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n        \"\"\"\n        Emits an event and triggers its associated event handlers.\n\n        **Notes:**\n\n        -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n            to the event handler as the first positional argument, even if you pass additional `*args`\n            or `**kwargs`.\n        -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n            they will also be triggered each time an event or exception is emitted.\n\n        :param event: The event to be emitted. It can be `str`, a `dataclass`\n            object, or an `Exception` object.\n        :param args: Positional arguments to be passed to the event handlers.\n        :param kwargs: Keyword arguments to be passed to the event handlers.\n        :return: None\n        \"\"\"\n        # Raise an exception if the event is None\n        if event is None:\n            raise PyventusException(\"The 'event' argument cannot be None.\")\n\n        # Raise an exception if the event is a type\n        if isinstance(event, type):\n            raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n        # Determine the event name\n        event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n        # Retrieve the event handlers associated with the event\n        event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n        # Sort the event handlers by timestamp\n        event_handlers.sort(key=lambda handler: handler.timestamp)\n\n        # Initialize the list of event handlers to be executed\n        pending_event_handlers: List[EventHandler] = []\n\n        # Iterate through each event handler\n        for event_handler in event_handlers:\n            # Check if the event handler is a one-time subscription\n            if event_handler.once:\n                # If the event handler is a one-time subscription, we attempt to remove it.\n                if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                    # If the removal is successful, it indicates that the handler has not\n                    # been processed before, so we add it to the pending list.\n                    pending_event_handlers.append(event_handler)\n            else:\n                pending_event_handlers.append(event_handler)\n\n        # Check if the pending list of event handlers is not empty\n        if len(pending_event_handlers) > 0:\n            # Create a new EventEmission instance\n            event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n                event=event_name,\n                event_handlers=pending_event_handlers,\n                event_args=args if isinstance(event, str) else (event, *args),\n                event_kwargs=kwargs,\n                debug=self._logger.debug_enabled,\n            )\n\n            # Log the event emission when debug is enabled\n            if self._logger.debug_enabled:  # pragma: no cover\n                self._logger.debug(\n                    action=\"Emitting Event:\",\n                    msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n                )\n\n            # Delegate the event emission processing to subclasses\n            self._process(event_emission)\n\n        # Log a debug message if there are no event handlers subscribed to the event\n        elif self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n            )\n\n    @abstractmethod\n    def _process(self, event_emission: EventEmission) -> None:\n        \"\"\"\n        Process the event emission execution. Subclasses must implement\n        this method to define the specific processing logic.\n\n        :param event_emission: The event emission to be processed.\n        :return: None\n        \"\"\"\n        pass\n

"},{"location":"api/emitters/#pyventus.EventEmitter-classes","title":"Classes","text":""},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission","title":"EventEmission","text":"

Represents an event emission that has been triggered but whose propagation is not yet complete. It provides a self-contained context for executing the event emission, encapsulating both the event data and the associated event handlers.

This class acts as an isolated unit of work to asynchronously propagate the emission of an event. When an event occurs, the EventEmitter class creates an EventEmission instance, which is then processed by the _process() method to handle the event propagation.

Source code in pyventus/emitters/event_emitter.py
@final\nclass EventEmission:\n    \"\"\"\n    Represents an event emission that has been triggered but whose propagation is not\n    yet complete. It provides a self-contained context for executing the event emission,\n    encapsulating both the event data and the associated event handlers.\n\n    This class acts as an isolated unit of work to asynchronously propagate the emission\n    of an event. When an event occurs, the `EventEmitter` class creates an `EventEmission`\n    instance, which is then processed by the `_process()` method to handle the event\n    propagation.\n    \"\"\"\n\n    # Event emission attributes\n    __slots__ = (\"_id\", \"_timestamp\", \"_debug\", \"_event\", \"_event_handlers\", \"_event_args\", \"_event_kwargs\")\n\n    @property\n    def id(self) -> str:\n        \"\"\"\n        Gets the unique identifier of the event emission.\n        :return: The unique identifier of the event emission.\n        \"\"\"\n        return self._id\n\n    @property\n    def timestamp(self) -> datetime:\n        \"\"\"\n        Gets the timestamp when the event emission was created.\n        :return: The timestamp when the event emission was created.\n        \"\"\"\n        return self._timestamp\n\n    @property\n    def event(self) -> str:\n        \"\"\"\n        Gets the name of the event being emitted.\n        :return: The name of the event.\n        \"\"\"\n        return self._event\n\n    def __init__(\n        self,\n        event: str,\n        event_handlers: List[EventHandler],\n        event_args: Tuple[Any, ...],\n        event_kwargs: Dict[str, Any],\n        debug: bool,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `EventEmission`.\n        :param event: The name of the event being emitted.\n        :param event_handlers: List of event handlers associated with the event.\n        :param event_args: Positional arguments to be passed to the event handlers.\n        :param event_kwargs: Keyword arguments to be passed to the event handlers.\n        :param debug: Indicates if debug mode is enabled.\n        :raises PyventusException: If `event_handlers` or `event` is empty.\n        \"\"\"\n        if not event_handlers:  # pragma: no cover\n            raise PyventusException(\"The 'event_handlers' argument cannot be empty.\")\n\n        if not event:  # pragma: no cover\n            raise PyventusException(\"The 'event' argument cannot be empty.\")\n\n        # Set the event emission metadata\n        self._id: str = str(uuid4())\n        self._timestamp: datetime = datetime.now()\n        self._debug: bool = debug\n\n        # Set the event emission properties\n        self._event: str = event\n        self._event_handlers: Tuple[EventHandler, ...] = tuple(event_handlers)\n        self._event_args: Tuple[Any, ...] = event_args\n        self._event_kwargs: Dict[str, Any] = event_kwargs\n\n    async def __call__(self) -> None:\n        \"\"\"\n        Execute the event handlers concurrently.\n        :return: None\n        \"\"\"\n        # Log the event execution if debug is enabled\n        if self._debug:  # pragma: no cover\n            StdOutLogger.debug(name=self.__class__.__name__, action=\"Running:\", msg=str(self))\n\n        # Execute the event handlers concurrently\n        await gather(\n            *[event_handler(*self._event_args, **self._event_kwargs) for event_handler in self._event_handlers],\n            return_exceptions=True,\n        )\n\n    def __str__(self) -> str:\n        \"\"\"\n        Returns a formatted string representation of the event emission.\n        :return: The formatted string representation of the event emission.\n        \"\"\"\n        return (\n            f\"ID: {self.id} | Timestamp: {self.timestamp.strftime('%Y-%m-%d %I:%M:%S %p')} | \"\n            f\"Event: {self.event} | Handlers: {len(self._event_handlers)}\"\n        )\n
"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission-attributes","title":"Attributes","text":""},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.id","title":"id property","text":"
id: str\n

Gets the unique identifier of the event emission.

RETURNS DESCRIPTION str

The unique identifier of the event emission.

"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.timestamp","title":"timestamp property","text":"
timestamp: datetime\n

Gets the timestamp when the event emission was created.

RETURNS DESCRIPTION datetime

The timestamp when the event emission was created.

"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.event","title":"event property","text":"
event: str\n

Gets the name of the event being emitted.

RETURNS DESCRIPTION str

The name of the event.

"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission-functions","title":"Functions","text":""},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.__init__","title":"__init__","text":"
__init__(event: str, event_handlers: List[EventHandler], event_args: Tuple[Any, ...], event_kwargs: Dict[str, Any], debug: bool) -> None\n

Initialize an instance of EventEmission.

PARAMETER DESCRIPTION event

The name of the event being emitted.

TYPE: str

event_handlers

List of event handlers associated with the event.

TYPE: List[EventHandler]

event_args

Positional arguments to be passed to the event handlers.

TYPE: Tuple[Any, ...]

event_kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Dict[str, Any]

debug

Indicates if debug mode is enabled.

TYPE: bool

RAISES DESCRIPTION PyventusException

If event_handlers or event is empty.

Source code in pyventus/emitters/event_emitter.py
def __init__(\n    self,\n    event: str,\n    event_handlers: List[EventHandler],\n    event_args: Tuple[Any, ...],\n    event_kwargs: Dict[str, Any],\n    debug: bool,\n) -> None:\n    \"\"\"\n    Initialize an instance of `EventEmission`.\n    :param event: The name of the event being emitted.\n    :param event_handlers: List of event handlers associated with the event.\n    :param event_args: Positional arguments to be passed to the event handlers.\n    :param event_kwargs: Keyword arguments to be passed to the event handlers.\n    :param debug: Indicates if debug mode is enabled.\n    :raises PyventusException: If `event_handlers` or `event` is empty.\n    \"\"\"\n    if not event_handlers:  # pragma: no cover\n        raise PyventusException(\"The 'event_handlers' argument cannot be empty.\")\n\n    if not event:  # pragma: no cover\n        raise PyventusException(\"The 'event' argument cannot be empty.\")\n\n    # Set the event emission metadata\n    self._id: str = str(uuid4())\n    self._timestamp: datetime = datetime.now()\n    self._debug: bool = debug\n\n    # Set the event emission properties\n    self._event: str = event\n    self._event_handlers: Tuple[EventHandler, ...] = tuple(event_handlers)\n    self._event_args: Tuple[Any, ...] = event_args\n    self._event_kwargs: Dict[str, Any] = event_kwargs\n
"},{"location":"api/emitters/#pyventus.EventEmitter.EventEmission.__call__","title":"__call__ async","text":"
__call__() -> None\n

Execute the event handlers concurrently.

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
async def __call__(self) -> None:\n    \"\"\"\n    Execute the event handlers concurrently.\n    :return: None\n    \"\"\"\n    # Log the event execution if debug is enabled\n    if self._debug:  # pragma: no cover\n        StdOutLogger.debug(name=self.__class__.__name__, action=\"Running:\", msg=str(self))\n\n    # Execute the event handlers concurrently\n    await gather(\n        *[event_handler(*self._event_args, **self._event_kwargs) for event_handler in self._event_handlers],\n        return_exceptions=True,\n    )\n
"},{"location":"api/emitters/#pyventus.EventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/#pyventus.EventEmitter.__init__","title":"__init__","text":"
__init__(event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of EventEmitter.

PARAMETER DESCRIPTION event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the subclass logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

RAISES DESCRIPTION PyventusException

If the event_linker argument is None.

Source code in pyventus/emitters/event_emitter.py
def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n    \"\"\"\n    Initialize an instance of `EventEmitter`.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the subclass logger. If `None`,\n        it is determined based on the execution environment.\n    :raises PyventusException: If the `event_linker` argument is None.\n    \"\"\"\n    # Validate the event linker argument\n    if event_linker is None:\n        raise PyventusException(\"The 'event_linker' argument cannot be None.\")\n    if not issubclass(event_linker, EventLinker):\n        raise PyventusException(\"The 'event_linker' argument must be a subtype of the EventLinker class.\")\n\n    # Set the event_linker value\n    self._event_linker: Type[EventLinker] = event_linker\n    \"\"\"\n    Specifies the type of event linker to use for associating events with their \n    respective event handlers.\n    \"\"\"\n\n    self._logger: Logger = Logger(\n        name=self.__class__.__name__,\n        debug=debug if debug is not None else bool(gettrace() is not None),\n    )\n    \"\"\"\n    An instance of the logger used for logging events and debugging information.\n    \"\"\"\n
"},{"location":"api/emitters/#pyventus.EventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/#pyventus.EventEmitter._process","title":"_process abstractmethod","text":"
_process(event_emission: EventEmission) -> None\n

Process the event emission execution. Subclasses must implement this method to define the specific processing logic.

PARAMETER DESCRIPTION event_emission

The event emission to be processed.

TYPE: EventEmission

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
@abstractmethod\ndef _process(self, event_emission: EventEmission) -> None:\n    \"\"\"\n    Process the event emission execution. Subclasses must implement\n    this method to define the specific processing logic.\n\n    :param event_emission: The event emission to be processed.\n    :return: None\n    \"\"\"\n    pass\n
"},{"location":"api/emitters/asyncio/","title":"AsyncIOEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the AsyncIO framework to handle the execution of event emissions.

Notes:

  • When used in an asynchronous context where an event loop is already running, the event emission is scheduled and processed on that existing loop. If the event loop is closed before all callbacks complete, any remaining scheduled callbacks will be canceled.

  • When used in a synchronous context where no event loop is active, a new event loop is started and subsequently closed by the asyncio.run() method. Within this loop, the event emission is executed. The loop then waits for all scheduled tasks to finish before closing.

Read more in the Pyventus docs for AsyncIO Event Emitter.

Source code in pyventus/emitters/asyncio/asyncio_event_emitter.py
class AsyncIOEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the AsyncIO framework to handle\n    the execution of event emissions.\n\n    **Notes:**\n\n    -   When used in an asynchronous context where an event loop is already running,\n        the event emission is scheduled and processed on that existing loop. If the\n        event loop is closed before all callbacks complete, any remaining scheduled\n        callbacks will be canceled.\n\n    -   When used in a synchronous context where no event loop is active, a new event\n        loop is started and subsequently closed by the `asyncio.run()` method. Within\n        this loop, the event emission is executed. The loop then waits for all\n        scheduled tasks to finish before closing.\n\n    ---\n    Read more in the\n    [Pyventus docs for AsyncIO Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/asyncio/).\n    \"\"\"\n\n    @property\n    def __is_loop_running(self) -> bool:\n        \"\"\"\n        Check if there is currently an active AsyncIO event loop.\n        :return: `True` if an event loop is running, `False` otherwise.\n        \"\"\"\n        try:\n            asyncio.get_running_loop()\n            return True\n        except RuntimeError:\n            return False\n\n    def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n        \"\"\"\n        Initialize an instance of `AsyncIOEventEmitter`.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Initialize the set of background futures\n        self._background_futures: Set[Future] = set()  # type: ignore[type-arg]\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Check if there is an active event loop\n        is_loop_running: bool = self.__is_loop_running\n\n        # Log the execution context, if debug mode is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(action=f\"Context:\", msg=f\"{'Async' if is_loop_running else 'Sync'}\")\n\n        if is_loop_running:\n            # Schedule the event emission in the running loop as a future\n            future = asyncio.ensure_future(event_emission())\n\n            # Remove the Future from the set of background futures after completion\n            future.add_done_callback(self._background_futures.remove)\n\n            # Add the Future to the set of background futures\n            self._background_futures.add(future)\n        else:\n            # Run the event emission in a blocking manner\n            asyncio.run(event_emission())\n

"},{"location":"api/emitters/asyncio/#pyventus.AsyncIOEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/asyncio/#pyventus.AsyncIOEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/asyncio/#pyventus.AsyncIOEventEmitter.__init__","title":"__init__","text":"
__init__(event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of AsyncIOEventEmitter.

PARAMETER DESCRIPTION event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/asyncio/asyncio_event_emitter.py
def __init__(self, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n    \"\"\"\n    Initialize an instance of `AsyncIOEventEmitter`.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Initialize the set of background futures\n    self._background_futures: Set[Future] = set()  # type: ignore[type-arg]\n
"},{"location":"api/emitters/celery/","title":"CeleryEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the Celery distributed system to handle the execution of event emissions.

Notes:

  • This class uses a Celery Queue instance to enqueue event emissions, which are subsequently executed by Celery workers. This approach provides a scalable and distributed method for handling the execution of event emissions.

Read more in the Pyventus docs for Celery Event Emitter.

Source code in pyventus/emitters/celery/celery_event_emitter.py
class CeleryEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the Celery distributed system\n    to handle the execution of event emissions.\n\n    **Notes:**\n\n    -   This class uses a Celery Queue instance to enqueue event emissions, which are\n        subsequently executed by Celery workers. This approach provides a scalable\n        and distributed method for handling the execution of event emissions.\n\n    ---\n    Read more in the\n    [Pyventus docs for Celery Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/celery/).\n    \"\"\"\n\n    class Queue:\n        \"\"\"A Celery event emitter queue used for enqueuing event emissions.\"\"\"\n\n        class Serializer:\n            \"\"\"An event emitter object serializer for Celery queues.\"\"\"\n\n            @staticmethod\n            def dumps(obj: EventEmitter.EventEmission) -> Any:\n                \"\"\"\n                Serializes the event emission object.\n                :param obj: The event emission object to be serialized.\n                :return: The serialized representation of the event emission object.\n                \"\"\"\n                return dumps(obj)  # pragma: no cover\n\n            @staticmethod\n            def loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n                \"\"\"\n                Deserializes the task payload back to the event emission object.\n                :param serialized_obj: The serialized object to be loaded.\n                :return: The deserialized event emission object.\n                \"\"\"\n                return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n\n        class _Payload(NamedTuple):\n            \"\"\"The Celery event emitter queue payload.\"\"\"\n\n            serialized_obj: bytes\n            \"\"\"Serialized event emission object.\"\"\"\n\n            obj_hash: bytes | None\n            \"\"\"Hash of the serialized event emission object.\"\"\"\n\n            @classmethod\n            def from_json(cls, **kwargs: Any) -> \"CeleryEventEmitter.Queue._Payload\":\n                \"\"\"\n                Creates a Payload instance from a JSON-compatible dictionary.\n                :param kwargs: JSON-compatible dictionary representing the payload.\n                :return: Payload instance.\n                :raises ValueError: If the JSON data is missing fields or contains extra keys.\n                \"\"\"\n                # Get the field names of the named tuple\n                tuple_fields: tuple[str, ...] = CeleryEventEmitter.Queue._Payload._fields\n\n                # Check if all expected fields are present\n                if not set(tuple_fields).issubset(kwargs.keys()):\n                    raise PyventusException(\"Missing fields in JSON data.\")\n\n                # Check for extra keys in the JSON data\n                extra_keys = set(kwargs.keys()) - set(tuple_fields)\n                if extra_keys:\n                    raise PyventusException(\"Extra keys in JSON data: {}\".format(extra_keys))\n\n                # Create the named tuple using the JSON data\n                return cls(**kwargs)\n\n            def to_json(self) -> Dict[str, Any]:\n                \"\"\"\n                Converts the payload to a JSON-compatible dictionary.\n                :return: JSON-compatible dictionary representing the payload.\n                \"\"\"\n                return self._asdict()\n\n        def __init__(\n            self,\n            celery: Celery,\n            name: str | None = None,\n            secret: str | None = None,\n            serializer: Type[Serializer] = Serializer,\n        ) -> None:\n            \"\"\"\n            Initialize an instance of `CeleryEventEmitter.Queue`.\n            :param celery: The Celery object used to enqueue and process event emissions.\n            :param name: The name of the queue where the event emission will be enqueued.\n                Default is None (task_default_queue).\n            :param secret: The secret key used for message authentication and integrity validation.\n                This key is used for hashing the event emission object and verifying its integrity.\n            :param serializer: The serializer class used for serializing and deserializing event\n                emission objects.\n            :raises PyventusException: If the Celery object is None, or the secret key is not None\n                and empty, or if the content type 'application/x-python-serialize' is not accepted.\n            \"\"\"\n            if celery is None:\n                raise PyventusException(\"The 'celery' argument cannot be None.\")\n            if not isinstance(celery, Celery):\n                raise PyventusException(\"The 'celery' argument must be an instance of the Celery class.\")\n\n            if secret is not None and len(secret) == 0:\n                raise PyventusException(\"The 'secret' argument cannot be empty.\")\n\n            if \"application/x-python-serialize\" not in celery.conf.accept_content:\n                raise PyventusException(\n                    \"Unsupported content type. \"\n                    \"'application/x-python-serialize' is not in the list of accepted content types.\"\n                )\n\n            # Set the Celery queue properties\n            self._celery: Celery = celery\n            self._name: str = self._celery.conf.task_default_queue if name is None else name\n            self._secret: bytes | None = secret.encode(\"utf-8\") if secret else None\n            self._digestmod: str | Callable[[], Any] | ModuleType = sha256  # The digest algorithm used for hashing\n            self._serializer: Type[CeleryEventEmitter.Queue.Serializer] = serializer\n\n            # Register the event processor method as a Celery task\n            self._celery.task(self._executor, name=f\"pyventus{self._executor.__name__}\", queue=self._name)\n\n        def enqueue(self, event_emission: EventEmitter.EventEmission) -> None:\n            \"\"\"\n            Enqueues an event emission object for asynchronous processing in Celery.\n\n            This method takes an `EventEmission` object and enqueues it for asynchronous\n            execution by Celery workers. If a secret key is provided during initialization,\n            the event emission object is first serialized, and its hash is calculated using\n            the secret key. This hash is used to verify the integrity of the event emission\n            object during execution.\n\n            :param event_emission: The event emission object to be enqueued for asynchronous execution.\n            :return: None\n            \"\"\"\n            # Serialize the event emission object\n            serialized_obj: Any = self._serializer.dumps(event_emission)\n\n            # Calculate the hash of the serialized object if a secret key was provided\n            obj_hash: Any | None = None\n            if self._secret:  # pragma: no cover\n                obj_hash = hmac.new(key=self._secret, msg=serialized_obj, digestmod=self._digestmod).digest()\n\n            # Create a payload with the serialized event emission instance and its hash\n            payload = CeleryEventEmitter.Queue._Payload(\n                serialized_obj=serialized_obj,\n                obj_hash=obj_hash,\n            )\n\n            # Send the event emission object to Celery for asynchronous execution\n            self._celery.send_task(\n                f\"pyventus{self._executor.__name__}\",\n                kwargs=payload.to_json(),\n                queue=self._name,\n            )\n\n        def _executor(self, **kwargs: Any) -> None:\n            \"\"\"\n            Process the enqueued event emission object.\n\n            This method serves as the Celery task responsible\n            for processing the enqueued event emission.\n\n            :param kwargs: The JSON-compatible dictionary representing the payload.\n            :return: None\n            \"\"\"\n            # Create a Payload instance from the JSON data\n            payload = CeleryEventEmitter.Queue._Payload.from_json(**kwargs)\n\n            # Check payload\n            if self._secret:  # pragma: no cover\n                if not payload.obj_hash:\n                    raise PyventusException(\"Invalid payload structure.\")\n\n                # Verify the integrity of the payload\n                obj_hash: bytes = hmac.new(\n                    key=self._secret, msg=payload.serialized_obj, digestmod=self._digestmod\n                ).digest()\n\n                # Compare the calculated hash with the provided payload hash\n                if not hmac.compare_digest(payload.obj_hash, obj_hash):\n                    raise PyventusException(\"Payload integrity verification failed.\")\n            elif payload.obj_hash:  # pragma: no cover\n                raise PyventusException(\"Unexpected payload structure.\")\n\n            # Deserialize the event emission object\n            event_emission = self._serializer.loads(payload.serialized_obj)\n\n            # Check if the deserialized event emission object is valid\n            if event_emission is None:  # pragma: no cover\n                raise PyventusException(\"Failed to deserialize the event emission object.\")\n\n            # Run the event emission\n            asyncio.run(event_emission())\n\n    def __init__(self, queue: Queue, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n        \"\"\"\n        Initialize an instance of `CeleryEventEmitter`.\n        :param queue: The queue used for enqueuing event emissions in the Celery event emitter.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Validate the queue argument\n        if queue is None:\n            raise PyventusException(\"The 'queue' argument cannot be None\")\n        if not isinstance(queue, CeleryEventEmitter.Queue):\n            raise PyventusException(\"The 'queue' argument must be an instance of the CeleryEventEmitter.Queue class.\")\n\n        # Store the queue object\n        self._queue: CeleryEventEmitter.Queue = queue\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Add the event emission object to the Celery queue for asynchronous execution\n        self._queue.enqueue(event_emission=event_emission)\n

"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter-classes","title":"Classes","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue","title":"Queue","text":"

A Celery event emitter queue used for enqueuing event emissions.

Source code in pyventus/emitters/celery/celery_event_emitter.py
class Queue:\n    \"\"\"A Celery event emitter queue used for enqueuing event emissions.\"\"\"\n\n    class Serializer:\n        \"\"\"An event emitter object serializer for Celery queues.\"\"\"\n\n        @staticmethod\n        def dumps(obj: EventEmitter.EventEmission) -> Any:\n            \"\"\"\n            Serializes the event emission object.\n            :param obj: The event emission object to be serialized.\n            :return: The serialized representation of the event emission object.\n            \"\"\"\n            return dumps(obj)  # pragma: no cover\n\n        @staticmethod\n        def loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n            \"\"\"\n            Deserializes the task payload back to the event emission object.\n            :param serialized_obj: The serialized object to be loaded.\n            :return: The deserialized event emission object.\n            \"\"\"\n            return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n\n    class _Payload(NamedTuple):\n        \"\"\"The Celery event emitter queue payload.\"\"\"\n\n        serialized_obj: bytes\n        \"\"\"Serialized event emission object.\"\"\"\n\n        obj_hash: bytes | None\n        \"\"\"Hash of the serialized event emission object.\"\"\"\n\n        @classmethod\n        def from_json(cls, **kwargs: Any) -> \"CeleryEventEmitter.Queue._Payload\":\n            \"\"\"\n            Creates a Payload instance from a JSON-compatible dictionary.\n            :param kwargs: JSON-compatible dictionary representing the payload.\n            :return: Payload instance.\n            :raises ValueError: If the JSON data is missing fields or contains extra keys.\n            \"\"\"\n            # Get the field names of the named tuple\n            tuple_fields: tuple[str, ...] = CeleryEventEmitter.Queue._Payload._fields\n\n            # Check if all expected fields are present\n            if not set(tuple_fields).issubset(kwargs.keys()):\n                raise PyventusException(\"Missing fields in JSON data.\")\n\n            # Check for extra keys in the JSON data\n            extra_keys = set(kwargs.keys()) - set(tuple_fields)\n            if extra_keys:\n                raise PyventusException(\"Extra keys in JSON data: {}\".format(extra_keys))\n\n            # Create the named tuple using the JSON data\n            return cls(**kwargs)\n\n        def to_json(self) -> Dict[str, Any]:\n            \"\"\"\n            Converts the payload to a JSON-compatible dictionary.\n            :return: JSON-compatible dictionary representing the payload.\n            \"\"\"\n            return self._asdict()\n\n    def __init__(\n        self,\n        celery: Celery,\n        name: str | None = None,\n        secret: str | None = None,\n        serializer: Type[Serializer] = Serializer,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `CeleryEventEmitter.Queue`.\n        :param celery: The Celery object used to enqueue and process event emissions.\n        :param name: The name of the queue where the event emission will be enqueued.\n            Default is None (task_default_queue).\n        :param secret: The secret key used for message authentication and integrity validation.\n            This key is used for hashing the event emission object and verifying its integrity.\n        :param serializer: The serializer class used for serializing and deserializing event\n            emission objects.\n        :raises PyventusException: If the Celery object is None, or the secret key is not None\n            and empty, or if the content type 'application/x-python-serialize' is not accepted.\n        \"\"\"\n        if celery is None:\n            raise PyventusException(\"The 'celery' argument cannot be None.\")\n        if not isinstance(celery, Celery):\n            raise PyventusException(\"The 'celery' argument must be an instance of the Celery class.\")\n\n        if secret is not None and len(secret) == 0:\n            raise PyventusException(\"The 'secret' argument cannot be empty.\")\n\n        if \"application/x-python-serialize\" not in celery.conf.accept_content:\n            raise PyventusException(\n                \"Unsupported content type. \"\n                \"'application/x-python-serialize' is not in the list of accepted content types.\"\n            )\n\n        # Set the Celery queue properties\n        self._celery: Celery = celery\n        self._name: str = self._celery.conf.task_default_queue if name is None else name\n        self._secret: bytes | None = secret.encode(\"utf-8\") if secret else None\n        self._digestmod: str | Callable[[], Any] | ModuleType = sha256  # The digest algorithm used for hashing\n        self._serializer: Type[CeleryEventEmitter.Queue.Serializer] = serializer\n\n        # Register the event processor method as a Celery task\n        self._celery.task(self._executor, name=f\"pyventus{self._executor.__name__}\", queue=self._name)\n\n    def enqueue(self, event_emission: EventEmitter.EventEmission) -> None:\n        \"\"\"\n        Enqueues an event emission object for asynchronous processing in Celery.\n\n        This method takes an `EventEmission` object and enqueues it for asynchronous\n        execution by Celery workers. If a secret key is provided during initialization,\n        the event emission object is first serialized, and its hash is calculated using\n        the secret key. This hash is used to verify the integrity of the event emission\n        object during execution.\n\n        :param event_emission: The event emission object to be enqueued for asynchronous execution.\n        :return: None\n        \"\"\"\n        # Serialize the event emission object\n        serialized_obj: Any = self._serializer.dumps(event_emission)\n\n        # Calculate the hash of the serialized object if a secret key was provided\n        obj_hash: Any | None = None\n        if self._secret:  # pragma: no cover\n            obj_hash = hmac.new(key=self._secret, msg=serialized_obj, digestmod=self._digestmod).digest()\n\n        # Create a payload with the serialized event emission instance and its hash\n        payload = CeleryEventEmitter.Queue._Payload(\n            serialized_obj=serialized_obj,\n            obj_hash=obj_hash,\n        )\n\n        # Send the event emission object to Celery for asynchronous execution\n        self._celery.send_task(\n            f\"pyventus{self._executor.__name__}\",\n            kwargs=payload.to_json(),\n            queue=self._name,\n        )\n\n    def _executor(self, **kwargs: Any) -> None:\n        \"\"\"\n        Process the enqueued event emission object.\n\n        This method serves as the Celery task responsible\n        for processing the enqueued event emission.\n\n        :param kwargs: The JSON-compatible dictionary representing the payload.\n        :return: None\n        \"\"\"\n        # Create a Payload instance from the JSON data\n        payload = CeleryEventEmitter.Queue._Payload.from_json(**kwargs)\n\n        # Check payload\n        if self._secret:  # pragma: no cover\n            if not payload.obj_hash:\n                raise PyventusException(\"Invalid payload structure.\")\n\n            # Verify the integrity of the payload\n            obj_hash: bytes = hmac.new(\n                key=self._secret, msg=payload.serialized_obj, digestmod=self._digestmod\n            ).digest()\n\n            # Compare the calculated hash with the provided payload hash\n            if not hmac.compare_digest(payload.obj_hash, obj_hash):\n                raise PyventusException(\"Payload integrity verification failed.\")\n        elif payload.obj_hash:  # pragma: no cover\n            raise PyventusException(\"Unexpected payload structure.\")\n\n        # Deserialize the event emission object\n        event_emission = self._serializer.loads(payload.serialized_obj)\n\n        # Check if the deserialized event emission object is valid\n        if event_emission is None:  # pragma: no cover\n            raise PyventusException(\"Failed to deserialize the event emission object.\")\n\n        # Run the event emission\n        asyncio.run(event_emission())\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue-classes","title":"Classes","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.Serializer","title":"Serializer","text":"

An event emitter object serializer for Celery queues.

Source code in pyventus/emitters/celery/celery_event_emitter.py
class Serializer:\n    \"\"\"An event emitter object serializer for Celery queues.\"\"\"\n\n    @staticmethod\n    def dumps(obj: EventEmitter.EventEmission) -> Any:\n        \"\"\"\n        Serializes the event emission object.\n        :param obj: The event emission object to be serialized.\n        :return: The serialized representation of the event emission object.\n        \"\"\"\n        return dumps(obj)  # pragma: no cover\n\n    @staticmethod\n    def loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n        \"\"\"\n        Deserializes the task payload back to the event emission object.\n        :param serialized_obj: The serialized object to be loaded.\n        :return: The deserialized event emission object.\n        \"\"\"\n        return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.Serializer-functions","title":"Functions","text":"dumps staticmethod \u00b6
dumps(obj: EventEmission) -> Any\n

Serializes the event emission object.

PARAMETER DESCRIPTION obj

The event emission object to be serialized.

TYPE: EventEmission

RETURNS DESCRIPTION Any

The serialized representation of the event emission object.

Source code in pyventus/emitters/celery/celery_event_emitter.py
@staticmethod\ndef dumps(obj: EventEmitter.EventEmission) -> Any:\n    \"\"\"\n    Serializes the event emission object.\n    :param obj: The event emission object to be serialized.\n    :return: The serialized representation of the event emission object.\n    \"\"\"\n    return dumps(obj)  # pragma: no cover\n
loads staticmethod \u00b6
loads(serialized_obj: Any) -> EventEmission\n

Deserializes the task payload back to the event emission object.

PARAMETER DESCRIPTION serialized_obj

The serialized object to be loaded.

TYPE: Any

RETURNS DESCRIPTION EventEmission

The deserialized event emission object.

Source code in pyventus/emitters/celery/celery_event_emitter.py
@staticmethod\ndef loads(serialized_obj: Any) -> EventEmitter.EventEmission:\n    \"\"\"\n    Deserializes the task payload back to the event emission object.\n    :param serialized_obj: The serialized object to be loaded.\n    :return: The deserialized event emission object.\n    \"\"\"\n    return cast(EventEmitter.EventEmission, loads(serialized_obj))  # pragma: no cover\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue-functions","title":"Functions","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.__init__","title":"__init__","text":"
__init__(celery: Celery, name: str | None = None, secret: str | None = None, serializer: Type[Serializer] = Serializer) -> None\n

Initialize an instance of CeleryEventEmitter.Queue.

PARAMETER DESCRIPTION celery

The Celery object used to enqueue and process event emissions.

TYPE: Celery

name

The name of the queue where the event emission will be enqueued. Default is None (task_default_queue).

TYPE: str | None DEFAULT: None

secret

The secret key used for message authentication and integrity validation. This key is used for hashing the event emission object and verifying its integrity.

TYPE: str | None DEFAULT: None

serializer

The serializer class used for serializing and deserializing event emission objects.

TYPE: Type[Serializer] DEFAULT: Serializer

RAISES DESCRIPTION PyventusException

If the Celery object is None, or the secret key is not None and empty, or if the content type 'application/x-python-serialize' is not accepted.

Source code in pyventus/emitters/celery/celery_event_emitter.py
def __init__(\n    self,\n    celery: Celery,\n    name: str | None = None,\n    secret: str | None = None,\n    serializer: Type[Serializer] = Serializer,\n) -> None:\n    \"\"\"\n    Initialize an instance of `CeleryEventEmitter.Queue`.\n    :param celery: The Celery object used to enqueue and process event emissions.\n    :param name: The name of the queue where the event emission will be enqueued.\n        Default is None (task_default_queue).\n    :param secret: The secret key used for message authentication and integrity validation.\n        This key is used for hashing the event emission object and verifying its integrity.\n    :param serializer: The serializer class used for serializing and deserializing event\n        emission objects.\n    :raises PyventusException: If the Celery object is None, or the secret key is not None\n        and empty, or if the content type 'application/x-python-serialize' is not accepted.\n    \"\"\"\n    if celery is None:\n        raise PyventusException(\"The 'celery' argument cannot be None.\")\n    if not isinstance(celery, Celery):\n        raise PyventusException(\"The 'celery' argument must be an instance of the Celery class.\")\n\n    if secret is not None and len(secret) == 0:\n        raise PyventusException(\"The 'secret' argument cannot be empty.\")\n\n    if \"application/x-python-serialize\" not in celery.conf.accept_content:\n        raise PyventusException(\n            \"Unsupported content type. \"\n            \"'application/x-python-serialize' is not in the list of accepted content types.\"\n        )\n\n    # Set the Celery queue properties\n    self._celery: Celery = celery\n    self._name: str = self._celery.conf.task_default_queue if name is None else name\n    self._secret: bytes | None = secret.encode(\"utf-8\") if secret else None\n    self._digestmod: str | Callable[[], Any] | ModuleType = sha256  # The digest algorithm used for hashing\n    self._serializer: Type[CeleryEventEmitter.Queue.Serializer] = serializer\n\n    # Register the event processor method as a Celery task\n    self._celery.task(self._executor, name=f\"pyventus{self._executor.__name__}\", queue=self._name)\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.Queue.enqueue","title":"enqueue","text":"
enqueue(event_emission: EventEmission) -> None\n

Enqueues an event emission object for asynchronous processing in Celery.

This method takes an EventEmission object and enqueues it for asynchronous execution by Celery workers. If a secret key is provided during initialization, the event emission object is first serialized, and its hash is calculated using the secret key. This hash is used to verify the integrity of the event emission object during execution.

PARAMETER DESCRIPTION event_emission

The event emission object to be enqueued for asynchronous execution.

TYPE: EventEmission

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/celery/celery_event_emitter.py
def enqueue(self, event_emission: EventEmitter.EventEmission) -> None:\n    \"\"\"\n    Enqueues an event emission object for asynchronous processing in Celery.\n\n    This method takes an `EventEmission` object and enqueues it for asynchronous\n    execution by Celery workers. If a secret key is provided during initialization,\n    the event emission object is first serialized, and its hash is calculated using\n    the secret key. This hash is used to verify the integrity of the event emission\n    object during execution.\n\n    :param event_emission: The event emission object to be enqueued for asynchronous execution.\n    :return: None\n    \"\"\"\n    # Serialize the event emission object\n    serialized_obj: Any = self._serializer.dumps(event_emission)\n\n    # Calculate the hash of the serialized object if a secret key was provided\n    obj_hash: Any | None = None\n    if self._secret:  # pragma: no cover\n        obj_hash = hmac.new(key=self._secret, msg=serialized_obj, digestmod=self._digestmod).digest()\n\n    # Create a payload with the serialized event emission instance and its hash\n    payload = CeleryEventEmitter.Queue._Payload(\n        serialized_obj=serialized_obj,\n        obj_hash=obj_hash,\n    )\n\n    # Send the event emission object to Celery for asynchronous execution\n    self._celery.send_task(\n        f\"pyventus{self._executor.__name__}\",\n        kwargs=payload.to_json(),\n        queue=self._name,\n    )\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/celery/#pyventus.emitters.celery.CeleryEventEmitter.__init__","title":"__init__","text":"
__init__(queue: Queue, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of CeleryEventEmitter.

PARAMETER DESCRIPTION queue

The queue used for enqueuing event emissions in the Celery event emitter.

TYPE: Queue

event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/celery/celery_event_emitter.py
def __init__(self, queue: Queue, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None:\n    \"\"\"\n    Initialize an instance of `CeleryEventEmitter`.\n    :param queue: The queue used for enqueuing event emissions in the Celery event emitter.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Validate the queue argument\n    if queue is None:\n        raise PyventusException(\"The 'queue' argument cannot be None\")\n    if not isinstance(queue, CeleryEventEmitter.Queue):\n        raise PyventusException(\"The 'queue' argument must be an instance of the CeleryEventEmitter.Queue class.\")\n\n    # Store the queue object\n    self._queue: CeleryEventEmitter.Queue = queue\n
"},{"location":"api/emitters/executor/","title":"ExecutorEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the concurrent.futures Executor base class to handle the execution of event emissions. It can work with either ThreadPoolExecutor for thread-based execution or ProcessPoolExecutor for process-based execution.

Notes:

  • When using this event emitter, it is important to properly manage the underlying Executor. Once you have finished emitting events, call the shutdown() method to signal the executor to free any resources for pending futures. You can avoid the need to call this method explicitly by using the with statement, which will automatically shut down the Executor (waiting as if Executor.shutdown() were called with wait set to True).

Read more in the Pyventus docs for Executor Event Emitter.

Source code in pyventus/emitters/executor/executor_event_emitter.py
class ExecutorEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the `concurrent.futures` Executor base class to\n    handle the execution of event emissions. It can work with either `ThreadPoolExecutor`\n    for thread-based execution or `ProcessPoolExecutor` for process-based execution.\n\n    **Notes:**\n\n    -   When using this event emitter, it is important to properly manage the underlying `Executor`.\n        Once you have finished emitting events, call the `shutdown()` method to signal the executor to\n        free any resources for pending futures. You can avoid the need to call this method explicitly\n        by using the `with` statement, which will automatically shut down the `Executor` (waiting as\n        if `Executor.shutdown()` were called with `wait` set to `True`).\n\n    ---\n    Read more in the\n    [Pyventus docs for Executor Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/executor/).\n    \"\"\"\n\n    @staticmethod\n    def _callback(event_emission: EventEmitter.EventEmission) -> None:\n        \"\"\"\n        This method is used as the callback function for the executor\n        to process the event emission.\n        :param event_emission: The event emission to be executed.\n        :return: None\n        \"\"\"\n        asyncio.run(event_emission())\n\n    def __init__(\n        self,\n        executor: Executor = ThreadPoolExecutor(),\n        event_linker: Type[EventLinker] = EventLinker,\n        debug: bool | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `ExecutorEventEmitter`.\n        :param executor: The executor object used to handle the execution of event\n            emissions. Defaults to `ThreadPoolExecutor()`.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Validate the executor argument\n        if executor is None:\n            raise PyventusException(\"The 'executor' argument cannot be None.\")\n        if not isinstance(executor, Executor):\n            raise PyventusException(\"The 'executor' argument must be an instance of the Executor class.\")\n\n        # Set the executor object reference\n        self._executor: Executor = executor\n\n    def __enter__(self) -> \"ExecutorEventEmitter\":\n        \"\"\"\n        Returns the current instance of `ExecutorEventEmitter` for context management.\n        :return: The current instance of `ExecutorEventEmitter`.\n        \"\"\"\n        return self\n\n    def __exit__(\n        self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n    ) -> None:\n        \"\"\"\n        Cleans up the executor resources when exiting the context.\n        :param exc_type: The exception type, if any.\n        :param exc_val: The exception value, if any.\n        :param exc_tb: The traceback information, if any.\n        :return: None\n        \"\"\"\n        self.shutdown(wait=True)\n\n    def shutdown(self, wait: bool = True, cancel_futures: bool = False) -> None:\n        \"\"\"\n        Shuts down the executor and frees any resources it is using.\n        :param wait: A boolean indicating whether to wait for the currently pending futures\n            to complete before shutting down.\n        :param cancel_futures: A boolean indicating whether to cancel any pending futures.\n        :return: None\n        \"\"\"\n        self._executor.shutdown(wait=wait, cancel_futures=cancel_futures)\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Submit the event emission to the executor\n        self._executor.submit(ExecutorEventEmitter._callback, event_emission)\n

"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.__init__","title":"__init__","text":"
__init__(executor: Executor = ThreadPoolExecutor(), event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of ExecutorEventEmitter.

PARAMETER DESCRIPTION executor

The executor object used to handle the execution of event emissions. Defaults to ThreadPoolExecutor().

TYPE: Executor DEFAULT: ThreadPoolExecutor()

event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/executor/executor_event_emitter.py
def __init__(\n    self,\n    executor: Executor = ThreadPoolExecutor(),\n    event_linker: Type[EventLinker] = EventLinker,\n    debug: bool | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `ExecutorEventEmitter`.\n    :param executor: The executor object used to handle the execution of event\n        emissions. Defaults to `ThreadPoolExecutor()`.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Validate the executor argument\n    if executor is None:\n        raise PyventusException(\"The 'executor' argument cannot be None.\")\n    if not isinstance(executor, Executor):\n        raise PyventusException(\"The 'executor' argument must be an instance of the Executor class.\")\n\n    # Set the executor object reference\n    self._executor: Executor = executor\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.__enter__","title":"__enter__","text":"
__enter__() -> ExecutorEventEmitter\n

Returns the current instance of ExecutorEventEmitter for context management.

RETURNS DESCRIPTION ExecutorEventEmitter

The current instance of ExecutorEventEmitter.

Source code in pyventus/emitters/executor/executor_event_emitter.py
def __enter__(self) -> \"ExecutorEventEmitter\":\n    \"\"\"\n    Returns the current instance of `ExecutorEventEmitter` for context management.\n    :return: The current instance of `ExecutorEventEmitter`.\n    \"\"\"\n    return self\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.__exit__","title":"__exit__","text":"
__exit__(exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) -> None\n

Cleans up the executor resources when exiting the context.

PARAMETER DESCRIPTION exc_type

The exception type, if any.

TYPE: Type[BaseException] | None

exc_val

The exception value, if any.

TYPE: BaseException | None

exc_tb

The traceback information, if any.

TYPE: TracebackType | None

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/executor/executor_event_emitter.py
def __exit__(\n    self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None\n) -> None:\n    \"\"\"\n    Cleans up the executor resources when exiting the context.\n    :param exc_type: The exception type, if any.\n    :param exc_val: The exception value, if any.\n    :param exc_tb: The traceback information, if any.\n    :return: None\n    \"\"\"\n    self.shutdown(wait=True)\n
"},{"location":"api/emitters/executor/#pyventus.ExecutorEventEmitter.shutdown","title":"shutdown","text":"
shutdown(wait: bool = True, cancel_futures: bool = False) -> None\n

Shuts down the executor and frees any resources it is using.

PARAMETER DESCRIPTION wait

A boolean indicating whether to wait for the currently pending futures to complete before shutting down.

TYPE: bool DEFAULT: True

cancel_futures

A boolean indicating whether to cancel any pending futures.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/executor/executor_event_emitter.py
def shutdown(self, wait: bool = True, cancel_futures: bool = False) -> None:\n    \"\"\"\n    Shuts down the executor and frees any resources it is using.\n    :param wait: A boolean indicating whether to wait for the currently pending futures\n        to complete before shutting down.\n    :param cancel_futures: A boolean indicating whether to cancel any pending futures.\n    :return: None\n    \"\"\"\n    self._executor.shutdown(wait=wait, cancel_futures=cancel_futures)\n
"},{"location":"api/emitters/fastapi/","title":"FastAPIEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes FastAPI's BackgroundTasks system to handle the execution of event emissions.

Notes:

  • It provides a convenient way to incorporate event-driven functionality into FastAPI apps.
  • This class offers a powerful mechanism for implementing asynchronous and decoupled operations in FastAPI, such as asynchronously sending emails in an event-driven manner.

Read more in the Pyventus docs for FastAPI Event Emitter.

Source code in pyventus/emitters/fastapi/fastapi_event_emitter.py
class FastAPIEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes FastAPI's BackgroundTasks system\n    to handle the execution of event emissions.\n\n    **Notes:**\n\n    -   It provides a convenient way to incorporate event-driven functionality\n        into FastAPI apps.\n    -   This class offers a powerful mechanism for implementing asynchronous\n        and decoupled operations in FastAPI, such as asynchronously sending\n        emails in an event-driven manner.\n\n    ---\n    Read more in the\n    [Pyventus docs for FastAPI Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/fastapi/).\n    \"\"\"\n\n    @classmethod\n    def options(\n        cls, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None\n    ) -> Callable[[BackgroundTasks], \"FastAPIEventEmitter\"]:\n        \"\"\"\n        Returns a decorator that allows you to configure the `FastAPIEventEmitter` class\n        when using FastAPI's `Depends` method.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        :return: A decorator that can be used with the `Depends` method.\n        \"\"\"\n\n        def wrapper(background_tasks: BackgroundTasks) -> \"FastAPIEventEmitter\":\n            \"\"\"\n            A decorator wrapper function that configures the `FastAPIEventEmitter` class with\n            the provided options.\n            :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n                the execution of event emissions.\n            :return: An instance of `FastAPIEventEmitter` configured with the specified options.\n            \"\"\"\n            return cls(background_tasks=background_tasks, event_linker=event_linker, debug=debug)\n\n        return wrapper\n\n    def __init__(\n        self,\n        background_tasks: BackgroundTasks,\n        event_linker: Type[EventLinker] = EventLinker,\n        debug: bool | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `FastAPIEventEmitter`.\n        :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n            the execution of event emissions.\n        :param event_linker: Specifies the type of event linker to use for associating\n            events with their respective event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Check if the provided background_tasks object is valid\n        if background_tasks is None:\n            raise PyventusException(\"The 'background_tasks' argument cannot be None.\")\n        if not isinstance(background_tasks, BackgroundTasks):\n            raise PyventusException(\"The 'background_tasks' argument must be an instance of the BackgroundTasks class.\")\n\n        # Set the background tasks\n        self._background_tasks: BackgroundTasks = background_tasks\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Submit the event emission to the background tasks\n        self._background_tasks.add_task(event_emission)\n

"},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter.options","title":"options classmethod","text":"
options(event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> Callable[[BackgroundTasks], FastAPIEventEmitter]\n

Returns a decorator that allows you to configure the FastAPIEventEmitter class when using FastAPI's Depends method.

PARAMETER DESCRIPTION event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

RETURNS DESCRIPTION Callable[[BackgroundTasks], FastAPIEventEmitter]

A decorator that can be used with the Depends method.

Source code in pyventus/emitters/fastapi/fastapi_event_emitter.py
@classmethod\ndef options(\n    cls, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None\n) -> Callable[[BackgroundTasks], \"FastAPIEventEmitter\"]:\n    \"\"\"\n    Returns a decorator that allows you to configure the `FastAPIEventEmitter` class\n    when using FastAPI's `Depends` method.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    :return: A decorator that can be used with the `Depends` method.\n    \"\"\"\n\n    def wrapper(background_tasks: BackgroundTasks) -> \"FastAPIEventEmitter\":\n        \"\"\"\n        A decorator wrapper function that configures the `FastAPIEventEmitter` class with\n        the provided options.\n        :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n            the execution of event emissions.\n        :return: An instance of `FastAPIEventEmitter` configured with the specified options.\n        \"\"\"\n        return cls(background_tasks=background_tasks, event_linker=event_linker, debug=debug)\n\n    return wrapper\n
"},{"location":"api/emitters/fastapi/#pyventus.emitters.fastapi.FastAPIEventEmitter.__init__","title":"__init__","text":"
__init__(background_tasks: BackgroundTasks, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of FastAPIEventEmitter.

PARAMETER DESCRIPTION background_tasks

The FastAPI BackgroundTasks object used to handle the execution of event emissions.

TYPE: BackgroundTasks

event_linker

Specifies the type of event linker to use for associating events with their respective event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/fastapi/fastapi_event_emitter.py
def __init__(\n    self,\n    background_tasks: BackgroundTasks,\n    event_linker: Type[EventLinker] = EventLinker,\n    debug: bool | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `FastAPIEventEmitter`.\n    :param background_tasks: The FastAPI `BackgroundTasks` object used to handle\n        the execution of event emissions.\n    :param event_linker: Specifies the type of event linker to use for associating\n        events with their respective event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Check if the provided background_tasks object is valid\n    if background_tasks is None:\n        raise PyventusException(\"The 'background_tasks' argument cannot be None.\")\n    if not isinstance(background_tasks, BackgroundTasks):\n        raise PyventusException(\"The 'background_tasks' argument must be an instance of the BackgroundTasks class.\")\n\n    # Set the background tasks\n    self._background_tasks: BackgroundTasks = background_tasks\n
"},{"location":"api/emitters/rq/","title":"RQEventEmitter class","text":"

Bases: EventEmitter

An event emitter subclass that utilizes the Redis Queue system to handle the execution of event emissions.

Notes:

  • This class uses a Redis Queue instance to enqueue event emissions, which are subsequently executed by Redis Queue workers. This approach provides a scalable and distributed method for handling the execution of event emissions.

Read more in the Pyventus docs for RQ Event Emitter.

Source code in pyventus/emitters/rq/rq_event_emitter.py
class RQEventEmitter(EventEmitter):\n    \"\"\"\n    An event emitter subclass that utilizes the Redis Queue system to handle the\n    execution of event emissions.\n\n    **Notes:**\n\n    -   This class uses a Redis Queue instance to enqueue event emissions, which are\n        subsequently executed by Redis Queue workers. This approach provides a scalable\n        and distributed method for handling the execution of event emissions.\n\n    ---\n    Read more in the\n    [Pyventus docs for RQ Event Emitter](https://mdapena.github.io/pyventus/tutorials/emitters/rq/).\n    \"\"\"\n\n    def __init__(\n        self,\n        queue: Queue,\n        options: Dict[str, Any] | None = None,\n        event_linker: Type[EventLinker] = EventLinker,\n        debug: bool | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize an instance of `RQEventEmitter`.\n        :param queue: The Redis queue for enqueuing event handlers.\n        :param options: Additional options for the RQ package enqueueing method.\n            Defaults to an empty dictionary.\n        :param event_linker: Specifies the type of event linker used to manage and access\n            events along with their corresponding event handlers. Defaults to `EventLinker`.\n        :param debug: Specifies the debug mode for the logger. If `None`, it is\n            determined based on the execution environment.\n        \"\"\"\n        # Call the parent class' __init__ method\n        super().__init__(event_linker=event_linker, debug=debug)\n\n        # Validate the queue argument\n        if queue is None:\n            raise PyventusException(\"The 'queue' argument cannot be None.\")\n        if not isinstance(queue, Queue):\n            raise PyventusException(\"The 'queue' argument must be an instance of the Queue class.\")\n\n        # Store the Redis queue and RQ options\n        self._queue: Queue = queue\n        self._options: Dict[str, Any] = options if options is not None else {}\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        # Add the event emission to the Redis Queue\n        self._queue.enqueue(event_emission, **self._options)\n

"},{"location":"api/emitters/rq/#pyventus.emitters.rq.RQEventEmitter-functions","title":"Functions","text":""},{"location":"api/emitters/rq/#pyventus.emitters.rq.RQEventEmitter.emit","title":"emit","text":"
emit(event: EmittableEventType, *args: Any, **kwargs: Any) -> None\n

Emits an event and triggers its associated event handlers.

Notes:

  • When emitting dataclass objects or Exception objects, they are automatically passed to the event handler as the first positional argument, even if you pass additional *args or **kwargs.
  • If there are event handlers subscribed to the global event ..., also known as Ellipsis, they will also be triggered each time an event or exception is emitted.
PARAMETER DESCRIPTION event

The event to be emitted. It can be str, a dataclass object, or an Exception object.

TYPE: EmittableEventType

args

Positional arguments to be passed to the event handlers.

TYPE: Any DEFAULT: ()

kwargs

Keyword arguments to be passed to the event handlers.

TYPE: Any DEFAULT: {}

RETURNS DESCRIPTION None

None

Source code in pyventus/emitters/event_emitter.py
def emit(self, /, event: EmittableEventType, *args: Any, **kwargs: Any) -> None:\n    \"\"\"\n    Emits an event and triggers its associated event handlers.\n\n    **Notes:**\n\n    -   When emitting `dataclass` objects or `Exception` objects, they are automatically passed\n        to the event handler as the first positional argument, even if you pass additional `*args`\n        or `**kwargs`.\n    -   If there are event handlers subscribed to the global event `...`, also known as `Ellipsis`,\n        they will also be triggered each time an event or exception is emitted.\n\n    :param event: The event to be emitted. It can be `str`, a `dataclass`\n        object, or an `Exception` object.\n    :param args: Positional arguments to be passed to the event handlers.\n    :param kwargs: Keyword arguments to be passed to the event handlers.\n    :return: None\n    \"\"\"\n    # Raise an exception if the event is None\n    if event is None:\n        raise PyventusException(\"The 'event' argument cannot be None.\")\n\n    # Raise an exception if the event is a type\n    if isinstance(event, type):\n        raise PyventusException(\"The 'event' argument cannot be a type.\")\n\n    # Determine the event name\n    event_name: str = self._event_linker.get_event_name(event=event if isinstance(event, str) else type(event))\n\n    # Retrieve the event handlers associated with the event\n    event_handlers: List[EventHandler] = self._event_linker.get_event_handlers_by_events(event_name, Ellipsis)\n\n    # Sort the event handlers by timestamp\n    event_handlers.sort(key=lambda handler: handler.timestamp)\n\n    # Initialize the list of event handlers to be executed\n    pending_event_handlers: List[EventHandler] = []\n\n    # Iterate through each event handler\n    for event_handler in event_handlers:\n        # Check if the event handler is a one-time subscription\n        if event_handler.once:\n            # If the event handler is a one-time subscription, we attempt to remove it.\n            if self._event_linker.remove_event_handler(event_handler=event_handler):  # pragma: no cover (Race-Cond)\n                # If the removal is successful, it indicates that the handler has not\n                # been processed before, so we add it to the pending list.\n                pending_event_handlers.append(event_handler)\n        else:\n            pending_event_handlers.append(event_handler)\n\n    # Check if the pending list of event handlers is not empty\n    if len(pending_event_handlers) > 0:\n        # Create a new EventEmission instance\n        event_emission: EventEmitter.EventEmission = EventEmitter.EventEmission(\n            event=event_name,\n            event_handlers=pending_event_handlers,\n            event_args=args if isinstance(event, str) else (event, *args),\n            event_kwargs=kwargs,\n            debug=self._logger.debug_enabled,\n        )\n\n        # Log the event emission when debug is enabled\n        if self._logger.debug_enabled:  # pragma: no cover\n            self._logger.debug(\n                action=\"Emitting Event:\",\n                msg=f\"{event_emission.event}{StdOutColors.PURPLE} ID:{StdOutColors.DEFAULT} {event_emission.id}\",\n            )\n\n        # Delegate the event emission processing to subclasses\n        self._process(event_emission)\n\n    # Log a debug message if there are no event handlers subscribed to the event\n    elif self._logger.debug_enabled:  # pragma: no cover\n        self._logger.debug(\n            action=\"Emitting Event:\",\n            msg=f\"{event_name}{StdOutColors.PURPLE} Message:{StdOutColors.DEFAULT} No event handlers subscribed\",\n        )\n
"},{"location":"api/emitters/rq/#pyventus.emitters.rq.RQEventEmitter.__init__","title":"__init__","text":"
__init__(queue: Queue, options: Dict[str, Any] | None = None, event_linker: Type[EventLinker] = EventLinker, debug: bool | None = None) -> None\n

Initialize an instance of RQEventEmitter.

PARAMETER DESCRIPTION queue

The Redis queue for enqueuing event handlers.

TYPE: Queue

options

Additional options for the RQ package enqueueing method. Defaults to an empty dictionary.

TYPE: Dict[str, Any] | None DEFAULT: None

event_linker

Specifies the type of event linker used to manage and access events along with their corresponding event handlers. Defaults to EventLinker.

TYPE: Type[EventLinker] DEFAULT: EventLinker

debug

Specifies the debug mode for the logger. If None, it is determined based on the execution environment.

TYPE: bool | None DEFAULT: None

Source code in pyventus/emitters/rq/rq_event_emitter.py
def __init__(\n    self,\n    queue: Queue,\n    options: Dict[str, Any] | None = None,\n    event_linker: Type[EventLinker] = EventLinker,\n    debug: bool | None = None,\n) -> None:\n    \"\"\"\n    Initialize an instance of `RQEventEmitter`.\n    :param queue: The Redis queue for enqueuing event handlers.\n    :param options: Additional options for the RQ package enqueueing method.\n        Defaults to an empty dictionary.\n    :param event_linker: Specifies the type of event linker used to manage and access\n        events along with their corresponding event handlers. Defaults to `EventLinker`.\n    :param debug: Specifies the debug mode for the logger. If `None`, it is\n        determined based on the execution environment.\n    \"\"\"\n    # Call the parent class' __init__ method\n    super().__init__(event_linker=event_linker, debug=debug)\n\n    # Validate the queue argument\n    if queue is None:\n        raise PyventusException(\"The 'queue' argument cannot be None.\")\n    if not isinstance(queue, Queue):\n        raise PyventusException(\"The 'queue' argument must be an instance of the Queue class.\")\n\n    # Store the Redis queue and RQ options\n    self._queue: Queue = queue\n    self._options: Dict[str, Any] = options if options is not None else {}\n
"},{"location":"tutorials/","title":"Tutorials","text":"

\u2003\u2003Welcome to the Tutorials section, where you can learn the key concepts and features of Pyventus through step-by-step examples. These tutorials are designed to be user-friendly, covering a range of topics from the basics to more advanced concepts.

\u2003\u2003By following these tutorials, you'll gain a solid understanding of Pyventus' core abstractions and how to effectively apply them when building event-driven applications. The tutorials are organized in a progressive manner, allowing you to gradually enhance your knowledge and skills.

Let's kickstart your Pyventus experience!

"},{"location":"tutorials/event-linker/","title":"The EventLinker Registry","text":"

\u2003\u2003Events are essential for building reactive applications with Pyventus. However, we need a way to connect events to the code that should run in response. This is where the EventLinker comes in.

"},{"location":"tutorials/event-linker/#what-is-the-eventlinker","title":"What is the EventLinker?","text":"

\u2003\u2003The EventLinker is a central class that acts as a registry for linking events to their associated event handlers. It keeps track of which events have event handlers assigned to them, so when an event occurs it knows which code needs to run.

\u2003\u2003The EventLinker can also be subclassed to create separate linking registries, allowing you to define different namespaces or contexts for events and event handlers.

"},{"location":"tutorials/event-linker/#benefits-of-using-the-eventlinker","title":"Benefits of Using The EventLinker","text":"

Using the EventLinker class offers several advantages:

  • Clear separation of concerns - The emitter class focuses only on emitting events, while the linker handles all the subscription logic. This makes both classes cleaner and easier to understand, as well as allowing them to be modified independently as needed.
  • Centralized logic - Having global registries means there is only one place to go to see which events have event handlers. This simplifies management of the overall event system.
  • Flexibility - You can change the event emitter instance at runtime without the need to reconfigure all connections.
"},{"location":"tutorials/event-linker/#event-handlers","title":"Event Handlers","text":"

\u2003\u2003Before proceeding, we should first understand the concept behind event handlers and what they do. An event handler is a callable object designed to respond to a specific event. When an event occurs, such as a button click, mouse movement, or a timer expiration, these objects are executed concurrently in response.

\u2003\u2003The EventHandler class encapsulates the event callbacks and provides a standardized mechanism for executing them when the event occurs. It handles the asynchronous and synchronous execution of event callbacks, as well as the completion workflow for success and error handling.

"},{"location":"tutorials/event-linker/#subscribing-event-handlers","title":"Subscribing Event Handlers","text":"

\u2003\u2003The EventLinker makes it easy to subscribe event handlers to respond to events. Let's explore the different approaches to subscribing event handlers.

"},{"location":"tutorials/event-linker/#subscription-basics","title":"Subscription Basics","text":"

\u2003\u2003Pyventus supports two types of subscriptions to handle callback registration: regular subscriptions and one-time subscriptions. Each subscription, regardless of type, will create a separate EventHandler instance independently. So subscribing the same callback multiple times to an event will cause it to be invoked multiple times.

"},{"location":"tutorials/event-linker/#regular-subscriptions","title":"Regular Subscriptions","text":"

\u2003\u2003Regular subscriptions trigger the event handler each time the subscribed event(s) occur, and the handler remains subscribed until explicitly unsubscribed. Below we will explore how to subscribe regular event handlers.

"},{"location":"tutorials/event-linker/#using-decorators","title":"Using decorators","text":"

Decorators provide a clean Python syntax for subscribing handlers.

Sync contextAsync context
# Subscribe to one event\n@EventLinker.on(\"StringEvent\")\ndef event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.on(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\ndef event_callback2():\n    print(\"Event received!\")\n
# Subscribe to one event\n@EventLinker.on(\"StringEvent\")\nasync def event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.on(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\nasync def event_callback2():\n    print(\"Event received!\")\n
"},{"location":"tutorials/event-linker/#using-the-subscribe-method","title":"Using the subscribe() method","text":"

You can also subscribe event handlers by calling the subscribe() method.

Sync contextAsync context
def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\"StringEvent\", event_callback=event_callback)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", event_callback=event_callback)\n
async def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\"StringEvent\", event_callback=event_callback)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", event_callback=event_callback)\n
"},{"location":"tutorials/event-linker/#one-time-subscriptions","title":"One-time Subscriptions","text":"

\u2003\u2003One-time subscriptions trigger the event handler only once, then automatically unsubscribe it. One-time handlers are useful for tasks that should only run once.

Behavior with Multiple Events

When subscribing a one-time handler to multiple events, if one event fires it will automatically unsubscribe the event handler from all other events.

"},{"location":"tutorials/event-linker/#using-decorators_1","title":"Using decorators","text":"

In order to perform a one-time subscription using decorators we use the once() method:

Sync contextAsync context
# Subscribe to one event\n@EventLinker.once(\"StringEvent\")\ndef event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.once(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\ndef event_callback2():\n    print(\"Event received!\")\n
# Subscribe to one event\n@EventLinker.once(\"StringEvent\")\nasync def event_callback1():\n    print(\"Event received!\")\n\n\n# Subscribe to multiple events at once\n@EventLinker.once(\"StringEvent\", \"AnotherEvent\", \"ThirdEvent\")\nasync def event_callback2():\n    print(\"Event received!\")\n
"},{"location":"tutorials/event-linker/#using-the-subscribe-method_1","title":"Using the subscribe() method","text":"

Alternatively, you can also use the subscribe() method to do a one-time subscription too:

Sync contextAsync context
def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\n    \"StringEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\n    \"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n
async def event_callback():\n    print(\"Event received!\")\n\n\n# Subscribe to one event\nEventLinker.subscribe(\n    \"StringEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n\n# Subscribe to multiple events at once\nEventLinker.subscribe(\n    \"StringEvent\", \"AnotherEvent\", \"ThirdEvent\", \n    event_callback=event_callback, \n    once=True,\n)\n
"},{"location":"tutorials/event-linker/#success-and-error-handling","title":"Success and Error Handling","text":"

\u2003\u2003In the previous sections, we discussed the process of subscribing callback functions to handle events using Pyventus' event linker. However, there may be times when we need more control over the exact workflow.

\u2003\u2003In this section, we'll show you how to define custom logic to process events upon completion using success and failure handlers. It's important to have reliable control over the asynchronous flow to build robust apps.

"},{"location":"tutorials/event-linker/#response-logic-for-regular-subscriptions","title":"Response Logic For Regular Subscriptions","text":"Using DecoratorsUsing the subscribe() method
with EventLinker.on(\"DivisionEvent\") as linker:  # (1)!\n\n    @linker.on_event\n    def handle_division(a: float, b: float) -> float:\n        return a / b\n\n    @linker.on_success\n    def handle_success(result: float) -> None:\n        print(f\"Division result: {result:.3g}\")\n\n    @linker.on_failure\n    def handle_failure(e: Exception) -> None:\n        print(f\"Oops, something went wrong: {e}\")\n
  1. When the EventLinker.on method is used as a context manager via the with statement, it allows multiple callbacks to be associated with events within the linkage context block, defining the event workflow.
def handle_division(a: float, b: float) -> float:\n    return a / b\n\ndef handle_success(result: float) -> None:\n    print(f\"Division result: {result:.3g}\")\n\ndef handle_failure(e: Exception) -> None:\n    print(f\"Oops, something went wrong: {e}\")\n\n\nEventLinker.subscribe(\n    \"DivisionEvent\", \n    event_callback=handle_division, \n    success_callback=handle_success, \n    failure_callback=handle_failure,\n)\n
"},{"location":"tutorials/event-linker/#response-logic-for-one-time-subscriptions","title":"Response Logic For One-time Subscriptions","text":"Using DecoratorsUsing the subscribe() method
with EventLinker.once(\"DivisionEvent\") as linker:  # (1)!\n\n    @linker.on_event\n    def handle_division(a: float, b: float) -> float:\n        return a / b\n\n    @linker.on_success\n    def handle_success(result: float) -> None:\n        print(f\"Division result: {result:.3g}\")\n\n    @linker.on_failure\n    def handle_failure(e: Exception) -> None:\n        print(f\"Oops, something went wrong: {e}\")\n
  1. When the EventLinker.once method is used as a context manager via the with statement, it allows multiple callbacks to be associated with events within the linkage context block, defining the event workflow.
def handle_division(a: float, b: float) -> float:\n    return a / b\n\ndef handle_success(result: float) -> None:\n    print(f\"Division result: {result:.3g}\")\n\ndef handle_failure(e: Exception) -> None:\n    print(f\"Oops, something went wrong: {e}\")\n\n\nEventLinker.subscribe(\n    \"DivisionEvent\", \n    event_callback=handle_division, \n    success_callback=handle_success, \n    failure_callback=handle_failure,\n    once=True,\n)\n
"},{"location":"tutorials/event-linker/#optimizing-callbacks-execution","title":"Optimizing Callbacks Execution","text":"

\u2003\u2003By default, event handlers in Pyventus are executed concurrently during an event emission, running their sync and async callbacks as defined. However, if you have a sync callback that involves I/O or non-CPU bound operations, you can enable the force_async parameter to offload it to a thread pool, ensuring optimal performance and responsiveness. The force_async parameter utilizes the asyncio.to_thread() function to execute sync callbacks asynchronously.

Using DecoratorsUsing the subscribe() method
# You can also set this when using the `with` \n# statement and the `once()` decorator\n@EventLinker.on(\"BlockingIO\", force_async=True)\ndef blocking_io():\n    print(f\"start blocking_io at {time.strftime('%X')}\")\n    # Note that time.sleep() can be replaced with any blocking\n    # IO-bound operation, such as file operations.\n    time.sleep(1)\n    print(f\"blocking_io complete at {time.strftime('%X')}\")\n
def blocking_io():\n    print(f\"start blocking_io at {time.strftime('%X')}\")\n    # Note that time.sleep() can be replaced with any blocking\n    # IO-bound operation, such as file operations.\n    time.sleep(1)\n    print(f\"blocking_io complete at {time.strftime('%X')}\")\n\nEventLinker.subscribe(\n    \"BlockingIO\",\n    event_callback=blocking_io,\n    force_async=True,\n)\n
"},{"location":"tutorials/event-linker/#unsubscribing-event-handlers","title":"Unsubscribing Event Handlers","text":"

\u2003\u2003Removing event handlers is an important part of working with events. Let's look at different approaches to properly teardown event subscriptions:

"},{"location":"tutorials/event-linker/#removing-an-event-handler-from-an-event","title":"Removing an Event Handler from an Event","text":"

To remove a single event handler from a specific event:

def event_callback():\n    print(\"Event received!\")\n\n\nevent_handler = EventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", event_callback=event_callback)\n\nEventLinker.unsubscribe(\"StringEvent\", event_handler=event_handler)  # (1)!\n
  1. Removes the event_handler from just the StringEvent
"},{"location":"tutorials/event-linker/#removing-event-handlers-from-all-events","title":"Removing Event Handlers from All Events","text":"

To remove an event handler from all subscribed events:

def event_callback():\n    print(\"Event received!\")\n\n\nevent_handler = EventLinker.subscribe(\"StringEvent\", \"AnotherEvent\", event_callback=event_callback)\n\nEventLinker.remove_event_handler(event_handler=event_handler)  # (1)!\n
  1. Removes the event_handler from the StringEvent and AnotherEvent
"},{"location":"tutorials/event-linker/#removing-an-event-and-its-event-handlers","title":"Removing an Event and its Event Handlers","text":"

To delete an event and all associated handlers:

@EventLinker.on(\"StringEvent\")\ndef event_callback1():\n    print(\"Event received!\")\n\n\n@EventLinker.on(\"StringEvent\", \"AnotherEvent\")\ndef event_callback2():\n    print(\"Event received!\")\n\n\nEventLinker.remove_event(event=\"StringEvent\")  # (1)!\n
  1. Removes all event handlers associated with the StringEvent
"},{"location":"tutorials/event-linker/#clearing-all-events","title":"Clearing All Events","text":"

To remove all events and their handlers:

@EventLinker.on(\"StringEvent\", ...)\ndef event_callback1():\n    pass\n\n\n@EventLinker.on(...)\ndef event_callback2():\n    pass\n\n\nEventLinker.remove_all() \n
"},{"location":"tutorials/event-linker/#custom-event-linkers","title":"Custom Event Linkers","text":"

\u2003\u2003The EventLinker class in Pyventus was designed to support subclassing to allow you to define separate linking registries or namespaces for your events and handlers, as well as configure the EventLinker behavior. This approach provides a powerful way to customize how events are linked within your applications. Some key reasons for using custom linkers include:

  • Organizing events into logical domains/contexts.
  • Isolating events to only be accessible within certain scopes.
  • Configuring linker-specific options like max handlers per event.

To define a custom linker, subclass the EventLinker, as shown below:

class UserEventLinker(EventLinker, max_event_handlers=10):\n    \"\"\" EventLinker for User's events only \"\"\"\n    pass  # Additional logic can be added here if needed...\n\n\n@UserEventLinker.on(\"PasswordResetEvent\")\nasync def handle_password_reset_event(email: str):\n    print(\"PasswordResetEvent received!\")\n\n\n@UserEventLinker.on(\"EmailVerifiedEvent\")\nasync def handle_email_verified_event(email: str):\n    print(\"EmailVerifiedEvent received!\")\n
"},{"location":"tutorials/event-linker/#debug-mode","title":"Debug Mode","text":"

\u2003\u2003The EventLinker also offers a debug mode feature which helps you understand how event subscriptions and unsubscriptions are happening during runtime.

"},{"location":"tutorials/event-linker/#global-debug-mode","title":"Global Debug Mode","text":"

\u2003\u2003By default, Pyventus leverages the Python's global debug tracing feature. Simply run your code in an IDE's debugger mode to activate the global debug mode tracing.

"},{"location":"tutorials/event-linker/#namespace-debug-flag","title":"Namespace Debug Flag","text":"

\u2003\u2003Alternatively, if you want to enable or disable the debug mode specifically for a certain EventLinker namespace, you can use the debug flag that is available in the subclass configurations. Setting the debug flag to True enables debug mode for that namespace, while setting it to False disables debug mode. Here's an example:

Debug Flag OnDebug Flag Off
class CustomEventLinker(EventLinker, debug=True):\n    pass  # Additional logic can be added here if needed...\n
class CustomEventLinker(EventLinker, debug=False):\n    pass  # Additional logic can be added here if needed...\n
"},{"location":"tutorials/event-linker/#recap","title":"Recap","text":"

In this tutorial we covered:

  • The standardized mechanism for executing event callbacks (Event Handlers).
  • Subscribing regular and one-time handlers with decorators and the subscribe() method.
  • Unsubscribing a single event handler, all handlers, an event, or clearing all.
  • Success and error handling by defining custom logic for event completion.
  • Techniques for optimizing synchronous callback execution.
  • Custom Event Linkers to separate event namespaces.
  • Debug mode to trace subscriptions

We learned the core EventLinker concepts of:

  • Use the EventLinker to link events to code responses.
  • Subscribe/unsubscribe as needed using various approaches.

"},{"location":"tutorials/event/","title":"Exploring Event Types","text":"

\u2003\u2003In this first tutorial, you'll learn about defining and handling events in Pyventus. Whether you're new to event-driven programming or just getting started with the package, this guide will explain the key concepts.

"},{"location":"tutorials/event/#what-are-events","title":"What are Events?","text":"

\u2003\u2003In event-driven programming, events refer to specific occurrences or incidents that happen within the program or system. These events play an important role in Pyventus by enabling different parts of a program to communicate and react to changes. Pyventus provides a powerful event system that supports various event types, allowing developers to effectively define and handle events.

"},{"location":"tutorials/event/#string-events","title":"String Events","text":"

\u2003\u2003We'll begin with Pyventus' basic string event type. These provide an easy way to define and handle events using event names as strings. This makes them straightforward to work with and are suited for simpler applications requiring a minimal approach.

"},{"location":"tutorials/event/#passing-data","title":"Passing Data","text":"

\u2003\u2003When subscribing to a String Event, event callbacks can define parameters like regular functions. This allows flexibility in passing different data types like strings, integers, etc. The event emitters forward any arguments emitted with the string event to handlers using *args and **kwargs, ensuring they receive the same data payload.

"},{"location":"tutorials/event/#usage","title":"Usage","text":"

Let's look at some code examples of defining and handling String Events:

@EventLinker.on(\"StringEvent\")\ndef event_handler(param1: str, param2: str, **kwargs):\n    print(\"Parameters:\", param1, param2)\n    print(\"**kwargs:\", kwargs)\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"StringEvent\", \"value1\", param2=\"value2\", key1=\"value3\", key2=\"value4\")\n

You can also use the subscribe() method to define and link string-named events:

def event_handler():\n    pass\n\nEventLinker.subscribe(\"StringEvent\", event_callback=event_handler)\n
"},{"location":"tutorials/event/#event-objects","title":"Event Objects","text":"

\u2003\u2003Let's move on to Event Objects in Pyventus. They provide a structured way to define events and encapsulate relevant data payloads. Some benefits include better organization, maintainability, and validation support.

"},{"location":"tutorials/event/#defining-event-objects","title":"Defining Event Objects","text":"

To create an Event Object:

  1. Define a Python dataclass.
  2. Declare fields for the event's payload within the class.

For example:

@dataclass\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n
"},{"location":"tutorials/event/#adding-validation","title":"Adding Validation","text":"

\u2003\u2003You can also ensure valid data before propagation by adding validation logic to the Event class using the dataclass' __post_init__() method:

@dataclass\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n\n    def __post_init__(self):\n        if not isinstance(self.order_id, int):\n            raise ValueError(\"Error: 'order_id' must be a valid int number!\")\n
"},{"location":"tutorials/event/#usage_1","title":"Usage","text":"

Here's an example demonstrating subscription and emission:

@dataclass  # Define a Python dataclass.\nclass OrderCreatedEvent:\n    order_id: int\n    payload: dict[str, any]\n\n\n@EventLinker.on(OrderCreatedEvent)  # Subscribe event handlers to the event.\ndef handle_order_created_event(event: OrderCreatedEvent):\n    # Pyventus will automatically pass the Event Object \n    # as the first positional argument.\n    print(f\"Event Object: {event}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit( # (1)!\n    event=OrderCreatedEvent(  # Emit an instance of the event!\n        order_id=6452879,\n        payload={},\n    ),\n)\n
  1. You can also emit the Event object as a positional argument:
    event_emitter.emit( \n    OrderCreatedEvent(\n        order_id=6452879,\n        payload={},\n    ),\n)  \n
    As well as pass extra *args and **kwargs too:
    event_emitter.emit( \n    OrderCreatedEvent(\n        order_id=6452879,\n        payload={},\n    ),\n    \"param1\",\n    param2=\"param2\",\n)  \n

Event Object's Behavior

By default, Pyventus retrieves the event name from the event class and automatically passes the instance of the Event Object as the first positional argument to the event callback, even if you provide additional *args or **kwargs.

"},{"location":"tutorials/event/#benefits","title":"Benefits","text":"
  • Natural emission of event payloads: Emitting an event object is a simple and intuitive process. Once an event object is created, it can be sent using the event emitter, providing a natural and straightforward approach to event emission. Since the event object carries the relevant data, the event emitter ensures that the same data is received by the event handlers.
  • Structured and organized event definitions: Event objects provide a structured and encapsulated representation of events, enabling better organization and management of events throughout the system.
  • Custom data validation: Event objects can include custom validation logic to ensure the validity of the encapsulated data before propagation.
  • Auto-completion when handling events: Event objects benefit from autocompletion integration provided by code editors and IDEs.
"},{"location":"tutorials/event/#exception-events","title":"Exception Events","text":"

\u2003\u2003In addition to normal events, Pyventus allows exceptions to be treated as first-class events. This enables propagating and handling errors in an event-driven manner. If you're interested in incorporating error handling in event emission, you can check out Success and Error Handling.

"},{"location":"tutorials/event/#usage_2","title":"Usage","text":"

Let's look at some code examples that demonstrates the usage of event exceptions:

@EventLinker.on(ValueError)\ndef handle_validation_error(exc: ValueError):\n    print(f\"Validation failed for; '{exc}'\")\n\n\ntry:\n    raise ValueError(\"`username`, already in use.\")\nexcept ValueError as e:\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(e)\n
You can also work with custom exceptions...
class UserValidationError(ValueError):\n    def __init__(self, error: str = \"Validation Error\"):\n        super().__init__(error)\n        self.error: str = error\n\n\n@EventLinker.on(UserValidationError)\ndef handle_validation_error(exc: UserValidationError):\n    print(f\"Validation failed for; '{exc.error}'\")\n\n\ntry:\n    raise UserValidationError(\"`username`, already in use.\")\nexcept UserValidationError as e:\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(e)\n
"},{"location":"tutorials/event/#benefits_1","title":"Benefits","text":"

\u2003\u2003By treating exceptions as first-class events, Pyventus provides a unified approach to handling errors in an event-driven manner. This approach leverages the existing event-driven infrastructure, promotes code reuse, and enables flexible and powerful error handling strategies.

"},{"location":"tutorials/event/#global-events","title":"Global Events","text":"

\u2003\u2003In addition to individual events, Pyventus provides support for Global Events within the context of an EventLinker. This feature allows you to register handlers that respond to event occurrences across a specific namespace, regardless of where they happen in your code. Global Events are particularly useful for implementing cross-cutting concerns such as logging, monitoring, or analytics. By subscribing event handlers to ... or Ellipsis, you can capture all events that may occur within that EventLinker context.

@EventLinker.on(...)\ndef handle_any_event(*args, **kwargs):  #(1)!\n    print(f\"Perform logging...\\nArgs: {args}\\tKwargs: {kwargs}\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter()\nevent_emitter.emit(\"GreetEvent\", name=\"Pyventus\")\n
  1. This handler will be triggered by any event type that occurs.
"},{"location":"tutorials/event/#recap","title":"Recap","text":"

\u2003\u2003In summary, we've covered the different types of events supported by Pyventus - string named events, event objects, and exception events. String events provide a simple way to define and handle basic events using names as strings. Event objects offer a more structured approach with encapsulated payloads and validation capabilities. And exception events allow treating errors as first-class events.

\u2003\u2003Additionally, Pyventus provides support for Global Events within an EventLinker context. Global Events allow registering handlers across a namespace to respond to events anywhere in the code. This feature is useful for implementing cross-cutting concerns like logging, monitoring, or analytics.

Using Different Emitters and Linkers

The EventEmitter and EventLinker used in the code examples can be easily replaced with any custom or built-in Pyventus implementation of your choice. For more information on available options, consult the official documentation.

"},{"location":"tutorials/emitters/","title":"Master the Event Emitter","text":"

\u2003\u2003In the previous tutorial, we learned how to link events with their event handlers using the EventLinker. Now, let's dive into the process of dispatching events and triggering the associated callbacks. This tutorial will introduce you to the EventEmitter class, which plays a crucial role in this event-driven system.

"},{"location":"tutorials/emitters/#what-is-an-event-emitter","title":"What is an Event Emitter?","text":"

\u2003\u2003In event-driven programming, an Event Emitter is a variation of the Observer pattern that allows you to easily manage and handle events in a decoupled manner. The Observer pattern provides a way for objects to subscribe to and receive notifications from a subject when its state changes. Similarly, an Event Emitter enables objects/functions to subscribe to and receive notifications when specific events occur.

"},{"location":"tutorials/emitters/#pyventus-event-emitter","title":"Pyventus Event Emitter","text":"

\u2003\u2003In Pyventus, the Event Emitter concept remains largely the same, but with a few unique features of its own. The Pyventus Event Emitter focuses only on emitting events, while the association logic is handled by the Event Linker class. This separation of concerns makes both classes cleaner and easier to understand, as well as allowing them to be modified independently as needed. Furthermore, it offers the flexibility to change the event emitter instance at runtime without the need to reconfigure all connections.

\u2003\u2003So, what exactly is the Pyventus EventEmitter? It is an abstract base class that provides a common interface for emitting events and notifying registered callbacks when those events occur. It serves as the foundation for implementing custom event emitters with specific dispatch strategies.

"},{"location":"tutorials/emitters/#purpose-of-pyventus-event-emitter","title":"Purpose of Pyventus Event Emitter","text":"

\u2003\u2003The main goal of the EventEmitter base class is to decouple the event emission process from the underlying implementation. This decoupling promotes flexibility, adaptability, and adheres to the Open-Closed principle, allowing the implementation of custom event emitters without impacting existing consumers.

\u2003\u2003The EventEmitter presents a unified API with two key methods: emit() and _process(). These methods can be used in both synchronous and asynchronous contexts to emit events and handle their emission. The emit() method is used to invoke an event, while the _process() method is an abstract method responsible for processing the execution of the emitted event.

"},{"location":"tutorials/emitters/#built-in-event-emitters","title":"Built-in Event Emitters","text":"

\u2003\u2003Pyventus includes several built-in event emitters by default. For instance, the AsyncIOEventEmitter leverages the AsyncIO framework to handle the execution of event emissions, while the RQEventEmitter utilizes Redis Queue pub/sub system with workers to manage the execution of event emissions.

Driving Innovation Through Collaboration

Pyventus is an open source project that welcomes community involvement. If you wish to contribute additional event emitters, improvements, or bug fixes, please check the Contributing section for guidelines on collaborating. Together, we can further the possibilities of event-driven development.

"},{"location":"tutorials/emitters/#custom-event-emitters","title":"Custom Event Emitters","text":"

\u2003\u2003Pyventus provides a powerful abstraction layer for creating custom event emitters, allowing you to tailor their behavior and capabilities to suit your specific needs. In this section, we will guide you through the process of creating a custom event emitter specifically designed for the FastAPI framework.

\u2003\u2003The objective is to leverage FastAPI's BackgroundTasks feature to efficiently process the execution of event emissions within your FastAPI application. Before we jump into the implementation details, make sure you have FastAPI properly installed and set up in your development environment.

"},{"location":"tutorials/emitters/#defining-and-implementing-the-custom-event-emitter-class","title":"Defining and Implementing the Custom Event Emitter Class","text":"

\u2003\u2003To create the custom event emitter for FastAPI, we'll define a class called FastAPIEventEmitter. This class will extend the base EventEmitter class and implement the abstract _process() method using the FastAPI's background tasks to handle the event emission properly.

from fastapi import BackgroundTasks\n\nfrom pyventus import EventEmitter, EventLinker\n\n\nclass FastAPIEventEmitter(EventEmitter):\n    \"\"\"A custom event emitter that uses the FastAPI background tasks.\"\"\"\n\n    def __init__(self, background_tasks: BackgroundTasks):\n        super().__init__(event_linker=EventLinker, debug=False)\n        self._background_tasks = background_tasks  # (1)!\n\n    def _process(self, event_emission: EventEmitter.EventEmission) -> None:\n        self._background_tasks.add_task(event_emission)  # (2)!\n
  1. Stores the FastAPI background tasks object.
  2. Executes the event handler callbacks as background tasks.

Once the custom event emitter is defined, you can integrate it into your code as follows:

@EventLinker.on(\"ConsolePrintEvent\")\nasync def handle_console_print_event(message: str):\n    await sleep(randint(0, 3))  # (1)!\n    print(message)\n\n\napp = FastAPI()\n\n\n@app.get(\"/print\")\nasync def console_print_endpoint(background_tasks: BackgroundTasks):\n    \"\"\" FastAPI endpoint that triggers the console_print event. \"\"\"\n\n    def app_service(event_emitter: EventEmitter) -> None:\n        event_emitter.emit(\n            event=\"ConsolePrintEvent\", \n            message=f\"\\n{type(event_emitter).__name__}\",\n        )\n\n    fastapi_event_emitter = FastAPIEventEmitter(background_tasks)\n    app_service(event_emitter=fastapi_event_emitter)\n\n    return {\"message\": \"Console print triggered!\"}\n
  1. Simulate a random delay.
To test the custom event emitter integration follow these steps...

Run the server with:

uvicorn main:app --reload\n

Open your browser at http://127.0.0.1:8000/print. You will see the JSON response as:

{ \"message\": \"Console print triggered!\" }\n

And also you are going see the outputs of the event emitter in the console logs as:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\nINFO:     Started reloader process [28720]\nINFO:     Started server process [28722]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\nINFO:     127.0.0.1:52391 - \"GET /print HTTP/1.1\" 200 OK\n\nFastAPIEventEmitter\n

Official FastAPIEventEmitter Integration

In case you're interested in integrating Pyventus with FastAPI, you can refer to the official Pyventus FastAPI Event Emitter implementation.

"},{"location":"tutorials/emitters/#runtime-flexibility","title":"Runtime Flexibility","text":"

\u2003\u2003Another key feature of the Pyventus EventEmitter is the decoupling of event dispatching from the underlying implementation that processes the event handlers. This, combined with the EventLinker, allows you to change the event emitter at runtime without reconfiguring all the connections or any complex logic. We can use the base EventEmitter as a dependency and then change the concrete instance to suit your needs. Let's demonstrate this using the AsyncIOEventEmitter and ExecutorEventEmitter:

from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter, ExecutorEventEmitter\n\n\n@EventLinker.on(\"GreetEvent\")\ndef handle_greet_event(name: str = \"World\"):\n    print(f\"Hello, {name}!\")\n\n\nif __name__ == \"__main__\":\n    def main(event_emitter: EventEmitter) -> None:\n        event_emitter.emit(\"GreetEvent\", name=type(event_emitter).__name__)\n\n\n    main(event_emitter=AsyncIOEventEmitter())\n    with ExecutorEventEmitter() as executor_event_emitter:\n        main(event_emitter=executor_event_emitter)\n

\u2003\u2003In the example above, we defined a helper function handle_greet_event that accepts an EventEmitter instance as a parameter. This allows us to dynamically switch between the AsyncIOEventEmitter and the ExecutorEventEmitter depending on our requirements. This flexibility enables us to adapt the event emitter implementation at runtime without modifying the core application logic.

"},{"location":"tutorials/emitters/#using-custom-event-linkers","title":"Using Custom Event Linkers","text":"

\u2003\u2003By default, event emitters come with the base EventLinker registry assigned to the event_linker property. However, you have the flexibility to specify the EventLinker class that will be used by the EventEmitter. To configure this option, simply manually set the EventLinker class in the constructor.

from pyventus import EventEmitter, EventLinker, AsyncIOEventEmitter\n\n\nclass UserEventLinker(EventLinker, max_event_handlers=10):\n    \"\"\" EventLinker for User's events only \"\"\"\n    pass  # Additional logic can be added here if needed...\n\n\n@UserEventLinker.once(\"PasswordResetEvent\")\nasync def handle_users_password_reset_event(email: str):\n    print(\"User's PasswordResetEvent received!\")\n\n\n@EventLinker.once(\"PasswordResetEvent\")\nasync def handle_any_password_reset_event(email: str):\n    print(\"Any PasswordResetEvent received!\")\n\n\nevent_emitter: EventEmitter = AsyncIOEventEmitter(event_linker=UserEventLinker)\nevent_emitter.emit(\"PasswordResetEvent\", \"example@email.com\")\n

\u2003\u2003In the example above, we have assigned a custom event linker to the specific class of the EventEmitter. When we emit the PasswordResetEvent, we can see that only the handle_users_password_reset_event(), which was registered within the UserEventLinker namespace, gets triggered and removed. The handle_any_password_reset_event() callback, registered in a different EventLinker context, does not get triggered.

"},{"location":"tutorials/emitters/#debug-mode","title":"Debug Mode","text":"

\u2003\u2003Pyventus' EventEmitter offers a useful debug mode feature to help you understand the flow of events and troubleshoot your event-driven application. You can enable debug mode in the EventEmitter using the following options:

"},{"location":"tutorials/emitters/#global-debug-mode","title":"Global Debug Mode","text":"

\u2003\u2003By default, Pyventus makes use of Python's global debug tracing feature. To activate the global debug mode, simply run your code in an IDE's debugger mode. This allows you to observe the execution of events and trace their paths.

"},{"location":"tutorials/emitters/#instance-debug-flag","title":"Instance Debug Flag","text":"

\u2003\u2003Alternatively, if you want to enable or disable debug mode for a specific EventEmitter instance, you can use the debug flag provided by the concrete implementation. Setting the debug flag to True enables debug mode for that instance, while setting it to False disables debug mode. Here's an example:

Debug flag OnDebug flag Off
# Enable debug mode for a specific EventEmitter instance\nevent_emitter: EventEmitter = AsyncIOEventEmitter(debug=True)\nevent_emitter.emit(\"Hello\", \"Pyventus\")\n
# Disable debug mode for a specific EventEmitter instance\nevent_emitter: EventEmitter = AsyncIOEventEmitter(debug=False)\nevent_emitter.emit(\"Hello\", \"Pyventus\")\n
"},{"location":"tutorials/emitters/#best-practices","title":"Best Practices","text":"

\u2003\u2003To fully leverage the power of the EventEmitter, it is recommended to use the base EventEmitter as a dependency instead of any concrete implementation. This allows you to easily switch between different event emitter implementations at runtime, providing flexibility and adaptability to your code.

"},{"location":"tutorials/emitters/#recap","title":"Recap","text":"

\u2003\u2003In this tutorial, we learned about the EventEmitter component and its role in dispatching events and triggering associated callbacks. We explored the base EventEmitter class, its unified async/sync API, and the process of creating custom event emitters. We also covered the usage of custom event linkers, best practices for using the EventEmitter, and the debug mode options provided by Pyventus.

"},{"location":"tutorials/emitters/asyncio/","title":"AsyncIO Event Emitter","text":"

\u2003\u2003Now that we've covered the base EventEmitter interface, let's examine one of its official implementations: the AsyncIOEventEmitter.

"},{"location":"tutorials/emitters/asyncio/#what-is-it","title":"What is it?","text":"

\u2003\u2003The AsyncIOEventEmitter is a class that inherits from EventEmitter and uses the AsyncIO framework to handle the execution of event emissions.

"},{"location":"tutorials/emitters/asyncio/#how-it-works","title":"How it works","text":"

\u2003\u2003The AsyncIOEventEmitter handles the event emission differently depending on whether it is operating in a synchronous or asynchronous execution context. In synchronous contexts, it will automatically start an event loop to run handlers concurrently. In asynchronous contexts, it leverages any existing event loop. Let's explore the AsyncIOEventEmitter's behavior in more detail:

"},{"location":"tutorials/emitters/asyncio/#sync-context","title":"Sync context","text":"

\u2003\u2003When running without an existing AsyncIO event loop, the AsyncIOEventEmitter automatically starts a new loop using asyncio.run(). Within this loop, it executes the event emission. The loop then waits for all scheduled tasks to finish before closing. This preserves synchronous execution while still gaining the benefits of the concurrent execution.

"},{"location":"tutorials/emitters/asyncio/#async-context","title":"Async context","text":"

\u2003\u2003In an asynchronous context where an event loop is already running, the event emission is scheduled and processed on that existing loop.

AsyncIO Event Loop Behavior

If the event loop is closed before all callbacks complete, any remaining scheduled tasks will be canceled.

"},{"location":"tutorials/emitters/asyncio/#usage","title":"Usage","text":"

\u2003\u2003Using the AsyncIOEventEmitter is straightforward. To get started, simply create a new instance of the class and call its emit() methods, as shown below:

Sync contextAsync context Usage of the AsyncIOEventEmitter
from pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"Sync event callback!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"Async event callback!\")\n\n\ndef main():\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(\"StringEvent\")\n\n\nmain()\n
Usage of the AsyncIOEventEmitter
import asyncio\nfrom pyventus import EventLinker, EventEmitter, AsyncIOEventEmitter\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"Sync event callback!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"Async event callback!\")\n\n\nasync def main():\n    event_emitter: EventEmitter = AsyncIOEventEmitter()\n    event_emitter.emit(\"StringEvent\")\n    await asyncio.sleep(0.5) # (1)!\n\nasyncio.run(main())\n
  1. By awaiting the asyncio.sleep(0.5), we ensure the existing event loop continues running long enough for all scheduled tasks to finish processing before concluding. Without waiting, closing the loop prematurely could cause unfinished tasks to be canceled.
"},{"location":"tutorials/emitters/asyncio/#recap","title":"Recap","text":"

We've explored the AsyncIOEventEmitter class in depth:

  • The AsyncIOEventEmitter inherits from the EventEmitter class
  • To use it, instantiate the class and call methods like emit()

\u2003\u2003By understanding these concepts, you can effectively utilize the AsyncIOEventEmitter to emit events in both synchronous and asynchronous contexts, benefiting from the concurrency features provided by the AsyncIO framework.

"},{"location":"tutorials/emitters/celery/","title":"Celery Event Emitter","text":"

\u2003\u2003The CeleryEventEmitter provides a powerful way to build event-driven applications that can handle high volumes of work in a scalable and asynchronous manner.

"},{"location":"tutorials/emitters/celery/#what-is-it","title":"What is it?","text":"

\u2003\u2003The CeleryEventEmitter is a concrete implementation of the EventEmitter that leverages the Celery distributed task queue system for event handling. It provides the capability to enqueue and process event emissions in a scalable and asynchronous manner using Celery. This makes the CeleryEventEmitter particularly useful for handling resource-intensive tasks.

"},{"location":"tutorials/emitters/celery/#how-it-works","title":"How it works","text":"

\u2003\u2003The CeleryEventEmitter seamlessly handles the emission and processing of events by utilizing the Celery package. Here's a breakdown of how it functions:

  1. Event emission: When an event is triggered, an object is created and submitted as a task to the Celery queue.
  2. Task queue: The Celery broker stores the task in its queue, where it can be retrieved by workers.
  3. Worker processing: Idle Celery workers pull tasks from the queue and execute the event emissions asynchronously in parallel.
"},{"location":"tutorials/emitters/celery/#usage","title":"Usage","text":"

To start using the CeleryEventEmitter, follow these steps:

  1. Install Celery: Before proceeding, make sure you have installed the Celery optional dependency.
  2. Define event handlers: Let's start with the definition of the event handlers. It is important to note that these functions cannot reside in the main module. Therefore, we need to create another module where all our event handlers can be placed. For this example, let's create a file called event_handlers.py and add the handlers to be processed. event_handlers.py
    import asyncio\nimport time\n\nfrom pyventus.linkers import EventLinker\n\n\n@EventLinker.on(\"StringEvent\")\nasync def slow_async_event_callback():\n    print(\"Starting the async slow process...\")\n    await asyncio.sleep(5)\n    print(\"Finishing the async slow process!\")\n\n\n@EventLinker.on(\"StringEvent\")\ndef slow_sync_event_callback():\n    print(\"Starting the sync slow process...\")\n    time.sleep(5)\n    print(\"Finishing the sync slow process!\")\n
  3. Celery worker: Once you have defined the event handlers, the next step is to configure the Celery workers to process event emissions within a distributed task queue system. To accomplish this, create a file called worker.py and include the following worker configuration. These workers will actively listen to a message broker like RabbitMQ or Redis and process incoming tasks. For more advanced configurations, refer to the official Celery documentation. Serialization Security

    \u2003\u2003It's important to set the content type in the Celery app to application/x-python-serialize. This allows the event emission object to be serialized and deserialized when tasks are processed. The CeleryEventEmitter queue can authenticate and validate any serialized payloads through hashing methods and a secret key. Moreover, a custom serializer can be implemented if the default does not meet the specific needs of your project.

    worker.py
    from celery import Celery\nfrom pyventus.emitters.celery import CeleryEventEmitter\n\n# To ensure Python recognizes the existence of the event handlers, we need to import them.\nfrom event_handlers import slow_sync_event_callback, slow_async_event_callback\n\n# Using Redis as a broker for example purpose. For the Redis support 'pip install celery[redis]'\napp: Celery = Celery(\"worker\", broker=\"redis://default:redispw@localhost:6379\")\n\n# Optional configuration, see the Celery app user guide.\napp.conf.update(result_expires=3600)\n\n# Set the accepted content type to \"application/x-python-serialize\" in the Celery app.\napp.conf.accept_content = [\"application/json\", \"application/x-python-serialize\"]\n\n# Create the celery event emitter queue.\ncelery_event_emitter_queue = CeleryEventEmitter.Queue(celery=app, secret=\"secret-key\")\n\nif __name__ == \"__main__\":\n    app.start()\n
  4. Launching Celery Workers: With the previous configuration and setup complete, we can now launch the Celery worker processes. There are a few differences depending on your operating system:
    • For Linux/macOS:
      celery -A worker worker -l INFO\n
    • For Windows:
      celery -A worker worker -l INFO --pool=solo\n
    • Specifying a Queue:
      celery -A worker worker -l INFO -Q [queue-name]\n
  5. Emitting events: To emit events, we will create a main.py file where we instantiate the CeleryEventEmitter and trigger our first event. main.py
    from pyventus import EventEmitter\nfrom pyventus.emitters.celery import CeleryEventEmitter\n\nfrom worker import celery_event_emitter_queue\n\nif __name__ == \"__main__\":\n    event_emitter: EventEmitter = CeleryEventEmitter(queue=celery_event_emitter_queue)\n    event_emitter.emit(\"StringEvent\")\n
"},{"location":"tutorials/emitters/celery/#recap","title":"Recap","text":"

\u2003\u2003We've explored how the CeleryEventEmitter provides an asynchronous and scalable solution for processing events. Here are the key points:

  • Events are emitted and serialized into tasks submitted to the Celery queue.
  • Celery workers then asynchronously process the queued event emissions independently and in parallel.
  • This distributed approach provides scalable event handling under any workload.

\u2003\u2003In summary, the CeleryEventEmitter leverages Celery's distributed task queue architecture to efficiently scale event-driven applications through asynchronous parallel processing of event emissions. This elastic approach allows applications to handle increasing workloads in a scalable manner.

"},{"location":"tutorials/emitters/executor/","title":"Executor Event Emitter","text":"

\u2003\u2003The ExecutorEventEmitter leverages Python's concurrent.futures module to asynchronously execute event emissions across threads or processes. This approach helps optimize performance for applications with I/O-intensive or CPU-bound tasks by utilizing all available CPU resources.

"},{"location":"tutorials/emitters/executor/#what-is-it","title":"What is it?","text":"

\u2003\u2003The ExecutorEventEmitter inherits from the base EventEmitter class and uses an Executor interface to asynchronously run event emissions in either threads or processes. This flexibility in execution models allows you to choose the optimal approach based on your specific application requirements.

"},{"location":"tutorials/emitters/executor/#how-it-works","title":"How it Works","text":"

\u2003\u2003This class utilizes the concurrent.futures Executor interface to handle asynchronous execution of event handlers. It can work with either ThreadPoolExecutor for thread-based execution or ProcessPoolExecutor for process-based execution. When an event is emitted, its execution is submitted to the executor to run asynchronously in either threads (ThreadPoolExecutor) or processes (ProcessPoolExecutor).

ProcessPoolExecutor

\u2003\u2003The ProcessPoolExecutor utilizes Python's multiprocessing module to run event emissions in separate processes instead of threads. This sidesteps the Global Interpreter Lock to enable true parallel execution. However, only pickleable objects can be executed and returned.

"},{"location":"tutorials/emitters/executor/#usage","title":"Usage","text":"

\u2003\u2003Using the ExecutorEventEmitter is straightforward. To get started, simply create a new instance of the class, pass the desired executor concrete instance and call its emit() methods.

Executor Management

\u2003\u2003It is important to properly manage the underlying Executor when using this event emitter. Once finished emitting events, call the shutdown() method to signal the executor to free any resources for pending futures or use the with statement, which will shut down the Executor automatically.

"},{"location":"tutorials/emitters/executor/#threadpoolexecutor-example","title":"ThreadPoolExecutor Example","text":"Using the with statementUsing the shutdown() method
import asyncio\nimport time\nfrom concurrent.futures import ThreadPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\n\nif __name__ == \"__main__\":\n    with ExecutorEventEmitter() as event_emitter: #(1)!\n        event_emitter.emit(\"StringEvent\")\n        event_emitter.emit(\"StringEvent\")\n
  1. The ExecutorEventEmitter uses the ThreadPoolExecutor by default, but you can customize it by providing your own instance.
import asyncio\nimport time\nfrom concurrent.futures import ThreadPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\nif __name__ == \"__main__\":\n    event_emitter = ExecutorEventEmitter()\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.shutdown(wait=True)\n
"},{"location":"tutorials/emitters/executor/#processpoolexecutor-example","title":"ProcessPoolExecutor Example","text":"Using the with statementUsing the shutdown() method
import asyncio\nimport time\nfrom concurrent.futures import ProcessPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\n\nif __name__ == \"__main__\":\n    with ExecutorEventEmitter(executor=ProcessPoolExecutor()) as event_emitter:\n        event_emitter.emit(\"StringEvent\")\n        event_emitter.emit(\"StringEvent\")\n
import asyncio\nimport time\nfrom concurrent.futures import ProcessPoolExecutor\n\nfrom pyventus import EventLinker, ExecutorEventEmitter\n\n\n@EventLinker.on(\"StringEvent\")\ndef sync_event_callback():\n    print(\"[Sync] Started!\")\n    time.sleep(1)\n    print(\"[Sync] Finished!\")\n\n\n@EventLinker.on(\"StringEvent\")\nasync def async_event_callback():\n    print(\"[Async] Started!\")\n    await asyncio.sleep(1)\n    print(\"[Async] Finished!\")\n\nif __name__ == \"__main__\":\n    event_emitter = ExecutorEventEmitter(executor=ProcessPoolExecutor())\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.emit(\"StringEvent\")\n    event_emitter.shutdown(wait=True)\n
"},{"location":"tutorials/emitters/executor/#recap","title":"Recap","text":"

\u2003\u2003By learning how this event emitter leverages executors for concurrent/parallel execution, you can optimize your applications to take full advantage of multicore systems through balanced workload distribution. Proper use of this approach can significantly improve performance.

"},{"location":"tutorials/emitters/fastapi/","title":"FastAPI Event Emitter","text":"

\u2003\u2003The FastAPIEventEmitter provides a powerful way to build reactive FastAPI applications using an event-driven architecture. It leverages FastAPI's asynchronous BackgroundTasks to handle events outside the request-response cycle.

"},{"location":"tutorials/emitters/fastapi/#what-is-it","title":"What is it?","text":"

\u2003\u2003The FastAPIEventEmitter is a concrete implementation of the EventEmitter class that utilizes FastAPI's BackgroundTasks for event handling. It provides a convenient way to incorporate event-driven functionality into FastAPI applications, allowing you to implement tasks such as sending emails in a decoupled and asynchronous manner.

"},{"location":"tutorials/emitters/fastapi/#how-it-works","title":"How it Works","text":"

\u2003\u2003The FastAPIEventEmitter handles the emission and processing of events by utilizing the FastAPI's background tasks queue. When an event is emitted, its execution is scheduled into the FastAPI's background tasks to run asynchronously after the response is sent.

"},{"location":"tutorials/emitters/fastapi/#usage","title":"Usage","text":"

To start using the FastAPIEventEmitter, follow these steps:

  1. Install Dependencies: Ensure FastAPI and Pyventus are installed.

  2. Dependency injection and usage: The FastAPIEventEmitter integrates fully with FastAPI and can be used in routes or elsewhere via dependency injection. As an example, we'll create a simple FastAPI app to simulate a non-blocking email notification. Create a main.py file and add the following code:

    Without optionsWith options main.py
    import time\nfrom typing import Dict\n\nfrom fastapi import FastAPI, Depends\n\nfrom pyventus import EventLinker\nfrom pyventus.emitters.fastapi import FastAPIEventEmitter\n\n\n@EventLinker.on(\"SendEmail\")\ndef event_callback(email: str):\n    print(f\"Sending email to: {email}\")\n    time.sleep(2)\n    print(\"Email sent successfully!\")\n\n\napp = FastAPI()\n\n@app.get(\"/\")\nasync def send_email(\n    event_emitter: FastAPIEventEmitter = Depends(FastAPIEventEmitter),\n) -> Dict[str, str]:\n    event_emitter.emit(\"SendEmail\", \"email@pyventus.com\")\n    return {\"message\": \"Email sent!\"}\n
    main.py
    import time\nfrom typing import Dict\n\nfrom fastapi import FastAPI, Depends\n\nfrom pyventus import EventLinker\nfrom pyventus.emitters.fastapi import FastAPIEventEmitter\n\n\n@EventLinker.on(\"SendEmail\")\ndef event_callback(email: str):\n    print(f\"Sending email to: {email}\")\n    time.sleep(2)\n    print(\"Email sent successfully!\")\n\n\napp = FastAPI()\n\n@app.get(\"/\")\nasync def send_email(\n    event_emitter: FastAPIEventEmitter = Depends(FastAPIEventEmitter.options(debug=False)),\n) -> Dict[str, str]:\n    event_emitter.emit(\"SendEmail\", \"email@pyventus.com\")\n    return {\"message\": \"Email sent!\"}\n
  3. Run the server: Start the app with:

    uvicorn main:app --reload\n

    Open your browser at http://127.0.0.1:8000/. You will see the JSON response as:

    { \"message\": \"Email sent!\" }\n

    You'll also be able to see the outputs of the functions in the console logs as follows:

    INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\nINFO:     Started reloader process [28720]\nINFO:     Started server process [28722]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\nINFO:     127.0.0.1:57926 - \"GET / HTTP/1.1\" 200 OK\n\nSending email to: email@pyventus.com\nEmail sent successfully!\n
"},{"location":"tutorials/emitters/fastapi/#recap","title":"Recap","text":"

\u2003\u2003As we have seen, the FastAPIEventEmitter allows building reactive FastAPI apps using an event-driven architecture. By leveraging background tasks, events can be emitted from routes and processed independently without blocking responses. This delivers asynchronous and non-blocking behavior for tasks like emails, jobs, streams and more. The emitter integrates seamlessly with FastAPI via dependency injection.

"},{"location":"tutorials/emitters/rq/","title":"RQ Event Emitter","text":"

\u2003\u2003In the previous sections, we explored different event emitters, such as AsyncIOEventEmitter and ExecutorEventEmitter. Now, let's dive into the RQEventEmitter, a powerful tool for handling events that involve intensive asynchronous background tasks.

"},{"location":"tutorials/emitters/rq/#what-is-it","title":"What is it?","text":"

\u2003\u2003The RQEventEmitter is a concrete implementation of the EventEmitter that takes advantage of the Redis Queue pub/sub and worker system to execute event emissions.

\u2003\u2003This event emitter is particularly useful when dealing with events that require resource-intensive tasks like model optimization or video processing. By leveraging RQ workers, it enables non-blocking execution and enhances performance.

"},{"location":"tutorials/emitters/rq/#how-it-works","title":"How it Works","text":"

\u2003\u2003The RQEventEmitter seamlessly handles the emission and processing of events by utilizing the RQ package. Here's how it works:

  1. Event emission: When an event is emitted, all associated event handlers are bundled into an EventEmission object, which is then enqueued into the Redis Queue system.
  2. Workers processing: The enqueued event emission object is asynchronously processed by available Python RQ workers, enabling efficient parallel execution.
"},{"location":"tutorials/emitters/rq/#usage","title":"Usage","text":"

To start using the RQEventEmitter, follow these steps:

  1. Install Python RQ: Before proceeding, make sure you have installed the Redis Queue (RQ) optional dependency.
  2. Python RQ worker configuration: The Python RQ workers act as processors for event emission objects. They listen to the Redis Queue pub/sub channel and process tasks when enqueued. To configure the workers, create a file named worker.py and include the worker configuration code. You can refer to the official RQ documentation for more advanced configurations. worker.py
    from multiprocessing import Process\nfrom typing import List\n\nfrom redis import Redis\nfrom rq import Queue, SimpleWorker\nfrom rq.timeouts import TimerDeathPenalty\n\n# Creates a new Redis connection with the given URL\nredis_conn = Redis.from_url(\"redis://default:redispw@localhost:6379\")\ndefault_queue: Queue = Queue(name=\"default\", connection=redis_conn)\n\n\ndef worker_process() -> None:\n    \"\"\"Creates a new Worker instance and starts the work loop.\"\"\"\n\n    class WindowsSimpleWorker(SimpleWorker):\n        \"\"\"\n        A class that inherits from SimpleWorker and is used to\n        create a new worker instance in a Windows based system.\n        \"\"\"\n        death_penalty_class = TimerDeathPenalty\n\n    worker = WindowsSimpleWorker(\n        connection=redis_conn,\n        queues=[default_queue]\n    )\n    worker.work()\n\n\nif __name__ == \"__main__\":\n    # Test connection\n    redis_conn.ping()\n\n    # Set the number of workers. For auto-assignment\n    # use: multiprocessing.cpu_count()\n    num_workers = 1  # Default 1\n\n    # Workers list\n    worker_processes: List[Process] = []\n\n    # Creates and starts new\n    # Processes for each worker\n    for _ in range(num_workers):\n        p = Process(target=worker_process)\n        worker_processes.append(p)\n        p.start()\n\n    # Join every worker process\n    for process in worker_processes:\n        process.join()\n
  3. Define event handlers: After defining the worker file, let's focus on the event handlers. According to the RQ documentation, these functions should not reside in the main module. Therefore, we need to create another module where all our event handlers can be placed. For this example, let's create a file called event_handlers.py and add the handlers to be processed. event_handlers.py
    import asyncio\nimport time\n\nfrom pyventus.linkers import EventLinker\n\n\n@EventLinker.on(\"StringEvent\")\nasync def slow_async_event_callback():\n    print(\"Starting the async slow process...\")\n    await asyncio.sleep(5)\n    print(\"Finishing the async slow process!\")\n\n\n@EventLinker.on(\"StringEvent\")\ndef slow_sync_event_callback():\n    print(\"Starting the sync slow process...\")\n    time.sleep(5)\n    print(\"Finishing the sync slow process!\")\n
  4. Emitting events: Once the previous steps have been completed, the RQ workers can be started by running the worker.py script. Following that, a main.py file should be created to instantiate an RQEventEmitter and configure the Queue where the event emission objects will be enqueued. For full details on the configuration options, please refer to the RQ website and documentation on the enqueue method settings. At this point, we are ready to emit our first event using the RQEventEmitter. main.py
    from redis import Redis\nfrom rq import Queue\n\nfrom pyventus import EventEmitter\nfrom pyventus.emitters.rq import RQEventEmitter\n\n# To ensure Python recognizes the existence of the event handlers, we need to import them.\nfrom event_handlers import slow_sync_event_callback, slow_async_event_callback\n\nredis_conn = Redis.from_url(\"redis://default:redispw@localhost:6379\")\ndefault_queue: Queue = Queue(name=\"default\", connection=redis_conn)\n\nif __name__ == \"__main__\":\n    event_emitter: EventEmitter = RQEventEmitter(queue=default_queue)\n    event_emitter.emit(\"StringEvent\")\n
"},{"location":"tutorials/emitters/rq/#recap","title":"Recap","text":"

\u2003\u2003We've seen how the RQEventEmitter provides an asynchronous approach to event handling using Redis Queues and RQ workers. The main points are:

  • It leverages existing Redis Queue infrastructure for asynchronous task processing.
  • Event emissions are enqueued in Redis, and workers independently process them.
  • This distributed model scales efficiently regardless of workload volume.

"}]} \ No newline at end of file diff --git a/0.5/sitemap.xml b/0.5/sitemap.xml index 93d2285..366e9a6 100644 --- a/0.5/sitemap.xml +++ b/0.5/sitemap.xml @@ -2,90 +2,90 @@ https://mdapena.github.io/pyventus/latest/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/contributing/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/getting-started/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/release-notes/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/event-handler/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/event-linker/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/emitters/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/emitters/asyncio/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/emitters/celery/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/emitters/executor/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/emitters/fastapi/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/api/emitters/rq/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/event-linker/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/event/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/emitters/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/emitters/asyncio/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/emitters/celery/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/emitters/executor/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/emitters/fastapi/ - 2024-10-15 + 2024-10-16 https://mdapena.github.io/pyventus/latest/tutorials/emitters/rq/ - 2024-10-15 + 2024-10-16 \ No newline at end of file diff --git a/0.5/sitemap.xml.gz b/0.5/sitemap.xml.gz index c3ce793eeea5ec4745c2e9ce902e4e230f86b074..cd2e56e273801eb2e022b037a5e806b7a16251d7 100644 GIT binary patch delta 30 mcmcb~bd!l)zMF%Cfr)=2yBuTaMm23lj<)4Lk9Y7eFaQ8}76>5# delta 30 mcmcb~bd!l)zMF%iVFT|(b~#4vjcVGA99>rrdUxAsyncIO Event Emitter
-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  Now that we've covered the base EventEmitter interface, let's examine one of its official implementations: the AsyncIOEventEmitter. @@ -1475,7 +1471,7 @@

Recap& - 2024-04-09 + 2024-10-16 diff --git a/0.5/tutorials/emitters/celery/index.html b/0.5/tutorials/emitters/celery/index.html index 072f19c..886a954 100644 --- a/0.5/tutorials/emitters/celery/index.html +++ b/0.5/tutorials/emitters/celery/index.html @@ -1255,10 +1255,6 @@

Celery Event Emitter

-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  The CeleryEventEmitter provides a powerful way to build event-driven applications that can handle high volumes of work in a scalable and asynchronous manner. @@ -1486,7 +1482,7 @@

Recap& - 2024-04-09 + 2024-10-16 diff --git a/0.5/tutorials/emitters/executor/index.html b/0.5/tutorials/emitters/executor/index.html index 5b6f9fa..d23e43a 100644 --- a/0.5/tutorials/emitters/executor/index.html +++ b/0.5/tutorials/emitters/executor/index.html @@ -1303,10 +1303,6 @@

Executor Event Emitter

-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  The ExecutorEventEmitter leverages Python's concurrent.futures module to asynchronously execute event emissions across threads or processes. This approach helps optimize performance for applications with @@ -1597,7 +1593,7 @@

Recap& - 2024-04-09 + 2024-10-16 diff --git a/0.5/tutorials/emitters/fastapi/index.html b/0.5/tutorials/emitters/fastapi/index.html index 9caaa8e..a27a757 100644 --- a/0.5/tutorials/emitters/fastapi/index.html +++ b/0.5/tutorials/emitters/fastapi/index.html @@ -1255,10 +1255,6 @@

FastAPI Event Emitter

-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  The FastAPIEventEmitter provides a powerful way to build reactive FastAPI applications using an event-driven architecture. It leverages FastAPI's asynchronous BackgroundTasks @@ -1449,7 +1445,7 @@

Recap& - 2024-04-09 + 2024-10-16 diff --git a/0.5/tutorials/emitters/index.html b/0.5/tutorials/emitters/index.html index 8df18c0..9af106f 100644 --- a/0.5/tutorials/emitters/index.html +++ b/0.5/tutorials/emitters/index.html @@ -1278,10 +1278,6 @@

Master the Event Emitter

-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  In the previous tutorial, we learned how to link events with their event handlers using the EventLinker. Now, let's dive into the process of dispatching events and triggering the associated callbacks. @@ -1648,7 +1644,7 @@

Recap& - 2024-04-09 + 2024-10-16 diff --git a/0.5/tutorials/emitters/rq/index.html b/0.5/tutorials/emitters/rq/index.html index 4af2725..f7dbb3a 100644 --- a/0.5/tutorials/emitters/rq/index.html +++ b/0.5/tutorials/emitters/rq/index.html @@ -1255,10 +1255,6 @@

RQ Event Emitter

-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  In the previous sections, we explored different event emitters, such as AsyncIOEventEmitter and ExecutorEventEmitter. Now, let's dive into the RQEventEmitter, a powerful tool for handling events that @@ -1528,7 +1524,7 @@

Recap& - 2024-04-09 + 2024-10-16 diff --git a/0.5/tutorials/event-linker/index.html b/0.5/tutorials/event-linker/index.html index cd4e405..ec2dfde 100644 --- a/0.5/tutorials/event-linker/index.html +++ b/0.5/tutorials/event-linker/index.html @@ -1705,10 +1705,6 @@

The EventLinker Registry

-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  Events are essential for building reactive applications with Pyventus. However, we need a way to connect events to the code that should run in response. This is where the EventLinker comes in. @@ -2474,7 +2470,7 @@

Recap& - 2024-04-09 + 2024-10-16 diff --git a/0.5/tutorials/event/index.html b/0.5/tutorials/event/index.html index 9d5aab2..41a9056 100644 --- a/0.5/tutorials/event/index.html +++ b/0.5/tutorials/event/index.html @@ -1471,10 +1471,6 @@

Exploring Event Types

-
-

🏗️ Work in Progress

-

This page is a work in progress.

-

  In this first tutorial, you'll learn about defining and handling events in Pyventus. Whether you're new to event-driven programming or just getting started with the package, this guide will explain the key concepts. @@ -1818,7 +1814,7 @@

Recap& - 2024-04-09 + 2024-10-16