diff --git a/news/2563.bugfix b/news/2563.bugfix new file mode 100644 index 0000000000..4d2e65709b --- /dev/null +++ b/news/2563.bugfix @@ -0,0 +1,2 @@ +Improve quoting logic for ``pipenv run`` so it works better with Windows +built-in commands. diff --git a/pipenv/cmdparse.py b/pipenv/cmdparse.py index aa3442b8ef..087a95b14d 100644 --- a/pipenv/cmdparse.py +++ b/pipenv/cmdparse.py @@ -55,11 +55,18 @@ def cmdify(self): The result is then quoted into a pair of double quotes to be grouped. + An argument is intentionally not quoted if it does not contain + whitespaces. This is done to be compatible with Windows built-in + commands that don't work well with quotes, e.g. everything with `echo`, + and DOS-style (forward slash) switches. + The intended use of this function is to pre-process an argument list before passing it into ``subprocess.Popen(..., shell=True)``. See also: https://docs.python.org/3/library/subprocess.html#converting-argument-sequence """ return " ".join( - '"{0}"'.format(re.sub(r'(\\*)"', r'\1\1\\"', arg)) for arg in self._parts + arg if not next(re.finditer(r'\s', arg), None) + else '"{0}"'.format(re.sub(r'(\\*)"', r'\1\1\\"', arg)) + for arg in self._parts ) diff --git a/tests/unit/test_cmdparse.py b/tests/unit/test_cmdparse.py index 064eda7970..06012d0770 100644 --- a/tests/unit/test_cmdparse.py +++ b/tests/unit/test_cmdparse.py @@ -30,9 +30,9 @@ def test_extend(): @pytest.mark.run @pytest.mark.script def test_cmdify(): - script = Script('python', ['-c', "print('hello')"]) + script = Script('python', ['-c', "print('hello world')"]) cmd = script.cmdify() - assert cmd == '"python" "-c" "print(\'hello\')"', script + assert cmd == 'python -c "print(\'hello world\')"', script @pytest.mark.run @@ -44,6 +44,6 @@ def test_cmdify_complex(): ])) assert script.cmdify() == ' '.join([ '"C:\\Program Files\\Python36\\python.exe"', - '"-c"', + '-c', """ "print(\'Double quote: \\\"\')" """.strip(), ]), script