synthetic_env.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. # coding:utf-8
  2. #!/usr/bin/python
  3. #
  4. # Copyright (c) Contributors to the Open 3D Engine Project.
  5. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  6. #
  7. # SPDX-License-Identifier: Apache-2.0 OR MIT
  8. #
  9. #
  10. # -- This line is 75 characters -------------------------------------------
  11. from __future__ import unicode_literals
  12. """
  13. Module Description:
  14. DccScriptingInterface\\azpy\\shared\\synthetic_env.py
  15. Stands up a synthetic version of the environment in:
  16. DccScriptingInterface\\Launchers\\windows\\Env.bat
  17. DccScriptingInterface a.k.a. DCCsi
  18. This module (softly) assumes that the cwd is the DCCsi
  19. AND that you can get access to imports from azpy
  20. The configures a synthetic version of the default DCCsi env,
  21. which includes additional code access paths and some other settings.
  22. It does not assume that the external environment is configured.
  23. It provides best guess defaults for the environment (when they are not set.)
  24. It will not overwrite the envars that are configured.
  25. To do this, it uses the pattern:
  26. _SOME_ENVAR = os.getenv('ENVAR_TAG', <default>)
  27. The environment is conveniently packed into a dictionary,
  28. this allows the dictionary to be imported into another module,
  29. the import will resolve and standup the synthetic environment.
  30. Configures several useful environment config settings and paths,
  31. [key] : [value]
  32. # this is the required base environment
  33. O3DE_PROJECT : name of project (project directory)
  34. O3DE_DEV : path to Lumberyard \dev root
  35. PATH_O3DE_PROJECT : path to project dir
  36. PATH_DCCSIG : path to the DCCsi Gem root
  37. PATH_DCCSI_TOOLS : path to associated (non-api code) DCC SDK
  38. # nice to haves in base env to define core support
  39. DCCSI_GDEBUG : sets global debug prints
  40. DCCSI_DEV_MODE : extends support like early debugger attachment
  41. DCCSI_GDEBUGGER : default debugger (WING)
  42. DCCsi has been (experimentally) tested with : PY27, PY37
  43. - py27 for DCC tools like Maya
  44. - py37 for Lumberyard
  45. - it probably will work with 3.6 (substance)
  46. :: Default version py37 has a launcher
  47. (activates the env, starts py interpreter)
  48. set DCCSI_PY_BASE=%PATH_O3DE_PYTHON_INSTALL%\python.exe
  49. :: shared location for 64bit python 3.10 BASE location
  50. set DCCSI_PY_DCCSI=%DCCSI_LAUNCHERS_PATH%\Launch_pyBASE.bat
  51. :: Override DCCSI_PY_DCCSI to set a default version for the DCCsi
  52. :: ide and debugger plugs
  53. :: for instance with WingIDE this defines using the default py interpreter
  54. ${DCCSI_PY_DEFAULT}
  55. ::Other python runtimes can be defined, for py27 we use this
  56. :: shared location for 64bit DCCSI_PY_MAYA python 2.7 DEV location
  57. set DCCSI_PY_MAYA=%MAYA_LOCATION%\bin\mayapy.exe
  58. :: wingIDE can use more then one defined/managed interpreters
  59. :: allowing you to _DCCSI_GDEBUG code in multiple runtimes in one session
  60. ${DCCSI_PY_MAYA}
  61. # related to the WING as the default DCCSI_GDEBUGGER
  62. DCCSI_WING_VERSION_MAJOR : the major version of Wing IDE (default IDE)
  63. DCCSI_WING_VERSION_MINOR : minor version #
  64. WINGHOME : path for wing (for debug) integration
  65. NOTES:
  66. The default IDE and debugger is WING primarily because of author preference.
  67. Other options are available to configure out-of-the-box (PyCharm)
  68. """
  69. # -------------------------------------------------------------------------
  70. # built in's
  71. import os
  72. import sys
  73. import site
  74. import re
  75. import inspect
  76. import json
  77. import importlib.util
  78. import logging as _logging
  79. from collections import OrderedDict
  80. # Lumberyard and Atom standalone should be launched in py3.7+
  81. # because of use of pathlib (unless externally provided in py2.7)
  82. # -------------------------------------------------------------------------
  83. # -------------------------------------------------------------------------
  84. os.environ['PYTHONINSPECT'] = 'True'
  85. _MODULE_PATH = os.path.realpath(__file__) # To Do: what if frozen?
  86. _PATH_DCCSIG = os.path.normpath(os.path.join(_MODULE_PATH, '../..'))
  87. _PATH_DCCSIG = os.getenv('PATH_DCCSIG', _PATH_DCCSIG)
  88. site.addsitedir(_PATH_DCCSIG)
  89. # -------------------------------------------------------------------------
  90. # -------------------------------------------------------------------------
  91. # O3DE extensions
  92. from pathlib import Path
  93. # set up global space, logging etc.
  94. import azpy.env_bool as env_bool
  95. from azpy.constants import ENVAR_DCCSI_GDEBUG
  96. from azpy.constants import ENVAR_DCCSI_DEV_MODE
  97. from azpy.constants import FRMT_LOG_LONG
  98. _DCCSI_GDEBUG = env_bool.env_bool(ENVAR_DCCSI_GDEBUG, False)
  99. _DCCSI_DEV_MODE = env_bool.env_bool(ENVAR_DCCSI_DEV_MODE, False)
  100. _PACKAGENAME = 'DCCsi.azpy.synthetic_env'
  101. # set up module logging
  102. for handler in _logging.root.handlers[:]:
  103. _logging.root.removeHandler(handler)
  104. _LOGGER = _logging.getLogger(_PACKAGENAME)
  105. _logging.basicConfig(format=FRMT_LOG_LONG)
  106. _LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME}))
  107. _LOGGER.debug('_PATH_DCCSIG: {}'.format(_PATH_DCCSIG))
  108. _LOGGER.debug('_DCCSI_GDEBUG: {}'.format(_DCCSI_GDEBUG))
  109. _LOGGER.debug('_DCCSI_DEV_MODE: {}'.format(_DCCSI_DEV_MODE))
  110. if _DCCSI_DEV_MODE:
  111. from azpy.test.entry_test import connect_wing
  112. foo = connect_wing()
  113. # we can go ahead and just make sure the the DCCsi env is set
  114. # config is SO generic this ensures we are importing a specific one
  115. _spec_dccsi_config = importlib.util.spec_from_file_location("dccsi.config",
  116. Path(_PATH_DCCSIG,
  117. "config.py"))
  118. _dccsi_config = importlib.util.module_from_spec(_spec_dccsi_config)
  119. _spec_dccsi_config.loader.exec_module(_dccsi_config)
  120. settings = _dccsi_config.get_config_settings()
  121. # -------------------------------------------------------------------------
  122. # Lumberyard extensions
  123. from azpy.constants import *
  124. from azpy.shared.common.core_utils import walk_up_dir
  125. from azpy.shared.common.core_utils import get_stub_check_path
  126. _PATH_DCCSI_PYTHON_LIB = os.getenv(ENVAR_PATH_DCCSI_PYTHON_LIB,
  127. PATH_DCCSI_PYTHON_LIB)
  128. _LOGGER.debug('Dccsi Lib Path: {0}'.format(_PATH_DCCSI_PYTHON_LIB))
  129. if os.path.exists(_PATH_DCCSI_PYTHON_LIB):
  130. site.addsitedir(_PATH_DCCSI_PYTHON_LIB) # add access
  131. # -------------------------------------------------------------------------
  132. # post-bootstrap global space
  133. _DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
  134. _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
  135. # -------------------------------------------------------------------------
  136. FRMT_LOG_LONG = ("[%(name)s][%(levelname)s] >> "
  137. "%(message)s (%(asctime)s; %(filename)s:%(lineno)d)")
  138. _PACKAGENAME = 'azpy.synthetic_env'
  139. _logging.basicConfig(level=_logging.INFO,
  140. format=FRMT_LOG_LONG,
  141. datefmt='%m-%d %H:%M')
  142. _LOGGER = _logging.getLogger(_PACKAGENAME)
  143. _log_level = int(10)
  144. console_handler = _logging.StreamHandler(sys.stdout)
  145. console_handler.setLevel(_log_level)
  146. formatter = _logging.Formatter(FRMT_LOG_LONG)
  147. console_handler.setFormatter(formatter)
  148. _LOGGER.addHandler(console_handler)
  149. _LOGGER.setLevel(_log_level)
  150. _LOGGER.debug('Initializing: {0}.'.format({_PACKAGENAME}))
  151. # -------------------------------------------------------------------------
  152. # This module is semi-standalone (no azpy YET) to avoid circular imports
  153. # To Do: figure out how to bets NOPT dup this methods (there are also moudles)
  154. # we want to run this potentially with no preformed env
  155. # and thus we may not have full acess to the DCCsi and azpy package
  156. # -------------------------------------------------------------------------
  157. def get_current_project(dev_folder):
  158. boostrap_filepath = Path(dev_folder, "bootstrap.cfg")
  159. bootstrap = open(boostrap_filepath, "r")
  160. game_project_regex = re.compile("^sys_game_folder\s*=\s*(.*)")
  161. for line in bootstrap:
  162. game_folder_match = game_project_regex.match(line)
  163. if game_folder_match:
  164. return game_folder_match.group(1)
  165. return None
  166. # -------------------------------------------------------------------------
  167. # -------------------------------------------------------------------------
  168. # TO DO: Move to a util package or module
  169. def return_stub(stub='dccsi_stub'):
  170. '''Take a file name (stub) and returns the directory of the file (stub)'''
  171. _dir_to_last_file = None
  172. if _dir_to_last_file is None:
  173. path = Path(__file__).absolute()
  174. while 1:
  175. path, tail = Path(path).split()
  176. if Path(path, stub).is_file():
  177. break
  178. if (len(tail) == 0):
  179. path = ""
  180. if _DCCSI_GDEBUG:
  181. _LOGGER.debug('~Not able to find the path to that file '
  182. '(stub) in a walk-up from currnet path.')
  183. break
  184. _dir_to_last_file = path
  185. return _dir_to_last_file
  186. # -------------------------------------------------------------------------
  187. # -------------------------------------------------------------------------
  188. def get_stub_check_path(in_path, check_stub='engineroot.txt'):
  189. '''
  190. Returns the branch root directory of the dev\'engineroot.txt'
  191. (... or you can pass it another known stub)
  192. so we can safely build relative filepaths within that branch.
  193. If the stub is not found, it returns None
  194. '''
  195. path = Path(in_path).absolute()
  196. while 1:
  197. test_path = Path(path, check_stub)
  198. if test_path.is_file():
  199. return Path(test_path)
  200. else:
  201. path, tail = (path.parent, path.name)
  202. if (len(tail) == 0):
  203. return None
  204. # -------------------------------------------------------------------------
  205. # -------------------------------------------------------------------------
  206. # TO DO: Move to a util package or module
  207. def resolve_envar_path(envar='O3DE_DEV',
  208. start_path=__file__,
  209. check_stub='engineroot.txt',
  210. dir_name='dev',
  211. return_posix_str=False):
  212. """This is meant to resolve a '\dev' path for LY
  213. First we validate the start_path (or we can't walk)
  214. Then we walk up the startPath looking for stub
  215. engineroot.txt is the default file marker for lumberyard
  216. That is a pretty safe indicator that we found the right '\dev'
  217. Second it checks if the env var 'O3DE_DEV' is set, use that instead!
  218. """
  219. fallback = None
  220. # amke sure the start_path exists, otherwise we have nothing to walk up
  221. try:
  222. start_path = Path(start_path)
  223. start_path.exists()
  224. if start_path.is_dir():
  225. fallback = start_path
  226. elif start_path.is_file():
  227. fallback = start_path.parent
  228. except Exception as e:
  229. _LOGGER.info('Does NOT exist, no valid path: {0}'.format(start_path))
  230. raise EnvironmentError
  231. # generate a fallback based on finding know stub
  232. # known stub is probably more reliable then dir name
  233. fallback_stub = get_stub_check_path(fallback, check_stub)
  234. # there is alos a chance this comes back None
  235. # cast from is_file to directory
  236. if fallback_stub and fallback_stub.is_file():
  237. fallback = fallback_stub.parent
  238. # check if it's set in env and fetch, or set to fallback
  239. envar_path = Path(os.getenv(envar, fallback))
  240. try:
  241. envar_path.exists()
  242. envar_path.is_dir()
  243. envar_path.name.lower == dir_name
  244. except Exception as e:
  245. _LOGGER.info('Not a lumbertard \dev path: {0}'.format(e))
  246. raise EnvironmentError
  247. # last resort see if you can walk up to the dir name
  248. if not envar_path:
  249. envar_path = walk_up_dir(start_path, dir_name)
  250. if envar_path and Path(envar_path).exists():
  251. # box module can't serialize WindowsPath objects
  252. # so Path object being stashed in this dict need .resolve() or .as_posix()
  253. if return_posix_str:
  254. return Path(envar_path).as_posix()
  255. else:
  256. return Path(envar_path)
  257. else:
  258. return None
  259. # -------------------------------------------------------------------------
  260. # -------------------------------------------------------------------------
  261. # setting up storage dict, only using a Box (super dict) instead
  262. _SYNTH_ENV_DICT = OrderedDict()
  263. # -------------------------------------------------------------------------
  264. # --- Build Defaults ------------------------------------------------------
  265. def stash_env(_SYNTH_ENV_DICT = OrderedDict()):
  266. """This block attempts to fetch or derive best guess fallback"""
  267. # basic default fall backs (in case no environment exists)
  268. # this first set is just the expected base_env
  269. _LOGGER.info(STR_CROSSBAR)
  270. _LOGGER.info('~ stash_env(), setting module fallbacks')
  271. # box module can't serialize WindowsPath objects
  272. # so Path object being stashed in this dict need .resolve() or .as_posix()
  273. _THIS_MODULE_PATH = Path(__file__).as_posix()
  274. # company name from env or default
  275. # TAG_DEFAULT_COMPANY = str('Amazon.Lumberyard')
  276. _G_COMPANY_NAME = os.getenv(ENVAR_COMPANY,
  277. TAG_DEFAULT_COMPANY)
  278. # we want to pack these all into a easy access dict
  279. _SYNTH_ENV_DICT[ENVAR_COMPANY] = _G_COMPANY_NAME
  280. # <ly>\dev Lumberyard ROOT PATH
  281. # someone decided to use this as a root stub (for similar reasons in C++?)
  282. # STUB_O3DE_DEV = str('engineroot.txt')
  283. # I don't own \dev so I didn't want to check in anything new there
  284. _O3DE_DEV = resolve_envar_path(ENVAR_O3DE_DEV, # envar
  285. _THIS_MODULE_PATH, # search path
  286. STUB_O3DE_DEV, # stub
  287. TAG_DIR_O3DE_DEV) # dir
  288. _SYNTH_ENV_DICT[ENVAR_O3DE_DEV] = _O3DE_DEV.as_posix()
  289. # project name is a string, it should be project dir name
  290. # for siloed testing and a purely synthetc env (nothing previously set)
  291. # the default should probably be the 'DccScriptingInterface' (DCCsi)
  292. # we also have a 'MockProject' sanndbox/silo, but we want to reseve that
  293. # for testing overrides of the default synthetic env
  294. # we can do two things here,
  295. # first we can try to fetch from the env os.getenv('O3DE_PROJECT')
  296. # If comes back None, allows you to specify a default fallback
  297. # changed to just make the fallback what is set in boostrap
  298. # so now it's less of a fallnack and more correct if not
  299. # explicitly set
  300. _O3DE_PROJECT = os.getenv(ENVAR_O3DE_PROJECT)
  301. _SYNTH_ENV_DICT[ENVAR_O3DE_PROJECT] = _O3DE_PROJECT
  302. _O3DE_BUILD_DIR_NAME = os.getenv(ENVAR_O3DE_BUILD_DIR_NAME,
  303. TAG_DIR_O3DE_BUILD_FOLDER)
  304. _SYNTH_ENV_DICT[ENVAR_O3DE_BUILD_DIR_NAME] = _O3DE_BUILD_DIR_NAME
  305. # pattern for the above is (and will be repeated)
  306. # _SOME_ENVAR = resolve_envar_path('ENVAR',
  307. # 'path\\to\\start\\search',
  308. # 'knownStubFileName',
  309. # 'knownDirName')
  310. # checks env for ENVAR first,
  311. # then walks up search path looking for stub (fallback 0),
  312. # then walks up niavely looking for a dirName (fallback 1)
  313. # other paths that don't have a stub to search for as a fallback
  314. # can be stashed in this manner
  315. # variable containers for all of the default/base env config dccsi entry
  316. # setting most basic defaults, not using an module import dependancies
  317. # we can't know where the user installed or set up any of these,
  318. # so we guess based on how I set up the original dev environment
  319. # -- envar --
  320. _PATH_O3DE_BUILD = Path(os.getenv(ENVAR_PATH_O3DE_BUILD,
  321. PATH_O3DE_BUILD))
  322. _SYNTH_ENV_DICT[ENVAR_PATH_O3DE_BUILD] = _PATH_O3DE_BUILD.as_posix()
  323. # -- envar --
  324. _PATH_O3DE_BIN = Path(os.getenv(ENVAR_PATH_O3DE_BIN,
  325. PATH_O3DE_BIN))
  326. # some of these need hard checks
  327. if not _PATH_O3DE_BIN.exists():
  328. raise Exception('PATH_O3DE_BIN does NOT exist: {0}'.format(_PATH_O3DE_BIN))
  329. else:
  330. _SYNTH_ENV_DICT[ENVAR_PATH_O3DE_BIN] = _PATH_O3DE_BIN.as_posix()
  331. # adding to sys.path apparently doesn't work for .dll locations like Qt
  332. os.environ['PATH'] = _PATH_O3DE_BIN.as_posix() + os.pathsep + os.environ['PATH']
  333. # -- envar --
  334. # if that stub marker doesn't exist assume DCCsi path (fallback 1)
  335. _PATH_O3DE_PROJECT = Path(os.getenv(ENVAR_PATH_O3DE_PROJECT,
  336. Path(_O3DE_DEV, _O3DE_PROJECT)))
  337. _SYNTH_ENV_DICT[ENVAR_PATH_O3DE_PROJECT] = _PATH_O3DE_PROJECT.as_posix()
  338. # -- envar --
  339. _PATH_DCCSIG = resolve_envar_path(ENVAR_PATH_DCCSIG, # envar
  340. _THIS_MODULE_PATH, # search path
  341. STUB_O3DE_ROOT_DCCSI, # stub name
  342. TAG_DEFAULT_PROJECT) # dir
  343. _SYNTH_ENV_DICT[ENVAR_PATH_DCCSIG] = _PATH_DCCSIG.as_posix()
  344. # -- envar --
  345. _AZPY_PATH = Path(os.getenv(ENVAR_DCCSI_AZPY_PATH,
  346. Path(_PATH_DCCSIG, TAG_DIR_DCCSI_AZPY)))
  347. _SYNTH_ENV_DICT[ENVAR_DCCSI_AZPY_PATH] = _AZPY_PATH.as_posix()
  348. # -- envar --
  349. _PATH_DCCSI_TOOLS = Path(os.getenv(ENVAR_PATH_DCCSI_TOOLS,
  350. Path(_PATH_DCCSIG, TAG_DIR_DCCSI_TOOLS)))
  351. _SYNTH_ENV_DICT[ENVAR_PATH_DCCSI_TOOLS] = _PATH_DCCSI_TOOLS.as_posix()
  352. # -- envar --
  353. # external dccsi site-packages
  354. _PATH_DCCSI_PYTHON_LIB = Path(os.getenv(ENVAR_PATH_DCCSI_PYTHON_LIB,
  355. PATH_DCCSI_PYTHON_LIB))
  356. _SYNTH_ENV_DICT[ENVAR_PATH_DCCSI_PYTHON_LIB] = _PATH_DCCSI_PYTHON_LIB.as_posix()
  357. # -- envar --
  358. # extend to py36 (conda env) and interpreter (wrapped as a .bat file)
  359. _DEFAULT_PY_PATH = Path(_PATH_DCCSIG, TAG_DEFAULT_PY)
  360. _DEFAULT_PY_PATH = Path(os.getenv(ENVAR_DCCSI_PY_DEFAULT,
  361. _DEFAULT_PY_PATH))
  362. _SYNTH_ENV_DICT[ENVAR_DCCSI_PY_DEFAULT] = _DEFAULT_PY_PATH.as_posix()
  363. # -- envar --
  364. # wing ide vars
  365. _WINGHOME_DEFAULT_PATH = Path(os.getenv(ENVAR_WINGHOME,
  366. PATH_DEFAULT_WINGHOME))
  367. _SYNTH_ENV_DICT[ENVAR_WINGHOME] = _WINGHOME_DEFAULT_PATH.as_posix()
  368. # -- done --
  369. return _SYNTH_ENV_DICT
  370. # -------------------------------------------------------------------------
  371. # -------------------------------------------------------------------------
  372. def init_ly_pyside(env_dict=_SYNTH_ENV_DICT):
  373. """sets access to lumberyards Qt dlls and PySide"""
  374. # -- envar --
  375. _QTFORPYTHON_PATH = Path(os.getenv(ENVAR_QTFORPYTHON_PATH,
  376. PATH_QTFORPYTHON_PATH))
  377. # some of these need hard checks
  378. if not _QTFORPYTHON_PATH.exists():
  379. raise Exception('QTFORPYTHON_PATH does NOT exist: {0}'.format(_QTFORPYTHON_PATH))
  380. else:
  381. _SYNTH_ENV_DICT[ENVAR_QTFORPYTHON_PATH] = _QTFORPYTHON_PATH.as_posix()
  382. # ^ some of these should be put on sys.path and/or PYTHONPATH or PATH
  383. #os.environ['PATH'] = _QTFORPYTHON_PATH.as_posix() + os.pathsep + os.environ['PATH']
  384. site.addsitedir(_QTFORPYTHON_PATH.as_posix()) # PYTHONPATH
  385. # -- envar --
  386. _QT_PLUGIN_PATH = Path(os.getenv(ENVAR_QT_PLUGIN_PATH,
  387. PATH_QT_PLUGIN_PATH))
  388. # some of these need hard checks
  389. if not _QT_PLUGIN_PATH.exists():
  390. raise Exception('QT_PLUGIN_PATH does NOT exist: {0}'.format(_QT_PLUGIN_PATH))
  391. else:
  392. _SYNTH_ENV_DICT[ENVAR_QT_PLUGIN_PATH] = _QT_PLUGIN_PATH.as_posix()
  393. # https://stackoverflow.com/questions/214852/python-module-dlls
  394. os.environ['PATH'] = _QT_PLUGIN_PATH.as_posix() + os.pathsep + os.environ['PATH']
  395. QTFORPYTHON_PATH = Path.joinpath(O3DE_DEV,
  396. 'Gems',
  397. 'QtForPython',
  398. '3rdParty',
  399. 'pyside2',
  400. 'windows',
  401. 'release').resolve()
  402. os.environ["DYNACONF_QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH)
  403. os.environ["QTFORPYTHON_PATH"] = str(QTFORPYTHON_PATH)
  404. sys.path.insert(1, str(QTFORPYTHON_PATH))
  405. site.addsitedir(str(QTFORPYTHON_PATH))
  406. PATH_O3DE_BIN = Path.joinpath(O3DE_DEV,
  407. 'windows',
  408. 'bin',
  409. 'profile').resolve()
  410. os.environ["DYNACONF_PATH_O3DE_BIN"] = str(PATH_O3DE_BIN)
  411. os.environ["PATH_O3DE_BIN"] = str(PATH_O3DE_BIN)
  412. site.addsitedir(str(PATH_O3DE_BIN))
  413. sys.path.insert(1, str(PATH_O3DE_BIN))
  414. QT_PLUGIN_PATH = Path.joinpath(PATH_O3DE_BIN,
  415. 'EditorPlugins').resolve()
  416. os.environ["DYNACONF_QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH)
  417. os.environ["QT_PLUGIN_PATH"] = str(QT_PLUGIN_PATH)
  418. site.addsitedir(str(QT_PLUGIN_PATH))
  419. sys.path.insert(1, str(QT_PLUGIN_PATH))
  420. QT_QPA_PLATFORM_PLUGIN_PATH = Path.joinpath(QT_PLUGIN_PATH,
  421. 'platforms').resolve()
  422. os.environ["DYNACONF_QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH)
  423. os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(QT_QPA_PLATFORM_PLUGIN_PATH)
  424. site.addsitedir(str(QT_QPA_PLATFORM_PLUGIN_PATH))
  425. sys.path.insert(1, str(QT_QPA_PLATFORM_PLUGIN_PATH))
  426. # add Qt binaries to the Windows path to handle findings DLL file dependencies
  427. if sys.platform.startswith('win'):
  428. path = os.environ['PATH']
  429. newPath = ''
  430. newPath += str(PATH_O3DE_BIN) + os.pathsep
  431. newPath += str(Path.joinpath(QTFORPYTHON_PATH,
  432. 'shiboken2').resolve()) + os.pathsep
  433. newPath += str(Path.joinpath(QTFORPYTHON_PATH,
  434. 'PySide2').resolve()) + os.pathsep
  435. newPath += path
  436. os.environ['PATH']=newPath
  437. _LOGGER.debug('PySide2 bootstrapped PATH for Windows.')
  438. try:
  439. import PySide2
  440. _LOGGER.debug('DCCsi, config.py: SUCCESS: import PySide2')
  441. _LOGGER.debug(PySide2)
  442. status = True
  443. except ImportError as e:
  444. _LOGGER.debug('DCCsi, config.py: FAILURE: import PySide2')
  445. status = False
  446. raise(e)
  447. try:
  448. import shiboken2
  449. _LOGGER.debug('DCCsi, config.py: SUCCESS: import shiboken2')
  450. _LOGGER.debug(shiboken2)
  451. status = True
  452. except ImportError as e:
  453. _LOGGER.debug('DCCsi, config.py: FAILURE: import shiboken2')
  454. status = False
  455. raise(e)
  456. return status
  457. # -------------------------------------------------------------------------
  458. # -------------------------------------------------------------------------
  459. # py 2 and 3 compatible iter
  460. def get_items(dict_object):
  461. for key in dict_object:
  462. yield key, dict_object[key]
  463. def set_env(dict_object):
  464. for key, value in get_items(dict_object):
  465. try:
  466. os.environ[key] = value
  467. except EnvironmentError as e:
  468. _LOGGER.error('ERROR: {e}')
  469. return dict_object
  470. # will trigger on any import
  471. # suggested use: from synthetic_env import _SYNTH_ENV_DICT
  472. #_SYNTH_ENV_DICT = set_env(_SYNTH_ENV_DICT)
  473. # -------------------------------------------------------------------------
  474. def test_Qt():
  475. try:
  476. import PySide2
  477. _LOGGER.info('PySide2: {0}'.format(Path(PySide2.__file__).as_posix()))
  478. # builtins.ImportError: DLL load failed: The specified procedure could not be found.
  479. from PySide2 import QtCore
  480. from PySide2 import QtWidgets
  481. except IOError as e:
  482. _LOGGER.error('ERROR: {0}'.format(e))
  483. raise e
  484. try:
  485. qapp = QtWidgets.QApplication([])
  486. except:
  487. # already exists
  488. qapp = QtWidgets.QApplication.instance()
  489. try:
  490. buttonFlags = QtWidgets.QMessageBox.information(QtWidgets.QApplication.activeWindow(), 'title', 'ok')
  491. qapp.instance().quit
  492. qapp.exit()
  493. except Exception as e:
  494. _LOGGER.error('ERROR: {0}'.format(e))
  495. raise e
  496. # -------------------------------------------------------------------------
  497. # -------------------------------------------------------------------------
  498. def main(argv, env_dict_object, debug=False, devmode=False):
  499. import getopt
  500. try:
  501. opts, args = getopt.getopt(argv, "hvt:", ["verbose=", "test="])
  502. except getopt.GetoptError as e:
  503. # not logging, print to cmd line console
  504. _LOGGER.error('synthetic_env.py -v <print_dict> -t <run_test>')
  505. sys.exit(2)
  506. for opt, arg in opts:
  507. if opt == '-h':
  508. _LOGGER.info('synthetic_env.py -v <print_dict> -t <run test>')
  509. sys.exit()
  510. elif opt in ("-t", "--test"):
  511. debug = True
  512. devmode = True
  513. test()
  514. elif opt in ("-v", "--verbose"):
  515. try:
  516. from box import Box
  517. except ImportError as e:
  518. _LOGGER.error('ERROR: {0}'.format(e))
  519. raise e
  520. try:
  521. env_dict_object = Box(env_dict_object)
  522. _LOGGER.info(str(env_dict_object.to_json(sort_keys=False,
  523. indent=4)))
  524. except Exception as e:
  525. _LOGGER.error('ERROR: {0}'.format(e))
  526. raise e
  527. # -------------------------------------------------------------------------
  528. ###########################################################################
  529. # Main Code Block, runs this script as main (testing)
  530. # -------------------------------------------------------------------------
  531. if __name__ == '__main__':
  532. # run simple tests?
  533. _DCCSI_GDEBUG = True
  534. _DCCSI_DEV_MODE = True
  535. if _DCCSI_DEV_MODE:
  536. try:
  537. import azpy.test.entry_test
  538. _LOGGER.info('SUCCESS: import azpy.test.entry_test')
  539. azpy.test.entry_test.main(verbose=True, connect_debugger=True)
  540. except ImportError as e:
  541. _LOGGER.error('ERROR: {0}'.format(e))
  542. raise e
  543. # init, stash and then activate
  544. _SYNTH_ENV_DICT = OrderedDict()
  545. _SYNTH_ENV_DICT = stash_env(_SYNTH_ENV_DICT)
  546. _SYNTH_ENV_DICT = set_env(_SYNTH_ENV_DICT)
  547. main(sys.argv[1:], _SYNTH_ENV_DICT, _DCCSI_GDEBUG, _DCCSI_DEV_MODE)
  548. if _DCCSI_GDEBUG:
  549. tempBoxJsonFilePath = Path(_SYNTH_ENV_DICT['PATH_DCCSIG'], '.temp')
  550. tempBoxJsonFilePath = Path(tempBoxJsonFilePath, 'boxDumpTest.json')
  551. _LOGGER.info(f'tempBoxJsonFilePath: {tempBoxJsonFilePath}')
  552. try:
  553. tempBoxJsonFilePath.mkdir(parents=True, exist_ok=True)
  554. tempBoxJsonFilePath.touch(mode=0o777, exist_ok=True)
  555. except Exception as e:
  556. _LOGGER.info(e)
  557. _LOGGER.info('~ writting with Box.to_json')
  558. try:
  559. from box import Box
  560. _SYNTH_ENV_DICT = Box(_SYNTH_ENV_DICT)
  561. _LOGGER.info(type(_SYNTH_ENV_DICT))
  562. except Exception as e:
  563. _LOGGER.info(e)
  564. # -- BOX STORE ------
  565. _LOGGER.info(str(_SYNTH_ENV_DICT.to_json(sort_keys=False,
  566. indent=4)))
  567. try:
  568. #_SYNTH_ENV_DICT.to_json(filename=None, encoding='utf-8', errors='strict')
  569. #from os import fspath
  570. #tempBoxJsonFilePath = fspath(tempBoxJsonFilePath)
  571. _SYNTH_ENV_DICT.to_json(filename=tempBoxJsonFilePath.as_posix(),
  572. sort_keys=False,
  573. indent=4)
  574. _LOGGER.info('~ Box.to_json SUCCESS')
  575. except Exception as e:
  576. _LOGGER.info(e)
  577. # if this raises an exception related to WindowsPath
  578. # that a path obj was stashed, use .as_posix() when stashing
  579. raise e
  580. _LOGGER.info('listing envar keys: {0}'.format(_SYNTH_ENV_DICT.keys()))
  581. # -- BOX READ ------
  582. _LOGGER.info('~ read Box.from_json')
  583. parseJsonBox = Box.from_json(filename=tempBoxJsonFilePath,
  584. encoding="utf-8",
  585. errors="strict",
  586. object_pairs_hook=OrderedDict)
  587. _LOGGER.info('~ pretty print parsed Box.from_json')
  588. _LOGGER.info(json.dumps(parseJsonBox, indent=4, sort_keys=False, ensure_ascii=False))
  589. # also run the Qt/PySide2 test
  590. test_Qt()
  591. del _LOGGER