Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Update subprocess types and improve error handling in run.py #485

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

Merged
merged 3 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions 8 CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ $ pip install --user --upgrade --pre libvcs

<!-- Maintainers, insert changes / features for the next release here -->

### Breaking changes

#### `run()` now uses `text=True` (#485)

This means that unicode, not bytes, will be used for running `subprocess`
commands in libvcs. If there are any compatibility issues with this, please file
a ticket.

### Development

#### chore: Implement PEP 563 deferred annotation resolution (#483)
Expand Down
57 changes: 28 additions & 29 deletions 57 src/libvcs/_internal/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,10 @@
from collections.abc import Iterable, Mapping, MutableMapping, Sequence

from libvcs import exc
from libvcs._internal.types import StrOrBytesPath
from libvcs._internal.types import StrPath

logger = logging.getLogger(__name__)

console_encoding = sys.stdout.encoding


def console_to_str(s: bytes) -> str:
"""From pypa/pip project, pip.backwardwardcompat. License MIT."""
try:
return s.decode(console_encoding)
except UnicodeDecodeError:
return s.decode("utf_8")
except AttributeError: # for tests, #13
return str(s)


if t.TYPE_CHECKING:
_LoggerAdapter = logging.LoggerAdapter[logging.Logger]
Expand Down Expand Up @@ -99,34 +87,32 @@ def __call__(self, output: t.AnyStr, timestamp: datetime.datetime) -> None:
_ENV: TypeAlias = Mapping[str, str]
else:
_ENV: TypeAlias = t.Union[
Mapping[bytes, StrOrBytesPath],
Mapping[str, StrOrBytesPath],
Mapping[bytes, StrPath],
Mapping[str, StrPath],
]

_CMD = t.Union[StrOrBytesPath, Sequence[StrOrBytesPath]]
_CMD = t.Union[StrPath, Sequence[StrPath]]
_FILE: TypeAlias = t.Optional[t.Union[int, t.IO[t.Any]]]


def run(
args: _CMD,
bufsize: int = -1,
executable: StrOrBytesPath | None = None,
executable: StrPath | None = None,
stdin: _FILE | None = None,
stdout: _FILE | None = None,
stderr: _FILE | None = None,
preexec_fn: t.Callable[[], t.Any] | None = None,
close_fds: bool = True,
shell: bool = False,
cwd: StrOrBytesPath | None = None,
cwd: StrPath | None = None,
env: _ENV | None = None,
universal_newlines: bool = False,
startupinfo: t.Any | None = None,
creationflags: int = 0,
restore_signals: bool = True,
start_new_session: bool = False,
pass_fds: t.Any = (),
*,
text: bool | None = None,
encoding: str | None = None,
errors: str | None = None,
user: str | int | None = None,
Expand Down Expand Up @@ -191,13 +177,12 @@ def progress_cb(output, timestamp):
shell=shell,
cwd=cwd,
env=env,
universal_newlines=universal_newlines,
startupinfo=startupinfo,
creationflags=creationflags,
restore_signals=restore_signals,
start_new_session=start_new_session,
pass_fds=pass_fds,
text=text,
text=True,
encoding=encoding,
errors=errors,
user=user,
Expand All @@ -206,7 +191,7 @@ def progress_cb(output, timestamp):
umask=umask,
)

all_output: list[str] = []
all_output: str = ""
code = None
line = None
if log_in_real_time and callback is None:
Expand All @@ -220,18 +205,32 @@ def progress_cb(output: t.AnyStr, timestamp: datetime.datetime) -> None:
code = proc.poll()

if callback and callable(callback) and proc.stderr is not None:
line = console_to_str(proc.stderr.read(128))
line = str(proc.stderr.read(128))
if line:
callback(output=line, timestamp=datetime.datetime.now())
if callback and callable(callback):
callback(output="\r", timestamp=datetime.datetime.now())

lines = filter(None, (line.strip() for line in proc.stdout.readlines()))
tony marked this conversation as resolved.
Show resolved Hide resolved
tony marked this conversation as resolved.
Show resolved Hide resolved
all_output = console_to_str(b"\n".join(lines))
lines = (
filter(None, (line.strip() for line in proc.stdout.readlines()))
if proc.stdout is not None
else []
)
all_output = "\n".join(lines)
if code:
stderr_lines = filter(None, (line.strip() for line in proc.stderr.readlines()))
all_output = console_to_str(b"".join(stderr_lines))
stderr_lines = (
filter(None, (line.strip() for line in proc.stderr.readlines()))
if proc.stderr is not None
else []
)
all_output = "".join(stderr_lines)
output = "".join(all_output)
if code != 0 and check_returncode:
raise exc.CommandError(output=output, returncode=code, cmd=args)
raise exc.CommandError(
output=output,
returncode=code,
cmd=[str(arg) for arg in args]
if isinstance(args, (list, tuple))
else str(args),
)
return output
6 changes: 3 additions & 3 deletions 6 src/libvcs/_internal/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def Popen(
args: _CMD | None = ...,
universal_newlines: t.Literal[False] = ...,
*,
text: t.Literal[None, False] = ...,
text: t.Literal[False] | None = ...,
encoding: None = ...,
errors: None = ...,
) -> subprocess.Popen[bytes]: ...
Expand Down Expand Up @@ -370,7 +370,7 @@ def check_output(
input: str | bytes | None = ...,
encoding: None = ...,
errors: None = ...,
text: t.Literal[None, False] = ...,
text: t.Literal[False] | None = ...,
**kwargs: t.Any,
) -> bytes: ...

Expand Down Expand Up @@ -484,7 +484,7 @@ def run(
encoding: None = ...,
errors: None = ...,
input: bytes | None = ...,
text: t.Literal[None, False] = ...,
text: t.Literal[False] | None = ...,
) -> subprocess.CompletedProcess[bytes]: ...

def run(
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.