Skip to content
This repository has been archived by the owner on Jun 22, 2024. It is now read-only.

[pull] master from giampaolo:master #64

Open
wants to merge 75 commits into
base: master
Choose a base branch
from
Open

Conversation

pull[bot]
Copy link

@pull pull bot commented Jun 5, 2024

See Commits and Changes for more details.


Created by pull[bot]

Can you help keep this open source service alive? 💖 Please sponsor : )

@pull pull bot added the ⤵️ pull label Jun 5, 2024
giampaolo added 2 commits June 6, 2024 21:30
`Process.cmdline()` sometimes fail with EBUSY. It
usually happens for long cmdlines with lots of
arguments. In this case retry getting the
cmdline for up to 50 times, and return an empty
list as last resort.
@pull pull bot added the merge-conflict Resolve conflicts manually label Jun 8, 2024
@pull pull bot requested a review from ddelange June 8, 2024 00:08
giampaolo and others added 23 commits June 9, 2024 23:06
…hon (#2428)

* Fix GIL warnings and a few thread-safety issues in free-threaded CPython
* The temporary `argv` C array is no longer global in OpenBSD's
  proc_cmdline
* The `maxcpus` variable is no longer global in FreeBSD's per_cpu_times.

Signed-off-by: Sam Gross <[email protected]>
See #2446.

This PR:

* gets rid of [psutil/tests/runner.py](https://github.com/giampaolo/psutil/blob/622bd442eef704627202282d5805e4b39358b897/psutil/tests/runner.py#L1) (less code to maintain)
* use `pytest-xdist` to allow for parallel test execution
* get rid of [concurrencytest](https://pypi.org/project/concurrencytest/) dep
* update Github and Appveyor CI config
* removes 400 lines of code

Replacing `self.assert*` APIs will  be done in a separate PR.
* Add make install-sysdeps target. I want a standardized way to satisfy all system deps from a central point across all UNIX platforms. From now on CI config files should always rely on make install-sysdeps.
* Rename setup-dev-env make target to install-pydeps (these are deps intended for running tests)
* Re-enable BSD builds
giampaolo and others added 30 commits November 24, 2024 19:49
About dropping Python 2.7 support, 3 years ago [I stated](#2014 (comment)):

>  [...] not a chance, for many years to come. [Python 2.7] currently represents 7-10% of total downloads, aka around 70k /100k downloads per day

3 years later (and to my surprise) **downloads for Python 2.7 dropped to 0.36%**. These are downloads per month:

```
$ pypinfo --percent psutil pyversion
Served from cache: False
Data processed: 4.65 GiB
Data billed: 4.65 GiB
Estimated cost: $0.03

| python_version | percent | download_count |
| -------------- | ------- | -------------- |
| 3.10           |  23.84% |     26,354,506 |
| 3.8            |  18.87% |     20,862,015 |
| 3.7            |  17.38% |     19,217,960 |
| 3.9            |  17.00% |     18,798,843 |
| 3.11           |  13.63% |     15,066,706 |
| 3.12           |   7.01% |      7,754,751 |
| 3.13           |   1.15% |      1,267,008 |
| 3.6            |   0.73% |        803,189 |
| 2.7            |   0.36% |        402,111 |
| 3.5            |   0.03% |         28,656 |
| Total          |         |    110,555,745 |
```

According to [pypistats.org](https://archive.is/wip/knzql) it's 0.28% of the total, and around 15.000 downloads per day.

Maintaining 2.7 support has become increasingly difficult, but still possible. E.g. we can still run tests by using [old PYPI backports](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/setup.py#L76-L85). GitHub Actions can still be [tweaked](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/.github/workflows/build.yml#L77-L112) to run tests and produce wheels on Linux and macOS. Not Windows though, for which we have to use a separate service (Appveyor). 

Still, the amount of hacks in psutil source code necessary to support Python 2.7 piled up over the years, and became quite big. Some disadvantages that come to mind:

* (high) having to maintain various python compatibility layers (e.g. [psutil/_compat.py](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/psutil/_compat.py#L1)) + all the compromises that come with it (extra imports, extra code, str vs. unicode differences, etc.)
* (medium) having to maintain a C compatibility layer (`#if PY_MAJOR_VERSION <= 3`, etc.)
* (medium) inability to use modern language features, especially f-strings
* (low) inability to freely use `enum`s, which creates a difference on how CONSTANTS are exposed in terms of API
* (medium) having to install a specific version of pip and other (outdated) [deps](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/setup.py#L76-L85)
* (high) relying on third-party Appveyor CI service, just to run tests on python 2.7 and produce wheels, when we could rely on a single CI service instead (GitHub)
  * (high) soon I want to distribute wheels via GitHub instead of manually via `twine`, so that'll be a problem (CC @potiuk)
* (high) gradual lack of support from third-party libraries and services
* (medium) 4 extra CI jobs which are run on every commit (Linux, macOS, Windows 32-bit, Windows 64-bit) 
* (medium) the distribution of 7 wheels specific for Python 2.7. From last release: 
  * psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl
  * psutil-6.1.1-cp27-none-win32.whl
  * psutil-6.1.1-cp27-none-win_amd64.whl
  * psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl                                                                          
  * psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl                                                                        
  * psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl                                                                         
  * psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl                                                                       
* etc. 

As such I decided to finally **drop support for Python 2.7**. Current psutil 6.1.1 release will still support Python 2.7, but next 7.0.0 will not. 

We can still make a promise that the 6.1.* line (EDIT: see [python2 branch](https://github.com/giampaolo/psutil/tree/python2)) will keep supporting Python 2.7 and will **receive critical bug-fixes only** (no new features).

In 7.0.0 we can keep the [setup.py](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/setup.py) script compatible with Python 2.7 in terms of syntax, so that it can emit an informative error message on pip install. E.g. the user  will see something like this:

```
$ pip2 install psutil
As of version 7.0.0 psutil no longer supports Python 2.7. 
Latest version supporting Python 2.7 is psutil 6.1.X.  
Install it with: "pip2 install psutil==6.1.*".
```

Related tickets:
* 2017-06: #1053
* 2022-04: #2099
* 2023-04: #2246
* giampaolo/pyftpdlib#635
It could not be called anymore via:

$ python3 -m psutil
/usr/bin/python3: No module named psutil.__main__; 'psutil' is a package and cannot be directly executed

...plus code it's a duplicate of scripts/ps.py.
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:

```python
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:

```python
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:

```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)
```
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
⤵️ pull merge-conflict Resolve conflicts manually
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants