Search code examples
pythonpippathsetuptoolspackaging

Get Path to Python File Before Installation


I have a project that includes c++ binaries and python scripts, it's setup such that it should be installed using setuptools. One of the python files is intended to be both used as a script " python3 script_name.py params and for it's primary function to be used in other python projects from script_name import function. The primary function calls a binary which is in a known relative location before the installation (the user is expected to call pip install project_folder). So in order to call the binary I want to get this files location (pre installation)

To get this I used something like

Path(__file__).resolve().parent

however, since the installation moves the file to another folder like ~/.local/... this doesn't work when imported after the installation.

Is there a way to get the original file path, or to make the installation save that path somewhere?

EDIT: After @sinoroc 's suggestion I tried including the binary as a resource by putting an __init__.py in the build folder and putting

from importlib.resources import files
import build

binary = files(build).joinpath("binary")

in the main init. After that package.binary still gives me a path to my .local/lib and binary.is_file() still returns False

from importlib_resources import files

GenerateHistograms = files("build").joinpath("GenerateHistograms")

gave the same result


Solution

  • Since you are installing your package, you also need to include your C++ binary in the installation. You cannot have a mixed setup. I suggest something like this.

    In your setup.py:

    from setuptools import setup, find_packages
    
    setup(
        name="mypkg",
        packages=find_packages(exclude=["tests"]),
        package_data={
            "mypkg": [
                "binary",  # relative path to your package directory
            ]
        },
        include_package_data=True,
    )
    

    Then in your module use pkg_resources:

    from pathlib import Path
    
    from pkg_resources import resource_filename
    
    # "binary" is whatever relative path you put in package_data
    path_to_binary = Path(resource_filename("mypkg", "binary"))
    

    pkg_resources should be pulled in by setuptools.

    EDIT: the recipe above might be a bit out of date; as @sinoroc suggests, using importlib.resources instead of pkg_resources is probably the modern equivalent.