Call another click command from a click command

后端 未结 3 412
栀梦
栀梦 2021-01-01 09:37

I want to use some useful functions as commands. For that I am testing the click library. I defined my three original functions then decorated as click.co

相关标签:
3条回答
  • 2021-01-01 09:52

    Due to the click decorators the functions can no longer be called just by specifying the arguments. The Context class is your friend here, specifically:

    1. Context.invoke() - invokes another command with the arguments you supply
    2. Context.forward() - fills in the arguments from the current command

    So your code for add_name_and_surname should look like:

    @click.command()
    @click.argument('content', required=False)
    @click.option('--to_stdout', default=False)
    @click.pass_context
    def add_name_and_surname(ctx, content, to_stdout=False):
        result = ctx.invoke(add_surname, content=ctx.forward(add_name))
        if to_stdout is True:
            sys.stdout.writelines(result)
        return result
    

    Reference: http://click.pocoo.org/6/advanced/#invoking-other-commands

    0 讨论(0)
  • 2021-01-01 09:52

    I found these solutions more complicated. I wanted this function below to be called from another place in another package:

    @click.command(help='Clean up')
    @click.argument('path', nargs=1, default='def')
    @click.option('--info', '-i', is_flag=True,
                  help='some info1')
    @click.option('--total', '-t', is_flag=True,
                  help='some info2')
    def clean(path, info, total):
    #some definition, some actions
    
    #this function will help us
    def get_function(function_name):
    if function_name == 'clean':
        return clean
    

    I have another package, so, I would like click command above in this pack

    import somepackage1 #here is clean up click command
    from click.testing import CliRunner
    
    
    @check.command(context_settings=dict(
               ignore_unknown_options=True,
              ))
    @click.argument('args', nargs=-1)
    @click.pass_context
    def check(ctx, args):
        runner = CliRunner()
    
        if len(args[0]) == 0:
            logger.error('Put name of a command')
    
        if len(args) > 0:
            result = runner.invoke(somepackage1.get_function(args[0]), args[1:])
            logger.print(result.output)
        else:
            result = runner.invoke(somepackage1.get_function(args[0]))
        logger.print(result.output)
    

    So, it works.

    python somepackage2 check clean params1 --info
    
    0 讨论(0)
  • 2021-01-01 10:00

    When you call add_name() and add_surname() directly from another function, you actually call the decorated versions of them so the arguments expected may not be as you defined them (see the answers to How to strip decorators from a function in python for some details on why).

    I would suggest modifying your implementation so that you keep the original functions undecorated and create thin click-specific wrappers for them, for example:

    def add_name(content, to_stdout=False):
        if not content:
            content = ''.join(sys.stdin.readlines())
        result = content + "\n\tadded name"
        if to_stdout is True:
            sys.stdout.writelines(result)
        return result
    
    @click.command()
    @click.argument('content', required=False)
    @click.option('--to_stdout', default=True)
    def add_name_command(content, to_stdout=False):
        return add_name(content, to_stdout)
    

    You can then either call these functions directly or invoke them via a CLI wrapper script created by setup.py.

    This might seem redundant but in fact is probably the right way to do it: one function represents your business logic, the other (the click command) is a "controller" exposing this logic via command line (there could be, for the sake of example, also a function exposing the same logic via a Web service for example).

    In fact, I would even advise to put them in separate Python modules - Your "core" logic and a click-specific implementation which could be replaced for any other interface if needed.

    0 讨论(0)
提交回复
热议问题