diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2019-02-23 21:15:54 -0800 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2019-02-23 22:35:10 -0800 |
commit | 77aee9c3069851e53a772d5b1f24392932d0801f (patch) | |
tree | 9f2f2ded6c26d5e63bcb1c8b66db5911cf18c451 /numpy/distutils/tests | |
parent | 490b8542c8d042f712cfe4671ebfc5a2afe8f8ae (diff) | |
download | numpy-77aee9c3069851e53a772d5b1f24392932d0801f.tar.gz |
MAINT: Add functions to parse shell-strings in the platform-native way
There are places in distutils where we accept a single string from the user, and interpret it as a set of command line arguments.
Previously, these were passed on as a string unmodified to exec_command, and interpreted by subprocess in a platform-specific way.
Recent changes to distutils now pass a list of arguments to subprocess, meaning we have to split the strings ourselves.
While `shlex.split` is perfect on posix systems, it is not a good approximation of either the old or the expected behavior on windows.
Provides the building blocks needed to fix gh-12979
Diffstat (limited to 'numpy/distutils/tests')
-rw-r--r-- | numpy/distutils/tests/test_shell_utils.py | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/numpy/distutils/tests/test_shell_utils.py b/numpy/distutils/tests/test_shell_utils.py new file mode 100644 index 000000000..a0344244f --- /dev/null +++ b/numpy/distutils/tests/test_shell_utils.py @@ -0,0 +1,79 @@ +from __future__ import division, absolute_import, print_function + +import pytest +import subprocess +import os +import json +import sys + +from numpy.distutils import _shell_utils + +argv_cases = [ + [r'exe'], + [r'path/exe'], + [r'path\exe'], + [r'\\server\path\exe'], + [r'path to/exe'], + [r'path to\exe'], + + [r'exe', '--flag'], + [r'path/exe', '--flag'], + [r'path\exe', '--flag'], + [r'path to/exe', '--flag'], + [r'path to\exe', '--flag'], + + # flags containing literal quotes in their name + [r'path to/exe', '--flag-"quoted"'], + [r'path to\exe', '--flag-"quoted"'], + [r'path to/exe', '"--flag-quoted"'], + [r'path to\exe', '"--flag-quoted"'], +] + + +@pytest.fixture(params=[ + _shell_utils.WindowsParser, + _shell_utils.PosixParser +]) +def Parser(request): + return request.param + + +@pytest.fixture +def runner(Parser): + if Parser != _shell_utils.NativeParser: + pytest.skip('Unable to run with non-native parser') + + if Parser == _shell_utils.WindowsParser: + return lambda cmd: subprocess.check_output(cmd) + elif Parser == _shell_utils.PosixParser: + # posix has no non-shell string parsing + return lambda cmd: subprocess.check_output(cmd, shell=True) + else: + raise NotImplementedError + + +@pytest.mark.parametrize('argv', argv_cases) +def test_join_matches_subprocess(Parser, runner, argv): + """ + Test that join produces strings understood by subprocess + """ + # invoke python to return its arguments as json + cmd = [ + sys.executable, '-c', + 'import json, sys; print(json.dumps(sys.argv[1:]))' + ] + joined = Parser.join(cmd + argv) + json_out = runner(joined).decode() + assert json.loads(json_out) == argv + + +@pytest.mark.parametrize('argv', argv_cases) +def test_roundtrip(Parser, argv): + """ + Test that split is the inverse operation of join + """ + try: + joined = Parser.join(argv) + assert argv == Parser.split(joined) + except NotImplementedError: + pytest.skip("Not implemented") |