Ryflex
Ryflex

Reputation: 5779

Complete a task during certain time frames within a python script

As you can see below here is a copy of my script what I've created, could someone help me improve/fix my currenttime tasks.

Basically, during the day periodically calls test every 3 mins +- 60 seconds each periodically cycle it is supposed to do the follow tasks:

Any help would be much appreciated.

Edit for @abarnert

In regards to the loops this is what the function is supposed to be doing:

23:3x:xx - While loop initiated as currenttime fits the while loop's conditions (time is inbetween 23:30:00 and 23:40:00)
23:3x:xx - clearscreen is false, setting it to true.
23:3x:xx to 23:40:00 - currenttime is updated every 1 second(s)
23:40:01 - While loop ended as currenttime no longer fits the while loop's conditions (time is outside of 23:30:00 and 23:40:00)

23:40:01 - While loop initiated as currenttime fits the while loop's conditions (time is inbetween 23:40:00 and 23:50:00)
23:40:01 - clearscreen is true, doing some stuff and then changing clearscreen to false
23:40:01 to 23:50:00 - currenttime is updated every 1 second(s)
23:50:01 - While loop ended as currenttime no longer fits the while loop's conditions (time is outside of 23:40:00 and 23:50:00)

23:50:01 - While loop initiated as currenttime fits the while loop's conditions (time is inbetween 23:50:00 and 23:59:59)
23:50:01 to 23:59:59 - currenttime is updated every 1 second(s)
00:00:00 - While loop ended as currenttime no longer fits the while loop's conditions (time is outside of 23:50:00 and 23:59:59)

00:00:00 - While loop initiated as currenttime fits the while loop's conditions (time is inbetween 00:00:00 and 01:30:00)
00:00:00 and 01:30:00 - currenttime is updated every 1 second(s)
00:00:00 - While loop ended as currenttime no longer fits the while loop's conditions (time is outside of 00:00:00 and 01:30:00)

You mention that I repeatedly update currenttime, then print, then sleep. How would I get around this "problem"?


In regards to the global clearscreen I am unsure what you mean by; "you don't have a lock on it"


I'm running Windows so signal.setitemer is a no go and also I need certain values/variables to be stored in memory for my script so scheduled tasks on windows won't be appropriate correct?

You built an example bit of code using the sched module but it doesn't work and I am unable to work out how to get it working, plus it's also rather confusing for me. I'm still learning and it's rather confusing.

Upvotes: 1

Views: 2879

Answers (1)

abarnert
abarnert

Reputation: 366103

There's no way Python can "ignore the while loops" (unless you have some other condition outside of them). But if the while loop tells Python to loop 0 times, it will do exactly that.


First, you've got this loop:

while currenttime > '23:30:00' and currenttime < '23:40:00':

… followed by this one:

while currenttime > '23:40:00' and currenttime < '23:50:00':

Think about what happens at 23:40:00. It's neither before nor after 23:40:00, so you'll skip the second loop before you even get into it.

While we're at it, two side notes on these lines:

  • You can just write '23:40:00' < currenttime < '23:50:00' in Python.
  • You can use datetime or time objects instead of strings to compare times.

Next, you repeatedly do this:

currenttime = strftime('%H:%M:%S')
print ("""23:40:00 to 23:50:00 | %s""" % (currenttime))
sleep(1)

This means that currenttime is never actually the current time. It's usually a second ago. Think about what that does to your loop conditions at the edges.

As a side note, sleep(1) isn't guaranteed to sleep exactly one second. If your computer is busy, or decides it wants to go into low-power mode, it can go significantly longer than a second. If interrupts are flying, it can go shorter than a second. Even in the best case, it'll often by off by half a clock tick in one direction or the other. So, if you need this to fire exactly 600 times, it's generally not going to do that.


Meanwhile, you've got this:

global clearscreen

Obviously there's no way anyone can ever change this in your current code, so presumably in your real code you're attempting to change it from another thread. But you don't have a lock on it. So, it's perfectly possible that you will not see a change immediately, or even ever.


Writing schedulers is a lot harder than it looks. That's why you're usually better off using an existing one. Options include:

  • The stdlib sched module.
  • The stdlib threading.Timer.
  • The stdlib signal.setitemer. Only on platforms with real signals (meaning not Windows), and possibly not appropriate on some platforms if you're using threads or fork.
  • Various third-party modules on PyPI/recipes on ActiveState that give you a better Timer (e.g., using a single timer thread with a queue of upcoming jobs, rather than a thread for each job).
  • An event loop framework that handles timers—although this is probably overkill if scheduling is all you need, if you have some other reason to use Twisted or wx or PyGame or gevent, let it do the scheduling.
  • An external timer that runs your script—cron on any Unix, LaunchServices on Mac and some other Unixes, Scheduled Tasks on Windows, etc. Probably not appropriate for running a task every second, but from your comments, it sounds like your real need is "to be able to call a function every 3 mins +- 60 seconds."

Since you specifically asked about sched… There are two approaches:

  1. Build the whole day's schedule at once and call enterabs repeatedly to fill it with the day's tasks, plus one more task that runs at midnight tomorrow and does the same thing.
  2. Write a function that figures out, based on the current time, which task to schedule next and when, and does so. Call that function after the actual work.

Here's what the first one looks like:

import sched
import datetime
import time

s = sched.scheduler(time.time, time.sleep)

def dotoday():
    now = datetime.date.now()
    stime = now.time()

    # Schedule "first" every 3 minutes from 22:00 to 22:57
    if stime < datetime.time(22, 0):
        stime = datetime.time(22, 0)
    while stime <= datetime.time(22, 57):
        s.enterabs(stime, 1, first, ())
        stime += datetime.timedelta(0, 180)

    # Schedule "second" every 3 minutes from 23:00 to 23:57
    stime = datetime.time(23, 0)
    while stime <= datetime.time(23, 57):
        s.enterabs(stime, 1, second, ())
        stime += datetime.timedelta(0, 180)

    # Schedule "dotoday" to run tomorrow
    midnight = now.replace(hour=0, minute=0, second=0)
    tomorrow = midnight + datetime.timedelta(1, 0)
    s.enterabs(tomorrow, 1, dotoday, ())

dotoday()
s.run()

I made this a bit more complicated than necessary, so that you can start it at 23:31:17 and it will run the first batch of tasks at 23:31:17, 23:34:17, etc. instead of waiting until 23:33:00, 23:36:00, etc.

Upvotes: 3

Related Questions