Call_command argument required

I am trying to use Django call_command in a very similar way to this unanswered question .

As I call it:

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

In theory, this should work as per the docs . But it does not work, just not.

Here is the add_arguments method for my Command class:

 def add_arguments(self, parser): parser.add_argument( '--type', type=valid_obj_type, required=True, help='Because the Solr indexes are loosely bound to the database, ' 'commands require that the correct model is provided in this ' 'argument. Current choices are "audio" or "opinions".' ) parser.add_argument( '--solr-url', required=True, type=str, help='When swapping cores, it can be valuable to use a temporary ' 'Solr URL, overriding the default value that\ in the ' 'settings, eg, http://127.0.0.1:8983/solr/swap_core' ) actions_group = parser.add_mutually_exclusive_group() actions_group.add_argument( '--update', action='store_true', default=False, help='Run the command in update mode. Use this to add or update ' 'items.' ) actions_group.add_argument( '--delete', action='store_true', default=False, help='Run the command in delete mode. Use this to remove items ' 'from the index. Note that this will not delete items from ' 'the index that do not continue to exist in the database.' ) parser.add_argument( '--optimize', action='store_true', default=False, help='Run the optimize command against the current index after ' 'any updates or deletions are completed.' ) parser.add_argument( '--do-commit', action='store_true', default=False, help='Performs a simple commit and nothing more.' ) act_upon_group = parser.add_mutually_exclusive_group() act_upon_group.add_argument( '--everything', action='store_true', default=False, help='Take action on everything in the database', ) act_upon_group.add_argument( '--query', help='Take action on items fulfilling a query. Queries should be ' 'formatted as Python dicts such as: "{\'court_id\':\'haw\'}"' ) act_upon_group.add_argument( '--items', type=int, nargs='*', help='Take action on a list of items using a single ' 'Celery task' ) act_upon_group.add_argument( '--datetime', type=valid_date_time, help='Take action on items newer than a date (YYYY-MM-DD) or a ' 'date and time (YYYY-MM-DD HH:MM:SS)' ) 

No matter what I do here, I get:

CommandError: Error: --type argument required

Any ideas? If you are really interested, you can see all the code here .

+6
source share
1 answer

You defined the argument with the '--type' flag and made it required . This command line will require a line or lines that look like --type avalue .

This looks like the corresponding 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 its own arguments, plus the ones you add.

parser._actions if s_opt.option_strings are the arguments (actions) that accept the option flag (start with - or -). opt_mapping is the map between the flag lines (minus the leading -s) and the dest attribute.

arg_options converts your **kwargs into something that can be combined with parser output.

defaults = parser.parse_args(args=args) the actual parsing is performed. That is, this is the only code that actually uses the argparse parsing argparse . Thus, part of your *args call mimics the generation of sys.argv[1:] from an interactive call.

Based on this 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 pass the values ​​as a list of strings. Or the two required arguments can be passed to args , and the rest to **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 the argument is required , it must go through *args . **kwargs bypasses the parser, forcing it to bypass missing arguments.


I downloaded the latest version of django but did not install it. But here is the call_command simulation, which should check the call parameters:

 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) 

doing everything stubs.

 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} 
+10
source

All Articles