graylagx2
graylagx2

Reputation: 80

Using module schedule run schedule immediately then again every hour

Im trying to schedule a task with the module "schedule" for every hour. My problem is i need the task to first run then run again every hour.

This code works fine but it waits an hour before initial run

import schedule
import time

def job():
    print("This happens every hour")

schedule.every().hour.do(job)

while True:
    schedule.run_pending()

I would like to avoid doing this:

import schedule
import time

def job():
    print("This happens immediately then every hour")

schedule.every().hour.do(job)

while i == 0: 
    job()
    i = i+1

while i == 1:
    schedule.run_pending()

Ideally it would be nice to have a option like this:

schedule.run_pending_now()

Upvotes: 6

Views: 9277

Answers (5)

Alex Kaplan
Alex Kaplan

Reputation: 33

Wrote a utility to schedule a job to run once and at a recurring interval. This has an advantage over kicking off the job manually as the first run will happen outside of the main thread.

import schedule

from typing import Callable

def run_once_then_every(initial_delay: int, interval: int, job_func: Callable, *args, **kwargs):
    """
    Convenience wrapper to run a job once immediately (with some delay) then at a recurring interval.
    * Requires all intervals to be provided in minutes.
    * An initial delay of 0 results in the job running within 1 second.

    Note that the waiting period for the first periodic interval starts immediately and thus the
    interval between an initial call with a delay may be less than the interval between all other
    iterations.

    Args:
        - initial_delay (int): Delay before the first run of the job in minutes.
            An initial delay of 0 results in the job running within 1 second.
        - interval (int): Period between job recurrences.
        - job_func (Callable): The job to execute
        - args: args passed through to "job_func"
        - kwargs: kwargs passed through to "job_func"

    """

    def wrapped_job_func(*args, **kwargs):
        """
        Runs job_func once then returns a value indicating the scheduler
        should remove the job from its registry preventing future runs.
        """
        job_func(*args, **kwargs)
        return schedule.CancelJob

    # Schedule the first iteration
    if initial_delay == 0:
        # Close enough to immediate
        schedule.every(1).seconds.do(wrapped_job_func, *args, **kwargs)
    else:
        schedule.every(initial_delay).minutes.do(wrapped_job_func, *args, **kwargs)

    # Schedule periodic runs.
    schedule.every(interval).minutes.do(job_func, *args, **kwargs)

Upvotes: 1

Macilias
Macilias

Reputation: 3543

Actually I don't think that calling the function directly would be so wise, since it will block the thread without the scheduler, right?

I think there is nothing wrong about setting the job to be executed once, and every 30 sec for example like that:

scheduler.add_job(MPOStarter.run, args=ppi_args) # run once, then every 30 sec 
scheduler.add_job(MPOStarter.run, "interval", seconds=30, args=ppi_args)

Upvotes: 0

sam
sam

Reputation: 11

If you have many tasks that takes some time to execute and you want to run them independently during start you can use threading

import schedule
import time

def job():
    print("This happens every hour")

def run_threaded(task):
    job_thread = threading.Thread(target=task)
    job_thread.start()

run_threaded(job)       #runs job once during start

schedule.every().hour.do(run_threaded, job)
while True:
    schedule.run_pending()  # Runs every hour, starting one hour from now.

Upvotes: 1

user2229336
user2229336

Reputation: 289

To run all jobs regardless if they are scheduled to run or not, use schedule.run_all(). Jobs are re-scheduled after finishing, just like they would if they were executed using run_pending().


def job_1():
    print('Foo')

def job_2():
    print('Bar')

schedule.every().monday.at("12:40").do(job_1)
schedule.every().tuesday.at("16:40").do(job_2)

schedule.run_all()

# Add the delay_seconds argument to run the jobs with a number
# of seconds delay in between.
schedule.run_all(delay_seconds=10)```

Upvotes: 2

paxdiablo
paxdiablo

Reputation: 882626

Probably the easiest solution is to just run it immediately as well as scheduling it, such as with:

import schedule
import time

def job():
    print("This happens every hour")

schedule.every().hour.do(job)

job()                       # Runs now.
while True:
    schedule.run_pending()  # Runs every hour, starting one hour from now.

Upvotes: 9

Related Questions