Detonation velocity python-fire

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

Basic

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

Create multiple commands

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:

Simply call only fire

--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

pass dict and fire

--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,
  })

Pass an instance of a command-only class

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)

Pass the command-only class itself

--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

Command grouping

--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.

Access class properties

--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

Apply a function to the return value of a command

--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

Customize the command class for display

--If the return value of the called method is some class, that __str__ method will be called.

Various function calls from cli

--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"

Other

Make a cli without embedding fire in the code itself

def hello(name):
  return 'Hello {name}!'.format(name=name)
python -m fire example hello --name=World
# => Hello World!

Argument name specification in cli

--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

Whether the argument passed by cli is required or optional

--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!

How to pass list as an argument of cli

python some.py "[test, test1]”

Add a help message to arg

--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
	"""

Recommended Posts

Detonation velocity python-fire
[Easy detonation velocity 2nd] Deploy Streamlit to heroku