Skip to content

Commit 810ed2d

Browse files
[apiview-stub-generator] Make package-install timeout configurable (120s -> 300s) (#16395)
* [apiview-stub-generator] Make package-install timeout configurable (120s -> 300s) The API-stub generator installs the target package (with all runtime deps) into an isolated venv before introspection, under a hardcoded 120s timeout. Packages with large dependency trees can exceed this on slower CI agents even when the install is healthy — e.g. azure-ai-agentserver-core (which pulls the Microsoft OpenTelemetry distro, ~200 transitive packages) was observed installing in 35s on one azsdk-pool agent and 136s on another (identical tree, pure per-agent speed variance), failing API Review on both PRs and main. Raise the default to 300s and allow override via APISTUB_INSTALL_TIMEOUT. * Validate APISTUB_INSTALL_TIMEOUT: fall back to default on non-integer or non-positive values Addresses review: a malformed env value (e.g. 'abc', '0', '-1') no longer crashes with a raw ValueError or an immediate timeout. Invalid or non-positive values are ignored with a warning and the 300s default is used. * Keep default install timeout at 120s; only make it overridable Per review feedback: preserve the previous default (120s) and provide APISTUB_INSTALL_TIMEOUT purely as an opt-in override for pipelines whose packages need more time. No behavior change for existing consumers. * Bump apistub package-install timeout 120s -> 300s Large dependency trees (e.g. the Microsoft OpenTelemetry distro) can exceed the 120s install timeout on slower CI agents even when the install is healthy, causing spurious API Review failures. Raise to 300s. * ci: authenticate pip to dev feed before installing tox The azsdk-pool CI agents no longer have direct public PyPI egress, so 'pip install tox' fails with '[Errno 1] Operation not permitted' / 'No matching distribution found for tox'. Add a PipAuthenticate@1 step so pip (and tox's nested pip) route through the authenticated public/azure-sdk-for-python feed, which proxies PyPI. * ci: bump lazy-object-proxy 1.10.0 -> 1.12.0 for Python 3.13/3.14 wheels lazy-object-proxy 1.10.0 only ships wheels for cp310-cp312. On Python 3.13/3.14 CI legs, pip fell back to building it from sdist, which triggered a nested build-dependency install that prompted for feed auth (User for pkgs.dev.azure.com:) and failed with EOFError. 1.12.0 publishes prebuilt cp313 and cp314 wheels (and still cp310-cp312), so no sdist build is needed and the 3.13/3.14 legs no longer hit the auth prompt. * ci: fix python-tool Build stage for network-restricted agents Two failures in the shared python-tool archetype Build stage: 1. 'Use Python 3.8' failed - the CI images now ship only Python 3.10-3.14 and 3.8 can no longer be downloaded. Bump the tooling bootstrap version in use-python-version.yml from 3.8 to 3.10 (lowest version on the image). 2. The Build job's pip installs (tooling packaging install + Setup Python Environment) had no feed auth, so they failed with '[Errno 1] Operation not permitted' now that agents lack public PyPI egress. Add a PipAuthenticate@1 step as the first step of the Build job so PIP_INDEX_URL is set before any pip install. * Use auth-sev-feed.yml * update azure_test.ynl to download packages from devOps feed rather that public pypi * Raise apistub install timeout 300s -> 1000s to diagnose slow core install * Revert install timeout back to 300s (1000s was a diagnostic experiment) --------- Co-authored-by: Chidozie Ononiwu <chononiw@microsoft.com>
1 parent 69e96fa commit 810ed2d

9 files changed

Lines changed: 70 additions & 26 deletions

File tree

eng/common/pipelines/templates/steps/auth-dev-feed.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ steps:
99
# For safety default to publishing to the private feed.
1010
# Publish to https://dev.azure.com/azure-sdk/internal/_packaging?_a=feed&feed=azure-sdk-for-python-pr
1111
$devopsFeedName = 'internal/azure-sdk-for-python-pr'
12-
if ('$(Build.Repository.Name)' -eq 'Azure/azure-sdk-for-python') {
12+
if (-not ('$(Build.Repository.Name)').EndsWith('-pr')) {
1313
# Publish to https://dev.azure.com/azure-sdk/public/_packaging?_a=feed&feed=azure-sdk-for-python
1414
$devopsFeedName = '${{ parameters.DevFeedName }}'
1515
}

eng/pipelines/templates/stages/archetype-sdk-tool-python.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ extends:
4242
os: linux
4343

4444
steps:
45+
- template: /eng/common/pipelines/templates/steps/auth-dev-feed.yml
46+
4547
- template: /eng/pipelines/templates/steps/use-python-version.yml
4648
parameters:
4749
versionSpec: '${{ parameters.PythonVersion }}'

eng/pipelines/templates/steps/use-python-version.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ parameters:
22
versionSpec: ''
33

44
steps:
5-
# use python 3.8 for tooling. packaging. platform.
5+
# use python 3.10 for tooling. packaging. platform.
6+
# (3.8 is no longer present on the CI images, which ship 3.10+.)
67
- task: UsePythonVersion@0
7-
displayName: "Use Python 3.8"
8+
displayName: "Use Python 3.10"
89
inputs:
9-
versionSpec: 3.8
10+
versionSpec: '3.10'
1011

1112
- pwsh: |
1213
python -m pip install packaging==20.4

packages/python-packages/apiview-stub-generator/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Release History
22

3+
## Version 0.3.29 (2026-07-17)
4+
Raised the package-install timeout in the API-stub generator from 120s to 300s. Packages with large dependency trees (e.g. those pulling the Microsoft OpenTelemetry distro) could exceed the previous 120s limit on slower CI agents even though the install was healthy, causing spurious API Review failures.
5+
36
## Version 0.3.28 (2026-04-14)
47
Dropped 3.7/3.8/3.9 support and added 3.11/3.12/3.13/3.14.
58
Added `crossLanguageVersion` to the `CrossLanguageMetadata` model.

packages/python-packages/apiview-stub-generator/apistub/_stub_generator.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,12 @@ def _get_package_name_from_metadata_files(self, path):
450450

451451
def _install_package(self):
452452
commands = [sys.executable, "-m", "pip", "install", self.pkg_path, "-q"]
453-
result = run(commands, timeout=120, stderr=PIPE, text=True)
453+
# Packages with large dependency trees (e.g. those pulling the Microsoft
454+
# OpenTelemetry distro) can exceed a short install timeout on slower CI
455+
# agents even though the install is healthy — the same install has been
456+
# observed to take 35s on one agent and 136s on another. Use 300s to
457+
# avoid spurious API Review failures.
458+
result = run(commands, timeout=300, stderr=PIPE, text=True)
454459
if result.stderr:
455460
logging.debug("pip stderr: %s", result.stderr.strip())
456461
if result.returncode != 0:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4-
VERSION = "0.3.28"
4+
VERSION = "0.3.29"

packages/python-packages/apiview-stub-generator/apiview_reqs.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ charset-normalizer==3.4.1
33
dill==0.3.9
44
isodate==0.6.1
55
isort==5.13.2
6-
lazy-object-proxy==1.10.0
6+
lazy-object-proxy==1.12.0
77
mccabe==0.7.0
88
pkginfo==1.12.1.2
99
platformdirs==4.3.6

packages/python-packages/apiview-stub-generator/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ extends:
3737
ArtifactName: 'apiviewparserpython'
3838
PackageName: 'Python API View Parser'
3939
TestSteps:
40+
- template: /eng/common/pipelines/templates/steps/auth-dev-feed.yml
4041
- script: |
4142
python $(Build.SourcesDirectory)/eng/scripts/python_version_check.py
4243
displayName: 'Verify Python APIView version consistency'

packages/python-packages/apiview-stub-generator/tests/azure_tests/azure_test.py

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,56 @@ def _download_file(dest_folder, url):
128128
print(f"Error downloading {url}: {e}")
129129
return None
130130

131+
def _download_package_from_index(temp_dir, package_name, version, pkg_type):
132+
"""
133+
Download a wheel/sdist from the configured pip index.
134+
135+
In CI, PIP_INDEX_URL is set by PipAuthenticate to the authenticated
136+
Azure DevOps feed URL.
137+
"""
138+
index_url = os.environ.get(
139+
"PIP_INDEX_URL",
140+
"https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/",
141+
)
142+
package_spec = f"{package_name}=={version}"
143+
144+
download_cmd = [
145+
"python",
146+
"-m",
147+
"pip",
148+
"download",
149+
package_spec,
150+
"--no-deps",
151+
"--dest",
152+
temp_dir,
153+
"--index-url",
154+
index_url,
155+
]
156+
157+
if pkg_type == "whl":
158+
download_cmd.append("--only-binary=:all:")
159+
elif pkg_type == "sdist":
160+
download_cmd.append("--no-binary=:all:")
161+
else:
162+
raise ValueError(f"Unsupported package type: {pkg_type}")
163+
164+
existing_files = set(os.listdir(temp_dir))
165+
check_call(download_cmd)
166+
downloaded_files = set(os.listdir(temp_dir)) - existing_files
167+
168+
if pkg_type == "whl":
169+
candidates = [f for f in downloaded_files if f.endswith(".whl")]
170+
else:
171+
candidates = [f for f in downloaded_files if f.endswith(".tar.gz") or f.endswith(".zip")]
172+
173+
if not candidates:
174+
raise RuntimeError(
175+
f"Could not find downloaded {pkg_type} artifact for {package_spec} from {index_url}"
176+
)
177+
178+
candidates.sort()
179+
return os.path.join(temp_dir, candidates[0])
180+
131181
def _get_pypi_files(temp_dir, package_name, version, pkg_type, subdirectory):
132182
"""
133183
Get the wheel and tar.gz files from PyPI for a specific version of a package.
@@ -136,25 +186,7 @@ def _get_pypi_files(temp_dir, package_name, version, pkg_type, subdirectory):
136186
:param version: Version of the package
137187
:return: Paths to the downloaded wheel and tar.gz files
138188
"""
139-
pypi_url = f"https://pypi.org/pypi/{package_name}/{version}/json"
140-
response = requests.get(pypi_url)
141-
response.raise_for_status()
142-
data = response.json()
143-
144-
url = None
145-
146-
for file_info in data['urls']:
147-
if pkg_type == "whl" and file_info['packagetype'] == 'bdist_wheel':
148-
url = file_info['url']
149-
break
150-
elif pkg_type == "sdist" and file_info['packagetype'] == 'sdist':
151-
url = file_info['url']
152-
break
153-
154-
if not url:
155-
raise ValueError(f"Could not find {pkg_type} file for the specified version.")
156-
157-
pkg_path = _download_file(temp_dir, url)
189+
pkg_path = _download_package_from_index(temp_dir, package_name, version, pkg_type)
158190
# Copy apiview-properties.json to pkg_path from github repo if it exists
159191
mapping_file = _get_mapping_file(temp_dir, subdirectory, package_name, version)
160192

0 commit comments

Comments
 (0)