-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Improve types for subprocess module #1059
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
Conversation
This adds union types in the constructor to account for parameters that might be either byte strings or unicode strings. At the same time, this *removes* specific types from the user-accessible fields, to avoid the need for users to at every use site specify which types their particular instance was instantiated with.
stdlib/2/subprocess.pyi
Outdated
| returncode = 0 | ||
| cmd = ... # type: str | ||
| output = ... # type: str # May be None | ||
| # morally: Union[str, bytes, List[str], List[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.
str and bytes are the same thing in Python 2. You should use str and unicode, or bytes and Text if you want the same code in 2 and 3. (Maybe we can merge these stubs in the future?)
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.
Oops! I guess I got mixed up because of the python 3 stub syntax.
stdlib/2/subprocess.pyi
Outdated
| def __init__(self, returncode: int, cmd: str, output: Optional[str] = ...) -> None: ... | ||
| def __init__(self, | ||
| returncode: int, | ||
| cmd: Union[bytes, Text, List[bytes], List[Text]], |
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.
Instead of List, can't it be any Sequence? That's what check_output is annotated as.
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 indeed! Should I modify check_output (and family) to support bytes, 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.
Sure, please do. Also, I think the argument should be Sequence[Union[bytes, Text]], since mixing bytes and str seems to work fine. Using a Union as the type argument is OK because Sequence is covariant.
| returncode = 0 | ||
| cmd = ... # type: str | ||
| output = ... # type: str # May be None | ||
| cmd = ... # type: 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.
I think the "moral" comments were actually helpful 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.
OK, I'll add them back! I thought they might be redundant with the actual types in the constructor.
|
Thanks! Sorry for not noticing the thing I commented on earlier. |
This defines _TXT and _CMD aliases, and uses them everywhere applicable. Also brings the _FILE alias to python3.
| from typing import Sequence, Any, AnyStr, Mapping, Callable, Tuple, IO, Optional, Union, List, Type, Text | ||
| from types import TracebackType | ||
|
|
||
| _FILE = Union[int, IO[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.
I brought this _FILE thing over from the python 2 stubs, though I'm actually not sure it's correct.
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 actually pass file descriptors (i.e., ints) to the stdout and stderr arguments in both Python 2 and 3. Didn't know about this either, but it seems to work.
|
I've standardized on There's still a disorganized mixture of Optional and non-Optional arguments. Maybe I should also standardize this to have the explicit Optional everywhere? (Or maybe I should stop offering to incorporate more changes into this PR so that it can eventually get merged :P) |
|
Feel free to add more changes; I'll review it tonight and merge if I don't see any other issues. |
stdlib/2/subprocess.pyi
Outdated
| startupinfo: Any = ..., | ||
| creationflags: int = ...) -> str: ... | ||
| creationflags: int = ... | ||
| ) -> Any: ... # morally: _TXT |
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 it really return unicode in py2? It returns str for me even with universal_newlines on. (In Python 3 it's bytes without universal_newlines and str with it.)
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.
Indeed; fascinating! I didn't compare the documentation for the different versions closely enough. It looks like in python 2, the difference is that os.fdopen() is called with "U" in the mode to; but file objects with universal newlines turned still just return strings. I think that means I should convert all of the py2 output/stderr types to be bytes.
| def wait(self) -> int: ... | ||
| def communicate(self, input: Optional[AnyStr] = ...) -> Tuple[Optional[bytes], Optional[bytes]]: ... | ||
| # morally: -> Tuple[_TXT, _TXT] | ||
| def communicate(self, input: Optional[_TXT] = ...) -> Tuple[Any, 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.
I think the original return annotation was also correct 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.
I think Any is preferable in the same way it is for the other sort of output annotations; with Optional, a user who knows they passed in a PIPE must explicitly check for None (or cast). I'm updating the descriptive comment to say the right thing though (the original annotation).
| from typing import Sequence, Any, AnyStr, Mapping, Callable, Tuple, IO, Optional, Union, List, Type, Text | ||
| from types import TracebackType | ||
|
|
||
| _FILE = Union[int, IO[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.
You can actually pass file descriptors (i.e., ints) to the stdout and stderr arguments in both Python 2 and 3. Didn't know about this either, but it seems to work.
| def __init__(self, | ||
| returncode: int, | ||
| cmd: _CMD, | ||
| output: Optional[_TXT] = ..., |
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.
Odd that this argument isn't called "stdout", but confirmed in the source that it's 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.
yeah; I think that's because it predates the addition of "stderr", and the "stdout" alias was added at the same time for consistency.
|
Merged! Thanks for all the fixes. |
|
Thanks for the several rounds of review! |
|
Sadly now the mypy tests fail, like this: |
|
Oh no :(. I guess it's because Mapping is covariant in the value type but not the key type (not sure why). If that's true, we should be able to fix by reverting the key type to |
|
The stdlibsamples test failures mystified me for a moment; but I think those are actually a result of #1080 now that there are more, and more specific, annotations for |
|
I just posted a PR in #1090 to add a tox.ini that includes a way to test mypy's test suite against typeshed. I'll send a followup PR later to include it in Travis. (I'd prefer to catch these issues before we merge.) |
|
FWIW all you need to do to repro this is |
|
Indeed, switching the key type back to |
|
I'll send a PR. |
This adds union types in the constructor to account for parameters that
might be either byte strings or unicode strings. At the same time, this
removes specific types from the user-accessible fields, to avoid the
need for users to at every use site specify which types their particular
instance was instantiated with.
Fixes #1004