summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjpic <jamespic@gmail.com>2016-12-13 01:36:08 +0100
committerTim Graham <timograham@gmail.com>2016-12-21 17:05:33 -0500
commitbf6392bb7565c21f01ed6f682b8558dcf1cc8070 (patch)
tree0f124dcbf4c9eb3e10c07415e93aae7478f3fdc1
parent24fa728a476a6da3e565dbe33959ea62c02c250b (diff)
Fixed #27600 -- Suppressed the REPL during shell's reading from stdin.
Thanks Adam Chainz for review and guidance.
-rw-r--r--django/core/management/commands/shell.py15
-rw-r--r--docs/ref/django-admin.txt16
-rw-r--r--tests/shell/tests.py16
3 files changed, 44 insertions, 3 deletions
diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py
index b4e9caf0d1..9cc40902f4 100644
--- a/django/core/management/commands/shell.py
+++ b/django/core/management/commands/shell.py
@@ -1,4 +1,6 @@
import os
+import select
+import sys
import warnings
from django.core.management.base import BaseCommand
@@ -7,7 +9,12 @@ from django.utils.deprecation import RemovedInDjango20Warning
class Command(BaseCommand):
- help = "Runs a Python interactive interpreter. Tries to use IPython or bpython, if one of them is available."
+ help = (
+ "Runs a Python interactive interpreter. Tries to use IPython or "
+ "bpython, if one of them is available. Any standard input is executed "
+ "as code."
+ )
+
requires_system_checks = False
shells = ['ipython', 'bpython', 'python']
@@ -114,6 +121,12 @@ class Command(BaseCommand):
exec(options['command'])
return
+ # Execute stdin if it has anything to read and exit.
+ # Not supported on Windows due to select.select() limitations.
+ if sys.platform != 'win32' and select.select([sys.stdin], [], [], 0)[0]:
+ exec(sys.stdin.read())
+ return
+
available_shells = [options['interface']] if options['interface'] else self.shells
for shell in available_shells:
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index c35e728f26..7a77f4d1e7 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -979,6 +979,22 @@ Lets you pass a command as a string to execute it as Django, like so::
django-admin shell --command="import django; print(django.__version__)"
+You can also pass code in on standard input to execute it. For example:
+
+.. code-block:: console
+
+ $ django-admin shell <<EOF
+ > import django
+ > print(django.__version__)
+ > EOF
+
+On Windows, the REPL is output due to implementation limits of
+:func:`select.select` on that platform.
+
+.. versionchanged:: 1.11
+
+ In older versions, the REPL is also output on UNIX systems.
+
``showmigrations``
------------------
diff --git a/tests/shell/tests.py b/tests/shell/tests.py
index 7285c968a2..11f1826d30 100644
--- a/tests/shell/tests.py
+++ b/tests/shell/tests.py
@@ -1,7 +1,10 @@
+import sys
+import unittest
+
from django import __version__
from django.core.management import call_command
-from django.test import SimpleTestCase
-from django.test.utils import patch_logger
+from django.test import SimpleTestCase, mock
+from django.test.utils import captured_stdin, captured_stdout, patch_logger
class ShellCommandTestCase(SimpleTestCase):
@@ -17,3 +20,12 @@ class ShellCommandTestCase(SimpleTestCase):
)
self.assertEqual(len(logger), 1)
self.assertEqual(logger[0], __version__)
+
+ @unittest.skipIf(sys.platform == 'win32', "Windows select() doesn't support file descriptors.")
+ @mock.patch('django.core.management.commands.shell.select')
+ def test_stdin_read(self, select):
+ with captured_stdin() as stdin, captured_stdout() as stdout:
+ stdin.write('print(100)\n')
+ stdin.seek(0)
+ call_command('shell')
+ self.assertEqual(stdout.getvalue().strip(), '100')