Prodesire

The sample code and history articles involved have been synchronized to the HelloGithub-Team repository

One, foreword

In our last article, we covered fire’s subcommands, nested commands, and property access, and today we’ll dig deeper into the rest of fire’s features.

This series of articles uses Python 3 as the interpreter by default. If you are still using Python 2, be aware of the differences in syntax and library usageCopy the code

Second, the function of

2.1 Minimal Command implementation

In the previous section, we introduced how you can implement a command-line program by defining a single function. Such as:

import fire

def english(a):
  return 'Hello, fire! '

def chinese(a):
  return 'Hello, Fire! '

if __name__ == '__main__':
  fire.Fire()
Copy the code

But that’s not the easiest way to do it. Fire even lets you implement the command line by defining variables! The above example can be written as follows:

import fire

english = 'Hello, fire! '
chinese = 'Hello, Fire! '

if __name__ == '__main__':
  fire.Fire()
Copy the code

2.2 Chain call

In the Fire CLI, you can continuously process the last result through chain calls.

This can be done simply by returning self in the instance method.

In the example below, we implement a simple four-order arithmetic command that chain-calls add, sub, mul, and div.

import fire

class Calculator:

  def __init__(self):
    self.result = 0
    self.express = '0'

  def __str__(self):
    return f'{self.express} = {self.result}'

  def add(self, x):
    self.result += x
    self.express = f'{self.express}+{x}'
    return self

  def sub(self, x):
    self.result -= x
    self.express = f'{self.express}-{x}'
    return self

  def mul(self, x):
    self.result *= x
    self.express = f'({self.express}) *{x}'
    return self

  def div(self, x):
    self.result /= x
    self.express = f'({self.express}) /{x}'
    return self

if __name__ == '__main__':
  fire.Fire(Calculator)
Copy the code

Add, sub, mul and div in the above code correspond to the logic of addition, subtraction, multiplication and division, respectively. Each method takes an x parameter as the number involved in the operation and returns self, which allows infinite chain calls. After the chain call ends on the command line, the __str__ method is finally called to print out the result.

__str__ is used in fire for custom serialization. If this method is not provided, the help content will be printed after the chain call completes.

For example, we could call:

$python calculator.py add 1 sub 2 mul 3 div 4 ((+1-2)*3)/4 = -0.75 $python calculator.py add 1 sub 2 mul 3 div 4 add 4 3 the mul 2 sub div 1 (((* 3) (0 + 1-2) / 4 + 4-3) * 2) / 1 = 0.5Copy the code

2.3 Position Parameters and Option Parameters

We’ve also made it clear that you don’t have to explicitly define positional or option parameters in fire.

Using the following example, we will refine the use of two types of parameters:

import fire

class Building(object):

  def __init__(self, name, stories=1):
    self.name = name
    self.stories = stories

  def __str__(self):
    return f'name: {self.name}, stories: {self.stories}'

  def climb_stairs(self, stairs_per_story=10):
    yield self.name
    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)
Copy the code
  • The parameters defined in the constructor (such asnamestories) on the command line only as option arguments (e.g--name--stories). We can call it like this:
$ python example.py --name="Sherrerd Hall" --stories=3
Copy the code
  • The parameters defined in the constructor can be placed anywhere in the command. For example, the following two calls are ok:
$ python example.py --name="Sherrerd Hall" climb-stairs --stairs-per-story 10
$ python example.py climb-stairs --stairs-per-story 10 --name="Sherrerd Hall"
Copy the code
  • Default parameters defined in constructors and normal methods (e.gstories) is optional on the command line. We can call it like this:
$ python example.py --name="Sherrerd Hall"
Copy the code
  • Parameters defined in normal methods such asstairs_per_story) can be either positional or option arguments on the command line. We can call it like this:
# as a positional argument
$ python example.py --name="Sherrerd Hall" climb_stairs 10
# as an option parameter
$ python example.py --name="Sherrerd Hall" climb_stairs --stairs_per_story=10
Copy the code
  • The bar (-) and underline (_) is equivalent. So it can also be called like this:
# as an option parameter
$ python example.py --name="Sherrerd Hall" climb_stairs --stairs-per-story=10
Copy the code

In addition, fire supports defining *args and **kwargs in functions.

import fire

def fargs(*args):
  return str(args)


def fkwargs(**kwargs):
  return str(kwargs)

if __name__ == '__main__':
  fire.Fire()
Copy the code
  • In the function*argsIs a positional argument on the command line. We can call it like this:
$ python example.py fargs a b c
Copy the code
  • In the function**kwargsOn the command line as an option parameter. We can call it like this:
$ python example.py fargs --a a1 --b b1 --c c1
Copy the code
  • By separator-You can explicitly tell that the delimiter is followed by a subcommand, not a command parameter. Take a look at the following example:
# No separator is used, upper is used as positional argument
$ python example.py fargs a b c upper
('a'.'b'.'c'.'upper')

# uses the delimiter, upper is used as a subcommand
$ python example.py fargs a b c - upper
('A'.'B'.'C')
Copy the code
  • throughfireThe built-in--separatorYou can customize the delimiter. This option parameter needs to be followed by a separate--The back:
$ python example.py a b c X upper -- --separator=X
('A'.'B'.'C')
Copy the code

2.4 Parameter Types

In fire, the type of a parameter is determined by its value. With the following simple code, we can see what type fire resolves when given different values:

import fire
fire.Fire(lambda obj: type(obj).__name__)
Copy the code
$python example.py 10 int $python example.py 10.0float
$ python example.py hello
str
$ python example.py '(1, 2)Py [1,2] list $python example.py True bool $python example.py {name: David} dictCopy the code

If you want to pass numbers as strings, you need to be careful with quotes, either by raising them or by escaping them:

# the number 10
$ python example.py 10
int
# does not handle quotation marks and is still the number 10
$ python example.py "10"
int
# put quotes around it, so it's string "10"
$ python example.py '" 10 "'
str
# another way to cause quotes
$ python example.py "' 10 '"
str
# Escape quotes
$ python example.py \"10\"
str
Copy the code

Consider a more complex scenario where you pass a dictionary with strings in it, so be careful with quotes:

# Recommended Practices
$ python example.py '{"name": "David Bieber"}'
dict
# is also ok
$ python example.py {"name":'"David Bieber"'}
dict
# error, will be resolved to a string
$ python example.py {"name":"David Bieber"}
str
# error, not reported as a single argument (because there is a space in the middle)
$ python example.py {"name": "David Bieber"}
<error>
Copy the code

If True or False is considered Boolean, fire also supports setting name to True via –name or False via –noname:

$ python example.py --obj=True
bool
$ python example.py --obj=False
bool
$ python example.py --obj
bool
$ python example.py --noobj
bool
Copy the code

2.5 Fire built-in option parameters

Fire has some built-in options to make it easier to use command-line programs. To use the built-in options function, add the option argument after –. In the previous section, we introduced the –separator argument. In addition to this, fire supports the following options:

  • command -- --helpList detailed help information
  • command -- --interactiveEnter interactive mode
  • command -- --completion [shell]Generate automatic completion scripts for CLI programs to support automatic completion
  • command -- --traceGet the command’s Fire trace to see what happens after Fire is called
  • command -- --verboseGets details including private members

Third, summary

Fire makes command-line programs particularly easy to implement, and this article focuses on chained calls, option arguments, positional arguments, parameter types, and built-in option arguments. Fire doesn’t have many concepts. It’s a true practice of “keep the simple for others and the complex for yourself.”

So much for fire, which is definitely a great tool for writing command line programs. In the next article, we’ll still put fire into action by implementing a simple Git program.


“Explain Open Source Project series” — let the people who are interested in open source projects not be afraid, let the initiator of open source projects not be alone. Follow along as you discover the joys of programming, use, and how easy it is to get involved in open source projects. Welcome to leave a message to contact us, join us, let more people fall in love with open source, contribute to open source ~