Source code for pymt.events.manager

"""Manage the execution of events on a timeline.

The :class:`EventManager` puts events on a :class:`~pymt.timeline.Timeline` and
then
orchestrates the setup, execution, and teardown of the events as the time line
advances. Events are classes that implement an event interface. That is, they
implement the following methods:

* `initialize()`
* `run(time)`
* `finalize()`

Because the :class:`EventManager` class itself is event-like, it is able to
manage other :class:`EventManager` instances and even handle recursive events.

Examples
--------
Create an event that prints, "hello".

>>> class PrintHello:
...     def initialize(self):
...         print("hello from initialize")
...     def run(self, time):
...         print("hello!")
...     def finalize(self):
...         print("hello from finalize")

Add an instance of the event to be run at a regular interval to the manager.

>>> mngr = EventManager([(PrintHello(), 2.)])
>>> mngr.initialize()
hello from initialize
>>> mngr.run(1.)
>>> mngr.run(2.)
hello!
>>> mngr.finalize()
hello from finalize

You can also use an :class:`EventManager` as a context,

>>> with EventManager([(PrintHello(), 2.)]) as mngr:
...     mngr.run(4.)
hello from initialize
hello!
hello!
hello from finalize
"""
from configparser import ConfigParser
from io import StringIO

from ..timeline import Timeline
from ..utils.prefix import names_with_prefix


[docs]class EventManager: """ Parameters ---------- events : dict-like Events as event-object/repeat interval pairs. See Also -------- :class:`pymt.timeline.Timeline` Examples -------- Create an :class:`EventManager` without any events. >>> mngr = EventManager() >>> mngr.time 0.0 >>> len(mngr) 0 Create a manager with one recurring event. >>> from pymt.events.empty import PassEvent >>> mngr = EventManager([(PassEvent(), 1.)]) >>> len(mngr) 1 Create a manager with two recurring events. >>> mngr = EventManager([(PassEvent(), 1.), (PassEvent(), 2.)]) >>> len(mngr) 2 """ def __init__(self, *args): if len(args) > 1: raise TypeError( "__init__() takes 1 or 2 arguments (%d given)" % (len(args) + 1,) ) self._timeline = Timeline(*args) self._initializing = False self._initialized = False self._running = False self._finalizing = False self._order = list(*args)
[docs] def initialize(self): """Initialize the managed events. Execute the `initialize` methods of each of the events that are being managed, making sure the manager is not initialized if it is already in the initialization process. Events are initialized in the order they were given at creation. """ if not self._initializing: self._initializing = True for event, _ in self._order: try: event.initialize() except Exception: print("error initializing") print(event) raise self._initialized = True
[docs] def run(self, stop_time): """Run events until some time. Execute the `run` method of each of the events being managed until *stop_time* is reached on the time line. Parameters ---------- stop_time : float Time to run events until. """ self.initialize() if not self._running: self._running = True for event in self._timeline.iter_until(stop_time): try: event.run except AttributeError: event.update(self._timeline.time) else: event.run(self._timeline.time) self._running = False
[docs] def finalize(self): """Finalize managed events. Execute the `finalize` method for each of the events being managed. Events are finalized in the reverse order in which they were initialized, and only if the manager has not yet been initialized. """ if self._initialized: if not self._finalizing: self._finalizing = True # for event in self._timeline.events: for event, _ in self._order[::-1]: event.finalize() self._initialized = False
[docs] def add_recurring_event(self, event, interval): """Add a managed event. Add *event* to the list of managed events and run it with the recurrence interval, *interval*. Parameters ---------- event : event-like Event to be managed. interval : float Recurrence interval for the event. """ self._timeline.add_recurring_event(event, interval) self._order.append((event, interval))
@property def time(self): """Current time along the time line.""" return self._timeline.time
[docs] @classmethod def from_string(cls, source, prefix=""): """Create an `EventManager` from a string. Parameters ---------- source : str Ini-formatted string. prefix : str Prefix for section. Returns ------- EventManager A newly-created `EventManager`. See Also -------- :meth:`from_path` : Alternate constructor that uses a path name. """ config = ConfigParser() config.readfp(StringIO(source)) return cls._from_config(config, prefix=prefix)
[docs] @classmethod def from_path(cls, path, prefix=""): """Create an `EventManager` from a file. Parameters ---------- path : str Path to ini-formatted file. prefix : str Prefix for section. Returns ------- EventManager A newly-created `EventManager`. See Also -------- :meth:`from_string` : Alternate constructor that uses a string. """ config = ConfigParser() config.read(path) return cls._from_config(config, prefix=prefix)
@classmethod def _from_config(cls, config, prefix=""): event_names = names_with_prefix(config.sections(), prefix) events = [] for name in event_names: events.append((name, config.get(name, "interval"))) return cls(events) def __enter__(self): self.initialize() return self def __exit__(self, exception_type, value, traceback): self.finalize() def __len__(self): return len(self._order)