Package: shil
shilShell-util library for python.Includes helpers for subprocess invocation, shell-formatters / pretty-printers, and more.OverviewTheshillibrary provides various shell-utilities for python.FeaturesShell-formatters / pretty-printersA somewhat usable parser / grammar for bashConsole support forrich&rich protocolsSubprocess InvocationThere's a lot of shell-related libraries out there, especially for invocation (see for examplethis list).
The interface forshilis 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.>>>importshil>>>proc=shil.invoke('echo hello world')>>>assertproc.succeeded>>>assertproc.stdout.strip()=='hello world'>>>Beyond such basics, shil includes support forrich outputand uses pydantic for datastructures.See theAPI docsfor more detailed information.InstallationSeepypifor available releases.$pipinstallshilUsage (CLI)The shil library publishes a small CLI tool, mostly just for testing & demoing the API behaviour. Seethe CLI docsfor the latest (autogenerated) help.Usage (API)See also:the unit-testsfor some examples of library usagethe smoke-testsfor example usage of stand-alone scriptsOOP-style DispatchThis usesshil.Invocationand returnsshil.InvocationResponse.>>>importshil>>>req=cmd=shil.Invocation(command='printf hello-world\n')>>>resp=cmd()>>>print(resp.stdout)hello-world>>>Functional approach to dispatchUseshil.invoke, get backshil.InvocationResponse>>>importshil>>>resp=shil.invoke(command='printf hello-world\n')>>>print(resp.stdout)hello-world>>>Loading data when command-output is JSON>>>importshil>>>cmd=shil.Invocation(command="""echo '{"foo":"bar"}'""",load_json=True)>>>resp=cmd()>>>print(resp.data){'foo':'bar'}>>>asserttype(resp.data)==type({})>>>assertresp.data['foo']=='bar'>>>Serialization with Pydantic>>>importjson,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()>>>assertall([kinkeysforkinexpected])>>>Caller determines loggingWorks like this with basic logger:>>>importlogging,shil>>>logger=logging.getLogger()>>>resp=shil.invoke('ls /tmp',command_logger=logger.critical,output_logger=logger.warning)>>>Supports usingrich-loggertoo:>>>importshil>>>fromrich.consoleimportConsole>>>console=Console(stderr=True)>>>resp=shil.invoke('ls /tmp',command_logger=console.log)>>>Rich-console SupportBesides using rich-logger as above, you can use therich-protocolmore directly.Printing works the way you'd expect forInvocationandInvocationResponse.importrichimportshilreq=cmd=shil.Invocation(command='echo {"foo":"bar"}')resp=cmd()rich.print(req)rich.print(resp)By default, output looks roughly like this:Stay DRY with RunnersRunner's are basically justpartialsonshil.invoke.  It's simple but this can help reduce copying around repetitive configuration.>>>importshil>>>fromrich.consoleimportConsole>>>console=Console(stderr=True)>>>runner=shil.Runner(output_logger=console.log,command_logger=console.log)>>>resp=runner('ls /tmp')>>>assertisinstance(resp,(shil.InvocationResult,))>>>