Skip to content

Commit bc5f5e5

Browse files
SG-42625 Fix tank_vendor imports for pip installations and developer scripts (#1091)
1 parent 28f457c commit bc5f5e5

6 files changed

Lines changed: 183 additions & 4 deletions

File tree

developer/bake_config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
# add sgtk API
2222
this_folder = os.path.abspath(os.path.dirname(__file__))
2323
python_folder = os.path.abspath(os.path.join(this_folder, "..", "python"))
24-
sys.path.append(python_folder)
24+
# Insert at the beginning to ensure local tk-core takes precedence over
25+
# any installed version in site-packages
26+
sys.path.insert(0, python_folder)
2527

2628
# sgtk imports
2729
from tank import LogManager

developer/build_plugin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
# add sgtk API
2626
this_folder = os.path.abspath(os.path.dirname(__file__))
2727
python_folder = os.path.abspath(os.path.join(this_folder, "..", "python"))
28-
sys.path.append(python_folder)
28+
# Insert at the beginning to ensure local tk-core takes precedence over
29+
# any installed version in site-packages
30+
sys.path.insert(0, python_folder)
2931

3032
# sgtk imports
3133
from tank import LogManager

developer/populate_bundle_cache.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
# add sgtk API
2525
this_folder = os.path.abspath(os.path.dirname(__file__))
2626
python_folder = os.path.abspath(os.path.join(this_folder, "..", "python"))
27-
sys.path.append(python_folder)
27+
# Insert at the beginning to ensure local tk-core takes precedence over
28+
# any installed version in site-packages
29+
sys.path.insert(0, python_folder)
2830

2931
# sgtk imports
3032
from sgtk import LogManager

setup.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010

1111
# Basic setup.py so tk-core could be installed as
1212
# a standard Python package
13+
import os
1314
import re
14-
from setuptools import setup, find_packages
1515
import subprocess
16+
import sys
17+
18+
from setuptools import setup, find_packages
1619

1720

1821
def get_version():
@@ -50,6 +53,35 @@ def get_version():
5053
return "dev"
5154

5255

56+
def get_install_requires():
57+
"""
58+
Read dependencies from the version-specific requirements.txt.
59+
60+
This ensures pip installations use the same dependency versions
61+
as those vendored in pkgs.zip for Toolkit distributions.
62+
63+
:returns: A list of requirement strings, e.g. ["PyYAML==6.0.2", ...].
64+
"""
65+
req_file = os.path.join(
66+
os.path.dirname(os.path.abspath(__file__)),
67+
"requirements",
68+
f"{sys.version_info.major}.{sys.version_info.minor}",
69+
"requirements.txt",
70+
)
71+
if not os.path.exists(req_file):
72+
raise Exception(
73+
f"Python {sys.version_info.major}.{sys.version_info.minor}"
74+
" is not supported"
75+
)
76+
77+
with open(req_file) as f:
78+
return [
79+
line.strip()
80+
for line in f
81+
if line.strip() and not line.strip().startswith("#")
82+
]
83+
84+
5385
# Retrieve long description and licence from external files
5486
try:
5587
f = open("README.md")
@@ -72,6 +104,10 @@ def get_version():
72104
author="Autodesk, Inc",
73105
url="https://github.com/shotgunsoftware/tk-core",
74106
license=license,
107+
# Dependencies for pip installations (when pkgs.zip is not available).
108+
# Versions are read from requirements/<python_version>/requirements.txt
109+
# to stay in sync with the vendored packages in pkgs.zip.
110+
install_requires=get_install_requires(),
75111
# Recursively discover all packages in python folder, excluding any tests
76112
packages=find_packages(
77113
"python", exclude=("*.tests", "*.tests.*", "tests.*", "tests")
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright 2026 Autodesk, Inc. All rights reserved.
2+
#
3+
# Use of this software is subject to the terms of the Autodesk license agreement
4+
# provided at the time of installation or download, or which otherwise accompanies
5+
# this software in either electronic or hard copy form.
6+
#
7+
8+
"""
9+
Validate that developer scripts work correctly even when sgtk is also
10+
installed via pip in the same Python environment.
11+
12+
This ensures that developer scripts always use the local tk-core
13+
(via sys.path.insert) and not the pip-installed version.
14+
"""
15+
16+
import os
17+
import subprocess # nosec B404
18+
import sys
19+
import tempfile
20+
import unittest
21+
22+
from sgtk_integration_test import SgtkIntegrationTest
23+
24+
25+
class DeveloperScriptsTests(SgtkIntegrationTest):
26+
"""Validate developer scripts work with pip sgtk installed.
27+
28+
Creates a temporary venv with sgtk pip-installed, then runs each
29+
developer script from that venv to confirm they still prioritize
30+
the local tk-core (via sys.path.insert) over site-packages.
31+
"""
32+
33+
@classmethod
34+
def _get_venv_python(cls, venv_dir):
35+
"""Return the path to the Python executable inside the venv."""
36+
if sys.platform == "win32":
37+
return os.path.join(venv_dir, "Scripts", "python.exe")
38+
return os.path.join(venv_dir, "bin", "python")
39+
40+
@classmethod
41+
def setUpClass(cls):
42+
"""Create a temp venv and install sgtk via pip."""
43+
super().setUpClass()
44+
cls._venv_dir = tempfile.mkdtemp(prefix="test_dev_scripts_")
45+
subprocess.check_call( # nosec B603
46+
[sys.executable, "-m", "venv", cls._venv_dir]
47+
)
48+
cls._venv_python = cls._get_venv_python(cls._venv_dir)
49+
subprocess.check_call( # nosec B603
50+
[cls._venv_python, "-m", "pip", "install", cls.tk_core_repo_root]
51+
)
52+
53+
def test_bake_config(self):
54+
"""Validate bake_config.py --help works."""
55+
script = os.path.join(self.tk_core_repo_root, "developer", "bake_config.py")
56+
subprocess.check_call([self._venv_python, script, "--help"]) # nosec B603
57+
58+
def test_build_plugin(self):
59+
"""Validate build_plugin.py --help works."""
60+
script = os.path.join(self.tk_core_repo_root, "developer", "build_plugin.py")
61+
subprocess.check_call([self._venv_python, script, "--help"]) # nosec B603
62+
63+
def test_populate_bundle_cache(self):
64+
"""Validate populate_bundle_cache.py --help works."""
65+
script = os.path.join(
66+
self.tk_core_repo_root, "developer", "populate_bundle_cache.py"
67+
)
68+
subprocess.check_call([self._venv_python, script, "--help"]) # nosec B603
69+
70+
71+
if __name__ == "__main__":
72+
unittest.main(failfast=True, verbosity=2)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright 2025 Autodesk, Inc. All rights reserved.
2+
#
3+
# Use of this software is subject to the terms of the Autodesk license agreement
4+
# provided at the time of installation or download, or which otherwise accompanies
5+
# this software in either electronic or hard copy form.
6+
#
7+
8+
"""
9+
Validate that sgtk can be installed via pip in a clean virtual environment
10+
and that all tank_vendor imports resolve correctly.
11+
12+
This script creates a temporary venv, runs pip install, and verifies
13+
that critical imports work without the pkgs.zip vendored dependencies.
14+
"""
15+
16+
import os
17+
import subprocess # nosec B404
18+
import sys
19+
import tempfile
20+
import unittest
21+
22+
from sgtk_integration_test import SgtkIntegrationTest
23+
24+
25+
class PipInstallTests(SgtkIntegrationTest):
26+
"""Validate pip install and tank_vendor imports in a clean venv."""
27+
28+
@classmethod
29+
def _get_venv_python(cls, venv_dir):
30+
"""Return the path to the Python executable inside the venv."""
31+
if sys.platform == "win32":
32+
return os.path.join(venv_dir, "Scripts", "python.exe")
33+
return os.path.join(venv_dir, "bin", "python")
34+
35+
def test_pip_install_and_import(self):
36+
"""Validate pip install followed by import sgtk in a clean venv."""
37+
tk_core_root = self.tk_core_repo_root
38+
39+
with tempfile.TemporaryDirectory(prefix="test_pip_") as venv_dir:
40+
subprocess.check_call( # nosec B603
41+
[sys.executable, "-m", "venv", venv_dir]
42+
)
43+
python = self._get_venv_python(venv_dir)
44+
45+
subprocess.check_call( # nosec B603
46+
[python, "-m", "pip", "install", tk_core_root]
47+
)
48+
subprocess.check_call([python, "-c", "import sgtk"]) # nosec B603
49+
subprocess.check_call( # nosec B603
50+
[python, "-c", "from tank_vendor.shotgun_api3 import Shotgun"]
51+
)
52+
subprocess.check_call( # nosec B603
53+
[python, "-c", "from tank_vendor import yaml"]
54+
)
55+
subprocess.check_call( # nosec B603
56+
[
57+
python,
58+
"-c",
59+
"from tank_vendor.shotgun_api3.lib import httplib2",
60+
]
61+
)
62+
63+
64+
if __name__ == "__main__":
65+
unittest.main(failfast=True, verbosity=2)

0 commit comments

Comments
 (0)