Shell helper utilities for python


Keywords
python, shell
License
MIT
Install
pip install shil==2024.5.30.18.30

Documentation

shil    
Shell-util library for python.
Includes helpers for subprocess invocation, shell-formatters / pretty-printers, and more.


Overview

The shil library provides various shell-utilities for python.


Features

Shell-formatters / pretty-printers

Subprocess Invocation

There's a lot of shell-related libraries out there, especially for invocation (see for example this list). The interface for shil is hopefully unsurprising, and something that's convenient but not fancy. It's a utility, and not a huge framework.

The main goal is to provide an API that is simple and stable, without a ton of dependencies.

>>> import shil
>>> proc = shil.invoke('echo hello world')
>>> assert proc.succeeded
>>> assert proc.stdout.strip()=='hello world'
>>>

Beyond such basics, shil includes support for rich output and uses pydantic for datastructures.

See the API docs for more detailed information.


Installation

See pypi for available releases.

$ pip install shil

Usage (CLI)

The shil library publishes a small CLI tool, mostly just for testing & demoing the API behaviour. See the CLI docs for the latest (autogenerated) help.


Usage (API)

See also:

OOP-style Dispatch

This uses shil.Invocation and returns shil.InvocationResponse.

>>> import shil
>>> req = cmd = shil.Invocation(command='printf hello-world\n')
>>> resp = cmd()
>>> print(resp.stdout)
hello-world
>>>

Functional approach to dispatch

Use shil.invoke, get back shil.InvocationResponse

>>> import shil
>>> resp = shil.invoke(command='printf hello-world\n')
>>> print(resp.stdout)
hello-world
>>>

Loading data when command-output is JSON

>>> import shil
>>> cmd = shil.Invocation(command="""echo '{"foo":"bar"}'""", load_json=True)
>>> resp = cmd()
>>> print(resp.data)
{'foo': 'bar'}
>>> assert type(resp.data) == type({})
>>> assert resp.data['foo'] == 'bar'
>>>

Serialization with Pydantic

>>> import json, shil
>>> req = cmd = shil.Invocation(command="""echo pipes-are-allowed|grep allowed""")
>>> resp = cmd()
>>> keys = resp.dict().keys()
>>> expected = 'stdout stderr failed failure success succeeded data'.split()
>>> assert all([k in keys for k in expected])
>>>

Caller determines logging

Works like this with basic logger:

>>> import logging, shil
>>> logger = logging.getLogger()
>>> resp = shil.invoke('ls /tmp', command_logger=logger.critical, output_logger=logger.warning)
>>>

Supports using rich-logger too:

>>> import shil
>>> from rich.console import Console
>>> console = Console(stderr=True)
>>> resp = shil.invoke('ls /tmp', command_logger=console.log)
>>>

Rich-console Support

Besides using rich-logger as above, you can use the rich-protocol more directly.

Printing works the way you'd expect for Invocation and InvocationResponse.

>>> import shil, rich
>>> req = cmd = shil.Invocation(command='echo {"foo":"bar"}')
>>> resp = cmd()
>>> rich.print(req)
>>> rich.print(resp)

By default, output looks roughly like this:

rich console
rich console

Stay DRY with Runners

Runner's are basically just partials on shil.invoke. It's simple but this can help reduce copying around repetitive configuration.

>>> import shil
>>> from rich.console import Console
>>> console=Console(stderr=True)
>>> runner = shil.Runner(output_logger=console.log, command_logger=console.log)
>>> resp = runner('ls /tmp')
>>> assert isinstance(resp,(shil.InvocationResult,))
>>>