Skip to content

Commit 4f6704c

Browse files
ericvergnaudasnare
andauthored
Support spaces in run cmd args (#2330)
## Changes Current implementation only accepts a full command line, which it splits on spaces. This breaks when the command line contains arguments with spaces, such as `'some stuff'` (note the enclosing quotes) The new implementation adds support for argument lists, which are passes 'as is' to `Popen` ### Linked issues Resolves #1859 ### Functionality None ### Tests - [x] enabled integration tests --------- Co-authored-by: Eric Vergnaud <[email protected]> Co-authored-by: Andrew Snare <[email protected]>
1 parent d2048dc commit 4f6704c

File tree

4 files changed

+20
-12
lines changed

4 files changed

+20
-12
lines changed

src/databricks/labs/ucx/framework/utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ def escape_sql_identifier(path: str, optional: bool | None = True) -> str:
2929
return ".".join(escaped)
3030

3131

32-
def run_command(command: str) -> tuple[int, str, str]:
33-
logger.info(f"Invoking command: {command}")
34-
with subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) as process:
32+
def run_command(command: str | list[str]) -> tuple[int, str, str]:
33+
args = command.split() if isinstance(command, str) else command
34+
logger.info(f"Invoking command: {args!r}")
35+
with subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as process:
3536
output, error = process.communicate()
3637
return process.returncode, output.decode("utf-8"), error.decode("utf-8")

src/databricks/labs/ucx/source_code/python_libraries.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from __future__ import annotations
33

44
import logging
5-
import shlex
65
import tempfile
76
import zipfile
87
from collections.abc import Callable
@@ -25,7 +24,9 @@
2524
class PythonLibraryResolver(LibraryResolver):
2625
# TODO: https://github.com/databrickslabs/ucx/issues/1640
2726

28-
def __init__(self, allow_list: KnownList, runner: Callable[[str], tuple[int, str, str]] = run_command) -> None:
27+
def __init__(
28+
self, allow_list: KnownList, runner: Callable[[str | list[str]], tuple[int, str, str]] = run_command
29+
) -> None:
2930
self._allow_list = allow_list
3031
self._runner = runner
3132

@@ -71,13 +72,19 @@ def _resolve_libraries(path_lookup: PathLookup, *libraries: str) -> list[str]:
7172
return libs
7273

7374
def _install_pip(self, *libraries: str) -> list[DependencyProblem]:
74-
install_command = (
75-
f"pip --disable-pip-version-check install {shlex.join(libraries)} -t {self._temporary_virtual_environment}"
76-
)
77-
return_code, stdout, stderr = self._runner(install_command)
75+
args = [
76+
"pip",
77+
"--disable-pip-version-check",
78+
"install",
79+
*libraries,
80+
"-t",
81+
str(self._temporary_virtual_environment),
82+
]
83+
return_code, stdout, stderr = self._runner(args)
7884
logger.debug(f"pip output:\n{stdout}\n{stderr}")
7985
if return_code != 0:
80-
problem = DependencyProblem("library-install-failed", f"'{install_command}' failed with '{stderr}'")
86+
command = " ".join(args)
87+
problem = DependencyProblem("library-install-failed", f"'{command}' failed with '{stderr}'")
8188
return [problem]
8289
return []
8390

tests/integration/source_code/test_libraries.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ def test_build_notebook_dependency_graphs_installs_pypi_packages(simple_ctx):
4848
assert maybe.graph.path_lookup.resolve(Path("hyperopt"))
4949

5050

51-
@pytest.mark.xfail(reason="Spaces in path are not handled by subprocess when quoted")
5251
@pytest.mark.parametrize("notebook", ("pip_install_demo_wheel_with_spaces_in_target_directory",))
5352
def test_build_notebook_dependency_graphs_fails_installing_when_spaces(simple_ctx, notebook):
5453
ctx = simple_ctx.replace(path_lookup=MockPathLookup())

tests/unit/source_code/test_python_libraries.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
def test_python_library_resolver_resolves_library(mock_path_lookup):
99
def mock_pip_install(command):
10-
assert command.startswith("pip --disable-pip-version-check install anything -t")
10+
command_str = command if isinstance(command, str) else " ".join(command)
11+
assert command_str.startswith("pip --disable-pip-version-check install anything -t")
1112
return 0, "", ""
1213

1314
python_library_resolver = PythonLibraryResolver(KnownList(), mock_pip_install)

0 commit comments

Comments
 (0)