1

Under certain circumstances, the variable __file__ is relative (see this stackoverflow question for details). If the variable is relative and one changes the current working directory, __file__ doesn't change accordingly. As a result, the relative path is invalid.

Please see this minimal example:

import os


if __name__ == '__main__':
    filepath = __file__
    filepath_real = os.path.realpath(filepath)
    print('pwd', os.getcwd())
    print('relative', filepath, os.path.exists(filepath))
    print('absolute', filepath_real, os.path.exists(filepath_real))

    os.chdir('..')
    print('... changing the working directory ...')

    filepath = __file__
    filepath_real = os.path.realpath(filepath)
    print('pwd', os.getcwd())
    print('relative', filepath, os.path.exists(filepath))
    print('absolute', filepath_real, os.path.exists(filepath_real))

Suppose this file is located at /home/user/project/script.py. If I execute script.py, this is the output:

$ cd /home/user/project/
$ python script.py
pwd /home/user/project
relative test.py True
absolute /home/user/project/test.py True
... changing the working directory ...
pwd /home/user
relative test.py False
absolute /home/user/test.py False

Is it possible to get a correct __file__ path (one that exists according to os.path.exists) after changing the working directory in Python 3.7? I am aware that this issue does not exist in higher Python versions (such as 3.9), where __file__ always returns an absolute path.

Frank Zalkow
  • 3,850
  • 1
  • 22
  • 23
  • 2
    assign the __file__ path at the beginning of the python file, which ensure that you get the path before changing working dir – gftea Mar 17 '22 at 13:16
  • Thanks for your comment. Your suggestion might be a solution in some cases. However, suppose that I need to get the current filename of a module, where I call `__file__` in a function of this module. I don't know when this function will be executed. It might happen after changing the working directory. So, I would need to save the output of `__file__` in a global variable of this module rather than calling it in the function. Is this really the best solution in this case? – Frank Zalkow Mar 17 '22 at 13:25
  • python interpreter evaluate the file line by line in sequence, so when you import the file, all codes have been evaluated and you path vallue is assigned, and import is only done once by interpreter except you are doing some advanced and dymaic import, otherwise, this guarantee it is executed before any function all. – gftea Mar 17 '22 at 13:30
  • I will paste my comments as answer if no more question – gftea Mar 17 '22 at 13:34

1 Answers1

0

get the __file__ path at the beginning of the python file, which ensure that you get the path when the file is imported before you calling the function.

python interpreter evaluate the file line by line in sequence, file will be evaulated only once by interpreter even you import the file multiple times , so first time the file is imported, all codes of the file have been evaluated and you path vallue is assigned.

you will only have issue when you change working dir, then force reload the file programmatically and reimport the file, then the value will be changed, otherwise it stays constant duing life time of your program.

EDIT

below example to show the after you import the module, the __file__ does not change after you change the working dir

import os


if __name__ == '__main__':
    filepath = os.__file__
    filepath_real = os.path.realpath(filepath)
    print('pwd', os.getcwd())
    print('relative', filepath, os.path.exists(filepath))
    print('absolute', filepath_real, os.path.exists(filepath_real))

    os.chdir('..')
    print('... changing the working directory ...')

    filepath = os.__file__
    filepath_real = os.path.realpath(filepath)
    print('pwd', os.getcwd())
    print('relative', filepath, os.path.exists(filepath))
    print('absolute', filepath_real, os.path.exists(filepath_real))

output

pwd /home/gftea/repo/jackygao/eiffel-system/utils
relative /usr/lib/python3.8/os.py True
absolute /usr/lib/python3.8/os.py True
... changing the working directory ...
pwd /home/gftea/repo/jackygao/eiffel-system
relative /usr/lib/python3.8/os.py True
absolute /usr/lib/python3.8/os.py True
gftea
  • 558
  • 3
  • 7
  • Use backslash or code formatting to show double underscores properly – Mad Physicist Mar 17 '22 at 13:46
  • Thanks for your answer. For a simple script, your suggestion might be perfectly fine. However, suppose I need to get the file path inside a function of a module. To apply your solution, I need to assign the output of `__file__` to a global variable in that module and use this global variable in the function. Some consider [global variables evil](https://stackoverflow.com/questions/19158339). So if there are any alternatives, I would still be open to another solution. – Frank Zalkow Mar 17 '22 at 14:27
  • OK, but to clarify it is not global variable, the variable is module scope not global scope. not the same as when declare with `global` keyword. You access the variable by `mymodule.something`. you can get the builtin module file path in this way also, like `os.__file__`, I edit the answer for an example. – gftea Mar 17 '22 at 15:55