Repository: python/cpython
Pull request: gh-115596: Fix `ProgramPriorityTests` in `test_os` permanently changing the process priority
Fixes #115596

Runs the priority setting logic from `ProgramPriorityTests.test_set_get_priority`  in a subprocess to prevent the priority change from taking permanent effect.

<!-- gh-issue-number: gh-115596 -->
* Issue: gh-115596
<!-- /gh-issue-number -->


Problem statement:
test_os permanently change the process priority
# Bug report

ProgramPriorityTests in test_os changes the process priority (makes it less prioritized in the scheduler) and then tries to restore the old value, why is fail unless the test is ran as root. It affects all further tests if run in sequential mode and all further tests in test_os if run in parallel mode. It can makes the testing process less responsible and make some tests (especially relying on timeouts or interruption) unstable.

This test should be run in a separate subprocess.

<!-- gh-linked-prs -->
### Linked PRs
* gh-115610
* gh-115616
* gh-115617
<!-- /gh-linked-prs -->

Hints from issue discussion:

Commit message: Use assert_python_ok for better readout on subprocess failure

Patch to review:
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index ce778498c61d87..5b38c1c4f7e49a 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -3505,24 +3505,24 @@ def test_getlogin(self):
 class ProgramPriorityTests(unittest.TestCase):
     """Tests for os.getpriority() and os.setpriority()."""
 
+    @support.requires_subprocess()
     def test_set_get_priority(self):
-
         base = os.getpriority(os.PRIO_PROCESS, os.getpid())
-        os.setpriority(os.PRIO_PROCESS, os.getpid(), base + 1)
-        try:
-            new_prio = os.getpriority(os.PRIO_PROCESS, os.getpid())
-            # nice value cap is 19 for linux and 20 for FreeBSD
-            if base >= 19 and new_prio <= base:
-                raise unittest.SkipTest("unable to reliably test setpriority "
-                                        "at current nice level of %s" % base)
-            else:
-                self.assertEqual(new_prio, base + 1)
-        finally:
-            try:
-                os.setpriority(os.PRIO_PROCESS, os.getpid(), base)
-            except OSError as err:
-                if err.errno != errno.EACCES:
-                    raise
+        code = f"""if 1:
+        import os
+        os.setpriority(os.PRIO_PROCESS, os.getpid(), {base} + 1)
+        print(os.getpriority(os.PRIO_PROCESS, os.getpid()))
+        """
+
+        # Subprocess inherits the current process' priority.
+        _, out, _ = assert_python_ok("-c", code)
+        new_prio = int(out.decode("utf-8"))
+        # nice value cap is 19 for linux and 20 for FreeBSD
+        if base >= 19 and new_prio <= base:
+            raise unittest.SkipTest("unable to reliably test setpriority "
+                                    "at current nice level of %s" % base)
+        else:
+            self.assertEqual(new_prio, base + 1)
 
 
 @unittest.skipUnless(hasattr(os, 'sendfile'), "test needs os.sendfile()")
diff --git a/Misc/NEWS.d/next/Tests/2024-02-17-08-25-01.gh-issue-115596.RGPCrR.rst b/Misc/NEWS.d/next/Tests/2024-02-17-08-25-01.gh-issue-115596.RGPCrR.rst
new file mode 100644
index 00000000000000..2bcb8b9ac6bcd4
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2024-02-17-08-25-01.gh-issue-115596.RGPCrR.rst
@@ -0,0 +1,2 @@
+Fix ``ProgramPriorityTests`` in ``test_os`` permanently changing the process
+priority.


Code review comments:
diff_hunk: @@ -3505,24 +3505,24 @@ def test_getlogin(self):
 class ProgramPriorityTests(unittest.TestCase):
     """Tests for os.getpriority() and os.setpriority()."""
 
+    @support.requires_subprocess()
     def test_set_get_priority(self):
-
         base = os.getpriority(os.PRIO_PROCESS, os.getpid())
-        os.setpriority(os.PRIO_PROCESS, os.getpid(), base + 1)
-        try:
-            new_prio = os.getpriority(os.PRIO_PROCESS, os.getpid())
-            # nice value cap is 19 for linux and 20 for FreeBSD
-            if base >= 19 and new_prio <= base:
-                raise unittest.SkipTest("unable to reliably test setpriority "
-                                        "at current nice level of %s" % base)
-            else:
-                self.assertEqual(new_prio, base + 1)
-        finally:
-            try:
-                os.setpriority(os.PRIO_PROCESS, os.getpid(), base)
-            except OSError as err:
-                if err.errno != errno.EACCES:
-                    raise
+        code = f"""if 1:
+        import os
+        os.setpriority(os.PRIO_PROCESS, os.getpid(), {base} + 1)
+        print(os.getpriority(os.PRIO_PROCESS, os.getpid()))
+        """
+
+        # Subprocess inherits the current process' priority.
+        _, out, _ = assert_python_ok("-c", code)
+        new_prio = int(out.decode("utf-8"))
path: Lib/test/test_os.py
text: @user1:
`int()` accepts bytestrings.
```suggestion
        new_prio = int(out)
```