diff --git a/docs/cli.rst b/docs/cli.rst index fec337c3..81e4208b 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -1,3 +1,5 @@ +.. _guide-cli: + Command-Line Interface (CLI) ============================ diff --git a/docs/index.rst b/docs/index.rst index 05a4c6ba..13b8aabf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,13 +6,17 @@ Say hello to *Plumbum Shell Combinators*. Plumbum (Latin for *lead*, which was u pipes back in the day) is a small yet feature-rich library for shell script-like programs in Python. The motto of the library is *"You'll never have to write shell scripts again"*, and thus it attempts to mimic the shell syntax where it makes sense, while keeping it all pythonic and -cross-platform. +cross-platform. +Apart from :ref:`shell-like syntax ` and :ref:`handy shortcuts `, +the library provides local and :ref:`remote ` command execution (over *SSH*), +local and remote file-system :ref:`paths `, easy working-directory and +environment :ref:`manipulation `, and a programmatic +:ref:`guide-cli` application toolkit. Let's see some code! -Apart from shell-like syntax and handy shortcuts, the library provides *local* and *remote* -command execution (over SSH), local and remote file-system *paths*, easy working-directory and -environment manipulation, and a programmatic *command-line interface* (CLI) application toolkit. +Cheat Sheet +----------- -Let's see some code! :: +Basics :: >>> from plumbum import local, FG, BG >>> from plumbum.cmd import ls, grep, wc, cat, head @@ -21,14 +25,16 @@ Let's see some code! :: >>> ls() u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n' - # piping +Piping :: + >>> chain = ls["-a"] | grep["-v", "\\.py"] | wc["-l"] >>> print chain /bin/ls -a | /bin/grep -v '\.py' | /usr/bin/wc -l >>> chain() u'13\n' - # cwd manipulation +Workign-directory manipulation :: + >>> local.cwd >>> with local.cwd(local.cwd / "docs"): @@ -36,7 +42,8 @@ Let's see some code! :: ... u'15\n' - # redirection +Output/Input Redirection :: + >>> ((cat < "setup.py") | head["-n", 4])() u'#!/usr/bin/env python\nimport os\n\ntry:\n' >>> (ls["-a"] > "file.list")() @@ -44,65 +51,48 @@ Let's see some code! :: >>> (cat["file.list"] | wc["-l"])() u'17\n' - # FG and BG +Foreground and Background Execution :: + >>> (ls["-a"] | grep["\\.py"]) & FG # The output is printed to stdout directly build.py .pydevproject setup.py - >>> (ls["-a"] | grep["\\.py"]) & BG # The process runs in the backround + >>> (ls["-a"] | grep["\\.py"]) & BG # The process runs "in the background" - -And you no longer have to stuggle with ``optparse``/``argparse`` to write CLI applications... -Plumbum makes it damn easy! :: - - from plumbum import cli, local - from plumbum.utils import delete, copy - logger = ... - class FileCopier(cli.Application): - overwrite = cli.Flag("-o", help = "If given, overwrite existing files") +Command nesting :: - @cli.switch(["-l", "--log-to-file"], argtype = str) - def log_to_file(self, filename): - """Logs all output to the given file""" - handler = logging.FileHandler(filename) - logger.addHandler(handler) + >>> from plumbum.cmd import sudo + >>> print sudo[ifconfig["-a"]] + /usr/bin/sudo /sbin/ifconfig -a + >>> (sudo[ifconfig["-a"]] | grep["-i", "loop"]) & FG + lo Link encap:Local Loopback + UP LOOPBACK RUNNING MTU:16436 Metric:1 + +Remote commands (over SSH) :: - @cli.switch(["--verbose"], requires=["--log-to-file"]) - def set_debug(self): - """Sets verbose mode""" - logger.setLevel(logging.DEBUG) - - def main(self, src, dst): - if local.path(dst).exists(): - if not self.overwrite: - logger.debug("Oh no! It's terrible") - raise ValueError("Destination already exists") - else: - delete(dst) - - logger.debug("I'm going to copy %s to %s", src, dst) - copy(src, dst) - logger.debug("Great success") - - if __name__ == "__main__": - FileCopier.run() + >>> from plumbum import SshMachine + >>> remote = SshMachine("your-host.com") + >>> r_ls = remote["ls"] + >>> with remote.cwd("/lib"): + ... (r_ls | grep["0.so.0"])() + ... + u'libusb-1.0.so.0\nlibusb-1.0.so.0.0.0\n' Development =========== -The library is developed on `github `_, using -its lovely `issue tracker `_; if you encounter -a problem with the library, please open an issue. +The library is developed on `github `_, and will happily +accept `pull requests `_ from users. +Please use the github's built-in `issue tracker `_ +to report any problem you encounter or to request features. The library is released under the +permissive `MIT license `_. You can **download** the library from `PyPI `_, or -``easy-install``/``pip install`` it directly. - -The library is released under the `MIT license -`_. +``easy-install`` / ``pip install`` it directly. -Guide -===== +User Guide +========== .. toctree:: :maxdepth: 2 @@ -111,6 +101,7 @@ Guide paths remote_machine cli + utils API Reference ============= diff --git a/docs/local_commands.rst b/docs/local_commands.rst index 5905f93d..674347a2 100644 --- a/docs/local_commands.rst +++ b/docs/local_commands.rst @@ -1,3 +1,5 @@ +.. _guide-local-commands: + Local Commands ============== Plumbum exposes a special singleton object named ``local``, which represents your local machine diff --git a/docs/local_machine.rst b/docs/local_machine.rst index 5819b548..7f7e963d 100644 --- a/docs/local_machine.rst +++ b/docs/local_machine.rst @@ -1,3 +1,5 @@ +.. _guide-local-machine: + The Local Object ================ So far we've only seen running local commands, but there's more to the ``local`` object than diff --git a/docs/paths.rst b/docs/paths.rst index efb57f79..92813da9 100644 --- a/docs/paths.rst +++ b/docs/paths.rst @@ -1,3 +1,5 @@ +.. _guide-paths: + Local Paths =========== diff --git a/docs/remote_machine.rst b/docs/remote_machine.rst index 679afce1..50727193 100644 --- a/docs/remote_machine.rst +++ b/docs/remote_machine.rst @@ -1,3 +1,5 @@ +.. _guide-remote-machines: + Remote Machines =============== diff --git a/docs/utils.rst b/docs/utils.rst new file mode 100644 index 00000000..c7122110 --- /dev/null +++ b/docs/utils.rst @@ -0,0 +1,7 @@ +.. _guide-utils: + +Utilities +========= +copy +move +delete diff --git a/plumbum/commands.py b/plumbum/commands.py index f0252502..9fba564d 100644 --- a/plumbum/commands.py +++ b/plumbum/commands.py @@ -79,8 +79,10 @@ def run_proc(proc, retcode): :param proc: a running Popen-like object - :param retcode: the expected return (exit) code. 0 is the convention for success. - pass ``None`` in order to ignore the return code + :param retcode: the expected return (exit) code of the process. It defaults to 0 (the + convention for success). If ``None``, the return code is ignored. + It may also be a tuple (or any object that supports ``__contains__``) + of expected return codes. :returns: A tuple of (return code, stdout, stderr) """ @@ -93,9 +95,14 @@ def run_proc(proc, retcode): stdout = stdout.decode(proc.encoding, "ignore") stderr = stderr.decode(proc.encoding, "ignore") - if retcode is not None and proc.returncode != retcode: - raise ProcessExecutionError(getattr(proc, "argv", None), - proc.returncode, stdout, stderr) + if retcode is not None: + if hasattr(retcode, "__contains__"): + if proc.returncode not in retcode: + raise ProcessExecutionError(getattr(proc, "argv", None), + proc.returncode, stdout, stderr) + elif proc.returncode != retcode: + raise ProcessExecutionError(getattr(proc, "argv", None), + proc.returncode, stdout, stderr) return proc.returncode, stdout, stderr #=================================================================================================== @@ -179,8 +186,10 @@ def run(self, args = (), **kwargs): :param args: Any arguments to be passed to the process (a tuple) :param retcode: The expected return code of this process (defaults to 0). - In order to disable exit-code validation, pass ``None``. - Note: this argument must be passed as a keyword argument. + In order to disable exit-code validation, pass ``None``. It may also + be a tuple (or any iterable) of expected exit codes. + + .. note:: this argument must be passed as a keyword argument. :param kwargs: Any keyword-arguments to be passed to the ``Popen`` constructor