-
-
Notifications
You must be signed in to change notification settings - Fork 25
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
Are the unused_tcp_port fixtures really useful for trio? #11
Comments
I can very much relate ! That's precisely what I was doing with trio at the very beginning (using a However I still make use of the
I pretty much agree (I a living example of this !), I think we should remove this However this means a special case for |
Those are great use cases!
I wonder if we should make a Or maybe this is overthinking things :-)
Yeah, we should definitely do this, for lots of reasons – it's actually got an issue open for it already: python-trio/trio#280 I guess this is the other advantage of removing All that said, I'm not really bothered by the unused tcp port pattern; the theoretical race condition bugs me a bit, but in practice like... every networking library test suite uses it and it works fine in practice. I mostly don't want people to be using the more awkward approaches just because they don't realize that trio has alternatives. Thinking about it more, I'm realizing that part of what's bugging me here is just that the use of fixtures for this is kind of awkward. The port ends up with a long name, there's no way to configure behavior (e.g. right now it has a baked in assumption that you're always going to be using the port for IPv4, not IPv6, and for TCP, not UDP), etc. Maybe what we should do is add a from trio.testing import unused_port
async def some_test():
tcp_port = unused_port()
udp_port = unused_port(type=trio.socket.SOCK_DGRAM)
... And then the docs for this function can point out the alternatives? Anyway, it does sound for like we agree that dropping the fixtures from pytest-trio is a good idea, at least for now, so I'll make a PR :-) |
I'm not sure either way, so here's an issue to discuss it.
The reason why they might not be: here's a little trio test of a simple server:
This is broken... well, in two ways, actually.
The very minor way it's broken is intrinsic to the use of
unused_tcp_port
: there's always a race condition between whenunused_tcp_port
checks to find the open port, and when we actually use it -- someone might open the port in the mean time. This is rarely a problem, but it does bug me a bit.There's also a much more important race condition: because we spawn the server in the background, and then immediately attempt to connect to it, we might end up connecting before the server is actually started up and has bound its port. In my experience this is a very common problem with other frameworks, and often requires ugly hacks like sticking a retry loop around the connect call.
Here's a better way to write this same test:
We changed two things here: First, we directly told our server to bind to port 0, i.e., to pick a random unused port. In the old way of doing things, this would be a problem, because we wouldn't have any way to know what port it ended up picking. And second, we used
nursery.start
instead ofnursery.start_soon
.Using
nursery.start
fixes the big important race condition, because it doesn't return until after the background task (in this case our echo server) has fully finished initializing itself. So by the time we make a client connection, we know the server is ready to accept clients.But
nursery.start
also fixes our problem with knowing which port was picked, because it passes back a value after the server was initialized – in this case the value comes fromtrio.serve_tcp
, which passes back theSocketListeners
that it opened. And then can use `trio.testing.open_stream_to_socket_listener to connect to one of these listeners -- it automatically introspects it to figure out what port it's using, and makes a connection.Summing up: this way of solving our problem uses 1 less feature, while solving 2 problems, in the same number of lines of code.
So given that trio has these features, I'm wondering whether
unused_tcp_port
is something we want to include or not. It's potentially still useful in some cases, I guess, and if nothing else then people who are expecting it might be grumpy if they don't find it. But OTOH maybe including it will just encourage people to do things the old kinda-broken way. As a general rule, if there are two ways of doing something and one of them works better than the other, trio tries to only include the one that works better :-)The text was updated successfully, but these errors were encountered: