Skip to content

Commit b8485ff

Browse files
authored
chore(run): Pass through to subprocess.Popen (#361)
2 parents b7bde3b + b4b7a5d commit b8485ff

File tree

5 files changed

+90
-16
lines changed

5 files changed

+90
-16
lines changed

CHANGES

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ $ pip install --user --upgrade --pre libvcs
1717

1818
- {issue}`343`: `libvcs.cmd.core` (including {func}`~libvcs._internal.run.run`) have been moved to
1919
`libvcs._internal.run`. It will be supported as an unstable, internal API.
20+
- {issue}`361`: {class}`~libvcs._internal.run.run`'s params are now a pass-through to
21+
{class}`subprocess.Popen`.
22+
23+
- `run(cmd, ...)` is now `run(args, ...)` to match `Popen`'s convention.
2024

2125
### What's new
2226

libvcs/_internal/run.py

+79-11
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,19 @@
1313
import os
1414
import subprocess
1515
import sys
16-
from typing import Optional, Protocol, Union
16+
from typing import (
17+
IO,
18+
Any,
19+
Callable,
20+
Iterable,
21+
Mapping,
22+
Optional,
23+
Protocol,
24+
Sequence,
25+
Union,
26+
)
27+
28+
from typing_extensions import TypeAlias
1729

1830
from .. import exc
1931
from ..types import StrOrBytesPath
@@ -143,28 +155,65 @@ def __call__(self, output: Union[str, bytes], timestamp: datetime.datetime):
143155
...
144156

145157

158+
if sys.platform == "win32":
159+
_ENV: TypeAlias = Mapping[str, str]
160+
else:
161+
_ENV: TypeAlias = Union[
162+
Mapping[bytes, StrOrBytesPath], Mapping[str, StrOrBytesPath]
163+
]
164+
165+
_CMD = Union[StrOrBytesPath, Sequence[StrOrBytesPath]]
166+
_FILE: TypeAlias = Optional[Union[int, IO[Any]]]
167+
168+
146169
def run(
147-
cmd: Union[str, list[str]],
170+
args: _CMD,
171+
bufsize: int = -1,
172+
executable: Optional[StrOrBytesPath] = None,
173+
stdin: Optional[_FILE] = None,
174+
stdout: Optional[_FILE] = None,
175+
stderr: Optional[_FILE] = None,
176+
preexec_fn: Optional[Callable[[], Any]] = None,
177+
close_fds: bool = True,
148178
shell: bool = False,
149179
cwd: Optional[StrOrBytesPath] = None,
180+
env: Optional[_ENV] = None,
181+
universal_newlines: Optional[bool] = None,
182+
startupinfo: Optional[Any] = None,
183+
creationflags: int = 0,
184+
restore_signals: bool = True,
185+
start_new_session: bool = False,
186+
pass_fds: Any = (),
187+
*,
188+
text: Optional[bool] = None,
189+
encoding: Optional[str] = None,
190+
errors: Optional[str] = None,
191+
user: Optional[Union[str, int]] = None,
192+
group: Optional[Union[str, int]] = None,
193+
extra_groups: Optional[Iterable[Union[str, int]]] = None,
194+
umask: int = -1,
195+
# Not until sys.version_info >= (3, 10)
196+
# pipesize: int = -1,
197+
# custom
150198
log_in_real_time: bool = True,
151199
check_returncode: bool = True,
152200
callback: Optional[ProgressCallbackProtocol] = None,
153201
):
154-
"""Run 'cmd' in a shell and return the combined contents of stdout and
155-
stderr (Blocking). Throws an exception if the command exits non-zero.
202+
"""Run 'args' in a shell and return the combined contents of stdout and
203+
stderr (Blocking). Throws an exception if the command exits non-zero.
204+
205+
Keyword arguments are passthrough to {class}`subprocess.Popen`.
156206
157207
Parameters
158208
----------
159-
cmd : list or str, or single str, if shell=True
209+
args : list or str, or single str, if shell=True
160210
the command to run
161211
162212
shell : boolean
163213
boolean indicating whether we are using advanced shell
164214
features. Use only when absolutely necessary, since this allows a lot
165215
more freedom which could be exploited by malicious code. See the
166-
warning here:
167-
http://docs.python.org/library/subprocess.html#popen-constructor
216+
warning here: http://docs.python.org/library/subprocess.html#popen-constructor
168217
169218
cwd : str
170219
dir command is run from. Defaults to ``path``.
@@ -187,11 +236,30 @@ def progress_cb(output, timestamp):
187236
run(['git', 'pull'], callback=progress_cb)
188237
"""
189238
proc = subprocess.Popen(
190-
cmd,
239+
args,
240+
bufsize=bufsize,
241+
executable=executable,
242+
stdin=stdin,
243+
stdout=stdout or subprocess.PIPE,
244+
stderr=stderr or subprocess.PIPE,
245+
preexec_fn=preexec_fn,
246+
close_fds=close_fds,
191247
shell=shell,
192-
stderr=subprocess.PIPE,
193-
stdout=subprocess.PIPE,
194248
cwd=cwd,
249+
env=env,
250+
universal_newlines=universal_newlines,
251+
startupinfo=startupinfo,
252+
creationflags=creationflags,
253+
restore_signals=restore_signals,
254+
start_new_session=start_new_session,
255+
pass_fds=pass_fds,
256+
text=text,
257+
encoding=encoding,
258+
errors=errors,
259+
user=user,
260+
group=group,
261+
extra_groups=extra_groups,
262+
umask=umask,
195263
)
196264

197265
all_output = []
@@ -216,5 +284,5 @@ def progress_cb(output, timestamp):
216284
all_output = console_to_str(b"".join(stderr_lines))
217285
output = "".join(all_output)
218286
if code != 0 and check_returncode:
219-
raise exc.CommandError(output=output, returncode=code, cmd=cmd)
287+
raise exc.CommandError(output=output, returncode=code, cmd=args)
220288
return output

libvcs/cmd/git.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def run(
181181
if no_optional_locks is True:
182182
cli_args.append("--no-optional-locks")
183183

184-
return run(cmd=cli_args, **kwargs)
184+
return run(args=cli_args, **kwargs)
185185

186186
def clone(
187187
self,

libvcs/cmd/hg.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
import pathlib
33
from typing import Optional, Sequence, Union
44

5-
from ..types import StrOrBytesPath, StrPath
65
from libvcs._internal.run import run
76

7+
from ..types import StrOrBytesPath, StrPath
8+
89
_CMD = Union[StrOrBytesPath, Sequence[StrOrBytesPath]]
910

1011

@@ -156,7 +157,7 @@ def run(
156157
if help is True:
157158
cli_args.append("--help")
158159

159-
return run(cmd=cli_args, **kwargs)
160+
return run(args=cli_args, **kwargs)
160161

161162
def clone(
162163
self,

libvcs/cmd/svn.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import pathlib
22
from typing import Literal, Optional, Sequence, Union
33

4-
from ..types import StrOrBytesPath, StrPath
54
from libvcs._internal.run import run
65

6+
from ..types import StrOrBytesPath, StrPath
7+
78
_CMD = Union[StrOrBytesPath, Sequence[StrOrBytesPath]]
89

910
DepthLiteral = Union[Literal["infinity", "empty", "files", "immediates"], None]
@@ -107,7 +108,7 @@ def run(
107108
if config_option is not None:
108109
cli_args.append("--config-option {config_option}")
109110

110-
return run(cmd=cli_args, **kwargs)
111+
return run(args=cli_args, **kwargs)
111112

112113
def checkout(
113114
self,

0 commit comments

Comments
 (0)