6

Ok, so i made a python script places inside a package. The tree looks something like this:

├── foo
│   ├── __init__.py
│   ├── funcs
│   │   ├── __init__.py
│   │   └── stuff.py
│   ├── resources
│   │   └── haarcascade_frontalface_default.xml
│   └── scripts
│       ├── __init__.py
│       └── script.py
└── setup.py

So inside the script file, im using openCV's cv2 to detect faces, and for that the cv2.CascadeClassifier requires the path of the XML file located under /resources. Now because this is a script, i need to be able to run it from anywhere, so a relative path to the resource file sadly doesn't do the trick. How can I get the absolute path to the xml file from within script.py? You can assume that the script and the xml file is located relative to each other respectively, just like the example above. Thanks :))

PS: Bonus if the solution works with eggs as well. Much appreciated

3 Answers 3

9

Currently the best way to do this is importlib.resources. Since Python 3.7 it is available in the standard library. For earlier versions, there is a backport called importlib_resources.

Follow the documentation.

In your case this should more or less look like this :

import importlib.resources
xml_path = importlib.resources.path('foo.resources', 'haarcascade_frontalface_default.xml')

There are many advantages to this, most importantly it is standard and it will work wherever the package is installed even if it's in a zip file.

In your case, you might have to add an __init__.py file to your resources directory.

Sign up to request clarification or add additional context in comments.

Comments

2

Using the os module works, but if you have access to a python version >= 3.4, then pathlib is an alternative that handles itself a little easier and performs better across platforms:

from pathlib import Path

# when using pathlib.Path, slashes get automatically transformed into the 
# correct path-division character, depending on the platform
RESOURCES_PATH = Path(__file__).parent.parent / "resources"

face_cascade = cv2.CascadeClassifier()
face_cascade.load(RESOURCES_PATH / "haarcascade_frontalface_default.xml")

If you find yourself defining lots of these kinds of constants, consider putting all of them in a file like foo/util.py so that they are easily reusable within your project and don't need to be re-declared or imported from a script.


An even better option in python versions >=3.7 is using importlib.resources.path, which resolves resources automatically from the package root, so you don't need to find it by hand by walking up from __file__:

import importlib

face_cascade = cv2.CascadeClassifier()
with importlib.resources.path("foo.resources", "haarcascade_frontalface_default.xml") as haar_resource:
    # haar_resource is a pathlib.Path object here as well, so plugging it is simple
    face_cascade.load(haar_resource)

This is a lot more elegant and should be the preferred solution given it's available.

2 Comments

No. Use the standard library importlib.resources or its backport importlib_resources instead.
Didn't know about that, thanks. That looks a lot better than the alternatives.
-4

I'm not sure I understand the question correctly, but maybe os.path will help? Something like:

>>> import os
>>> os.path.abspath("directory/somefile.txt")
'C:/somedirectory/directory/directory/somefile.txt'

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.