From 69336bc0ae92c193aa926400eddc7117c8eb3402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Deverly?= <39291844+gplsteph@users.noreply.github.com> Date: Mon, 14 Feb 2022 16:57:26 +0000 Subject: [PATCH 1/3] 6645 ue5 fixes (#10) Workarounds for: - Mac - Linux - U5 dev where Shotgun components were renamed to Shotgrid, without backward compatibility in mind. --- hooks/tk-multi-publish2/basic/collector.py | 1 - plugins/basic/bootstrap.py | 13 ++ .../tk_unreal_basic/plugin_bootstrap.py | 6 +- python/tk_unreal/unreal_sg_engine.py | 118 +++++++++++++----- 4 files changed, 106 insertions(+), 32 deletions(-) diff --git a/hooks/tk-multi-publish2/basic/collector.py b/hooks/tk-multi-publish2/basic/collector.py index b20a4db..2e1c5b0 100644 --- a/hooks/tk-multi-publish2/basic/collector.py +++ b/hooks/tk-multi-publish2/basic/collector.py @@ -136,7 +136,6 @@ def collect_selected_assets(self, parent_item): :param parent_item: Parent Item instance """ unreal_sg = sgtk.platform.current_engine().unreal_sg_engine - # Iterate through the selected assets and get their info and add them as items to be published for asset in unreal_sg.selected_assets: asset_name = str(asset.asset_name) diff --git a/plugins/basic/bootstrap.py b/plugins/basic/bootstrap.py index 22bd2c3..270cedd 100644 --- a/plugins/basic/bootstrap.py +++ b/plugins/basic/bootstrap.py @@ -4,10 +4,23 @@ import sys import os +import unreal # Setup sys.path so that we can import our bootstrapper. plugin_root_dir = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(0, os.path.join(plugin_root_dir, "python")) +# Temp workaround for UE5 dev: for some reasons the core/python path is missing +# in sys.path but is present in the PYTHONPATH. +# Make sure sys.path is up to date with what is in the PYTHON PATH +python_path = os.environ.get("PYTHONPATH") or "" +for path in python_path.split(os.pathsep): + # We can have empty entries for consecutives separators + if path and path not in sys.path: + unreal.log_warning( + "Adding missing %s Python path to sys paths" % path, + ) + sys.path.append(path) + from tk_unreal_basic import plugin_bootstrap plugin_bootstrap.bootstrap_plugin(plugin_root_dir) diff --git a/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py b/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py index a4a2b8c..ff6d272 100644 --- a/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py +++ b/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py @@ -68,8 +68,10 @@ def _on_engine_initialized(): sgtk_logger.debug("tk-unreal finished initialization.") import unreal - - unreal.ShotgunEngine.get_instance().on_engine_initialized() + if hasattr(unreal, "ShotgridEngine"): + unreal.ShotgridEngine.get_instance().on_engine_initialized() + else: + unreal.ShotgunEngine.get_instance().on_engine_initialized() def _initialize_manager(plugin_root_path): diff --git a/python/tk_unreal/unreal_sg_engine.py b/python/tk_unreal/unreal_sg_engine.py index bf8958f..247a87d 100644 --- a/python/tk_unreal/unreal_sg_engine.py +++ b/python/tk_unreal/unreal_sg_engine.py @@ -8,11 +8,18 @@ import sys import os -unreal.log("Loading Shotgun Engine for Unreal from {}".format(__file__)) +unreal.log("Loading SG Engine for Unreal from {}".format(__file__)) + +# Shotgun integration components were renamed to Shotgrid from UE5 +if hasattr(unreal, "ShotgridEngine"): + UESGEngine = unreal.ShotgridEngine + +else: + UESGEngine = unreal.ShotgunEngine @unreal.uclass() -class ShotgunEngineWrapper(unreal.ShotgunEngine): +class ShotgunEngineWrapper(UESGEngine): def _post_init(self): """ @@ -20,24 +27,65 @@ def _post_init(self): """ config.wrapper_instance = self - @unreal.ufunction(override=True) - def get_shotgun_menu_items(self): - """ - Returns the list of available menu items to populate the Shotgun menu in Unreal - """ - menu_items = [] - - engine = sgtk.platform.current_engine() - menu_items = self.create_menu(engine) - - unreal.log("get_shotgun_menu_items returned: {0}".format(menu_items.__str__())) - - return menu_items + # Shotgun integration components were renamed to Shotgrid from UE5 + if hasattr(UESGEngine, "get_shotgrid_menu_items"): + @unreal.ufunction(override=True) + def get_shotgrid_menu_items(self): + """ + Returns the list of available menu items to populate the SG menu in Unreal. + """ + menu_items = [] + + engine = sgtk.platform.current_engine() + menu_items = self.create_menu(engine) + + unreal.log("get_shotgrid_menu_items returned: {0}".format(menu_items.__str__())) + + return menu_items + + def get_shotgun_menu_items(self): + """ + Provide backward compatibility. + """ + return self.get_shotgrid_menu_items() + else: + @unreal.ufunction(override=True) + def get_shotgun_menu_items(self): + """ + Returns the list of available menu items to populate the SG menu in Unreal. + """ + menu_items = [] + + engine = sgtk.platform.current_engine() + menu_items = self.create_menu(engine) + + unreal.log("get_shotgun_menu_items returned: {0}".format(menu_items.__str__())) + + return menu_items + + def get_shotgrid_menu_items(self): + """ + Provide forward compatibility. + """ + return self.get_shotgun_menu_items() + + if hasattr(UESGEngine, "get_shotgrid_work_dir"): + def get_shotgun_work_dir(self, *args, **kwargs): + """ + Provide backward compatibility. + """ + return self.get_shotgrid_work_dir(*args, **kwargs) + else: + def get_shotgrid_work_dir(self, *args, **kwargs): + """ + Provide forward compatibility. + """ + return self.get_shotgun_work_dir(*args, **kwargs) @unreal.ufunction(override=True) def execute_command(self, command_name): """ - Callback to execute the menu item selected in the Shotgun menu in Unreal + Callback to execute the menu item selected in the SG menu in Unreal. """ engine = sgtk.platform.current_engine() @@ -58,9 +106,9 @@ def _get_command_override(self, engine, command_name, default_callback): :param command_name: The command name to override :param default_callback: The callback to use when there's no override """ - # Override the Shotgun Panel command to use the Shotgun Entity context + # Override the SG Panel command to use the SG Entity context # and also reuse the dialog if one already exists - if command_name == "Shotgun Panel...": + if command_name in ["Shotgun Panel...", "ShotGrid Panel..."]: def show_shotgunpanel_with_context(): app = engine.apps["tk-multi-shotgunpanel"] entity_type, entity_id = self._get_context(engine) @@ -75,7 +123,7 @@ def show_shotgunpanel_with_context(): def _get_context_url(self, engine): """ - Get the Shotgun entity URL from the metadata of the selected asset, if present + Get the SG entity URL from the metadata of the selected asset, if present. """ # By default, use the URL of the project url = engine.context.shotgun_url @@ -110,12 +158,12 @@ def _get_context_url(self, engine): def _get_context(self, engine): """ - Get the Shotgun context (entity type and id) that is associated with the selected menu command + Get the SG context (entity type and id) that is associated with the selected menu command. """ entity_type = None entity_id = None - # The context is derived from the Shotgun entity URL + # The context is derived from the SG entity URL url = self._get_context_url(engine) if url: # Extract entity type and id from URL, which should follow this pattern: @@ -173,7 +221,7 @@ def shutdown(self): engine = sgtk.platform.current_engine() if engine is not None: - unreal.log("Shutting down ShotgunEngineWrapper") + unreal.log("Shutting down %s" % self.__class__.__name__) # destroy_engine of tk-unreal will take care of closing all dialogs that are still opened engine.destroy() @@ -184,12 +232,12 @@ def shutdown(self): Menu generation functionality for Unreal (based on the 3ds max Menu Generation implementation) Actual menu creation is done in Unreal - The following functions simply generate a list of available commands that will populate the Shotgun menu in Unreal + The following functions simply generate a list of available commands that will populate the SG menu in Unreal """ def create_menu(self, engine): """ - Populate the Shotgun Menu with the available commands + Populate the SG Menu with the available commands. """ menu_items = [] @@ -257,9 +305,13 @@ def _add_menu_item_from_command(self, menu_items, command): def _add_menu_item(self, menu_items, type, name="", title="", description=""): """ - Adds a new Unreal ShotgunMenuItem to the menu items + Adds a new Unreal SG MenuItem to the menu items. """ - menu_item = unreal.ShotgunMenuItem() + # Shotgun integration components were renamed to Shotgrid from UE5 + if hasattr(unreal, "ShotgridMenuItem"): + menu_item = unreal.ShotgridMenuItem() + else: + menu_item = unreal.ShotgunMenuItem() menu_item.title = title menu_item.name = name menu_item.type = type @@ -275,15 +327,23 @@ def _start_contextual_menu(self, engine, menu_items): self._add_menu_item(menu_items, "context_begin", ctx_name, ctx_name) - engine.register_command("Jump to Shotgun", self._jump_to_sg, {"type": "context_menu", "short_name": "jump_to_sg"}) + engine.register_command( + "Jump to ShotGrid", + self._jump_to_sg, + {"type": "context_menu", "short_name": "jump_to_sg"} + ) # Add the menu item only when there are some file system locations. if ctx.filesystem_locations: - engine.register_command("Jump to File System", self._jump_to_fs, {"type": "context_menu", "short_name": "jump_to_fs"}) + engine.register_command( + "Jump to File System", + self._jump_to_fs, + {"type": "context_menu", "short_name": "jump_to_fs"} + ) def _jump_to_sg(self): """ - Callback to Jump to Shotgun from context + Callback to Jump to SG from context. """ from sgtk.platform.qt5 import QtGui, QtCore url = self._get_context_url(sgtk.platform.current_engine()) From e480311e658a311885dc048b56e78959a59539eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Deverly?= <39291844+gplsteph@users.noreply.github.com> Date: Wed, 16 Feb 2022 17:34:20 +0000 Subject: [PATCH 2/3] 7932 win registeries (#11) Added back checking Unreal install in registry for Windows. Revisited the [original code](https://github.com/ue4plugins/tk-unreal/blob/6863ca47b12def732883e7418934ca6590576c9b/startup.py#L111) to make it easier to understand and maintain. --- startup.py | 179 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 152 insertions(+), 27 deletions(-) diff --git a/startup.py b/startup.py index 01e4ee4..ad95250 100644 --- a/startup.py +++ b/startup.py @@ -6,6 +6,7 @@ import sys import pprint import json +import re import sgtk from sgtk.platform import SoftwareLauncher, SoftwareVersion, LaunchInformation @@ -148,6 +149,7 @@ def _find_software(self): Find installed UE executable. :returns: List of :class:`SoftwareVersion` instances. + :raises RuntimeError: For unsupported OSes. """ self.logger.info("Finding Unreal Engine executables") sw_versions = [] @@ -161,35 +163,43 @@ def _find_software(self): elif sgtk.util.is_linux(): executable_templates = self.EXECUTABLE_TEMPLATES.get("linux") - if executable_templates: - for executable_template in executable_templates: - self.logger.debug("Processing template %s.", executable_template) - executable_matches = self._glob_and_match( - executable_template, self.COMPONENT_REGEX_LOOKUP - ) - # Extract all products from that executable. - for (executable_path, key_dict) in executable_matches: - # extract the matched keys form the key_dict (default to None if - # not included) - executable_version = key_dict.get("version") - details = self._get_unreal_version_details(executable_path) - if details and all(x in details for x in ["MajorVersion", "MinorVersion", "PatchVersion"]): - executable_version = "%s.%s.%s" % ( - details["MajorVersion"], - details["MinorVersion"], - details["PatchVersion"], - ) - sw_versions.append( - SoftwareVersion( - executable_version, - "Unreal Engine", - executable_path, - os.path.join(self.disk_location, "icon_256.png"), - ) - ) - else: + if not executable_templates: raise RuntimeError("Unsupported platform %s" % sys.platform) + for executable_template in executable_templates: + self.logger.debug("Processing template %s.", executable_template) + executable_matches = self._glob_and_match( + executable_template, self.COMPONENT_REGEX_LOOKUP + ) + # Extract all products from that executable. + for (executable_path, key_dict) in executable_matches: + # extract the matched keys form the key_dict (default to None if + # not included) + executable_version = key_dict.get("version") + details = self._get_unreal_version_details(executable_path) + if details and all(x in details for x in ["MajorVersion", "MinorVersion", "PatchVersion"]): + executable_version = "%s.%s.%s" % ( + details["MajorVersion"], + details["MinorVersion"], + details["PatchVersion"], + ) + sw_versions.append( + SoftwareVersion( + executable_version, + "Unreal Engine", + executable_path, + os.path.join(self.disk_location, "icon_256.png"), + ) + ) + if sgtk.util.is_windows(): + # On Windows we also explore registry, but make sure to not add + # things twice. + found_paths = [sw.path for sw in sw_versions] + for sw in self._find_software_from_registry(): + if sw.path not in found_paths: + sw_versions.append(sw) + found_paths.append(sw.path) + return sw_versions def _get_unreal_version_details(self, executable_path): @@ -208,3 +218,118 @@ def _get_unreal_version_details(self, executable_path): with open(full_path) as pf: version_details = json.load(pf) return version_details + + def _get_unreal_version(self, executable_path): + """ + Return the Unreal Editor version for the given executable. + + :returns: A potentially empty version string. + """ + details = self._get_unreal_version_details(executable_path) + if details and all(x in details for x in ["MajorVersion", "MinorVersion", "PatchVersion"]): + return "%s.%s.%s" % ( + details["MajorVersion"], + details["MinorVersion"], + details["PatchVersion"], + ) + # Fall back on parsing the executable path + # Look for ue_4.7, ue_4.7.2, etc.. + for part in executable_path.lower().split(os.path.sep): + m = re.match(r"ue_([0-9]+(?:\.[0-9]+)*)$", part) + if m: + return m.group(1) + return "" + + def _get_win_executable_path(self, install_path): + """ + Return the Unreal Editor exe path for the given install path, if one + exists. + + Check if the executable exists on the filesystem. + :returns: A string, full path to the Unreal editor executable or ``None``. + """ + binary_path = os.path.normpath( + os.path.join(install_path, "Engine", "Binaries", "Win64") + ) + exec_path = os.path.join(binary_path, "UE4Editor.exe") + if os.path.exists(exec_path): + self.logger.info("Found %s" % exec_path) + return exec_path + # From UE5, the exe name changed + exec_path = os.path.join(binary_path, "UnrealEditor.exe") + if os.path.exists(exec_path): + self.logger.info("Found %s" % exec_path) + return exec_path + + self.logger.info( + "Couldn't find executable in installation path %s" % install_path + ) + return None + + def _find_software_from_registry(self): + """ + Find executables in the Windows Registry. + + :returns: List of :class:`SoftwareVersion` instances. + """ + self.logger.info("Detecting Unreal Engine from registry...") + try: + # Note: keeping this as is, without knowing why it was implemented + # like that, instead of just doing import winreg + import _winreg + except ImportError: + import winreg as _winreg + + self.logger.debug( + "Querying windows registry for key HKEY_LOCAL_MACHINE\\SOFTWARE\\EpicGames\\Unreal Engine" + ) + base_key_name = "SOFTWARE\\EpicGames\\Unreal Engine" + # find all subkeys in key HKEY_LOCAL_MACHINE\SOFTWARE\EpicGames\Unreal Engine + sw_versions = [] + try: + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, base_key_name) + sub_key_count = _winreg.QueryInfoKey(key)[0] + for i in range(sub_key_count): + sub_key_name = _winreg.EnumKey(key, i) + sub_key = _winreg.OpenKey(key, sub_key_name) + values_count = _winreg.QueryInfoKey(sub_key)[1] + if sub_key_name == "Builds": + for j in range(values_count): + value = _winreg.EnumValue(sub_key, j)[1] + self.logger.info("Checking %s" % value) + if value and os.path.exists(value): + executable_path = self._get_win_executable_path(value) + if executable_path: + sw = SoftwareVersion( + self._get_unreal_version(executable_path), + "Unreal Engine (Dev Build)", + executable_path, + os.path.join(self.disk_location, "icon_256.png") + ) + sw_versions.append(sw) + else: + for j in range(values_count): + value_name, value, _ = _winreg.EnumValue(sub_key, j) + if value_name == "InstalledDirectory": + if value and os.path.exists(value): + executable_path = self._get_win_executable_path(value) + if executable_path: + sw = SoftwareVersion( + self._get_unreal_version(executable_path), + "Unreal Engine", + executable_path, + os.path.join(self.disk_location, "icon_256.png") + ) + sw_versions.append(sw) + + break + _winreg.CloseKey(key) + except WindowsError as e: + self.logger.error("Error handling key %s: %s" % (base_key_name, e)) + # Log the traceback in debug + self.logger.debug( + "Error opening key %s: %s" % (base_key_name, e), + exc_info=True + ) + + return sw_versions From 9cd4d3a632ec6bf3f2481b89e8e6d3d20876047b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Deverly?= <39291844+gplsteph@users.noreply.github.com> Date: Mon, 21 Feb 2022 16:46:08 +0100 Subject: [PATCH 3/3] 8010 more ue5 fixes (#12) Mostly addressed CR comments from Epic. Fixed a warning with yaml loading. --- engine.py | 2 +- hooks/tk-multi-publish2/basic/publish_movie.py | 4 ++++ plugins/basic/bootstrap.py | 13 ------------- .../python/tk_unreal_basic/plugin_bootstrap.py | 3 ++- python/tk_unreal/unreal_sg_engine.py | 7 +++++++ startup.py | 11 +++++++++-- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/engine.py b/engine.py index 99a6ed8..35c0d08 100644 --- a/engine.py +++ b/engine.py @@ -88,7 +88,7 @@ def pre_app_init(self): self.init_qt_app() - # Load the tk_unreal module (the Shotgun engine wrapper for Unreal) + # Load the tk_unreal module (the SG engine wrapper for Unreal) self.tk_unreal = self.import_module("tk_unreal") self.unreal_sg_engine = self.tk_unreal.config.wrapper_instance diff --git a/hooks/tk-multi-publish2/basic/publish_movie.py b/hooks/tk-multi-publish2/basic/publish_movie.py index 25ef3ea..6ef9141 100644 --- a/hooks/tk-multi-publish2/basic/publish_movie.py +++ b/hooks/tk-multi-publish2/basic/publish_movie.py @@ -756,6 +756,8 @@ def _unreal_render_sequence_with_sequencer(self, output_path, unreal_map_path, s # Prevent SG TK to try to bootstrap in the new process if "UE_SHOTGUN_BOOTSTRAP" in run_env: del run_env["UE_SHOTGUN_BOOTSTRAP"] + if "UE_SHOTGRID_BOOTSTRAP" in run_env: + del run_env["UE_SHOTGRID_BOOTSTRAP"] subprocess.call(cmdline_args, env=run_env) @@ -896,6 +898,8 @@ def _unreal_render_sequence_with_movie_queue(self, output_path, unreal_map_path, # Prevent SG TK to try to bootstrap in the new process if "UE_SHOTGUN_BOOTSTRAP" in run_env: del run_env["UE_SHOTGUN_BOOTSTRAP"] + if "UE_SHOTGRID_BOOTSTRAP" in run_env: + del run_env["UE_SHOTGRID_BOOTSTRAP"] self.logger.info("Running %s" % cmd_args) subprocess.call(cmd_args, env=run_env) return os.path.isfile(output_path), output_path diff --git a/plugins/basic/bootstrap.py b/plugins/basic/bootstrap.py index 270cedd..22bd2c3 100644 --- a/plugins/basic/bootstrap.py +++ b/plugins/basic/bootstrap.py @@ -4,23 +4,10 @@ import sys import os -import unreal # Setup sys.path so that we can import our bootstrapper. plugin_root_dir = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(0, os.path.join(plugin_root_dir, "python")) -# Temp workaround for UE5 dev: for some reasons the core/python path is missing -# in sys.path but is present in the PYTHONPATH. -# Make sure sys.path is up to date with what is in the PYTHON PATH -python_path = os.environ.get("PYTHONPATH") or "" -for path in python_path.split(os.pathsep): - # We can have empty entries for consecutives separators - if path and path not in sys.path: - unreal.log_warning( - "Adding missing %s Python path to sys paths" % path, - ) - sys.path.append(path) - from tk_unreal_basic import plugin_bootstrap plugin_bootstrap.bootstrap_plugin(plugin_root_dir) diff --git a/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py b/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py index ff6d272..3827005 100644 --- a/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py +++ b/plugins/basic/python/tk_unreal_basic/plugin_bootstrap.py @@ -68,6 +68,7 @@ def _on_engine_initialized(): sgtk_logger.debug("tk-unreal finished initialization.") import unreal + # ShotgunEngine was renamed to ShotgridEngine from UE5 if hasattr(unreal, "ShotgridEngine"): unreal.ShotgridEngine.get_instance().on_engine_initialized() else: @@ -89,7 +90,7 @@ def _initialize_manager(plugin_root_path): # open the yaml file and read the data with open(plugin_info_yml, "r") as plugin_info_fh: - plugin_info = yaml.load(plugin_info_fh) + plugin_info = yaml.load(plugin_info_fh, yaml.SafeLoader) base_config = plugin_info["base_configuration"] plugin_id = plugin_info["plugin_id"] diff --git a/python/tk_unreal/unreal_sg_engine.py b/python/tk_unreal/unreal_sg_engine.py index 247a87d..c803ca4 100644 --- a/python/tk_unreal/unreal_sg_engine.py +++ b/python/tk_unreal/unreal_sg_engine.py @@ -28,6 +28,10 @@ def _post_init(self): config.wrapper_instance = self # Shotgun integration components were renamed to Shotgrid from UE5 + # these new methods are not available in UE4, we provide backward + # compatibility so scripts using the old methods don't break in UE5, + # but also forward compatibility, so users can start using the new + # names in UE4. if hasattr(UESGEngine, "get_shotgrid_menu_items"): @unreal.ufunction(override=True) def get_shotgrid_menu_items(self): @@ -47,6 +51,7 @@ def get_shotgun_menu_items(self): """ Provide backward compatibility. """ + unreal.log_warning("get_shotgun_menu_items is deprecated, get_shotgrid_menu_items should be used instead.") return self.get_shotgrid_menu_items() else: @unreal.ufunction(override=True) @@ -59,6 +64,7 @@ def get_shotgun_menu_items(self): engine = sgtk.platform.current_engine() menu_items = self.create_menu(engine) + unreal.log_warning("get_shotgun_menu_items is deprecated, get_shotgrid_menu_items should be used instead.") unreal.log("get_shotgun_menu_items returned: {0}".format(menu_items.__str__())) return menu_items @@ -74,6 +80,7 @@ def get_shotgun_work_dir(self, *args, **kwargs): """ Provide backward compatibility. """ + unreal.log_warning("get_shotgun_work_dir is deprecated, get_shotgrid_work_dir should be used instead.") return self.get_shotgrid_work_dir(*args, **kwargs) else: def get_shotgrid_work_dir(self, *args, **kwargs): diff --git a/startup.py b/startup.py index ad95250..483eb79 100644 --- a/startup.py +++ b/startup.py @@ -90,10 +90,17 @@ def prepare_launch(self, exec_path, args, file_to_open=None): # Signals which engine instance from the environment is going to be used. required_env["SHOTGUN_ENGINE"] = self.engine_name - # Set the bootstrap location in the environment variable that will be used by the Unreal Shotgun startup script + # Set the bootstrap location in the environment variable that will be used by the Unreal Shotgun plugin + # startup script bootstrap_script = os.path.join(self.disk_location, "plugins", "basic", "bootstrap.py") + # From UE5, the UE_SHOTGRID_BOOTSTRAP environment variable is supported. + # We still need to support UE_SHOTGUN_BOOTSTRAP for previous UE versions. required_env["UE_SHOTGUN_BOOTSTRAP"] = bootstrap_script - + required_env["UE_SHOTGRID_BOOTSTRAP"] = bootstrap_script + # UE5 Python ignores PYTHONPATH and only uses UE_PYTHONPATH + # blindly copy what was set so modules (e.g. SG TK core) are found + # when bootstrapping. + required_env["UE_PYTHONPATH"] = os.environ.get("PYTHONPATH") or "" self.logger.debug("Executable path: %s", exec_path) self.logger.debug("Launch environment: %s", pprint.pformat(required_env)) self.logger.debug("Launch arguments: %s", args)