From f22089c9bd68acd23bc3363f9aac4872504eae11 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Tue, 15 Apr 2025 09:20:56 +0100 Subject: [PATCH 1/8] Change to the iOS testing option semantics Don't assume the presence of `python -m` in the test command. Less magic and allows more option reuse between platforms. --- cibuildwheel/platforms/ios.py | 24 ++++++++++++++++++++++-- docs/options.md | 11 ++++++++++- test/test_ios.py | 6 +++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py index 3473ef740..d24f3fac3 100644 --- a/cibuildwheel/platforms/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -32,7 +32,7 @@ download, move_file, ) -from ..util.helpers import prepare_command +from ..util.helpers import prepare_command, unwrap from ..util.packaging import ( combine_constraints, find_compatible_wheel, @@ -593,6 +593,26 @@ def build(options: Options, tmp_path: Path) -> None: ) log.step("Running test suite...") + + test_command_parts = shlex.split(build_options.test_command) + if test_command_parts[0:2] != ["python", "-m"]: + log.warning( + unwrap(f""" + iOS tests run with test command '{build_options.test_command}' that + doesn't start with 'python -m'. iOS tests must execute python + modules - other entrypoints are not supported. + + cibuildwheel will try to execute it as if it started with 'python + -m'. If this works, all you need to do is add that to your test + command. + """) + ) + else: + # the testbed run command actually doesn't want the + # python -m prefix - it's implicit, so we remove it + # here. + test_command_parts = test_command_parts[2:] + try: call( "python", @@ -600,7 +620,7 @@ def build(options: Options, tmp_path: Path) -> None: "run", *(["--verbose"] if build_options.build_verbosity > 0 else []), "--", - *(shlex.split(build_options.test_command)), + *test_command_parts, env=build_env, ) failed = False diff --git a/docs/options.md b/docs/options.md index de4317b4d..3ff5537f5 100644 --- a/docs/options.md +++ b/docs/options.md @@ -1242,7 +1242,7 @@ run your test suite. On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`. -On iOS, the value of the `CIBW_TEST_COMMAND` setting is interpreted as the arguments to pass to `python -m` - that is, a Python module name, followed by arguments that will be assigned to `sys.argv`. Shell commands cannot be used. +On iOS, the value of the `CIBW_TEST_COMMAND` setting should follow the format `python -m MODULE [...ARGS]` - where MODULE is a Python module name, followed by arguments that will be assigned to `sys.argv`. Shell commands cannot be used. Platform-specific environment variables are also available:
`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE` @@ -1262,6 +1262,10 @@ Platform-specific environment variables are also available:
CIBW_TEST_COMMAND: > pytest ./tests && python ./test.py + + # run tests on ios + CIBW_TEST_SOURCES_IOS: tests + CIBW_TEST_COMMAND_IOS: python -m pytest ./tests ``` !!! tab examples "pyproject.toml" @@ -1279,6 +1283,11 @@ Platform-specific environment variables are also available:
"pytest ./tests", "python ./test.py", ] + + # run tests on ios + [tool.cibuildwheel.ios] + test-sources = ["tests"] + test-command = "python -m pytest ./tests" ``` In configuration files, you can use an array, and the items will be joined with `&&`. diff --git a/test/test_ios.py b/test/test_ios.py index c3cfa61af..54fa66a8a 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -73,7 +73,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd): "CIBW_BUILD": "cp313-*", "CIBW_XBUILD_TOOLS": "does-exist", "CIBW_TEST_SOURCES": "tests", - "CIBW_TEST_COMMAND": "unittest discover tests test_platform.py", + "CIBW_TEST_COMMAND": "python -m unittest discover tests test_platform.py", "CIBW_BUILD_VERBOSITY": "1", **build_config, }, @@ -119,7 +119,7 @@ def test_no_test_sources(tmp_path, capfd): add_env={ "CIBW_PLATFORM": "ios", "CIBW_BUILD": "cp313-*", - "CIBW_TEST_COMMAND": "tests", + "CIBW_TEST_COMMAND": "python -m tests", }, ) @@ -146,7 +146,7 @@ def test_missing_xbuild_tool(tmp_path, capfd): add_env={ "CIBW_PLATFORM": "ios", "CIBW_BUILD": "cp313-*", - "CIBW_TEST_COMMAND": "tests", + "CIBW_TEST_COMMAND": "python -m tests", "CIBW_XBUILD_TOOLS": "does-not-exist", }, ) From 42da0d70462b3cc17e105cbef6b74a803d1fa74d Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 20 Apr 2025 11:35:33 +0100 Subject: [PATCH 2/8] Update schema --- .../resources/cibuildwheel.schema.json | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json index 10e07cc15..21ace341b 100644 --- a/cibuildwheel/resources/cibuildwheel.schema.json +++ b/cibuildwheel/resources/cibuildwheel.schema.json @@ -566,6 +566,9 @@ "before-build": { "$ref": "#/$defs/inherit" }, + "xbuild-tools": { + "$ref": "#/$defs/inherit" + }, "before-test": { "$ref": "#/$defs/inherit" }, @@ -581,9 +584,6 @@ "environment-pass": { "$ref": "#/$defs/inherit" }, - "xbuild-tools": { - "$ref": "#/$defs/inherit" - }, "repair-wheel-command": { "$ref": "#/$defs/inherit" }, @@ -676,6 +676,9 @@ "musllinux-x86_64-image": { "$ref": "#/properties/musllinux-x86_64-image" }, + "xbuild-tools": { + "$ref": "#/properties/xbuild-tools" + }, "repair-wheel-command": { "$ref": "#/properties/repair-wheel-command" }, @@ -776,6 +779,9 @@ "musllinux-x86_64-image": { "$ref": "#/properties/musllinux-x86_64-image" }, + "xbuild-tools": { + "$ref": "#/properties/xbuild-tools" + }, "repair-wheel-command": { "description": "Execute a shell command to repair each built wheel.", "oneOf": [ @@ -840,6 +846,9 @@ "environment": { "$ref": "#/properties/environment" }, + "xbuild-tools": { + "$ref": "#/properties/xbuild-tools" + }, "repair-wheel-command": { "$ref": "#/properties/repair-wheel-command" }, @@ -891,6 +900,9 @@ "environment": { "$ref": "#/properties/environment" }, + "xbuild-tools": { + "$ref": "#/properties/xbuild-tools" + }, "repair-wheel-command": { "description": "Execute a shell command to repair each built wheel.", "oneOf": [ @@ -955,6 +967,9 @@ "environment": { "$ref": "#/properties/environment" }, + "xbuild-tools": { + "$ref": "#/properties/xbuild-tools" + }, "repair-wheel-command": { "$ref": "#/properties/repair-wheel-command" }, @@ -1006,22 +1021,28 @@ "environment": { "$ref": "#/properties/environment" }, - "repair-wheel-command": { - "$ref": "#/properties/repair-wheel-command" - }, "xbuild-tools": { "$ref": "#/properties/xbuild-tools" }, + "repair-wheel-command": { + "$ref": "#/properties/repair-wheel-command" + }, "test-command": { "$ref": "#/properties/test-command" }, "test-extras": { "$ref": "#/properties/test-extras" }, + "test-sources": { + "$ref": "#/properties/test-sources" + }, + "test-groups": { + "$ref": "#/properties/test-groups" + }, "test-requires": { "$ref": "#/properties/test-requires" } - } + } } } } From a4d3c9775e9ccf1a018e43413ab3138461334a48 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Sun, 20 Apr 2025 15:33:53 +0100 Subject: [PATCH 3/8] Add a test for this warning --- cibuildwheel/platforms/ios.py | 12 +++-- cibuildwheel/util/helpers.py | 15 ++++++ test/test_ios.py | 96 ++++++++++++++++++----------------- test/utils.py | 43 ++++++++++++---- 4 files changed, 105 insertions(+), 61 deletions(-) diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py index d24f3fac3..af599a93e 100644 --- a/cibuildwheel/platforms/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -32,7 +32,7 @@ download, move_file, ) -from ..util.helpers import prepare_command, unwrap +from ..util.helpers import prepare_command, unwrap_preserving_paragraphs from ..util.packaging import ( combine_constraints, find_compatible_wheel, @@ -597,14 +597,16 @@ def build(options: Options, tmp_path: Path) -> None: test_command_parts = shlex.split(build_options.test_command) if test_command_parts[0:2] != ["python", "-m"]: log.warning( - unwrap(f""" - iOS tests run with test command '{build_options.test_command}' that - doesn't start with 'python -m'. iOS tests must execute python - modules - other entrypoints are not supported. + unwrap_preserving_paragraphs(f""" + iOS tests configured with a test command which doesn't start with + 'python -m'. iOS tests must execute python modules - other + entrypoints are not supported. cibuildwheel will try to execute it as if it started with 'python -m'. If this works, all you need to do is add that to your test command. + + Test command: {build_options.test_command} """) ) else: diff --git a/cibuildwheel/util/helpers.py b/cibuildwheel/util/helpers.py index dbef452c0..363fd3345 100644 --- a/cibuildwheel/util/helpers.py +++ b/cibuildwheel/util/helpers.py @@ -76,6 +76,21 @@ def unwrap(text: str) -> str: return re.sub(r"\s+", " ", text) +def unwrap_preserving_paragraphs(text: str) -> str: + """ + Unwraps multi-line text to a single line, but preserves paragraphs + """ + # remove initial line indent + text = textwrap.dedent(text) + # remove leading/trailing whitespace + text = text.strip() + + paragraphs = text.split("\n\n") + # remove consecutive whitespace + paragraphs = [re.sub(r"\s+", " ", paragraph) for paragraph in paragraphs] + return "\n\n".join(paragraphs) + + def parse_key_value_string( key_value_string: str, positional_arg_names: Sequence[str] | None = None, diff --git a/test/test_ios.py b/test/test_ios.py index 54fa66a8a..4a83c700f 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -4,6 +4,7 @@ import platform import shutil import subprocess +import textwrap import pytest @@ -80,21 +81,10 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd): ) # The expected wheels were produced. - ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_") - platform_machine = platform.machine() - - if platform_machine == "x86_64": - expected_wheels = { - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl", - } - - elif platform_machine == "arm64": - expected_wheels = { - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl", - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl", - } - - assert set(actual_wheels) == expected_wheels + expected_wheels = utils.expected_wheels( + "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"] + ) + assert set(actual_wheels) == set(expected_wheels) # The user was notified that the cross-build tool was found. captured = capfd.readouterr() @@ -180,21 +170,10 @@ def test_no_xbuild_tool_definition(tmp_path, capfd): ) # The expected wheels were produced. - ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_") - platform_machine = platform.machine() - - if platform_machine == "x86_64": - expected_wheels = { - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl", - } - - elif platform_machine == "arm64": - expected_wheels = { - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl", - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl", - } - - assert set(actual_wheels) == expected_wheels + expected_wheels = utils.expected_wheels( + "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"] + ) + assert set(actual_wheels) == set(expected_wheels) # The user was notified that there was no cross-build tool definition. captured = capfd.readouterr() @@ -225,23 +204,48 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd): }, ) - # The expected wheels were produced. - ios_version = os.getenv("IPHONEOS_DEPLOYMENT_TARGET", "13.0").replace(".", "_") - platform_machine = platform.machine() - - if platform_machine == "x86_64": - expected_wheels = { - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_x86_64_iphonesimulator.whl", - } - - elif platform_machine == "arm64": - expected_wheels = { - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphoneos.whl", - f"spam-0.1.0-cp313-cp313-ios_{ios_version}_arm64_iphonesimulator.whl", - } - - assert set(actual_wheels) == expected_wheels + expected_wheels = utils.expected_wheels( + "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"] + ) + assert set(actual_wheels) == set(expected_wheels) # The warnings about cross-build notifications were silenced. captured = capfd.readouterr() assert "Your project configuration does not define any cross-build tools." not in captured.err + + +def test_ios_test_command_without_python_dash_m(tmp_path, capfd): + """Test command should be able to run without python -m.""" + if utils.platform != "macos": + pytest.skip("this test can only run on macOS") + if utils.get_xcode_version() < (13, 0): + pytest.skip("this test only works with Xcode 13.0 or greater") + + project_dir = tmp_path / "project" + project = test_projects.new_c_project() + project.files["tests_module/__init__.py"] = "" + project.files["tests_module/__main__.py"] = textwrap.dedent(""" + if __name__ == "__main__": + print("Hello from tests_module") + """) + project.generate(project_dir) + + actual_wheels = utils.cibuildwheel_run( + project_dir, + add_env={ + "CIBW_PLATFORM": "ios", + "CIBW_BUILD": "cp313-*", + "CIBW_TEST_COMMAND": "tests_module", + "CIBW_TEST_SOURCES": "tests_module", + "CIBW_XBUILD_TOOLS": "", + }, + ) + + expected_wheels = utils.expected_wheels( + "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"] + ) + assert set(actual_wheels) == set(expected_wheels) + + out, err = capfd.readouterr() + + assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err diff --git a/test/utils.py b/test/utils.py index d999e1b40..31a974ecb 100644 --- a/test/utils.py +++ b/test/utils.py @@ -158,7 +158,9 @@ def expected_wheels( manylinux_versions: list[str] | None = None, musllinux_versions: list[str] | None = None, macosx_deployment_target: str = "10.9", + iphoneos_deployment_target: str = "13.0", machine_arch: str | None = None, + platform: str = platform, python_abi_tags: list[str] | None = None, include_universal2: bool = False, single_python: bool = False, @@ -190,15 +192,17 @@ def expected_wheels( wheel for architecture in architectures for wheel in _expected_wheels( - package_name, - package_version, - architecture, - manylinux_versions, - musllinux_versions, - macosx_deployment_target, - python_abi_tags, - include_universal2, - single_python, + package_name=package_name, + package_version=package_version, + machine_arch=architecture, + manylinux_versions=manylinux_versions, + musllinux_versions=musllinux_versions, + macosx_deployment_target=macosx_deployment_target, + iphoneos_deployment_target=iphoneos_deployment_target, + platform=platform, + python_abi_tags=python_abi_tags, + include_universal2=include_universal2, + single_python=single_python, ) ] @@ -210,6 +214,8 @@ def _expected_wheels( manylinux_versions: list[str] | None, musllinux_versions: list[str] | None, macosx_deployment_target: str, + iphoneos_deployment_target: str, + platform: str, python_abi_tags: list[str] | None, include_universal2: bool, single_python: bool, @@ -234,7 +240,9 @@ def _expected_wheels( if platform == "pyodide" and python_abi_tags is None: python_abi_tags = ["cp312-cp312"] - if python_abi_tags is None: + elif platform == "ios" and python_abi_tags is None: + python_abi_tags = ["cp313-cp313"] + elif python_abi_tags is None: python_abi_tags = [ "cp38-cp38", "cp39-cp39", @@ -314,6 +322,21 @@ def _expected_wheels( if include_universal2: platform_tags.append(f"macosx_{min_macosx.replace('.', '_')}_universal2") + + elif platform == "ios": + if machine_arch == "x86_64": + platform_tags = [ + f"ios_{iphoneos_deployment_target.replace('.', '_')}_x86_64_iphonesimulator" + ] + elif machine_arch == "arm64": + platform_tags = [ + f"ios_{iphoneos_deployment_target.replace('.', '_')}_arm64_iphoneos", + f"ios_{iphoneos_deployment_target.replace('.', '_')}_arm64_iphonesimulator", + ] + else: + msg = f"Unsupported architecture {machine_arch!r} for iOS" + raise Exception(msg) + else: msg = f"Unsupported platform {platform!r}" raise Exception(msg) From 96bd43c61015aa880aabe33ed8eb9fe4137d10ea Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 21 Apr 2025 20:24:24 +0100 Subject: [PATCH 4/8] Don't try to execute a test-command when it doesn't look like a module --- cibuildwheel/platforms/ios.py | 41 +++++++++++++++++++++++++---------- test/test_ios.py | 26 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py index af599a93e..ee251f5e9 100644 --- a/cibuildwheel/platforms/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -596,19 +596,36 @@ def build(options: Options, tmp_path: Path) -> None: test_command_parts = shlex.split(build_options.test_command) if test_command_parts[0:2] != ["python", "-m"]: - log.warning( - unwrap_preserving_paragraphs(f""" - iOS tests configured with a test command which doesn't start with - 'python -m'. iOS tests must execute python modules - other - entrypoints are not supported. - - cibuildwheel will try to execute it as if it started with 'python - -m'. If this works, all you need to do is add that to your test - command. - - Test command: {build_options.test_command} - """) + first_part = test_command_parts[0] + first_arg_looks_like_a_module_name = all( + n.isidentifier() for n in first_part.split(".") ) + if first_arg_looks_like_a_module_name: + log.warning( + unwrap_preserving_paragraphs(f""" + iOS tests configured with a test command which doesn't start + with 'python -m'. iOS tests must execute python modules - other + entrypoints are not supported. + + cibuildwheel will try to execute it as if it started with + 'python -m'. If this works, all you need to do is add that to + your test command. + + Test command: {build_options.test_command} + """) + ) + else: + # no point in trying to run it as a module - it's not a module + msg = unwrap_preserving_paragraphs( + f""" + iOS tests configured with a test command which doesn't start + with 'python -m'. iOS tests must execute python modules - other + entrypoints are not supported. + + Test command: {build_options.test_command} + """ + ) + raise errors.FatalError(msg) else: # the testbed run command actually doesn't want the # python -m prefix - it's implicit, so we remove it diff --git a/test/test_ios.py b/test/test_ios.py index 4a83c700f..e3e4464c8 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -249,3 +249,29 @@ def test_ios_test_command_without_python_dash_m(tmp_path, capfd): out, err = capfd.readouterr() assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err + + +def test_ios_test_command_invalid(tmp_path, capfd): + """Test command should raise an error if it's clearly invalid.""" + if utils.platform != "macos": + pytest.skip("this test can only run on macOS") + if utils.get_xcode_version() < (13, 0): + pytest.skip("this test only works with Xcode 13.0 or greater") + + project_dir = tmp_path / "project" + basic_project = test_projects.new_c_project() + basic_project.files["./my_test_script.sh"] = "echo hello" + basic_project.generate(project_dir) + + with pytest.raises(subprocess.CalledProcessError): + utils.cibuildwheel_run( + project_dir, + add_env={ + "CIBW_PLATFORM": "ios", + "CIBW_TEST_COMMAND": "./my_test_script.sh", + "CIBW_TEST_SOURCES": "./my_test_script.sh", + "CIBW_XBUILD_TOOLS": "", + }, + ) + out, err = capfd.readouterr() + assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err From 6658adc9b76f5bbbffba66b0ebe65308c5d0065c Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 5 May 2025 15:32:47 +0100 Subject: [PATCH 5/8] Update docs/options.md Co-authored-by: Malcolm Smith --- docs/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/options.md b/docs/options.md index 3ff5537f5..7c55d58d1 100644 --- a/docs/options.md +++ b/docs/options.md @@ -1242,7 +1242,7 @@ run your test suite. On all platforms other than iOS, the command is run in a shell, so you can write things like `cmd1 && cmd2`. -On iOS, the value of the `CIBW_TEST_COMMAND` setting should follow the format `python -m MODULE [...ARGS]` - where MODULE is a Python module name, followed by arguments that will be assigned to `sys.argv`. Shell commands cannot be used. +On iOS, the value of the `CIBW_TEST_COMMAND` setting must follow the format `python -m MODULE [ARGS...]` - where MODULE is a Python module name, followed by arguments that will be assigned to `sys.argv`. Other commands cannot be used. Platform-specific environment variables are also available:
`CIBW_TEST_COMMAND_MACOS` | `CIBW_TEST_COMMAND_WINDOWS` | `CIBW_TEST_COMMAND_LINUX` | `CIBW_TEST_COMMAND_IOS` | `CIBW_TEST_COMMAND_PYODIDE` From 36f5d7b9e59e0ef53962f6fdc02dd1a8b1598b56 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 5 May 2025 14:05:58 +0100 Subject: [PATCH 6/8] Only allow invalid test command if the first part is 'pytest' --- cibuildwheel/platforms/ios.py | 12 +++++------- test/test_ios.py | 18 +++++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py index ee251f5e9..9e06ecd07 100644 --- a/cibuildwheel/platforms/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -597,10 +597,9 @@ def build(options: Options, tmp_path: Path) -> None: test_command_parts = shlex.split(build_options.test_command) if test_command_parts[0:2] != ["python", "-m"]: first_part = test_command_parts[0] - first_arg_looks_like_a_module_name = all( - n.isidentifier() for n in first_part.split(".") - ) - if first_arg_looks_like_a_module_name: + if first_part == "pytest": + # pytest works exactly the same as a module, so we + # can just run it as a module. log.warning( unwrap_preserving_paragraphs(f""" iOS tests configured with a test command which doesn't start @@ -611,18 +610,17 @@ def build(options: Options, tmp_path: Path) -> None: 'python -m'. If this works, all you need to do is add that to your test command. - Test command: {build_options.test_command} + Test command: {build_options.test_command!r} """) ) else: - # no point in trying to run it as a module - it's not a module msg = unwrap_preserving_paragraphs( f""" iOS tests configured with a test command which doesn't start with 'python -m'. iOS tests must execute python modules - other entrypoints are not supported. - Test command: {build_options.test_command} + Test command: {build_options.test_command!r} """ ) raise errors.FatalError(msg) diff --git a/test/test_ios.py b/test/test_ios.py index e3e4464c8..cce36f5ec 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -215,18 +215,21 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd): def test_ios_test_command_without_python_dash_m(tmp_path, capfd): - """Test command should be able to run without python -m.""" + """pytest should be able to run without python -m, but it should warn.""" if utils.platform != "macos": pytest.skip("this test can only run on macOS") if utils.get_xcode_version() < (13, 0): pytest.skip("this test only works with Xcode 13.0 or greater") project_dir = tmp_path / "project" + project = test_projects.new_c_project() - project.files["tests_module/__init__.py"] = "" - project.files["tests_module/__main__.py"] = textwrap.dedent(""" - if __name__ == "__main__": - print("Hello from tests_module") + project.files["tests/__init__.py"] = "" + project.files["tests/test_spam.py"] = textwrap.dedent(""" + import spam + def test_spam(): + assert spam.filter("spam") == 0 + assert spam.filter("ham") != 0 """) project.generate(project_dir) @@ -235,8 +238,9 @@ def test_ios_test_command_without_python_dash_m(tmp_path, capfd): add_env={ "CIBW_PLATFORM": "ios", "CIBW_BUILD": "cp313-*", - "CIBW_TEST_COMMAND": "tests_module", - "CIBW_TEST_SOURCES": "tests_module", + "CIBW_TEST_COMMAND": "pytest ./tests", + "CIBW_TEST_SOURCES": "tests", + "CIBW_TEST_REQUIRES": "pytest", "CIBW_XBUILD_TOOLS": "", }, ) From 7c73ec2e77a6df1dd9704a57cdb33888f8a4eae1 Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Mon, 5 May 2025 15:32:25 +0100 Subject: [PATCH 7/8] Responses to code review from @freakboy3742 --- test/test_ios.py | 6 +++++- test/utils.py | 10 ++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test/test_ios.py b/test/test_ios.py index cce36f5ec..64f675c70 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -171,7 +171,10 @@ def test_no_xbuild_tool_definition(tmp_path, capfd): # The expected wheels were produced. expected_wheels = utils.expected_wheels( - "spam", "0.1.0", platform="ios", python_abi_tags=["cp313-cp313"] + "spam", + "0.1.0", + platform="ios", + python_abi_tags=["cp313-cp313"], ) assert set(actual_wheels) == set(expected_wheels) @@ -214,6 +217,7 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd): assert "Your project configuration does not define any cross-build tools." not in captured.err +@pytest.mark.serial def test_ios_test_command_without_python_dash_m(tmp_path, capfd): """pytest should be able to run without python -m, but it should warn.""" if utils.platform != "macos": diff --git a/test/utils.py b/test/utils.py index 31a974ecb..d64241dc6 100644 --- a/test/utils.py +++ b/test/utils.py @@ -157,8 +157,8 @@ def expected_wheels( package_version: str, manylinux_versions: list[str] | None = None, musllinux_versions: list[str] | None = None, - macosx_deployment_target: str = "10.9", - iphoneos_deployment_target: str = "13.0", + macosx_deployment_target: str | None = None, + iphoneos_deployment_target: str | None = None, machine_arch: str | None = None, platform: str = platform, python_abi_tags: list[str] | None = None, @@ -174,6 +174,12 @@ def expected_wheels( if platform == "linux": machine_arch = arch_name_for_linux(machine_arch) + if macosx_deployment_target is None: + macosx_deployment_target = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "10.9") + + if iphoneos_deployment_target is None: + iphoneos_deployment_target = os.environ.get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") + architectures = [machine_arch] if not single_arch: if platform == "linux": From 643b41b808fe0294299260da1f815e95d52dc0cb Mon Sep 17 00:00:00 2001 From: Joe Rickerby Date: Fri, 16 May 2025 12:22:03 +0100 Subject: [PATCH 8/8] Fixes post-merge --- test/test_ios.py | 4 ++-- test/utils.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/test_ios.py b/test/test_ios.py index 6ec9fdbb2..39d54ae91 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -220,7 +220,7 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd): @pytest.mark.serial def test_ios_test_command_without_python_dash_m(tmp_path, capfd): """pytest should be able to run without python -m, but it should warn.""" - if utils.platform != "macos": + if utils.get_platform() != "macos": pytest.skip("this test can only run on macOS") if utils.get_xcode_version() < (13, 0): pytest.skip("this test only works with Xcode 13.0 or greater") @@ -261,7 +261,7 @@ def test_spam(): def test_ios_test_command_invalid(tmp_path, capfd): """Test command should raise an error if it's clearly invalid.""" - if utils.platform != "macos": + if utils.get_platform() != "macos": pytest.skip("this test can only run on macOS") if utils.get_xcode_version() < (13, 0): pytest.skip("this test only works with Xcode 13.0 or greater") diff --git a/test/utils.py b/test/utils.py index 5d947a913..028e88863 100644 --- a/test/utils.py +++ b/test/utils.py @@ -170,7 +170,7 @@ def expected_wheels( macosx_deployment_target: str | None = None, iphoneos_deployment_target: str | None = None, machine_arch: str | None = None, - platform: str = platform, + platform: str | None = None, python_abi_tags: list[str] | None = None, include_universal2: bool = False, single_python: bool = False, @@ -179,9 +179,11 @@ def expected_wheels( """ Returns the expected wheels from a run of cibuildwheel. """ + platform = platform or get_platform() + if machine_arch is None: machine_arch = pm.machine() - if get_platform() == "linux": + if platform == "linux": machine_arch = arch_name_for_linux(machine_arch) if macosx_deployment_target is None: @@ -192,7 +194,7 @@ def expected_wheels( architectures = [machine_arch] if not single_arch: - if get_platform() == "linux": + if platform == "linux": if machine_arch == "x86_64": architectures.append("i686") elif ( @@ -201,7 +203,7 @@ def expected_wheels( and _AARCH64_CAN_RUN_ARMV7 ): architectures.append("armv7l") - elif get_platform() == "windows" and machine_arch == "AMD64": + elif platform == "windows" and machine_arch == "AMD64": architectures.append("x86") return [ @@ -239,8 +241,6 @@ def _expected_wheels( """ Returns a list of expected wheels from a run of cibuildwheel. """ - platform = get_platform() - # per PEP 425 (https://www.python.org/dev/peps/pep-0425/), wheel files shall have name of the form # {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl # {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel