41

I have a Python module which uses some resources in a subdirectory of the module directory. After searching around on stack overflow and finding related answers, I managed to direct the module to the resources by using something like

import os
os.path.join(os.path.dirname(__file__), 'fonts/myfont.ttf')

This works fine when I call the module from elsewhere, but it breaks when I call the module after changing the current working directory. The problem is that the contents of __file__ are a relative path, which doesn't take into account the fact that I changed the directory:

>>> mymodule.__file__
'mymodule/__init__.pyc'
>>> os.chdir('..')
>>> mymodule.__file__
'mymodule/__init__.pyc'

How can I encode the absolute path in __file__, or barring that, how can I access my resources in the module no matter what the current working directory is? Thanks!

Machavity
  • 30,841
  • 27
  • 92
  • 100
jvkersch
  • 575
  • 1
  • 4
  • 9

2 Answers2

70

Store the absolute path to the module directory at the very beginning of the module:

package_directory = os.path.dirname(os.path.abspath(__file__))

Afterwards, load your resources based on this package_directory:

font_file = os.path.join(package_directory, 'fonts', 'myfont.ttf')

And after all, do not modify of process-wide resources like the current working directory. There is never a real need to change the working directory in a well-written program, consequently avoid os.chdir().

  • Thanks, that works perfectly! I'll also keep your remark in mind to avoid changing the working directory. – jvkersch Nov 16 '10 at 00:47
  • I know this is an old question but somehow it popped up again. -- This is bad practice, and may break in many different scenarios. Use [`pkgutil.get_data()`](https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data) instead. – sinoroc Aug 04 '20 at 17:16
  • @sinoroc Could you please elaborate why you consider this bad practice and give an example of when is might break? – pfabri Mar 01 '21 at 14:04
  • 1
    @pfabri Sometimes things are imported directly from a zip archive, so `__file__` will not have a meaningful/usable value. There is some info about it all around, I don't have one specific reference to give you. But maybe read this other question: https://stackoverflow.com/questions/6028000/how-to-read-a-static-file-from-inside-a-python-package – sinoroc Mar 01 '21 at 17:58
2

Building on lunaryorn's answer, I keep a function at the top of my modules in which I have to build multiple paths. This saves me repeated typing of joins.

def package_path(*paths, package_directory=os.path.dirname(os.path.abspath(__file__))):
    return os.path.join(package_directory, *paths)

To build the path, call it like this:

font_file = package_path('fonts', 'myfont.ttf')

Or if you just need the package directory:

package_directory = package_path()
Ivo Mori
  • 2,177
  • 5
  • 24
  • 35
Weeesel
  • 29
  • 3
  • This is bad practice, and may break in many different scenarios. Use [`pkgutil.get_data()`](https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data) instead. – sinoroc Aug 04 '20 at 17:14