summaryrefslogtreecommitdiff
path: root/docs/extending.rst
diff options
context:
space:
mode:
authorAlex Grönholm <alex.gronholm@nextday.fi>2014-06-17 20:14:43 +0300
committerAlex Grönholm <alex.gronholm@nextday.fi>2014-06-17 20:14:43 +0300
commit03f05ce16628e708f8e0deeafe98f2c9928de9c7 (patch)
tree226d20f531e430bc809cd17c9a86e3ab8510ef80 /docs/extending.rst
parent73c5e4870e96444b9100dc64cf67988bb3618c00 (diff)
downloadapscheduler-03f05ce16628e708f8e0deeafe98f2c9928de9c7.tar.gz
Finished the "Extending APScheduler" documentation
Diffstat (limited to 'docs/extending.rst')
-rw-r--r--docs/extending.rst130
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.