I'm using Typer to write a command line program using python.
Here's an example script of were I'm running into trouble.
import typer
app = typer.Typer()
@app.command()
def hello(name):
typer.echo(f'Hello!')
@app.command()
def goodbye():
typer.echo(f'Goodbye.')
class Grettings:
@app.command()
def aloha(self):
typer.echo('Aloha!')
@app.command()
def bonjour(self):
typer.echo('Bonjour!')
if __name__ == '__main__':
app()
When the following commands are entered into the terminal the expected output is given.
python main.py hello
python main.py goodbye
However, when the class methods are called, I get the following exception.
python main.py aloha
python main.py bonjour
Usage: main.py aloha [OPTIONS] SELF
Try 'main.py aloha --help' for help.
Error: Missing argument 'SELF'.
Clearly, this is from the class not being initialized, yet. But it seems like it'd be a common problem, so I assume there's a simple solution to the issue.
Possible solutions I've found include using decorators on the class/methods being used, or using a special class that'd need to be inherited to "expose" the class methods.
Hint from answer of Can a decorator of an instance method access the class? :
Any decorator is called BEFORE class is built, so is unknown to the decorator.
Typer tries to invoke the callback as Grettings().aloha()
.
This will fail in Python with error:
TypeError: hallo() missing 1 required positional argument: 'self'
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.
I changed 3 things:
Grettings
into Greetings
(spelling)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()
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With