diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index e98e52c2ea2045..bcf7f7f3fd1e1f 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -603,6 +603,27 @@ def test_install_scripts_selinux(self): venv.create(self.env_dir) listxattr_mock.assert_not_called() + @unittest.skipIf(os.name == 'nt', 'POSIX-specific permission test') + def test_install_scripts_honors_umask(self): + """ + gh-127172: Activation scripts must inherit the process umask (and any + default ACL) like every other created file, rather than a fixed mode + copied from the source template. + """ + old_umask = os.umask(0o002) + try: + venv.create(self.env_dir, with_pip=False) + bindir = os.path.join(self.env_dir, self.bindir) + control = os.path.join(bindir, 'control') + with open(control, 'w'): + pass + expected = os.stat(control).st_mode & 0o777 + for name in ('activate', 'activate.csh', 'activate.fish'): + mode = os.stat(os.path.join(bindir, name)).st_mode & 0o777 + self.assertEqual(mode, expected) + finally: + os.umask(old_umask) + def test_overwrite_existing(self): """ Test creating environment in an existing directory. diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index bd2762d55ef696..86d980dbbcc408 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -592,11 +592,10 @@ def skip_file(f): 'may be binary: %s', srcfile, e) continue if new_data == data: - shutil.copy(srcfile, dstfile) + shutil.copyfile(srcfile, dstfile) else: with open(dstfile, 'wb') as f: f.write(new_data) - shutil.copymode(srcfile, dstfile) def upgrade_dependencies(self, context): logger.debug( diff --git a/Misc/NEWS.d/next/Library/2026-07-29-12-20-39.gh-issue-127172.Kf3Xa9.rst b/Misc/NEWS.d/next/Library/2026-07-29-12-20-39.gh-issue-127172.Kf3Xa9.rst new file mode 100644 index 00000000000000..c4bf1f07a2ea57 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-29-12-20-39.gh-issue-127172.Kf3Xa9.rst @@ -0,0 +1,4 @@ +Creating a virtual environment with :mod:`venv` now writes the activation +scripts using permissions derived from the process umask (and any default +ACL), consistent with the other files it creates, instead of a fixed mode +copied from the packaged templates. Patch by tonghuaroot.