Search code examples
pythonpython-3.xmodulereload

How to make a module reload in python after the script is compiled?


The basic idea involved:

I am trying to make an application where students can write code related to a specific problem(say to check if the number is even) The code given by the student is then checked by the application by comparing the output given by the user's code with the correct output given by the correct code which is already present in the application.

The basic version of the project I am working on:

An application in which you can write a python script (in tkinter text box). The contents of the text box are first stored in a test_it.py file. This file is then imported (on the click of a button) by the application. The function present in test_it.py is then called to get the output of the code(by the user).

The problem:

Since I am "importing" the contents of test_it.py , therefore, during the runtime of the application the user can test his script only once. The reason is that python will import the test_it.py file only once. So even after saving the new script of the user in test_it.py , it wont be available to the application.

The solution:

Reload test_it.py every time when the button to test the script is clicked.

The actual problem:

While this works perfectly when I run the application from the script, this method fails to work for the compiled/executable version(.exe) of the file (which is expected since during compilation all the imported modules would be compiled too and so modifying them later will not work)

The question:

I want my test_it.py file to be reloaded even after compiling the application.


If you would like to see the working version of the application to test it yourself. You will find it here.


Solution

  • Even for the bundled application imports work the standard way. That means whenever an import is encountered, the interpreter will try to find the corresponding module. You can make your test_it.py module discoverable by appending the containing directory to sys.path. The import test_it should be dynamic, e.g. inside a function, so that it won't be discovered by PyInstaller (so that PyInstaller won't make an attempt to bundle it with the application).

    Consider the following example script, where the app data is stored inside a temporary directory which hosts the test_it.py module:

    import importlib
    import os
    import sys
    import tempfile
    
    def main():
        with tempfile.TemporaryDirectory() as td:
            f_name = os.path.join(td, 'test_it.py')
    
            with open(f_name, 'w') as fh:  # write the code
                fh.write('foo = 1')
    
            sys.path.append(td)  # make available for import
            import test_it
            print(f'{test_it.foo=}')
    
            with open(f_name, 'w') as fh:  # update the code
                fh.write('foo = 2')
    
            importlib.reload(test_it)
            print(f'{test_it.foo=}')
    
    main()