Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python threading.Timer object not functioning when compiled to .exe

This is a follow up to https://stackoverflow.com/questions/37684111/ironpython-exe-file-closing-immediately-no-exception-thrown

I figured out that my program is not working once compiled due to an issue with the Timer object in the threading library. I've included the library in my \Lib\site-packages directory and added the directory to the path in the program. Here is the test code I'm using - a simple counting program:

import sys
from threading import Timer

sys.path.append('C:\Users\[user]\Documents\Visual Studio 2015\Projects\Testing Timer Compilation issue\Testing Timer Compilation issue')
sys.path.append('C:\Users\[user]\Documents\Visual Studio 2015\Projects\Testing Timer Compilation issue\Testing Timer Compilation issue\Lib')

class Chron():
    def __init__(self):
        self.t = Timer(2, self.count)
        self.t.start()
        self.i = 0

    def count(self):
        print(self.i)
        self.i += 1
        if self.i <= 15:
            self.t = Timer(2, self.count)
            self.t.start()

c = Chron()

Works perfectly in the Interactive Interpreter within Visual Studio, but once I use pyc.py to compile to an exe file, it will not run, and simply closes after ~5 seconds, no exception thrown.

As mentioned in the previous question, I have a program with a Timer in it that I need compiled, as the source code contains sensitive credentials. Are there any tricks necessary to make the Timer work within an exe? Is it simply incompatible?

Edit: 6 days without an answer. Unfortunately, there doesn't seem to be any resources for this specific issue anywhere on the internet. It's almost as if I'm the only one having this issue. This seems strange to me, since the problem seems to be with the Timer object itself, and I can't imagine that no one else has tried to deploy an application with a Timer in it. Any insight would be helpful at this point, as I am completely stumped.

like image 884
Inagnikai Avatar asked Jun 07 '16 17:06

Inagnikai


People also ask

How to perform threading timer in Python?

Python timer functionsstart() is a function that is used to initialize a timer. To end or quit the timer, one must use a cancel() function. Importing the threading class is necessary for one to use the threading class. The calling thread can be suspended for seconds using the function time.

How to Start timer in Python?

start() to start a new Python timer, you first check that the timer isn't already running. Then you store the current value of perf_counter() in . _start_time . On the other hand, when you call .

How do you create a timer for an object in Python?

It is started by calling the start() function corresponding to the timer explicitly. Create a timer that will run function with arguments args and keyword arguments kwargs, after interval seconds have passed. If args is None (the default) then an empty list will be used.

Does threading timer create a new thread?

Yes, it will create new threads.


Video Answer


1 Answers

The problem is that you are relying on the underlying Python interpreter to gracefully handle the case where the main thread of your executable has terminated, but there should be some others still running.

Running the code directly with CPython or IronPython works as expected. The Timer objects that you create are actually a specialization of a Thread. The interpreter recognizes that there are some non-daemon Threads still active and so doesn't terminate. See the docs for an explanation of daemon threads if you don't know the difference between the 2 types of Threads.

However, when you run as an executable, it would appear that the code IronPython uses to wrap the interpreter is not so kind. It just waits for the main thread to end and then closes everything. This happens despite the fact that your Timers are declared to be non-daemon Threads. Arguably this is a bug in IronPython.

The solution is therefore to leave your main thread running while your Timer threads are still operating. The simplest way to do that for this sample code is just to sleep - for example:

import sys
sys.path.append(r"c:\Program Files (x86)\IronPython 2.7\Lib")
from threading import Timer
from time import sleep

class Chron():
    def __init__(self):
        self.t = Timer(2, self.count)
        self.t.start()
        self.i = 0

    def count(self):
        print(self.i)
        self.i += 1
        if self.i <= 15:
            self.t = Timer(2, self.count)
            self.t.start()

c = Chron()
sleep(35)

However, for more complicated applications, you should consider some communication between your threads to coordinate when to close - e.g. using join() to wait for thread termination.

like image 164
Peter Brittain Avatar answered Oct 19 '22 20:10

Peter Brittain