Search code examples
pythonpython-idle

Cannot use __file__ when running startup file in IDLE's normal mode


I've written a little batch file (Windows 8.1) to start my script directly in IDLE:

START "" pythonw.exe "C:\Program Files (x86)\Python36-32\Lib\idlelib\idle.py" -r my_script.py

The script contains the line

my_dir = os.path.split(__file__)[0]

which produces a name error

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Program Files (x86)\Python36-32\lib\tkinter\__init__.py", line 1699, in __call__
    return self.func(*args)
  File "my_script.py", line 245, in out_file_dia
    my_dir = os.path.split(__file__)[0]
NameError: name '__file__' is not defined

If I open IDLE first and then start the script, it works fine. Why is

__file__

not defined in this situation?


Solution

  • In interactive mode, the input 'file' is the stream of statements entered by the user, so __file__ is left unset. If there is a PYTHONSTARTUP file, __file__ is set to that filename while the file is run, then unset before the first >>> prompt. Currently, IDLE does not do this when running user code in a separate process, which is now the normal mode.

    I opened https://bugs.python.org/issue32984 to fix this. I will try to remember to report here when it is.

    When IDLE is started with -n, to use the old deprecated mode in which user code is executed in the IDLE GUI process, startup files and interactive input see __file__ set to an idlelib file. Both are wrong but will not be fixed.

    EDIT: PR-5981, which works for me on Windows, modifies pyshell.execfile to begin as follows:

    def execfile(self, filename, source=None):  # currently line 633
        "Execute an existing file"
        if source is None:
            with tokenize.open(filename) as fp:
                source = fp.read()
                if use_subprocess:
                    source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n"
                              + source + "\ndel __file__")
    

    The last 3 lines are new. uwain12345, adding them should solve your problem. If not, I would like to know.

    EDIT 2: tweek replacement code to allow for ' in startup file name.

    Note: f strings are new in 3.6. For older Pythons, replace the source line with

                    filename = os.path.abspath(filename)
                    source = ("__file__ = r'''{}'''\n".format(filename)