I would like to have some CLI options to be re-used between two programs that share a common library file. Currently, each file has a similar structure to the following:
from .shared import G
@click.command()
@click.option("--thing1", …)
@click.optoin("--thing2", …)
…
def main(**kwargs):
g = G()
g.do_your_thing()
What I'd like to do is move all that to the __init__
of G
or a subclass of G
.
class G:
@click.command()
@click.option("--thing1")
def __init__(**kwargs):
pass
class G6(G):
@click.command()
@click.option("--specific-flag")
def __init__(**kwargs):
super().__init__(**kwargs)
if kwargs.get("--specific-flag"):
do_something()
self.seven = 7
However, when I try this, I get the following error.
Usage: something.py [OPTIONS]
Try 'something.py --help' for help.
Error: no such option: --thing1
My primary goal with this refactoring is to eliminate lots of duplicative code decorating the main
function of each program by moving the shared decorators to the library.
If this is possible, is it also possible to use an option decorator of the subclass to supersede the same decorator on the base class? I assume the answer is yes.
Based on the answer below, my program now works with a structure similar to the following snippet:
_common_options = [
click.option(…), click.option(…), …
]
def common_options(func):
for option in _common_options:
func = option(func)
return func
class G:
def __init__(**kwargs):
print(kwargs)
@click.command()
@common_options
@click.option("--specific-option", …)
def main(**kwargs):
f = G(**kwargs)
The problem with my original attempt was putting all the @click.whatever
stuff as a decorator of __init__
instead of decorating main
and passing **kwargs
to __init__
.
What I've done previously was create a list of options to be applied to a command or group. For example:
_global_options = [
click.option('-v', '--verbose', count=True, default=0, help='Verbose output.'),
click.option('-n', '--dry-run', is_flag=True, default=False, help='Dry-run mode.')
]
def common_options(func):
for option in reversed(_global_options):
func = option(func)
return func
These would then be applied as such:
click.group(name='my_group')
@common_options
def my_group(*args, **kwargs):
pass
Decorators seem to work really well with Click. Does this help?