Skip to content

Commit b9268de

Browse files
committed
Additional python 3.13 and 3.14 support
- remove debugpy_info.json for DEBUGPY_VERSION in noxfile.py
1 parent a6ee0a8 commit b9268de

File tree

3 files changed

+127
-196
lines changed

3 files changed

+127
-196
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@
2222
},
2323
"git.branchProtection": ["main"],
2424
"git.branchRandomName.enable": true,
25-
"python-envs.defaultEnvManager": "ms-python.python:system",
25+
"python-envs.defaultEnvManager": "ms-python.python:venv",
2626
"python-envs.pythonProjects": []
2727
}

debugpy_info.json

Lines changed: 0 additions & 70 deletions
This file was deleted.

noxfile.py

Lines changed: 126 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,99 @@
22
# Licensed under the MIT License.
33
"""All the action we need during build"""
44

5-
import hashlib
6-
import io
75
import json
86
import os
97
import pathlib
108
import re
119
import tempfile
12-
import urllib.request as url_lib
1310
import zipfile
1411

1512
import nox # pylint: disable=import-error
13+
from nox.command import CommandFailed
14+
15+
# Keep this list explicit and ordered (oldest -> newest).
16+
# Update it whenever we bump supported Python versions.
17+
SUPPORTED_DEBUGPY_CPYTHONS = [
18+
"cp310",
19+
"cp311",
20+
"cp312",
21+
"cp313",
22+
"cp314",
23+
]
24+
25+
26+
# Single source of truth for the debugpy version we bundle.
27+
# Update this when bumping debugpy (and update bundled/libs/debugpy accordingly).
28+
DEBUGPY_VERSION = "1.8.19"
29+
30+
31+
def _build_debugpy_wheel_requests(vsce_target: str, version: str) -> list[dict]:
32+
# Platform tags are pip --platform values; keep a small fallback list for resiliency.
33+
# Note: these are used only when we build per-target VSIXs (VSCETARGET is set in CI).
34+
if "darwin" in vsce_target:
35+
platforms = [
36+
"macosx_15_0_universal2",
37+
"macosx_14_0_universal2",
38+
"macosx_13_0_universal2",
39+
"macosx_12_0_universal2",
40+
]
41+
return [
42+
{
43+
"version": version,
44+
"python_version": cp.removeprefix("cp"),
45+
"implementation": "cp",
46+
"abi": cp,
47+
"platforms": platforms,
48+
}
49+
for cp in SUPPORTED_DEBUGPY_CPYTHONS
50+
]
51+
52+
if vsce_target == "win32-x64":
53+
return [
54+
{
55+
"version": version,
56+
"python_version": cp.removeprefix("cp"),
57+
"implementation": "cp",
58+
"abi": cp,
59+
"platforms": ["win_amd64"],
60+
}
61+
for cp in SUPPORTED_DEBUGPY_CPYTHONS
62+
]
63+
64+
if vsce_target == "linux-x64":
65+
platforms = [
66+
"manylinux_2_34_x86_64",
67+
"manylinux_2_31_x86_64",
68+
"manylinux_2_28_x86_64",
69+
"manylinux_2_27_x86_64",
70+
"manylinux_2_17_x86_64",
71+
]
72+
return [
73+
{
74+
"version": version,
75+
"python_version": cp.removeprefix("cp"),
76+
"implementation": "cp",
77+
"abi": cp,
78+
"platforms": platforms,
79+
}
80+
for cp in SUPPORTED_DEBUGPY_CPYTHONS
81+
]
82+
83+
# Default/fallback: ensure we only download the pure-Python wheel (py2.py3-none-any).
84+
# This is used for targets that don't have compiled wheels (e.g., linux-arm64) and
85+
# for workflows that don't set VSCETARGET.
86+
return [
87+
{
88+
"version": version,
89+
# Intentionally omit pip targeting flags here.
90+
# Passing --python-version 3 makes pip treat it as Python 3.0, which
91+
# excludes debugpy (Requires-Python >= 3.8).
92+
"python_version": None,
93+
"implementation": None,
94+
"abi": None,
95+
"platforms": [],
96+
}
97+
]
1698

1799

18100
@nox.session()
@@ -60,54 +142,14 @@ def install_bundled_libs(session):
60142
)
61143
session.install("packaging")
62144

63-
debugpy_info_json_path = pathlib.Path(__file__).parent / "debugpy_info.json"
64-
debugpy_info = json.loads(debugpy_info_json_path.read_text(encoding="utf-8"))
65-
66145
target = os.environ.get("VSCETARGET", "")
67146
print("target:", target)
68-
if "darwin" in target:
69-
wheels = debugpy_info["macOS"]
70-
elif "win32-ia32" == target:
71-
wheels = debugpy_info.get("win32", debugpy_info["any"])
72-
elif "win32-x64" == target:
73-
wheels = debugpy_info["win64"]
74-
elif "linux-x64" == target:
75-
wheels = debugpy_info["linux"]
76-
else:
77-
wheels = debugpy_info["any"]
78-
79-
download_debugpy_via_pip(session, wheels)
80147

148+
requests = _build_debugpy_wheel_requests(target, DEBUGPY_VERSION)
149+
download_debugpy_via_pip(session, requests)
81150

82-
def _parse_wheel_info(url: str) -> dict:
83-
"""Parse version and platform info from a wheel URL.
84151

85-
Example URL: .../debugpy-1.8.19-cp311-cp311-win_amd64.whl
86-
Returns: {"version": "1.8.19", "py_ver": "311", "abi": "cp311", "platform": "win_amd64"}
87-
"""
88-
filename = url.rsplit("/", 1)[-1]
89-
# Wheel filename format: {name}-{version}-{python}-{abi}-{platform}.whl
90-
match = re.match(r"debugpy-([^-]+)-cp(\d+)-([^-]+)-(.+)\.whl", filename)
91-
if match:
92-
return {
93-
"version": match.group(1),
94-
"py_ver": match.group(2),
95-
"abi": match.group(3),
96-
"platform": match.group(4),
97-
}
98-
# Fallback for py2.py3-none-any wheels
99-
match = re.match(r"debugpy-([^-]+)-py\d\.py\d-none-any\.whl", filename)
100-
if match:
101-
return {
102-
"version": match.group(1),
103-
"py_ver": None,
104-
"abi": "none",
105-
"platform": "any",
106-
}
107-
raise ValueError(f"Could not parse wheel filename: {filename}")
108-
109-
110-
def download_debugpy_via_pip(session: nox.Session, wheels: list) -> None:
152+
def download_debugpy_via_pip(session: nox.Session, requests: list[dict]) -> None:
111153
"""Downloads debugpy wheels via pip and extracts them into bundled/libs.
112154
113155
Uses pip to download by package name, allowing pip to use configured
@@ -116,35 +158,59 @@ def download_debugpy_via_pip(session: nox.Session, wheels: list) -> None:
116158
libs_dir = pathlib.Path.cwd() / "bundled" / "libs"
117159
libs_dir.mkdir(parents=True, exist_ok=True)
118160

119-
# Parse version and platform info from wheel URLs
120-
parsed = [_parse_wheel_info(w["url"]) for w in wheels]
121-
version = parsed[0]["version"]
161+
if not requests:
162+
raise ValueError("No debugpy wheel requests were provided.")
163+
version = requests[0]["version"]
122164

123165
with tempfile.TemporaryDirectory(prefix="debugpy_wheels_") as tmp_dir:
124166
tmp_path = pathlib.Path(tmp_dir)
125167

126-
for info in parsed:
127-
args = [
168+
for req in requests:
169+
base_args = [
128170
"python",
129171
"-m",
130172
"pip",
131173
"download",
132-
f"debugpy=={version}",
174+
f"debugpy=={req['version']}",
133175
"--no-deps",
134176
"--only-binary",
135177
":all:",
136178
"--dest",
137179
str(tmp_path),
138180
]
139-
if info["py_ver"]:
140-
# Platform-specific wheel
141-
args.extend(["--python-version", info["py_ver"]])
142-
args.extend(["--implementation", "cp"])
143-
args.extend(["--abi", info["abi"]])
144-
args.extend(["--platform", info["platform"]])
145-
# For none-any wheels, no platform args needed
146181

147-
session.run(*args)
182+
python_version = req.get("python_version")
183+
implementation = req.get("implementation")
184+
abi = req.get("abi")
185+
platforms = req.get("platforms") or []
186+
187+
if python_version is None and implementation is None and abi is None and not platforms:
188+
session.run(*base_args)
189+
continue
190+
191+
last_error = None
192+
for platform in platforms:
193+
args = base_args + [
194+
"--python-version",
195+
python_version,
196+
"--implementation",
197+
implementation,
198+
"--abi",
199+
abi,
200+
"--platform",
201+
platform,
202+
]
203+
204+
try:
205+
session.run(*args)
206+
last_error = None
207+
break
208+
except CommandFailed as exc:
209+
last_error = exc
210+
continue
211+
212+
if last_error is not None:
213+
raise last_error
148214

149215
wheel_paths = sorted(tmp_path.glob("debugpy-*.whl"))
150216
if not wheel_paths:
@@ -160,22 +226,6 @@ def download_debugpy_via_pip(session: nox.Session, wheels: list) -> None:
160226
wheel.extract(zip_info.filename, libs_dir)
161227

162228

163-
def download_url(values):
164-
for value in values:
165-
with url_lib.urlopen(value["url"]) as response:
166-
data = response.read()
167-
digest = value["hash"]["sha256"]
168-
if hashlib.new("sha256", data).hexdigest() != digest:
169-
raise ValueError(f"Failed hash verification for {value['url']}.")
170-
171-
print("Download: ", value["url"])
172-
with zipfile.ZipFile(io.BytesIO(data), "r") as wheel:
173-
libs_dir = pathlib.Path.cwd() / "bundled" / "libs"
174-
for zip_info in wheel.infolist():
175-
print("\t" + zip_info.filename)
176-
wheel.extract(zip_info.filename, libs_dir)
177-
178-
179229
@nox.session()
180230
def update_build_number(session: nox.Session) -> None:
181231
"""Updates build number for the extension."""
@@ -197,52 +247,3 @@ def update_build_number(session: nox.Session) -> None:
197247
session.log(f"Updating version from {package_json['version']} to {version}")
198248
package_json["version"] = version
199249
package_json_path.write_text(json.dumps(package_json, indent=4), encoding="utf-8")
200-
201-
202-
def _get_pypi_package_data(package_name):
203-
json_uri = "https://pypi.org/pypi/{0}/json".format(package_name)
204-
# Response format: https://warehouse.readthedocs.io/api-reference/json/#project
205-
# Release metadata format: https://github.com/pypa/interoperability-peps/blob/master/pep-0426-core-metadata.rst
206-
with url_lib.urlopen(json_uri) as response:
207-
return json.loads(response.read())
208-
209-
210-
def _get_debugpy_info(version="latest", platform="none-any", cp="cp311"):
211-
from packaging.version import parse as version_parser
212-
213-
data = _get_pypi_package_data("debugpy")
214-
215-
if version == "latest":
216-
use_version = max(data["releases"].keys(), key=version_parser)
217-
else:
218-
use_version = version
219-
220-
return list(
221-
{"url": r["url"], "hash": {"sha256": r["digests"]["sha256"]}}
222-
for r in data["releases"][use_version]
223-
if f"{cp}-{platform}" in r["url"] or f"py3-{platform}" in r["url"]
224-
)[0]
225-
226-
227-
@nox.session
228-
def create_debugpy_json(session: nox.Session):
229-
platforms = [
230-
("macOS", "macosx"),
231-
# ("win32", "win32"), # VS Code does not support 32-bit Windows anymore
232-
("win64", "win_amd64"),
233-
("linux", "manylinux"),
234-
("any", "none-any"),
235-
]
236-
debugpy_info_json_path = pathlib.Path(__file__).parent / "debugpy_info.json"
237-
debugpy_info = {}
238-
for p, id in platforms:
239-
# we typically have the latest 3 versions of debugpy compiled bits
240-
downloads = []
241-
for cp in ["cp310", "cp311", "cp312"]:
242-
data = _get_debugpy_info("latest", id, cp)
243-
if not any(d["url"] == data["url"] for d in downloads):
244-
downloads.append(data)
245-
debugpy_info[p] = downloads
246-
debugpy_info_json_path.write_text(
247-
json.dumps(debugpy_info, indent=4), encoding="utf-8"
248-
)

0 commit comments

Comments
 (0)