-
-
Notifications
You must be signed in to change notification settings - Fork 341
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
Permit referring to some generic types in generic ways #908
Conversation
Specifically, declare `SendChannel`, `ReceiveChannel`, `Listener`, and `RunVar` to be generic in one type parameter, and also support the `open_memory_channel[T](bufsize)` syntax at runtime. Until trio is able to support typing directly, this change allows users of external stubs to use correctly-typed channels without too many hacks.
Codecov Report
@@ Coverage Diff @@
## master #908 +/- ##
==========================================
- Coverage 99.4% 99.36% -0.04%
==========================================
Files 102 102
Lines 12679 12707 +28
Branches 926 927 +1
==========================================
+ Hits 12603 12626 +23
- Misses 56 60 +4
- Partials 20 21 +1
|
Codecov Report
@@ Coverage Diff @@
## master #908 +/- ##
=========================================
+ Coverage 99.4% 99.4% +<.01%
=========================================
Files 102 102
Lines 12681 12715 +34
Branches 927 927
=========================================
+ Hits 12605 12639 +34
Misses 56 56
Partials 20 20
|
What's the advantage of keeping the actual stubs out of trio here? Oh, I see the force-push. We gotta fix sphinxcontrib-trio to handle these better... |
Putting the type hints in trio currently has limited utility because in the absence of stubs mypy chokes when you try to More context here: I'm working on a |
Looks like 3.5.0 doesn't support instantiating generic slots classes. :-( I'll remove the RunVar genericization because it's much less usability-relevant than the others: not many people create RunVars, and no one inherits from them so the workaround is just quotes rather than some elaborate |
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 wrote a few comments. They're mostly "thoughts that occurred to me while reading" than "critique". Overall it seems fine, and moves us incrementally in a good direction, so feel free to take them or leave them :-)
|
||
# The type of object produced by a Listener (covariant plus must be | ||
# an AsyncResource) | ||
T_resource = TypeVar("T_resource", bound=AsyncResource, covariant=True) |
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 bound=AsyncResource
will disappear when we fix #636. We could start skipping it now, or we could put it in and then take it out later, doesn't make much difference IMO.
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 think we'll have Listeners that provide objects not representable as an AsyncResource? I'm a bit surprised at that, can you give an 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.
I don't have an example in mind. But the reason we restrict it to AsyncResource
right now is because serve_listeners
automatically closes the object after the handler returns:
trio/trio/_highlevel_serve_listeners.py
Lines 25 to 29 in 0a815a0
async def _run_handler(stream, handler): | |
try: | |
await handler(stream) | |
finally: | |
await trio.aclose_forcefully(stream) |
With #636, this code goes away, because the Listener
itself is responsible for managing the handler lifetime. So... at that point it's really between the person developing a Listener[X]
and their users, what kind of API X
should support :-).
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.
Ah, that makes sense! I think it's probably sensible to keep the AsyncResource bound for now, since it reflects the current requirements, and we can easily change it later.
return self._fn(*args, **kwargs) | ||
|
||
def __getitem__(self, _): | ||
return self |
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.
So you actually can make open_memory_channel
directly typeable by mypy without any plugin, and it's not too terribly awful: python/mypy#6073 (comment)
But I think it does require inlining this class into the definition of open_memory_channel
.
If that's where were going to end up, then maybe setting up a standalone @generic_function
decorator, testing it, etc., is overkill, and we should instead move this code into _memory_channels.py
right now?
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 problem with that approach is that AFAICT, when you write Type[T]
, mypy can't infer T
as one of its "special forms" (Tuple, Union, Callable, etc). You just silently get Any
if you use such a one. I figured that was error-prone enough that we should go this route instead, at least for now.
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 ick.
CI problem is codecov.io returning an invalid script |
Specifically, declare
SendChannel
,ReceiveChannel
,Listener
, andRunVar
tobe generic in one type parameter, and also support the
open_memory_channel[T](bufsize)
syntax at runtime. Until trio is able to support typing directly, this change allows
users of external stubs to use correctly-typed channels without too many hacks.