-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-32373: Add socket.getblocking() method. #4926
Conversation
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.
Please add self.assertFalse(self.serv.getblocking()) in testInitNonBlocking(), or write a new test to ensure that socket created with SOCK_NONBLOCK is non-blocking: getblocking() is false.
Doc/library/socket.rst
Outdated
|
||
Return ``True`` if socket is in blocking mode, ``False`` if in | ||
non-blocking. This reflects the last call to :meth:`setblocking` | ||
or :meth:`settimeout`. |
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 reflects the last call to :meth:setblocking
or :meth:settimeout
."
What abou the following case?
>>> import socket; socket.socket(type=socket.SOCK_STREAM | socket.SOCK_NONBLOCK).getblocking()
False
I suggest to remove the second sentence.
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.
Sure
There're extensive tests for that, see the updated |
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. You may document the new change in Doc/whatsnew/3.7.rst.
PyDoc_STRVAR(getblocking_doc, | ||
"getblocking()\n\ | ||
\n\ | ||
Returns True if socket is in blocking mode, or False if it\n\ |
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.
Very minor thing: moving if it
to next line increases docstrict readability imho.
Modules/socketmodule.c
Outdated
@@ -2583,7 +2605,7 @@ sock_settimeout(PySocketSockObject *s, PyObject *arg) | |||
return NULL; | |||
|
|||
s->sock_timeout = timeout; | |||
if (internal_setblocking(s, timeout < 0) == -1) { | |||
if (internal_setblocking(s, timeout != 0) == -1) { |
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.
Hum, wait, what is this change? Timeouts > 0 now set the socket is blocking mode?
Would you mind to document this subtle behaviour change in settimeout() documentation?
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.
Yes. I'm still working on it. Turns out sendfile currently tests test sendfile on non-blocking sockets, but at the same time the documentation says that it's not supported for non-blocking sockets at all. It's a mess.
3b8074e
to
5f1cf87
Compare
# If timeout > 0, the socket will be in a "blocking" mode | ||
# from the standpoint of the Python API. For Python socket | ||
# object, "blocking" means that operations like 'sock.recv()' | ||
# will block. Internally, file descriptors for |
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 don't think there's any point in explaining this inside test_socket
.
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.
In this test it makes sense, because I use fcntl to check the blocking mode of the underlying FD. Usually comments don't hurt in tests.
if (internal_setblocking(s, timeout < 0) == -1) { | ||
|
||
int block = timeout < 0; | ||
/* Blocking mode for a Python socket object means that operations |
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.
Hmm, I'm sure that comment can be made much shorter :-)
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 write your version?
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.
Sure:
/* When a socket socket has a non-infinite timeout,
* its underlying fd is internally set to non-blocking.
* This table summarizes [etc.]
*/
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.
Hm, that's too short :) But I'll trim down my version.
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.
Pushed an update.
Doc/library/socket.rst
Outdated
a :exc:`BlockingIOError` if they cannot complete immediately. | ||
|
||
Internally, if a Python socket object has a positive timeout, its | ||
underlying file descriptor (FD) is switched to a non-blocking mode. |
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 wary of documenting internals here. It could be quite confusing to the reader, and doesn't seem to provide any useful information.
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.
Yeah, but my confusion with this thing came from me trying to check if the FD is blocking/nonblocking with fcntl.fcntl(sock, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK
, and from there it's was quite a painful experience to discover how socket objects really work in Python (probably because I was debugging a failing sendfile test after some changes, but whatever).
I agree that all this information might overwhelm an average Python user who just wants to read the documentation. On the other hand, socket
module is usually used by more or less advanced Python users.
That said, I'd be OK to keep this detailed documentation piece in socketmodule.c
if everyone thinks it's too much to have it 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.
I think it would be best located in socketmodule.c
indeed. Not sure where.
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.
Let's keep it in sock_settimeout()
, as it's the central place that triggers blocking/non-blocking modes.
I remove my vote since Yury rewrote his PR after I approved it
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 don't think that getblocking() doc is the right place to explain in depth the behaviour of all timeouts and blocking/non-blocking modes.
getblocking() should be a simple as: return True if the timeout is equal to zero, False otherwise.
Maybe the long description of blocking/non-blocking should even be a separated PR?
When you're done making the requested changes, leave the comment: |
@@ -0,0 +1 @@ | |||
Add socket.getblocking() method. |
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 seems like you modified settimeout() behaviour. It must be documented here and in settimeout() doc.
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 wrong, settimeout() is (no longer) modified by this PR.
My comment was for a previous version of the PR. The PR evolved too quickly, I was lost, sorry.
The PR is in the exact same shape as it was the moment you approved it + few comments. I've pushed a few commits experimenting with different approaches, and later removed those experimental commits.
That's exactly what is in the PR. |
@@ -0,0 +1 @@ | |||
Add socket.getblocking() method. |
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 wrong, settimeout() is (no longer) modified by this PR.
My comment was for a previous version of the PR. The PR evolved too quickly, I was lost, sorry.
.. method:: socket.getblocking() | ||
|
||
Return ``True`` if socket is in blocking mode, ``False`` if in | ||
non-blocking. |
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.
Victor: "getblocking() should be a simple as: return True if the timeout is equal to zero, False otherwise."
Yury: "That's exactly what is in the PR."
Hum, this doc doesn't mention the timeout but refers to a "blocking mode" which is not well defined for timeout > 0.
The socket module mentions 3 modes: blocking, non-blocking and "timeout mode":
https://docs.python.org/dev/library/socket.html#notes-on-socket-timeouts
Replace "if socket is in blocking mode" with "if the timeout is equal to zero", or replace "is in blocking mode" with "is in blocking or timeout mode"?
By the way, there is already a note: "At the operating system level, sockets in timeout mode are internally set in non-blocking mode."
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 think the docstring here should say "This is equivalent to gettimeout() == 0
", just like the docstring for setblocking
explains that it's equivalent to a certain settimeout
call.
self.serv.setblocking(False) | ||
self.assertEqual(self.serv.gettimeout(), 0.0) | ||
self.assertFalse(self.serv.getblocking()) | ||
if fcntl: | ||
self.assertFalse(_is_fd_in_blocking_mode(self.serv)) |
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 may write an helper function which makes sure that timeout, blocking and SOCK_NONBLOCK flag are consistent.
Something like:
def check_timeout(sock, timeout):
assert sock.gettimeout() == timeout
assert sock.getblocking() == (timeout == 0.0)
assert _is_fd_in_blocking_mode(sock) == (timeout != 0.0)
(I'm not sure of these assertions :-))
==================== ===================== ============== | ||
``None`` ``True`` blocking | ||
``0.0`` ``False`` non-blocking | ||
``> 0`` ``True`` non-blocking |
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.
Since this is internal C docs, it might be helpful to add a column to the table for s->sock_timeout
values – IIUC the rows would be < 0
, 0.0
, and > 0
respectively, but it took some grovelling through the source to figure that out.
@vstinner Do you have any objections here? I'd like to merge this to 3.7 and I don't see any open questions regarding this PR. Reorganizing tests to make them prettier is fine, but we can surely do it later. |
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.
If I recall correctly, I voted -1 on a previous because setblocking() was modified. It's not more the case. Moreover, the new doc is also better now.
"getblocking()\n\ | ||
\n\ | ||
Returns True if socket is in blocking mode, or False if it\n\ | ||
is in non-blocking mode."); |
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 may add "This is equivalent to checking socket.gettimeout() == 0
." here as well.
@1st1: Please replace |
Thanks, Victor! |
https://bugs.python.org/issue32373