I was also planning to extend it to support python3 asyncio, but never got around it
It provides an idiomatic shell scripting experience for python.
For example, it uses the "pipe" operator overload to allow complex pipelines to be created (pipes are created using platform native pipes, so there's no performance difference from a shell pipeline). The same operator can also be used for redirections
By the way, why not put the raise inside sh?
EDIT: Ok, the difference is that os.system does not return stdout. But you can use the subprocess package to capture stdout (and stderr as well). If you want pipe syntax, you can invoke e.g. bash -c "...", where the dots are your command. But the problem with this is that it's prone to injection attacks, unless you are very careful. I guess the same warning applies in your original "sh" example.
>>> from invoke import run
>>> date = run("date")
Sat Apr 13 11:09:18 PDT 2019
>>> dir(date)
['__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'command', 'encoding', 'env', 'exited', 'failed', 'hide', 'ok', 'pty', 'return_code', 'shell', 'stderr', 'stdout']
>>> date.return_code
0
>>> date.stdout
'Sat Apr 13 11:09:18 PDT 2019\n'
>>> do_they_exist = run("grep banana /etc/passwd")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/pgraydon/Envs/python3/lib/python3.7/site-packages/invoke/__init__.py", line 46, in run
return Context().run(command, **kwargs)
File "/Users/pgraydon/Envs/python3/lib/python3.7/site-packages/invoke/context.py", line 94, in run
return self._run(runner, command, **kwargs)
File "/Users/pgraydon/Envs/python3/lib/python3.7/site-packages/invoke/context.py", line 101, in _run
return runner.run(command, **kwargs)
File "/Users/pgraydon/Envs/python3/lib/python3.7/site-packages/invoke/runners.py", line 271, in run
return self._run_body(command, **kwargs)
File "/Users/pgraydon/Envs/python3/lib/python3.7/site-packages/invoke/runners.py", line 404, in _run_body
raise UnexpectedExit(result)
invoke.exceptions.UnexpectedExit: Encountered a bad command exit code!
Command: 'grep banana /etc/passwd'
Exit code: 1
Stdout: already printed
Stderr: already printed
If it gets a non-zero code back (like grep does when it fails to get a match), it raises an UnexpectedExit exception.