Capris Tutorial =============== By the end of this tutorial you should have a decent idea behind **Capris**'s philosophy and why it does things in a certain way, as well as how to do common tasks using the library. Installation ------------ To install the latest release version, you can simply:: $ pip install capris Else if you want to install the latest development version it is recommended that you pull and clone the latest git repository, for example:: $ git clone ssh://git@github.com/eugene-eeo/capris $ cd capris $ pip install --editable . If you want to run the tests suite you can execute the following command (substitute ``python`` with your version of Python, i.e. ``python3``):: $ python -m capris.tests Running your first command -------------------------- Start a new REPL instance and import the ``capris`` library, you should be able to do the following:: >>> from capris import Command >>> echo = Command('echo') Once we defined our ``echo`` variable we can then call it with the command line options/flags required, in this case we'll just echo a simple string, and store the response:: >>> response = echo('Hello World!') >>> print(response.std_out) Hello World! Notice that the stdout of the command is stored in the ``response`` object as the ``std_out`` attribute, which you can then manipulate/use. Similarly, the `std_err` attribute contains the contents of stderr. A few other attributes of the ``response`` object are also available, for an exhaustive list:: >>> response.env {...} >>> response.status_code 0 >>> response.history [] >>> response.exception None >>> response.process >>> response.command 'echo' Very frequently we check for whether the command has executed properly by determining if it has exited with a zero exit code. We can do that too with capris, but the `response` object has a special convenience method:: >>> response.ok() True This will definitely help with testing applications due to the extremely readable code that you can write. Piping Stuff Around ------------------- In the shell you have a very convenient piping syntax, for example:: $ git log | grep pattern The **Capris** DSL also has a similar feature, but implemented using operator overloading. For example consider the translation of the previous code block into Python with the **Capris** DSL:: >>> git.log | grep('pattern') Note that neither the ``git`` or ``grep`` commands are mutated, but a new ``Pipe`` object is created. The pipe object allows you to pipe the output of commands to one another in sequence. You can also programmatically build up the ``Pipe`` object:: >>> pipe = capris.Pipe() >>> pipe += command Or alternatively if you prefer using methods, you can call the ``append`` method and pass a pipe object into a function which expects an object with an ``append`` method:: >>> pipe.append(command) You can also pipe pipes to one another. For a very simple example, using the syntactic sugar:: >>> pipe | echo Note that using the ``|`` operator will always create a new pipe to allow for easy composability of commands. Sometimes this is undesirable and you may need to use the ``append`` method or the ``+=`` operator to mutate the pipe. Similar to the `Command` class, you can run the pipe object using the `run` method. IO Redirection -------------- In the shell we often redirect file contents into the stdin and redirect the stdout of a command into another file, for example:: $ file.txt > echo > copy.txt You can also do that easily using the **Capris** DSL, although it is more involved due to the need for `correctness` in the API. But basically to redirect:: >>> iostream = echo.iostream >>> open('file.txt') > iostream > open('copy.txt', 'w') >>> iostream.run() Notice that the ``>`` and ``<`` operators **will** mutate the iostream object. If you do not want this behaviour you should create a new iostream object by using the ``iostream`` property. Small Quirks ------------ To pass data to stdin, you can use the ``data`` keyword argument (it is more readable) and assign it to a string:: >>> command.run(data='strings') This can be an alternative to using a file-like object like ``StringIO`` to store the string value. You can also specify a timeout for the command being ran and updates to the environment, for example:: >>> command.run(timeout=5) >>> command.run(env={'OPTION':'value'}) If you want to persist the updates to the environment variables the recommended way is to change the ``env`` attribute, for example:: >>> command.env = {'OPTION':'value'} This will ensure that all subcommands will also run with the updates to the environment variables. Note that environment variables are not copied from base command to subcommand. Instead, lookups are made up the internal command hierarchy. For example, if you have the following command, you will get the tree following it:: git.log.subcommand git |-- log |-- subcommand