You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/config.py

336 lines
14 KiB
Python

"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
# -------------------------------------------------------------------------
"""Extend the .env using dynaconf (dynamic configuration and settings)
This config.py module assumes a minimal enviornment is defined in the .env
To do: ensure that we can stack/layer the dynamic env to work with O3DE projects
"""
# built in's
import os
import sys
import site
import re
# 3rdParty (possibly) py3 ships with pathlib, 2.7 does not
# import pathlib
# our framework for dcc tools need to run in apps like Maya that may still be
# on py27 so we need to import and use after some boostrapping
# -------------------------------------------------------------------+------
#os.environ['PYTHONINSPECT'] = 'True'
_MODULE_PATH = os.path.abspath(__file__)
# we don't have access yet to the DCCsi Lib\site-packages
# (1) this will give us import access to azpy (always?)
_DCCSIG_PATH = os.getenv('DCCSIG_PATH',
os.path.abspath(os.path.dirname(_MODULE_PATH)))
# ^ we assume this config is in the root of the DCCsi
# if it's not, be sure to set envar 'DCCSIG_PATH' to ensure it
site.addsitedir(_DCCSIG_PATH) # PYTHONPATH
# now we have azpy api access
import azpy
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
# set up global space, logging etc.
# set these true if you want them set globally for debugging
_DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_PACKAGENAME = 'DCCsi.config'
_LOG_LEVEL = int(20)
if _DCCSI_GDEBUG:
_LOG_LEVEL = int(10)
_LOGGER = azpy.initialize_logger(_PACKAGENAME,
log_to_file=False,
default_log_level=_LOG_LEVEL)
_LOGGER.info('Starting up: {}.'.format({_PACKAGENAME}))
_LOGGER.info('site.addsitedir({})'.format(_DCCSIG_PATH))
_LOGGER.debug('_DCCSI_GDEBUG: {}'.format(_DCCSI_GDEBUG))
_LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(_DCCSI_DEV_MODE))
# early attach WingIDE debugger (can refactor to include other IDEs later)
if _DCCSI_DEV_MODE:
from azpy.test.entry_test import connect_wing
foo = connect_wing()
# to do: ^ this should be replaced with full featured azpy.dev.util
# that supports additional debuggers (pycharm, vscode, etc.)
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# (2) this will give us import access to modules we provide
_DCCSI_PYTHON_LIB_PATH = azpy.config_utils.bootstrap_dccsi_py_libs(_DCCSIG_PATH)
# Now we should be able to just carry on with pth lib and dynaconf
from dynaconf import Dynaconf
try:
import pathlib
except:
import pathlib2 as pathlib
from pathlib import Path
_DCCSIG_PATH = Path(_DCCSIG_PATH).resolve()
_DCCSI_PYTHON_LIB_PATH = Path(_DCCSI_PYTHON_LIB_PATH).resolve()
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
def init_ly_pyside(LY_DEV=None):
"""sets access to lumberyards Qt dlls and PySide"""
LY_DEV = Path(LY_DEV).resolve()
if not LY_DEV.exists():
raise Exception('LY_DEV does NOT exist: {0}'.format(LY_DEV))
else:
# to do: 'windows_vs2019' might change or be different locally
# 'windows_vs2019' is defined as a str tag in constants
# we may not yet have access to azpy.constants :(
from azpy.constants import TAG_DIR_LY_BUILD
from azpy.constants import PATH_LY_BUILD_PATH
from azpy.constants import PATH_LY_BIN_PATH
# to do: pull some of these str and tags from constants
LY_BUILD_PATH = Path.joinpath(LY_DEV,
TAG_DIR_LY_BUILD).resolve()
LY_BIN_PATH = Path.joinpath(LY_BUILD_PATH,
'bin',
'profile').resolve()
# allows to retreive from settings.QTFORPYTHON_PATH
from azpy.constants import STR_QTFORPYTHON_PATH # a path string constructor
QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(LY_DEV)).resolve()
os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH)
site.addsitedir(str(QTFORPYTHON_PATH)) # PYTHONPATH
QT_PLUGIN_PATH = Path.joinpath(LY_BIN_PATH,
'EditorPlugins').resolve()
os.environ["DYNACONF_QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH)
os.environ['PATH'] = QT_PLUGIN_PATH.as_posix() + os.pathsep + os.environ['PATH']
QT_QPA_PLATFORM_PLUGIN_PATH = Path.joinpath(QT_PLUGIN_PATH,
'platforms').resolve()
os.environ["DYNACONF_QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH)
# if the line below is removed external standalone apps can't load PySide2
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH)
# ^^ bypass trying to set only with DYNACONF environment
os.environ['PATH'] = QT_QPA_PLATFORM_PLUGIN_PATH.as_posix() + os.pathsep + os.environ['PATH']
# ^^ this particular env only works correctly if put on the PATH in this manner
# add Qt binaries to the Windows path to handle findings DLL file dependencies
if sys.platform.startswith('win'):
path = os.environ['PATH']
newPath = ''
newPath += str(LY_BIN_PATH) + os.pathsep
newPath += str(Path.joinpath(QTFORPYTHON_PATH,
'shiboken2').resolve()) + os.pathsep
newPath += str(Path.joinpath(QTFORPYTHON_PATH,
'PySide2').resolve()) + os.pathsep
newPath += path
os.environ['PATH']=newPath
_LOGGER.debug('PySide2 bootstrapped PATH for Windows.')
try:
import PySide2
_LOGGER.debug('DCCsi, config.py: SUCCESS: import PySide2')
_LOGGER.debug(PySide2)
status = True
except ImportError as e:
_LOGGER.debug('DCCsi, config.py: FAILURE: import PySide2')
status = False
raise(e)
try:
import shiboken2
_LOGGER.debug('DCCsi, config.py: SUCCESS: import shiboken2')
_LOGGER.debug(shiboken2)
status = True
except ImportError as e:
_LOGGER.debug('DCCsi, config.py: FAILURE: import shiboken2')
status = False
raise(e)
# set up the pyside2-tools (pyside2uic)
# to do: move path construction string to constants and build off of SDK
# have not done that yet as I really want to get legal approval and
# add this to the QtForPython Gem
# please pass this on the current code review
DCCSI_PYSIDE2_TOOLS = Path.joinpath(LY_DEV,
'Gems',
'AtomLyIntegration',
'TechnicalArt',
'DccScriptingInterface',
'.dev',
'QtForPython',
'pyside2-tools-dev')
os.environ["DYNACONF_DCCSI_PYSIDE2_TOOLS"] = str(DCCSI_PYSIDE2_TOOLS.resolve())
os.environ['PATH'] = DCCSI_PYSIDE2_TOOLS.as_posix() + os.pathsep + os.environ['PATH']
return status
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
def test_pyside2():
"""Convenience method to test Qt / PySide2 access"""
# now test
_LOGGER.info('Testing Qt / PySide2')
try:
from PySide2.QtWidgets import QApplication, QPushButton
app = QApplication(sys.argv)
hello = QPushButton("Hello world!")
hello.resize(200, 60)
hello.show()
except Exception as e:
_LOGGER.error('FAILURE: Qt / PySide2')
status = False
raise(e)
_LOGGER.info('SUCCESS: .test_pyside2()')
sys.exit(app.exec_())
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# `envvar_prefix` = export envvars with `export DYNACONF_FOO=bar`.
# `settings_files` = Load this files in the order.
# here we are modifying or adding to the dynamic config settings on import
settings = Dynaconf(
envvar_prefix="DYNACONF",
settings_files=['settings.json', '.secrets.json'],
)
from azpy.constants import PATH_LY_BUILD_PATH
from azpy.constants import PATH_LY_BIN_PATH
# global settings
os.environ["DYNACONF_DCCSI_GDEBUG"] = str(_DCCSI_GDEBUG)
os.environ["DYNACONF_DCCSI_DEV_MODE"] = str(_DCCSI_DEV_MODE)
# search up to get \dev
_LY_DEV = azpy.config_utils.get_stub_check_path(in_path=_DCCSIG_PATH,
check_stub='engine.json')
os.environ["DYNACONF_LY_DEV"] = str(_LY_DEV.resolve())
_LY_PROJECT = azpy.config_utils.get_current_project(_LY_DEV)
os.environ["DYNACONF_LY_PROJECT"] = _LY_PROJECT
_LY_PROJECT_PATH = Path(_LY_DEV, _LY_PROJECT)
os.environ["DYNACONF_LY_PROJECT_PATH"] = str(_LY_PROJECT_PATH)
os.environ["DYNACONF_DCCSIG_PATH"] = str(_DCCSIG_PATH)
_DCCSI_CONFIG_PATH = Path(_MODULE_PATH).resolve()
os.environ["DYNACONF_DCCSI_CONFIG_PATH"] = str(_DCCSI_CONFIG_PATH)
_DCCSIG_SDK_PATH = Path.joinpath(_DCCSIG_PATH, 'SDK').resolve()
os.environ["DYNACONF_DCCSIG_SDK_PATH"] = str(_DCCSIG_SDK_PATH)
os.environ["DYNACONF_DCCSI_PYTHON_LIB_PATH"] = str(_DCCSI_PYTHON_LIB_PATH)
os.environ["DYNACONF_OS_FOLDER"] = azpy.config_utils.get_os()
# we need to set up the Ly dev build \bin\path (for Qt dll access)
_LY_BUILD_PATH = Path(PATH_LY_BUILD_PATH).resolve()
os.environ["DYNACONF_LY_BUILD_PATH"] = str(_LY_BUILD_PATH)
_LY_BIN_PATH = Path(PATH_LY_BIN_PATH).resolve()
os.environ["DYNACONF_LY_BIN_PATH"] = str(_LY_BIN_PATH)
# project cache log dir path
from azpy.constants import ENVAR_DCCSI_LOG_PATH
from azpy.constants import PATH_DCCSI_LOG_PATH
_DCCSI_LOG_PATH = Path(os.getenv(ENVAR_DCCSI_LOG_PATH,
Path(PATH_DCCSI_LOG_PATH.format(LY_DEV=_LY_DEV,
LY_PROJECT=_LY_PROJECT))))
os.environ["DYNACONF_DCCSI_LOG_PATH"] = str(_DCCSI_LOG_PATH)
# hard checks
if not _LY_BIN_PATH.exists():
raise Exception('LY_BIN_PATH does NOT exist: {0}'.format(_LY_BIN_PATH))
else:
# adding to sys.path apparently doesn't work for .dll locations like Qt
os.environ['PATH'] = _LY_BIN_PATH.as_posix() + os.pathsep + os.environ['PATH']
_LOGGER.info('Dynaconf config.py ... DONE')
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# settings.setenv() # doing this will add the additional DYNACONF_ envars
def get_config_settings(setup_ly_pyside=False):
"""Convenience method to set and retreive settings directly from module."""
from dynaconf import settings
if setup_ly_pyside:
init_ly_pyside(settings.LY_DEV)
settings.setenv()
return settings
# --- END -----------------------------------------------------------------
###########################################################################
# Main Code Block, runs this script as main (testing)
# -------------------------------------------------------------------------
if __name__ == '__main__':
"""Run this file as main"""
_LOG_LEVEL = int(10) # same as _logging.DEBUG
_LOGGER = azpy.initialize_logger(_PACKAGENAME,
log_to_file=True,
default_log_level=_LOG_LEVEL)
from dynaconf import settings
# not using fstrings in this module because it might run in py2.7 (maya)
_LOGGER.info('DCCSI_GDEBUG: {}'.format(settings.DCCSI_GDEBUG))
_LOGGER.info('DCCSI_DEV_MODE: {}'.format(settings.DCCSI_DEV_MODE))
_LOGGER.info('DCCSI_LOGLEVEL: {}'.format(settings.DCCSI_LOGLEVEL))
_LOGGER.info('OS_FOLDER: {}'.format(settings.OS_FOLDER))
_LOGGER.info('LY_PROJECT: {}'.format(settings.LY_PROJECT))
_LOGGER.info('LY_PROJECT_PATH: {}'.format(settings.LY_PROJECT_PATH))
_LOGGER.info('LY_DEV: {}'.format(settings.LY_DEV))
_LOGGER.info('LY_BUILD_PATH: {}'.format(settings.LY_BUILD_PATH))
_LOGGER.info('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH))
_LOGGER.info('DCCSI_LOG_PATH: {}'.format(settings.DCCSI_LOG_PATH))
_LOGGER.info('DCCSI_CONFIG_PATH: {}'.format(settings.DCCSI_CONFIG_PATH))
_LOGGER.info('DCCSIG_PATH: {}'.format(settings.DCCSIG_PATH))
_LOGGER.info('DCCSI_PYTHON_LIB_PATH: {}'.format(settings.DCCSI_PYTHON_LIB_PATH))
_LOGGER.info('DDCCSI_PY_BASE: {}'.format(settings.DDCCSI_PY_BASE))
# To Do: These should ONLY be set for Lumberyard and non-DCC environments
# They will most likely cause Qt/PySide DCC apps to fail
# or hopefully they can be overridden for DCC envionments
# that provide their own Qt dlls and Pyside2
#_LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH))
#_LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH))
init_ly_pyside(settings.LY_DEV) # init lumberyard Qt/PySide2
# from dynaconf import settings # <-- no need to reimport
settings.setenv() # doing this will add/set the additional DYNACONF_ envars
_LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH))
_LOGGER.info('LY_BIN_PATH: {}'.format(settings.LY_BIN_PATH))
_LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH))
_LOGGER.info('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(settings.QT_QPA_PLATFORM_PLUGIN_PATH))
_LOGGER.info('DCCSI_PYSIDE2_TOOLS: {}'.format(settings.DCCSI_PYSIDE2_TOOLS))
test_pyside2() # test PySide2 access with a pop-up button