Repository: pypa/setuptools

Issue:
[BUG] Running the "egg_info" command without proper rights results in a cryptic and unhelpful error message
### setuptools version

setuptools == 65.5.1

### Python version

Python 3.8, 3.9

### OS

Ubuntu Mate

### Additional environment information

I used setuptools via pip. However, the issue is not dependent on how exactly one runs the egg_info command.

### Description

Develoiping a package, I tried to install it using pip with pyproject.toml specifying setuptools as a backend. After running
`pip install -e .`
I got an output with the following error:
` running egg_info
  error: [Errno 13] Permission denied
`
The problem is that this error does not show to what the permission is denied.

I tracked it down, and the output comes from the `utime` function in this part (lines296-988) of `setuptools/command/egg_info.py`
[(Click to see the file in the repo)](https://github.com/pypa/setuptools/blob/main/setuptools/command/egg_info.py#L297)
```python
def run(self):
        self.mkpath(self.egg_info)
        os.utime(self.egg_info, None)
```
So `os.utime` raises an `OSError` which is then printed out. Unfortunately, for some weird reason, the raised error does not contain a properly filled field `filename`, hence one needs a workaround to really print the file out

### Expected behavior

As usually with this kind of error, the expected output is something like:
`error: [Errno 13] Permission denied: /foo/bar/package/src/package-egg.info`

### How to Reproduce

[dist-err.zip](https://github.com/pypa/setuptools/files/9971742/dist-err.zip)
0. I assume that `pip` is installed.
1. Unzip the above very simple package project
2. Go to the main directory of the project (in which the `pyproject.toml` is)
3. run `sudo mkdir src/dist_err_pkg.egg-info` (the quickest way of creating a directory inaccessible for a standard user in ubuntu)
4. run `pip install .`

### Output

```console
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error
  
  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [2 lines of output]
      running egg_info
      error: [Errno 13] Permission denied
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> See above for output.

note: This error originates from a subprocess, and is likely not a problem with pip.

```



Discussion:
One possible way of fixing it would be to slightly amend the beginning of `run` from `setuptools/command/egg_info.py`. As follows:
```python
    def run(self):
        self.mkpath(self.egg_info)
        try: 
          os.utime(self.egg_info, None)
        except OSError as e:
          raise distutils.errors.DistutilsFileError(f"{self.egg_info}: {e}")
```
I am happy to help fixing this issue via something alike the above, if the maintainers agree that the solution is acceptable.
Hi @droodev, thank you very much for reporting this.

The proposed solution sounds interesting and simple. Would you like to provide a PR? (Personally I would keep the traceback with `raise ... from e`)
Hi @abravalheri,

I will provide a PR soon. Thanks for the feedback!

Resolving patch:
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -295,7 +295,11 @@ def delete_file(self, filename):
 
     def run(self):
         self.mkpath(self.egg_info)
-        os.utime(self.egg_info, None)
+        try:
+            os.utime(self.egg_info, None)
+        except OSError as e:
+            msg = f"Cannot update time stamp of directory '{self.egg_info}'"
+            raise distutils.errors.DistutilsFileError(msg) from e
         for ep in metadata.entry_points(group='egg_info.writers'):
             writer = ep.load()
             writer(self, ep.name, os.path.join(self.egg_info, ep.name))


Test patch:
diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py
--- a/setuptools/tests/test_egg_info.py
+++ b/setuptools/tests/test_egg_info.py
@@ -7,6 +7,7 @@
 import time
 from typing import List, Tuple
 from pathlib import Path
+from unittest import mock
 
 import pytest
 from jaraco import path
@@ -158,6 +159,16 @@ def test_expected_files_produced(self, tmpdir_cwd, env):
         ]
         assert sorted(actual) == expected
 
+    def test_handling_utime_error(self, tmpdir_cwd, env):
+        dist = Distribution()
+        ei = egg_info(dist)
+        with mock.patch('os.utime', side_effect=OSError("TEST")),\
+        mock.patch('setuptools.command.egg_info.egg_info.mkpath', return_val=None):
+            import distutils.errors
+            with pytest.raises(distutils.errors.DistutilsFileError, match =
+            r"Cannot update time stamp of directory 'None'"):
+                ei.run()
+
     def test_license_is_a_string(self, tmpdir_cwd, env):
         setup_config = DALS("""
             [metadata]