Shell automation tools, like Make on steroids.
Project description
Description
The shell_utils
library provides some handy utilities for when you need to automate certain processes using shell commands.
Where you might otherwise write a bash script or muck around with the subprocess
, os
, and sys
modules in a Python script shell_utils
provides
some patterns and shortcuts for your automation scripts.
Let's say we have a new project we need to automate some build process(es) for. We might be tempted to write a Makefile or bash script(s) to help with that task. If that works for you, great. However, if you're like me, you prefer to python-all-the-things.
We can use shell-utils to create an automation script that will behave much the same way a Makefile would, but with all the Python goodness we want.
Some familiarity with the click
library will be helpful.
pip3 install shell_utils
shell_utils generate_runner
This will produce an executable python script with the following code
#!/usr/bin/env python3
import os
from pathlib import Path
from shell_utils import shell, cd, env, path, quiet
import click
@click.group()
def main():
"""
Development tasks; programmatically generated
"""
# ensure we're running commands from project root
root = Path(__file__).parent.absolute()
cwd = Path().absolute()
if root != cwd:
click.secho(f'Navigating from {cwd} to {root}',
fg='yellow')
os.chdir(root)
if __name__ == '__main__':
main()
Now let's say that we're using sphinx to generate the documentation we have in our project's docs
directory.
If we wanted to create a command that would re-generate our documentation and open a browser window when it's finished,
we could add the following code to our generated run.py
script
@main.command()
@click.option('--no-browser', is_flag=True, help="Don't open browser after building docs.")
def docs(no_browser):
"""
Generate Sphinx HTML documentation, including API docs.
"""
shell(
"""
rm -f docs/shell_utils.rst
rm -f docs/modules.rst
rm -rf docs/shell_utils*
sphinx-apidoc -o docs/ shell_utils
"""
)
with cd('docs'):
shell('make clean')
shell('make html')
shell('cp -rf docs/_build/html/ public/')
if no_browser:
return
shell('open public/index.html')
Then, we can execute the following command to do what we intended:
./run.py docs
The strings sent to the shell
function will be executed in a bash
subprocess shell. Before they are executed,
the shell
function will print the command to stdout
, similar to a Makefile
.
Also, notice we change directories into docs
using a context manager, that way the commands passed to the shell
function
will execute within that directory. Once we're out of the context manager's scope, further shell
function commands are once-again run
from the project root.
functions and context managers
shell
Executes the given command in a bash shell. It's just a thin wrapper around subprocess.run
that adds a couple handy features,
such as printing the command it's about to run before executing it.
from shell_utils import shell
p1 = shell('echo hello, world')
print(p1)
p2 = shell('echo goodbye, cruel world', capture=True)
print('captured the string:', p2.stdout)
outputs
user@hostname executing...
echo goodbye, cruel world
captured the string: goodbye, cruel world
cd
Temporarily changes the current working directory while within the context scope.
Within a python shell...
from shell_utils import shell, cd
with cd('~'):
shell('echo $PWD')
shell('mkdir -p foo')
with cd('foo'):
shell('echo $PWD')
shell('echo $PWD')
outputs
user@hostname executing...
echo $PWD
/Users/user
user@hostname executing...
mkdir -p foo
user@hostname executing...
echo $PWD
/Users/user/foo
user@hostname executing...
echo $PWD
/Users/user
env
Temporarily changes environment variables
from shell_utils import env
import os
print(os.getenv('foo', 'nothing'))
with env(foo='bar'):
print(os.getenv('foo'))
print(os.getenv('foo', 'nothing again'))
outputs
nothing
bar
nothing again
path
A special case of the env
context manager that alters your $PATH. It expands ~
to your home directory and returns
the elements of the $PATH variable as a list.
from shell_utils import path
import os
def print_path():
print('$PATH ==', os.getenv('PATH'))
print_path()
with path('~', prepend=True) as plist:
print_path()
print(plist)
outputs
$PATH == /Users/user/.venvs/shell-utils-py3.7/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin
$PATH == /Users/user:/Users/user/.venvs/shell-utils-py3.7/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin
['/Users/user', '/Users/user/.venvs/shell-utils-py3.7/bin', '/usr/local/sbin', '/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/Library/TeX/texbin']
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for shell_utils-2.3.1-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 9e304e60d739936f13f888ee38acd5ebe8f022f845ae0da1f89eb3e3bbbcc8cf |
|
MD5 | 562314533546c548850b9b59dec56120 |
|
BLAKE2b-256 | 80a264efc899b64924a5fd3186d6f17df6650c1641d5966fe7908267a7030bef |