Skip to content

Altering the behavior of commands

Binary mode

All commands execute in text mode by default (dealing with str objects). This can cause problems with binary data.

from pipepy import gzip
result = "hello world" | gzip
print(result.stdout)
# <<< UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b

Set _text=False for binary mode:

from pipepy import gzip
gzip = gzip(_text=False)
result = "hello world" | gzip
print(result.stdout)
# <<< b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03...'

Change encoding with the _encoding keyword argument:

from pipepy import gzip
gzip = gzip(_text=False)
result = "καλημέρα" | gzip
result = "καλημέρα" | gzip(_encoding="iso-8859-7")

Streaming to console

Set _stream_stdout=True or _stream_stderr=True to stream those streams to the console instead of capturing them. Useful for interactive commands like fzf:

from pipepy import fzf
fzf = fzf(_stream_stderr=True)

print("John\nMary" | fzf)
# <<< Mary

Or dialog:

from pipepy import dialog
dialog = dialog(_stream_stdout=True)

result = dialog(checklist=True)('Choose name', 30, 110, 0,
                                "John", '', "on",
                                "Mary", '', "off")
print(result.stderr)
# <<< John

Force full streaming with _stream:

from pipepy import wget
wget('https://...', _stream=True)()

returncode is still available, so you can use the command in boolean expressions:

from pipepy import wget
if wget('https://...', _stream=True):
     print("Download succeeded")
else:
     print("Download failed")

Global streaming mode

Call pipepy.set_always_stream(True) to make streaming the default:

import pipepy
from pipepy import ls
pipepy.set_always_stream(True)
ls()  # Equivalent to `ls(_stream=True)()`
pipepy.set_always_stream(False)

Override with _stream=False:

import pipepy
from pipepy import ls

pipepy.set_always_stream(True)
ls()                 # Will stream
ls(_stream=False)()  # Will capture
pipepy.set_always_stream(False)

Exceptions

Call .raise_for_returncode() on an evaluated result to raise an exception if returncode is not 0.

from pipepy import ping, PipePyError
result = ping("asdf")()  # Evaluate first

result.raise_for_returncode()
# <<< PipePyError: (2, '', 'ping: asdf: Name or service not known\n')

try:
    result.raise_for_returncode()
except PipePyError as exc:
    print(exc.returncode)
    # <<< 2
    print(exc.stdout)
    # <<< ""
    print(exc.stderr)
    # <<< ping: asdf: Name or service not known

Global raise mode

Set pipepy.set_always_raise(True) to have all commands raise on non-zero returncode:

import pipepy
from pipepy import ping
pipepy.set_always_raise(True)
ping("asdf")()
# <<< PipePyError: (2, '', 'ping: asdf: Name or service not known\n')

Override per-command with _raise=False:

import pipepy
from pipepy import ping
pipepy.set_always_raise(True)
ping("asdf", _raise=False)()  # Will not raise

Interactive mode

When "interactive" mode is set, __repr__ returns self.stdout + self.stderr. Useful for interactive Python shells.

import pipepy
from pipepy import ls, overload_chars
pipepy.set_interactive(True)
ls
# <<< demo.py
# ... interactive2.py
# ... interactive.py
# ... main.py

overload_chars(locals())
ls -l
# <<< total 20
# ... -rw-r--r-- 1 kbairak kbairak ...

Making alterations "permanent"

Create PipePy instances with alterations and use them as templates:

stream_sh = PipePy(_stream=True)
stream_sh
# <<< PipePy()
stream_sh._stream
# <<< True

stream_sh.ls
# <<< PipePy('ls')
stream_sh.ls._stream
# <<< True

r = stream_sh.ls()
# output is streamed to console
r.stdout
# <<< None
r.returncode
# <<< 0
raise_sh = PipePy(_raise=True)
raise_sh.false
# <<< PipePy('false')
raise_sh.false()
# <<< PipePyError: (1, '', '')

This is a more contained alternative to set_always_stream and set_always_raise.

Process control

.terminate(), .kill() and .send_signal() forward to the underlying Popen object.

from pipepy import sleep
cmd = sleep(100).delay()
cmd.terminate()
# or
cmd.kill()
# or
cmd.send_signal(signal.SIGTERM)