Complete a task during certain time frames within a python script

◇◆丶佛笑我妖孽 提交于 2020-01-15 12:33:07

问题


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:

  • Anywhere during 23:30:00 to 23:40:00 it turns clearscreen to false
  • Anywhere during 23:40:00 to 23:50:00 it checks if clearscreen is false and if it is then it clears certain files and then it immediately sets clearscreen to false
  • Anywhere during 23:50:00 to 01:30:00 it just idles, doing nothing other than checking the time.

    from datetime import date, timedelta
    from sched import scheduler
    from time import time, sleep, strftime
    import random
    clearscreen = 0
    
    def periodically(runtime, intsmall, intlarge, function):
        global clearscreen
    
         ## Get current time
        currenttime = strftime('%H:%M:%S')
    
        ## while currenttime is anywhere between 23:40 and 23:50 then...
        while currenttime > '23:30:00' and currenttime < '23:40:00':
            ## Update time
            currenttime = strftime('%H:%M:%S')
            print ("""23:30:00 to 23:40:00 | %s""" % (currenttime))
            sleep(1)
    
            ## If clearscreen = false then...
            if clearscreen == False:
                ## Set clearscreen to true to enable the next part work and to disable this part until the next day.
                clearscreen = True
                print "changed to true"
    
        ## If currenttime is anywhere between 23:50 and 23:59 then...
        while currenttime > '23:40:00' and currenttime < '23:50:00':
            ## Update time
            currenttime = strftime('%H:%M:%S')
            print ("""23:40:00 to 23:50:00 | %s""" % (currenttime))
            sleep(1)
    
            if clearscreen == True:
                ## clear stuff here
                print "cleared"
                ## Change clearscreen to to stop it running
                clearscreen = False
                print "changed to false"
    
        ## Only allow run_periodically during 01:30 and 23:30
        if clearscreen == False:
            while currenttime > '23:50:00' and currenttime < '23:59:59':
                ## Update time
                currenttime = strftime('%H:%M:%S')
                print ("""23:50:00 to 23:59:59 | %s""" % (currenttime))
                sleep(1)
            while currenttime > '00:00:00' and currenttime < '01:30:00':
                ## Update time
                currenttime = strftime('%H:%M:%S')
                print ("""00:00:00 to 01:30:00 | %s""" % (currenttime))
                sleep(1)
    
        runtime += random.randrange(intsmall, intlarge)
        s.enter(runtime, 1, function, ())
        s.run()
    
    def test():
        print "test function"
    
    while True:
        periodically(180, -60, +60, test)
    

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.


回答1:


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.



来源:https://stackoverflow.com/questions/17559933/complete-a-task-during-certain-time-frames-within-a-python-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!