-
-
Notifications
You must be signed in to change notification settings - Fork 32k
bpo-28724: Add methods send_fds, recv_fds to the socket module #12889
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
Conversation
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
40d7ea4
to
f3e0822
Compare
Lib/test/test_socket.py
Outdated
@requireAttrs(socket.socket, 'send_fds') | ||
def testSendAndRecvFds(self): | ||
fds = [] | ||
for i in range(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.
Why we need a loop of 1 iteration?
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 want to return a list of 1 file descriptors for created files.
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 you don't need a loop for that, right? Just substitute i by 0. I am missing something?
Misc/NEWS.d/next/Library/2019-05-10-19-27-07.bpo-28724.rHcm24.rst
Outdated
Show resolved
Hide resolved
Needs, Mac OS fixes. Will get a Mac to test this later. |
@pablogsal , Please help me confirm If the tests pass on MacOS now. I cant access a Mac now. |
I have made the requested changes; please review again |
Thanks for making the requested changes! @tiran, @pablogsal, @vstinner, @ncoghlan: please review the changes made to this pull request. |
Lib/socket.py
Outdated
import array | ||
|
||
def send_fds(sock, buffers, fds, flags=0, address=None): | ||
""" send_fds(sock, buffers, fds[, flags[, address]]) -> socket object |
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 sendmsg() returns an integer, not a socket object.
Lib/socket.py
Outdated
import array | ||
|
||
def recv_fds(sock, bufsize, maxfds, flags=0): | ||
""" recv_fds(sock, bufsize, maxfds[, flags]) -> (socket object, socket object) |
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.
The doc of the return value looks wrong. The code uses a tuple of 4 times: return msg, list(fds), flags, addr
.
Lib/socket.py
Outdated
def recv_fds(sock, bufsize, maxfds, flags=0): | ||
""" recv_fds(sock, bufsize, maxfds[, flags]) -> (socket object, socket object) | ||
|
||
receive up to maxfds file descriptors returning the message |
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.
Upper case for the first word of the doc?
receive up to maxfds file descriptors returning the message | |
Receive up to maxfds file descriptors returning the message |
Lib/test/test_socket.py
Outdated
with sock1, sock2: | ||
socket.send_fds(sock1, [MSG], fds) | ||
# request more data and file descriptors than expected | ||
msg, fds2, flags, addr = socket.recv_fds(sock2, len(MSG) * 2, len(fds) * 2) |
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.
nitpick (PEP 8): there is an useless space
msg, fds2, flags, addr = socket.recv_fds(sock2, len(MSG) * 2, len(fds) * 2) | |
msg, fds2, flags, addr = socket.recv_fds(sock2, len(MSG) * 2, len(fds) * 2) |
Doc/library/socket.rst
Outdated
Receive up to *maxfds* file descriptors. Return ``(msg, list(fds), flags, addr)``. Consult | ||
:meth:`recvmsg` for the documentation of these parameters. | ||
|
||
.. availability:: Unix supporting :meth:`~socket.sendmsg` and :const:`SCM_RIGHTS` mechanism. |
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 requires recvmsg() instead of sendmsg(), no?
.. method:: socket.send_fds(sock, buffers, fds[, flags[, address]]) | ||
|
||
Send the list of file descriptors *fds* over an :const:`AF_UNIX` socket. | ||
Consult :meth:`sendmsg` for the documentation of these parameters. |
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 should document at least what is the fds parameter. I suggest to document it as sequence of file descriptors. Is it possible to send 0 file descriptor?
What is the return value?
Does someone know if it's possible that the call send only a few file descriptors, but not all of them? What if it's called with 100 000 file descriptors for example?
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.
What is the return value?
The return type for sendmsg()
is int AFAIK. Am not sure about these other edge cases but I can look.
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 tested to send 1000 file descriptors: sendmsg() fails with "OSError: [Errno 22] Invalid argument".
For partial send, it can happen, as in socket.send(). In that case, according to https://stackoverflow.com/questions/38431713/partial-read-write-issue-in-sendmsg-recvmsg the file descriptors are already sent and should be be send again, socket.sendmsg() should be called again, but only with buffers (adjusted to the offset equal to the send_fds() result).
In short, you don't have to do anything. I'm not comfortable to document how to handle partial send since I'm not sure that the behavior is portable.
sendmsg() doesn't warn about partial send. Maybe we should copy send() warning into sendmsg() and send_fds(): "Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data."
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
I have made the requested changes; please review again |
Thanks for making the requested changes! @ncoghlan, @pablogsal, @vstinner, @tiran: please review the changes made to this pull request. |
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.
The test is skipped. When I enabled manually the test, it failed on Linux :-(
.. method:: socket.send_fds(sock, buffers, fds[, flags[, address]]) | ||
|
||
Send the list of file descriptors *fds* over an :const:`AF_UNIX` socket. | ||
Consult :meth:`sendmsg` for the documentation of these parameters. |
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 tested to send 1000 file descriptors: sendmsg() fails with "OSError: [Errno 22] Invalid argument".
For partial send, it can happen, as in socket.send(). In that case, according to https://stackoverflow.com/questions/38431713/partial-read-write-issue-in-sendmsg-recvmsg the file descriptors are already sent and should be be send again, socket.sendmsg() should be called again, but only with buffers (adjusted to the offset equal to the send_fds() result).
In short, you don't have to do anything. I'm not comfortable to document how to handle partial send since I'm not sure that the behavior is portable.
sendmsg() doesn't warn about partial send. Maybe we should copy send() warning into sendmsg() and send_fds(): "Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data."
Lib/test/test_socket.py
Outdated
@requireAttrs(socket, "send_fds") | ||
@requireAttrs(socket, "recv_fds") | ||
@requireAttrs(socket, "AF_UNIX") | ||
class SendRecvFdsTests(unittest.TestCase): |
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.
Oh, this test case must be added manually into test_main(). Currently, the test is not run...
I added it manually and... the test fails :-( The test writes into the read end of the pipe, and reads from the write end of the pipe. It should be the opposite. Maybe on FreeBSD, pipe ends are not directional, but it seems to be the case on Linux.
So the test should be fixed on Linux a well.
Please move this class before test_main().
Lib/socket.py
Outdated
_socket.CMSG_LEN(maxfds * fds.itemsize)) | ||
for cmsg_level, cmsg_type, cmsg_data in ancdata: | ||
if (cmsg_level == _socket.SOL_SOCKET and cmsg_type == _socket.SCM_RIGHTS): | ||
# Append data, ignoring any truncated integers at the end. |
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.
@nanjekyejoannah: Would you mind to document this behavior in recv_fds() documention?
Lib/socket.py
Outdated
@@ -12,6 +12,8 @@ | |||
socket() -- create a new socket object | |||
socketpair() -- create a pair of new socket objects [*] | |||
fromfd() -- create a socket object from an open file descriptor [*] | |||
send_fds() -- Send file descriptor to the socket. | |||
recv_fds() -- Recieve file descriptor from the 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.
I suggest to replace "file descriptor" with "file descriptors" for both functions here (add an S), as in their name, since they can send and receive more than one file descriptor ;-)
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
test_main() is awful... and it's no longer needed: unittest.main() can be used instead. I created https://bugs.python.org/issue38063 "Modify test_socket.py to use unittest test discovery" and I wrote a PR to implement it. |
Test passes with your fix.
Also the test ir running: |
I tested manually commit 1ac7fbf on Linux and FreeBSD. I merged the PR with the master branch, make && ./python -m test test_socket -m SendRecvFdsTests -v . The test pass on Linux and FreeBSD. Good! According to Azure Pipelines: test_socket also pass on macOS (and the test is no longer skipped on macOS). test_socket pass on Windows as well, even if the test is skipped on Windows. |
Thanks Shinya Okano for the original patch. Well done Joannah @nanjekyejoannah! Thanks for your tenacity :-) This PR has 100 comments and 27 commits which shows the complexity of the feature. Honestly, I'm not 100% happy with current documentation, but I chose to merge the PR anyway. Please leave https://bugs.python.org/issue28724 open until the documentation is completed to mention corner cases like partial send (similar to sock.send vs sock.sendall). |
…thonGH-12889) The socket module now has the socket.send_fds() and socket.recv.fds() functions. Contributed by Joannah Nanjekye, Shinya Okano (original patch) and Victor Stinner. Co-Authored-By: Victor Stinner <[email protected]>
I have added send_fds(), recv_fds() to the socket module.
https://bugs.python.org/issue28724