From e0a98e2374b1d7660ef3841a5bb2626f1c69970a Mon Sep 17 00:00:00 2001
From: Raffaele Salmaso <raffaele@salmaso.org>
Date: Mon, 18 Aug 2014 00:29:49 +0200
Subject: [PATCH] Fixed #23309 -- Fixed call_command to parse args correctly

---
 django/core/management/__init__.py            |  2 ++
 django/core/management/base.py                |  6 +---
 .../user_commands/management/commands/hal.py  | 28 +++++++++++++++++++
 tests/user_commands/tests.py                  | 26 +++++++++++++++++
 4 files changed, 57 insertions(+), 5 deletions(-)
 create mode 100644 tests/user_commands/management/commands/hal.py

diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index 2e1cdb149b..b25477fc82 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -108,6 +108,8 @@ def call_command(name, *args, **options):
         arg_options = dict((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=[])
diff --git a/django/core/management/base.py b/django/core/management/base.py
index 6e6ec19460..6b7a1be636 100644
--- a/django/core/management/base.py
+++ b/django/core/management/base.py
@@ -350,11 +350,7 @@ class BaseCommand(object):
             options = parser.parse_args(argv[2:])
             cmd_options = vars(options)
             # Move positional args out of options to mimic legacy optparse
-            if 'args' in options:
-                args = options.args
-                del cmd_options['args']
-            else:
-                args = ()
+            args = cmd_options.pop('args', ())
         else:
             options, args = parser.parse_args(argv[2:])
             cmd_options = vars(options)
diff --git a/tests/user_commands/management/commands/hal.py b/tests/user_commands/management/commands/hal.py
new file mode 100644
index 0000000000..c7ea51c9e5
--- /dev/null
+++ b/tests/user_commands/management/commands/hal.py
@@ -0,0 +1,28 @@
+from django.core.management.base import BaseCommand, CommandError
+
+
+class Command(BaseCommand):
+    help = "Useless command."
+
+    def add_arguments(self, parser):
+        parser.add_argument('args', metavar='app_label', nargs='*',
+            help='Specify the app label(s) to works on.')
+        parser.add_argument('--empty', action='store_true', dest='empty', default=False,
+            help="Do nothing.")
+
+    def handle(self, *app_labels, **options):
+        app_labels = set(app_labels)
+
+        if options['empty']:
+            self.stdout.write("Dave, I can't do that.")
+            return
+
+        if not app_labels:
+            raise CommandError("I'm sorry Dave, I'm afraid I can't do that.")
+
+        # raise an error if some --parameter is flowing from options to args
+        for app_label in app_labels:
+            if app_label.startswith('--'):
+                raise CommandError("Sorry, Dave, I can't let you do that.")
+
+        self.stdout.write("Dave, my mind is going. I can feel it. I can feel it.")
diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py
index 398e01eb6b..19383e6af4 100644
--- a/tests/user_commands/tests.py
+++ b/tests/user_commands/tests.py
@@ -108,6 +108,32 @@ class CommandTests(SimpleTestCase):
             sys.stdout, sys.stderr = old_stdout, old_stderr
         self.assertEqual(output, "All right, let's dance Rock'n'Roll.\n")
 
+    def test_calling_an_help_command_should_exit_with_systemexit_exception(self):
+        out = StringIO()
+        with self.assertRaises(SystemExit):
+            management.call_command('hal', "--help", stdout=out)
+        self.assertIn("", out.getvalue())
+
+    def test_calling_a_command_with_only_empty_parameter_should_ends_gracefully(self):
+        out = StringIO()
+        management.call_command('hal', "--empty", stdout=out)
+        self.assertIn("Dave, I can't do that.\n", out.getvalue())
+
+    def test_calling_command_with_app_labels_and_parameters_should_be_ok(self):
+        out = StringIO()
+        management.call_command('hal', 'myapp', "--verbosity", "3", stdout=out)
+        self.assertIn("Dave, my mind is going. I can feel it. I can feel it.\n", out.getvalue())
+
+    def test_calling_command_with_parameters_and_app_labels_at_the_end_should_be_ok(self):
+        out = StringIO()
+        management.call_command('hal', "--verbosity", "3", "myapp", stdout=out)
+        self.assertIn("Dave, my mind is going. I can feel it. I can feel it.\n", out.getvalue())
+
+    def test_calling_a_command_with_no_app_labels_and_parameters_should_raise_a_command_error(self):
+        out = StringIO()
+        with self.assertRaises(CommandError):
+            management.call_command('hal', stdout=out)
+
 
 class UtilsTests(SimpleTestCase):