From 2cb865ac326b501820dfe5c6667fde20397a745d Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Fri, 22 May 2026 14:02:52 -0700 Subject: [PATCH] SG-43269 Fix sys.path and sys.meta_path isolation during core swap When the site core (e.g. v0.23.8 bundled with SGD 3.0) boots, its tank_vendor/__init__.py inserts its pkgs.zip into sys.path[0] and registers a _TankVendorMetaFinder in sys.meta_path. Previously, _swap_core() cleaned up sys.modules but left both of those in place. This caused two distinct failures when swapping to an older project core: 1. sys.path pollution: direct imports like Version: ImageMagick 7.1.2-23 Q16-HDRI aarch64 6c51b2de5:20260516 https://imagemagick.org By default, 'file' is written in the MIFF image format. To specify a particular image format, precede the filename with an image format name and a colon (i.e. ps:image) or specify the image type as the filename suffix (i.e. image.ps). Specify 'file' as '-' for standard input or output. (not via tank_vendor.*) resolved to the site core's pkgs.zip instead of the project core's vendored copy. The site pkgs.zip lacks packages removed in newer releases (e.g. sgsix), causing ImportError. 2. Stale _TankVendorMetaFinder: when six.__path__ == [] caused CoreImportHandler to return None for , the stale meta finder intercepted the import and tried to resolve it against the old sys.path state (site pkgs.zip, no six), causing ModuleNotFoundError. Fix: in _swap_core(), before resetting self._core_path: - Strip all sys.path entries that live under the outgoing core root. - Remove any _TankVendorMetaFinder instances from sys.meta_path and delete sys._tank_vendor_meta_finder. The incoming core's tank_vendor/__init__.py will then insert its own pkgs.zip and register a fresh finder when Version: ImageMagick 7.1.2-23 Q16-HDRI aarch64 6c51b2de5:20260516 https://imagemagick.org By default, 'file' is written in the MIFF image format. To specify a particular image format, precede the filename with an image format name and a colon (i.e. ps:image) or specify the image type as the filename suffix (i.e. image.ps). Specify 'file' as '-' for standard input or output. loads it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/tank/bootstrap/import_handler.py | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/python/tank/bootstrap/import_handler.py b/python/tank/bootstrap/import_handler.py index 715d61e19..9b292e515 100644 --- a/python/tank/bootstrap/import_handler.py +++ b/python/tank/bootstrap/import_handler.py @@ -209,6 +209,38 @@ def _swap_core(self, core_path): # log.debug("Removing sys.modules[%s]" % module_name) del sys.modules[module_name] + # Remove the outgoing core's pkgs.zip from sys.path so the incoming + # core's tank_vendor/__init__.py can insert the correct one on load. + # Without this, a newer site core's pkgs.zip (which may lack packages + # like `six`) stays at sys.path[0] and is resolved by direct imports + # (e.g. `import shotgun_api3`) in older project core code. + current_core_root = os.path.normpath(os.path.dirname(self._core_path)) + new_sys_path = [] + for p in sys.path: + norm_p = os.path.normpath(p) + try: + if os.path.commonpath([norm_p, current_core_root]) == current_core_root: + log.debug("Removing sys.path entry belonging to outgoing core: %s" % p) + continue + except ValueError: + pass # commonpath raises ValueError on mixed-drive paths on Windows + new_sys_path.append(p) + sys.path[:] = new_sys_path + + # Remove the outgoing core's _TankVendorMetaFinder from sys.meta_path. + # This finder is installed by tank_vendor/__init__.py and redirects + # `tank_vendor.*` submodule lookups. After a swap it would intercept + # imports like `tank_vendor.six.moves` and fail to resolve them against + # the incoming core's environment. The incoming tank_vendor/__init__.py + # will install a fresh finder when it loads. + sys.meta_path[:] = [ + h for h in sys.meta_path + if type(h).__name__ != "_TankVendorMetaFinder" + ] + if hasattr(sys, "_tank_vendor_meta_finder"): + del sys._tank_vendor_meta_finder + log.debug("Removed stale _TankVendorMetaFinder from sys.meta_path.") + # reset importer to point at new core for future imports self._module_info = {} self._core_path = core_path