From 1a7abe562b51163c0b8d1d6901ab25da52550483 Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Mon, 25 Jul 2022 17:01:22 +0200 Subject: [PATCH 1/5] Remove dependency on Unix tools in Windows --- test/test_environment.py | 2 +- unit_test/environment_test.py | 29 +++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/test/test_environment.py b/test/test_environment.py index 6ad8ff9f..d2b75f0a 100644 --- a/test/test_environment.py +++ b/test/test_environment.py @@ -43,7 +43,7 @@ def test(tmp_path): project_dir, add_env={ "CIBW_ENVIRONMENT": """CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path""", - "CIBW_ENVIRONMENT_WINDOWS": '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''', + "CIBW_ENVIRONMENT_WINDOWS": '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(cmd /C echo test string 3)" PATH="$PATH;/opt/cibw_test_path"''', }, ) diff --git a/unit_test/environment_test.py b/unit_test/environment_test.py index c1d0a4a1..8c67b259 100644 --- a/unit_test/environment_test.py +++ b/unit_test/environment_test.py @@ -1,4 +1,5 @@ import os +import sys from cibuildwheel.environment import parse_environment @@ -28,7 +29,11 @@ def test_inheritance(): def test_shell_eval(): - environment_recipe = parse_environment('VAR="$(echo "a test" string)"') + if sys.platform == "win32": + env_string = 'VAR="$(cmd /C "echo a test string")"' + else: + env_string = 'VAR="$(echo "a test" string)"' + environment_recipe = parse_environment(env_string) env_copy = os.environ.copy() env_copy.pop("VAR", None) @@ -39,7 +44,11 @@ def test_shell_eval(): def test_shell_eval_and_env(): - environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"') + if sys.platform == "win32": + env_string = 'VAR="$(cmd /C "echo $PREV_VAR string")"' + else: + env_string = 'VAR="$(echo "$PREV_VAR" string)"' + environment_recipe = parse_environment(env_string) environment_dict = environment_recipe.as_dictionary(prev_environment={"PREV_VAR": "1 2 3"}) @@ -73,7 +82,11 @@ def test_no_vars_pass_through(): def test_operators_inside_eval(): - environment_recipe = parse_environment('SOMETHING="$(echo a; echo b; echo c)"') + if sys.platform == "win32": + env_string = 'SOMETHING="$(cmd /C echo a; cmd /C echo b; cmd /C echo c)"' + else: + env_string = 'SOMETHING="$(echo a; echo b; echo c)"' + environment_recipe = parse_environment(env_string) # pass the existing process env so PATH is available environment_dict = environment_recipe.as_dictionary(os.environ.copy()) @@ -91,9 +104,13 @@ def test_substitution_with_backslash(): def test_awkwardly_quoted_variable(): - environment_recipe = parse_environment( - 'VAR2=something"like this""$VAR1"$VAR1$(echo "there is more")"$(echo "and more!")"' - ) + if sys.platform == "win32": + env_string = 'VAR2=something"like this""$VAR1"$VAR1$(cmd /C echo there is more)"$(cmd /C echo and more!)"' + else: + env_string = ( + 'VAR2=something"like this""$VAR1"$VAR1$(echo "there is more")"$(echo "and more!")"' + ) + environment_recipe = parse_environment(env_string) # pass the existing process env so PATH is available environment_dict = environment_recipe.as_dictionary({"VAR1": "but wait"}) From cccc2acae9c746839df9c0006a34f9df4f79af99 Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Fri, 29 Jul 2022 18:04:27 +0200 Subject: [PATCH 2/5] Use an equivalent of Unix echo written in Python --- test/test_environment.py | 3 ++- unit_test/environment_test.py | 33 ++++++++++----------------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/test/test_environment.py b/test/test_environment.py index d2b75f0a..079081ce 100644 --- a/test/test_environment.py +++ b/test/test_environment.py @@ -33,6 +33,7 @@ project_with_environment_asserts = test_projects.new_c_project( def test(tmp_path): + python_echo = 'python -c "import sys; print(*sys.argv[1:])"' project_dir = tmp_path / "project" project_with_environment_asserts.generate(project_dir) @@ -43,7 +44,7 @@ def test(tmp_path): project_dir, add_env={ "CIBW_ENVIRONMENT": """CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path""", - "CIBW_ENVIRONMENT_WINDOWS": '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(cmd /C echo test string 3)" PATH="$PATH;/opt/cibw_test_path"''', + "CIBW_ENVIRONMENT_WINDOWS": f'''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$({python_echo} 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''', }, ) diff --git a/unit_test/environment_test.py b/unit_test/environment_test.py index 8c67b259..696dff63 100644 --- a/unit_test/environment_test.py +++ b/unit_test/environment_test.py @@ -1,8 +1,9 @@ import os -import sys from cibuildwheel.environment import parse_environment +PYTHON_ECHO = 'python -c "import sys; print(*sys.argv[1:])"' + def test_basic_parsing(): environment_recipe = parse_environment("VAR=1 VBR=2") @@ -29,11 +30,7 @@ def test_inheritance(): def test_shell_eval(): - if sys.platform == "win32": - env_string = 'VAR="$(cmd /C "echo a test string")"' - else: - env_string = 'VAR="$(echo "a test" string)"' - environment_recipe = parse_environment(env_string) + environment_recipe = parse_environment(f'VAR="$({PYTHON_ECHO} "a test" string)"') env_copy = os.environ.copy() env_copy.pop("VAR", None) @@ -44,11 +41,7 @@ def test_shell_eval(): def test_shell_eval_and_env(): - if sys.platform == "win32": - env_string = 'VAR="$(cmd /C "echo $PREV_VAR string")"' - else: - env_string = 'VAR="$(echo "$PREV_VAR" string)"' - environment_recipe = parse_environment(env_string) + environment_recipe = parse_environment(f'VAR="$({PYTHON_ECHO} "$PREV_VAR" string)"') environment_dict = environment_recipe.as_dictionary(prev_environment={"PREV_VAR": "1 2 3"}) @@ -82,11 +75,9 @@ def test_no_vars_pass_through(): def test_operators_inside_eval(): - if sys.platform == "win32": - env_string = 'SOMETHING="$(cmd /C echo a; cmd /C echo b; cmd /C echo c)"' - else: - env_string = 'SOMETHING="$(echo a; echo b; echo c)"' - environment_recipe = parse_environment(env_string) + environment_recipe = parse_environment( + f'SOMETHING="$({PYTHON_ECHO} a; {PYTHON_ECHO} b; {PYTHON_ECHO} c)"' + ) # pass the existing process env so PATH is available environment_dict = environment_recipe.as_dictionary(os.environ.copy()) @@ -104,13 +95,9 @@ def test_substitution_with_backslash(): def test_awkwardly_quoted_variable(): - if sys.platform == "win32": - env_string = 'VAR2=something"like this""$VAR1"$VAR1$(cmd /C echo there is more)"$(cmd /C echo and more!)"' - else: - env_string = ( - 'VAR2=something"like this""$VAR1"$VAR1$(echo "there is more")"$(echo "and more!")"' - ) - environment_recipe = parse_environment(env_string) + environment_recipe = parse_environment( + f'VAR2=something"like this""$VAR1"$VAR1$({PYTHON_ECHO} "there is more")"$({PYTHON_ECHO} "and more!")"' + ) # pass the existing process env so PATH is available environment_dict = environment_recipe.as_dictionary({"VAR1": "but wait"}) From e0f25c5fb56683b57c11bd55e3cae302b3c1dafd Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Fri, 29 Jul 2022 18:52:26 +0200 Subject: [PATCH 3/5] Use sys.executable --- test/test_environment.py | 3 ++- unit_test/environment_test.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_environment.py b/test/test_environment.py index 079081ce..97326549 100644 --- a/test/test_environment.py +++ b/test/test_environment.py @@ -1,5 +1,6 @@ import os import subprocess +import sys import textwrap import pytest @@ -33,7 +34,7 @@ project_with_environment_asserts = test_projects.new_c_project( def test(tmp_path): - python_echo = 'python -c "import sys; print(*sys.argv[1:])"' + python_echo = f'"{sys.executable}" -c "import sys; print(*sys.argv[1:])"' project_dir = tmp_path / "project" project_with_environment_asserts.generate(project_dir) diff --git a/unit_test/environment_test.py b/unit_test/environment_test.py index 696dff63..3161a294 100644 --- a/unit_test/environment_test.py +++ b/unit_test/environment_test.py @@ -1,8 +1,9 @@ import os +import sys from cibuildwheel.environment import parse_environment -PYTHON_ECHO = 'python -c "import sys; print(*sys.argv[1:])"' +PYTHON_ECHO = f'"{sys.executable}" -c "import sys; print(*sys.argv[1:])"' def test_basic_parsing(): From 3cbe1756327ee38d0f6b8f558ea9daf773ea9257 Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Fri, 29 Jul 2022 18:57:47 +0200 Subject: [PATCH 4/5] Fix Windows paths getting escaped --- test/test_environment.py | 2 +- unit_test/environment_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_environment.py b/test/test_environment.py index 97326549..b784edab 100644 --- a/test/test_environment.py +++ b/test/test_environment.py @@ -34,7 +34,7 @@ project_with_environment_asserts = test_projects.new_c_project( def test(tmp_path): - python_echo = f'"{sys.executable}" -c "import sys; print(*sys.argv[1:])"' + python_echo = f"'{sys.executable}' -c \"import sys; print(*sys.argv[1:])\"" project_dir = tmp_path / "project" project_with_environment_asserts.generate(project_dir) diff --git a/unit_test/environment_test.py b/unit_test/environment_test.py index 3161a294..405fc8ac 100644 --- a/unit_test/environment_test.py +++ b/unit_test/environment_test.py @@ -3,7 +3,7 @@ import sys from cibuildwheel.environment import parse_environment -PYTHON_ECHO = f'"{sys.executable}" -c "import sys; print(*sys.argv[1:])"' +PYTHON_ECHO = f"'{sys.executable}' -c \"import sys; print(*sys.argv[1:])\"" def test_basic_parsing(): From 7c95f77524d56682ded70770f4cb9892419ec572 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Fri, 29 Jul 2022 18:48:43 +0100 Subject: [PATCH 5/5] Ensure the tests that use subcommands have access to the system env --- unit_test/environment_test.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/unit_test/environment_test.py b/unit_test/environment_test.py index 405fc8ac..c5e6e57c 100644 --- a/unit_test/environment_test.py +++ b/unit_test/environment_test.py @@ -3,6 +3,7 @@ import sys from cibuildwheel.environment import parse_environment +# this command is equivalent to Unix 'echo', but works on Windows too PYTHON_ECHO = f"'{sys.executable}' -c \"import sys; print(*sys.argv[1:])\"" @@ -44,9 +45,10 @@ def test_shell_eval(): def test_shell_eval_and_env(): environment_recipe = parse_environment(f'VAR="$({PYTHON_ECHO} "$PREV_VAR" string)"') - environment_dict = environment_recipe.as_dictionary(prev_environment={"PREV_VAR": "1 2 3"}) + prev_environment = {**os.environ, "PREV_VAR": "1 2 3"} + environment_dict = environment_recipe.as_dictionary(prev_environment=prev_environment) - assert environment_dict == {"PREV_VAR": "1 2 3", "VAR": "1 2 3 string"} + assert environment_dict == {**prev_environment, "VAR": "1 2 3 string"} def test_empty_var(): @@ -80,8 +82,8 @@ def test_operators_inside_eval(): f'SOMETHING="$({PYTHON_ECHO} a; {PYTHON_ECHO} b; {PYTHON_ECHO} c)"' ) - # pass the existing process env so PATH is available - environment_dict = environment_recipe.as_dictionary(os.environ.copy()) + # pass the existing process env so subcommands can be run in the evaluation + environment_dict = environment_recipe.as_dictionary(prev_environment=os.environ.copy()) assert environment_dict.get("SOMETHING") == "a\nb\nc" @@ -89,7 +91,6 @@ def test_operators_inside_eval(): def test_substitution_with_backslash(): environment_recipe = parse_environment('PATH2="somewhere_else;$PATH1"') - # pass the existing process env so PATH is available environment_dict = environment_recipe.as_dictionary(prev_environment={"PATH1": "c:\\folder\\"}) assert environment_dict.get("PATH2") == "somewhere_else;c:\\folder\\" @@ -100,8 +101,8 @@ def test_awkwardly_quoted_variable(): f'VAR2=something"like this""$VAR1"$VAR1$({PYTHON_ECHO} "there is more")"$({PYTHON_ECHO} "and more!")"' ) - # pass the existing process env so PATH is available - environment_dict = environment_recipe.as_dictionary({"VAR1": "but wait"}) + prev_environment = {**os.environ, "VAR1": "but wait"} + environment_dict = environment_recipe.as_dictionary(prev_environment=prev_environment) assert ( environment_dict.get("VAR2") == "somethinglike thisbut waitbut waitthere is moreand more!"