-
-
Notifications
You must be signed in to change notification settings - Fork 343
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
Simplify sockets #670
Simplify sockets #670
Conversation
I will go ahead and do a static implementation to have a usable example on which we can judge about. I am aware that the socket constants are highly volatile, but at the moment this is our only option if we want code completion to be working. |
I think you're making this more complicated than you need to :-) For the first version, we want to:
|
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.
There's some irrelevant formatting churn here, not sure why... I guess yapf is acting up again. Kind of annoying, but not a big deal.
A number of small comments below.
A larger question, where I'm not sure of the answer: right now the way this PR is structured, we import all the ALLCAPS constants from socket
→ trio._socket
, and then we import them again from trio._socket
→ trio.socket
. Maybe it would be simpler to take them out of trio._socket
, and instead import them directly socket
→ trio.socket
?
What makes this complicated is that there are some constants that are missing from socket
, so we define them ourselves. And one of those constants, IPPROTO_IPV6
, we actually use inside trio._socket
. So we can't just move all this stuff to trio.socket
.
I guess it would still be simpler to have everything except IPPROTO_IPV6
imported directly from socket
→ trio.socket
, and just special-case IPPROTO_IPV6
?
(@Zac-HD, any thoughts here?)
trio/_socket.py
Outdated
from functools import wraps as _wraps | ||
|
||
_stdlib_socket = _importlib.import_module('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.
Why are you using importlib
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.
Any use of importlib removed again, as there is no benefit and it makes the code only harder to read.
trio/_socket.py
Outdated
try: | ||
from socket import ( | ||
if_nameindexif_nametoindex, if_indextoname, sethostname | ||
) |
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 you're missing a comma in if_nameindexif_nametoindex
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 thought I had fixed this already but I'll take a look again.
But this typo shows the weakness of this approach. I had to put these four into a try catch block because on py3.5 they are not there. Actually I should put each of the names in a try except block of its own but that would make the code even uglier.
So we do not only have a constant version/os dependency but also for functions.
I can see you are somehow reluctant towards a star import with overwriting which I now would prefer but I still do not know why exactly. Now you have about 25 years more experience than me so this is very likely well founded, I'd just like to understand why.
trio/_socket.py
Outdated
# # we only get here if the sync call in fact did fail with a | ||
# # BlockingIOError | ||
# return await do_it_properly_with_a_check_point() | ||
# |
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 did you delete this comment?
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.
Comment reintroduced deletion was an accident, held it for my own. I never touch comment of others.
trio/_socket.py
Outdated
except ImportError: | ||
if _sys.platform == "darwin": | ||
IPPROTO_SCTP = 132 | ||
globals()['IPPROTO_SCTP'] = IPPROTO_SCTP |
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.
Huh, is IPPROTO_SCTP
something you need for something? Not sure how it relates to this PR...
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.
Removed all unnecessary definitions from _socket.py. I did also just remove the import of SO_REUSEADDR from _socket.py as it is not used and deleted it in socket.py.
trio/_socket.py
Outdated
IPPROTO_IPV6 = 41 | ||
__all__.append("IPPROTO_IPV6") | ||
globals()['IPPROTO_IPV6'] = IPPROTO_IPV6 |
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 globals()
assignment here doesn't do anything :-). We're not inside a function, so when we wrote IPPROTO_IPV6 = 41
it already assigned to globals.
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.
Removed the noop statements.
trio/_socket.py
Outdated
@@ -310,13 +293,12 @@ def fromfd(*args, **kwargs): | |||
if hasattr(_stdlib_socket, "fromshare"): | |||
|
|||
@_wraps(_stdlib_socket.fromshare, assigned=(), updated=()) | |||
@_add_to_all | |||
# @_add_to_all |
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 delete this line instead of leaving it commented out.
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.
@jmfrank63 in general, please be sure that new pushes are addressing outstanding comments. Otherwise it seems that github automatically marks the comments as resolved, and they get lost. I think this was a pervasive communication problem on your last PR.
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.
@belm0 Thanks for letting me know, I will go through all of them and address them in the next push. There is so much to learn, thanks for the patience. I will also try to attempt squashing this time, as there is so much back and forth.
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.
Thank you for considering. (And again it's dubious that GitHub behaves that way.)
About squashing, that can be done trivially by the person doing the final merge into master.
trio/_socket.py
Outdated
@@ -549,6 +550,7 @@ def shutdown(self, flag): | |||
# empty list. | |||
assert len(gai_res) >= 1 | |||
# Address is the last item in the first entry | |||
# normed = gai_res[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.
Not sure this comment is adding much :-)
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 put it in because it does the same as your assignment but pylint doesn't complain. Was meant as a reminder to change this one day so we have one complaint less.
(actually this one was the last standing) But this should be very likely if at all a different PR. Right?
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, does pylint complain about the unpacking assignment? That's weird, it seems more readable to me...
And yes, better to separate it out in any case.
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, it did complain about too many stars in expression. Just FYI.
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.
Huh, yeah, looks like a pylint bug – it's supposed to catch illegal code like *a, *b = c
, but it also fires on valid code like this. Filed at pylint-dev/pylint#2513
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.
Reported 16 hours ago, fixed an hour ago, how do you do this?
Besides I would have never considered this a bug in pylint. So amazing.
Anyways, removed the comment again.
trio/socket.py
Outdated
# This is a public namespace, so we don't want to expose any non-underscored | ||
# attributes that aren't actually part of our public API. But it's very | ||
# This is a public namespace, so we dont want to expose any non-underscored | ||
# attributes that arent actually part of our public API. But its very |
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 did you delete the apostrophes?
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.
Back in, deleted by accident when doing a replace all.
trio/socket.py
Outdated
from ._socket import __all__ | ||
import importlib | ||
|
||
_smod = importlib.import_module('._socket', 'trio') |
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.
Probably clearer to write from . import _socket as _smod
(or just from . import _socket
, since it already has an underscore on the name).
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, done. I tried to pull directly into socket.py whatever I could. Interestingly the python interactive shell with jedi configured does have full code completion, while ipython hangs. Either some implications of pip install -e or just bugs.
Anyways it currently looks good.
trio/socket.py
Outdated
fromfd, from_stdlib_socket, getprotobyname, socketpair, getnameinfo, | ||
socket, getaddrinfo, set_custom_hostname_resolver, | ||
set_custom_socket_factory, real_socket_type, SocketType, fspath, | ||
run_sync_in_worker_thread |
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.
Some of these imports look strange... real_socket_type
wasn't public before, was it? and run_sync_in_worker_thread
is definitely not supposed to be exposed from trio.socket
... did you check that this PR doesn't change the things exported from trio.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.
Removed. Shouldn't be in the next push.
(not subscribed to this thread since know almost nothing about sockets, but @ me and I'll respond!)
I would certainly find it more tasteful to import them directly from
Define as many as possible in
Yep 😥 |
trio/_core/_io_windows.py
Outdated
@@ -341,8 +341,8 @@ def monitor_completion_key(self): | |||
if sock in self._socket_waiters[which]: | |||
await _core.checkpoint() | |||
raise _core.ResourceBusyError( | |||
"another task is already waiting to {} this socket" | |||
.format(which) | |||
"another task is already waiting to {} this 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.
suggest that unrelated code formatting be done in a separate PR
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.
@belm0 Oh, I didn't touch that module at all, guess that came from yapf and it did slip in with a git add --all
which I learned I shouldn't do. I'll have to check how to revert a single file of a commit to redo this change, I guess that should be possible.
Hmmh, didn't touch any of the tests, and in theory this message should be filtered out at line 596. |
trio/_socket.py
Outdated
if _sys.platform == "darwin": | ||
TCP_NOTSENT_LOWAT = 0x201 | ||
elif _sys.platform == "linux": | ||
TCP_NOTSENT_LOWAT = 25 |
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 whole block of code should just move into trio/socket.py
, with the other constant export stuff. IPPROTO_IPV6
has to stay in this file because we actually use it down below, but we don't use TCP_NOTSENT_LOWAT
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.
Makes sense, moved to socket.py
trio/socket.py
Outdated
try: | ||
del SO_REUSEADDR | ||
except NameError: | ||
pass |
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 just do del SO_REUSEADDR
without any try
/except
. It happens that SO_REUSEADDR
is always available, and you can see this by looking at the coverage report – the except NameError
lines are never executed: https://codecov.io/gh/python-trio/trio/src/2ce378d3dd3597022847d9ced06187ed799037f1/trio/socket.py
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.
Done.
trio/socket.py
Outdated
try: | ||
IPPROTO_IPV6 | ||
except NameError: | ||
from ._socket import IPPROTO_IPV6 |
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 can just be from ._socket import IPPROTO_IPV6
without the try
/except
, since _socket
has to make sure it's unconditionally defined.
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.
try/except removed, let's see if we still pass...
trio/socket.py
Outdated
TCP_NOTSENT_LOWAT | ||
except NameError: | ||
if _sys.platform != 'win32': | ||
from ._socket import TCP_NOTSENT_LOWAT |
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 block should be replaced by the code from trio/_socket.py
, as noted above.
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.
Done.
trio/_socket.py
Outdated
# As of at least 3.6, python on Windows is missing IPPROTO_IPV6 | ||
# https://bugs.python.org/issue29515 | ||
if not hasattr(_stdlib_socket, "IPPROTO_IPV6"): # pragma: no branch | ||
if _sys.platform == "win32": # pragma: no branch |
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.
According to https://codecov.io/gh/python-trio/trio/pull/670/changes you don't need pragma: no branch
: this is covered by the Windows tests.
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.
@pquentin I removed the comment. How do I learn about codecov? What would be some good resources to read?
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.
@jmfrank63 Nothing that I know of, sorry :(
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.
Just two small changes below, and then I think this is ready!
I also confirmed manually that this doesn't change the set of exported symbols from socket
(at least on my system), by comparing the output of
python -c 'import trio; [print(n) for n in sorted(dir(trio.socket)) if not n.startswith("_")]'
before and after the change.
newsfragments/670.feature.rst
Outdated
@@ -0,0 +1 @@ | |||
* Reworked :mod:`trio.socket` namespace construction, making it more understandable by static analysis tools. This should improve tab completion in editors, reduce false positives from pylint, and is another step towards providing type hints. |
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.
Thinking about it, it's going to be kind of weird-looking in our final change log to have multiple almost-identical entries with just the module name changed.
Since #542 is already an umbrella bug for these changes, how about we drop this file, and instead modify the existing 542.feature.rst
to cover the new work as well? And while we're at it, we can add mention of the work in #656 to fix trio.testing
... so it can say something like:
"Reworked :mod:trio
, :mod:trio.testing
, and :mod:trio.socket
namespace construction, making them more understandable..."
Also BTW, newsfragments shouldn't start with a *
. Towncrier will automatically add a *
when it assembles the release notes, and then things will look weird if we have two *
s :-)
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, makes sense. I'll correct this later this day, currently commuting. I actually put this in in haste to have a change, before I learned that I could actually do an empty commit.
trio/_core/_run.py
Outdated
"without some sort of compatibility shim." | ||
.format(async_fn) | ||
"without some sort of compatibility shim.". | ||
format(async_fn) |
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 upgraded yapf (#694), so I think you need to merge from master and then re-run yapf (using 0.24.0) before we can merge. Hopefully that will fix these weird formatting issues too...
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.
Guess what ;-) now yapf fails with yapf: Unknown style option "SPLIT_BEFORE_DOT"
... Oh, maybe I should upgrade yapf as well. Nevermind false alarm.
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, you definitely need yapf 0.24.0
Well, I filed google/yapf#624 anyway for this annoying thing in |
Looks like we are fine now, any fine tuning possibly? |
Good to see checks are finally passing ;-) I guess we're getting close now. |
newsfragments/542.feature.rst
Outdated
Reworked :mod:trio, :mod:trio.testing, and :mod:trio.socket namespace construction, making them more understandable by static analysis tools. This should improve tab completion in editors, reduce false positives from pylint, and is a first step towards providing type hints. |
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 need the backticks around the module names :-) It's a restructured text thing: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#roles
Reworked :mod:`trio`, :mod:`trio.testing`, and :mod:`trio.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.
Ups, the last 1% is 99% of the work :-)
🎉 🎈 |
This is code to look at as part of #542 It seems static analysis tools are not able to pick up any dynamically added attributes.
The only chance if we want code completion for all the
trio.socket
attributes is to define them statically.I have an example in socket.py that works, it looks complicated and is a leftover of my try to add the attributes to a class dynamically hoping the static analysis would pick it up.
But to no avail. davidhalter/jedi#997
Code generation might be able to do this, but that will make it even more difficult to deal with the subject.
There is a discussion open at davidhalter/jedi#385 which could point out the problem being solved on the other end of the stick.
@Zac-HD @njsmith What do you think?
I think this request will be closed after the discussion. The only reason I could think of it being accepted (with modification) is the micro optimisation of _reexport to update the globals once with a full dictionary instead of once per loop.