Skip to content

Conversation

@jboning
Copy link
Contributor

@jboning jboning commented Mar 21, 2017

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

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.
returncode = 0
cmd = ... # type: str
output = ... # type: str # May be None
# morally: Union[str, bytes, List[str], List[bytes]]
Copy link
Member

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?)

Copy link
Contributor Author

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.

def __init__(self, returncode: int, cmd: str, output: Optional[str] = ...) -> None: ...
def __init__(self,
returncode: int,
cmd: Union[bytes, Text, List[bytes], List[Text]],
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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
Copy link
Member

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.

Copy link
Contributor Author

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.

@JelleZijlstra
Copy link
Member

Thanks! Sorry for not noticing the thing I commented on earlier.

jboning added 2 commits March 22, 2017 11:24
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]]
Copy link
Contributor Author

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.

Copy link
Member

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.

@jboning jboning changed the title Improve types for CalledProcessError Improve types for subprocess module Mar 23, 2017
@jboning
Copy link
Contributor Author

jboning commented Mar 23, 2017

I've standardized on Union[bytes, Text] everywhere that strings are used in the subprocess module, since it seems happily to accept either, and Any everywhere that a return type might be either bytes or Text.

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)

@JelleZijlstra
Copy link
Member

Feel free to add more changes; I'll review it tonight and merge if I don't see any other issues.

startupinfo: Any = ...,
creationflags: int = ...) -> str: ...
creationflags: int = ...
) -> Any: ... # morally: _TXT
Copy link
Member

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.)

Copy link
Contributor Author

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]: ...
Copy link
Member

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.

Copy link
Contributor Author

@jboning jboning Mar 24, 2017

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]]
Copy link
Member

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] = ...,
Copy link
Member

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.

Copy link
Contributor Author

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.

@JelleZijlstra JelleZijlstra merged commit 62f57e4 into python:master Mar 24, 2017
@JelleZijlstra
Copy link
Member

Merged! Thanks for all the fixes.

@jboning
Copy link
Contributor Author

jboning commented Mar 24, 2017

Thanks for the several rounds of review!

@jboning jboning deleted the called-process-error branch March 24, 2017 17:40
@gvanrossum
Copy link
Member

Sadly now the mypy tests fail, like this:

PARALLEL 2
SUMMARY  139 tasks selected
passed 22, failed 1, pending 115; running 1                                     

FAILURE  #23 check file runtests.py

mypy/waiter.py:36: error: Argument 3 to "Popen" has incompatible type Dict[str, str]; expected Mapping[Union[bytes, str], Union[bytes, str]]
mypy/test/testpythoneval.py:123: error: Argument 2 to "Popen" has incompatible type Dict[str, str]; expected Mapping[Union[bytes, str], Union[bytes, str]]

passed 26, failed 2, pending 110; running 1                                     

FAILURE  #27 check package mypy

mypy/waiter.py:36: error: Argument 3 to "Popen" has incompatible type Dict[str, str]; expected Mapping[Union[bytes, str], Union[bytes, str]]
mypy/test/testpythoneval.py:123: error: Argument 2 to "Popen" has incompatible type Dict[str, str]; expected Mapping[Union[bytes, str], Union[bytes, str]]

passed 118, failed 3, pending 17; running 1                                     

FAILURE  #121 check stdlibsamples (3.2)

subprocess.py:1586: error: Need type annotation for variable
test/test_subprocess.py:1294: error: Need type annotation for variable

SUMMARY  3/139 tasks and 3/3981 tests failed
 FAILURE check file runtests.py
 FAILURE check package mypy
 FAILURE check stdlibsamples (3.2)
SUMMARY  3/139 tasks and 3/3981 tests failed
*** FAILURE ***

@JelleZijlstra
Copy link
Member

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 str. I can confirm whether that works and submit a PR tonight.

@jboning
Copy link
Contributor Author

jboning commented Mar 24, 2017

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 select().

@JelleZijlstra
Copy link
Member

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.)

@gvanrossum
Copy link
Member

FWIW all you need to do to repro this is mypy runtests.py.

@jboning
Copy link
Contributor Author

jboning commented Mar 24, 2017

Indeed, switching the key type back to str works. Union[Mapping[str, _TXT], Mapping[bytes, _TXT]] also makes the mypy tests pass. It seems like that might be better since it's a little more flexible; what do you think?

@gvanrossum
Copy link
Member

I'll send a PR.

gvanrossum pushed a commit that referenced this pull request Mar 24, 2017
@gvanrossum
Copy link
Member

#1091. (It does not fix the issue introduced by #1080, see discussion there.)

hswong3i pushed a commit to alvistack/python-typeshed that referenced this pull request May 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants