Expand source code
def cron(*,
quick_start: bool = False,
quick_start_skip_steps: int = 0,
quick_start_event: int = 0,
cron_file: str = "",
screen_cmd: str = "screen",
screen_log: str = "",
no_screen: bool = True,
keep_ld_library_path: bool = True,
sockname: str = "(path):cron",
python_exec: str = "",
detach: bool = False,
period: Optional[int] = None,
max_times: Optional[int] = None,
unique: bool = False,
window: bool = False
):
if not quick_start and quick_start_skip_steps>0:
raise RuntimeError("quick_start_skip_steps can only be used with quick_start")
if not quick_start and quick_start_event>0:
raise RuntimeError("quick_start_event can only be used with quick_start")
if no_screen:
if "BUSSILAB_CRON_SCREEN_ARGS" in os.environ:
env = json.loads(os.environ["BUSSILAB_CRON_SCREEN_ARGS"])
if "rcfile" in env:
os.unlink(env["rcfile"])
if unique:
raise RuntimeError("unique can only be used in screen mode")
if window:
raise RuntimeError("window can only be used in screen mode")
print(_now(),"start")
if max_times is not None:
print(_now(),"remaining iterations:",max_times)
counter=0
if quick_start:
print(_now(),"quick starting")
if quick_start_skip_steps>0:
print(_now(),"skipping",quick_start_skip_steps,"steps")
r=_run(cron_file,_find_period(cron_file,period),quick_start_event,counter,quick_start_skip_steps)
if isinstance(r,_reboot_now):
print("exit now")
return
counter += 1
if max_times is not None:
if counter >= max_times:
print("maximum iterations done")
return
while True:
if max_times is not None:
if counter >= max_times:
return
s=_time_to_next_event(_find_period(cron_file,period))
print(_now(),"Waiting " +"{:.3f}".format(s[0])+ " seconds for next scheduled event")
time.sleep(s[0])
r=_run(cron_file,_find_period(cron_file,period),s[1],counter)
if isinstance(r,_reboot_now):
return
counter += 1
else:
# this dictionary is passed as an environment variable
# it has two purposes:
# - passing the arguments to further calls in case there is a reboot
# - passing the path to the temporary screenrc file to be removed
# the latter is only added when running a new screen socket (not window)
env={ "arguments":{
"python_exec" : python_exec,
"screen_cmd" : screen_cmd,
"period" : period,
"cron_file" : cron_file,
"detach" : detach,
"max_times" : max_times
}}
if python_exec == "":
python_exec = sys.executable
print(_now(),"python_exec:", python_exec)
cmd = []
cmd.extend(shlex.split(screen_cmd)) # allows screen_cmd to contain space separated options
# in window mode, we do not launch a new socket.
# these arguments are thus ignored
if not window:
sockname = _adjust_sockname(sockname,cron_file)
# check if another process is already running
if unique:
cmd1=cmd.copy() # do not modity cmd
cmd1.append("-ls")
for l in subprocess.run(cmd1, # do not check errors here since screen -ls fails on MacOS
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True).stdout.split('\n'):
if "." + sockname +"\t" in l:
print("Another screen with socket name " + sockname + " is already present")
return
# run in detaced mode
if detach:
cmd.append("-d")
# write screen log
if screen_log != "":
cmd.append("-L")
if screen_log != "screenlog.0":
cmd.append("-Logfile")
cmd.append(screen_log)
# force a new screen
cmd.append("-m")
# create a named socket
cmd.append("-S")
cmd.append(sockname)
# create a temporary screenrc file
rcfile=tempfile.NamedTemporaryFile("w+t",delete=False)
print(_screenrcfile,file=rcfile)
rcfile.flush()
# pass the temporary screenrc file as an argument
cmd.append("-c")
cmd.append(rcfile.name)
# store the path so that the file can be cancelled later
env["rcfile"]=rcfile.name
cmd.append("-t")
cmd.append("running")
cmd.append("/usr/bin/env")
cmd.append("BUSSILAB_CRON_SCREEN_ARGS=" + json.dumps(env))
if keep_ld_library_path and 'LD_LIBRARY_PATH' in os.environ:
cmd.append("LD_LIBRARY_PATH=" + os.environ["LD_LIBRARY_PATH"])
cmd.extend(shlex.split(python_exec)) # allows python_exec to contain space separated options
cmd.append("-m")
cmd.append("bussilab")
cmd.append("cron")
cmd.append("--no-screen")
if period is not None:
cmd.append("--period")
cmd.append(str(period))
if len(cron_file)>0:
cmd.append("--cron-file")
cmd.append(cron_file)
if quick_start:
cmd.append("--quick-start")
if quick_start_skip_steps>0:
cmd.append("--quick-start-skip-steps")
cmd.append(str(quick_start_skip_steps))
if quick_start_event>0:
cmd.append("--quick-start-event")
cmd.append(str(quick_start_event))
if max_times is not None:
cmd.append("--max-times")
cmd.append(str(max_times))
print(_now(),"cmd:",cmd)
try:
ret=subprocess.call(cmd)
if ret != 0:
msg = "An error occurred."
if screen_log !="" and screen_log != "screenlog.0":
msg += " Notice that some screen versions do not support a logfile with a name different from screenlog.0"
raise RuntimeError(msg)
except OSError:
raise RuntimeError("Execution of failed. Perhaps '" + screen_cmd + "' command is not available on your system.")