diff options
author | Alex Grönholm <alex.gronholm@nextday.fi> | 2014-06-17 20:14:43 +0300 |
---|---|---|
committer | Alex Grönholm <alex.gronholm@nextday.fi> | 2014-06-17 20:14:43 +0300 |
commit | 03f05ce16628e708f8e0deeafe98f2c9928de9c7 (patch) | |
tree | 226d20f531e430bc809cd17c9a86e3ab8510ef80 /docs/extending.rst | |
parent | 73c5e4870e96444b9100dc64cf67988bb3618c00 (diff) | |
download | apscheduler-03f05ce16628e708f8e0deeafe98f2c9928de9c7.tar.gz |
Finished the "Extending APScheduler" documentation
Diffstat (limited to 'docs/extending.rst')
-rw-r--r-- | docs/extending.rst | 130 |
1 files changed, 106 insertions, 24 deletions
diff --git a/docs/extending.rst b/docs/extending.rst index b328c4d..4347e82 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -2,52 +2,134 @@ Extending APScheduler ##################### -This document is meant to explain how to develop your custom triggers, job stores, executors or schedulers. +This document is meant to explain how to develop your custom triggers, job stores, executors and schedulers. Custom triggers --------------- -Triggers determine the times when the jobs should be run. -APScheduler comes with three built-in triggers -- -:class:`~apscheduler.triggers.simple.SimpleTrigger`, -:class:`~apscheduler.triggers.interval.IntervalTrigger` and -:class:`~apscheduler.triggers.cron.CronTrigger`. You don't normally use these -directly, since the scheduler has shortcut methods for these built-in -triggers. +The built-in triggers cover the needs of the majority of all users. +However, some users may need specialized scheduling logic. To that end, the trigger system was made pluggable. -If you need to use some specialized scheduling algorithm, you can implement -that as a custom trigger class. The only method a trigger class has to -implement is ``get_next_fire_time``. This method receives a starting date -(a :class:`~datetime.datetime` object) as its sole argument. It should return -the next time the trigger will fire (starting from and including the given time), -according to whatever scheduling logic you wish to implement. If no such -datetime can be computed, it should return ``None``. +To implement your scheduling logic, subclass :class:`~apscheduler.triggers.base.BaseTrigger`. +Look at the interface documentation in that class. Then look at the existing trigger implementations. +That should give you a good idea what is expected of a trigger implementation. -To schedule a job using your custom trigger, you can either extends the -:class:`~apscheduler.schedulers.base.BaseScheduler` class to include your own shortcuts, -or use the generic :meth:`~apscheduler.schedulers.base.BaseScheduler.add_job` method to -add your jobs. +To use your trigger, you can use :meth:`~apscheduler.schedulers.base.BaseScheduler.add_job` like this:: + + trigger = MyTrigger(arg1='foo') + scheduler.add_job(target, trigger) + +You can also register it as a plugin so you can use can use the alternate form of ``add_jobstore``:: + + scheduler.add_job(target, 'my_trigger', arg1='foo') + +This is done by adding an entry point in your project's :file:`setup.py`: + +.. code-block:: ini + + ... + entry_points={ + 'apscheduler.triggers': ['my_trigger = mytoppackage.subpackage:MyTrigger'] + } Custom job stores ----------------- -Job store classes must inherit from :class:`apscheduler.jobstores.base.BaseJobStore`. This class provides stubbed out -methods which any implementation should override. These methods also contain -useful documentation regarding the responsibilities of a job store. It is -recommended that you look at the existing job store implementations for +If you want to store your jobs in a fancy new NoSQL database, or a totally custom datastore, you can implement your +own job store by subclassing :class:`~apscheduler.jobstores.base.BaseJobStore`. + +A job store typically serializes the :class:`~apscheduler.job.Job` objects given to it, and constructs new Job objects +from binary data when they are loaded from the backing store. It is important that the job store restores the +``_scheduler`` and ``_jobstore_alias`` attribute of any Job that it creates. Refer to existing implementations for examples. -To use your job store, you must add it to the scheduler as normal:: +It should be noted that :class:`~apscheduler.jobstores.memory.MemoryJobStore` is special in that it does not +deserialize the jobs. This comes with its own problems, which it handles in its own way. +If your job store does serialize jobs, you can of course use a serializer other than pickle. +You should, however, use the ``__getstate__`` and ``__setstate__`` special methods to respectively get and set the Job +state. Pickle uses them implicitly. + +To use your job store, you can add it to the scheduler like this:: jobstore = MyJobStore() scheduler.add_jobstore(jobstore, 'mystore') +You can also register it as a plugin so you can use can use the alternate form of ``add_jobstore``:: + + scheduler.add_jobstore('my_jobstore', 'mystore') + +This is done by adding an entry point in your project's :file:`setup.py`: + +.. code-block:: ini + + ... + entry_points={ + 'apscheduler.jobstores': ['my_jobstore = mytoppackage.subpackage:MyJobStore'] + } + Custom executors ---------------- +If you need custom logic for executing your jobs, you can create your own executor classes. +One scenario for this would be if you want to use distributed computing to run your jobs on other nodes. + +Start by subclassing :class:`~apscheduler.executors.base.BaseExecutor`. +The responsibilities of an executor are as follows: + * Performing any initialization when ``start()`` is called + * Releasing any resources when ``shutdown()`` is called + * Keeping track of the number of instances of each job running on it, and refusing to run more than the maximum + * Notifying the scheduler of the results of the job + +If your executor needs to serialize the jobs, make sure you either use pickle for it, or invoke the ``__getstate__`` and +``__setstate__`` special methods to respectively get and set the Job state. Pickle uses them implicitly. + +To use your executor, you can add it to the scheduler like this:: + + executor = MyExecutor() + scheduler.add_executor(executor, 'myexecutor') + +You can also register it as a plugin so you can use can use the alternate form of ``add_executor``:: + + scheduler.add_executor('my_executor', 'myexecutor') + +This is done by adding an entry point in your project's :file:`setup.py`: + +.. code-block:: ini + + ... + entry_points={ + 'apscheduler.executors': ['my_executor = mytoppackage.subpackage:MyExecutor'] + } + Custom schedulers ----------------- + +A typical situation where you would want to make your own scheduler subclass is when you want to integrate it with your +application framework of choice. + +Your custom scheduler should always be a subclass of :class:`~apscheduler.schedulers.base.BaseScheduler`. +But if you're not adapting to a framework that relies on callbacks, consider subclassing +:class:`~apscheduler.schedulers.blocking.BlockingScheduler` instead. + +The most typical extension points for scheduler subclasses are: + * :meth:`~apscheduler.schedulers.base.BaseScheduler.start` + must be overridden to wake up the scheduler for the first time + * :meth:`~apscheduler.schedulers.base.BaseScheduler.shutdown` + must be overridden to release resources allocated during ``start()`` + * :meth:`~apscheduler.schedulers.base.BaseScheduler.wakeup` + must be overridden to manage the timernotify the scheduler of changes in the job store + * :meth:`~apscheduler.schedulers.base.BaseScheduler._create_lock` + override if your framework uses some alternate locking implementation (like gevent) + * :meth:`~apscheduler.schedulers.base.BaseScheduler._create_default_executor` + override if you need to use an alternative default executor + +.. important:: Remember to call the superclass implementations of overridden methods, even abstract ones + (unless they're empty). + +The most important responsibility of the scheduler subclass is to manage the scheduler's sleeping based on the return +values of ``_process_jobs()``. This can be done in various ways, including setting timeouts in ``wakeup()`` or running +a blocking loop in ``start()``. Again, see the existing scheduler classes for examples. |