Skip to content

Commit b7e4d49

Browse files
authored
Merge branch '3.10' into backport-acfe02f-3.10
2 parents 122e7da + bd3939e commit b7e4d49

8 files changed

Lines changed: 156 additions & 15 deletions

File tree

.readthedocs.yml

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,60 @@
1-
# This is a dummy config file so that readthedocs.org doesn't fail on security branches.
2-
# Note that this won't result in docs actually getting built;
3-
# clicking on the docs preview link on a PR will result in a 404.
1+
# Read the Docs configuration file
2+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3+
# Project page: https://readthedocs.org/projects/cpython-previews/
4+
45
version: 2
5-
formats: []
6+
67
sphinx:
7-
configuration: Doc/conf.py
8+
configuration: Doc/conf.py
9+
810
build:
9-
os: "ubuntu-22.04"
11+
os: ubuntu-24.04
1012
tools:
11-
python: "3.11"
13+
python: "3.10"
14+
apt_packages:
15+
- jq
16+
1217
jobs:
13-
post_checkout:
14-
- exit 183
18+
post_system_dependencies:
19+
# https://docs.readthedocs.com/platform/stable/guides/build/skip-build.html#skip-builds-based-on-conditions
20+
#
21+
# Cancel building pull requests when there are no changes in the Doc
22+
# directory or RTD configuration, or if we can't cleanly merge the base
23+
# branch.
24+
- |
25+
set -eEux;
26+
if [ "$READTHEDOCS_VERSION_TYPE" = "external" ];
27+
then
28+
base_branch=$(wget -qO- "https://api.github.com/repos/python/cpython/pulls/$READTHEDOCS_VERSION" | jq -er ".base.ref");
29+
git fetch --depth=50 origin $base_branch:origin-$base_branch;
30+
for attempt in $(seq 10);
31+
do
32+
if ! git merge-base HEAD origin-$base_branch;
33+
then
34+
git fetch --deepen=50 origin $base_branch;
35+
else
36+
break;
37+
fi;
38+
done;
39+
if ! git -c "user.name=rtd" -c "user.email=no-reply@readthedocs.org" merge --no-stat --no-edit origin-$base_branch;
40+
then
41+
echo "Unsuccessful merge with '$base_branch' branch, skipping the build";
42+
exit 183;
43+
fi;
44+
if git diff --exit-code --stat origin-$base_branch -- Doc/ .readthedocs.yml;
45+
then
46+
echo "No changes to Doc/ - skipping the build.";
47+
exit 183;
48+
fi;
49+
fi;
50+
create_environment:
51+
- echo "Skipping default environment creation"
52+
install:
53+
- asdf plugin add uv
54+
- asdf install uv latest
55+
- asdf global uv latest
56+
build:
57+
html:
58+
- make -C Doc venv html
59+
- mkdir -p "$READTHEDOCS_OUTPUT"
60+
- mv Doc/build/html "$READTHEDOCS_OUTPUT/"

Doc/using/windows.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,9 @@ on using nuget. What follows is a summary that is sufficient for Python
397397
developers.
398398

399399
The ``nuget.exe`` command line tool may be downloaded directly from
400-
``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the
401-
tool, the latest version of Python for 64-bit or 32-bit machines is installed
402-
using::
400+
``https://dist.nuget.org/win-x86-commandline/latest/nuget.exe``, for example,
401+
using curl or PowerShell. With the tool, the latest version of Python for
402+
64-bit or 32-bit machines is installed using::
403403

404404
nuget.exe install python -ExcludeVersion -OutputDirectory .
405405
nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory .

Lib/tarfile.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2401,7 +2401,8 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False,
24012401
tarinfo, unfiltered = self._get_extract_tarinfo(
24022402
member, filter_function, path)
24032403
if tarinfo is not None:
2404-
self._extract_one(tarinfo, path, set_attrs, numeric_owner)
2404+
self._extract_one(tarinfo, path, set_attrs, numeric_owner,
2405+
filter_function=filter_function)
24052406

24062407
def _get_extract_tarinfo(self, member, filter_function, path):
24072408
"""Get (filtered, unfiltered) TarInfos from *member*

Lib/test/test_tarfile.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4040,6 +4040,96 @@ def test_chmod_outside_dir(self):
40404040
st_mode = cc.outerdir.stat().st_mode
40414041
self.assertNotEqual(st_mode & 0o777, 0o777)
40424042

4043+
@unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
4044+
@unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown")
4045+
@unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
4046+
@support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE))
4047+
def test_chown_links_on_extract(self, link_type):
4048+
with ArchiveMaker() as arc:
4049+
arc.add("test.txt",
4050+
uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
4051+
arc.add("link",
4052+
type=link_type,
4053+
linkname='test.txt',
4054+
uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
4055+
4056+
with (
4057+
os_helper.temp_dir() as tmpdir,
4058+
arc.open() as tar,
4059+
unittest.mock.patch("os.chown") as mock_chown,
4060+
unittest.mock.patch("os.lchown") as mock_lchown,
4061+
unittest.mock.patch("os.geteuid") as mock_geteuid,
4062+
):
4063+
# Set UID to 0 so chown() is attempted.
4064+
mock_geteuid.return_value = 0
4065+
tar.extract("link", path=tmpdir, filter='data')
4066+
extract_path = os.path.join(tmpdir, "link")
4067+
4068+
if link_type == tarfile.SYMTYPE:
4069+
mock_chown.assert_not_called()
4070+
mock_lchown.assert_called_once_with(extract_path, -1, -1)
4071+
else:
4072+
mock_chown.assert_has_calls([
4073+
unittest.mock.call(extract_path, -1, -1),
4074+
unittest.mock.call(extract_path, -1, -1)
4075+
])
4076+
mock_lchown.assert_not_called()
4077+
4078+
@unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
4079+
@unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown")
4080+
@unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
4081+
@support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE))
4082+
def test_chown_links_on_extractall(self, link_type):
4083+
with ArchiveMaker() as arc:
4084+
arc.add("test.txt",
4085+
uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
4086+
arc.add("link",
4087+
type=link_type,
4088+
linkname='test.txt',
4089+
uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
4090+
4091+
with (
4092+
os_helper.temp_dir() as tmpdir,
4093+
arc.open() as tar,
4094+
unittest.mock.patch("os.chown") as mock_chown,
4095+
unittest.mock.patch("os.lchown") as mock_lchown,
4096+
unittest.mock.patch("os.geteuid") as mock_geteuid,
4097+
):
4098+
# Set UID to 0 so chown() is attempted.
4099+
mock_geteuid.return_value = 0
4100+
tar.extractall(path=tmpdir, filter='data')
4101+
extract_link_path = os.path.join(tmpdir, "link")
4102+
extract_file_path = os.path.join(tmpdir, "test.txt")
4103+
4104+
if link_type == tarfile.SYMTYPE:
4105+
mock_chown.assert_called_once_with(extract_file_path, -1, -1)
4106+
mock_lchown.assert_called_once_with(extract_link_path, -1, -1)
4107+
else:
4108+
mock_chown.assert_has_calls([
4109+
unittest.mock.call(extract_file_path, -1, -1),
4110+
unittest.mock.call(extract_link_path, -1, -1)
4111+
])
4112+
mock_lchown.assert_not_called()
4113+
4114+
def test_extract_filters_target(self):
4115+
# Test that when extract() falls back to extracting (rather than
4116+
# linking) a hardlink target, it filters the target.
4117+
with ArchiveMaker() as arc:
4118+
arc.add("target")
4119+
arc.add("link", hardlink_to="target")
4120+
def testing_filter(member, path):
4121+
if member.name == 'target':
4122+
# target: set read-only
4123+
return member.replace(mode=stat.S_IRUSR)
4124+
# link: don't overwrite the mode
4125+
return member.replace(mode=None)
4126+
tempdir = pathlib.Path(TEMPDIR) / 'extract'
4127+
with os_helper.temp_dir(tempdir), arc.open() as tar:
4128+
tar.extract("link", path=tempdir, filter=testing_filter)
4129+
path = tempdir / 'link'
4130+
if sys.platform != 'win32':
4131+
self.assertFalse(path.stat().st_mode & stat.S_IWUSR)
4132+
40434133
def test_link_fallback_normalizes(self):
40444134
# Make sure hardlink fallbacks work for non-normalized paths for all
40454135
# filters
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Update Windows build and installer tooling and documentation to use the
2+
current download URL for ``nuget.exe``.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The :meth:`tarfile.TarFile.extract` method now applies the given filter when
2+
it extracts a link target from the archive as a fallback.

PCbuild/find_python.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
@set _Py_HOST_PYTHON=%HOST_PYTHON%
4646
@if "%_Py_HOST_PYTHON%"=="" set _Py_HOST_PYTHON=py
4747
@if "%_Py_NUGET%"=="" (set _Py_NUGET=%_Py_EXTERNALS_DIR%\nuget.exe)
48-
@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://aka.ms/nugetclidl)
48+
@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe)
4949
@if NOT exist "%_Py_NUGET%" (
5050
@echo Downloading nuget...
5151
@rem NB: Must use single quotes around NUGET here, NOT double!

Tools/msi/get_externals.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set HERE=%~dp0
66
if "%PCBUILD%"=="" (set PCBUILD=%HERE%..\..\PCbuild\)
77
if "%EXTERNALS_DIR%"=="" (set EXTERNALS_DIR=%HERE%..\..\externals\windows-installer)
88
if "%NUGET%"=="" (set NUGET=%EXTERNALS_DIR%\..\nuget.exe)
9-
if "%NUGET_URL%"=="" (set NUGET_URL=https://aka.ms/nugetclidl)
9+
if "%NUGET_URL%"=="" (set NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe)
1010

1111
set DO_FETCH=true
1212
set DO_CLEAN=false

0 commit comments

Comments
 (0)