-
-
Notifications
You must be signed in to change notification settings - Fork 348
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
typecheck _socket and _core._local #2705
typecheck _socket and _core._local #2705
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## master #2705 +/- ##
=======================================
Coverage 98.89% 98.89%
=======================================
Files 113 113
Lines 16552 16600 +48
Branches 3006 3014 +8
=======================================
+ Hits 16369 16417 +48
Misses 126 126
Partials 57 57
|
Typing |
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.
Found a bunch of minor things. _SocketType
seems tricky though, we wouldn't want to require users to use that private name for their annotations, but SocketType
is empty so you can't do anything with it...
trio/_socket.py
Outdated
assert len(normed) == 4 | ||
# typechecking certainly doesn't like this logic, but given just how broad | ||
# Address is, it's kind of impossible to write the below without type: ignore |
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 might be possible to type, if you define TypeGuard
functions for the two len checks here, then change each branch to just build a whole tuple instead of converting to list (so we preserve the heterogeneity). Or unpack it into four variables.
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.
Ooh, I didn't try with TypeGuard
. Though I don't think it's terribly important to fix these lines, unless those typeguard functions can be reused elsewhere.
I think the issue with The other option if you don't like that would be to make |
I think I'll just leave |
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 didn't look too much but this should be most of my comments.
trio/_core/_local.py
Outdated
"""Sets the value of this :class:`RunVar` for this current run | ||
call. | ||
|
||
""" | ||
try: | ||
old_value = self.get() | ||
except LookupError: | ||
token = _RunVarToken.empty(self) | ||
token = RunVarToken[T]._empty(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.
Huh, I feel like mypy should infer T
.
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 guess it doesn't backtrack from return token
to determine token: RunVarToken[T]
. Might be better to just add a variable annotation before the try
block for it, then it should be able to infer.
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.
Something something inheritance seems to break the typevar (or maybe because one is trio._socket.T
and one is trio._util.T
), so line 72 needs an explicit T
(otherwise I get Incompatible types in assignment (expression has type "RunVarToken[T]", variable has type "RunVarToken[T]")
) and a variable annotation before the try
doesn't help. But I could remove the one at line 70.
trio/_socket.py
Outdated
@@ -178,9 +225,10 @@ def numeric_only_failure(exc): | |||
# idna.encode will error out if the hostname has Capital Letters | |||
# in it; with uts46=True it will lowercase them instead. | |||
host = _idna.encode(host, uts46=True) | |||
hr = _resolver.get(None) | |||
hr: HostnameResolver | None = _resolver.get(None) |
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 these type hints should also be inferred?
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.
yep, removing it has no effect. Though idk if having redundant explicit type hints is a bad thing.
trio/_socket.py
Outdated
return self._sock.getsockname() | ||
|
||
@overload | ||
def getsockopt(self, __level: int, __optname: int) -> int: |
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.
We dropped support for 3.7
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'm not sure what you're referring to here, I'm not finding what's changed and this is what's in typeshed.
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.
3.8 introduced /
pos arguments, so we can use that instead of the __arg
hack.
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.
Changed it in a bunch of places, but I failed to remove them from sendto
, and from the ones where _make_simple_sock_method_wrapper
is used to verify the signature.
|
||
################################################################ | ||
# sendto | ||
################################################################ | ||
|
||
@overload | ||
async def sendto( | ||
self, __data: Buffer, __address: tuple[Any, ...] | str | Buffer |
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.
tuple[Any, ...]
...?
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.
taken from typeshed, I've been thinking I should improve them upstream.
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.
Go ahead, typeshed loves contributions! (If you haven't submitted a PR there, it's an experience: they merge them so quickly!)
trio/_socket.py
Outdated
) -> None: | ||
if optlen is None: | ||
return self._sock.setsockopt(level, optname, cast("int|Buffer", value)) | ||
return self._sock.setsockopt(level, optname, cast(None, value), optlen) |
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.
Looks like we need a test using value = None
. Instead of the casts here, perhaps it’d better to add an a check in each side of the branch to verify that value is or is not None
, so that can be eliminated from the union. Better to be a raise
instead of assert
since it’s user code that would be wrong.
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.
Implemented 👍
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 first exception message is wrong, optlen
isn’t set there?
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.
Aside from the sockopt message (and failing test on PyPy), everything looks good here.
I skimmed through socket documentation on windows and failed to find something that seemed interesting - might even be none, so I'll just skip testing that. It looks like pypy doesn't support optlen at all. I feel like a comment at the call site that will pop up in the traceback should be good enough if it ever pops up that somebody runs pypy (uncommon) and tries to use the optlen argument (very uncommon - it wasn't even tested for in the trio tests previously). But could also wrap the call in a try/except. Typeshed doesn't make a distinction for pypy, nor do type checkers support guards using |
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.
Looks good to me other than these. I don't think I need to rereview again!
def listen(self, /, backlog: int = min(_stdlib_socket.SOMAXCONN, 128)) -> None: | ||
return self._sock.listen(backlog) | ||
|
||
def get_inheritable(self) -> bool: |
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 understand why some methods (e.g. listen
, setsockopt
) have pos-only self
params, whereas some (e.g. get_inheritable
, set_inheritable
) don't.
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.
Uh, neither listen
nor setsockopt
have pos-only self
params? The only ones that do are ones that use __self
to match typeshed.
I guess there's some inconsistency with whether methods use pos-only at all, but that inconsistency seems to be there in typeshed as well (set_inheritable
doesn't have pos-only in typeshed) - and there were some mismatches previously (bind
has pos-only address
in typeshed, but previous versions of trio didn't, so I don't think I should change that and possibly break existing code).
# args and kwargs must be starred, otherwise pyright complains: | ||
# '"args" member of ParamSpec is valid only when used with *args parameter' | ||
# '"kwargs" member of ParamSpec is valid only when used with **kwargs parameter' | ||
# wait_fn and fn must also be first in the signature | ||
# 'Keyword parameter cannot appear in signature after ParamSpec args parameter' | ||
|
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.
FWIW this isn't just a pyright thing as both these requirements come directly from the Parameter Specification PEP.
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, weird that mypy didn't complain about it then.
if TYPE_CHECKING: | ||
|
||
async def recv(self, buffersize: int, flags: int = 0) -> bytes: | ||
def recv(__self, __buflen: int, __flags: int = 0) -> Awaitable[bytes]: |
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.
Don't forget /
!
if TYPE_CHECKING: | ||
|
||
def recv_into( | ||
__self, buffer: Buffer, nbytes: int = 0, flags: int = 0 |
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.
Another pos-only parameter than can use /
: I won't comment on any others, as you can just ctrl+f "__
"
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.
As I mentioned above (but gosh, navigating discussion threads in long PR's are impossible):
Changed it in a bunch of places, but I failed to remove them from sendto, and from the ones where _make_simple_sock_method_wrapper is used to verify the signature.
As it checks if the signature is identical to typeshed it will require updating those. I'll add a comment in the code
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, I do have a comment for that already.
Lines 866 to 868 in 8a8b0b7
# _make_simple_sock_method_wrapper is typed, so this checks that the above is correct | |
# this requires that we refrain from using `/` to specify pos-only | |
# args, or mypy thinks the signature differs from typeshed. |
|
||
################################################################ | ||
# sendto | ||
################################################################ | ||
|
||
@overload | ||
async def sendto( | ||
self, __data: Buffer, __address: tuple[Any, ...] | str | Buffer |
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.
Go ahead, typeshed loves contributions! (If you haven't submitted a PR there, it's an experience: they merge them so quickly!)
make `_NoValue` `Final` Co-authored-by: EXPLOSION <git@helvetica.moe>
You said you didn't need to re-review, and need to dismiss in order to be able to merge.
https://typing.readthedocs.io/en/latest/source/stubs.html#unsupported-features
This might be why typeshed is doing |
and even more relevant: python/typeshed#10113 |
This one's a bit messier, and I failed to resolve what to do with
_SocketType
. The stubs in typeshed forsocket
aren't the cleanest either, but I managed to mess around enough to satisfypyright --verifytypes
stringent requirements of unambigious and specify the actual default value - and I think/hope everything's correct, though no big harm if they aren't.Typing
RunVar
withno_value = object()
seemed not very possible, but I think this change shouldn't affect behaviour.I'll do another push to figure out if there's any decent way of resolving autodoc, otherwise I'll just spam
nitpick_ignore
in these three PRs._SocketType
is the main one here that might merit documenting.