Reputation: 547
So I have been playing around with schedule and finally got it to work. I was too excited to relized that there came ANOTHER problem haha. However the issue is now that it doesn't end whenever the main is finished and I can't really find the solution. I know the issue sits on the row Time.sleep(1) because whenever I keyboardInterrput then there comes a error saying Time.sleep(1) was the "Issue" and I can't really find a soulution to end it.
Im using a schedule from a github : https://github.com/dbader/schedule
while True:
UserInput = input('To run Schedule task - Press y\nTo run directly - Press n\n')
if(UserInput == 'y' or UserInput == 'Y'):
print(Fore.RESET + space)
TimeUser = input('What time to start script? Format - HH:MM\n')
schedule.every().day.at(TimeUser).do(main)
wipe()
print('Schedule starts at: ''' + TimeUser + ' - Waiting for time...')
idle = int(round(schedule.idle_seconds()))
while True:
schedule.run_pending()
time.sleep(1)
idle = int(round(schedule.idle_seconds()))
if(idle < 6.0) and (idle >= 0.0):
print('Starting in: ' + str(idle))
elif(UserInput == 'n' or UserInput == 'N'):
main()
print("Wrong input - Try again")
I did do get a recommendation from a very kind person about this: schedule library needs that busy loop. The real question here is how the OP can run that busy loop without blocking, and the answer in that library docs is: run it in another thread. If he breaks, the scheduled tasks will not complete
and I quite still don't understand what to do without blocking main. Is there maybe someone that has any idea?
Upvotes: 0
Views: 2368
Reputation: 2444
In that example, it's the standard Schedule object for that module, but extended with an extra method which you can just call once and then it will run in a separate thread. I'd recommend trying with that - the quickest way is probably to create your own object that subclasses Scheduler and implement that run_continuously()
method on the subclass. You can then call that method on your scheduler once and do whatever else you like in the main thread without having to periodically call run_pending()
The error you get when you press Ctrl+C isn't an issue - it's just complaining that sleep
was interrupted when you terminated it manually. If you want to exit based on some condition automatically, you can do this based on some logic in the loop
E.g. while not terminate:
where terminate is a variable you've set up, possibly globally, which can be changed by the scheduled tasks.
A lot of the utility of this schedule based model is for background tasks which run repeatedly for an extended period of time. Say you want to execute some more code, either you have some code that needs to run once and it can probably be run before you enter the while
loop, or you want to run it repeatedly and you can also add it to the schedule.
import schedule
import threading
import time
# this is a class which uses inheritance to act as a normal Scheduler,
# but also can run_continuously() in another thread
class ContinuousScheduler(schedule.Scheduler):
def run_continuously(self, interval=1):
"""Continuously run, while executing pending jobs at each elapsed
time interval.
@return cease_continuous_run: threading.Event which can be set to
cease continuous run.
Please note that it is *intended behavior that run_continuously()
does not run missed jobs*. For example, if you've registered a job
that should run every minute and you set a continuous run interval
of one hour then your job won't be run 60 times at each interval but
only once.
"""
cease_continuous_run = threading.Event()
class ScheduleThread(threading.Thread):
@classmethod
def run(cls):
# I've extended this a bit by adding self.jobs is None
# now it will stop running if there are no jobs stored on this schedule
while not cease_continuous_run.is_set() and self.jobs:
# for debugging
# print("ccr_flag: {0}, no. of jobs: {1}".format(cease_continuous_run.is_set(), len(self.jobs)))
self.run_pending()
time.sleep(interval)
continuous_thread = ScheduleThread()
continuous_thread.start()
return cease_continuous_run
# example using this custom scheduler that can be run in a separate thread
your_schedule = ContinuousScheduler()
your_schedule.every().day.do(print)
# it returns a threading.Event when you start it.
halt_schedule_flag = your_schedule.run_continuously()
# you can now do whatever else you like here while that runs
# if your main script doesn't stop the background thread, it will keep running
# and the main script will have to wait forever for it
# if you want to stop it running, just set the flag using set()
halt_schedule_flag.set()
# I've added another way you can stop the schedule to the class above
# if all the jobs are gone it stops, and you can remove all jobs with clear()
your_schedule.clear()
# the third way to empty the schedule is by using Single Run Jobs only
# single run jobs return schedule.CancelJob
def job_that_executes_once():
# Do some work ...
print("I'm only going to run once!")
return schedule.CancelJob
# using a different schedule for this example to avoid some threading issues
another_schedule = ContinuousScheduler()
another_schedule.every(5).seconds.do(job_that_executes_once)
halt_schedule_flag = another_schedule.run_continuously()
I would be mindful of whether you actually need to use threading for this - if you are just looking for the program to exit after doing jobs once, all you need to do is:
while schedule.jobs:
schedule.run_pending()
time.sleep(1)
and make sure your jobs return CancelJob
. Hopefully the example will be useful to tinker around with though, have tested in repl.it and everything should work with Python 3.
Upvotes: 3