I’m writing a program which allows me to control a vanilla Minecraft server using python. The first thing I wanted to make is a auto-restart feature. Everything works fine except that I cannot do sys.exit()
or similar things, I’m not sure but I think this is because of the Timer
.
I tried t.cancel()
but t
is a local variable so it’s complicated to play with it.
Here’s my code:
#! /usr/bin/env python3 import os, sys, time import subprocess from datetime import datetime, timedelta, date from threading import Timer server = subprocess.Popen('./start.sh', stdin=subprocess.PIPE,shell=True) #junk variables content = '' previousContent = '' def restart(): if datetime.today().weekday() is 3: server.stdin.write(bytes('stoprn', 'ascii')) server.stdin.flush() time.sleep(90) print('Restarting...') os.system('python3 start.py') sys.exit() else: timerStart() def timerStart(): today = datetime.today() restartDate = today.replace(day=today.day,hour=1,minute=0,second=0,microsecond=0) + timedelta(days=1) delta_t = restartDate-today secs= delta_t.total_seconds() t=Timer(secs, restart) t.start() timerStart() while True: time.sleep(0.1) #stdout f = open('logs/latest.log') content = f.read() if previousContent != '': if previousContent in content: content.replace(previousContent,'') if content != '': print(content) previousContent = f.read() f.close() #stdin command = input('') if command: if command == 'stop': server.stdin.write(bytes('stoprn', 'ascii')) server.stdin.flush() time.sleep(20) sys.exit() else: server.stdin.write(bytes(command + 'rn', 'ascii')) server.stdin.flush()
If someone could at least put me on the right track, it would really help me
Advertisement
Answer
When instantiate the threading.Timer class, and call its start() method in the corresponding object, a new non-daemon execution thread is created and started behind the scenes for execute the method “restart”. The created execution thread, waits for the time interval specified in the “secs” variable when instantiated the threading.Timer class to elapse, and then executes the specified “restart” method. The reason why the execution of the script does not end when you invoke sys.exit(), is because the main execution thread of the application, in order to finish its execution (and thus the execution of the script), has to wait for all other non-daemon threads created to finish first. That is, when you call sys.exit(), the process’s main execution thread, will have to wait for the thread created by the instantiation of the threading.Timer class, to finish its execution. To solve that, what you can do, as you mentioned, is to call t.cancel(). Now for that, you must first move the declaration of the variable “t” to the global scope, to be able to use it in the ‘timerStart’ function, and in the block of code executed to handle the “stop” command. For example, after the declaration and initialization of the variable “previousContent”, you can declare there the variable t and initialize it to None. Then, before assign to it the instance of threading.Timer, you have to use the “global” keyword that allows to modify the variable outside the current scope(outside the scope of the ‘timerStart’ function), and finally, when processing the “stop” command, call t.cancel(). In short, the final code would look like this:
# ! /usr/bin/env python3 import os, sys, time import subprocess from datetime import datetime, timedelta, date from threading import Timer server = subprocess.Popen('./start.sh', stdin=subprocess.PIPE, shell=True) # junk variables content = '' previousContent = '' t = None def restart(): if datetime.today().weekday() is 3: server.stdin.write(bytes('stoprn', 'ascii')) server.stdin.flush() time.sleep(90) print('Restarting...') os.system('python3 start.py') sys.exit() else: timerStart() def timerStart(): today = datetime.today() restartDate = today.replace(day=today.day, hour=1, minute=0, second=0, microsecond=0) + timedelta(days=1) delta_t = restartDate - today secs = delta_t.total_seconds() global t t = Timer(secs, restart) t.start() timerStart() while True: time.sleep(0.1) # stdout with open('logs/latest.log') as f: # Is better to open the file using a "with", such that the file is guaranteed to be closed, # even in the case of an exception content = f.read() if previousContent != '': if previousContent in content: content.replace(previousContent, '') if content != '': print(content) previousContent = f.read() # stdin command = input('') if command: if command == 'stop': # Abort the threading.Timer's non-daemon execution thread: t.cancel() # Blocks the calling thread until the thread whose join() method is called is terminated: t.join() server.stdin.write(bytes('stoprn', 'ascii')) server.stdin.flush() time.sleep(20) sys.exit() else: server.stdin.write(bytes(command + 'rn', 'ascii')) server.stdin.flush()
Also, is good to know, that the thread created by instantiating the threading.Timer class, could be canceled, only before it start to execute the “restart” method. The class threading.Timer, inherits from threading.Thread, that is way it creates and fires the execution of a new non-daemon execution thread, when start() is called.
References: