Question: schedule code to execute at some time later

Question

schedule code to execute at some time later

Answers 4
Added at 2016-12-31 20:12
Tags
Question

How can I schedule some code to be executed at a certain time later?

I tried:

import time
import datetime

time.sleep(420)
print(datetime.datetime.now())

But this doesn't work if the Mac goes to sleep.

To clarify, I need a script (well, some python function, which I could put into a separate script) to run at a precise time in the future.

time.sleep doesn't meet my needs because if the computer sleeps during time.sleep's timeout then the real-time delay is much longer than the one passed to time.sleep. E.g., I start a 7 minute delay at 12:00 with time.sleep. Then I close my macbook lid. Open it at 12:07, but the timeout hasn't finished yet. In fact, I have to wait until about 12:13 for the timeout to finish, even though originally I wanted the rest of my script to continue at 12:07.

So, I don't need it to run while the computer sleeps, but rather, any sleeping the computer does should not affect the time that it does run.

Answers
nr: #1 dodano: 2016-12-31 20:12

sched module is a generally useful event scheduler. It can schedule events in the future by relative (enter method) or absolute time (enterabs method) with priority.

As time.sleep uses select syscall on Linux which introduces delays we have to define a sleep function based on time.time which doesn't suffer from the unnecessary delay.

from __future__ import print_function
import sched
from datetime import datetime
from time import sleep, time

def my_sleep(n):
    s = time()
    while (time() - s) < n:
        # introduce small delay for optimization
        sleep(0.1)

schedule = sched.scheduler(time, my_sleep)
schedule.enter(2, 0, lambda: print(datetime.now()), ())
schedule.enter(3, 0, lambda: print(datetime.now()), ())
schedule.run()
nr: #2 dodano: 2017-01-01 00:01

Your best option is to use cron or Apple's launchd. Since you want whatever it is to be executed at set intervals without a delay after waking up from sleep this is what I recommend.

Cron Method

To setup a new cron job you would open up Terminal and edit it with the time information and script you are wanting to execute (eg. every 7 minutes):

$ crontab -e

*/7 * * * * /usr/bin/python /path/to/myscript.py

Here's a quick breakdown of the meaning:

* * * * *  command to execute
│ │ │ │ │
│ │ │ │ └─── day of week (0 - 6) (0 to 6 are Sunday to Saturday, or use names; 7 is Sunday, the same as 0)
│ │ │ └──────── month (1 - 12)
│ │ └───────────── day of month (1 - 31)
│ └────────────────── hour (0 - 23)
└─────────────────────── min (0 - 59)

To list jobs you have set in your crontab:

$ crontab -l

Timed Jobs Using launchd

Apple's recommendation is not to use crontab, rather launchd. Basically this entails creating a preference list with the information about your task and what time to run it, etc.

$ cd $HOME/Library/LaunchAgents
$ nano com.username.mytask.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.username.mytask</string>
    <key>ProgramArguments</key>
    <array>
        <string>/path/to/myscript.sh</string>
    </array>
    <key>StartInterval</key>
    <integer>7</integer>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

In nano press Control + O followed by Control + X to save.

$ chmod +x /path/to/myscript.sh
$ launchctl load com.username.mytask.plist
$ launchctl start com.username.mytask.plist

The following would make your script executable, and then load and start the launch agent.

To stop and unload:

$ launchctl stop com.username.mytask.plist
$ launchctl unload com.username.mytask.plist

More information:

Scheduling Timed JobsCreating a launchd Property List File

Effects of Sleeping and Powering Off

If the system is turned off or asleep, cron jobs do not execute; they will not run until the next designated time occurs.

If you schedule a launchd job by setting the StartCalendarInterval key and the computer is asleep when the job should have run, your job will run when the computer wakes up. However, if the machine is off when the job should have run, the job does not execute until the next designated time occurs.

All other launchd jobs are skipped when the computer is turned off or asleep; they will not run until the next designated time occurs.

Consequently, if the computer is always off at the job’s scheduled time, both cron jobs and launchd jobs never run. For example, if you always turn your computer off at night, a job scheduled to run at 1 A.M. will never be run.

nr: #3 dodano: 2017-01-01 00:01

EDIT
From the man page for "atrun"
Execute the following command as root to enable atrun:

launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist  

then something like:

at now + 5 minutes
echo 'testing the at command' > myfile.txt
<EOD>  

See the man page for "at" for other options

nr: #4 dodano: 2017-01-01 09:01

Update: User @I'L'I pointed out that there was an error in my code. In copy pasting the wait_time variable, I overwrote a necessary seconds= in timedelta which prevented the loop from ever exiting. This is now fixed.

Your code waits until the program has run for a set number of seconds. You don't actually want that, according to your Question, correct? You actually want to wait until a certain number of seconds have passed from your start time, and continue at the first opportunity after they have passed.

Do this:

from datetime import datetime
from datetime import timedelta

# Establish the start time as a static variable. Basically lock the start time in.
start_time = datetime.now()

# How long should we wait?
wait_time = 420

# What should I say while I wait?
wait_string = "Zzzzzzzzzzz"

# Loop until at least wait_time seconds have passed
while datetime.now() <= start_time + timedelta(seconds=wait_time):
    print(wait_string, end = "\r"),

print("Done waiting at:", datetime.now())
Source Show
◀ Wstecz