Open techtonik opened 7 years ago
That sounds like a great idea. I don't know much about pbs, so would appreciate help writing it from someone making the transition. :)
@henryiii the job should be easy for any student or for https://opensourcefriday.com/
pbs
and plumbum
thereIf not the lack of time I would do it myself.
As a bonus points, it is possible to contribute to MIT SDG project by porting https://github.com/sdg-mit/gitless to plumbum
and wait for tests green lights.
https://pypi.python.org/pypi/pbs got discontinued and its successor
sh
lost Windows support. Redoingpbs
/sh
help page inplumbum
can help users migrate and/or switch between libraries for tests.PBS is a unique subprocess wrapper that maps your system programs to Python functions dynamically. PBS helps you write shell scripts in Python by giving you the good features of Bash (easy command calling, easy piping) with all the power and flexibility of Python.
PBS is not a collection of system commands implemented in Python.
Getting
$> pip install pbs
Usage
The easiest way to get up and running is to import pbs directly or import your program from pbs:
A less common usage pattern is through PBS Command wrapper, which takes a full path to a command and returns a callable object. This is useful for programs that have weird characters in their names or programs that aren't in your $PATH:
The last usage pattern is for trying PBS through an interactive REPL. By default, this acts like a star import (so all of your system programs will be immediately available as functions):
$> python pbs.py
Examples
Executing Commands
Commands work like you'd expect. Just call your program's name like a function:
Note that these aren't Python functions, these are running the binary commands on your system dynamically by resolving your PATH, much like Bash does. In this way, all the programs on your system are easily available in Python.
You can also call attributes on commands. This translates to the command name followed by the attribute name:
It turns out this is extremely useful for commands whose first argument is often another sub-command (like git, svn, time, sudo, etc). See "Baking" for an advanced usage of this.
Keyword Arguments
Keyword arguments also work like you'd expect: they get replaced with the long-form and short-form commandline option:
Piping
Piping has become function composition:
Redirection
PBS can redirect the standard and error output streams of a process to a file. This is done with the special _out and _err keyword arguments. You can pass a filename or a file object as the argument value. When the name of an already existing file is passed, the contents of the file will be overwritten.
PBS can also redirect the error output stream to the standard output stream, using the special _err_to_out=True keyword argument.
Sudo and With Contexts
Commands can be run within a "with" context. Popular commands using this might be "sudo" or "fakeroot":
If you need to run a command in a with context AND call it, for example, specifying a -p prompt with sudo, you need to use the "_with" keyword argument. This let's the command know that it's being run from a with context so it can behave correctly.
Background Processes
Commands can be run in the background with the special _bg=True keyword argument:
You can also pipe together background processes!
This lets you start long-running commands at the beginning of your script (like a file download) and continue performing other commands in the foreground.
Foreground Processes
Foreground processes are processes that you want to interact directly with the default stdout and stdin of your terminal. In other words, these are processes that you do not want to return their output as a return value of their call. An example would be opening a text editor:
This will block because pbs will be trying to aggregate the output of the command to python, without displaying anything to the screen. The solution is the "_fg" special keyword arg:
This will open vim as expected and let you use it as expected, with all the input coming from the keyboard and the output going to the screen. The return value of a foreground process is an empty string.
Finding Commands
"Which" finds the full path of a program, or returns None if it doesn't exist. This command is one of the few commands implemented as a Python function, and therefore doesn't rely on the "which" program actually existing.
Baking
PBS is capable of "baking" arguments into commands. This is similar to the stdlib functools.partial wrapper. An example can speak volumes:
The idea is that calling "bake" on a command creates a callable object that automatically passes along all of the arguments passed into "bake". This gets really interesting when you combine this with the attribute access on a command:
Now that the "myserver" callable represents a baked ssh command, you can call anything on the server easily:
Environment Variables
Environment variables are available much like they are in Bash:
You can set enviroment variables the usual way, through the os.environ mapping:
Now any new subprocess commands called from the script will be able to access that environment variable.
Exceptions
Exceptions are dynamically generated based on the return code of the command. This lets you catch a specific return code, or catch all error return codes through the base class ErrorReturnCode:
Globbing
Glob-expansion is not done on your arguments. For example, this will not work:
You'll get an error to the effect of "cannot access '*': No such file or directory". This is because the "*" needs to be glob expanded:
Commandline Arguments
You can access commandline arguments similar to Bash's $1, $2, etc by using ARG1, ARG2, etc:
You can access the entire argparse/optparse-friendly list of commandline arguments through "ARGV". This is recommended for flexibility:
Weirdly-named Commands
PBS automatically handles underscore-dash conversions. For example, if you want to call apt-get:
PBS looks for "apt_get", but if it doesn't find it, replaces all underscores with dashes and searches again. If the command still isn't found, a CommandNotFound exception is raised.
Commands with other, less-commonly symbols in their names must be accessed directly through the "Command" class wrapper. The Command class takes the full path to the program as a string:
The Command wrapper is also useful for commands that are not in your standard PATH:
Non-standard Exit Codes
Normally, if a command returns an exit code that is not 0, PBS raises an exception based on that exit code. However, if you have determined that an error code is normal and want to retrieve the output of the command without PBS raising an exception, you can use the "_ok_code" special argument to suppress the exception:
In the above example, even though you're trying to list a directory that doesn't exist, you can still get the output from the directory that does exist by telling the command that 2 is an "ok" exit code, so don't raise an exception.
_ok_code can also take a list or tuple of numbers for multiple ok exit codes.