Skip to content

Commit 9bb9c03

Browse files
committed
Merge remote-tracking branch 'upstream/main' into more-readme-updates
2 parents 97e6f14 + c0f574f commit 9bb9c03

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

release.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
Self,
3535
overload,
3636
)
37+
from urllib.request import urlopen
3738

3839
COMMASPACE = ", "
3940
SPACE = " "
@@ -71,6 +72,11 @@ def get(
7172
@overload
7273
def get(self, key: Literal["sign_gpg"], default: bool | None = None) -> bool: ...
7374

75+
@overload
76+
def get(
77+
self, key: Literal["security_release"], default: bool | None = None
78+
) -> bool: ...
79+
7480
@overload
7581
def get(self, key: Literal["release"], default: Tag | None = None) -> Tag: ...
7682

@@ -98,6 +104,9 @@ def __getitem__(self, key: Literal["ssh_key"]) -> str | None: ...
98104
@overload
99105
def __getitem__(self, key: Literal["sign_gpg"]) -> bool: ...
100106

107+
@overload
108+
def __getitem__(self, key: Literal["security_release"]) -> bool: ...
109+
101110
@overload
102111
def __getitem__(self, key: Literal["release"]) -> Tag: ...
103112

@@ -127,6 +136,9 @@ def __setitem__(self, key: Literal["ssh_key"], value: str | None) -> None: ...
127136
@overload
128137
def __setitem__(self, key: Literal["sign_gpg"], value: bool) -> None: ...
129138

139+
@overload
140+
def __setitem__(self, key: Literal["security_release"], value: bool) -> None: ...
141+
130142
@overload
131143
def __setitem__(self, key: Literal["release"], value: Tag) -> None: ...
132144

@@ -196,6 +208,13 @@ def is_release_candidate(self) -> bool:
196208
def is_feature_freeze_release(self) -> bool:
197209
return self.level == "b" and self.serial == 1
198210

211+
@property
212+
def is_security_release(self) -> bool:
213+
url = "https://peps.python.org/api/release-cycle.json"
214+
with urlopen(url) as response:
215+
data = json.loads(response.read())
216+
return str(data[self.basic_version]["status"]) == "security"
217+
199218
@property
200219
def nickname(self) -> str:
201220
return self.text.replace(".", "")
@@ -465,10 +484,9 @@ def tweak_readme(tag: Tag, filename: str = "README.rst") -> None:
465484
print(f"Updating {filename}...", end=" ")
466485
readme = Path(filename)
467486

468-
# Update first line: "This is Python version 3.14.0 alpha 7"
487+
# Update first line: "This is Python version X.Y.Z {release_level} N"
469488
# and update length of underline in second line to match.
470-
content = readme.read_text()
471-
lines = content.split("\n")
489+
lines = readme.read_text().split("\n")
472490
this_is = f"This is Python version {tag.long_name}"
473491
underline = "=" * len(this_is)
474492
lines[0] = this_is

run_release.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,10 @@ def __init__(
248248
self.db["ssh_key"] = ssh_key
249249
if not self.db.get("sign_gpg"):
250250
self.db["sign_gpg"] = sign_gpg
251-
252251
if not self.db.get("release"):
253252
self.db["release"] = release_tag
253+
if not self.db.get("security_release"):
254+
self.db["security_release"] = self.db["release"].is_security_release
254255

255256
print("Release data: ")
256257
print(f"- Branch: {release_tag.branch}")
@@ -260,6 +261,7 @@ def __init__(
260261
print(f"- SSH username: {self.db['ssh_user']}")
261262
print(f"- SSH key: {self.db['ssh_key'] or 'Default'}")
262263
print(f"- Sign with GPG: {self.db['sign_gpg']}")
264+
print(f"- Security release: {self.db['security_release']}")
263265
print()
264266

265267
def checkpoint(self) -> None:
@@ -1000,18 +1002,29 @@ def wait_until_all_files_are_in_folder(db: ReleaseShelf) -> None:
10001002
are_windows_files_there = f"python-{release}.exe" in all_files
10011003
are_macos_files_there = f"python-{release}-macos11.pkg" in all_files
10021004
are_linux_files_there = f"Python-{release}.tgz" in all_files
1003-
are_all_files_there = (
1004-
are_linux_files_there and are_windows_files_there and are_macos_files_there
1005-
)
1005+
1006+
if db["security_release"]:
1007+
# For security releases, only check Linux files
1008+
are_all_files_there = are_linux_files_there
1009+
else:
1010+
# For regular releases, check all platforms
1011+
are_all_files_there = (
1012+
are_linux_files_there
1013+
and are_windows_files_there
1014+
and are_macos_files_there
1015+
)
1016+
10061017
if not are_all_files_there:
10071018
linux_tick = "✅" if are_linux_files_there else "❌"
10081019
windows_tick = "✅" if are_windows_files_there else "❌"
10091020
macos_tick = "✅" if are_macos_files_there else "❌"
1010-
print(
1011-
f"\rWaiting for files: Linux {linux_tick} Windows {windows_tick} Mac {macos_tick} ",
1012-
flush=True,
1013-
end="",
1014-
)
1021+
1022+
if db["security_release"]:
1023+
waiting = f"\rWaiting for files: Linux {linux_tick} (security release - only checking Linux)"
1024+
else:
1025+
waiting = f"\rWaiting for files: Linux {linux_tick} Windows {windows_tick} Mac {macos_tick} "
1026+
1027+
print(waiting, flush=True, end="")
10151028
time.sleep(1)
10161029
print()
10171030

tests/test_release.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,11 @@ def test_tweak_readme(
136136

137137
# Assert
138138
new_contents = readme_file.read_text()
139-
new_lines = new_contents.splitlines()
139+
new_lines = new_contents.split("\n")
140140
assert new_lines[0] == expected_version
141141
assert new_lines[1] == expected_underline
142142
assert expected_whatsnew in new_contents
143143
assert expected_docs in new_contents
144144
assert expected_pep_line in new_contents
145+
assert original_contents.endswith("\n")
146+
assert new_contents.endswith("\n")

tests/test_release_tag.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import io
12
from subprocess import CompletedProcess
23

34
import pytest
@@ -140,3 +141,33 @@ def test_tag_long_name() -> None:
140141
assert rc.long_name == "3.13.0 release candidate 3"
141142
assert final_zero.long_name == "3.13.0"
142143
assert final_3.long_name == "3.13.3"
144+
145+
146+
@pytest.mark.parametrize(
147+
["version", "expected"],
148+
[
149+
("3.12.10", True),
150+
("3.13.3", False),
151+
],
152+
)
153+
def test_tag_is_security_release(
154+
version: str, expected: str, mocker: MockerFixture
155+
) -> None:
156+
# Arrange
157+
mock_response = b"""
158+
{
159+
"3.13": {
160+
"status": "bugfix"
161+
},
162+
"3.12": {
163+
"status": "security"
164+
}
165+
}
166+
"""
167+
mocker.patch("urllib.request.urlopen", return_value=io.BytesIO(mock_response))
168+
169+
# Act
170+
tag = release.Tag(version)
171+
172+
# Assert
173+
assert tag.is_security_release is expected

0 commit comments

Comments
 (0)