Skip to content

bpo-28724: Add methods send_fds, recv_fds to the socket module - #12889

Merged
vstinner merged 27 commits into
python:masterfrom
nanjekyejoannah:issue28724
Sep 11, 2019
Merged

bpo-28724: Add methods send_fds, recv_fds to the socket module#12889
vstinner merged 27 commits into
python:masterfrom
nanjekyejoannah:issue28724

Conversation

@nanjekyejoannah

@nanjekyejoannahnanjekyejoannah commented Apr 20, 2019

Copy link
Copy Markdown
Contributor

I have added send_fds(), recv_fds() to the socket module.

https://bugs.python.org/issue28724

@nanjekyejoannahnanjekyejoannah changed the title Add method send_fds, recv_fds to the socket modulebpo-28724: Add method send_fds, recv_fds to the socket moduleApr 20, 2019
@nanjekyejoannahnanjekyejoannah changed the title bpo-28724: Add method send_fds, recv_fds to the socket modulebpo-28724: Add methodS send_fds, recv_fds to the socket moduleApr 20, 2019
@nanjekyejoannahnanjekyejoannah changed the title bpo-28724: Add methodS send_fds, recv_fds to the socket modulebpo-28724: Add methods send_fds, recv_fds to the socket moduleApr 22, 2019
Comment threadDoc/library/socket.rst Outdated
Comment threadDoc/library/socket.rst Outdated
Comment threadLib/socket.py Outdated
Comment threadLib/test/test_socket.py Outdated
Comment threadLib/socket.py Outdated
Comment threadLib/socket.py Outdated
@bedevere-bot

Copy link
Copy Markdown

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. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

Comment threadLib/test/test_socket.py Outdated

Copy link
Copy Markdown
Member

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?

Copy link
Copy Markdown
ContributorAuthor

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.

Copy link
Copy Markdown
Member

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?

Comment threadLib/test/test_socket.py Outdated
Comment threadMisc/NEWS.d/next/Library/2019-05-10-19-27-07.bpo-28724.rHcm24.rst Outdated
@nanjekyejoannah

Copy link
Copy Markdown
ContributorAuthor

Needs, Mac OS fixes. Will get a Mac to test this later.

Comment threadLib/test/test_socket.py Outdated
@vstinner

Copy link
Copy Markdown
Member

@nanjekyejoannah: I think that these new functions are quite cool and I like to get them in Python 3.9. Do you plan to work on this PR? There are a few pending requests, but the main code is already written and has been reviewed, so this PR is close to be ready to be merged!

@nanjekyejoannah

Copy link
Copy Markdown
ContributorAuthor

@nanjekyejoannah: I think that these new functions are quite cool and I like to get them in Python 3.9. Do you plan to work on this PR? There are a few pending requests, but the main code is already written and has been reviewed, so this PR is close to be ready to be merged!

I will look at this later this week maybe weekendish.

@nanjekyejoannah

Copy link
Copy Markdown
ContributorAuthor

@pablogsal , Please help me confirm If the tests pass on MacOS now. I cant access a Mac now.

@nanjekyejoannah

Copy link
Copy Markdown
ContributorAuthor

I have made the requested changes; please review again

@bedevere-bot

Copy link
Copy Markdown

Thanks for making the requested changes!

@tiran, @pablogsal, @vstinner, @ncoghlan: please review the changes made to this pull request.

Comment threadLib/socket.py Outdated
import array

def send_fds(sock, buffers, fds, flags=0, address=None):
""" send_fds(sock, buffers, fds[, flags[, address]]) -> socket object

Copy link
Copy Markdown
Member

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.

Comment threadLib/socket.py Outdated
import array

def recv_fds(sock, bufsize, maxfds, flags=0):
""" recv_fds(sock, bufsize, maxfds[, flags]) -> (socket object, socket object)

Copy link
Copy Markdown
Member

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.

Comment threadLib/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

Copy link
Copy Markdown
Member

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?

Suggested change
receiveuptomaxfdsfiledescriptorsreturningthemessage
Receiveuptomaxfdsfiledescriptorsreturningthemessage

Comment threadLib/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)

Copy link
Copy Markdown
Member

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

Suggested change
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)

Comment threadDoc/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.

Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Member

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?

Copy link
Copy Markdown
ContributorAuthor

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.

Copy link
Copy Markdown
Member

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

@bedevere-bot

Copy link
Copy Markdown

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. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@nanjekyejoannah

Copy link
Copy Markdown
ContributorAuthor

I have made the requested changes; please review again

@bedevere-bot

Copy link
Copy Markdown

Thanks for making the requested changes!

@ncoghlan, @pablogsal, @vstinner, @tiran: please review the changes made to this pull request.

@vstinnervstinner left a comment

Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Member

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

Comment threadLib/test/test_socket.py Outdated
@requireAttrs(socket, "send_fds")
@requireAttrs(socket, "recv_fds")
@requireAttrs(socket, "AF_UNIX")
class SendRecvFdsTests(unittest.TestCase):

Copy link
Copy Markdown
Member

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().

Comment threadLib/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.

Copy link
Copy Markdown
Member

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?

Comment threadLib/socket.py Outdated
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.

Copy link
Copy Markdown
Member

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 ;-)

@bedevere-bot

Copy link
Copy Markdown

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. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@vstinner

Copy link
Copy Markdown
Member

The test is skipped. When I enabled manually the test, it failed on Linux :-(

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.

@nanjekyejoannah

nanjekyejoannah commented Sep 11, 2019

Copy link
Copy Markdown
ContributorAuthor

@vstinner

Test passes with your fix.

Ran 580 tests in 24.587s
OK (skipped=118)
== Tests result: SUCCESS ==
1 test OK.
Total duration: 24 sec 956 ms
Tests result: SUCCES

Also the test ir running:
testSendAndRecvFds (test.test_socket.SendRecvFdsTests) ... ok

@vstinner

Copy link
Copy Markdown
Member

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.

@vstinner

Copy link
Copy Markdown
Member

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

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nanjekyejoannah@bedevere-bot@the-knights-who-say-ni@vstinner@koobs@tiran@ncoghlan@pablogsal