Skip to content

Commit

Permalink
feat: add --only (#1098)
Browse files Browse the repository at this point in the history
* fix(style): use sub recommended replacement method

See https://docs.python.org/3/library/re.html#re.escape

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* feat: add --build

* refactor: switch to using --only

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: cover --only

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* docs: undo change to --plat for auditwheel

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* (unrelated) speed up running the unit tests

based on a profile run, this speeds up the unit tests by 3x

* Remove `intercepted_build_args` reliance on `platform` fixture

* Add test that checks if 'only' overrides env var options

* Ensure that the --only command line option overrides env vars

* Fix UnboundLocalError

* Test cleanups

* style: typing.cast instead of plain cast

* feat: add only: to the GHA

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
  • Loading branch information
henryiii and joerick authored Sep 9, 2022
1 parent 3a46bde commit eb39da0
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 27 deletions.
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ inputs:
description: 'File containing the config, defaults to {package}/pyproject.toml'
required: false
default: ''
only:
description: 'Build a specific wheel only. No need for arch/platform if this is set'
required: false
default: ''
branding:
icon: package
color: yellow
Expand All @@ -36,5 +40,6 @@ runs:
${{ inputs.package-dir }}
--output-dir ${{ inputs.output-dir }}
--config-file "${{ inputs.config-file }}"
--only "${{ inputs.only }}"
2>&1
shell: bash
51 changes: 44 additions & 7 deletions cibuildwheel/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import sys
import tarfile
import textwrap
import typing
from pathlib import Path
from tempfile import mkdtemp

Expand Down Expand Up @@ -40,7 +41,7 @@ def main() -> None:
parser.add_argument(
"--platform",
choices=["auto", "linux", "macos", "windows"],
default=os.environ.get("CIBW_PLATFORM", "auto"),
default=None,
help="""
Platform to build for. Use this option to override the
auto-detected platform or to run cibuildwheel on your development
Expand All @@ -64,6 +65,16 @@ def main() -> None:
""",
)

parser.add_argument(
"--only",
default=None,
help="""
Force a single wheel build when given an identifier. Overrides
CIBW_BUILD/CIBW_SKIP. --platform and --arch cannot be specified
if this is given.
""",
)

parser.add_argument(
"--output-dir",
type=Path,
Expand Down Expand Up @@ -154,10 +165,40 @@ def main() -> None:


def build_in_directory(args: CommandLineArguments) -> None:
platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "auto")
platform: PlatformName

if args.platform != "auto":
platform = args.platform
if args.only:
if "linux_" in args.only:
platform = "linux"
elif "macosx_" in args.only:
platform = "macos"
elif "win_" in args.only:
platform = "windows"
else:
print(
f"Invalid --only='{args.only}', must be a build selector with a known platform",
file=sys.stderr,
)
sys.exit(2)
if args.platform is not None:
print(
"--platform cannot be specified with --only, it is computed from --only",
file=sys.stderr,
)
sys.exit(2)
if args.archs is not None:
print(
"--arch cannot be specified with --only, it is computed from --only",
file=sys.stderr,
)
sys.exit(2)
elif platform_option_value != "auto":
if platform_option_value not in PLATFORMS:
print(f"cibuildwheel: Unsupported platform: {platform_option_value}", file=sys.stderr)
sys.exit(2)

platform = typing.cast(PlatformName, platform_option_value)
else:
ci_provider = detect_ci_provider()
if ci_provider is None:
Expand Down Expand Up @@ -187,10 +228,6 @@ def build_in_directory(args: CommandLineArguments) -> None:
)
sys.exit(2)

if platform not in PLATFORMS:
print(f"cibuildwheel: Unsupported platform: {platform}", file=sys.stderr)
sys.exit(2)

options = compute_options(platform=platform, command_line_arguments=args)

package_dir = options.globals.package_dir
Expand Down
20 changes: 15 additions & 5 deletions cibuildwheel/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@

@dataclass
class CommandLineArguments:
platform: Literal["auto", "linux", "macos", "windows"]
platform: Literal["auto", "linux", "macos", "windows"] | None
archs: str | None
output_dir: Path
only: str | None
config_file: str
package_dir: Path
print_build_identifiers: bool
Expand Down Expand Up @@ -403,6 +404,15 @@ def globals(self) -> GlobalOptions:
)
requires_python = None if requires_python_str is None else SpecifierSet(requires_python_str)

archs_config_str = args.archs or self.reader.get("archs", sep=" ")
architectures = Architecture.parse_config(archs_config_str, platform=self.platform)

# Process `--only`
if args.only:
build_config = args.only
skip_config = ""
architectures = Architecture.all_archs(self.platform)

build_selector = BuildSelector(
build_config=build_config,
skip_config=skip_config,
Expand All @@ -411,9 +421,6 @@ def globals(self) -> GlobalOptions:
)
test_selector = TestSelector(skip_config=test_skip)

archs_config_str = args.archs or self.reader.get("archs", sep=" ")
architectures = Architecture.parse_config(archs_config_str, platform=self.platform)

container_engine_str = self.reader.get("container-engine")

if container_engine_str not in ["docker", "podman"]:
Expand Down Expand Up @@ -588,6 +595,9 @@ def summary(self, identifiers: list[str]) -> str:
]

build_option_defaults = self.build_options(identifier=None)
build_options_for_identifier = {
identifier: self.build_options(identifier) for identifier in identifiers
}

for option_name, default_value in sorted(asdict(build_option_defaults).items()):
if option_name == "globals":
Expand All @@ -597,7 +607,7 @@ def summary(self, identifiers: list[str]) -> str:

# if any identifiers have an overridden value, print that too
for identifier in identifiers:
option_value = getattr(self.build_options(identifier=identifier), option_name)
option_value = getattr(build_options_for_identifier[identifier], option_name)
if option_value != default_value:
lines.append(f" {identifier}: {option_value!r}")

Expand Down
6 changes: 2 additions & 4 deletions cibuildwheel/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def shell(*commands: str, env: dict[str, str] | None = None, cwd: PathOrStr | No
subprocess.run(command, env=env, cwd=cwd, shell=True, check=True)


def format_safe(template: str, **kwargs: Any) -> str:
def format_safe(template: str, **kwargs: str | os.PathLike[str]) -> str:
"""
Works similarly to `template.format(**kwargs)`, except that unmatched
fields in `template` are passed through untouched.
Expand Down Expand Up @@ -173,11 +173,9 @@ def format_safe(template: str, **kwargs: Any) -> str:
re.VERBOSE,
)

# we use a function for repl to prevent re.sub interpreting backslashes
# in repl as escape sequences.
result = re.sub(
pattern=find_pattern,
repl=lambda _: str(value), # pylint: disable=cell-var-from-loop
repl=str(value).replace("\\", r"\\"),
string=result,
)

Expand Down
6 changes: 5 additions & 1 deletion docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ The complete set of defaults for the current version of cibuildwheel are shown b

!!! tip
Static configuration works across all CI systems, and can be used locally if
you run `cibuildwheel --plat linux`. This is preferred, but environment
you run `cibuildwheel --platform linux`. This is preferred, but environment
variables are better if you need to change per-matrix element
(`CIBW_BUILD` is often in this category, for example), or if you cannot or do
not want to change a `pyproject.toml` file. You can specify a different file to
Expand Down Expand Up @@ -202,6 +202,10 @@ This option can also be set using the [command-line option](#command-line) `--pl

This is even more convenient if you store your cibuildwheel config in [`pyproject.toml`](#configuration-file).

You can also run a single identifier with `--only <identifier>`. This will
not require `--platform` or `--arch`, and will override any build/skip
configuration.

### `CIBW_BUILD`, `CIBW_SKIP` {: #build-skip}

> Choose the Python versions to build
Expand Down
24 changes: 14 additions & 10 deletions unit_test/main_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@


class ArgsInterceptor:
def __init__(self):
self.call_count = 0
self.args = None
self.kwargs = None

def __call__(self, *args, **kwargs):
self.call_count += 1
self.args = args
self.kwargs = kwargs

Expand Down Expand Up @@ -75,16 +81,14 @@ def platform(request, monkeypatch):


@pytest.fixture
def intercepted_build_args(platform, monkeypatch):
def intercepted_build_args(monkeypatch):
intercepted = ArgsInterceptor()

if platform == "linux":
monkeypatch.setattr(linux, "build", intercepted)
elif platform == "macos":
monkeypatch.setattr(macos, "build", intercepted)
elif platform == "windows":
monkeypatch.setattr(windows, "build", intercepted)
else:
raise ValueError(f"unknown platform value: {platform}")
monkeypatch.setattr(linux, "build", intercepted)
monkeypatch.setattr(macos, "build", intercepted)
monkeypatch.setattr(windows, "build", intercepted)

yield intercepted

return intercepted
# check that intercepted_build_args only ever had one set of args
assert intercepted.call_count <= 1
70 changes: 70 additions & 0 deletions unit_test/main_tests/main_platform_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,73 @@ def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
Architecture.arm64,
Architecture.universal2,
}


@pytest.mark.parametrize(
"only,plat",
(
("cp311-manylinux_x86_64", "linux"),
("cp310-win_amd64", "windows"),
("cp311-macosx_x86_64", "macos"),
),
)
def test_only_argument(intercepted_build_args, monkeypatch, only, plat):
monkeypatch.setenv("CIBW_BUILD", "unused")
monkeypatch.setenv("CIBW_SKIP", "unused")
monkeypatch.setattr(sys, "argv", sys.argv + ["--only", only])

main()

options = intercepted_build_args.args[0]
assert options.globals.build_selector.build_config == only
assert options.globals.build_selector.skip_config == ""
assert options.platform == plat
assert options.globals.architectures == Architecture.all_archs(plat)


@pytest.mark.parametrize("only", ("cp311-manylxinux_x86_64", "some_linux_thing"))
def test_only_failed(monkeypatch, only):
monkeypatch.setattr(sys, "argv", sys.argv + ["--only", only])

with pytest.raises(SystemExit):
main()


def test_only_no_platform(monkeypatch):
monkeypatch.setattr(
sys, "argv", sys.argv + ["--only", "cp311-manylinux_x86_64", "--platform", "macos"]
)

with pytest.raises(SystemExit):
main()


def test_only_no_archs(monkeypatch):
monkeypatch.setattr(
sys, "argv", sys.argv + ["--only", "cp311-manylinux_x86_64", "--archs", "x86_64"]
)

with pytest.raises(SystemExit):
main()


@pytest.mark.parametrize(
"envvar_name,envvar_value",
(
("CIBW_BUILD", "cp310-*"),
("CIBW_SKIP", "cp311-*"),
("CIBW_ARCHS", "auto32"),
("CIBW_PLATFORM", "macos"),
),
)
def test_only_overrides_env_vars(monkeypatch, intercepted_build_args, envvar_name, envvar_value):
monkeypatch.setattr(sys, "argv", sys.argv + ["--only", "cp311-manylinux_x86_64"])
monkeypatch.setenv(envvar_name, envvar_value)

main()

options = intercepted_build_args.args[0]
assert options.globals.build_selector.build_config == "cp311-manylinux_x86_64"
assert options.globals.build_selector.skip_config == ""
assert options.platform == "linux"
assert options.globals.architectures == Architecture.all_archs("linux")
1 change: 1 addition & 0 deletions unit_test/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def get_default_command_line_arguments() -> CommandLineArguments:
platform="auto",
allow_empty=False,
archs=None,
only=None,
config_file="",
output_dir=Path("wheelhouse"),
package_dir=Path("."),
Expand Down

0 comments on commit eb39da0

Please sign in to comment.