Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/resource.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------------
Expand Down
44 changes: 22 additions & 22 deletions Lib/test/test_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Comment on lines +1 to +4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we test/guarantee that RLIM_INFINITY is bigger than limited values. AFAIK, POSIX would have no problem with RLIM_INFINITY being zero.
So, this should be:

Suggested change
: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.
:data:`resource.RLIM_INFINITY` is now always a positive integer.
Previously, it could be negative, such as -1 or -3, depending on
platform.

or:

Suggested change
: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.
:data:`resource.RLIM_INFINITY` is now always a positive integer.
On supported platforms, it is larger than any limited resource value,
which simplifies comparison of the resource values.
Previously, it could be negative, such as -1 or -3, depending on
platform.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://pubs.opengroup.org/onlinepubs/9799919799/functions/getrlimit.html

The value RLIM_INFINITY, defined in <sys/resource.h>, shall be considered to be larger than any other limit value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, I do not think this should have been merged without this change.

12 changes: 8 additions & 4 deletions Modules/resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
Loading