Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.4k
gh-107545: Fix misleading setsockopt error message#107546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
bedevere-bot commentedAug 1, 2023
Most changes to Pythonrequire a NEWS entry. Please add it using theblurb_it web app or theblurb command-line tool. |
ghost commentedAug 1, 2023 • edited by ghost
Loading Uh oh!
There was an error while loading.Please reload this page.
edited by ghost
Uh oh!
There was an error while loading.Please reload this page.
naweiss commentedAug 1, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I choose to solve the issue using a white-list approach rather then a black-list approach since there might be more errors that Error message for: importsocketwithsocket.socket()ass:s.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,2**31) is now |
Looks good, how about adding some tests for new error messages. |
Misc/NEWS.d/next/Core and Builtins/2023-08-01-22-30-35.gh-issue-107545.f9i3pQ.rst OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
I added the relevant tests. Any new review comments? |
CharlieZhao95 commentedAug 7, 2023 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Considering this is your first PR in cpython, please be patient! The next step is waiting for the review by core devs. Generally, core developers don't have enough time to review every PR, so you can see there are many PRs that can't be merged into the main. Before they come to review, we just need to keep our PR compliant and wait :) |
python-cla-botbot commentedApr 18, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Since this PR is old, I validated that the issue is still reproducible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
LGTM, but I think that we can get rid of_testcapi
.
The more user friendly approach is to parse arguments as"iiOO|O"
and then use== Py_None
,PyIndex_Check()
andPyBuffer_Check()
for the third argument. This will allow to generate better error messages like "... should be integer, bytes-like object or None".
Lib/test/test_socket.py Outdated
@unittest.skipIf(_testcapi is None, "requires _testcapi") | ||
def test_setsockopt_errors(self): | ||
# See issue #107546. | ||
from _testcapi import INT_MAX, INT_MIN |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We could just use some large values like2**1000
and-2**1000
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'm scared by big numbers,2**100
and-2**100
should be enough to trigger OverflowError on all platforms :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
But not on platforms with 128-bitint
. I often use2**1000
as more future proof option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Does Python support any platform with 128-bit integer? Does such platform exist?
Thanks for the feedback. |
Modules/socketmodule.c Outdated
@@ -3338,26 +3338,32 @@ sock_setsockopt(PyObject *self, PyObject *args) | |||
Py_buffer optval; | |||
int flag; | |||
unsigned int optlen; | |||
PyObject *none; | |||
PyObject *type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Semantically, this is not type, but value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I choose the nameoptval
for all cases which access the value of the option.
Uh oh!
There was an error while loading.Please reload this page.
Lib/test/test_socket.py Outdated
with self.assertRaises(OverflowError): | ||
sock.setsockopt(2 ** 100, socket.SO_REUSEADDR, 1) | ||
with self.assertRaises(TypeError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
You can test the error message as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Good Idea, I also created some more type checks and added their exact message to the tests code.
Thank you for your update,@naweiss. Here is the next portion of comments. |
I noticed this too. We need to look at the history of the code. Anyway, this is a different issue. |
Modules/socketmodule.c Outdated
if (!PyArg_ParseTuple(args, "iiO:setsockopt", | ||
&level, &optname, &optval)) { | ||
return NULL; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Can you please move this test for None and 4 arguments here?
if (optval == Py_None) { PyErr_Format(...) }
Modules/socketmodule.c Outdated
} | ||
break; | ||
case 4: | ||
if (!PyArg_ParseTuple(args, "iiO!I:setsockopt", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
if (!PyArg_ParseTuple(args,"iiO!I:setsockopt", | |
if (!PyArg_ParseTuple(args,"iiOI:setsockopt", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This ensures that the 3rd argument would be of the specified type.
In my casePy_TYPE(Py_None)
that way I ensure that if we have 4 arguments the 3rd must beNone
.
Modules/socketmodule.c Outdated
@@ -3374,36 +3397,45 @@ sock_setsockopt(PyObject *self, PyObject *args) | |||
goto done; | |||
} | |||
PyErr_Clear(); | |||
/* setsockopt(level, opt, None, flag) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
/* setsockopt(level, opt, None,flag) */ | |
/* setsockopt(level, opt, None,optlen) */ |
Modules/socketmodule.c Outdated
res = setsockopt(get_sock_fd(s), level, optname, | ||
buffer.buf, (int)buffer.len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
res=setsockopt(get_sock_fd(s),level,optname, | |
buffer.buf, (int)buffer.len); | |
res=setsockopt(get_sock_fd(s),level,optname, | |
buffer.buf, (int)buffer.len); |
Modules/socketmodule.c Outdated
&level, &optname, Py_TYPE(Py_None), &optval, &optlen)) { | ||
return NULL; | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We needif (optval != Py_None) {error...}
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Look athttps://github.com/python/cpython/pull/107546/files#r2192102447.
The "O!" format along withPy_TYPE(Py_None)
ensures that the type of the 3rd argument would beNone
.
Lib/test/test_socket.py Outdated
sock.setsockopt(2 ** 100, socket.SO_REUSEADDR, 1) | ||
msg = "socket option should be should be integer, bytes-like object or None" | ||
with self.assertRaises(TypeError, msg=msg): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
It should beassertRaisesRegex
. And the expected error message is a regular expression, so you need to escape parentheses.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Modules/socketmodule.c Outdated
arglen = PyTuple_Size(args); | ||
if (arglen == 3 && optval == Py_None) { | ||
PyErr_Format(PyExc_TypeError, | ||
"setsockopt() take 4 arguments when socket option is None (%zd given)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Would not the following be better: "setsockopt() requires 4 arguments when the third argument is None"?
Modules/socketmodule.c Outdated
} | ||
if (arglen == 4 && optval != Py_None) { | ||
PyErr_Format(PyExc_TypeError, | ||
"setsockopt() argument 3 must be NoneType, not %T", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
"setsockopt() takes 3 arguments when the third argument is not None" or "setsockopt() only takes 4 arguments when the third argument is None"?
PyObject *none; | ||
PyObject *optval; | ||
if (!PyArg_ParseTuple(args, "iiO|I:setsockopt", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
All this should be after the code for AF_VSOCK. Otherwise we will get wrong error messages for AF_VSOCK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thedocs for setsockopt state there are 3 different overloads and does not mention anything different forAF_VSOCK
.
So, I think that the old error messages where misleading:
>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, 1, 2, 3)TypeError: setsockopt() takes exactly 3 arguments (5 given)>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, None, 1)TypeError: setsockopt() takes exactly 3 arguments (4 given)>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, b"aaa")TypeError: setsockopt() argument 3 must be int, not bytes
It weird that suddenly the error message tell you that setsockopt only supports 3 arguments although there is overload with 4. I personally would expect thatAF_VSOCK
would still allowbuffer
/None
argument (like in my implementation) but would raise an error telling the user that this are unsupported types specifically forAF_VSOCK
. Something like this:
>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, 1, 2, 3)TypeError: setsockopt() takes at most 4 arguments (5 given)>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, None, 1)TypeError: setsockopt() argument 3 for AF_VSOCK must only be an int (got None)>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, b"aaa")TypeError: setsockopt() argument 3 for AF_VSOCK must only be an int (got bytes)
Uh oh!
There was an error while loading.Please reload this page.
According to Python's documentation,
setsockopt
can get eitherint
,buffer
orNone
as the third argument.The way it is currently implemented is by trying to parse all of the three arguments as ints, if this failsno matter what the reason is we just try the next overload.
Since
2**31
causesPyExc_OverflowError
we try the next overloads. Because thebuffer
overload is last we get type error for the third argument instead ofOverflowError
(e.g.TypeError: a bytes-like object is required, not 'int'
).setsockopt
error message #107545