- Pipe data between Python functions in the command line.
- Create command line functions from Python scripts without modifying the scripts.
- Automatically parse input expressions, and serialize outputs.
- Simplify the process of reading from and writing to files.
- Facilitate interaction with other command line tools.
- Support for boolean flags and keyword arguments.
- Small, single-file source (~100 lines).
Features:
Lack.py Call Format
Setup for Examples
Simple Function Call
Boolean Flags
Named Arguments
Piped Data
File Input and Output
Composition with other Command Line Tools
Other Info:
Comparison with Similar Tools
Sample Output
./lack.py script.py funcName [args...]
The script gameOfLife.py
contains functions for running simulations of
Conway's Game of Life (cellular autonomaton).
The examples use the following aliases:
$ alias simulate="./lack.py gameOfLife.py simulate"
$ alias showCells="./lack.py gameOfLife.py showCells"
$ alias randomCells="./lack.py gameOfLife.py randomCells"
The variable initCells
contains an array of arrays of binary digits
(see initCells.txt)
python:
>>> simulate(initCells, 100)
using lack.py:
$ simulate initCells.txt 100
Positional Python arguments become positional command line arguments.
If the name of a file is given as an argument, the contents of that file become the argument.
If the given file contains a Python expression, the expression is parsed and the resulting
value becomes the argument.
Similarly, Python expressions given as positional arguments are parsed to their values.
Watch out for spaces in command line expressions: [1,2,3]
will work, [1, 2, 3]
will cause an error.
python:
>>> simulate(initCells, 100, showSteps=True)
using lack.py:
$ simulate initCells.txt 100 --showSteps
A flag of the form --flagName
sets the keyword-argument flagName
to the value True
.
For example: --flagName
is equivalent to flagName=True
.
python:
>>> simulate(initCells, 100, showSteps=True, delay=.04)
using lack.py:
$ simulate initCells.txt 100 --showSteps delay=.04
Named Python arguments become named command line arguments.
python:
>>> print(showCells(simulate(randomCells(10, 30), 100)))
using lack.py:
$ randomCells 10 30 | simulate 100 | showCells
A value sent through a pipe becomes the first argument to the function.
If the piped text contains a Python expression, the expression is parsed and its value becomes the argument.
For example: arg0 | foo arg1
is equivalent to foo arg0 arg1
.
A Note about Serialization:
Lack.py automatically serializes return values using Python's repr
function.
Since scripts run with lack.py always return text, their outputs can be easily written to files
or piped into other text-based command line programs. This does mean however that data piped between scripts can only
contain simple Python values: strings, lists, dicts, numbers, etc. (specifically, anything which can be parsed by ast.literal_eval
) which does not include functions, instances of user-defined classes, etc.
python:
import ast
with open("initCells.txt", "r") as file:
initCells = ast.literal_eval(file.read())
with open("output.txt", "w") as file:
output = showCells(simulate(initCells, 100))
file.write(output)
using lack.py:
$ simulate initCells.txt 100 | showCells >> output.txt
The file initCells.txt
contains a Python expression (a list of lists), which is
parsed by the ast.literal_eval
function before it becomes an argument.
Piped argument strings containing Python expressions are also parsed in this way.
For example: simulate initCells.txt 100
is equivalent to cat initCells.txt | simulate 100
.
python:
import ast
import urllib.request
url = "https://averyn.scripts.mit.edu/lack/initCells.txt"
initCells = ast.literal_eval(urllib.request.urlopen(url).read().decode('utf-8'))
simulate(initCells, 100, showSteps=True, delay=.04)
using lack.py:
$ curl -s https://averyn.scripts.mit.edu/lack/initCells.txt | simulate 100 --showSteps delay=.04
Since command line functions made with lack.py always take strings as arguments and return string values, they can be used with many existing command line tools.
google/python-fire
- supports pip for installation
- focused on objects, rather than functions
- many options for working with classes / methods
- includes help and debugging options
- doesn't support piped data or filename arguments
- requires modification of original script (import)
- larger codebase, more complex overall
shmuelamar/cbox
- supports pip for installation
- support for error handling, concurrency, streams, documentation
- supports piped data
- doesn't support filename arguments
- requires default values or type annotations for expression parsing
- requires modification of original script (import, decorators)
- larger codebase, more complex overall
tellapart/commandr
- supports pip, easy_install for installation
- automatic documentation
- supports boolean arguments, expression parsing
- doesn't support piped data or filename arguments
- requires modification of original script (import, decorators)
- larger codebase
$ randomCells 10 30 | simulate 20 --showSteps delay=.1
iteration 1 of 20:
[ ]
[ ]
[ ]
[ * * ]
[ * * ]
[ * * ]
[ * * ]
[ * * * * ]
[ ]
[ ]
iteration 2 of 20:
[ ]
[ ]
[ ]
[ * * * ]
[ * ]
[ * * ]
[ * * * * ]
[ * * * ]
[ * ]
[ ]
iteration 3 of 20:
[ ]
[ ]
[ * ]
[ * * ]
[ * * ]
[ * * ]
[ * * * ]
[ * * * * * ]
[ ]
[ ]
iteration 4 of 20:
[ ]
[ ]
[ * * ]
[ * * * ]
[ * * ]
[ * * ]
[ * * * * * ]
[ * * * * * ]
[ * ]
[ ]
iteration 5 of 20:
[ ]
[ ]
[ * * ]
[ * * ]
[ * * ]
[ * * * ]
[ * * * * ]
[ * * ]
[ * * * ]
[ ]
iteration 6 of 20:
[ ]
[ ]
[ * ]
[ * * * * ]
[ * * ]
[ * * * ]
[ * * * ]
[ * * * * ]
[ * ]
[ * ]
iteration 7 of 20:
[ ]
[ ]
[ * * * ]
[ * * * * ]
[ * * * ]
[ * * ]
[ * * * * * ]
[ * * * * ]
[ * * * ]
[ ]
iteration 8 of 20:
[ ]
[ * ]
[ * * * * ]
[ * ]
[ * * ]
[ * * * * ]
[ * * * * ]
[ * * * * ]
[ * * ]
[ * ]
iteration 9 of 20:
[ ]
[ * * * ]
[ * * * * ]
[ * * * * ]
[ * * * ]
[ * * * * ]
[ * * * * * * ]
[ * * * * * * ]
[ * * * ]
[ * ]
iteration 10 of 20:
[ * ]
[ * * ]
[ * ]
[ * * ]
[ * * ]
[ * * * ]
[ * * * * * * * ]
[ * * * * ]
[ * * ]
[ * * * ]
iteration 11 of 20:
[ * ]
[ ]
[ * * ]
[ * * ]
[ * * * ]
[ * * * * * * ]
[ * * * * * * ]
[ * * * * * * * * * ]
[ * * * ]
[ * * * ]
iteration 12 of 20:
[ * * * ]
[ ]
[ * * * ]
[ * ]
[ * * * * ]
[ * * * ]
[ * * * * * * * ]
[ * * * * * ]
[ * * ]
[ ]
iteration 13 of 20:
[ * ]
[ * * ]
[ * * ]
[ * * * ]
[ * * ]
[ * * * * * * * ]
[ * * * * * ]
[ * * * * * ]
[ * * ]
[ * ]
iteration 14 of 20:
[ * * * ]
[ * * ]
[ * * ]
[ * ]
[ * * * * * * * ]
[ * * * * * ]
[ * * * * * * * ]
[ * * * * * * * ]
[ * ]
[ ]
iteration 15 of 20:
[ * ]
[ * * * ]
[ * * ]
[* * * * ]
[ * * * * * * * ]
[ * * * * * * ]
[ * * * * ]
[ * * * * * * * ]
[ * * * ]
[ * ]
iteration 16 of 20:
[ * * * ]
[ * * ]
[* * * * ]
[* * ]
[* * * * * * * * ]
[ * * * * * * * ]
[ * * * * * * * ]
[ * * * * * * ]
[ * * * * ]
[ * ]
iteration 17 of 20:
[ * ]
[ * * * * * ]
[* * * * ]
[* * * * *]
[* * * * * * * * * * ]
[ * * * ]
[ * * * * ]
[ * * * * * * * * ]
[ ]
[ * * * * ]
iteration 18 of 20:
[ * * * * * * ]
[* * * ]
[ * *]
[ * * * * * * * *]
[* * * * * * * * * * * *]
[ * * * * * * ]
[ * * * * * * ]
[ * * * * * ]
[ * * * * * * ]
[ * ]
iteration 19 of 20:
[ * * * * ]
[* * * * * * ]
[ * * * * * * *]
[ * * * * * * * * *]
[* * * * * * * *]
[* * * * * ]
[ * * * * ]
[ * * * * * * * * ]
[ * * * ]
[ ]
iteration 20 of 20:
[* * ]
[ * ]
[ * * * *]
[ * * * * ]
[ * * * * * * ]
[* * * * * * *]
[ * * * * * * * * ]
[ * * * * * * * * * ]
[ * * * * ]
[ * ]