1########## 2User guide 3########## 4 5 6Installing APScheduler 7---------------------- 8 9The preferred installation method is by using `pip <http://pypi.python.org/pypi/pip/>`_:: 10 11 $ pip install apscheduler 12 13If you don't have pip installed, you can easily install it by downloading and running 14`get-pip.py <https://bootstrap.pypa.io/get-pip.py>`_. 15 16If, for some reason, pip won't work, you can manually `download the APScheduler distribution 17<https://pypi.python.org/pypi/APScheduler/>`_ from PyPI, extract and then install it:: 18 19 $ python setup.py install 20 21 22Code examples 23------------- 24 25The source distribution contains the :file:`examples` directory where you can find many working 26examples for using APScheduler in different ways. The examples can also be 27`browsed online <https://github.com/agronholm/apscheduler/tree/3.x/examples/?at=master>`_. 28 29 30Basic concepts 31-------------- 32 33APScheduler has four kinds of components: 34 35* triggers 36* job stores 37* executors 38* schedulers 39 40*Triggers* contain the scheduling logic. Each job has its own trigger which determines when the job 41should be run next. Beyond their initial configuration, triggers are completely stateless. 42 43*Job stores* house the scheduled jobs. The default job store simply keeps the jobs in memory, but 44others store them in various kinds of databases. A job's data is serialized when it is saved to a 45persistent job store, and deserialized when it's loaded back from it. Job stores (other than the 46default one) don't keep the job data in memory, but act as middlemen for saving, loading, updating 47and searching jobs in the backend. Job stores must never be shared between schedulers. 48 49*Executors* are what handle the running of the jobs. They do this typically by submitting the 50designated callable in a job to a thread or process pool. When the job is done, the executor 51notifies the scheduler which then emits an appropriate event. 52 53*Schedulers* are what bind the rest together. You typically have only one scheduler running in your 54application. The application developer doesn't normally deal with the job stores, executors or 55triggers directly. Instead, the scheduler provides the proper interface to handle all those. 56Configuring the job stores and executors is done through the scheduler, as is adding, modifying and 57removing jobs. 58 59 60Choosing the right scheduler, job store(s), executor(s) and trigger(s) 61---------------------------------------------------------------------- 62 63Your choice of scheduler depends mostly on your programming environment and what you'll be using 64APScheduler for. Here's a quick guide for choosing a scheduler: 65 66* :class:`~apscheduler.schedulers.blocking.BlockingScheduler`: 67 use when the scheduler is the only thing running in your process 68* :class:`~apscheduler.schedulers.background.BackgroundScheduler`: 69 use when you're not using any of the frameworks below, and want the scheduler to run in the 70 background inside your application 71* :class:`~apscheduler.schedulers.asyncio.AsyncIOScheduler`: 72 use if your application uses the asyncio module 73* :class:`~apscheduler.schedulers.gevent.GeventScheduler`: 74 use if your application uses gevent 75* :class:`~apscheduler.schedulers.tornado.TornadoScheduler`: 76 use if you're building a Tornado application 77* :class:`~apscheduler.schedulers.twisted.TwistedScheduler`: 78 use if you're building a Twisted application 79* :class:`~apscheduler.schedulers.qt.QtScheduler`: 80 use if you're building a Qt application 81 82Simple enough, yes? 83 84To pick the appropriate job store, you need to determine whether you need job persistence or not. 85If you always recreate your jobs at the start of your application, then you can probably go with 86the default (:class:`~apscheduler.jobstores.memory.MemoryJobStore`). But if you need your jobs to 87persist over scheduler restarts or application crashes, then your choice usually boils down to what 88tools are used in your programming environment. If, however, you are in the position to choose 89freely, then :class:`~apscheduler.jobstores.sqlalchemy.SQLAlchemyJobStore` on a 90`PostgreSQL <http://www.postgresql.org/>`_ backend is the recommended choice due to its strong data 91integrity protection. 92 93Likewise, the choice of executors is usually made for you if you use one of the frameworks above. 94Otherwise, the default :class:`~apscheduler.executors.pool.ThreadPoolExecutor` should be good 95enough for most purposes. If your workload involves CPU intensive operations, you should consider 96using :class:`~apscheduler.executors.pool.ProcessPoolExecutor` instead to make use of multiple CPU 97cores. You could even use both at once, adding the process pool executor as a secondary executor. 98 99When you schedule a job, you need to choose a *trigger* for it. The trigger determines the logic by 100which the dates/times are calculated when the job will be run. APScheduler comes with three 101built-in trigger types: 102 103* :mod:`~apscheduler.triggers.date`: 104 use when you want to run the job just once at a certain point of time 105* :mod:`~apscheduler.triggers.interval`: 106 use when you want to run the job at fixed intervals of time 107* :mod:`~apscheduler.triggers.cron`: 108 use when you want to run the job periodically at certain time(s) of day 109 110It is also possible to combine multiple triggers into one which fires either on times agreed on by 111all the participating triggers, or when any of the triggers would fire. For more information, see 112the documentation for :mod:`combining triggers <apscheduler.triggers.combining>`. 113 114You can find the plugin names of each job store, executor and trigger type on their respective API 115documentation pages. 116 117 118.. _scheduler-config: 119 120Configuring the scheduler 121------------------------- 122 123APScheduler provides many different ways to configure the scheduler. You can use a configuration 124dictionary or you can pass in the options as keyword arguments. You can also instantiate the 125scheduler first, add jobs and configure the scheduler afterwards. This way you get maximum 126flexibility for any environment. 127 128The full list of scheduler level configuration options can be found on the API reference of the 129:class:`~apscheduler.schedulers.base.BaseScheduler` class. Scheduler subclasses may also have 130additional options which are documented on their respective API references. Configuration options 131for individual job stores and executors can likewise be found on their API reference pages. 132 133Let's say you want to run BackgroundScheduler in your application with the default job store and 134the default executor:: 135 136 from apscheduler.schedulers.background import BackgroundScheduler 137 138 139 scheduler = BackgroundScheduler() 140 141 # Initialize the rest of the application here, or before the scheduler initialization 142 143This will get you a BackgroundScheduler with a MemoryJobStore named "default" and a 144ThreadPoolExecutor named "default" with a default maximum thread count of 10. 145 146Now, suppose you want more. You want to have *two* job stores using *two* executors and you also 147want to tweak the default values for new jobs and set a different timezone. 148The following three examples are completely equivalent, and will get you: 149 150* a MongoDBJobStore named "mongo" 151* an SQLAlchemyJobStore named "default" (using SQLite) 152* a ThreadPoolExecutor named "default", with a worker count of 20 153* a ProcessPoolExecutor named "processpool", with a worker count of 5 154* UTC as the scheduler's timezone 155* coalescing turned off for new jobs by default 156* a default maximum instance limit of 3 for new jobs 157 158Method 1:: 159 160 from pytz import utc 161 162 from apscheduler.schedulers.background import BackgroundScheduler 163 from apscheduler.jobstores.mongodb import MongoDBJobStore 164 from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore 165 from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor 166 167 168 jobstores = { 169 'mongo': MongoDBJobStore(), 170 'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') 171 } 172 executors = { 173 'default': ThreadPoolExecutor(20), 174 'processpool': ProcessPoolExecutor(5) 175 } 176 job_defaults = { 177 'coalesce': False, 178 'max_instances': 3 179 } 180 scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc) 181 182Method 2:: 183 184 from apscheduler.schedulers.background import BackgroundScheduler 185 186 187 # The "apscheduler." prefix is hard coded 188 scheduler = BackgroundScheduler({ 189 'apscheduler.jobstores.mongo': { 190 'type': 'mongodb' 191 }, 192 'apscheduler.jobstores.default': { 193 'type': 'sqlalchemy', 194 'url': 'sqlite:///jobs.sqlite' 195 }, 196 'apscheduler.executors.default': { 197 'class': 'apscheduler.executors.pool:ThreadPoolExecutor', 198 'max_workers': '20' 199 }, 200 'apscheduler.executors.processpool': { 201 'type': 'processpool', 202 'max_workers': '5' 203 }, 204 'apscheduler.job_defaults.coalesce': 'false', 205 'apscheduler.job_defaults.max_instances': '3', 206 'apscheduler.timezone': 'UTC', 207 }) 208 209Method 3:: 210 211 from pytz import utc 212 213 from apscheduler.schedulers.background import BackgroundScheduler 214 from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore 215 from apscheduler.executors.pool import ProcessPoolExecutor 216 217 218 jobstores = { 219 'mongo': {'type': 'mongodb'}, 220 'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') 221 } 222 executors = { 223 'default': {'type': 'threadpool', 'max_workers': 20}, 224 'processpool': ProcessPoolExecutor(max_workers=5) 225 } 226 job_defaults = { 227 'coalesce': False, 228 'max_instances': 3 229 } 230 scheduler = BackgroundScheduler() 231 232 # .. do something else here, maybe add jobs etc. 233 234 scheduler.configure(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc) 235 236 237Starting the scheduler 238---------------------- 239 240Starting the scheduler is done by simply calling 241:meth:`~apscheduler.schedulers.base.BaseScheduler.start` on the scheduler. For schedulers other 242than :class:`~apscheduler.schedulers.blocking.BlockingScheduler`, this call will return immediately and 243you can continue the initialization process of your application, possibly adding jobs to the 244scheduler. 245 246For BlockingScheduler, you will only want to call 247:meth:`~apscheduler.schedulers.base.BaseScheduler.start` after you're done with any initialization 248steps. 249 250.. note:: After the scheduler has been started, you can no longer alter its settings. 251 252 253Adding jobs 254----------- 255 256There are two ways to add jobs to a scheduler: 257 258#. by calling :meth:`~apscheduler.schedulers.base.BaseScheduler.add_job` 259#. by decorating a function with :meth:`~apscheduler.schedulers.base.BaseScheduler.scheduled_job` 260 261The first way is the most common way to do it. The second way is mostly a convenience to declare 262jobs that don't change during the application's run time. The 263:meth:`~apscheduler.schedulers.base.BaseScheduler.add_job` method returns a 264:class:`apscheduler.job.Job` instance that you can use to modify or remove the job later. 265 266You can schedule jobs on the scheduler **at any time**. If the scheduler is not yet running when 267the job is added, the job will be scheduled *tentatively* and its first run time will only be 268computed when the scheduler starts. 269 270It is important to note that if you use an executor or job store that serializes the job, it will 271add a couple requirements on your job: 272 273#. The target callable must be globally accessible 274#. Any arguments to the callable must be serializable 275 276Of the builtin job stores, only MemoryJobStore doesn't serialize jobs. 277Of the builtin executors, only ProcessPoolExecutor will serialize jobs. 278 279.. important:: If you schedule jobs in a persistent job store during your application's 280 initialization, you **MUST** define an explicit ID for the job and use ``replace_existing=True`` 281 or you will get a new copy of the job every time your application restarts! 282 283.. tip:: To run a job immediately, omit ``trigger`` argument when adding the job. 284 285 286Removing jobs 287------------- 288 289When you remove a job from the scheduler, it is removed from its associated job store and will not 290be executed anymore. There are two ways to make this happen: 291 292#. by calling :meth:`~apscheduler.schedulers.base.BaseScheduler.remove_job` with the job's ID and 293 job store alias 294#. by calling :meth:`~apscheduler.job.Job.remove` on the Job instance you got from 295 :meth:`~apscheduler.schedulers.base.BaseScheduler.add_job` 296 297The latter method is probably more convenient, but it requires that you store somewhere the 298:class:`~apscheduler.job.Job` instance you received when adding the job. For jobs scheduled via the 299:meth:`~apscheduler.schedulers.base.BaseScheduler.scheduled_job`, the first way is the only way. 300 301If the job's schedule ends (i.e. its trigger doesn't produce any further run times), it is 302automatically removed. 303 304Example:: 305 306 job = scheduler.add_job(myfunc, 'interval', minutes=2) 307 job.remove() 308 309Same, using an explicit job ID:: 310 311 scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id') 312 scheduler.remove_job('my_job_id') 313 314 315Pausing and resuming jobs 316------------------------- 317 318You can easily pause and resume jobs through either the :class:`~apscheduler.job.Job` instance or 319the scheduler itself. When a job is paused, its next run time is cleared and no further run times 320will be calculated for it until the job is resumed. To pause a job, use either method: 321 322* :meth:`apscheduler.job.Job.pause` 323* :meth:`apscheduler.schedulers.base.BaseScheduler.pause_job` 324 325To resume: 326 327* :meth:`apscheduler.job.Job.resume` 328* :meth:`apscheduler.schedulers.base.BaseScheduler.resume_job` 329 330 331Getting a list of scheduled jobs 332-------------------------------- 333 334To get a machine processable list of the scheduled jobs, you can use the 335:meth:`~apscheduler.schedulers.base.BaseScheduler.get_jobs` method. It will return a list of 336:class:`~apscheduler.job.Job` instances. If you're only interested in the jobs contained in a 337particular job store, then give a job store alias as the second argument. 338 339As a convenience, you can use the :meth:`~apscheduler.schedulers.base.BaseScheduler.print_jobs` 340method which will print out a formatted list of jobs, their triggers and next run times. 341 342 343Modifying jobs 344-------------- 345 346You can modify any job attributes by calling either :meth:`apscheduler.job.Job.modify` or 347:meth:`~apscheduler.schedulers.base.BaseScheduler.modify_job`. You can modify any Job attributes 348except for ``id``. 349 350Example:: 351 352 job.modify(max_instances=6, name='Alternate name') 353 354If you want to reschedule the job -- that is, change its trigger, you can use either 355:meth:`apscheduler.job.Job.reschedule` or 356:meth:`~apscheduler.schedulers.base.BaseScheduler.reschedule_job`. 357These methods construct a new trigger for the job and recalculate its next run time based on the 358new trigger. 359 360Example:: 361 362 scheduler.reschedule_job('my_job_id', trigger='cron', minute='*/5') 363 364 365Shutting down the scheduler 366--------------------------- 367 368To shut down the scheduler:: 369 370 scheduler.shutdown() 371 372By default, the scheduler shuts down its job stores and executors and waits until all currently 373executing jobs are finished. If you don't want to wait, you can do:: 374 375 scheduler.shutdown(wait=False) 376 377This will still shut down the job stores and executors but does not wait for any running 378tasks to complete. 379 380 381Pausing/resuming job processing 382------------------------------- 383 384It is possible to pause the processing of scheduled jobs:: 385 386 scheduler.pause() 387 388This will cause the scheduler to not wake up until processing is resumed:: 389 390 scheduler.resume() 391 392It is also possible to start the scheduler in paused state, that is, without the first wakeup 393call:: 394 395 scheduler.start(paused=True) 396 397This is useful when you need to prune unwanted jobs before they have a chance to run. 398 399 400Limiting the number of concurrently executing instances of a job 401---------------------------------------------------------------- 402 403By default, only one instance of each job is allowed to be run at the same time. 404This means that if the job is about to be run but the previous run hasn't finished yet, then the 405latest run is considered a misfire. It is possible to set the maximum number of instances for a 406particular job that the scheduler will let run concurrently, by using the ``max_instances`` keyword 407argument when adding the job. 408 409 410.. _missed-job-executions: 411 412Missed job executions and coalescing 413------------------------------------ 414 415Sometimes the scheduler may be unable to execute a scheduled job at the time it was scheduled to 416run. The most common case is when a job is scheduled in a persistent job store and the scheduler 417is shut down and restarted after the job was supposed to execute. When this happens, the job is 418considered to have "misfired". The scheduler will then check each missed execution time against the 419job's ``misfire_grace_time`` option (which can be set on per-job basis or globally in the 420scheduler) to see if the execution should still be triggered. This can lead into the job being 421executed several times in succession. 422 423If this behavior is undesirable for your particular use case, it is possible to use `coalescing` to 424roll all these missed executions into one. In other words, if coalescing is enabled for the job and 425the scheduler sees one or more queued executions for the job, it will only trigger it once. No 426misfire events will be sent for the "bypassed" runs. 427 428.. note:: 429 If the execution of a job is delayed due to no threads or processes being available in the 430 pool, the executor may skip it due to it being run too late (compared to its originally 431 designated run time). If this is likely to happen in your application, you may want to either 432 increase the number of threads/processes in the executor, or adjust the ``misfire_grace_time`` 433 setting to a higher value. 434 435 436.. _scheduler-events: 437 438Scheduler events 439---------------- 440 441It is possible to attach event listeners to the scheduler. Scheduler events are fired on certain 442occasions, and may carry additional information in them concerning the details of that particular 443event. It is possible to listen to only particular types of events by giving the appropriate 444``mask`` argument to :meth:`~apscheduler.schedulers.base.BaseScheduler.add_listener`, OR'ing the 445different constants together. The listener callable is called with one argument, the event object. 446 447See the documentation for the :mod:`~apscheduler.events` module for specifics on the available 448events and their attributes. 449 450Example:: 451 452 def my_listener(event): 453 if event.exception: 454 print('The job crashed :(') 455 else: 456 print('The job worked :)') 457 458 scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR) 459 460 461.. _troubleshooting: 462 463Troubleshooting 464--------------- 465 466If the scheduler isn't working as expected, it will be helpful to increase the logging level of the 467``apscheduler`` logger to the ``DEBUG`` level. 468 469If you do not yet have logging enabled in the first place, you can do this:: 470 471 import logging 472 473 logging.basicConfig() 474 logging.getLogger('apscheduler').setLevel(logging.DEBUG) 475 476This should provide lots of useful information about what's going on inside the scheduler. 477 478Also make sure that you check the :doc:`faq` section to see if your problem already has a solution. 479 480Reporting bugs 481-------------- 482 483.. include:: ../README.rst 484 :start-after: Reporting bugs 485 -------------- 486