config.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. # -------------------------------------------------------------------------
  9. """! The dccsi Core configuration module
  10. :file: < DCCsi >/config.py
  11. :Status: Prototype
  12. :Version: 0.0.2, first significant refactor
  13. This module handles core configuration of the dccsi
  14. - It initializes and generates a dynamic, and synthetic, environment context,
  15. and settings.
  16. - ConfigCore class, inherits from DccScriptingInterface.azpy.config_class.ConfigClass (not yet)
  17. - This module uses dynaconf (a dynamic configuration and settings package)
  18. This config.py synthetic env can be overridden or extended with a local .env
  19. Example, create such as file:
  20. "< o3de >/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/.env"
  21. ^ this is only appropriate to a developer making local settings changes,
  22. primarily to outside of this module, and want those settings to persist
  23. across the dccsi modules which draw from this core config.py
  24. See: example.env.tmp (copy and rename to .env)
  25. This file should not be committed
  26. The second way to locally override or persist settings, is to make changes in
  27. the file:
  28. DccScriptingInterface/settings.local.json
  29. ^ this file can be generated via the core config.py cli
  30. or from
  31. If you only want to make transient envar settings changes that only persist to
  32. an IDE session, you can create and modify this file:
  33. "C:/Depot/o3de-dev/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Dev.bat"
  34. (copy and rename Env_Dev.bat.example to Env_Dev.bat) this only modifies the launcher environments.
  35. ^ this is a good option is good if you only want to alter the behavior while in IDE,
  36. or if you are troubleshooting a broken dccsi and/or modifying config.py
  37. The third option, is there are a few points in this module to can temporarily,
  38. force the default value returned if an envar is not set externally.
  39. # example envar default .....
  40. _DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
  41. To Do: ensure that we can stack/layer the dynamic env to work with O3DE project config
  42. ^ this means the following:
  43. - this module can load a stashed settings file (from anywhere?)
  44. - so any tool using it, can have the core/synthetic env and also load specific settings
  45. - <project>/registry/dccsi_config.setreg provides persistent distributable configuration
  46. - <user home>/.o3de/registry/dccsi_config.setreg, user only extended/override settings
  47. """
  48. import timeit
  49. _MODULE_START = timeit.default_timer() # start tracking
  50. # standard imports
  51. import os
  52. import sys
  53. import site
  54. import re
  55. import pathlib
  56. from pathlib import Path
  57. import logging as _logging
  58. # -------------------------------------------------------------------------
  59. # -------------------------------------------------------------------------
  60. # module global scope, set up module logging, etc.
  61. from DccScriptingInterface import _PACKAGENAME
  62. _MODULENAME = f'{_PACKAGENAME}.config'
  63. _LOGGER = _logging.getLogger(_MODULENAME)
  64. _LOGGER.debug(f'Initializing: {_MODULENAME}.')
  65. _MODULE_PATH = Path(__file__) # what if frozen?
  66. _LOGGER.debug(f'_MODULE_PATH: {_MODULE_PATH}')
  67. # -------------------------------------------------------------------------
  68. # -------------------------------------------------------------------------
  69. # dccsi global scope, ensure site access to dccsi
  70. from DccScriptingInterface.constants import *
  71. from DccScriptingInterface.globals import *
  72. import DccScriptingInterface.azpy.test.entry_test
  73. import DccScriptingInterface.azpy.config_utils
  74. # set this manually if you want to raise exceptions/warnings while debugging
  75. DCCSI_STRICT = False
  76. # allow package to capture warnings
  77. #_logging.captureWarnings(capture=True)
  78. if DCCSI_DEV_MODE:
  79. # if dev mode, this will attempt to auto-attach the debugger
  80. # at the earliest possible point in this module
  81. DccScriptingInterface.azpy.test.entry_test.connect_wing()
  82. # we should be able to import dccsi pkg dependencies now
  83. try:
  84. from dynaconf import Dynaconf
  85. except ImportError as e:
  86. _LOGGER.warning(f'Could not import dynaconf')
  87. _LOGGER.warning(f'Most likely the application loading this module does not have DCCsi pkg dependancies installed. Use foundation.py to install pkgs for ')
  88. _LOGGER.exception(f'{e} , traceback =', exc_info=True)
  89. raise e
  90. # dynaconf boilerplate
  91. from dynaconf import Dynaconf
  92. settings = Dynaconf(envar_prefix='DYNACONF',
  93. # the following will also load settings.local.json
  94. settings_files=['settings.json', '.secrets.json'])
  95. settings.setenv() # ensure default file based settings are in the env
  96. #-------------------------------------------------------------------------
  97. # -------------------------------------------------------------------------
  98. # going to optimize config to speed things up
  99. # the fastest way to know the engine root is to ...
  100. # see if the engine is running (azlmbr.paths.engroot)
  101. # or check if dev set in env, both those things happen here
  102. from DccScriptingInterface import O3DE_DEV
  103. from DccScriptingInterface import PATH_O3DE_PROJECT
  104. from DccScriptingInterface import O3DE_PROJECT
  105. from DccScriptingInterface import PATH_O3DE_BIN
  106. from DccScriptingInterface import PATH_O3DE_3RDPARTY
  107. from DccScriptingInterface import PATH_O3DE_TECHART_GEMS
  108. from DccScriptingInterface import PATH_DCCSIG
  109. from DccScriptingInterface import PATH_DCCSI_PYTHON_LIB
  110. # O3DE_DEV could be set in .env or settings.local.json to override config
  111. # this call with search fallback will be deprecated hopefully
  112. #O3DE_DEV = DccScriptingInterface.azpy.config_utils.get_o3de_engine_root()
  113. # -------------------------------------------------------------------------
  114. # -------------------------------------------------------------------------
  115. # refactor this module into a class object, other configs can inherit
  116. from DccScriptingInterface.azpy.config_class import ConfigClass
  117. # it is suggested that the core <dccsi>\config.py is re-written
  118. class CoreConfig(ConfigClass):
  119. """A class representing the DCCsi core config"""
  120. def __init__(self, *args, **kwargs):
  121. super().__init__(*args, **kwargs)
  122. _LOGGER.info(f'Initializing: {self.get_classname()}')
  123. # -------------------------------------------------------------------------
  124. # -------------------------------------------------------------------------
  125. # build the config
  126. core_config = CoreConfig(config_name='dccsi_core', auto_set=True)
  127. # add settings to config here
  128. # foo
  129. # -------------------------------------------------------------------------
  130. # -------------------------------------------------------------------------
  131. # local global scope
  132. # start locally prepping known default values for dynamic environment settings
  133. # these global variables are passed as defaults into methods within the module
  134. # special, a global home for stashing PATHs for managed settings
  135. global _DCCSI_SYS_PATH
  136. _DCCSI_SYS_PATH = list()
  137. # special, a global home for stashing PYTHONPATHs for managed settings
  138. global _DCCSI_PYTHONPATH
  139. _DCCSI_PYTHONPATH = list()
  140. # special, stash local PYTHONPATHs in a non-managed way (won't end up in settings.local.json)
  141. global _DCCSI_PYTHONPATH_EXCLUDE
  142. _DCCSI_PYTHONPATH_EXCLUDE = list()
  143. # this is a dict bucket to store none-managed settings (fully local to module)
  144. global _DCCSI_LOCAL_SETTINGS
  145. _DCCSI_LOCAL_SETTINGS = {}
  146. # access the the o3de scripts so we can utilize things like o3de.py
  147. PATH_O3DE_PYTHON_SCRIPTS = Path(O3DE_DEV, 'scripts').resolve()
  148. site.addsitedir(PATH_O3DE_PYTHON_SCRIPTS.as_posix())
  149. # -------------------------------------------------------------------------
  150. # -------------------------------------------------------------------------
  151. # An improvement would be to move this to azpy.config_utils
  152. # and be refactored to utilize py3+ fstrings
  153. def add_path_list_to_envar(path_list=_DCCSI_SYS_PATH,
  154. envar='PATH'):
  155. """!
  156. Take in a list of Path objects to add to system ENVAR (like PATH).
  157. This method explicitly adds the paths to the system ENVAR.
  158. @param path_list: a list() of paths
  159. @param envar: add paths to this ENVAR
  160. """
  161. # this method is called a lot and has become verbose,
  162. # it's suggested to clean this up when the config is next refactored
  163. _LOGGER.info('checking envar: {}'.format(envar))
  164. # get or default to empty
  165. if envar in os.environ:
  166. _ENVAR = os.getenv(envar, "< NOT SET >")
  167. else:
  168. os.environ[envar]='' # create empty
  169. _ENVAR = os.getenv(envar)
  170. # this is separated (split) into a list
  171. known_pathlist = _ENVAR.split(os.pathsep)
  172. if len(path_list) == 0:
  173. _LOGGER.warning('No {} paths added, path_list is empty'.format(envar))
  174. return None
  175. else:
  176. _LOGGER.info('Adding paths to envar: {}'.format(envar))
  177. # To Do: more validation and error checking?
  178. for p in path_list:
  179. p = Path(p)
  180. if (p.exists() and p.as_posix() not in known_pathlist):
  181. # adding directly to sys.path apparently doesn't work
  182. # for .dll locations like Qt
  183. # this pattern by extending the ENVAR seems to work correctly
  184. os.environ[envar] = p.as_posix() + os.pathsep + os.environ[envar]
  185. return os.environ[envar]
  186. # -------------------------------------------------------------------------
  187. # -------------------------------------------------------------------------
  188. # An improvement would be to move this to azpy.config_utils
  189. # and be refactored to utilize py3+ fstrings
  190. def add_path_list_to_addsitedir(path_list=_DCCSI_PYTHONPATH,
  191. envar='PYTHONPATH'):
  192. """"!
  193. Take in a list of Path objects to add to system ENVAR (like PYTHONPATH).
  194. This makes sure each path is fully added as searchable code access.
  195. Mainly to use/access site.addsitedir so from imports work in our namespace.
  196. @param path_list: a list() of paths
  197. @param envar: add paths to this ENVAR (and site.addsitedir)
  198. """
  199. # this method is called a lot and has become verbose,
  200. # it's suggested to clean this up when the config is next refactored
  201. _LOGGER.info('checking envar: {}'.format(envar))
  202. # get or default to empty
  203. if envar in os.environ:
  204. _ENVAR = os.getenv(envar, "< NOT SET >")
  205. else:
  206. os.environ[envar]='' # create empty
  207. _ENVAR = os.getenv(envar)
  208. # this is separated (split) into a list
  209. known_pathlist = _ENVAR.split(os.pathsep)
  210. new_pathlist = known_pathlist.copy()
  211. if len(path_list) == 0:
  212. _LOGGER.warning('No {} paths added, path_list is empty'.format(envar))
  213. return None
  214. else:
  215. _LOGGER.info('Adding paths to envar: {}'.format(envar))
  216. for p in path_list:
  217. p = Path(p)
  218. if (p.exists() and p.as_posix() not in known_pathlist):
  219. sys.path.insert(0, p.as_posix())
  220. new_pathlist.insert(0, p.as_posix())
  221. # Add site directory to make from imports work in namespace
  222. site.addsitedir(p.as_posix())
  223. os.environ[envar] = os.pathsep.join(new_pathlist)
  224. return os.environ[envar]
  225. # -------------------------------------------------------------------------
  226. # -------------------------------------------------------------------------
  227. def init_o3de_pyside2(dccsi_sys_path=_DCCSI_SYS_PATH):
  228. """!
  229. Initialize the DCCsi Qt/PySide dynamic env and settings
  230. sets access to lumberyards Qt dlls and PySide access,
  231. which are built into the /bin folder
  232. @param dccsi_path
  233. @param engine_bin_path
  234. @param dccsi_sys_path
  235. """
  236. time_start = timeit.default_timer() # start tracking
  237. # we don't do this often but we want to stash to global dict
  238. global _DCCSI_LOCAL_SETTINGS # non-dynaconf managed settings
  239. if not PATH_O3DE_BIN.exists():
  240. raise Exception(f'_PATH_O3DE_BIN does NOT exist: {PATH_O3DE_BIN}')
  241. else:
  242. os.add_dll_directory(f'{PATH_O3DE_BIN}')
  243. # # allows to retrieve from settings.QTFORPYTHON_PATH
  244. # from DccScriptingInterface.azpy.constants import STR_QTFORPYTHON_PATH # a path string constructor
  245. # QTFORPYTHON_PATH = Path(STR_QTFORPYTHON_PATH.format(O3DE_DEV)).as_posix()
  246. # os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH)
  247. # site.addsitedir(str(QTFORPYTHON_PATH)) # PYTHONPATH
  248. QT_PLUGIN_PATH = Path.joinpath(PATH_O3DE_BIN, 'EditorPlugins').resolve()
  249. os.environ["QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH.as_posix())
  250. _DCCSI_LOCAL_SETTINGS['QT_PLUGIN_PATH'] = QT_PLUGIN_PATH.as_posix()
  251. dccsi_sys_path.append(QT_PLUGIN_PATH)
  252. QT_QPA_PLATFORM_PLUGIN_PATH = Path.joinpath(QT_PLUGIN_PATH, 'platforms').resolve()
  253. # if the line below is removed external standalone apps can't load PySide2
  254. os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = QT_QPA_PLATFORM_PLUGIN_PATH.as_posix()
  255. # ^^ bypass trying to set only with DYNACONF environment
  256. _DCCSI_LOCAL_SETTINGS['QT_QPA_PLATFORM_PLUGIN_PATH'] = QT_QPA_PLATFORM_PLUGIN_PATH.as_posix()
  257. dccsi_sys_path.append(QT_QPA_PLATFORM_PLUGIN_PATH)
  258. # Access to QtForPython:
  259. # PySide2 is added to the O3DE Python install during CMake built against O3DE Qt dlls
  260. # the only way import becomes an actual issue is if running this config script
  261. # within a completely foreign python interpreter,
  262. # and/or a non-Qt app (or a DCC tool that is a Qt app but doesn't provide PySide2 bindings)
  263. # To Do: tackle this when it becomes and issue (like Blender?)
  264. from dynaconf import settings
  265. time_complete = timeit.default_timer() - time_start
  266. _LOGGER.info(f'~ config.init_o3de_pyside() DONE: {time_complete} sec')
  267. return settings
  268. # -------------------------------------------------------------------------
  269. # -------------------------------------------------------------------------
  270. def init_o3de_pyside2_tools():
  271. global _DCCSI_PYTHONPATH
  272. # set up the pyside2-tools (pyside2uic)
  273. _DCCSI_PYSIDE2_TOOLS = Path(PATH_DCCSI_PYTHON, 'pyside2-tools').resolve()
  274. if _DCCSI_PYSIDE2_TOOLS.exists():
  275. os.environ["DYNACONF_DCCSI_PYSIDE2_TOOLS"] = _DCCSI_PYSIDE2_TOOLS.as_posix()
  276. os.environ['PATH'] = _DCCSI_PYSIDE2_TOOLS.as_posix() + os.pathsep + os.environ['PATH']
  277. site.addsitedir(_DCCSI_PYSIDE2_TOOLS)
  278. _DCCSI_PYTHONPATH.append(_DCCSI_PYSIDE2_TOOLS.as_posix())
  279. _LOGGER.info('~ PySide2-Tools bootstrapped PATH for Windows.')
  280. try:
  281. import pyside2uic
  282. _LOGGER.info('~ SUCCESS: import pyside2uic')
  283. _LOGGER.debug(pyside2uic)
  284. status = True
  285. except ImportError as e:
  286. _LOGGER.error('~ FAILURE: import pyside2uic')
  287. status = False
  288. raise(e)
  289. else:
  290. _LOGGER.warning(f'~ No PySide2 Tools: {_DCCSI_PYSIDE2_TOOLS.as_posix()}')
  291. # -------------------------------------------------------------------------
  292. # -------------------------------------------------------------------------
  293. def validate_o3de_pyside2():
  294. # To Do: finish implementing
  295. # To Do: doc strings
  296. # add Qt binaries to the Windows path to handle finding DLL file dependencies
  297. if sys.platform.startswith('win'):
  298. _LOGGER.info('~ Qt/PySide2 bootstrapped PATH for Windows.')
  299. else:
  300. _LOGGER.warning('~ Not tested on Non-Windows platforms.')
  301. # To Do: figure out how to test and/or modify to work
  302. try:
  303. import PySide2
  304. _LOGGER.info('~ SUCCESS: import PySide2')
  305. _LOGGER.debug(PySide2)
  306. status = True
  307. except ImportError as e:
  308. _LOGGER.error('~ FAILURE: import PySide2')
  309. status = False
  310. raise(e)
  311. try:
  312. import shiboken2
  313. _LOGGER.info('~ SUCCESS: import shiboken2')
  314. _LOGGER.debug(shiboken2)
  315. status = True
  316. except ImportError as e:
  317. _LOGGER.error('~ FAILURE: import shiboken2')
  318. status = False
  319. raise(e)
  320. # -------------------------------------------------------------------------
  321. # -------------------------------------------------------------------------
  322. def test_pyside2(exit=True):
  323. """Convenience method to test Qt / PySide2 access"""
  324. # now test
  325. _LOGGER.info('~ Testing Qt / PySide2')
  326. try:
  327. from PySide2.QtWidgets import QApplication, QPushButton
  328. app = QApplication(sys.argv)
  329. hello = QPushButton("~ O3DE DCCsi PySide2 Test!")
  330. hello.resize(200, 60)
  331. hello.show()
  332. except Exception as e:
  333. _LOGGER.error('~ FAILURE: Qt / PySide2')
  334. status = False
  335. raise(e)
  336. _LOGGER.info('~ SUCCESS: .test_pyside2()')
  337. if exit:
  338. sys.exit(app.exec_())
  339. # -------------------------------------------------------------------------
  340. # -------------------------------------------------------------------------
  341. def init_o3de_core(engine_path=O3DE_DEV,
  342. engine_bin_path=PATH_O3DE_BIN,
  343. engine_3rdParty=PATH_O3DE_3RDPARTY,
  344. project_name=O3DE_PROJECT,
  345. project_path=PATH_O3DE_PROJECT,
  346. dccsi_path=PATH_DCCSIG,
  347. dccsi_sys_path=_DCCSI_SYS_PATH,
  348. dccsi_pythonpath=_DCCSI_PYTHONPATH):
  349. """Initialize the DCCsi Core dynamic env and settings."""
  350. time_start = timeit.default_timer() # start tracking for perf
  351. # we don't do this often but we want to stash to global dict
  352. global _DCCSI_LOCAL_SETTINGS # non-dynaconf managed settings
  353. # ENVARS with DYNACONF_ are managed and exported to settings
  354. # These envars are set like the following
  355. # os.environ["DYNACONF_DCCSI_EXAMPLE_KEY"] = Path('example','path).as_posix()
  356. # from dynaconf import settings
  357. # settings.setenv()
  358. # if settings.DCCSI_EXAMPLE_KEY: do this code
  359. # ENVARS without prefix are transient and local to session
  360. # They will not be written to settings the impact of this is,
  361. # they can not be retrieved like this (will assert):
  362. # os.environ["DCCSI_EXAMPLE_KEY"] = Path('example','path).as_posix()
  363. # foo = settings.DCCSI_GDEBUG
  364. # `envar_prefix` = export envars with `export DYNACONF_FOO=bar`.
  365. # `settings_files` = Load these files in this order.
  366. # here we are modifying or adding to the dynamic config settings on import
  367. # this code block commented out below is how to change the prefix
  368. # and the order in which to load settings (settings.py,.json already default)
  369. #settings = Dynaconf(envar_prefix='DYNACONF',
  370. ## the following will also load settings.local.json
  371. #settings_files=['settings.json',
  372. #'.secrets.json'])
  373. # for validation of this core config module
  374. # as we may layer and fold config's together.
  375. os.environ['DYNACONF_DCCSI_CONFIG_CORE'] = "True"
  376. # global settings
  377. os.environ["DYNACONF_DCCSI_GDEBUG"] = str(DCCSI_GDEBUG) # cast bools
  378. os.environ["DYNACONF_DCCSI_DEV_MODE"] = str(DCCSI_DEV_MODE)
  379. os.environ['DYNACONF_DCCSI_LOGLEVEL'] = str(DCCSI_LOGLEVEL)
  380. dccsi_path = Path(dccsi_path)
  381. os.environ["DYNACONF_PATH_DCCSIG"] = dccsi_path.as_posix()
  382. # we store these and will manage them last (as a combined set)
  383. dccsi_sys_path.append(dccsi_path)
  384. # we already defaulted to discovering these two early because of importance
  385. os.environ["DYNACONF_O3DE_DEV"] = O3DE_DEV.as_posix()
  386. # quick hack to shoehorn this new envar in, refactor when config is re-written
  387. os.environ["DYNACONF_PATH_O3DE_3RDPARTY"] = PATH_O3DE_3RDPARTY.as_posix()
  388. # ensure access to o3de python scripts
  389. os.environ["DYNACONF_PATH_O3DE_PYTHON_SCRIPTS"] = PATH_O3DE_PYTHON_SCRIPTS.as_posix()
  390. dccsi_pythonpath.append(PATH_O3DE_PYTHON_SCRIPTS)
  391. # the project is transient and optional (falls back to dccsi)
  392. # it's more important to set this for tools that want to work with projects
  393. # which is why you can pass it in explicitly
  394. if project_path:
  395. _project_path = Path(project_path)
  396. try:
  397. _project_path.exists()
  398. _PATH_O3DE_PROJECT = _project_path
  399. except FileExistsError as e:
  400. _LOGGER.error('~ The project path specified does not appear to exist!')
  401. _LOGGER.warning('~ project_path: {}'.format(project_path))
  402. _LOGGER.warning('~ fallback to engine root: {}'.format())
  403. _PATH_O3DE_PROJECT = O3DE_DEV
  404. os.environ['PATH_O3DE_PROJECT'] = str(_PATH_O3DE_PROJECT.as_posix())
  405. _DCCSI_LOCAL_SETTINGS['PATH_O3DE_PROJECT'] = str(_PATH_O3DE_PROJECT.as_posix())
  406. # we can pull the O3DE_PROJECT (name) from the project path
  407. if not project_name:
  408. project_name = Path(_PATH_O3DE_PROJECT).name
  409. os.environ["O3DE_PROJECT"] = str(project_name)
  410. _DCCSI_LOCAL_SETTINGS['O3DE_PROJECT'] = str(project_name)
  411. # -- O3DE build -- set up \bin\path (for Qt dll access)
  412. if engine_bin_path:
  413. _PATH_O3DE_BIN = Path(engine_bin_path)
  414. if _PATH_O3DE_BIN.exists():
  415. os.environ["DYNACONF_PATH_O3DE_BIN"] = PATH_O3DE_BIN.as_posix()
  416. else:
  417. _LOGGER.warning(f'The engine binary folder not set/found, '
  418. f'method input var: engine_bin_path, '
  419. f'the ENVAR is PATH_O3DE_BIN')
  420. _PATH_O3DE_BIN = Path('< not set >')
  421. # hard check (but more forgiving now)
  422. if _PATH_O3DE_BIN.exists():
  423. dccsi_sys_path.append(_PATH_O3DE_BIN)
  424. else:
  425. _LOGGER.warning(f'PATH_O3DE_BIN does NOT exist: {str(_PATH_O3DE_BIN)}')
  426. if DCCSI_STRICT:
  427. raise NotADirectoryError
  428. # --
  429. from DccScriptingInterface.azpy.constants import TAG_DIR_DCCSI_TOOLS
  430. _PATH_DCCSI_TOOLS = Path(dccsi_path, TAG_DIR_DCCSI_TOOLS)
  431. os.environ["DYNACONF_PATH_DCCSI_TOOLS"] = str(_PATH_DCCSI_TOOLS.as_posix())
  432. from DccScriptingInterface.azpy.constants import TAG_DCCSI_NICKNAME
  433. from DccScriptingInterface.azpy.constants import PATH_DCCSI_LOG_PATH
  434. _DCCSI_LOG_PATH = Path(PATH_DCCSI_LOG_PATH.format(PATH_O3DE_PROJECT=project_path,
  435. TAG_DCCSI_NICKNAME=TAG_DCCSI_NICKNAME))
  436. os.environ["DYNACONF_DCCSI_LOG_PATH"] = str(_DCCSI_LOG_PATH.as_posix())
  437. from DccScriptingInterface.azpy.constants import SLUG_DIR_REGISTRY, TAG_DCCSI_CONFIG
  438. _PATH_DCCSI_CONFIG = Path(project_path, SLUG_DIR_REGISTRY, TAG_DCCSI_CONFIG)
  439. os.environ["DYNACONF_PATH_DCCSI_CONFIG"] = str(_PATH_DCCSI_CONFIG.as_posix())
  440. from DccScriptingInterface.azpy.constants import TAG_DIR_DCCSI_TOOLS
  441. _DCCSIG_TOOLS_PATH = Path.joinpath(dccsi_path, TAG_DIR_DCCSI_TOOLS)
  442. os.environ["DYNACONF_DCCSIG_TOOLS_PATH"] = str(_DCCSIG_TOOLS_PATH.as_posix())
  443. # To Do: Need to add some json validation for settings.json,
  444. # and settings.local.json, as importing settings will read json data
  445. # if the json is malformed it will cause an assert like:
  446. # "json.decoder.JSONDecodeError: Expecting ',' delimiter: line 45 column 5 (char 2463)"
  447. # but might be hard to quickly find out which settings file is bad
  448. from dynaconf import settings
  449. time_complete = timeit.default_timer() - time_start
  450. _LOGGER.info('~ config.init_o3de_core() DONE: {} sec'.format(time_complete))
  451. # we return dccsi_sys_path because we may want to pass it on
  452. # settings my be passes on and added to or re-initialized with changes
  453. return dccsi_sys_path, settings
  454. # -------------------------------------------------------------------------
  455. # -------------------------------------------------------------------------
  456. def init_o3de_python(engine_path=O3DE_DEV,
  457. engine_bin_path=PATH_O3DE_BIN,
  458. dccsi_path=PATH_DCCSIG,
  459. dccsi_sys_path=_DCCSI_SYS_PATH,
  460. dccsi_pythonpath=_DCCSI_PYTHONPATH):
  461. """Initialize the O3DE dynamic Python env and settings.
  462. Note: """
  463. global _DCCSI_LOCAL_SETTINGS
  464. global _DCCSI_PYTHONPATH_EXCLUDE
  465. time_start = timeit.default_timer()
  466. # pathify
  467. _O3DE_DEV = Path(engine_path)
  468. _PATH_O3DE_BIN = Path(engine_bin_path)
  469. _PATH_DCCSIG = Path(dccsi_path)
  470. # python config
  471. _PATH_DCCSI_PYTHON = Path(_PATH_DCCSIG,'3rdParty','Python')
  472. os.environ["DYNACONF_PATH_DCCSI_PYTHON"] = str(_PATH_DCCSI_PYTHON.as_posix())
  473. _PATH_DCCSI_PYTHON_LIB = DccScriptingInterface.azpy.config_utils.bootstrap_dccsi_py_libs(_PATH_DCCSIG)
  474. os.environ["PATH_DCCSI_PYTHON_LIB"] = str(_PATH_DCCSI_PYTHON_LIB.as_posix())
  475. _DCCSI_LOCAL_SETTINGS['PATH_DCCSI_PYTHON_LIB']= str(_PATH_DCCSI_PYTHON_LIB.as_posix())
  476. #dccsi_pythonpath.append(_PATH_DCCSI_PYTHON_LIB)
  477. # ^ adding here will cause this to end up in settings.local.json
  478. # and we don't want that, since this LIB location is transient/procedural
  479. # this is transient and will always track the exe this script is executing on
  480. _O3DE_PY_EXE = Path(sys.executable)
  481. _DCCSI_PY_IDE = Path(_O3DE_PY_EXE)
  482. os.environ["DCCSI_PY_IDE"] = str(_DCCSI_PY_IDE.as_posix())
  483. _DCCSI_LOCAL_SETTINGS['DCCSI_PY_IDE']= str(_DCCSI_PY_IDE.as_posix())
  484. _O3DE_PYTHONHOME = Path(_O3DE_PY_EXE.parents[0])
  485. os.environ["DYNACONF_O3DE_PYTHONHOME"] = str(_O3DE_PYTHONHOME.as_posix())
  486. dccsi_sys_path.append(_O3DE_PYTHONHOME)
  487. _LOGGER.info('~ O3DE_PYTHONHOME - is now the folder containing O3DE python executable')
  488. # this will always be the O3DE pythin interpreter
  489. _PATH_O3DE_PYTHON_INSTALL = Path(_O3DE_DEV, 'python')
  490. os.environ["DYNACONF_PATH_O3DE_PYTHON_INSTALL"] = str(_PATH_O3DE_PYTHON_INSTALL.as_posix())
  491. dccsi_sys_path.append(_PATH_O3DE_PYTHON_INSTALL)
  492. if sys.platform.startswith('win'):
  493. _DCCSI_PY_BASE = Path(_PATH_O3DE_PYTHON_INSTALL, 'python.cmd')
  494. elif sys.platform == "linux":
  495. _DCCSI_PY_BASE = Path(_PATH_O3DE_PYTHON_INSTALL, 'python.sh')
  496. elif sys.platform == "darwin":
  497. _DCCSI_PY_BASE = Path(_PATH_O3DE_PYTHON_INSTALL, 'python.sh')
  498. else:
  499. _DCCSI_PY_BASE = None
  500. # this is the system specific shell around O3DE Python install
  501. if _DCCSI_PY_BASE:
  502. os.environ["DYNACONF_DCCSI_PY_BASE"] = str(_DCCSI_PY_BASE.as_posix())
  503. from dynaconf import settings
  504. time_complete = timeit.default_timer() - time_start
  505. _LOGGER.info(f'~ config.init_o3de_python() ... DONE: {time_complete} sec')
  506. return dccsi_sys_path, dccsi_pythonpath, settings
  507. # -------------------------------------------------------------------------
  508. # -------------------------------------------------------------------------
  509. # settings.setenv() # doing this will add the additional DYNACONF_ envars
  510. def get_config_settings(enable_o3de_python=False,
  511. enable_o3de_pyside2=False,
  512. enable_o3de_pyside2_tools=False,
  513. set_env=True,
  514. dccsi_sys_path=_DCCSI_SYS_PATH,
  515. dccsi_pythonpath=_DCCSI_PYTHONPATH):
  516. """Convenience method to initialize and retrieve settings directly from module."""
  517. # always init the core
  518. dccsi_sys_path, settings = init_o3de_core(dccsi_sys_path=_DCCSI_SYS_PATH,
  519. dccsi_pythonpath=_DCCSI_PYTHONPATH)
  520. # These should ONLY be set for O3DE and non-DCC environments
  521. # They will most likely cause other Qt/PySide DCC apps to fail
  522. # or hopefully they can be overridden for DCC environments
  523. if enable_o3de_python:
  524. dccsi_sys_path,
  525. dccsi_pythonpath,
  526. settings = init_o3de_python(dccsi_sys_path=_DCCSI_SYS_PATH,
  527. dccsi_pythonpath=_DCCSI_PYTHONPATH)
  528. # Many DCC apps provide their own Qt dlls and Pyside2
  529. # _LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH))
  530. # _LOGGER.info('QT_PLUGIN_PATH: {}'.format(settings.QT_PLUGIN_PATH))
  531. # assume our standalone python tools wants this access?
  532. # it's safe to do this for dev and from ide
  533. if enable_o3de_pyside2:
  534. settings = init_o3de_pyside2(dccsi_sys_path=_DCCSI_SYS_PATH)
  535. # final stage, if we have managed path lists set them
  536. new_PATH = add_path_list_to_envar(dccsi_sys_path)
  537. new_PYTHONPATH = add_path_list_to_addsitedir(dccsi_pythonpath)
  538. # now standalone we can validate the config. env, settings.
  539. from dynaconf import settings
  540. if set_env:
  541. settings.setenv()
  542. return settings
  543. # -------------------------------------------------------------------------
  544. # -------------------------------------------------------------------------
  545. # get file name slug for exporting/caching local setting
  546. from DccScriptingInterface.azpy.constants import DCCSI_SETTINGS_LOCAL_FILENAME
  547. def export_settings(settings,
  548. dccsi_sys_path=_DCCSI_SYS_PATH,
  549. dccsi_pythonpath=_DCCSI_PYTHONPATH,
  550. settings_filepath=DCCSI_SETTINGS_LOCAL_FILENAME,
  551. use_dynabox=False,
  552. env=None,
  553. merge=False,
  554. log_settings=False):
  555. # To Do: when running script from IDE settings.local.json are
  556. # correctly written to the < dccsi > root folder (script is cwd?)
  557. # when I run this from cli using O3DE Python:
  558. # "C:\Depot\o3de-dev\python\python.cmd"
  559. # the setting end up in that folder:
  560. # C:\Depot\o3de-dev\python\settings.local.json"
  561. # we need to ensure that the settings are written to the < dccsi >
  562. # To Do: multiple configs in different states can actually be written,
  563. # so provide a output path and name so a collection can be made in
  564. # < dccsi >\configs\settings.core.json
  565. # < dccsi >\configs\settings.python.json
  566. # < dccsi >\configs\settings.pyside.json, etc.
  567. # dynaconf has way to load specific settings files,
  568. # which could be useful for tools?
  569. # It would be an improvement to add malformed json validation
  570. # this can easily cause a bad error that is deep and hard to debug
  571. # this just ensures that the settings.local.json file has a marker
  572. # we can use this marker to know the local settings exist
  573. os.environ['DYNACONF_DCCSI_LOCAL_SETTINGS'] = "True"
  574. # make sure the dynaconf synthetic env is updated before writing settings
  575. # this will make it into our settings file as "DCCSI_SYS_PATH":<value>
  576. add_path_list_to_envar(dccsi_sys_path, 'DYNACONF_DCCSI_SYS_PATH')
  577. add_path_list_to_envar(dccsi_pythonpath, 'DYNACONF_DCCSI_PYTHONPATH')
  578. # we did not use list_to_addsitedir() because we are just ensuring
  579. # the managed envar path list is updated so paths are written to settings
  580. if not use_dynabox:
  581. # update settings
  582. from dynaconf import settings
  583. settings.setenv()
  584. _settings_dict = settings.as_dict()
  585. # default temp filename
  586. _settings_file = Path(settings_filepath)
  587. # we want to possibly modify or stash our settings into a o3de .setreg
  588. from box import Box
  589. _settings_box = Box(_settings_dict)
  590. _LOGGER.info('Pretty print, _settings_box: {}'.format(_settings_file))
  591. if log_settings:
  592. _LOGGER.info(str(_settings_box.to_json(sort_keys=True,
  593. indent=4)))
  594. # writes settings box
  595. _settings_box.to_json(filename=_settings_file.as_posix(),
  596. sort_keys=True,
  597. indent=4)
  598. return _settings_box
  599. # experimental, have not utilized this yet but it would be the native
  600. # way for dynaconf to write settings, i went with a Box dictionary
  601. # as it is ordered and I can ensure the settings are pretty with indenting
  602. elif use_dynabox:
  603. # writing settings using dynabox
  604. from dynaconf import loaders
  605. from dynaconf.utils.boxing import DynaBox
  606. _settings_box = DynaBox(settings).to_dict()
  607. if not env:
  608. # writes to a file, the format is inferred by extension
  609. # can be .yaml, .toml, .ini, .json, .py
  610. loaders.write(_settings_file.as_posix(), _settings_box)
  611. return _settings_box
  612. elif env:
  613. # the env can also be written, though this isn't yet utilized by config.py
  614. # To do: we should probably set up each DCC tool as it's own env group!
  615. #loaders.write(_settings_file, DynaBox(data).to_dict(), merge=False, env='development')
  616. loaders.write(_settings_file.as_posix(), _settings_box, merge, env)
  617. return _settings_box
  618. else:
  619. _LOGGER.warning('something went wrong')
  620. return
  621. else:
  622. _LOGGER.warning('something went wrong')
  623. return
  624. # --- END -----------------------------------------------------------------
  625. # always init defaults
  626. settings = get_config_settings(enable_o3de_python=False,
  627. enable_o3de_pyside2=False,
  628. set_env=True)
  629. ###########################################################################
  630. # Main Code Block, runs this script as main (testing)
  631. # -------------------------------------------------------------------------
  632. if __name__ == '__main__':
  633. """Run this file as a standalone cli script for testing/debugging"""
  634. from DccScriptingInterface.azpy.env_bool import env_bool
  635. # temp internal debug flag, toggle values for manual testing
  636. DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
  637. DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
  638. DCCSI_LOGLEVEL = env_bool(ENVAR_DCCSI_LOGLEVEL, _logging.INFO)
  639. DCCSI_GDEBUGGER = env_bool(ENVAR_DCCSI_GDEBUGGER, 'WING')
  640. from DccScriptingInterface.azpy.shared.utils.arg_bool import arg_bool
  641. # Suggestion for next iteration, args set to anything but None will
  642. # evaluate as True
  643. from DccScriptingInterface.azpy.constants import STR_CROSSBAR
  644. _MODULENAME = 'DCCsi.config.cli'
  645. # default loglevel to info unless set
  646. DCCSI_LOGLEVEL = int(env_bool(ENVAR_DCCSI_LOGLEVEL, _logging.INFO))
  647. if DCCSI_GDEBUG:
  648. # override loglevel if running debug
  649. DCCSI_LOGLEVEL = _logging.DEBUG
  650. # configure basic logger
  651. # note: not using a common logger to reduce cyclical imports
  652. _logging.basicConfig(level=DCCSI_LOGLEVEL,
  653. format=FRMT_LOG_LONG,
  654. datefmt='%m-%d %H:%M')
  655. _LOGGER = _logging.getLogger(_MODULENAME)
  656. _LOGGER.debug('Initializing: {}.'.format({_MODULENAME}))
  657. _LOGGER.debug('site.addsitedir({})'.format(PATH_DCCSIG))
  658. _LOGGER.debug('_DCCSI_GDEBUG: {}'.format(DCCSI_GDEBUG))
  659. _LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(DCCSI_DEV_MODE))
  660. _LOGGER.debug('_DCCSI_LOGLEVEL: {}'.format(DCCSI_LOGLEVEL))
  661. _LOGGER.info(STR_CROSSBAR)
  662. _LOGGER.info(f'~ {_MODULENAME} ... Running module as __main__')
  663. _LOGGER.info(STR_CROSSBAR)
  664. # go ahead and run the rest of the configuration
  665. # parse the command line args
  666. import argparse
  667. parser = argparse.ArgumentParser(
  668. description='O3DE DCCsi Dynamic Config (dynaconf)',
  669. epilog="Attempts to determine O3DE project if -pp not set")
  670. parser.add_argument('-gd', '--global-debug',
  671. type=bool,
  672. required=False,
  673. default=False,
  674. help='Enables global debug flag.')
  675. parser.add_argument('-dm', '--developer-mode',
  676. type=bool,
  677. required=False,
  678. default=False,
  679. help='Enables dev mode for early auto attaching debugger.')
  680. parser.add_argument('-sd', '--set-debugger',
  681. type=str,
  682. required=False,
  683. default='WING',
  684. help='(NOT IMPLEMENTED) Default debugger: WING, others: PYCHARM and VSCODE.')
  685. parser.add_argument('-ep', '--engine-path',
  686. type=pathlib.Path,
  687. required=False,
  688. default=Path('{ to do: implement }'),
  689. help='The path to the o3de engine.')
  690. parser.add_argument('-eb', '--engine-bin-path',
  691. type=pathlib.Path,
  692. required=False,
  693. default=Path('{ to do: implement }'),
  694. help='The path to the o3de engine binaries (build/bin/profile).')
  695. parser.add_argument('-bf', '--build-folder',
  696. type=str,
  697. required=False,
  698. default='build',
  699. help='The name (tag) of the o3de build folder, example build or windows.')
  700. parser.add_argument('-pp', '--project-path',
  701. type=pathlib.Path,
  702. required=False,
  703. default=Path('{ to do: implement }'),
  704. help='The path to the project.')
  705. parser.add_argument('-pn', '--project-name',
  706. type=str,
  707. required=False,
  708. default='{ to do: implement }',
  709. help='The name of the project.')
  710. parser.add_argument('-py', '--enable-python',
  711. type=bool,
  712. required=False,
  713. default=False,
  714. help='Enables O3DE python access.')
  715. parser.add_argument('-qt', '--enable-qt',
  716. type=bool,
  717. required=False,
  718. default=False,
  719. help='Enables O3DE Qt & PySide2 access.')
  720. parser.add_argument('-pc', '--project-config',
  721. type=bool,
  722. required=False,
  723. default=False,
  724. help='(not implemented) Enables reading the < >project >/registry/dccsi_configuration.setreg.')
  725. parser.add_argument('-cls', '--cache-local-settings',
  726. type=bool,
  727. required=False,
  728. default=True,
  729. help='Ensures setting.local.json is written to cache settings')
  730. parser.add_argument('-es', '--export-settings',
  731. type=pathlib.Path,
  732. required=False,
  733. help='Writes managed settings to specified path.')
  734. parser.add_argument('-ls', '--load-settings',
  735. type=pathlib.Path,
  736. required=False,
  737. default=False,
  738. help='(Not Implemented) Would load and read settings from a specified path.')
  739. parser.add_argument('-ec', '--export-configuration',
  740. type=bool,
  741. required=False,
  742. default=False,
  743. help='(not implemented) writes settings as a O3DE < project >/registry/dccsi_configuration.setreg.')
  744. parser.add_argument('-tp', '--test-pyside2',
  745. type=bool,
  746. required=False,
  747. default=False,
  748. help='Runs Qt/PySide2 tests and reports.')
  749. parser.add_argument('-lps', '--log-print-settings',
  750. type=bool,
  751. required=False,
  752. default=True,
  753. help='Well dump settings results into the log.')
  754. parser.add_argument('-ex', '--exit',
  755. type=bool,
  756. required=False,
  757. help='Exits python. Do not exit if you want to be in interactive interpreter after config')
  758. args = parser.parse_args()
  759. from DccScriptingInterface.azpy.shared.utils.arg_bool import arg_bool
  760. # easy overrides
  761. if arg_bool(args.global_debug, desc="args.global_debug"):
  762. from DccScriptingInterface.azpy.constants import ENVAR_DCCSI_GDEBUG
  763. DCCSI_GDEBUG = True
  764. _LOGGER.setLevel(_logging.DEBUG)
  765. _LOGGER.info(f'Global debug is set, {ENVAR_DCCSI_GDEBUG}={DCCSI_GDEBUG}')
  766. if arg_bool(args.developer_mode, desc="args.developer_mode"):
  767. DCCSI_DEV_MODE = True
  768. # attempts to start debugger
  769. DccScriptingInterface.azpy.test.entry_test.connect_wing()
  770. if args.set_debugger:
  771. _LOGGER.info('Setting and switching debugger type not implemented (default=WING)')
  772. # To Do: implement debugger plugin pattern
  773. # need to do a little plumbing
  774. if not args.engine_path:
  775. args.engine_path=O3DE_DEV
  776. if not args.build_folder:
  777. from DccScriptingInterface.azpy.constants import TAG_DIR_O3DE_BUILD_FOLDER
  778. args.build_folder = TAG_DIR_O3DE_BUILD_FOLDER
  779. if not args.engine_bin_path:
  780. args.engine_bin_path=PATH_O3DE_BIN
  781. if not args.project_path:
  782. args.project_path=PATH_O3DE_PROJECT
  783. if DCCSI_GDEBUG:
  784. args.enable_python = True
  785. args.enable_qt = True
  786. # now standalone we can validate the config. env, settings.
  787. # for now hack to disable passing in other args
  788. # going to refactor anyway
  789. settings = get_config_settings(enable_o3de_python=args.enable_python,
  790. enable_o3de_pyside2=args.enable_qt)
  791. ## CORE
  792. _LOGGER.info(STR_CROSSBAR)
  793. # not using fstrings in this module because it might run in py2.7 (maya)
  794. _LOGGER.info('DCCSI_GDEBUG: {}'.format(settings.DCCSI_GDEBUG))
  795. _LOGGER.info('DCCSI_DEV_MODE: {}'.format(settings.DCCSI_DEV_MODE))
  796. _LOGGER.info('DCCSI_LOGLEVEL: {}'.format(settings.DCCSI_LOGLEVEL))
  797. _LOGGER.info('O3DE_DEV: {}'.format(settings.O3DE_DEV))
  798. _LOGGER.info('PATH_O3DE_BIN: {}'.format(settings.PATH_O3DE_BIN))
  799. _LOGGER.info('PATH_DCCSIG: {}'.format(settings.PATH_DCCSIG))
  800. _LOGGER.info('DCCSI_LOG_PATH: {}'.format(settings.DCCSI_LOG_PATH))
  801. _LOGGER.info('PATH_DCCSI_CONFIG: {}'.format(settings.PATH_DCCSI_CONFIG))
  802. # settings no longer managed with DYNACONF_ in config.py
  803. #_LOGGER.info('O3DE_PROJECT: {}'.format(settings.O3DE_PROJECT))
  804. #_LOGGER.info('PATH_O3DE_PROJECT: {}'.format(settings.PATH_O3DE_PROJECT))
  805. try:
  806. settings.O3DE_DCCSI_ENV_TEST
  807. _LOGGER.info('O3DE_DCCSI_ENV_TEST: {}'.format(settings.O3DE_DCCSI_ENV_TEST))
  808. except:
  809. pass # don't exist
  810. _LOGGER.info(STR_CROSSBAR)
  811. _LOGGER.info('')
  812. if args.enable_python:
  813. _LOGGER.info(STR_CROSSBAR)
  814. _LOGGER.info('PATH_DCCSI_PYTHON'.format(settings.PATH_DCCSI_PYTHON))
  815. _LOGGER.info('PATH_DCCSI_PYTHON_LIB: {}'.format(_DCCSI_LOCAL_SETTINGS['PATH_DCCSI_PYTHON_LIB']))
  816. _LOGGER.info('O3DE_PYTHONHOME'.format(settings.O3DE_PYTHONHOME))
  817. _LOGGER.info('PATH_O3DE_PYTHON_INSTALL'.format(settings.PATH_O3DE_PYTHON_INSTALL))
  818. _LOGGER.info('DCCSI_PY_BASE: {}'.format(settings.DCCSI_PY_BASE))
  819. _LOGGER.info(STR_CROSSBAR)
  820. _LOGGER.info('')
  821. # settings no longer managed with DYNACONF_ in config.py
  822. #_LOGGER.info('DCCSI_PY_IDE'.format(settings.DCCSI_PY_IDE))
  823. else:
  824. _LOGGER.info('Tip: add arg --enable-python (-py) to extend the environment with O3DE python access')
  825. if args.enable_qt:
  826. _LOGGER.info(STR_CROSSBAR)
  827. # _LOGGER.info('QTFORPYTHON_PATH: {}'.format(settings.QTFORPYTHON_PATH))
  828. _LOGGER.info('QT_PLUGIN_PATH: {}'.format(_DCCSI_LOCAL_SETTINGS['QT_PLUGIN_PATH']))
  829. _LOGGER.info('QT_QPA_PLATFORM_PLUGIN_PATH: {}'.format(_DCCSI_LOCAL_SETTINGS['QT_QPA_PLATFORM_PLUGIN_PATH']))
  830. try:
  831. settings.DCCSI_PYSIDE2_TOOLS
  832. _LOGGER.info('DCCSI_PYSIDE2_TOOLS: {}'.format(settings.DCCSI_PYSIDE2_TOOLS))
  833. except:
  834. pass # don't exist
  835. _LOGGER.info(STR_CROSSBAR)
  836. _LOGGER.info('')
  837. else:
  838. _LOGGER.info('Tip: add arg --enable-qt (-qt) to extend the environment with O3DE Qt/PySide2 support')
  839. _LOGGER.info('Tip: add arg --test-pyside2 (-tp) to test the O3DE Qt/PySide2 support')
  840. settings.setenv() # doing this will add/set the additional DYNACONF_ envars
  841. # # if paths are in settings.local.json we need them activated
  842. # add_path_list_to_envar(settings.DCCSI_SYS_PATH)
  843. # add_path_list_to_addsitedir(settings.DCCSI_PYTHONPATH)
  844. if DCCSI_GDEBUG or args.cache_local_settings:
  845. if args.log_print_settings:
  846. log_settings = True
  847. else:
  848. log_settings = False
  849. if args.export_settings:
  850. export_settings_path = Path(args.export_settings).resolve()
  851. export_settings(settings=settings,
  852. settings_filepath=export_settings_path.as_posix(),
  853. log_settings=log_settings)
  854. else:
  855. export_settings(settings=settings,
  856. log_settings=log_settings)
  857. # this should be set if there are local settings!?
  858. _LOGGER.debug('DCCSI_LOCAL_SETTINGS: {}'.format(settings.DCCSI_LOCAL_SETTINGS))
  859. # end tracking here, the pyside test exits before hitting the end of script
  860. _MODULE_END = timeit.default_timer() - _MODULE_START
  861. _LOGGER.info(f'CLI {_MODULENAME} took: {_MODULE_END} sec')
  862. if DCCSI_GDEBUG or args.test_pyside2:
  863. test_pyside2() # test PySide2 access with a pop-up button
  864. try:
  865. import pyside2uic
  866. except ImportError as e:
  867. _LOGGER.warning("Could not import 'pyside2uic'")
  868. _LOGGER.warning("Refer to: '{}/3rdParty/Python/README.txt'".format(settings.PATH_DCCSIG))
  869. _LOGGER.error(e)
  870. # custom prompt
  871. sys.ps1 = f"[{_MODULENAME}]>>"
  872. if args.exit:
  873. # return
  874. sys.exit()
  875. # --- END -----------------------------------------------------------------