diff --git a/news/8164.bugfix b/news/8164.bugfix new file mode 100644 index 00000000000..1707d28401a --- /dev/null +++ b/news/8164.bugfix @@ -0,0 +1 @@ +Fix metadata permission issues when umask has the executable bit set. diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py index e7315ee4b52..2fb86b866db 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -567,7 +567,7 @@ def is_entrypoint_wrapper(name): if msg is not None: logger.warning(msg) - generated_file_mode = 0o666 - current_umask() + generated_file_mode = 0o666 & ~current_umask() @contextlib.contextmanager def _generate_file(path, **kwargs): diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py index 2350927541d..b64d4cef312 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -243,15 +243,15 @@ def assert_permission(self, path, mode): target_mode = os.stat(path).st_mode & 0o777 assert (target_mode & mode) == mode, oct(target_mode) - def assert_installed(self): + def assert_installed(self, expected_permission): # lib assert os.path.isdir( os.path.join(self.scheme.purelib, 'sample')) # dist-info metadata = os.path.join(self.dest_dist_info, 'METADATA') - self.assert_permission(metadata, 0o644) + self.assert_permission(metadata, expected_permission) record = os.path.join(self.dest_dist_info, 'RECORD') - self.assert_permission(record, 0o644) + self.assert_permission(record, expected_permission) # data files data_file = os.path.join(self.scheme.data, 'my_data', 'data_file') assert os.path.isfile(data_file) @@ -268,7 +268,28 @@ def test_std_install(self, data, tmpdir): scheme=self.scheme, req_description=str(self.req), ) - self.assert_installed() + self.assert_installed(0o644) + + @pytest.mark.parametrize("user_mask, expected_permission", [ + (0o27, 0o640) + ]) + def test_std_install_with_custom_umask(self, data, tmpdir, + user_mask, expected_permission): + """Test that the files created after install honor the permissions + set when the user sets a custom umask""" + + prev_umask = os.umask(user_mask) + try: + self.prep(data, tmpdir) + wheel.install_wheel( + self.name, + self.wheelpath, + scheme=self.scheme, + req_description=str(self.req), + ) + self.assert_installed(expected_permission) + finally: + os.umask(prev_umask) def test_std_install_with_direct_url(self, data, tmpdir): """Test that install_wheel creates direct_url.json metadata when @@ -340,7 +361,7 @@ def test_dist_info_contains_empty_dir(self, data, tmpdir): req_description=str(self.req), _temp_dir_for_testing=self.src, ) - self.assert_installed() + self.assert_installed(0o644) assert not os.path.isdir( os.path.join(self.dest_dist_info, 'empty_dir'))