mirror of
https://github.com/davidhalter/typeshed.git
synced 2025-12-06 20:24:30 +08:00
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:
committed by
Jelle Zijlstra
parent
fb80dc3971
commit
62f57e4cef
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user