Fixed #32183 -- Fixed shell crash when passing code with nested scopes.

This commit is contained in:
William Schwartz 2020-11-09 16:41:13 -06:00 committed by Mariusz Felisiak
parent d26d1c196d
commit c0fc5ba380
2 changed files with 25 additions and 2 deletions

View File

@ -84,13 +84,13 @@ class Command(BaseCommand):
def handle(self, **options): def handle(self, **options):
# Execute the command and exit. # Execute the command and exit.
if options['command']: if options['command']:
exec(options['command']) exec(options['command'], globals())
return return
# Execute stdin if it has anything to read and exit. # Execute stdin if it has anything to read and exit.
# Not supported on Windows due to select.select() limitations. # Not supported on Windows due to select.select() limitations.
if sys.platform != 'win32' and not sys.stdin.isatty() and select.select([sys.stdin], [], [], 0)[0]: if sys.platform != 'win32' and not sys.stdin.isatty() and select.select([sys.stdin], [], [], 0)[0]:
exec(sys.stdin.read()) exec(sys.stdin.read(), globals())
return return
available_shells = [options['interface']] if options['interface'] else self.shells available_shells = [options['interface']] if options['interface'] else self.shells

View File

@ -10,6 +10,12 @@ from django.test.utils import captured_stdin, captured_stdout
class ShellCommandTestCase(SimpleTestCase): class ShellCommandTestCase(SimpleTestCase):
script_globals = 'print("__name__" in globals())' script_globals = 'print("__name__" in globals())'
script_with_inline_function = (
'import django\n'
'def f():\n'
' print(django.__version__)\n'
'f()'
)
def test_command_option(self): def test_command_option(self):
with self.assertLogs('test', 'INFO') as cm: with self.assertLogs('test', 'INFO') as cm:
@ -27,6 +33,11 @@ class ShellCommandTestCase(SimpleTestCase):
call_command('shell', command=self.script_globals) call_command('shell', command=self.script_globals)
self.assertEqual(stdout.getvalue().strip(), 'True') self.assertEqual(stdout.getvalue().strip(), 'True')
def test_command_option_inline_function_call(self):
with captured_stdout() as stdout:
call_command('shell', command=self.script_with_inline_function)
self.assertEqual(stdout.getvalue().strip(), __version__)
@unittest.skipIf(sys.platform == 'win32', "Windows select() doesn't support file descriptors.") @unittest.skipIf(sys.platform == 'win32', "Windows select() doesn't support file descriptors.")
@mock.patch('django.core.management.commands.shell.select') @mock.patch('django.core.management.commands.shell.select')
def test_stdin_read(self, select): def test_stdin_read(self, select):
@ -48,6 +59,18 @@ class ShellCommandTestCase(SimpleTestCase):
call_command('shell') call_command('shell')
self.assertEqual(stdout.getvalue().strip(), 'True') self.assertEqual(stdout.getvalue().strip(), 'True')
@unittest.skipIf(
sys.platform == 'win32',
"Windows select() doesn't support file descriptors.",
)
@mock.patch('django.core.management.commands.shell.select') # [1]
def test_stdin_read_inline_function_call(self, select):
with captured_stdin() as stdin, captured_stdout() as stdout:
stdin.write(self.script_with_inline_function)
stdin.seek(0)
call_command('shell')
self.assertEqual(stdout.getvalue().strip(), __version__)
@mock.patch('django.core.management.commands.shell.select.select') # [1] @mock.patch('django.core.management.commands.shell.select.select') # [1]
@mock.patch.dict('sys.modules', {'IPython': None}) @mock.patch.dict('sys.modules', {'IPython': None})
def test_shell_with_ipython_not_installed(self, select): def test_shell_with_ipython_not_installed(self, select):