Source code for blendersynth.utils.blender_setup.blender_locator

import platformdirs
import os
import configparser
import shutil
import subprocess
import warnings

appname = "blendersynth"
appauthor = "BlenderSynth"
config_dir = platformdirs.user_config_dir(appname, appauthor)
os.makedirs(config_dir, exist_ok=True)
config_file = os.path.join(config_dir, "config.ini")


[docs] def is_blender_in_path() -> bool: """Check if blender is in the PATH (i.e. can be run from the command line with 'blender')""" return shutil.which("blender") is not None
[docs] def find_blender_python(blender_path: str) -> str: """Given a blender executable, find the python interpreter it uses :param blender_path: path to blender executable""" if read_from_config("BLENDER_PYTHON_PATH") is not None: return read_from_config("BLENDER_PYTHON_PATH") # get blender to run script to find python path try: targ_script = os.path.abspath( os.path.join(os.path.dirname(__file__), "blender_python_path.py") ) output = subprocess.check_output( [blender_path, "--background", "--python", targ_script] ) except subprocess.CalledProcessError as e: raise Exception(f"Issues with finding blender python path. Error: {e}") # read output to find python path out = None for l in output.decode("utf-8").split("\n"): if "PYTHON INTERPRETER" in l: out = l.split(": ")[1].strip() break if out is not None: write_to_config("BLENDER_PYTHON_PATH", out) return out raise Exception("Could not find Python interpreter for Blender.")
[docs] def validate_blender_path(blender_path: str) -> bool: """Check if `blender_path` is a valid blender executable. :param blender_path: path to blender executable""" if os.access(blender_path, os.X_OK): return True return False
[docs] def set_blender_path(_blender_path: str = None) -> str: """First time set-up of Blender Path. Will try to find Blender path in following order of precedence: 1) Input argument _blender_path 2) Environment variable `BLENDER_PATH` 3) Blender in PATH 4) Ask user for path When it finds a valid path, it will save it to the Config file stored in user's `platformdirs.user_config_dir` """ if _blender_path is not None: if validate_blender_path(_blender_path): blender_path = _blender_path elif validate_blender_path( _blender_path + ".exe" ): # add .exe if not there (helps os.access checking) blender_path = _blender_path + ".exe" else: raise ValueError( f"Provided Blender path, {_blender_path}, is not executable." ) elif os.environ.get("BLENDER_PATH") is not None: blender_path = os.environ.get("BLENDER_PATH") elif is_blender_in_path(): blender_path = shutil.which("blender") else: blender_path = input( "Blender not found in PATH or Environment Variable.\nPlease provide path to blender executable: " ) blender_path = os.path.abspath(blender_path) # make sure it's absolute path if not validate_blender_path(blender_path): if validate_blender_path(blender_path + ".exe"): blender_path += ".exe" else: raise ValueError( f"Provided Blender path, {blender_path}, is not valid as a Blender executable." ) write_to_config("BLENDER_PATH", blender_path) return blender_path
[docs] def get_blender_path() -> str: """Return blender path, or None if no config file found""" return read_from_config("BLENDER_PATH")
[docs] def write_to_config(key, value, section="BLENDER_SETUP"): """Load config, and write key value pair to cfg[section]""" config = configparser.ConfigParser() if os.path.exists(config_file): config.read(config_file) if section not in config: config[section] = {} config[section][key] = value with open(config_file, "w") as configfile: config.write(configfile)
[docs] def read_from_config(key, section="BLENDER_SETUP"): """Load config, and read value from cfg[section]""" config = configparser.ConfigParser() if os.path.exists(config_file): config.read(config_file) if section not in config: return None if key not in config[section]: return None return config[section][key]
[docs] def remove_from_config(key, section="BLENDER_SETUP"): """Load config, and remove key from cfg[section]""" config = configparser.ConfigParser() if os.path.exists(config_file): config.read(config_file) if section not in config: return None if key not in config[section]: return None del config[section][key] with open(config_file, "w") as configfile: config.write(configfile)
[docs] def remove_config(): """Remove config file""" if os.path.exists(config_file): os.remove(config_file)