Home > Back-end >  Splitting python click options betwen functions
Splitting python click options betwen functions

Time:04-03

My codebase has multiple scripts, say sc1.py and sc2.py. Their code looks like this (just replace 1 for 2 to imagine what the other is like):

@click.command()
@click.option(--op1, default="def1", required=True)
def sc1(op1):
    pass

if __name__ == "__main__":
    sc1()

What I want to do is write a wrapper that handles concerns like loading the config file (and handling missing ones appropriately), configuring the logger, etc.

The logger, as an example, requires some options, e.g. the log level, and this option doesn't appear in any of the existing scripts. I'd like to handle it at the wrapper level (since the wrapper will deal with common concerns like log configuration).

That is what the wrapper might look like:

@click.command()
@click.option(--level, default=INFO, required=False)
def wrapper(level, wrapped_main_function):
   # use level to setup logger basic config.
   wrapped_main_function()

Then I would modify the "main" in the scripts (sc1.py, sc2.py etc):

from wrap_module import wrapper

@click.command()
@click.option(--op1, default="def1", required=True)
def sc1(op1):
    pass

if __name__ == "__main__":
    wrapper(???, sc1)

Put into words I am trying to split the options between two functions, say wrapper and sc1, and have wrapper deal with its own dedicated options and then call sc1, forwarding the remaining command line options. All while wrapper is not aware of what options those might be, since the whole point is to write something generic that can be used for all the scripts and having wrapper deal with the commonalities.

The command line should look something like:

python sc.py --op1 something --level debug

I can't figure out the right syntax to do what I want.

CodePudding user response:

I think you can get close to what you want with a slightly different approach. First, we have the following in common.py:

import click


def add_common_options(func):
    func = click.option("--level")(func)
    return func


def handle_common_options(level):
    print("log level is", level)

And then in sc1.py, we have:

import click
from common import add_common_options, handle_common_options


@click.command()
@click.option("--op1")
@add_common_options
def sc1(op1, **common_options):
    handle_common_options(**common_options)
    print("op1 is", op1)


if __name__ == "__main__":
    sc1()

(With sc2.py implemented in a similar fashion.)

This gets you the behavior you want. The help output for sc1.py looks like:

Usage: sc1.py [OPTIONS]

Options:
  --op1 TEXT
  --level TEXT
  --help        Show this message and exit.

And you can run a command line like this:

$ python sc1.py --op1 foo --level info
log level is info
op1 is foo
  • Related