Improve types for subprocess module (#1059)

* Improve types for CalledProcessError

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.

* remove 'moral' comments; add List import; change 'str' to 'Text'

* import Text

* List -> Sequence; reinsert 'moral' comments

* Regularize string types everywhere

This defines _TXT and _CMD aliases, and uses them everywhere applicable.

Also brings the _FILE alias to python3.

* fix typo; possibly fix indentation

* remove trailing comma, which caused problems in python 2 tests

* fix py2 outputs to be bytes; tweak descriptive comments; remove one AnyStr
This commit is contained in:
Josiah Boning
2017-03-24 08:23:36 -07:00
committed by Jelle Zijlstra
parent fb80dc3971
commit 62f57e4cef
2 changed files with 147 additions and 119 deletions

View File

@@ -2,65 +2,72 @@
# Based on http://docs.python.org/2/library/subprocess.html and Python 3 stub
from typing import Sequence, Any, AnyStr, Mapping, Callable, Tuple, IO, Union, Optional
from typing import Sequence, Any, AnyStr, Mapping, Callable, Tuple, IO, Union, Optional, List, Text
_FILE = Union[int, IO[Any]]
_TXT = Union[bytes, Text]
_CMD = Union[_TXT, Sequence[_TXT]]
# Same args as Popen.__init__
def call(args: Union[str, Sequence[str]],
def call(args: _CMD,
bufsize: int = ...,
executable: str = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...) -> int: ...
def check_call(args: Union[str, Sequence[str]],
def check_call(args: _CMD,
bufsize: int = ...,
executable: str = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...) -> int: ...
# Same args as Popen.__init__ except for stdout
def check_output(args: Union[str, Sequence[str]],
def check_output(args: _CMD,
bufsize: int = ...,
executable: str = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...) -> str: ...
creationflags: int = ...) -> bytes: ...
PIPE = ... # type: int
STDOUT = ... # type: int
class CalledProcessError(Exception):
returncode = 0
cmd = ... # type: str
output = ... # type: str # May be None
# morally: _CMD
cmd = ... # type: Any
# morally: Optional[bytes]
output = ... # type: Any
def __init__(self, returncode: int, cmd: str, output: Optional[str] = ...) -> None: ...
def __init__(self,
returncode: int,
cmd: _CMD,
output: Optional[bytes] = ...) -> None: ...
class Popen:
stdin = ... # type: Optional[IO[Any]]
@@ -70,33 +77,31 @@ class Popen:
returncode = 0
def __init__(self,
args: Union[str, Sequence[str]],
args: _CMD,
bufsize: int = ...,
executable: Optional[str] = ...,
executable: Optional[_TXT] = ...,
stdin: Optional[_FILE] = ...,
stdout: Optional[_FILE] = ...,
stderr: Optional[_FILE] = ...,
preexec_fn: Optional[Callable[[], Any]] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: Optional[str] = ...,
env: Optional[Mapping[str, str]] = ...,
cwd: Optional[_TXT] = ...,
env: Optional[Mapping[_TXT, _TXT]] = ...,
universal_newlines: bool = ...,
startupinfo: Optional[Any] = ...,
creationflags: int = ...) -> None: ...
def poll(self) -> int: ...
def wait(self) -> int: ...
def communicate(self, input: Optional[AnyStr] = ...) -> Tuple[Optional[bytes], Optional[bytes]]: ...
# morally: -> Tuple[Optional[bytes], Optional[bytes]]
def communicate(self, input: Optional[_TXT] = ...) -> Tuple[Any, Any]: ...
def send_signal(self, signal: int) -> None: ...
def terminate(self) -> None: ...
def kill(self) -> None: ...
def __enter__(self) -> 'Popen': ...
def __exit__(self, type, value, traceback) -> bool: ...
def getstatusoutput(cmd: str) -> Tuple[int, str]: ...
def getoutput(cmd: str) -> str: ...
# Windows-only: STARTUPINFO etc.
STD_INPUT_HANDLE = ... # type: Any

View File

@@ -2,37 +2,43 @@
# Based on http://docs.python.org/3.6/library/subprocess.html
import sys
from typing import Sequence, Any, AnyStr, Mapping, Callable, Tuple, IO, Optional, Union, List, Type
from typing import Sequence, Any, AnyStr, Mapping, Callable, Tuple, IO, Optional, Union, List, Type, Text
from types import TracebackType
_FILE = Union[int, IO[Any]]
_TXT = Union[bytes, Text]
_CMD = Union[_TXT, Sequence[_TXT]]
if sys.version_info >= (3, 5):
class CompletedProcess:
args = ... # type: Union[Sequence[str], str]
# morally: _CMD
args = ... # type: Any
returncode = ... # type: int
# morally: Optional[_TXT]
stdout = ... # type: Any
stderr = ... # type: Any
def __init__(self, args: Union[List, str],
def __init__(self, args: _CMD,
returncode: int,
stdout: Union[str, bytes, None] = ...,
stderr: Union[str, bytes, None] = ...) -> None: ...
stdout: Optional[_TXT] = ...,
stderr: Optional[_TXT] = ...) -> None: ...
def check_returncode(self) -> None: ...
if sys.version_info >= (3, 6):
# Nearly same args as Popen.__init__ except for timeout, input, and check
def run(args: Union[str, Sequence[str]],
def run(args: _CMD,
timeout: float = ...,
input: Union[str, bytes] = ...,
input: _TXT = ...,
check: bool = ...,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stdout: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
@@ -43,20 +49,20 @@ if sys.version_info >= (3, 5):
errors: str = ...) -> CompletedProcess: ...
else:
# Nearly same args as Popen.__init__ except for timeout, input, and check
def run(args: Union[str, Sequence[str]],
def run(args: _CMD,
timeout: float = ...,
input: Union[str, bytes] = ...,
input: _TXT = ...,
check: bool = ...,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stdout: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
@@ -67,17 +73,17 @@ if sys.version_info >= (3, 5):
# Same args as Popen.__init__
if sys.version_info >= (3, 3):
# 3.3 added timeout
def call(args: Union[str, Sequence[str]],
def call(args: _CMD,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stdout: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
@@ -86,17 +92,17 @@ if sys.version_info >= (3, 3):
pass_fds: Any = ...,
timeout: float = ...) -> int: ...
else:
def call(args: Union[str, Sequence[str]],
def call(args: _CMD,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stdout: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
@@ -107,17 +113,17 @@ else:
# Same args as Popen.__init__
if sys.version_info >= (3, 3):
# 3.3 added timeout
def check_call(args: Union[str, Sequence[str]],
def check_call(args: _CMD,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stdout: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
@@ -126,17 +132,17 @@ if sys.version_info >= (3, 3):
pass_fds: Any = ...,
timeout: float = ...) -> int: ...
else:
def check_call(args: Union[str, Sequence[str]],
def check_call(args: _CMD,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stdout: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stdout: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
@@ -146,16 +152,16 @@ else:
if sys.version_info >= (3, 4):
# 3.4 added input
def check_output(args: Union[str, Sequence[str]],
def check_output(args: _CMD,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
@@ -163,44 +169,47 @@ if sys.version_info >= (3, 4):
start_new_session: bool = ...,
pass_fds: Any = ...,
timeout: float = ...,
input: Union[str, bytes] = ...) -> Any: ...
input: _TXT = ...,
) -> Any: ... # morally: -> _TXT
elif sys.version_info >= (3, 3):
# 3.3 added timeout
def check_output(args: Union[str, Sequence[str]],
def check_output(args: _CMD,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
restore_signals: bool = ...,
start_new_session: bool = ...,
pass_fds: Any = ...,
timeout: float = ...) -> Any: ...
timeout: float = ...,
) -> Any: ... # morally: -> _TXT
else:
# Same args as Popen.__init__, except for stdout
def check_output(args: Union[str, Sequence[str]],
def check_output(args: _CMD,
bufsize: int = ...,
executable: str = ...,
stdin: Any = ...,
stderr: Any = ...,
executable: _TXT = ...,
stdin: _FILE = ...,
stderr: _FILE = ...,
preexec_fn: Callable[[], Any] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: str = ...,
env: Mapping[str, str] = ...,
cwd: _TXT = ...,
env: Mapping[_TXT, _TXT] = ...,
universal_newlines: bool = ...,
startupinfo: Any = ...,
creationflags: int = ...,
restore_signals: bool = ...,
start_new_session: bool = ...,
pass_fds: Any = ...) -> Any: ...
pass_fds: Any = ...,
) -> Any: ... # morally: -> _TXT
# TODO types
@@ -214,15 +223,21 @@ if sys.version_info >= (3, 3):
class CalledProcessError(Exception):
returncode = 0
cmd = ... # type: str
output = b'' # May be None
# morally: _CMD
cmd = ... # type: Any
# morally: Optional[_TXT]
output = ... # type: Any
if sys.version_info >= (3, 5):
stdout = b''
stderr = b''
# morally: Optional[_TXT]
stdout = ... # type: Any
stderr = ... # type: Any
def __init__(self, returncode: int, cmd: str, output: Optional[str] = ...,
stderr: Optional[str] = ...) -> None: ...
def __init__(self,
returncode: int,
cmd: _CMD,
output: Optional[_TXT] = ...,
stderr: Optional[_TXT] = ...) -> None: ...
class Popen:
stdin = ... # type: IO[Any]
@@ -233,17 +248,17 @@ class Popen:
if sys.version_info >= (3, 6):
def __init__(self,
args: Union[str, Sequence[str]],
args: _CMD,
bufsize: int = ...,
executable: Optional[str] = ...,
stdin: Optional[Any] = ...,
stdout: Optional[Any] = ...,
stderr: Optional[Any] = ...,
executable: Optional[_TXT] = ...,
stdin: Optional[_FILE] = ...,
stdout: Optional[_FILE] = ...,
stderr: Optional[_FILE] = ...,
preexec_fn: Optional[Callable[[], Any]] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: Optional[str] = ...,
env: Optional[Mapping[str, str]] = ...,
cwd: Optional[_TXT] = ...,
env: Optional[Mapping[_TXT, _TXT]] = ...,
universal_newlines: bool = ...,
startupinfo: Optional[Any] = ...,
creationflags: int = ...,
@@ -254,17 +269,17 @@ class Popen:
errors: str = ...) -> None: ...
else:
def __init__(self,
args: Union[str, Sequence[str]],
args: _CMD,
bufsize: int = ...,
executable: Optional[str] = ...,
stdin: Optional[Any] = ...,
stdout: Optional[Any] = ...,
stderr: Optional[Any] = ...,
executable: Optional[_TXT] = ...,
stdin: Optional[_FILE] = ...,
stdout: Optional[_FILE] = ...,
stderr: Optional[_FILE] = ...,
preexec_fn: Optional[Callable[[], Any]] = ...,
close_fds: bool = ...,
shell: bool = ...,
cwd: Optional[str] = ...,
env: Optional[Mapping[str, str]] = ...,
cwd: Optional[_TXT] = ...,
env: Optional[Mapping[_TXT, _TXT]] = ...,
universal_newlines: bool = ...,
startupinfo: Optional[Any] = ...,
creationflags: int = ...,
@@ -280,16 +295,24 @@ class Popen:
def wait(self) ->int: ...
# Return str/bytes
if sys.version_info >= (3, 3):
def communicate(self, input: Optional[AnyStr] = ..., timeout: Optional[float] = ...) -> Tuple[Any, Any]: ...
def communicate(self,
input: Optional[_TXT] = ...,
timeout: Optional[float] = ...,
# morally: -> Tuple[Optional[_TXT], Optional[_TXT]]
) -> Tuple[Any, Any]: ...
else:
def communicate(self, input: Optional[AnyStr] = ...) -> Tuple[Any, Any]: ...
def communicate(self,
input: Optional[_TXT] = ...,
# morally: -> Tuple[Optional[_TXT], Optional[_TXT]]
) -> Tuple[Any, Any]: ...
def send_signal(self, signal: int) -> None: ...
def terminate(self) -> None: ...
def kill(self) -> None: ...
def __enter__(self) -> 'Popen': ...
def __exit__(self, type: Optional[Type[BaseException]], value: Optional[BaseException], traceback: Optional[TracebackType]) -> bool: ...
def getstatusoutput(cmd: str) -> Tuple[int, str]: ...
def getoutput(cmd: str) -> str: ...
# The result really is always a str.
def getstatusoutput(cmd: _TXT) -> Tuple[int, str]: ...
def getoutput(cmd: _TXT) -> str: ...
# Windows-only: STARTUPINFO etc.