From c0652a817a5d59601923df4e4fd39eb3ce32c9cc Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:44:22 +0200 Subject: [PATCH 1/4] finishing notifcation docs and restrucutre imports a but --- docs/_static/exengine_bigpicture.svg | 4 +- docs/conf.py | 4 + docs/index.rst | 2 +- docs/usage/notifications.rst | 178 +++++++++++++++++- src/exengine/__init__.py | 3 + src/exengine/base_classes.py | 2 + src/exengine/examples/micromanager_example.py | 2 +- .../test_events_and_notifications.py | 2 +- .../integration_tests/test_imports.py | 22 +++ src/exengine/kernel/__init__.py | 2 + src/exengine/notifications.py | 5 + 11 files changed, 217 insertions(+), 9 deletions(-) create mode 100644 src/exengine/base_classes.py create mode 100644 src/exengine/integration_tests/test_imports.py create mode 100644 src/exengine/notifications.py diff --git a/docs/_static/exengine_bigpicture.svg b/docs/_static/exengine_bigpicture.svg index 419bb95..536e65b 100644 --- a/docs/_static/exengine_bigpicture.svg +++ b/docs/_static/exengine_bigpicture.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dcee6a939c7712485eea89ec99e24963394dbf1df77af6f4485df1538594fc6 -size 10332659 +oid sha256:04766fb288bcc938371067e3edb3e90c87532ec28573d4eed8af956efc3982af +size 10337528 diff --git a/docs/conf.py b/docs/conf.py index 7f38767..1066bcf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,6 +31,10 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "sphinx_rtd_theme" +html_theme_options = { + 'collapse_navigation': True, + 'navigation_depth': 6, +} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/docs/index.rst b/docs/index.rst index cc34895..a4d0a57 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,7 +30,7 @@ Key Features: .. toctree:: - :maxdepth: 2 + :maxdepth: 3 :caption: Contents: design diff --git a/docs/usage/notifications.rst b/docs/usage/notifications.rst index 1806ce2..4ef9944 100644 --- a/docs/usage/notifications.rst +++ b/docs/usage/notifications.rst @@ -1,9 +1,179 @@ .. _notifications: - -############## +============= Notifications -############## +============= + + +Overview +--------- + +Notifications in ExEngine provide a powerful mechanism for asynchronous communication between the Execution and user code. They allow devices, events, and other components to broadcast updates about their status or important occurrences. This enables reactive programming patterns, allowing your software to respond dynamically to changes in the system state or experimental conditions. + +Notifications can serve several purposes: + + - Inform about the completion of asynchronous operations (i.e. those occuring on a different thread) + - Alert about changes in device states + - Communicate errors or warnings + - Provide updates on the progress of long-running tasks + + + +Anatomy of a Notification +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Notifications in ExEngine are instances of classes derived from the base ``Notification`` class. Each notification has several components: + +1. **Category**: Defined by the ``NotificationCategory`` enum, this indicates the broad type of the notification (``Event``, ``Data``, ``Storage``, or ``Device``). + +2. **Description**: A string providing a explanation of what the notification represents. + +3. **Payload**: An optional piece of data associated with the notification, whose type depends on the particular notification. + +4. **Timestamp**: Automatically set to the time the notification was created. + + +Built-in Notification Types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ExEngine provides built-in notification types, such as: + +1. ``EventExecutedNotification``: Posted when an ExecutionEvent completes. Its payload is None, or an Exception if the event didn't complete successfully + +2. ``DataStoredNotification``: Posted when data is stored by a Storage object. Its payload is the ``DataCoordinates`` of the stored data. + + + +Subscribing to Notifications +---------------------------- + +To subscribe to notifications from ExEngine, you can use the ``subscribe_to_notifications`` method of the ``ExecutionEngine`` instance: + +.. code-block:: python + + from exengine import ExecutionEngine + + def notification_handler(notification): + print(f'Got Notification: time {notification.timestamp} and payload {notification.payload}') + + engine = ExecutionEngine.get_instance() + + engine.subscribe_to_notifications(notification_handler) + + + # When finished, unsubscribe + engine.unsubscribe_from_notifications(notification_handler) + + + +Your ``notification_handler`` function will be called each time a new notification is posted. Since there may be many notifications produced by the ``ExecutionEngine``, these handler functions should not contain code that takes a long time to run. + + +Filtering Subscriptions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can filter notifications by type (i.e. a specific notification subclass) or category when subscribing, so that the handler function only gets called for a subset of notifications + +.. code-block:: python + + + # Subscribe to a specific notification type + # SpecificNotificationClass should be a subclass of exengine.base_classes.Notification + engine.subscribe_to_notifications(handler, SpecificNotificationClass) + + + # Subscribe to notifications of a specific category + from exengine.kernel.notification_base import NotificationCategory + + engine.subscribe_to_notifications(handler, NotificationCategory.Data) + +Multiple subscriptions with different filters can be set up: + +.. code-block:: python + + engine.subscribe_to_notifications(handler1, NotificationA) + engine.subscribe_to_notifications(handler2, NotificationCategory.Device) + engine.subscribe_to_notifications(handler3) # No filter, receives all notifications + + + + +Determining Available Notifications +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``ExecutorEvents`` declare the types of notifications they might emit through the ``notification_types`` class attribute. This attribute is a list of Notification types that the event may produce during its execution. + +To discover which notification types are supported by a particular event: + +.. code-block:: python + + print(MyEvent.notification_types) + + +All ExecutorEvents include the ``EventExecutedNotification`` by default. Subclasses can add their additional custom types of notifications. + + + +Awaiting Notifications from a Future +------------------------------------ + +Notifications can be awaited on an :ref:`ExecutionFuture ` in addition to subscribing to ExEngine notifications. This is useful for waiting on specific conditions related to a particular ``ExecutorEvent``: + +.. code-block:: python + + future = engine.submit(some_event) + notification = future.await_notification(SomeSpecificNotification) + +The Future tracks all notifications for its event. If called after a notification occurs, it returns immediately. + + + +Publishing Notifications +------------------------- + +Events can emit notifications using the ``publish_notification`` method: + +.. code-block:: python + + class MyEvent(ExecutorEvent): + notification_types = [MyCustomNotification] + + def execute(self): + # ... do something ... + self.publish_notification(MyCustomNotification(payload="Something happened")) + + + + +Creating Custom Notifications +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To create a custom notification: + +1. Subclass ``exengine.base_classes.Notification`` +2. Use Python's ``@dataclass`` decorator +3. Define ``category`` (from ``exengine.notifications.NotificationCategory`` enum) and ``description`` (string) as class variables +4. Optionally, specify a payload type using a type hint in the class inheritance. For example, ``class MyCustomNotification(Notification[str])`` indicates this notification's payload will be a string. + +Keep payloads lightweight for efficient processing. Example: + +.. code-block:: python + + from dataclasses import dataclass + from exengine.base_classes import Notification + from exengine.notifications import NotificationCategory + + @dataclass + class MyCustomNotification(Notification[str]): + category = NotificationCategory.Device + description = "A custom device status update" + + # Usage + notification = MyCustomNotification(payload="Device XYZ is ready") + + + + + -Notifications provide a mechanism for asynchronous communication within the system. They allow devices, events, and other components to broadcast updates about their status or important occurrences. This feature enables reactive programming patterns, allowing your software to respond dynamically to changes in the system state or experimental conditions. \ No newline at end of file diff --git a/src/exengine/__init__.py b/src/exengine/__init__.py index 93b2513..b4fe1b9 100644 --- a/src/exengine/__init__.py +++ b/src/exengine/__init__.py @@ -4,3 +4,6 @@ A flexible multi-backend execution engine for microscopy """ from ._version import __version__, version_info + +from . import kernel +from .kernel.executor import ExecutionEngine diff --git a/src/exengine/base_classes.py b/src/exengine/base_classes.py new file mode 100644 index 0000000..13e2fbd --- /dev/null +++ b/src/exengine/base_classes.py @@ -0,0 +1,2 @@ +from .kernel.notification_base import Notification +from .kernel.ex_event_base import ExecutorEvent \ No newline at end of file diff --git a/src/exengine/examples/micromanager_example.py b/src/exengine/examples/micromanager_example.py index 7052a86..8569f33 100644 --- a/src/exengine/examples/micromanager_example.py +++ b/src/exengine/examples/micromanager_example.py @@ -9,7 +9,7 @@ # download_and_install_mm() # If needed # Start Micro-Manager core instance with Demo config -create_core_instance(mm_app_path=, mm_config_path=) +create_core_instance() executor = ExecutionEngine() diff --git a/src/exengine/integration_tests/test_events_and_notifications.py b/src/exengine/integration_tests/test_events_and_notifications.py index 05cf96d..9c17d6d 100644 --- a/src/exengine/integration_tests/test_events_and_notifications.py +++ b/src/exengine/integration_tests/test_events_and_notifications.py @@ -2,7 +2,7 @@ Integration tests for events, notifications, futures, and the execution engine """ import pytest -from exengine.kernel.executor import ExecutionEngine +from exengine import ExecutionEngine from exengine.kernel.ex_event_base import ExecutorEvent from exengine.kernel.notification_base import Notification, NotificationCategory from dataclasses import dataclass diff --git a/src/exengine/integration_tests/test_imports.py b/src/exengine/integration_tests/test_imports.py new file mode 100644 index 0000000..ca41650 --- /dev/null +++ b/src/exengine/integration_tests/test_imports.py @@ -0,0 +1,22 @@ +# src/exengine/integration_tests/test_imports.py + +import pytest + + +def test_import_engine(): + try: + from exengine import ExecutionEngine + except ImportError as e: + pytest.fail(f"Import failed for ExecutionEngine: {e}") + +def test_import_base_classes(): + try: + from exengine.base_classes import Notification, ExecutorEvent + except ImportError as e: + pytest.fail(f"Import failed for base_classes: {e}") + +def test_import_notifications(): + try: + from exengine.notifications import NotificationCategory, DataStoredNotification, EventExecutedNotification + except ImportError as e: + pytest.fail(f"Import failed for notifications: {e}") diff --git a/src/exengine/kernel/__init__.py b/src/exengine/kernel/__init__.py index e69de29..76b09d3 100644 --- a/src/exengine/kernel/__init__.py +++ b/src/exengine/kernel/__init__.py @@ -0,0 +1,2 @@ +from .notification_base import NotificationCategory, Notification +from .ex_event_base import ExecutorEvent, EventExecutedNotification \ No newline at end of file diff --git a/src/exengine/notifications.py b/src/exengine/notifications.py new file mode 100644 index 0000000..8469328 --- /dev/null +++ b/src/exengine/notifications.py @@ -0,0 +1,5 @@ +""" +Convenience file for imports +""" +from .kernel.notification_base import NotificationCategory +from .kernel.notification_base import EventExecutedNotification, DataStoredNotification \ No newline at end of file From 8f942f95a020ed81766f47ffef3fd32d40f76e91 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:03:20 -0400 Subject: [PATCH 2/4] change to relative import --- docs/_static/exengine_bigpicture.svg | 4 ++-- docs/_static/tokenization.svg | 4 ++-- src/exengine/examples/implicit_vs_explicit_excutor.py | 6 +++++- src/exengine/kernel/notification_base.py | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/_static/exengine_bigpicture.svg b/docs/_static/exengine_bigpicture.svg index 536e65b..1efb8d6 100644 --- a/docs/_static/exengine_bigpicture.svg +++ b/docs/_static/exengine_bigpicture.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04766fb288bcc938371067e3edb3e90c87532ec28573d4eed8af956efc3982af -size 10337528 +oid sha256:719799206d70f54ae412138691cafc347b5d659c4a6af9c963857e122793fc9b +size 10335093 diff --git a/docs/_static/tokenization.svg b/docs/_static/tokenization.svg index c4ab2af..0e1c310 100644 --- a/docs/_static/tokenization.svg +++ b/docs/_static/tokenization.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:540099a4fa9fa2d6a204364c1519356f7b1b09f5110409b31937044cfcffb1d9 -size 3181107 +oid sha256:6bca86284be246b6c8d09f8749c888a12f5911e48eb7eee2402741eee3df7f90 +size 770001 diff --git a/src/exengine/examples/implicit_vs_explicit_excutor.py b/src/exengine/examples/implicit_vs_explicit_excutor.py index 1741bc0..4fafe2e 100644 --- a/src/exengine/examples/implicit_vs_explicit_excutor.py +++ b/src/exengine/examples/implicit_vs_explicit_excutor.py @@ -13,11 +13,15 @@ # This occurs on the executor thread. The event is submitted to the executor and its result is awaited, # meaning the call will block until the method is executed. -z_stage.set_position(100) +z_stage.set_position(100, thread='device_setting_thread') # it is equivalent to: executor.submit(SetPosition1DEvent(position=100, device=z_stage)).await_execution() +executor.submit(SetPosition1DEvent(position=100, device=z_stage), thread='device_setting_thread') +executor.submit(ReadoutImages(), thread='readout_thread') + + executor.shutdown() \ No newline at end of file diff --git a/src/exengine/kernel/notification_base.py b/src/exengine/kernel/notification_base.py index 0db1f87..5500798 100644 --- a/src/exengine/kernel/notification_base.py +++ b/src/exengine/kernel/notification_base.py @@ -5,7 +5,7 @@ from datetime import datetime from dataclasses import field import uuid -from exengine.kernel.data_coords import DataCoordinates +from kernel.data_coords import DataCoordinates TNotificationPayload = TypeVar('TNotificationPayload') From 2a7442061f78376d03119aded57ece33237e1632 Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:18:33 -0400 Subject: [PATCH 3/4] change imports to relative --- pyproject.toml | 5 +++-- src/exengine/kernel/data_handler.py | 10 +++++----- src/exengine/kernel/data_storage_api.py | 2 +- src/exengine/kernel/device.py | 4 ++-- src/exengine/kernel/device_types_base.py | 3 +-- src/exengine/kernel/ex_event_base.py | 13 +++---------- src/exengine/kernel/ex_event_capabilities.py | 4 ++-- src/exengine/kernel/ex_future.py | 6 +++--- src/exengine/kernel/executor.py | 15 +++++++-------- src/exengine/kernel/notification_base.py | 2 +- 10 files changed, 28 insertions(+), 36 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b55d258..61f2459 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,8 @@ classifiers = [ "Operating System :: OS Independent" ] dependencies = [ -"pydantic" + "pydantic", + "numpy" ] dynamic = ["version"] @@ -21,7 +22,7 @@ readme = "README.md" [project.optional-dependencies] test = ["pytest"] -# all backends +# all backends -- this should be the union of all the specific backends below all = [ "mmpycorex", "ndstorage" diff --git a/src/exengine/kernel/data_handler.py b/src/exengine/kernel/data_handler.py index f4ba88d..ff830cf 100644 --- a/src/exengine/kernel/data_handler.py +++ b/src/exengine/kernel/data_handler.py @@ -5,14 +5,14 @@ from pydantic.types import JsonValue from dataclasses import dataclass -from exengine.kernel.notification_base import DataStoredNotification -from exengine.kernel.data_coords import DataCoordinates -from exengine.kernel.data_storage_api import DataStorageAPI +from .notification_base import DataStoredNotification +from .data_coords import DataCoordinates +from .data_storage_api import DataStorageAPI from typing import TYPE_CHECKING if TYPE_CHECKING: - from exengine.kernel.ex_future import ExecutionFuture + from .ex_future import ExecutionFuture class _PeekableQueue(queue.Queue): @@ -56,7 +56,7 @@ def __init__(self, storage: DataStorageAPI, _executor=None): # delayed import to avoid circular imports if _executor is None: - from exengine.kernel.executor import ExecutionEngine + from .executor import ExecutionEngine self._engine = ExecutionEngine.get_instance() else: self._engine = _executor diff --git a/src/exengine/kernel/data_storage_api.py b/src/exengine/kernel/data_storage_api.py index ff4a624..ece138f 100644 --- a/src/exengine/kernel/data_storage_api.py +++ b/src/exengine/kernel/data_storage_api.py @@ -3,7 +3,7 @@ """ from typing import Protocol, runtime_checkable, Union, Dict -from exengine.kernel.data_coords import DataCoordinates +from .data_coords import DataCoordinates import numpy as np from pydantic.types import JsonValue diff --git a/src/exengine/kernel/device.py b/src/exengine/kernel/device.py index ca31531..6488974 100644 --- a/src/exengine/kernel/device.py +++ b/src/exengine/kernel/device.py @@ -7,8 +7,8 @@ from weakref import WeakSet from dataclasses import dataclass -from exengine.kernel.ex_event_base import ExecutorEvent -from exengine.kernel.executor import ExecutionEngine +from .ex_event_base import ExecutorEvent +from .executor import ExecutionEngine import threading import sys diff --git a/src/exengine/kernel/device_types_base.py b/src/exengine/kernel/device_types_base.py index 36e9b76..31816a1 100644 --- a/src/exengine/kernel/device_types_base.py +++ b/src/exengine/kernel/device_types_base.py @@ -5,8 +5,7 @@ from abc import abstractmethod, ABC from typing import Tuple, List, Iterable, Union, Optional, Sequence import numpy as np -from exengine.kernel.device import DeviceMetaclass - +from .device import DeviceMetaclass class Device(ABC, metaclass=DeviceMetaclass): diff --git a/src/exengine/kernel/ex_event_base.py b/src/exengine/kernel/ex_event_base.py index e41ea5a..0ce3ef4 100644 --- a/src/exengine/kernel/ex_event_base.py +++ b/src/exengine/kernel/ex_event_base.py @@ -1,20 +1,13 @@ import warnings -import numpy as np from typing import Optional, Any,ClassVar, Type, List, Dict, Union, Iterable from abc import ABC, abstractmethod, ABCMeta import weakref -from dataclasses import dataclass, field -from exengine.kernel.notification_base import Notification -import itertools +from .notification_base import Notification -from typing import TYPE_CHECKING - -from exengine.kernel.notification_base import EventExecutedNotification -from exengine.kernel.data_coords import DataCoordinates, DataCoordinatesIterator -from exengine.kernel.data_handler import DataHandler +from .notification_base import EventExecutedNotification # if TYPE_CHECKING: # avoid circular imports -from exengine.kernel.ex_future import ExecutionFuture +from .ex_future import ExecutionFuture class _ExecutorEventMeta(ABCMeta): diff --git a/src/exengine/kernel/ex_event_capabilities.py b/src/exengine/kernel/ex_event_capabilities.py index 78af02e..8be0437 100644 --- a/src/exengine/kernel/ex_event_capabilities.py +++ b/src/exengine/kernel/ex_event_capabilities.py @@ -8,8 +8,8 @@ import numpy as np import warnings -from exengine.kernel.data_coords import DataCoordinates, DataCoordinatesIterator -from exengine.kernel.data_handler import DataHandler +from .data_coords import DataCoordinates, DataCoordinatesIterator +from .data_handler import DataHandler diff --git a/src/exengine/kernel/ex_future.py b/src/exengine/kernel/ex_future.py index 516aceb..9975c24 100644 --- a/src/exengine/kernel/ex_future.py +++ b/src/exengine/kernel/ex_future.py @@ -1,8 +1,8 @@ from typing import Union, Optional, Any, Dict, Tuple, Sequence, Set, TypeVar, Type, Iterable import threading import warnings -from exengine.kernel.data_coords import DataCoordinates, DataCoordinatesIterator -from exengine.kernel.notification_base import Notification +from .data_coords import DataCoordinates, DataCoordinatesIterator +from .notification_base import Notification import numpy as np from dataclasses import field @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: # avoid circular imports - from exengine.kernel.ex_event_base import ExecutorEvent + from .ex_event_base import ExecutorEvent class ExecutionFuture: diff --git a/src/exengine/kernel/executor.py b/src/exengine/kernel/executor.py index 27ad886..b88f9d8 100644 --- a/src/exengine/kernel/executor.py +++ b/src/exengine/kernel/executor.py @@ -9,11 +9,10 @@ from typing import Union, Iterable, List, Callable, Any, Type import queue -from exengine.kernel.notification_base import Notification, NotificationCategory -from exengine.kernel.ex_event_base import ExecutorEvent -from exengine.kernel.ex_future import ExecutionFuture +from .notification_base import Notification, NotificationCategory +from .ex_future import ExecutionFuture -from exengine.kernel.data_handler import DataHandler +from .data_handler import DataHandler class MultipleExceptions(Exception): @@ -176,7 +175,7 @@ def check_exceptions(self): else: raise MultipleExceptions(exceptions) - def submit(self, event_or_events: Union[ExecutorEvent, Iterable[ExecutorEvent]], + def submit(self, event_or_events: Union["ExecutorEvent", Iterable["ExecutorEvent"]], transpile: bool = True, prioritize: bool = False, use_free_thread: bool = False, data_handler: DataHandler = None) -> Union[ExecutionFuture, Iterable[ExecutionFuture]]: """ @@ -228,7 +227,7 @@ def submit(self, event_or_events: Union[ExecutorEvent, Iterable[ExecutorEvent]], global ExecutorEvent if isinstance(ExecutorEvent, str): # runtime import to avoid circular imports - from exengine.kernel.ex_event_base import ExecutorEvent + from .ex_event_base import ExecutorEvent if isinstance(event_or_events, ExecutorEvent): event_or_events = [event_or_events] @@ -242,7 +241,7 @@ def submit(self, event_or_events: Union[ExecutorEvent, Iterable[ExecutorEvent]], return futures[0] return futures - def _submit_single_event(self, event: ExecutorEvent, use_free_thread: bool = False, prioritize: bool = False): + def _submit_single_event(self, event: "ExecutorEvent", use_free_thread: bool = False, prioritize: bool = False): """ Submit a single event for execution """ @@ -293,7 +292,7 @@ class _ExecutionThreadManager: or events in its queue with the is_free method. """ - _deque: Deque[ExecutorEvent] + _deque: Deque["ExecutorEvent"] thread: threading.Thread def __init__(self, name='UnnamedExectorThread'): diff --git a/src/exengine/kernel/notification_base.py b/src/exengine/kernel/notification_base.py index 5500798..3c2342e 100644 --- a/src/exengine/kernel/notification_base.py +++ b/src/exengine/kernel/notification_base.py @@ -5,7 +5,7 @@ from datetime import datetime from dataclasses import field import uuid -from kernel.data_coords import DataCoordinates +from .data_coords import DataCoordinates TNotificationPayload = TypeVar('TNotificationPayload') From 139be7f62ac780d20f3a036f063373db0fdc07ca Mon Sep 17 00:00:00 2001 From: Henry Pinkard <7969470+henrypinkard@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:25:23 -0400 Subject: [PATCH 4/4] adjust imports --- src/exengine/kernel/executor.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/exengine/kernel/executor.py b/src/exengine/kernel/executor.py index b88f9d8..1b05139 100644 --- a/src/exengine/kernel/executor.py +++ b/src/exengine/kernel/executor.py @@ -10,6 +10,7 @@ import queue from .notification_base import Notification, NotificationCategory +from .ex_event_base import ExecutorEvent from .ex_future import ExecutionFuture from .data_handler import DataHandler @@ -175,7 +176,7 @@ def check_exceptions(self): else: raise MultipleExceptions(exceptions) - def submit(self, event_or_events: Union["ExecutorEvent", Iterable["ExecutorEvent"]], + def submit(self, event_or_events: Union[ExecutorEvent, Iterable[ExecutorEvent]], transpile: bool = True, prioritize: bool = False, use_free_thread: bool = False, data_handler: DataHandler = None) -> Union[ExecutionFuture, Iterable[ExecutionFuture]]: """ @@ -224,10 +225,10 @@ def submit(self, event_or_events: Union["ExecutorEvent", Iterable["ExecutorEvent - 'use_free_thread' is essential for operations that need to run independently, like cancellation events. """ - global ExecutorEvent - if isinstance(ExecutorEvent, str): - # runtime import to avoid circular imports - from .ex_event_base import ExecutorEvent + # global ExecutorEvent + # if isinstance(ExecutorEvent, str): + # # runtime import to avoid circular imports + # from .ex_event_base import ExecutorEvent if isinstance(event_or_events, ExecutorEvent): event_or_events = [event_or_events] @@ -241,7 +242,7 @@ def submit(self, event_or_events: Union["ExecutorEvent", Iterable["ExecutorEvent return futures[0] return futures - def _submit_single_event(self, event: "ExecutorEvent", use_free_thread: bool = False, prioritize: bool = False): + def _submit_single_event(self, event: ExecutorEvent, use_free_thread: bool = False, prioritize: bool = False): """ Submit a single event for execution """ @@ -292,7 +293,7 @@ class _ExecutionThreadManager: or events in its queue with the is_free method. """ - _deque: Deque["ExecutorEvent"] + _deque: Deque[ExecutorEvent] thread: threading.Thread def __init__(self, name='UnnamedExectorThread'):