4
$\begingroup$

The path for the Python executable used in Blender (up to 2.79) is not configurable.

I looked around and did not find a consistent way to be able to use custom 3rd party Python modules without installing then directly in the O.S. Python.

This would work, although not the ideal solution, but then I had issues with that in Linux, as running pip install system wide adds the modules to /usr/local/lib, but /usr/local is not the in the PYTHONPATH when Blender is started.

An active Python virtualenv - Python's own way of isolating 3rd party modules is ignored by Blender - as the way to activate a virtualenv is simply using it's Python binary (in a different Path than the one used by Blender).

$\endgroup$
  • $\begingroup$ I'm on ubuntu, where pretty much you get stuck with the system python (3.6 for 18) or updates get broken. Using update-alternatives works somewhat but breaks partial upgrades (doesn't like the alternative link). I build blender with python 3.7.1 I would recommend installing python3.7.1 on your system, and its associated pip, pip3.7. When you wish to install 3rd party module pip3.7 install plumbum --user The user flag being the key here. Any 3rd party modules installed this way import directly into blender, and require no dicking around with system path. $\endgroup$ – batFINGER Feb 21 at 4:26
  • $\begingroup$ The key here is that "you build Blender" - not everyone build their own Blender. And certainly an add-on author would not have many users if it would require a custom build to run. But please, paste this comment as an answer to the question so it gets better documented - I myself did something like that in a previous install. $\endgroup$ – jsbueno Feb 21 at 11:22
  • $\begingroup$ No It really has nothing to do with building blender. (mentioned as not sure what python version ships with blender) It only matters that the python versions match. Blender (from wherever) python version to an installed python version eg simple install on ubuntu with sudo apt install python3.7 all whilst 3.6 remains my "system" python. pip3.7 with user flag installs to '/home/batfinger/.local/lib/python3.7/site-packages' which if it exists is appended to sys.path IMO this is far simpler than matching blender and ubuntu system python versions required in answer below. $\endgroup$ – batFINGER Feb 21 at 12:40
  • $\begingroup$ Thabk you for the duplicate suggestion - It had useful things for me, but I think I needed some more specific things I updated in the answer bellow. I made this question Linux specific so it differs from the other. $\endgroup$ – jsbueno Feb 22 at 13:08
5
$\begingroup$

update I found out that the user-configuration folder for Blender, in ~/.config/blender/<version>/scripts/, though only containing an addons folder by default, can have also a modules and a startup folder. If packages are pip-installed in the modules folder, they are visible from Blender, so, no custom virtualenv, or custom path manipulation at runtime is needed.

In other words, in a modern Linux, to have a Python package available for my user visible in Python's blender, I can do:

mkdir ~/.config/blender/2.79/scripts/modules
pip install -t ~/.config/blender/2.79/scripts/modules <python-package>

On Windows, the scripts folder for Blender will have a modules folder that can be used in the same way, as per the answers in the similar question here: Using 3rd party Python modules

previous answer

What I did do is a small Python function that is run prior to other imports in the add-on .py file to introspect the system Path and add any active virtualenv's to the Python import directory list sys.path.

The final function I came up with may be robust enough to help others. I add this to the beggining of my .py add-ons:

def fix_path():
    """Enable 3rd party Python modules installed in an
    active Virtualenv when Python is run.
    (Blender ignores the virtualenv, and uses a
    Python binary hardcoded in the build. This works
    if the virtualenv's Python is the same as blenders')
    """

    import sys, os
    from pathlib import Path

    pyversion_path = f"python{sys.version_info.major}.{sys.version_info.minor}"

    for pathcomp in os.environ["PATH"].split(os.pathsep)[::-1]:
        p = Path(pathcomp)
        if p.name != "bin":
            continue
        lib_path = p.parent / "lib" / pyversion_path / "site-packages"
        if not lib_path.exists():
            continue
        if str(lib_path) in sys.path:
            continue
        sys.path.insert(0, str(lib_path))

        print(f"{__name__} Add-on: prepended {lib_path!r} to PYTHONPATH")

fix_path()

(import statements follow)

$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.