Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved exceptions (raise from ...) #2487

Merged
merged 14 commits into from
Dec 21, 2024
Merged

Improved exceptions (raise from ...) #2487

merged 14 commits into from
Dec 21, 2024

Conversation

giampaolo
Copy link
Owner

@giampaolo giampaolo commented Dec 21, 2024

As part of the dropping of Python 2.7 support (#2480), we can now take advantage of chained exceptions machinery (raise x from y and raise x from None). In practical terms, this is what changes:

Shorter tracebacks

When adding the full traceback info adds no value, we now shorten tracebacks if raise X from None.
A similar (hackish) attempt was made in 633d8019, when we were still stuck with Python 2. The notable example is passing a PID that does not exist to the Process class:

psutil.Process(333)

Before we got:

Traceback (most recent call last):
  File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1647, in wrapper
    return fun(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_common.py", line 464, in wrapper
    raise err from None
  File "/home/giampaolo/svn/psutil/psutil/_common.py", line 462, in wrapper
    return fun(self)
           ^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1713, in _parse_stat_file
    data = bcat(f"{self._procfs_path}/{self.pid}/stat")
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_common.py", line 794, in bcat
    return cat(fname, fallback=fallback, _open=open_binary)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_common.py", line 782, in cat
    with _open(fname) as f:
         ^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_common.py", line 746, in open_binary
    return open(fname, "rb", buffering=FILE_READ_BUFFER_SIZE)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/proc/341244/stat'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 350, in _init
    self._ident = self._get_ident()
                  ^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 391, in _get_ident
    return (self.pid, self.create_time())
                      ^^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 773, in create_time
    self._create_time = self._proc.create_time()
                        ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1647, in wrapper
    return fun(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1885, in create_time
    ctime = float(self._parse_stat_file()['create_time'])
                  ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1659, in wrapper
    raise NoSuchProcess(pid, name) from err
psutil.NoSuchProcess: process no longer exists (pid=341244)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/giampaolo/svn/psutil/foo.py", line 5, in <module>
    psutil.Process(341244)
  File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 317, in __init__
    self._init(pid)
  File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 363, in _init
    raise NoSuchProcess(pid, msg=msg)
psutil.NoSuchProcess: process PID not found (pid=341244)

Now we get:

Traceback (most recent call last):
  File "/home/giampaolo/svn/psutil/foo.py", line 5, in <module>
    psutil.Process(341244)
  File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 317, in __init__
    self._init(pid)
  File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 363, in _init
    raise NoSuchProcess(pid, msg=msg) from None
psutil.NoSuchProcess: process PID not found (pid=341244)

Different wording for "translated" exceptions

By "translated" I mean psutil's NoSuchProcess, ZombieProcess and AccessDenied.
Given the following code:

import psutil
from psutil.tests import spawn_testproc

sproc = spawn_testproc()
p = psutil.Process(sproc.pid)
p.terminate()
p.wait()
p.name()

Before we got:

Traceback (most recent call last):
  [...]
  File "/home/giampaolo/svn/psutil/psutil/_common.py", line 746, in open_binary
    return open(fname, "rb", buffering=FILE_READ_BUFFER_SIZE)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/proc/105496/stat'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  [...]
  File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1659, in wrapper
    raise NoSuchProcess(pid, name)
psutil.NoSuchProcess: process no longer exists (pid=105496)

Now we get:

Traceback (most recent call last):
  [...]
  File "/home/giampaolo/svn/psutil/psutil/_common.py", line 746, in open_binary
    return open(fname, "rb", buffering=FILE_READ_BUFFER_SIZE)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/proc/105496/stat'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  [...]
  File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1659, in wrapper
    raise NoSuchProcess(pid, name) from None
psutil.NoSuchProcess: process no longer exists (pid=105496)

Diff:

     FileNotFoundError: [Errno 2] No such file or directory: '/proc/105496/stat'
 
-    During handling of the above exception, another exception occurred:
+    The above exception was the direct cause of the following exception:
 
     Traceback (most recent call last):
       [...]
       File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1659, in wrapper
-        raise NoSuchProcess(pid, name)
+        raise NoSuchProcess(pid, name) from None
     psutil.NoSuchProcess: process no longer exists (pid=105496)

Signed-off-by: Giampaolo Rodola <[email protected]>
When passing a non existent PID to Process(), we now get a very compact traceback:

    ```
    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/foo.py", line 5, in <module>
        p = psutil.Process(123124)
            ^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 317, in __init__
        self._init(pid)
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 363, in _init
        raise NoSuchProcess(pid, msg=msg) from None
    psutil.NoSuchProcess: process PID not found (pid=123124)
    ```

Before we got:

```
    <traceback object at 0x74d0dd6ddb40>
    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 327, in _init
        _psplatform.cext.check_pid_range(pid)
    OverflowError: Python int too large to convert to C long

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/foo.py", line 5, in <module>
        p = psutil.Process(132798127392783333912839812793)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 317, in __init__
        self._init(pid)
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 332, in _init
        raise NoSuchProcess(pid, msg=msg).with_traceback(tb)
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 327, in _init
        _psplatform.cext.check_pid_range(pid)
    psutil.NoSuchProcess: process PID out of range (pid=132798127392783333912839812793)
    ~/svn/psutil {raise-from}$ ^C
    ~/svn/psutil {raise-from}$ ^C
    ~/svn/psutil {raise-from}$ python3 foo.py
    <traceback object at 0x7ef45b787800>
    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 327, in _init
        _psplatform.cext.check_pid_range(pid)
    OverflowError: Python int too large to convert to C long

    The above exception was the direct cause of the following exception:

    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/foo.py", line 5, in <module>
        p = psutil.Process(132798127392783333912839812793)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 317, in __init__
        self._init(pid)
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 332, in _init
        raise NoSuchProcess(pid, msg=msg) from e
    psutil.NoSuchProcess: process PID out of range (pid=132798127392783333912839812793)
    ~/svn/psutil {raise-from}$ ^C
    ~/svn/psutil {raise-from}$ python3 foo.py
    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1647, in wrapper
        return fun(self, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_common.py", line 464, in wrapper
        raise err from None
      File "/home/giampaolo/svn/psutil/psutil/_common.py", line 462, in wrapper
        return fun(self)
               ^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1713, in _parse_stat_file
        data = bcat(f"{self._procfs_path}/{self.pid}/stat")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_common.py", line 794, in bcat
        return cat(fname, fallback=fallback, _open=open_binary)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_common.py", line 782, in cat
        with _open(fname) as f:
             ^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_common.py", line 746, in open_binary
        return open(fname, "rb", buffering=FILE_READ_BUFFER_SIZE)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    FileNotFoundError: [Errno 2] No such file or directory: '/proc/123124/stat'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 350, in _init
        self._ident = self._get_ident()
                      ^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 391, in _get_ident
        return (self.pid, self.create_time())
                          ^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 773, in create_time
        self._create_time = self._proc.create_time()
                            ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1647, in wrapper
        return fun(self, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1885, in create_time
        ctime = float(self._parse_stat_file()['create_time'])
                      ^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/_pslinux.py", line 1659, in wrapper
        raise NoSuchProcess(self.pid, self._name)
    psutil.NoSuchProcess: process no longer exists (pid=123124)

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/foo.py", line 5, in <module>
        p = psutil.Process(123124)
            ^^^^^^^^^^^^^^^^^^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 317, in __init__
        self._init(pid)
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 363, in _init
        raise NoSuchProcess(pid, msg=msg)
    psutil.NoSuchProcess: process PID not found (pid=123124)
```
Code:

```
p = psutil.Popen(["ls"])
p.foo
```

Trceback now:

```
    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/foo.py", line 16, in <module>
        p.foo()
        ^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 1442, in __getattribute__
        raise AttributeError(msg) from None
    AttributeError: <class 'psutil.Popen'> class no attribute 'foo'
```

Traback before:

```
    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 1436, in __getattribute__
        return object.__getattribute__(self, name)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    AttributeError: 'Popen' object has no attribute 'foo'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 1439, in __getattribute__
        return object.__getattribute__(self.__subproc, name)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    AttributeError: 'Popen' object has no attribute 'foo'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/home/giampaolo/svn/psutil/foo.py", line 16, in <module>
        p.foo()
        ^^^^^
      File "/home/giampaolo/svn/psutil/psutil/__init__.py", line 1445, in __getattribute__
        raise AttributeError(msg)
    AttributeError: Popen instance has no attribute 'foo'
```
@giampaolo giampaolo merged commit 7403704 into master Dec 21, 2024
26 of 27 checks passed
@giampaolo giampaolo deleted the raise-from branch December 21, 2024 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant