145 lines
5.9 KiB
Python
145 lines
5.9 KiB
Python
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
||
|
import bpy
|
||
|
import os, sys
|
||
|
import subprocess
|
||
|
from pathlib import Path
|
||
|
|
||
|
# THIRD_PARTY = Path(__file__).resolve().parent / "libs"# os.path.join(os.path.dirname(os.path.abspath(__file__)), "libs")
|
||
|
# DEFAULT_CACHE_DIR = Path(__file__).resolve().parent / "cache"# os.path.join(os.path.dirname(os.path.abspath(__file__)), "cache")
|
||
|
# SUBPROCESS_DIR = PYTHON_PATH.parent
|
||
|
|
||
|
PYTHON_PATH = Path(sys.executable)
|
||
|
BLENDER_SITE_PACKAGE = PYTHON_PATH.parents[1] / 'lib' / 'site-packages'
|
||
|
# or: BLENDER_SITE_PACKAGE = Path(bpy.utils.resource_path('LOCAL')) / 'python' / 'lib' / 'site-packages'
|
||
|
|
||
|
|
||
|
def module_can_be_imported(name):
|
||
|
try:
|
||
|
__import__(name)
|
||
|
return True
|
||
|
except ModuleNotFoundError:
|
||
|
return False
|
||
|
|
||
|
def install_pip():
|
||
|
# pip can not necessarily be imported into Blender after this
|
||
|
subprocess.run([str(PYTHON_PATH), "-m", "ensurepip"])
|
||
|
|
||
|
def install_package(name):
|
||
|
# import logging
|
||
|
# logging.debug(f"Using {PYTHON_PATH} for installation")
|
||
|
## !!! try with , '--user' ?
|
||
|
subprocess.run([str(PYTHON_PATH), "-m", "pip", "install", name])
|
||
|
|
||
|
def setup(dependencies):
|
||
|
'''
|
||
|
Get a set containing multiple tuple of (module, package) names pair (str)
|
||
|
install pip with ensurepip if needed, try to import, install, retry
|
||
|
'''
|
||
|
|
||
|
os.environ['PYTHONUSERBASE'] = str(BLENDER_SITE_PACKAGE)
|
||
|
if not module_can_be_imported("pip"):
|
||
|
install_pip()
|
||
|
|
||
|
for module_name, package_name in dependencies:
|
||
|
if not module_can_be_imported(module_name):
|
||
|
print(f'Installing module: {module_name} - package: {package_name}')
|
||
|
install_package(package_name)
|
||
|
module_can_be_imported(package_name)
|
||
|
|
||
|
|
||
|
### --- classic install (not used)
|
||
|
|
||
|
def pip_install(package_name):
|
||
|
'''Get a package name (str) and try to install and print in console'''
|
||
|
print(f'---Installing {package_name}---')
|
||
|
try:
|
||
|
output = subprocess.check_output([bpy.app.binary_path_python, '-m', 'pip', 'install', package_name])
|
||
|
print(output)
|
||
|
except subprocess.CalledProcessError as e:
|
||
|
print(e.output)
|
||
|
return e.output
|
||
|
|
||
|
|
||
|
def pip_install_and_import(dependencies):
|
||
|
'''
|
||
|
Get a set containing multiple tuple of (module, package) names pair (str)
|
||
|
try to import, if import fail, try to install then try to reimport
|
||
|
'''
|
||
|
# os.environ['PYTHONUSERBASE'] = str(BLENDER_SITE_PACKAGE)
|
||
|
for module_name, package_name in dependencies:
|
||
|
# '--user'# install for all version of blender (suposely in app data roaming or config files...)
|
||
|
# '--no-deps'# dont update dependancy (in this case, avoid installing downloading a duplication of numpy)
|
||
|
|
||
|
try:
|
||
|
__import__(module_name)
|
||
|
continue
|
||
|
|
||
|
except ImportError:
|
||
|
try:
|
||
|
## auto install dependancy (need to run as admin)
|
||
|
print(f'Installing module: {module_name} - package: {package_name}') #"--user" ?
|
||
|
|
||
|
#### Using target
|
||
|
## within built-in modules (need run as admin in most case)
|
||
|
# subprocess.check_call([str(PYTHON_PATH), "-m", "pip", "install", f'--target={BLENDER_SITE_PACKAGE}', package_name])
|
||
|
|
||
|
done = False
|
||
|
|
||
|
## within external modules (if script files are there)
|
||
|
external_scripts = bpy.context.preferences.filepaths.script_directory
|
||
|
print('external_scripts: ', external_scripts)
|
||
|
print('__file__: ', __file__)
|
||
|
in_external = str(Path(__file__)).startswith(str(Path(external_scripts)))
|
||
|
|
||
|
if external_scripts and len(external_scripts) > 2 and in_external:
|
||
|
external_scripts = Path(external_scripts)
|
||
|
if external_scripts.exists():
|
||
|
external_modules = external_scripts / 'modules'
|
||
|
print(f'using external scripts modules: {external_modules}')
|
||
|
external_modules.mkdir(exist_ok=True)# dont raise error if already exists
|
||
|
|
||
|
# cmd = [str(PYTHON_PATH), "-m", "pip", "install", f'--target={external_modules}', package_name]
|
||
|
cmd = [str(PYTHON_PATH), "-m", "pip", '--no-cache-dir', "install", f'--target={external_modules}', package_name, '--no-deps']
|
||
|
print('Run', ' '.join(cmd))
|
||
|
subprocess.check_call(cmd)
|
||
|
done=True
|
||
|
|
||
|
## within user local modules (if not in external scripts)
|
||
|
if not done:
|
||
|
user_module = Path(bpy.utils.user_resource('SCRIPTS', path='modules', create=True)) # create the folder if not exists
|
||
|
print(f'Using users modules: {user_module}')
|
||
|
|
||
|
# cmd = [str(PYTHON_PATH), "-m", "pip", "install", f'--target={user_module}', package_name]
|
||
|
cmd = [str(PYTHON_PATH), "-m", "pip", '--no-cache-dir', "install", f'--target={user_module}', package_name, '--no-deps']
|
||
|
print('Run', ' '.join(cmd))
|
||
|
subprocess.check_call(cmd)
|
||
|
|
||
|
except Exception as e:
|
||
|
print(f'{package_name} install error: {e}')
|
||
|
print('Maybe try restarting blender as Admin')
|
||
|
return e
|
||
|
|
||
|
try:
|
||
|
__import__(module_name)
|
||
|
except ImportError as e:
|
||
|
print(f'!!! module {module_name} still cannot be imported')
|
||
|
return e
|
||
|
|
||
|
|
||
|
"""
|
||
|
## addons Paths
|
||
|
|
||
|
# natives
|
||
|
built_in_addons = os.path.join(bpy.utils.resource_path('LOCAL') , path='scripts', 'addons')
|
||
|
|
||
|
# users
|
||
|
users_addons = bpy.utils.user_resource('SCRIPTS', path='addons')
|
||
|
|
||
|
#external
|
||
|
external_addons = None
|
||
|
external_script_dir = bpy.context.preferences.filepaths.script_directory
|
||
|
if external_script_dir and len(external_script_dir) > 2:
|
||
|
external_addons = os.path.join(external_script_dir, 'addons')
|
||
|
"""
|
||
|
|