python-fire/guide.md at master · google/python-fire · GitHub
--Google library that makes it easy to create cli with python ――It's so easy to use argparser or something like that
Pass the function to fire and call it and you're done.
import fire
def hello(name="World"):
return "Hello %s!" % name
if __name__ == '__main__':
fire.Fire(hello)
The code above turns into a cli command as shown below.
python hello.py # Hello World!
python hello.py --name=David # Hello David!
If you pass a class, you can call the method from cli.
import fire
class Calculator(object):
"""A simple calculator class."""
def double(self, number):
return 2 * number
if __name__ == '__main__':
fire.Fire(Calculator)
python calculator.py double 10 # 20
python calculator.py double --number=15 # 30
First of all, what happens to the cli command is as follows.
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
There are multiple ways to achieve this:
--All functions in the file are converted to cli commands ――Even if there is a function that is easy but unnecessary, it becomes a command
import fire
def add(x, y):
return x + y
def multiply(x, y):
return x * y
if __name__ == '__main__':
fire.Fire()
--Even if there is a variable in the file, it can also be called with a command
import fire
english = 'Hello World'
spanish = 'Hola Mundo'
fire.Fire()
$ python example.py english
Hello World
$ python example.py spanish
Hola Mundo
--You can select the required function from the file and use it as a cli command.
import fire
def add(x, y):
return x + y
def multiply(x, y):
return x * y
if __name__ == '__main__':
fire.Fire({
'add': add,
'multiply': multiply,
})
import fire
class Calculator(object):
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
if __name__ == '__main__':
calculator = Calculator()
fire.Fire(calculator)
--It is more convenient than passing an instance because even the instance variable passed when instantiating can be used as an argument of cli.
import fire
class BrokenCalculator(object):
def __init__(self, offset=1):
self._offset = offset
def add(self, x, y):
return x + y + self._offset
def multiply(self, x, y):
return x * y + self._offset
if __name__ == '__main__':
fire.Fire(BrokenCalculator)
$ python example.py add 10 20 --offset=0
30
$ python example.py multiply 10 20 --offset=0
200
--Complex commands can be realized by creating multiple command classes and nesting them via instance variables.
class IngestionStage(object):
def run(self):
return 'Ingesting! Nom nom nom...'
class DigestionStage(object):
def run(self, volume=1):
return ' '.join(['Burp!'] * volume)
def status(self):
return 'Satiated.'
class Pipeline(object):
def __init__(self):
self.ingestion = IngestionStage()
self.digestion = DigestionStage()
def run(self):
self.ingestion.run()
self.digestion.run()
if __name__ == '__main__':
fire.Fire(Pipeline)
$ python example.py run
Ingesting! Nom nom nom...
Burp!
$ python example.py ingestion run
Ingesting! Nom nom nom...
$ python example.py digestion run
Burp!
$ python example.py digestion status
Satiated.
--If you pass a class to fire, you can call instance variables instead of methods.
from airports import airports
import fire
class Airport(object):
def __init__(self, code):
self.code = code
self.name = dict(airports).get(self.code)
self.city = self.name.split(',')[0] if self.name else None
if __name__ == '__main__':
fire.Fire(Airport)
$ python example.py --code=JFK code
JFK
$ python example.py --code=SJC name
San Jose-Sunnyvale-Santa Clara, CA - Norman Y. Mineta San Jose International (SJC)
$ python example.py --code=ALB city
Albany-Schenectady-Troy
--You can chain commands to call a method on the return value
--In the example below, the str.upper
method is called
#The code itself is the Airport example above
$ python example.py --code=ALB city upper
ALBANY-SCHENECTADY-TROY
--It is convenient because you can chain methods one after another if all the methods of the command class return themselves.
import fire
class BinaryCanvas(object):
"""A canvas with which to make binary art, one bit at a time."""
def __init__(self, size=10):
self.pixels = [[0] * size for _ in range(size)]
self._size = size
self._row = 0 # The row of the cursor.
self._col = 0 # The column of the cursor.
def __str__(self):
return '\n'.join(' '.join(str(pixel) for pixel in row) for row in self.pixels)
def show(self):
print(self)
return self
def move(self, row, col):
self._row = row % self._size
self._col = col % self._size
return self
def on(self):
return self.set(1)
def off(self):
return self.set(0)
def set(self, value):
self.pixels[self._row][self._col] = value
return self
if __name__ == '__main__':
fire.Fire(BinaryCanvas)
$ python example.py move 3 3 on move 3 6 on move 6 3 on move 6 6 on move 7 4 on move 7 5 on
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
--If the return value of the called method is some class, that __str__
method will be called.
--There are multiple options for calling the following files --Hyphens and underscores can be replaced --The argument can be either position or named --Named arguments and their value = may or may not be present
import fire
class Building(object):
def __init__(self, name, stories=1):
self.name = name
self.stories = stories
def climb_stairs(self, stairs_per_story=10):
for story in range(self.stories):
for stair in range(1, stairs_per_story):
yield stair
yield 'Phew!'
yield 'Done!'
if __name__ == '__main__':
fire.Fire(Building)
$ python example.py --name="Sherrerd Hall" --stories=3 climb_stairs 10
$ python example.py --name="Sherrerd Hall" climb_stairs --stairs_per_story=10
$ python example.py --name="Sherrerd Hall" climb_stairs --stairs-per-story 10
$ python example.py climb-stairs --stairs-per-story 10 --name="Sherrerd Hall"
def hello(name):
return 'Hello {name}!'.format(name=name)
python -m fire example hello --name=World
# => Hello World!
--Method arguments are applied as arguments in that order without specifying a name in cli
--But if you specify it with — <arg-name>
, you can apply it by name.
def say(name, content):
print(name + ", " + content)
fire.Fire(say)
python say.py taro hello # taro, hello
python say.py --content=hello --name=taro # taro, hello
--Basically, all arguments are required, and if you do not pass the arguments with cli, an error will occur. --But if you specify a default value for the argument in the method, it becomes optional
def say(name, content="hello"):
print(name + ", " + content)
fire.Fire(say)
python say.py taro # => taro, hello
python say.py # => error!
python some.py "[test, test1]”
--If you write a description of the argument in docstring, it will be help
--In the following, if you do python some.py -h
, ʻa` will be explained as hogehoge.
def main(a):
"""
a: hogehoge
"""