Extending BasePlanner

Databay comes with two implementations of BasePlanner - ApsPlanner and SchedulePlanner. If you require custom scheduling functionality outside of these two interfaces, you can create your own implementation of BasePlanner. Have a look at the two existing implementations for reference: ApsPlanner and SchedulePlanner.

To extend the BasePlanner you need to provide a way of executing Link.transfer method repeatedly by implementing the following methods. Note that some of these methods are private since they are called internally by BasePlanner and should not be executed directly.

_schedule

Schedule a Link. This method runs whenever add_links is called and should not be executed directly. It should accept a link and add the Link.transfer method to the scheduling system you’re using. Note that you do not need to store the link in your planner - BasePlanner will automatically store it under BasePlanner.links when add_links is called. It isn’t required for the scheduling to be already running when _schedule is called.

Each link comes with a datetime.timedelta interval providing the frequency at which its Link.transfer method should be run. Use Link.interval and schedule according to the interval specified.

If the scheduler you’re using utilises some form of task-managing job objects, you must assign these to the link being scheduled using Link.set_job. This is to ensure the job can be correctly destroyed later when remove_links is called.

Example from ApsPlanner._schedule:

def _schedule(self, link:Link):
    job = self._scheduler.add_job(link.transfer,
        trigger=IntervalTrigger(seconds=link.interval.total_seconds()))

    link.set_job(job)

_unschedule

Unschedule a Link. This method runs whenever remove_links is called and should not be executed directly. It should accept a link and remove it from the scheduling system you’re using. Note that you do not need to remove the link from your planner - BasePlanner will automatically remove that link from BasePlanner.links when remove_links is called. It isn’t required for the scheduling to be already stopped when _unschedule is called.

If the scheduler you’re using utilises some form of task-managing job objects, you may access these using Link.job in order to correctly destroy them if necessary when _unschedule is called.

Example from ApsPlanner._unschedule:

def _unschedule(self, link:Link):
    if link.job is not None:
        link.job.remove()
        link.set_job(None)

_start_planner

Start the scheduling. This method runs whenever BasePlanner.start is called and should not be executed directly. It should begin the scheduling of links.

This method will be called just after all Inlet.on_start and Outlet.on_start are called.

Example from ApsPlanner._start_planner:

def _start_planner(self):
    self._scheduler.start()

_shutdown_planner

Shutdown the scheduling. This method runs whenever BasePlanner.shutdown is called and should not be executed directly. It should shut down the scheduling of links.

A wait parameter is provided that you can pass down to your scheduling system if it allows waiting for the remaining jobs to complete before shutting down.

This method will be called just before all Inlet.on_shutdown and Outlet.on_shutdown are called.

Example from ApsPlanner._shutdown_planner:

def _shutdown_planner(self, wait:bool=True):
    self._scheduler.shutdown(wait=wait)

Running property

BasePlanner.running property should return a boolean value indicating whether the scheduler is currently running. By default this property always returns True.

Exceptions

When implementing your planner you should consider that links may raise exceptions when executing. Your planner should anticipate this and allow handling the exceptions appropriately to ensure continuous execution. BasePlanner exposes a protected BasePlanner._on_exception method that can be called to handle the exception, allowing to ignore exceptions when ignore_exceptions=True is passed on construction. Otherwise the exceptions will be logged and the planner will attempt a graceful shutdown. Both ApsPlanner and SchedulePlanner support this behaviour by default. See Exception handling for more.

Immediate transfer on start

By default BasePlanner will execute Link.transfer function on all its links once upon calling BasePlanner.start. This is to avoid having to wait for the link’s interval to expire before the first transfer. You can disable this behaviour by passing immediate_transfer=False parameter on construction of the BasePlanner to disable it for all governed links or individually for selected links by setting their immediate_transfer to False.

Shutdown atexit

Each BasePlanner registers an atexit callback, which will attempt to gracefully shut the planner down if it is created with shutdown_at_exit parameter set to True.


Next Steps

  1. Learn about extending Inlets and Outlets.

  2. See the Examples