call_command argument is required

≡放荡痞女 提交于 2019-12-05 05:38:51

You defined an argument with a '--type' flag, and made it required. That command line will require a string or strings that look like --type avalue.

This looks like the relevant part of call_command:

def call_command(name, *args, **options):
    ....
    parser = command.create_parser('', name)
    if command.use_argparse:
        # Use the `dest` option name from the parser option
        opt_mapping = {sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest
                       for s_opt in parser._actions if s_opt.option_strings}
        arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
        defaults = parser.parse_args(args=args)
        defaults = dict(defaults._get_kwargs(), **arg_options)
        # Move positional args out of options to mimic legacy optparse
        args = defaults.pop('args', ())

It creates a parser, using it's own arguments plus the ones you add.

parser._actions if s_opt.option_strings are the arguments (Actions) that take an option flag (start with - or --). opt_mapping is map between the flag strings (minus the leading -s) and the 'dest' attribute.

arg_options converts your **kwargs to something that can be merged with the parser output.

defaults = parser.parse_args(args=args) does the actual parsing. That is, it's the only code that actually uses the argparse parsing mechanism. So the *args part of your call simulates generating sys.argv[1:] from an interactive call.

Based on that reading I think this should work:

args = [
    '--solr-url', 'http://127.0.0.1:8983/solr/collection1',
    '--type', 'opinions',
    '--update'
    '--everything',
    '--do_commit',
    '--traceback',
}
call_command('cl_update_index', *args)

Instead of **kwargs I am passing in values as a list of strings. Or the two required arguments could be passed in args, and the rest in **kwargs.

args = ['--solr-url', 'http://127.0.0.1:8983/solr/collection1',
    '--type', 'opinions']
kwargs = {
    'update': True,
    'everything': True,
    'do_commit': True,
    'traceback': True,
}
call_command('cl_update_index', *args, **kwargs)

If an argument is required it needs to passed in through *args. **kwargs bypass the parser, causing it to object about missing arguments.


I've downloaded the latest django, but haven't installed it. But here's a simulation of call_command that should test the calling options:

import argparse

def call_command(name, *args, **options):
    """
    Calls the given command, with the given options and args/kwargs.
    standalone simulation of django.core.mangement call_command
    """
    command = name
    """
    ....
    """
    # Simulate argument parsing to get the option defaults (see #10080 for details).
    parser = command.create_parser('', name)
    if command.use_argparse:
        # Use the `dest` option name from the parser option
        opt_mapping = {sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest
                       for s_opt in parser._actions if s_opt.option_strings}
        arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
        defaults = parser.parse_args(args=args)
        defaults = dict(defaults._get_kwargs(), **arg_options)
        # Move positional args out of options to mimic legacy optparse
        args = defaults.pop('args', ())
    else:
        # Legacy optparse method
        defaults, _ = parser.parse_args(args=[])
        defaults = dict(defaults.__dict__, **options)
    if 'skip_checks' not in options:
        defaults['skip_checks'] = True

    return command.execute(*args, **defaults)

class BaseCommand():
    def __init__(self):
        self.use_argparse = True
        self.stdout= sys.stdout
        self.stderr=sys.stderr
    def execute(self, *args, **kwargs):
        self.handle(*args, **kwargs)
    def handle(self, *args, **kwargs):
        print('args: ', args)
        print('kwargs: ', kwargs)
    def create_parser(self, *args, **kwargs):
        parser = argparse.ArgumentParser()
        self.add_arguments(parser)
        return parser
    def add_arguments(self, parser):
        parser.add_argument('--type', required=True)
        parser.add_argument('--update', action='store_true')
        parser.add_argument('--optional', default='default')
        parser.add_argument('foo')
        parser.add_argument('args', nargs='*')

if __name__=='__main__':

    testcmd = BaseCommand()
    # testcmd.execute('one','tow', three='four')

    call_command(testcmd, '--type','typevalue','foovalue', 'argsvalue', update=True)

    args = ['--type=argvalue', 'foovalue', '1', '2']
    kwargs = {
        'solr_url': 'http://127.0.0.1...',
        'type': 'opinions',
        'update': True,
        'everything': True,
    }
    call_command(testcmd, *args, **kwargs)

which produces:

python3 stack32036562.py 
args:  ('argsvalue',)
kwargs:  {'optional': 'default', 'type': 'typevalue', 'update': True, 'skip_checks': True, 'foo': 'foovalue'}
args:  ('1', '2')
kwargs:  {'optional': 'default', 'update': True, 'foo': 'foovalue', 'type': 'opinions', 'skip_checks': True, 'everything': True, 'solr_url': 'http://127.0.0.1...'}

With a bunch of stubs, I can make your cl Command work with my BaseCommand, and the following call works:

clupdate = Command()
args = ['--type','opinions','--solr-url','dummy']
kwargs = {
    'solr_url': 'http://127.0.0.1:8983/solr/collection1',
    #'type': 'opinions',
    'update': True,
    'everything': True,
    'do_commit': True,
    'traceback': True,
}
call_command(clupdate, *args, **kwargs)

performing a stub everything.

Running in update mode...
everything
args:  ()
options:  {'type': 'opinions', 'query': None, 'solr_url': 'http://127.0.0.1:8983/solr/collection1', 'items': None, 'do_commit': True, 'update': True, 'delete': False, 'datetime': None, 'optimize': False, 'skip_checks': True, 'everything': True, 'traceback': True}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!