]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-137044: Make resource.RLIM_INFINITY always positive (GH-137511)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 18 Aug 2025 16:28:56 +0000 (19:28 +0300)
committerGitHub <noreply@github.com>
Mon, 18 Aug 2025 16:28:56 +0000 (19:28 +0300)
It is now a positive integer larger larger than any limited resource value.
This simplifies comparison of the resource values.
Previously, it could be negative, such as -1 or -3, depending on platform.

Deprecation warning is emitted if the old negative value is passed.

Doc/library/resource.rst
Doc/whatsnew/3.15.rst
Lib/test/test_resource.py
Misc/NEWS.d/next/Library/2025-08-07-12-32-23.gh-issue-137044.abNoIy.rst [new file with mode: 0644]
Modules/resource.c

index 0515d205bbca0bbf9e12ce0ebb9bfd331b0ce03f..0421b355056419f6f3c42a3306feedbfdf5d6a7c 100644 (file)
@@ -50,6 +50,11 @@ this module for those platforms.
 .. data:: RLIM_INFINITY
 
    Constant used to represent the limit for an unlimited resource.
+   Its value is larger than any limited resource value.
+
+   .. versionchanged:: next
+      It is now always positive.
+      Previously, it could be negative, such as -1 or -3.
 
 
 .. function:: getrlimit(resource)
index 81aa12184ed35c14b8a968c866f33d20bc5d7a76..eb073cae9be4919db1fd12f5f42aaad1735b49e1 100644 (file)
@@ -595,6 +595,12 @@ Porting to Python 3.15
   The |pythoncapi_compat_project| can be used to get most of these new
   functions on Python 3.14 and older.
 
+* :data:`resource.RLIM_INFINITY` is now always positive.
+  Passing a negative integer value that corresponded to its old value
+  (such as ``-1`` or ``-3``, depending on platform) to
+  :func:`resource.setrlimit` and :func:`resource.prlimit` is now deprecated.
+  (Contributed by Serhiy Storchaka in :gh:`137044`.)
+
 
 Deprecated C APIs
 -----------------
index fe05224828bd278acc98c5e45bb6a0f9ee0eb3b7..7391ce59da0ec4c181cb61a1db077f4d4201c24c 100644 (file)
@@ -40,7 +40,10 @@ class ResourceTest(unittest.TestCase):
         # we need to test that the get/setrlimit functions properly convert
         # the number to a C long long and that the conversion doesn't raise
         # an error.
+        self.assertGreater(resource.RLIM_INFINITY, 0)
         self.assertEqual(resource.RLIM_INFINITY, max)
+        self.assertLessEqual(cur, max)
+        resource.setrlimit(resource.RLIMIT_FSIZE, (max, max))
         resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max))
 
     @unittest.skipIf(sys.platform == "vxworks",
@@ -113,56 +116,53 @@ class ResourceTest(unittest.TestCase):
         self.addCleanup(resource.setrlimit, resource.RLIMIT_FSIZE, (cur, max))
 
         def expected(cur):
-            if resource.RLIM_INFINITY < 0:
-                return [(cur, max), (resource.RLIM_INFINITY, max)]
-            elif resource.RLIM_INFINITY < cur:
-                return [(resource.RLIM_INFINITY, max)]
-            else:
-                return [(cur, max)]
+            return (min(cur, resource.RLIM_INFINITY), max)
 
         resource.setrlimit(resource.RLIMIT_FSIZE, (2**31-5, max))
         self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**31-5, max))
+        resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max))
+        self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**31))
+        resource.setrlimit(resource.RLIMIT_FSIZE, (2**32-5, max))
+        self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32-5))
 
         try:
             resource.setrlimit(resource.RLIMIT_FSIZE, (2**32, max))
         except OverflowError:
-            resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max))
-            self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**31))
-            resource.setrlimit(resource.RLIMIT_FSIZE, (2**32-5, max))
-            self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32-5))
+            pass
         else:
-            self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32))
-            resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max))
-            self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**31, max))
-            resource.setrlimit(resource.RLIMIT_FSIZE, (2**32-5, max))
-            self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**32-5, max))
+            self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32))
 
             resource.setrlimit(resource.RLIMIT_FSIZE, (2**63-5, max))
-            self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63-5))
+            self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63-5))
             try:
                 resource.setrlimit(resource.RLIMIT_FSIZE, (2**63, max))
             except ValueError:
                 # There is a hard limit on macOS.
                 pass
             else:
-                self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63))
+                self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63))
                 resource.setrlimit(resource.RLIMIT_FSIZE, (2**64-5, max))
-                self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**64-5))
+                self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**64-5))
 
     @unittest.skipIf(sys.platform == "vxworks",
                      "setting RLIMIT_FSIZE is not supported on VxWorks")
     @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE')
     def test_fsize_negative(self):
+        self.assertGreater(resource.RLIM_INFINITY, 0)
         (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
         for value in -5, -2**31, -2**32-5, -2**63, -2**64-5, -2**1000:
             with self.subTest(value=value):
-                # This test assumes that the values don't map to RLIM_INFINITY,
-                # though Posix doesn't guarantee it.
-                self.assertNotEqual(value, resource.RLIM_INFINITY)
-
                 self.assertRaises(ValueError, resource.setrlimit, resource.RLIMIT_FSIZE, (value, max))
                 self.assertRaises(ValueError, resource.setrlimit, resource.RLIMIT_FSIZE, (cur, value))
 
+        if resource.RLIM_INFINITY in (2**32-3, 2**32-1, 2**64-3, 2**64-1):
+            value = (resource.RLIM_INFINITY & 0xffff) - 0x10000
+            with self.assertWarnsRegex(DeprecationWarning, "RLIM_INFINITY"):
+                resource.setrlimit(resource.RLIMIT_FSIZE, (value, max))
+            with self.assertWarnsRegex(DeprecationWarning, "RLIM_INFINITY"):
+                resource.setrlimit(resource.RLIMIT_FSIZE, (cur, value))
+
+
     @unittest.skipUnless(hasattr(resource, "getrusage"), "needs getrusage")
     def test_getrusage(self):
         self.assertRaises(TypeError, resource.getrusage)
diff --git a/Misc/NEWS.d/next/Library/2025-08-07-12-32-23.gh-issue-137044.abNoIy.rst b/Misc/NEWS.d/next/Library/2025-08-07-12-32-23.gh-issue-137044.abNoIy.rst
new file mode 100644 (file)
index 0000000..4bbf307
--- /dev/null
@@ -0,0 +1,4 @@
+:data:`resource.RLIM_INFINITY` is now always a positive integer larger than
+any limited resource value. This simplifies comparison of the resource
+values. Previously, it could be negative, such as -1 or -3, depending on
+platform.
index 2353bc6653abd881f11931859d2b7b9d4e9322b9..263730288c3dcfea166f210ee3a95b64d2b1a561 100644 (file)
@@ -164,7 +164,14 @@ py2rlim(PyObject *obj, rlim_t *out)
     if (bytes < 0) {
         return -1;
     }
-    else if (neg && (*out != RLIM_INFINITY || bytes > (Py_ssize_t)sizeof(*out))) {
+    else if (neg && *out == RLIM_INFINITY && bytes <= (Py_ssize_t)sizeof(*out)) {
+        if (PyErr_WarnEx(PyExc_DeprecationWarning,
+            "Use RLIM_INFINITY instead of negative limit value.", 1))
+        {
+            return -1;
+        }
+    }
+    else if (neg) {
         PyErr_SetString(PyExc_ValueError,
             "Cannot convert negative int");
         return -1;
@@ -210,9 +217,6 @@ error:
 static PyObject*
 rlim2py(rlim_t value)
 {
-    if (value == RLIM_INFINITY) {
-        return PyLong_FromNativeBytes(&value, sizeof(value), -1);
-    }
     return PyLong_FromUnsignedNativeBytes(&value, sizeof(value), -1);
 }