Release Notes
v0.7.0 January 7, 2025¶
Breaking Changes¶
-
The
typing-extensions
package is now a required dependency for Pyventus. This dependency has been added to support advanced typing features in older versions of Python. -
All previous event-driven features must now be imported from the inner package
pyventus.events
instead of directly frompyventus
. These changes were necessary due to the integration of the reactive programming paradigm. This series of refactors were implemented not only to ensure an organized codebase but also to establish a clear boundary between event handling and reactive programming features, enabling optimized imports based on the required paradigm. -
The inheritance structure of the
EventEmitter
has been replaced with composition using the newProcessingService
interface. This interface establishes a common ground for processing calls, such as event emissions, in a decoupled and flexible manner. TheEventEmitter
is now a concrete class that requires aProcessingService
instance, referred to asevent_processor
, for initialization. These changes not only preserve the overall behavior and workflow of theEventEmitter
but also enhance its modularity and flexibility. -
The
EventLinker
class has experienced multiple method renames and return type modifications to align with the new redesigned codebase, enhancing the API for improved usability and intuitiveness. Below is a detailed list of the breaking changes made to theEventLinker
class:- The inner class
EventLinkageWrapper
was renamed toEventLinkerSubCtx
for consistency with the new concept of a subscription context and now extends from the base classSubscriptionContext
. The overall workflow remains roughly the same but was reworked to align with its base class and introduce optimizations based on user needs. - The method
get_events()
now returns aset
of all registered events instead of alist
with non-duplicated events. - The method
get_event_handlers()
was renamed toget_subscribers()
for consistency, and it now returns aset
of all registered subscribers instead of alist
with non-duplicated subscribers. - The method
get_events_by_event_handler()
was renamed toget_events_from_subscribers()
for consistency. It now returns aset
of events associated with the specified subscribers instead of alist
of non-duplicated events associated with the specified subscribers. This method now also supports retrieving events from multiple subscribers instead of only one at a time. - The method
get_event_handlers_by_events()
was renamed toget_subscribers_from_events()
for consistency. It now returns aset
of subscribers associated with the specified events instead of alist
of non-duplicated subscribers associated with the provided events. Additionally, a new flag calledpop_onetime_subscribers
was added to remove and return those subscribers that are one-time subscriptions. - The method
unsubscribe()
was renamed toremove()
for consistency with the new concept of subscription and the encapsulation of the unsubscription process through theteardown_callback
. This method now allows you to remove one event and subscriber from the registry at a time instead of multiple events of the given subscriber. - The method
remove_event_handler()
was renamed toremove_subscriber()
for consistency. - Parameters named
event_handler
were renamed tosubscriber
for consistency.
- The inner class
-
The
EventHandler
has been refactored from a class to an interface, outlining the overall workflow and essential protocols for event handling. However, the previous implementation of theEventHandler
has been transitioned to one of its concrete classes namedEventSubscriber
. This newEventSubscriber
class not only implements theEventHandler
interface but also combines it with theSubscription
base class, providing a convenient way to both handle event responses and manage the subscription lifecycle.
Added¶
-
Added a reactive programming module to expand Python's event-driven capabilities for data-oriented processes and the ability to react efficiently to state changes over time.
- Added the
Observable
base class, which defines a lazy push-style notification mechanism for streaming data to subscribers. - Added the
ObservableTask
class, an observable subclass that encapsulates a unit of work and offers a mechanism for streaming its results reactively. - Added the
as_observable_task()
decorator to easily convert any given callable into an observable task. - Added the
Observer
interface, which defines the overall workflow and essential protocols for responding to notifications from an observable. - Added the
Subscriber
class, which combines theObserver
interface with theSubscription
base class to provide a convenient way to respond to state changes emitted by an observable and manage the subscription lifecycle.
- Added the
-
Added the
Unsubscribable
interface, which provides a standardized method for objects to unsubscribe from a source and release any associated resources. -
Added the
Subscription
base class to simplify subscription management and resource cleanup with ateardown_callback
that is called during unsubscription. -
Added the
SubscriptionContext
base class, which defines the overall workflow for subscription contexts, allowing the user to define step-by-step the object that will later be subscribed to the specified source. -
Added a new package global exception called
PyventusImportException
, which is a custom Pyventus exception for handling missing imports within the library. -
Added the
MultiBidict
data structure, a generic multikeyed, multivalued bidirectional dictionary that offers a flexible mapping structure for efficient lookups, updates, and deletions of keys and their corresponding values. -
Introduced the
ProcessingService
interface to define flexible execution strategies for various use cases. This release includes the following concrete implementations:AsyncIOProcessingService
: A processing service that utilizes theAsyncIO
framework to handle the execution of calls.CeleryProcessingService
: A processing service that utilizes theCelery
framework to handle the execution of calls.ExecutorProcessingService
: A processing service that utilizes the Python'sExecutor
to handle the execution of calls.FastAPIProcessingService
: A processing service that uses FastAPI'sBackgroundTasks
to handle the execution of calls.RedisProcessingService
: A processing service that utilizes theRedis Queue
framework to handle the execution of calls.
-
Added the
CallableWrapper
class to encapsulate callables and provide a unified asynchronous interface for their execution. -
Added new features to the
EventLinker
class, including the following methods:- Introduced the
get_valid_subscriber()
method for a centralized mechanism to validate event subscribers. - Added the
is_empty()
method to efficiently check if the main registry is empty. - Added the
get_event_count()
method to return the total number of events in the registry. - Added the
get_subscriber_count()
method to return the total number of subscribers in the registry. - Added the
get_event_count_from_subscriber()
method to return the number of events for a specific subscriber. - Added the
get_subscriber_count_from_event()
method to return the number of subscribers for a specific event. - Added the
contains_event()
method to check if a specific event is present in the registry. - Added the
contains_subscriber()
method to check if a specific subscriber is present in the registry. - Added the
are_linked()
method to determine if a specific event is linked to a given subscriber. - Introduced the
stateful_subctx
parameter in theonce()
andon()
methods to configure theEventLinkerSubCtx
behavior and optimize the subscription context based on user needs.
- Introduced the
-
Introduced the
EventSubscriber
class, which combines event handling capabilities with subscription lifecycle management. -
A new
benchmarks
package has been added to thetests
directory for performance evaluation of Pyventus. This release introduces theEventEmitterBenchmark
, which measures the efficiency of theEventEmitter
in handling event emissions. -
Added a set of utilities for creating preconfigured event emitter instances, making the setup process easier. These utilities also provide retro compatibility with the previous class-based design.
Changed¶
-
Enhanced the
emit()
method in theEventEmitter
to support the emission of global events (...
). -
Moved callback utilities to a dedicated module within
pyventus.core
for improved organization and reusability. -
Standardized the structure of Python classes and their representation, including the use of the
@override()
decorator for consistency andmypy
static type checking. -
Standardized the structure of the
pyproject.toml
file. -
Enhanced Pyventus logs by adding process and thread IDs for better debugging.
-
Switched from the
Black
formatter toRuff
for improved development efficiency and enhanced code quality. -
Refactored all project docstrings to follow a standardized format, enhancing consistency and clarity in the documentation.
-
Upgraded several development dependencies in the
pyproject.toml
, includingpytest-asyncio
from version0.21.0
to0.24.0
, to enable global configuration of theasyncio_mode
. -
Simplified the
EventCallbackType
type alias by removing the unnecessaryParamSpec
. -
Refactored the test suite to improve validation across all package features, ensuring correctness and achieving 100% code coverage.
Optimized¶
-
The time complexity of the
emit()
method in theEventEmitter
class has been significantly optimized. It has been reduced from \(O(E \cdot S^2)\) to \(O(E \cdot S)\), where:- \(E\): Denotes the total number of events in the event linker.
- \(S\): Corresponds to the total number of subscribers in the event linker.
This optimization results from the more efficient management of one-time subscribers by the event linker during event emission. Instead of traversing the entire event linker registry to remove each one-time subscriber involved in the event emission, it now iterates solely through the linked events.
-
Major optimizations have been implemented for the
EventLinker
class through the integration of theMultiBidict
data structure. This data structure is a multikeyed, multivalued bidirectional dictionary implementation that enables efficient lookups, updates, and deletions of events and their corresponding subscribers. Despite utilizing a bidirectional mapping structure, its memory footprint remains minimal due to the use of references between keys and values instead of duplication, which limits the impact to the additional dictionary and set data structures.-
The time complexity of the method
get_subscribers()
, previously known asget_event_handlers()
, has been reduced from \(O(E \cdot S)\) to \(O(S)\), where:- \(E\): Denotes the total number of events in the event linker.
- \(S\): Corresponds to the total number of subscribers in the event linker.
-
The time complexity of the
get_events_from_subscribers()
, previously known asget_events_by_event_handler()
, has been reduced from \(O(E \cdot S)\) to \(O(E)\), where:- \(E\): Denotes the total number of events in the event linker.
- \(S\): Corresponds to the total number of subscribers in the event linker.
-
The time complexity of the method
remove()
, previously known asunsubscribe()
, has been enhanced from \(O(S)\) to a constant time complexity of \(O(1)\), where:- \(S\): Corresponds to the total number of subscribers in the event linker.
-
The time complexity of the method
remove_subscriber()
, previously known asremove_event_handler()
, has been reduced from \(O(E \cdot S)\) to \(O(E)\), where:- \(E\): Denotes the total number of events in the event linker.
- \(S\): Corresponds to the total number of subscribers in the event linker.
-
-
Introduced
__slots__
in several classes to optimize memory usage and enhance attribute access speed.
Benchmarks¶
Finally, to provide a quick visualization of the overall improvements and illustrate the time complexity enhancements of this release, a series of benchmarks were conducted. These benchmarks were specifically designed for the event emission process, as it encompasses all event-driven features and provides a clear overview of the improvements. Following this, a detailed explanation of the benchmarks and their results is presented.
-
Methodology: The benchmarks utilized a volume testing approach to assess how different subscription counts affect the event emission time.
-
Environment: The benchmarks were conducted in the following environment:
- Operating System: Windows 11 64bit (v10.0.22631)
- CPU: AMD Ryzen 5 2600, 3400.0 MHz, 6 cores, 12 logical processors
- Total Memory: 16 GB
- Python Version: 3.12.7
-
Setup: The benchmark setup consisted of two key components: a main Python script that managed the overall workflow of the benchmarks and the
EventEmitterBenchmark
class, which was essential for standardizing and organizing the performance tests. Following this, the main workflow of the benchmarks will be outlined, including the required packages and the adjustable settings available for different tests.Benchmark Workflow and Settings (
main.py
)Before running the benchmarks, make sure all necessary packages are installed. You can install them using the following
pip
command:Additionally, an extra package is required for visualizing the results:
matplotlib
. You can also install it with apip
command as follows:Once the packages are installed, you can configure the benchmarks in the
main.py
script as needed and execute it. The script will first install Pyventusv0.6.0
, run the benchmarks in a separate process, and then uninstall it. Next, it will install the current version of Pyventus and run the benchmarks again in another process. Finally, the results for each version will be saved in aJSON
file, and plots will be generated based on the benchmark reports.main.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
import gc import os import subprocess import sys from concurrent.futures import ProcessPoolExecutor from dataclasses import asdict from json import dumps import matplotlib.pyplot as plt import numpy as np def run_event_emitter_benchmarks(): from tests.benchmarks.event_emitter_benchmark import EventEmitterBenchmark print(f"Pyventus v{EventEmitterBenchmark.PYVENTUS_VERSION}\n") print("Starting benchmarks...") # Define a list of benchmarks to run with different configurations. benchmarks: list[EventEmitterBenchmark] = [ # Benchmark with SINGLE event subscription mode and NONE one-time subscriptions. EventEmitterBenchmark( event_subscription_mode=EventEmitterBenchmark.EventSubscriptionMode.SINGLE, onetime_subscription_mode=EventEmitterBenchmark.OneTimeSubscriptionMode.NONE, subscription_sizes=[100, 500, 1000, 5000, 10000], num_repeats=5, num_executions=1250, ), # Benchmark with SINGLE event subscription mode and ALL one-time subscriptions. EventEmitterBenchmark( event_subscription_mode=EventEmitterBenchmark.EventSubscriptionMode.SINGLE, onetime_subscription_mode=EventEmitterBenchmark.OneTimeSubscriptionMode.ALL, subscription_sizes=[100, 500, 1000, 5000, 10000], num_repeats=5, num_executions=1250, ), # Benchmark with ALL event subscription mode and ALL one-time subscriptions. EventEmitterBenchmark( event_subscription_mode=EventEmitterBenchmark.EventSubscriptionMode.ALL, onetime_subscription_mode=EventEmitterBenchmark.OneTimeSubscriptionMode.ALL, subscription_sizes=[100, 500, 1000, 5000, 10000], num_repeats=5, num_executions=1250, ), # Benchmark with RANDOM event subscription mode and RANDOM one-time subscriptions. EventEmitterBenchmark( event_subscription_mode=EventEmitterBenchmark.EventSubscriptionMode.RANDOM, onetime_subscription_mode=EventEmitterBenchmark.OneTimeSubscriptionMode.RANDOM, subscription_sizes=[100, 500, 1000, 5000, 10000], num_repeats=3, num_executions=500, ), ] # Initialize an empty list to store reports generated by the benchmarks. reports: list[EventEmitterBenchmark.Report] = [] # Iterate through each benchmark and execute it. for benchmark in benchmarks: reports.append(benchmark()) # Return the list of reports. return reports def plot_event_emitter_benchmark_comparison( title: str, subscription_sizes: list[int], benchmark_bars: dict[tuple[str, str], list[float]], output_file: str ) -> None: # Prepare the x positions for the bars based on the number of subscription sizes x_positions = np.arange(len(subscription_sizes)) bar_width = 0.355 # Width of each bar bar_offset = 0 # Offset for positioning bars # Create a figure and axis for the plot fig, ax = plt.subplots(figsize=(9.6, 5), layout="constrained") # Initialize the maximum y-value for setting the y-axis limit later max_y_value = 0 # Loop through each benchmark report to create bars for (label, color), heights in benchmark_bars.items(): # Calculate the offset for the current set of bars current_offset = bar_width * bar_offset # Create bars for the current benchmark data bars = ax.bar(x=(x_positions + current_offset), height=heights, width=bar_width, label=label, color=color) # Add labels on top of the bars with formatted emission times ax.bar_label(bars, padding=2, fontsize=10, fmt=lambda value: f"{value:.5f}") # Update the maximum y-value if the current heights exceed it max_y_value = max(max(heights), max_y_value) # Increment the offset for the next set of bars bar_offset += 1 # Set plot title and labels ax.set_title(title, fontsize=10, pad=0) ax.legend(loc="upper left", ncols=3) ax.set_xlabel("Subscription Count", labelpad=7) ax.set_ylabel("Event Emission Time (seconds)", labelpad=7) # Set x-ticks to correspond to subscription sizes ax.set_xticks(x_positions + (bar_width / 2), subscription_sizes) # Set y-axis limit to accommodate the highest bar with some padding ax.set_ylim(ymax=max_y_value + (max_y_value * 0.15), auto=True) # Save the plot to the specified filename plt.savefig(output_file) def main(): def clear_console(): """Clear the console based on the operating system.""" if os.name == "nt": # For Windows os.system("cls") else: # For macOS and Linux os.system("clear") def uninstall_pyventus() -> None: """Uninstall the currently installed version of pyventus using pip.""" subprocess.check_call([sys.executable, "-m", "pip", "uninstall", "-y", "pyventus"]) def install_pyventus(version: str = "") -> None: """Install pyventus using pip, with an optional version parameter.""" version = ("." if version == "." else (f"pyventus=={version}" if version else "pyventus")) subprocess.check_call([sys.executable, "-m", "pip", "install", version]) # Uninstall the currently installed version of pyventus to avoid conflicts # and ensure a clean environment for the installation of the specified version (0.6.0). uninstall_pyventus() install_pyventus(version="0.6.0") clear_console() # Run the benchmark with pyventus v0.6.0 in a separate process to avoid conflicts. with ProcessPoolExecutor() as executor: fut = executor.submit(run_event_emitter_benchmarks) pyventus_v060_reports = fut.result() clear_console() # Remove executor reference del executor # Uninstall the current version of pyventus again to prepare for the next version. uninstall_pyventus() install_pyventus(version=".") clear_console() # Force a garbage collection. gc.collect() # Run the benchmark with the current version of pyventus (0.7.0) in a separate process. with ProcessPoolExecutor() as executor: fut = executor.submit(run_event_emitter_benchmarks) pyventus_v070_reports = fut.result() clear_console() print("Almost done. Saving reports...") # Save the reports for pyventus v0.6.0 to a JSON file. with open("dist/pyventus_v060_eeb_reports.json", "w") as f1: f1.write(f"{dumps([asdict(report) for report in pyventus_v060_reports])}\n") # Save the reports for pyventus v0.7.0 to a JSON file. with open("dist/pyventus_v070_eeb_reports.json", "w") as f2: f2.write(f"{dumps([asdict(report) for report in pyventus_v070_reports])}\n") clear_console() # Indicate that charts are being generated. print("Generating charts...") # Generate comparison charts for the two versions of pyventus. for i, (v060_report, v070_report) in enumerate(zip(pyventus_v060_reports, pyventus_v070_reports, strict=False)): plot_event_emitter_benchmark_comparison( title=( f"Impact of Subscription Count on Event Emission Time: Comparison of Pyventus v0.6.0 and v0.7.0\n" f"(Event Subscription: {v060_report.event_subscription_mode.capitalize()}, " f"One-time Subscription: {v060_report.onetime_subscription_mode.capitalize()}, " f"Repeats: {v060_report.num_repeats}, Executions: {v060_report.num_executions})\n" ), subscription_sizes=v060_report.subscription_sizes, benchmark_bars={ ("Pyventus v0.6.0", "#0aaec4"): [ measurement.execution_time for measurement in v060_report.measurements ], ("Pyventus v0.7.0", "#fba700"): [ measurement.execution_time for measurement in v070_report.measurements ], }, output_file=f"dist/pyventus_eeb_chart_{i}.png", ) print("All Done!") if __name__ == "__main__": main()
As shown in the previous script, the
EventEmitterBenchmark
class provides several parameters that can be configured to tailor the performance tests for various scenarios. This flexibility enables a comprehensive evaluation of the event emission process. Below are the key configurations available in theEventEmitterBenchmark
class:-
Event Subscription Mode: This parameter defines how event subscriptions behave during the benchmark.
- Single: In this mode, each subscriber is limited to a single event.
- Random: This mode allows subscribers to register for the specified event along with a random selection of additional events from the registry.
- All: When this mode is selected, subscribers will register for the specified event as well as all other registered events.
-
One-Time Subscription Mode: This parameter defines how one-time subscriptions are handled in the benchmark.
- None: In this mode, no one-time subscribers will be registered; the
once
property is always set toFalse
. - Random: This mode randomizes the
once
property, allowing for a mix of one-time and regular subscriptions. - All: When this mode is selected, all subscriptions are treated as one-time subscriptions; the
once
property is always set toTrue
.
- None: In this mode, no one-time subscribers will be registered; the
-
Subscription Sizes: This parameter specifies a list of varying subscription sizes used in the benchmark.
-
A mathematical formula is employed to proportionally subdivide the number of events and subscribers based on the specified subscription size.
- Number of Events: Calculated as: \(\lfloor \sqrt{num\_subscriptions} \rfloor\).
- Subscribers per Event: Calculated as: \(\lfloor num\_subscriptions/num\_events \rfloor\).
- Remaining Subscribers: Calculated as: \(num\_subscriptions - num\_events \cdot num\_subscribers\).
-
Note that depending on the selected event subscription mode, the number of subscribers per event may exceed the calculated proportion. For instance, if the event subscription mode is set to
ALL
, each subscriber will be registered across all event sets, resulting in each event set's length being equal to the subscription size.
-
-
Number of Repeats: This parameter indicates how many times the benchmark is repeated.
- Number of Executions: This parameter represents the total number of executions performed during the benchmark.
-
-
Metrics Collected: The main metric used to assess the efficiency of the event emission process during the performance tests is the Event Emission Time. This metric tracks the time (in seconds) that it takes to complete the event emission process for each subscription size.
-
Calculation Method: To determine the event emission time for a given subscription size, the event emission process is executed multiple times (as specified by
num_executions
) and repeated for a number of iterations (as declared bynum_repeats
). The elapsed time for each execution is recorded, and the median of these recorded times is calculated to establish the event emission time for that repetition, which helps reduce the impact of outliers. Once all repetitions are completed, the final event emission time is determined by calculating the mean of the medians from each repetition. Additionally, to minimize timing noise, the garbage collector is disabled during the tests, and theperf_counter
function is used for accurate measurements. -
Results:
-
Event Emitter Benchmark 01: This benchmark was configured with the event subscription mode set to Single and the one-time subscription mode set to None. The test was repeated 5 times, with 1,250 executions for each benchmark repetition.
Benchmark Report (
JSON
format)Subscription Size Metric Pyventus v0.6.0
Pyventus v0.7.0
Improvement 100 Event Emission Time (s) 0.00217135 0.00216965 0.08% faster 500 Event Emission Time (s) 0.00229231 0.00222423 2.97% faster 1000 Event Emission Time (s) 0.00231953 0.00229093 1.23% faster 1000 Event Emission Time (s) 0.00234521 0.00231134 1.44% faster 10000 Event Emission Time (s) 0.00237625 0.00235225 1.01% faster -
Event Emitter Benchmark 02: This benchmark was configured with the event subscription mode set to Single and the one-time subscription mode set to All. The test was repeated 5 times, with 1,250 executions for each benchmark repetition.
Benchmark Report (
JSON
format)Subscription Size Metric Pyventus v0.6.0
Pyventus v0.7.0
Improvement 100 Event Emission Time (s) 0.00215918 0.00212170 1.74% faster 500 Event Emission Time (s) 0.00247831 0.00227472 8.21% faster 1000 Event Emission Time (s) 0.00263769 0.00231161 12.36% faster 5000 Event Emission Time (s) 0.00496245 0.00233253 53% faster 10000 Event Emission Time (s) 0.00928467 0.00236797 74.5% faster -
Event Emitter Benchmark 03: This benchmark was configured with the event subscription mode set to All and the one-time subscription mode set to All. The test was repeated 5 times, with 1,250 executions for each benchmark repetition.
Benchmark Report (
JSON
format)Subscription Size Metric Pyventus v0.6.0
Pyventus v0.7.0
Improvement 100 Event Emission Time (s) 0.00326616 0.00286774 12.2% faster 500 Event Emission Time (s) 0.00985788 0.00701719 28.82% faster 1000 Event Emission Time (s) 0.02087080 0.01340036 35.79% faster 5000 Event Emission Time (s) 0.30020038 0.10753673 64.18% faster 10000 Event Emission Time (s) 1.60752635 0.25623989 84.06% faster -
Event Emitter Benchmark 04: This benchmark was configured with both the event and one-time subscription modes set to Random. The test was repeated 3 times, with 500 executions for each benchmark repetition.
Benchmark Report (
JSON
format)Subscription Size Metric Pyventus v0.6.0
Pyventus v0.7.0
Improvement 100 Event Emission Time (s) 0.00205765 0.00200465 2.64% faster 500 Event Emission Time (s) 0.00766185 0.00416398 45.65% faster 1000 Event Emission Time (s) 0.02636188 0.00654307 75.18% faster 5000 Event Emission Time (s) 1.12663353 0.03050753 97.29% faster 10000 Event Emission Time (s) 6.60188468 0.07419393 98.88% faster
As shown in the previous tables, reports, and charts, the improvements made to the event-driven features are significant, especially when one-time subscribers are involved and randomized. It's important to note that these optimizations apply not only to the event emission process but also to other components, such as the
EventLinker
. -
v0.6.0 October 19, 2024¶
Added¶
- Added support for Python
3.13
, ensuring compatibility with the latest features and improvements. - Added
mike
package integration tomkdocs-material
for documentation versioning. This allows users to access previous documentation alongside new changes, ensuring that legacy content remains intact for reference. Additionally, a newdev
documentation has been introduced to showcase the current development of the package, including unreleased features and updates.
Changed¶
- Updated documentation links from absolute to relative paths to prevent broken links and avoid redirecting users to incorrect documentation versions, ensuring consistent navigation throughout the docs.
- Upgraded the
download-artifact
andcache
actions tov4
in thepublish-to-pypi.yml
workflow. - Updated the
deploy-docs.yml
workflow to deploy bothdev
and versioned documentation usingmike
's CLI commands.
Fixed¶
- Fixed broken links to non-versioned documentation by adding a custom
404.html
page togh-pages
, which redirects users to the first version of the documentation when no version is specified, or to a new custom 404 page with helpful suggestions.
v0.5.0 April 9, 2024¶
Breaking Changes¶
- Removed the base
Event
class due to improved event semantics and unnecessary redundancy. - Renamed the
get_event_registry()
method ofEventLinker
toget_registry()
. - Renamed the
__event_registry
inner property ofEventLinker
to__registry
. - Renamed the
get_events_by_handler()
method ofEventLinker
toget_events_by_event_handler()
. - Renamed the
get_handlers_by_events()
method ofEventLinker
toget_event_handlers_by_events()
. - Renamed the protected method
_executor_callback()
of theExecutorEventEmitter
to_callback()
. - Renamed the task name of
CeleryEventEmitter
from_executor
topyventus_executor
to avoid collisions with other task names.
Added¶
- Added
__slots__
toEventLinkageWrapper
class for more efficient memory usage. - Extended support for subscription and emission of any
dataclass
object, removing the limitation of onlyEvent
subclasses. - Added the
force_async
parameter to theEventHandler
class andEventLinker
subscription methods to be able to optimize the execution ofsync
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 theonAny()
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.
Changed¶
- 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
andEventEmitter
classes. - Improved validation of variable instances in the event emitters,
EventLinker
, andEventHandler
. - 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 thegit-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.
Removed¶
- Removed the default value of the
once
flag in theEventHandler
class.
Fixed¶
- Fixed and standardized all package docstrings and code comments for consistency and clarity.
- Addressed minor errors and details in the documentation.
v0.4.1 January 30, 2024¶
Changed¶
- 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
, andLICENSE
. - Refactored documentation dependencies into an optional dependency called
docs
. - Updated the
deploy-docs.yml
GitHub workflow to leverage the new optional dependencydocs
. - 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.
Fixed¶
- Addressed minor errors and details in the documentation.
v0.4.0 January 6, 2024¶
Added¶
- Added
FastAPIEventEmitter
implementation to facilitate seamless integration with theFastAPI
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.
Changed¶
- Updated
pyproject.toml
with the new optional dependency forFastAPI
integration.
Fixed¶
- Addressed minor errors in the Pyventus documentation to improve accuracy and clarity.
v0.3.0 December 29, 2023¶
Breaking Changes¶
- Introduced
EventEmission
object to encapsulate the processing of event emissions. This changes the_execute()
method ofEventEmitter
but provides a cleaner, more scalable, and efficient approach. - Renamed all debug flags from
debug_mode
todebug
for enhanced clarity and consistency. - Renamed EventEmitter's
_execute()
method to_process()
to better reflect its purpose of processing event emissions.
Added¶
- 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.
Changed¶
- 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 theEventEmission
class reference. - Updated
pyproject.toml
with the new optional dependency forCelery
integration. - Updated
mypy
ignore flags to properly silence specific false positive error codes.
Fixed¶
- Addressed minor errors in the Pyventus documentation.
v0.2.1 December 17, 2023¶
Changed¶
- 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.
Fixed¶
- 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.
v0.2.0 December 16, 2023¶
Added¶
- 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.
Changed¶
- Renamed the
tests.yml
workflow torun-tests.yml
. - Updated the
deploy-docs.yml
workflow with themkdocs-git-authors
plugin dependency. - Modified the
mkdocs.yml
config file by adding thesite_url
andsite_author
properties. - Updated the
pyproject.toml
file with themkdocs-git-authors
plugin dependency and python package keywords.
Fixed¶
- Fixed the python version in the
deploy-docs.yml
workflow. - Resolved issues with relative links in the documentation.
v0.1.0 December 15, 2023¶
Initial Implementation¶
This 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.