Skip to content

Commit

Permalink
Merge pull request #21 from pvarki/shell_helper
Browse files Browse the repository at this point in the history
Add shell helper
  • Loading branch information
rambo authored Feb 7, 2024
2 parents 9928132 + 50ed2e0 commit 0a08fc5
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.8.0
current_version = 1.9.0
commit = False
tag = False

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "libpvarki"
version = "1.8.0"
version = "1.9.0"
description = "Common helpers like standard logging init"
authors = ["Eero af Heurlin <eero.afheurlin@iki.fi>"]
homepage = "https://github.com/pvarki/python-libpvarki/"
Expand Down
2 changes: 1 addition & 1 deletion src/libpvarki/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
""" Common helpers like standard logging init """
__version__ = "1.8.0" # NOTE Use `bump2version --config-file patch` to bump versions correctly
__version__ = "1.9.0" # NOTE Use `bump2version --config-file patch` to bump versions correctly
2 changes: 1 addition & 1 deletion src/libpvarki/middleware/mtlsheader.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


LOGGER = logging.getLogger(__name__)
CONFIG = Config(".env")
CONFIG = Config() # not supporting .env files anymore because https://github.com/encode/starlette/discussions/2446
DNDict = Mapping[str, str]


Expand Down
2 changes: 1 addition & 1 deletion src/libpvarki/mtlshelp/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


LOGGER = logging.getLogger(__name__)
CONFIG = Config(".env")
CONFIG = Config() # not supporting .env files anymore because https://github.com/encode/starlette/discussions/2446

# https://github.com/miguelgrinberg/python-socketio/discussions/1040 was very helpful

Expand Down
29 changes: 29 additions & 0 deletions src/libpvarki/shell.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Shell related helpers"""
from typing import Tuple
import asyncio
import logging

LOGGER = logging.getLogger(__name__)


async def call_cmd(cmd: str, timeout: float = 2.5, *, stderr_warn: bool = True) -> Tuple[int, str, str]:
"""Do the boilerplate for calling cmd and returning the exit code and output as strings"""
LOGGER.debug("Calling create_subprocess_shell(({})".format(cmd))
process = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
out, err = await asyncio.wait_for(process.communicate(), timeout=timeout)
out_str = out.decode("utf-8")
err_str = err.decode("utf-8")
if err and stderr_warn:
LOGGER.warning("{} stderr: {}".format(cmd, err_str))
LOGGER.info(out_str)
assert isinstance(process.returncode, int) # at this point it is, keep mypy happy
if process.returncode != 0:
LOGGER.error("{} returned nonzero code: {} (process: {})".format(cmd, process.returncode, process))
LOGGER.error("{} stderr: {}".format(cmd, err_str))
LOGGER.error("{} stdout: {}".format(cmd, out_str))

return process.returncode, out_str, err_str
2 changes: 1 addition & 1 deletion tests/test_libpvarki.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

def test_version() -> None:
"""Make sure version matches expected"""
assert __version__ == "1.8.0"
assert __version__ == "1.9.0"
63 changes: 63 additions & 0 deletions tests/test_shell.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Test shell helpers"""
import logging
import asyncio

import pytest

from libpvarki.shell import call_cmd

LOGGER = logging.getLogger(__name__)


@pytest.mark.asyncio
async def test_true() -> None:
"""Test that true exists with code 0"""
code, stdout, stderr = await call_cmd("true")
assert code == 0
assert stdout == ""
assert stderr == ""


@pytest.mark.asyncio
async def test_false() -> None:
"""Test that false exists with nonzero code"""
code, stdout, stderr = await call_cmd("false")
assert code != 0
assert stdout == ""
assert stderr == ""


@pytest.mark.asyncio
async def test_stdout() -> None:
"""Test that echo exists with code 0 and ouputs what we expect to stdout"""
code, stdout, stderr = await call_cmd("echo 'hello world'")
assert code == 0
assert stdout == "hello world\n"
assert stderr == ""


@pytest.mark.asyncio
async def test_stderr() -> None:
"""Test that echo exists with code 0 and ouputs what we expect to stderr"""
code, stdout, stderr = await call_cmd("echo 'goodbye world' >&2")
# FIXME: Capture log output and check for the warning
assert code == 0
assert stdout == ""
assert stderr == "goodbye world\n"


@pytest.mark.asyncio
async def test_stderr_nowarn() -> None:
"""Test that echo exists with code 0 and ouputs what we expect to stderr"""
code, stdout, stderr = await call_cmd("echo 'goodbye world' >&2", stderr_warn=False)
# FIXME: Capture log output and check that there is no warning
assert code == 0
assert stdout == ""
assert stderr == "goodbye world\n"


@pytest.mark.asyncio
async def test_timeout() -> None:
"""Test that long sleeps are aborted on timeout"""
with pytest.raises(asyncio.TimeoutError):
await call_cmd("sleep 5", timeout=1.0)

0 comments on commit 0a08fc5

Please sign in to comment.