Is module __file__ attribute absolute or relative?

Question:

I’m having trouble understanding __file__. From what I understand, __file__ returns the absolute path from which the module was loaded.

I’m having problem producing this: I have a abc.py with one statement print __file__, running from /d/projects/ python abc.py returns abc.py. running from /d/ returns projects/abc.py. Any reasons why?

Asked By: goh

||

Answers:

From the documentation:

__file__ is the pathname of the file from which the module was loaded, if it was loaded from a file. The __file__ attribute is not present for C modules that are statically linked into the interpreter; for extension modules loaded dynamically from a shared library, it is the pathname of the shared library file.

From the mailing list thread linked by @kindall in a comment to the question:

I haven’t tried to repro this particular example, but the reason is
that we don’t want to have to call getpwd() on every import nor do we
want to have some kind of in-process variable to cache the current
directory. (getpwd() is relatively slow and can sometimes fail
outright, and trying to cache it has a certain risk of being wrong.)

What we do instead, is code in site.py that walks over the elements of
sys.path and turns them into absolute paths. However this code runs
before ” is inserted in the front of sys.path, so that the initial
value of sys.path is ”.

For the rest of this, consider sys.path not to include ''.

So, if you are outside the part of sys.path that contains the module, you’ll get an absolute path. If you are inside the part of sys.path that contains the module, you’ll get a relative path.

If you load a module in the current directory, and the current directory isn’t in sys.path, you’ll get an absolute path.

If you load a module in the current directory, and the current directory is in sys.path, you’ll get a relative path.

Answered By: agf

With the help of the of Guido mail provided by @kindall, we can understand the standard import process as trying to find the module in each member of sys.path, and file as the result of this lookup (more details in PyMOTW Modules and Imports.). So if the module is located in an absolute path in sys.path the result is absolute, but if it is located in a relative path in sys.path the result is relative.

Now the site.py startup file takes care of delivering only absolute path in sys.path, except the initial '', so if you don’t change it by other means than setting the PYTHONPATH (whose path are also made absolute, before prefixing sys.path), you will get always an absolute path, but when the module is accessed through the current directory.

Now if you trick sys.path in a funny way you can get anything.

As example if you have a sample module foo.py in /tmp/ with the code:

import sys
print(sys.path)
print (__file__)

If you go in /tmp you get:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
./foo.py

When in in /home/user, if you add /tmp your PYTHONPATH you get:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
/tmp/foo.py

Even if you add ../../tmp, it will be normalized and the result is the same.

But if instead of using PYTHONPATH you use directly some funny path
you get a result as funny as the cause.

>>> import sys
>>> sys.path.append('../../tmp')
>>> import foo
['', '/usr/lib/python3.3', .... , '../../tmp']
../../tmp/foo.py

Guido explains in the above cited thread, why python do not try to transform all entries in absolute paths:

we don’t want to have to call getpwd() on every import ….
getpwd() is relatively slow and can sometimes fail outright,

So your path is used as it is.

Answered By: marcz

__file__ is absolute since Python 3.4, except when executing a script directly using a relative path:

Module __file__ attributes (and related values) should now always contain absolute paths by default, with the sole exception of __main__.__file__ when a script has been executed directly using a relative path. (Contributed by Brett Cannon in bpo-18416.)

Not sure if it resolves symlinks though.

Example of passing a relative path:

$ python script.py
Answered By: anatoly techtonik

Late simple example:

from os import path, getcwd, chdir

def print_my_path():
    print('cwd:     {}'.format(getcwd()))
    print('__file__:{}'.format(__file__))
    print('abspath: {}'.format(path.abspath(__file__)))

print_my_path()

chdir('..')

print_my_path()

Under Python-2.*, the second call incorrectly determines the path.abspath(__file__) based on the current directory:

cwd:     C:codespy
__file__:cwd_mayhem.py
abspath: C:codespycwd_mayhem.py
cwd:     C:codes
__file__:cwd_mayhem.py
abspath: C:codescwd_mayhem.py

As noted by @techtonik, in Python 3.4+, this will work fine since __file__ returns an absolute path.

Answered By: SimplyKnownAsG

__file__ can be relative or absolute depending on the python version used and whether the module is executed directly or not.

TL;DR:

  • From python 3.5 to 3.8, __file__ is set to the relative path of the module w.r.t. the current working directory if the module is called directly. Otherwise it is set to the absolute path.
  • Since python 3.9, __file__ is set to the absolute path even if the corresponding module is executed directly.

For instance, having the following setup (inspired by this answer):

# x.py:
from pathlib import Path
import y
print(__file__)
print(Path(__file__))
print(Path(__file__).resolve())
# y.py:
from pathlib import Path
print(__file__)
print(Path(__file__))

with x.py and y.py in the same directory. The different outputs after going to this directory and executing:

python x.py

are:

Python 3.5 – 3.8:

D:py_testsy.py
D:py_testsy.py
x.py
x.py
D:py_testsx.py

Python 3.9 – 3.11

D:py_testsy.py
D:py_testsy.py
D:py_testsx.py
D:py_testsx.py
D:py_testsx.py
Answered By: Javier TG
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.