diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst index 0515d205bbca0b..0421b355056419 100644 --- a/Doc/library/resource.rst +++ b/Doc/library/resource.rst @@ -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) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 93f56eed857068..82c2f200c74e49 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -560,6 +560,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 ----------------- diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index fe05224828bd27..7391ce59da0ec4 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -40,7 +40,10 @@ def test_fsize_ismax(self): # 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 @@ def test_fsize_not_too_big(self): 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 index 00000000000000..4bbf3075dfcafe --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-07-12-32-23.gh-issue-137044.abNoIy.rst @@ -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. diff --git a/Modules/resource.c b/Modules/resource.c index 2353bc6653abd8..263730288c3dcf 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -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 @@ py2rlimit(PyObject *limits, struct rlimit *rl_out) static PyObject* rlim2py(rlim_t value) { - if (value == RLIM_INFINITY) { - return PyLong_FromNativeBytes(&value, sizeof(value), -1); - } return PyLong_FromUnsignedNativeBytes(&value, sizeof(value), -1); }