Commit 8e0b6bdc authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #6017 -- Modified the Lax parser to allow --settings and the other core...

Fixed #6017 -- Modified the Lax parser to allow --settings and the other core management arguments to appear anywhere in the argument list. Thanks to Todd O'Bryan for the suggestion and patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@7888 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 4516233f
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -135,6 +135,35 @@ class LaxOptionParser(OptionParser):
    def error(self, msg):
        pass
    
    def _process_args(self, largs, rargs, values):
        """
        Overrides OptionParser._process_args to exclusively handle default
        options and ignore args and other options. 
        
        This overrides the behavior of the super class, which stop parsing 
        at the first unrecognized option.
        """
        while rargs:
            arg = rargs[0]
            try:
                if arg[0:2] == "--" and len(arg) > 2:
                    # process a single long option (possibly with value(s))
                    # the superclass code pops the arg off rargs
                    self._process_long_opt(rargs, values)
                elif arg[:1] == "-" and len(arg) > 1:
                    # process a cluster of short options (possibly with
                    # value(s) for the last one only)
                    # the superclass code pops the arg off rargs
                    self._process_short_opts(rargs, values)
                else:
                    # it's either a non-default option or an arg
                    # either way, add it to the args list so we can keep
                    # dealing with options
                    del rargs[0]
                    raise error
            except:
                largs.append(arg)

class ManagementUtility(object):
    """
    Encapsulates the logic of the django-admin.py and manage.py utilities.
+6 −0
Original line number Diff line number Diff line
from django.core.management.base import BaseCommand
from optparse import make_option

class Command(BaseCommand):
    option_list = BaseCommand.option_list + (
        make_option('--option_a','-a', action='store', dest='option_a', default='1'),
        make_option('--option_b','-b', action='store', dest='option_b', default='2'),
        make_option('--option_c','-c', action='store', dest='option_c', default='3'),
    )
    help = 'Test basic commands'
    requires_model_validation = False
    args = '[labels ...]'
+93 −4
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ import os
import unittest
import shutil

from django import conf, bin
from django import conf, bin, get_version
from django.conf import settings

class AdminScriptTestCase(unittest.TestCase):
@@ -725,26 +725,62 @@ class CommandTypes(AdminScriptTestCase):
    def tearDown(self):
        self.remove_settings('settings.py')
    
    def test_version(self):
        "--version is handled as a special case"
        args = ['--version']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        # Only check the first part of the version number
        self.assertOutput(out, get_version().split('-')[0])

    def test_help(self):
        "--help is handled as a special case"
        args = ['--help']
        out, err = self.run_manage(args)
        self.assertOutput(out, "Usage: manage.py [options]")
        self.assertOutput(err, "Type 'manage.py help <subcommand>' for help on a specific subcommand.")

    def test_specific_help(self):
        "--help can be used on a specific command"
        args = ['sqlall','--help']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s).")
    
    def test_base_command(self):
        "User BaseCommands can execute when a label is provided"
        args = ['base_command','testlabel']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")
        
    def test_base_command_no_label(self):
        "User BaseCommands can execute when no labels are provided"
        args = ['base_command']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
        self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")

    def test_base_command_multiple_label(self):
        "User BaseCommands can execute when no labels are provided"
        args = ['base_command','testlabel','anotherlabel']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")

    def test_base_command_with_option(self):
        "User BaseCommands can execute with options when a label is provided"
        args = ['base_command','testlabel','--option_a=x']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")

    def test_base_command_with_options(self):
        "User BaseCommands can execute with multiple options when a label is provided"
        args = ['base_command','testlabel','-a','x','--option_b=y']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")
                
    def test_noargs(self):
        "NoArg Commands can be executed"
@@ -815,3 +851,56 @@ class CommandTypes(AdminScriptTestCase):
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
        self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")

class ArgumentOrder(AdminScriptTestCase):
    """Tests for 2-stage argument parsing scheme.

    django-admin command arguments are parsed in 2 parts; the core arguments
    (--settings, --traceback and --pythonpath) are parsed using a Lax parser.
    This Lax parser ignores any unknown options. Then the full settings are 
    passed to the command parser, which extracts commands of interest to the
    individual command.    
    """
    def setUp(self):
        self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
        self.write_settings('alternate_settings.py')
        
    def tearDown(self):
        self.remove_settings('settings.py')
        self.remove_settings('alternate_settings.py')

    def test_setting_then_option(self):
        "Options passed after settings are correctly handled"
        args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")

    def test_setting_then_short_option(self):
        "Short options passed after settings are correctly handled"
        args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")

    def test_option_then_setting(self):
        "Options passed before settings are correctly handled"
        args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")

    def test_short_option_then_setting(self):
        "Short options passed before settings are correctly handled"
        args = ['base_command','testlabel','-a','x','--settings=alternate_settings']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")

    def test_option_then_setting_then_option(self):
        "Options are correctly handled when they are passed before and after a setting"
        args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings','--option_b=y']
        out, err = self.run_manage(args)
        self.assertNoOutput(err)
        self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")