Relative file paths in Python packages
-
06-07-2019 - |
Question
How do I reference a file relatively to a package's directory?
My directory structure is:
/foo package1/ resources/ __init__.py package2/ resources/ __init__.py script.py
script.py
imports packages package1
and package2
. Although the packages can be imported by any other script on the system. How should I reference resources inside, say, package1
to ensure it would work in case os.path.curdir
is arbitrary?
Solution
If you want to reference files from the foo/package1/resources
folder you would want to use the __file__
variable of the module. Inside foo/package1/__init__.py
:
from os import path
resources_dir = path.join(path.dirname(__file__), 'resources')
OTHER TIPS
A simple/safe way to do this is using the resource_filename
method from pkg_resources (which is distributed with setuptools) like so:
from pkg_resources import resource_filename
filepath = resource_filename('package1', 'resources/thefile')
Or, if you're implementing this inside package1/___init___.py
:
from pkg_resources import resource_filename
filepath = resource_filename(__name__, 'resources/thefile')
This gives you a clean solution that is also (if I'm not mistaken) zip safe.
You can be zip-safe and at the same time use a nice convenient API if you use twisted.python.modules.
For example, if I have a data.txt
with some text in it and and this sample.py
in one directory:
from twisted.python.modules import getModule
moduleDirectory = getModule(__name__).filePath.parent()
print repr(moduleDirectory.child("data.txt").open().read())
then importing sample
will do this:
>>> import sample
'Hello, data!\n'
>>>
If your module is in a regular directory, getModule(__name__).filePath
will be a FilePath; if it's in a zip file, it will be a ZipPath, which supports most, but not all, of the same APIs.
This is a bad idea, because, if your package was installed as zipped egg, then resources can be unavailable.
If you use setuptool, don't forget to add zip_safe=False to the setup.py config.