Reputation: 1885
I want to schedule a task to run after a delay in my quart/flask application whenever a certain query is made. I'm hoping to use the sched module so there are fewer external dependencies, but I'm having difficulty understanding how to set up the scheduler. Below is my attempt so far (quart/Quart can be replaced with flask/Flask and the async keyword can be dropped to convert it to a flask app):
from quart import Quart
import sched, time, threading
app = Quart(__name__)
scheduler = sched.scheduler(time.time, time.sleep)
scheduler_thread = threading.Thread(target = scheduler.run)
scheduler_thread.start()
def do_task(message):
print(f'{int(time.time())} From do_task, running {message}')
@app.route('/')
async def index():
do_task("Immediate task")
# Schedule a task after a 5 second delay with priority 1
scheduler.enter(5, 1, do_task, argument=("Scheduled task",))
return '<h1>Hello World!</h1>'
The goal is that whenever I query the app, it will add an event to the scheduler, and the scheduler will execute it once the delay has elapsed.
E.g. if I do curl http://127.0.0.1:5000/
one time then I would expect roughly the following to be printed:
1651519374 From do_task, running Immediate task
1651519379 From do_task, running Scheduled task
And if I run it again a few seconds later, then again I would expect something like:
1651519415 From do_task, running Immediate task
1651519420 From do_task, running Scheduled task
And if I run it three times, three seconds apart each time, then I would expect something like:
1651519500 From do_task, running Immediate task
1651519503 From do_task, running Immediate task
1651519505 From do_task, running Scheduled task
1651519506 From do_task, running Immediate task
1651519508 From do_task, running Scheduled task
1651519511 From do_task, running Scheduled task
However, what I am getting is just the Immediate task is run, and the Scheduled task never runs.
I've tried some permutations, like:
from quart import Quart
import sched, time, threading
app = Quart(__name__)
scheduler = sched.scheduler(time.time, time.sleep)
scheduler_thread = threading.Thread(target = scheduler.run)
def do_task(message):
print(f'{int(time.time())} From do_task, running {message}')
@app.route('/')
async def index():
do_task("Immediate task")
scheduler.enter(5, 1, do_task, argument=("Scheduled task",))
scheduler_thread.start()
return '<h1>Hello World!</h1>'
In this case, the first time I query it prints what I expect:
1651521193 From do_task, running Scheduled task
1651521202 From do_task, running Immediate task
But the second time fails with RuntimeError: threads can only be started once
What am I missing?
Upvotes: 0
Views: 247
Reputation: 1885
Well, it looks like quart has a feature that does this in another way, and I think it's doing what I need:
from quart import Quart
import time
app = Quart(__name__)
def do_task(message, delay=0):
time.sleep(delay)
print(f'{int(time.time())} From do_task, running {message}')
@app.route('/')
async def index():
do_task("Immediate task")
app.add_background_task(do_task, "Scheduled task", delay=10)
return '<h1>Hello World!</h1>'
Upvotes: 0