Skip to content

Commit

Permalink
Normalize the command earlier so we can output exactly what we run.
Browse files Browse the repository at this point in the history
  • Loading branch information
jamadden committed Mar 31, 2021
1 parent 90e48e8 commit 45e3724
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 17 deletions.
36 changes: 23 additions & 13 deletions src/sphinxcontrib/programoutput/__init__.py
Expand Up @@ -133,17 +133,38 @@ class Command(_Command):

def __new__(cls, command, shell=False, hide_standard_error=False,
working_directory='/'):
if isinstance(command, list):
command = tuple(command)
# `chdir()` resolves symlinks, so we need to resolve them too for
# caching to make sure that different symlinks to the same directory
# don't result in different cache keys. Also normalize paths to make
# sure that identical paths are also equal as strings.
working_directory = os.path.normpath(os.path.realpath(
working_directory))
# Likewise, normalize the command now for better caching, and so
# that we can present *exactly* what we run to the user.
command = cls.__normalize_command(command, shell)
return _Command.__new__(cls, command, shell, hide_standard_error,
working_directory)

@staticmethod
def __normalize_command(command, shell):
# Returns either a native string, to a tuple.
if (bytes is str
and not isinstance(command, str)
and hasattr(command, 'encode')):
# Python 2, given a unicode string
command = command.encode(sys.getfilesystemencoding())
assert isinstance(command, str)

if not shell and isinstance(command, str):
command = shlex.split(command)

if isinstance(command, list):
command = tuple(command)

assert isinstance(command, (str, tuple)), command

return command

@classmethod
def from_program_output_node(cls, node):
"""
Expand All @@ -162,18 +183,7 @@ def execute(self):
command.
"""
command = self.command
if (bytes is str
and not isinstance(command, str)
and hasattr(command, 'encode')):
# Python 2, given a unicode string
command = command.encode(sys.getfilesystemencoding())
assert isinstance(command, str)

if not self.shell:
if isinstance(command, str):
command = shlex.split(command)
else:
command = self.command

return Popen(command, shell=self.shell, stdout=PIPE,
stderr=PIPE if self.hide_standard_error else STDOUT,
Expand Down
9 changes: 6 additions & 3 deletions src/sphinxcontrib/programoutput/tests/test_command.py
Expand Up @@ -37,7 +37,8 @@ class TestCommand(unittest.TestCase):

def test_new_with_string_command(self):
cmd = 'echo "spam with eggs"'
self.assertEqual(Command(cmd).command, cmd)
parsed_cmd = ('echo', 'spam with eggs')
self.assertEqual(Command(cmd).command, parsed_cmd)
self.assertEqual(Command(cmd, shell=True).command, cmd)


Expand All @@ -59,7 +60,8 @@ def test_from_programoutput_node(self):
node['hide_standard_error'] = False
node['working_directory'] = '/spam/with/eggs'
command = Command.from_program_output_node(node)
self.assertEqual(command.command, 'echo spam')
parsed_command = ('echo', 'spam')
self.assertEqual(command.command, parsed_command)
self.assertEqual(command.working_directory, '/spam/with/eggs')
self.assertFalse(command.shell)
self.assertFalse(command.hide_standard_error)
Expand All @@ -78,7 +80,8 @@ def test_from_programoutput_node_extraargs(self):
node['extraargs'] = 'with eggs'
node['working_directory'] = '/'
command = Command.from_program_output_node(node)
self.assertEqual(command.command, 'echo spam with eggs')
parsed_command = ('echo', 'spam', 'with', 'eggs')
self.assertEqual(command.command, parsed_command)


def test_execute(self, **kwargs):
Expand Down
3 changes: 2 additions & 1 deletion src/sphinxcontrib/programoutput/tests/test_directive.py
Expand Up @@ -292,7 +292,8 @@ def test_unexpected_return_code(self):
self.app.build()
self.assertIn('Unexpected return code 1 from command',
excinfo.exception.args[0])
self.assertIn(sys.executable + " -c 'import sys; sys.exit(1)'",
parsed_command = (sys.executable, '-c', 'import sys; sys.exit(1)')
self.assertIn(repr(parsed_command),
excinfo.exception.args[0])


Expand Down

0 comments on commit 45e3724

Please sign in to comment.