writing command line for class methods using python and typer

Solution 1:

Issue with decorated instance methods

Typer tries to invoke the callback as Grettings().aloha(). This will fail in Python with error:

TypeError: hallo() missing 1 required positional argument: 'self'

Demo of command invocation in Typer

See following demo recorded in Python shell:

Part 1: How it works (with static functions, no self argument)

>>> import typer
>>> app = typer.Typer()
>>> app
<typer.main.Typer object at 0x7f0713f59c18>
>>> app.__dict__
{'_add_completion': True, 'info': <typer.models.TyperInfo object at 0x7f0713f59c50>, 'registered_groups': [], 'registered_commands': [], 'registered_callback': None}
>>> @app.command()
... def hello():
...     typer.echo('hello')
... 
>>> app.__dict__['registered_commands']
[<typer.models.CommandInfo object at 0x7f0711e69cf8>]
>>> app.__dict__['registered_commands'][0].cls
<class 'typer.core.TyperCommand'>
>>> app.__dict__['registered_commands'][0].callback
<function hello at 0x7f070f539378>
>>> app.__dict__['registered_commands'][0].callback()
hello

Part 2: How it wont work (with instance methods, self argument required)

>>> class German:
...     @app.command()
...     def hallo(self):
...         typer.echo('Hallo')
... 
>>> app.__dict__['registered_commands'][1]
<typer.models.CommandInfo object at 0x7f070f59ccf8>
>>> app.__dict__['registered_commands'][1].callback
<function German.hallo at 0x7f070f539158>
>>> app.__dict__['registered_commands'][1].callback()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hallo() missing 1 required positional argument: 'self'
>>> app.__dict__['registered_commands'][1].callback(German())
Hallo

Note: In the last statement I a fresh instance was passed as argument self to the callback and the invocation succeeded with expected output.

Fixed code

I changed 3 things:

  1. Renamed your class Grettings into Greetings (spelling)
  2. Redefined the 2 existing methods as static class methods like Barmar's comment suggested.
  3. Additionally I added a new instance method nihao(self) to demonstrate the failure.
import typer

app = typer.Typer()


@app.command()
def hello(name):
    typer.echo(f'Hello!')

@app.command()
def goodbye():
    typer.echo(f'Goodbye.')

class Greetings:
    @app.command()
    def aloha():              # function or class-method (implicitly static)
        typer.echo('Aloha!')

    @staticmethod             # explicitly static
    @app.command()
    def bonjour():            # no self argument!
        typer.echo('Bonjour!')

    @app.command()
    def nihao(self):          # callback invocation fails because missing self argument
        typer.echo('Nihao!')


if __name__ == '__main__':
    app()

Behavior and output as expected

Although the offered commands still list nihao as available, the invocation of it will fail equally as you experienced.

But the command-decorated static methods can be invoked now.

$ python3 SO_typer.py --help
Usage: SO_typer.py [OPTIONS] COMMAND [ARGS]...

Options:
  --install-completion [bash|zsh|fish|powershell|pwsh]
                                  Install completion for the specified shell.
  --show-completion [bash|zsh|fish|powershell|pwsh]
                                  Show completion for the specified shell, to
                                  copy it or customize the installation.

  --help                          Show this message and exit.

Commands:
  aloha
  bonjour
  goodbye
  hello
  nihao

🇨🇳️ Chinese greeting fails because no argument self passed with invocation:

$ python3 SO_typer.py nihao
Usage: SO_typer.py nihao [OPTIONS] SELF
Try 'SO_typer.py nihao --help' for help.

Error: Missing argument 'SELF'.

🏴󠁵󠁳󠁨󠁩󠁿 Hawaiian greeting works because static invocation possible now:

$ python3 SO_typer.py aloha
Aloha!

See also

  • Do we really need @staticmethod decorator in python to declare static method
  • Click Documentation (8.0.x): Callback Invocation