gh-137044: Make resource.RLIM_INFINITY always positive (GH-137511)

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.
This commit is contained in:
Serhiy Storchaka 2025-08-18 19:28:56 +03:00 committed by GitHub
parent 138ed6db9f
commit 0324c726de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 45 additions and 26 deletions

View file

@ -50,6 +50,11 @@ this module for those platforms.
.. data:: RLIM_INFINITY .. data:: RLIM_INFINITY
Constant used to represent the limit for an unlimited resource. 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) .. function:: getrlimit(resource)

View file

@ -595,6 +595,12 @@ Porting to Python 3.15
The |pythoncapi_compat_project| can be used to get most of these new The |pythoncapi_compat_project| can be used to get most of these new
functions on Python 3.14 and older. 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 Deprecated C APIs
----------------- -----------------

View file

@ -40,7 +40,10 @@ def test_fsize_ismax(self):
# we need to test that the get/setrlimit functions properly convert # 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 # the number to a C long long and that the conversion doesn't raise
# an error. # an error.
self.assertGreater(resource.RLIM_INFINITY, 0)
self.assertEqual(resource.RLIM_INFINITY, max) self.assertEqual(resource.RLIM_INFINITY, max)
self.assertLessEqual(cur, max)
resource.setrlimit(resource.RLIMIT_FSIZE, (max, max))
resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max))
@unittest.skipIf(sys.platform == "vxworks", @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)) self.addCleanup(resource.setrlimit, resource.RLIMIT_FSIZE, (cur, max))
def expected(cur): def expected(cur):
if resource.RLIM_INFINITY < 0: return (min(cur, resource.RLIM_INFINITY), max)
return [(cur, max), (resource.RLIM_INFINITY, max)]
elif resource.RLIM_INFINITY < cur:
return [(resource.RLIM_INFINITY, max)]
else:
return [(cur, max)]
resource.setrlimit(resource.RLIMIT_FSIZE, (2**31-5, max)) resource.setrlimit(resource.RLIMIT_FSIZE, (2**31-5, max))
self.assertEqual(resource.getrlimit(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: try:
resource.setrlimit(resource.RLIMIT_FSIZE, (2**32, max)) resource.setrlimit(resource.RLIMIT_FSIZE, (2**32, max))
except OverflowError: except OverflowError:
resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max)) pass
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))
else: else:
self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32)) self.assertEqual(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))
resource.setrlimit(resource.RLIMIT_FSIZE, (2**63-5, max)) 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: try:
resource.setrlimit(resource.RLIMIT_FSIZE, (2**63, max)) resource.setrlimit(resource.RLIMIT_FSIZE, (2**63, max))
except ValueError: except ValueError:
# There is a hard limit on macOS. # There is a hard limit on macOS.
pass pass
else: 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)) 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", @unittest.skipIf(sys.platform == "vxworks",
"setting RLIMIT_FSIZE is not supported on VxWorks") "setting RLIMIT_FSIZE is not supported on VxWorks")
@unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE') @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE')
def test_fsize_negative(self): def test_fsize_negative(self):
self.assertGreater(resource.RLIM_INFINITY, 0)
(cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE)
for value in -5, -2**31, -2**32-5, -2**63, -2**64-5, -2**1000: for value in -5, -2**31, -2**32-5, -2**63, -2**64-5, -2**1000:
with self.subTest(value=value): 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, (value, max))
self.assertRaises(ValueError, resource.setrlimit, resource.RLIMIT_FSIZE, (cur, value)) 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") @unittest.skipUnless(hasattr(resource, "getrusage"), "needs getrusage")
def test_getrusage(self): def test_getrusage(self):
self.assertRaises(TypeError, resource.getrusage) self.assertRaises(TypeError, resource.getrusage)

View file

@ -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.

View file

@ -164,7 +164,14 @@ py2rlim(PyObject *obj, rlim_t *out)
if (bytes < 0) { if (bytes < 0) {
return -1; 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, PyErr_SetString(PyExc_ValueError,
"Cannot convert negative int"); "Cannot convert negative int");
return -1; return -1;
@ -210,9 +217,6 @@ py2rlimit(PyObject *limits, struct rlimit *rl_out)
static PyObject* static PyObject*
rlim2py(rlim_t value) rlim2py(rlim_t value)
{ {
if (value == RLIM_INFINITY) {
return PyLong_FromNativeBytes(&value, sizeof(value), -1);
}
return PyLong_FromUnsignedNativeBytes(&value, sizeof(value), -1); return PyLong_FromUnsignedNativeBytes(&value, sizeof(value), -1);
} }