feat: configuration support (#684)
* feat: configuration support
* refactor: include manylinux, minor redesign
* "project global" overrides "default platform"
This allows overriding `repair-wheel-command` in the `[tool.cibuildwheel.global]` section.
* fix: add PyPy images
* refactor: remove global
* fix: support tables and arrays
* docs: add pyproject.toml config to docs
* docs: updates based on feedback
* fix: only join if sensible
* refactor: remove manylinux dict
* docs: examples headers and sections on each
* feat: support changing the config from the command line
* fix: use {package}
* tests: add a few tests and mention config one more place
* Restyle examples tabs to remove indent
* Unrelated docs improvements
* Absorb extra content into the tab
* Make the tabs smaller and to the right
* Copy edits to options
* Fix header ids
* Use fewer [tool.cibuildwheel] headers
* docs: minor additions/fixes
* feat: disallow options on some platforms
* docs: make the examples a tiny bit more tabby
* fix: enforce tables/lists are optionally separate
* Some unrelated fixes to the other tab styling
* Minor fix to tab styling
* Refactor to make code a bit more linear and immutable
* Minor docs changes
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
This commit is contained in:
co-authored by
mayeut
Joe Rickerby
parent
f7bdccfbd7
commit
f62cbc303a
@@ -123,6 +123,7 @@ Options
|
||||
| | [`CIBW_TEST_SKIP`](https://cibuildwheel.readthedocs.io/en/stable/options/#test-skip) | Skip running tests on some builds |
|
||||
| **Other** | [`CIBW_BUILD_VERBOSITY`](https://cibuildwheel.readthedocs.io/en/stable/options/#build-verbosity) | Increase/decrease the output of pip wheel |
|
||||
|
||||
These options can be specified in a pyproject.toml file, as well; see [configuration](https://cibuildwheel.readthedocs.io/en/stable/options/#configuration).
|
||||
|
||||
Working examples
|
||||
----------------
|
||||
|
||||
+62
-95
@@ -5,7 +5,7 @@ import textwrap
|
||||
import traceback
|
||||
from configparser import ConfigParser
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Set, Union, overload
|
||||
from typing import Dict, List, Optional, Set, Union
|
||||
|
||||
from packaging.specifiers import SpecifierSet
|
||||
|
||||
@@ -16,6 +16,7 @@ import cibuildwheel.util
|
||||
import cibuildwheel.windows
|
||||
from cibuildwheel.architecture import Architecture, allowed_architectures_check
|
||||
from cibuildwheel.environment import EnvironmentParseError, parse_environment
|
||||
from cibuildwheel.options import ConfigOptions
|
||||
from cibuildwheel.projectfiles import get_requires_python_str
|
||||
from cibuildwheel.typing import PLATFORMS, PlatformName, assert_never
|
||||
from cibuildwheel.util import (
|
||||
@@ -28,39 +29,16 @@ from cibuildwheel.util import (
|
||||
resources_dir,
|
||||
)
|
||||
|
||||
|
||||
@overload
|
||||
def get_option_from_environment(
|
||||
option_name: str, *, platform: Optional[str] = None, default: str
|
||||
) -> str:
|
||||
... # noqa: E704
|
||||
|
||||
|
||||
@overload
|
||||
def get_option_from_environment(
|
||||
option_name: str, *, platform: Optional[str] = None, default: None = None
|
||||
) -> Optional[str]:
|
||||
... # noqa: E704 E302
|
||||
|
||||
|
||||
def get_option_from_environment(
|
||||
option_name: str, *, platform: Optional[str] = None, default: Optional[str] = None
|
||||
) -> Optional[str]: # noqa: E302
|
||||
"""
|
||||
Returns an option from the environment, optionally scoped by the platform.
|
||||
|
||||
Example:
|
||||
get_option_from_environment('CIBW_COLOR', platform='macos')
|
||||
|
||||
This will return the value of CIBW_COLOR_MACOS if it exists, otherwise the value of
|
||||
CIBW_COLOR.
|
||||
"""
|
||||
if platform:
|
||||
option = os.environ.get(f"{option_name}_{platform.upper()}")
|
||||
if option is not None:
|
||||
return option
|
||||
|
||||
return os.environ.get(option_name, default)
|
||||
MANYLINUX_ARCHS = (
|
||||
"x86_64",
|
||||
"i686",
|
||||
"pypy_x86_64",
|
||||
"aarch64",
|
||||
"ppc64le",
|
||||
"s390x",
|
||||
"pypy_aarch64",
|
||||
"pypy_i686",
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -69,8 +47,9 @@ def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Build wheels for all the platforms.",
|
||||
epilog="""
|
||||
Most options are supplied via environment variables.
|
||||
See https://github.com/pypa/cibuildwheel#options for info.
|
||||
Most options are supplied via environment variables or in
|
||||
--config-file (pyproject.toml usually). See
|
||||
https://github.com/pypa/cibuildwheel#options for info.
|
||||
""",
|
||||
)
|
||||
|
||||
@@ -104,10 +83,17 @@ def main() -> None:
|
||||
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
default=os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse"),
|
||||
help="Destination folder for the wheels.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--config-file",
|
||||
help="""
|
||||
TOML config file for cibuildwheel. Defaults to pyproject.toml, but
|
||||
can be overridden with this option.
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"package_dir",
|
||||
default=".",
|
||||
@@ -176,40 +162,40 @@ def main() -> None:
|
||||
sys.exit(2)
|
||||
|
||||
package_dir = Path(args.package_dir)
|
||||
output_dir = Path(args.output_dir)
|
||||
|
||||
if platform == "linux":
|
||||
repair_command_default = "auditwheel repair -w {dest_dir} {wheel}"
|
||||
elif platform == "macos":
|
||||
repair_command_default = "delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}"
|
||||
elif platform == "windows":
|
||||
repair_command_default = ""
|
||||
else:
|
||||
assert_never(platform)
|
||||
manylinux_identifiers = {
|
||||
f"manylinux-{build_platform}-image" for build_platform in MANYLINUX_ARCHS
|
||||
}
|
||||
disallow = {
|
||||
"linux": {"dependency-versions"},
|
||||
"macos": manylinux_identifiers,
|
||||
"windows": manylinux_identifiers,
|
||||
}
|
||||
options = ConfigOptions(package_dir, args.config_file, platform=platform, disallow=disallow)
|
||||
output_dir = Path(
|
||||
args.output_dir
|
||||
if args.output_dir is not None
|
||||
else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
|
||||
)
|
||||
|
||||
build_config = options("build", env_plat=False, sep=" ") or "*"
|
||||
skip_config = options("skip", env_plat=False, sep=" ")
|
||||
test_skip = options("test-skip", env_plat=False, sep=" ")
|
||||
|
||||
archs_config_str = args.archs or options("archs", sep=" ")
|
||||
|
||||
environment_config = options("environment", table={"item": '{k}="{v}"', "sep": " "})
|
||||
before_all = options("before-all", sep=" && ")
|
||||
before_build = options("before-build", sep=" && ")
|
||||
repair_command = options("repair-wheel-command", sep=" && ")
|
||||
|
||||
dependency_versions = options("dependency-versions")
|
||||
test_command = options("test-command", sep=" && ")
|
||||
before_test = options("before-test", sep=" && ")
|
||||
test_requires = options("test-requires", sep=" ").split()
|
||||
test_extras = options("test-extras", sep=",")
|
||||
build_verbosity_str = options("build-verbosity")
|
||||
|
||||
build_config = os.environ.get("CIBW_BUILD") or "*"
|
||||
skip_config = os.environ.get("CIBW_SKIP", "")
|
||||
test_skip = os.environ.get("CIBW_TEST_SKIP", "")
|
||||
environment_config = get_option_from_environment(
|
||||
"CIBW_ENVIRONMENT", platform=platform, default=""
|
||||
)
|
||||
before_all = get_option_from_environment("CIBW_BEFORE_ALL", platform=platform, default="")
|
||||
before_build = get_option_from_environment("CIBW_BEFORE_BUILD", platform=platform)
|
||||
repair_command = get_option_from_environment(
|
||||
"CIBW_REPAIR_WHEEL_COMMAND", platform=platform, default=repair_command_default
|
||||
)
|
||||
dependency_versions = get_option_from_environment(
|
||||
"CIBW_DEPENDENCY_VERSIONS", platform=platform, default="pinned"
|
||||
)
|
||||
test_command = get_option_from_environment("CIBW_TEST_COMMAND", platform=platform)
|
||||
before_test = get_option_from_environment("CIBW_BEFORE_TEST", platform=platform)
|
||||
test_requires = get_option_from_environment(
|
||||
"CIBW_TEST_REQUIRES", platform=platform, default=""
|
||||
).split()
|
||||
test_extras = get_option_from_environment("CIBW_TEST_EXTRAS", platform=platform, default="")
|
||||
build_verbosity_str = get_option_from_environment(
|
||||
"CIBW_BUILD_VERBOSITY", platform=platform, default=""
|
||||
)
|
||||
prerelease_pythons = args.prerelease_pythons or cibuildwheel.util.strtobool(
|
||||
os.environ.get("CIBW_PRERELEASE_PYTHONS", "0")
|
||||
)
|
||||
@@ -218,11 +204,11 @@ def main() -> None:
|
||||
|
||||
if not any(package_dir.joinpath(name).exists() for name in package_files):
|
||||
names = ", ".join(sorted(package_files, reverse=True))
|
||||
print(
|
||||
f"cibuildwheel: Could not find any of {{{names}}} at root of package", file=sys.stderr
|
||||
)
|
||||
msg = f"cibuildwheel: Could not find any of {{{names}}} at root of package"
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
# This is not supported in tool.cibuildwheel, as it comes from a standard location.
|
||||
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
|
||||
requires_python_str: Optional[str] = os.environ.get(
|
||||
"CIBW_PROJECT_REQUIRES_PYTHON"
|
||||
@@ -270,13 +256,6 @@ def main() -> None:
|
||||
# This needs to be passed on to the docker container in linux.py
|
||||
os.environ["CIBUILDWHEEL"] = "1"
|
||||
|
||||
if args.archs is not None:
|
||||
archs_config_str = args.archs
|
||||
else:
|
||||
archs_config_str = get_option_from_environment(
|
||||
"CIBW_ARCHS", platform=platform, default="auto"
|
||||
)
|
||||
|
||||
archs = Architecture.parse_config(archs_config_str, platform=platform)
|
||||
|
||||
identifiers = get_build_identifiers(platform, build_selector, archs)
|
||||
@@ -286,7 +265,7 @@ def main() -> None:
|
||||
print(identifier)
|
||||
sys.exit(0)
|
||||
|
||||
manylinux_images: Optional[Dict[str, str]] = None
|
||||
manylinux_images: Dict[str, str] = {}
|
||||
if platform == "linux":
|
||||
pinned_docker_images_file = resources_dir / "pinned_docker_images.cfg"
|
||||
all_pinned_docker_images = ConfigParser()
|
||||
@@ -297,22 +276,10 @@ def main() -> None:
|
||||
# 'pypy_x86_64': {'manylinux2010': '...' }
|
||||
# ... }
|
||||
|
||||
manylinux_images = {}
|
||||
|
||||
for build_platform in [
|
||||
"x86_64",
|
||||
"i686",
|
||||
"pypy_x86_64",
|
||||
"aarch64",
|
||||
"ppc64le",
|
||||
"s390x",
|
||||
"pypy_aarch64",
|
||||
"pypy_i686",
|
||||
]:
|
||||
for build_platform in MANYLINUX_ARCHS:
|
||||
pinned_images = all_pinned_docker_images[build_platform]
|
||||
|
||||
config_name = f"CIBW_MANYLINUX_{build_platform.upper()}_IMAGE"
|
||||
config_value = os.environ.get(config_name)
|
||||
config_value = options(f"manylinux-{build_platform}-image")
|
||||
|
||||
if config_value is None:
|
||||
# default to manylinux2010 if it's available, otherwise manylinux2014
|
||||
@@ -340,7 +307,7 @@ def main() -> None:
|
||||
repair_command=repair_command,
|
||||
environment=environment,
|
||||
dependency_constraints=dependency_constraints,
|
||||
manylinux_images=manylinux_images,
|
||||
manylinux_images=manylinux_images or None,
|
||||
)
|
||||
|
||||
# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Mapping, Optional, Set, Tuple, Union
|
||||
|
||||
import toml
|
||||
|
||||
from .typing import PLATFORMS, TypedDict
|
||||
from .util import resources_dir
|
||||
|
||||
Setting = Union[Dict[str, str], List[str], str]
|
||||
|
||||
|
||||
class TableFmt(TypedDict):
|
||||
item: str
|
||||
sep: str
|
||||
|
||||
|
||||
class ConfigOptionError(KeyError):
|
||||
pass
|
||||
|
||||
|
||||
def _dig_first(*pairs: Tuple[Mapping[str, Any], str]) -> Setting:
|
||||
"""
|
||||
Return the first dict item that matches from pairs of dicts and keys.
|
||||
Final result is will throw a KeyError if missing.
|
||||
|
||||
_dig_first((dict1, "key1"), (dict2, "key2"), ...)
|
||||
"""
|
||||
(dict_like, key), *others = pairs
|
||||
return dict_like.get(key, _dig_first(*others)) if others else dict_like[key]
|
||||
|
||||
|
||||
class ConfigOptions:
|
||||
"""
|
||||
Gets options from the environment, config or defaults, optionally scoped
|
||||
by the platform.
|
||||
|
||||
Example:
|
||||
>>> options = ConfigOptions(package_dir, platform='macos')
|
||||
>>> options('cool-color')
|
||||
|
||||
This will return the value of CIBW_COOL_COLOR_MACOS if it exists,
|
||||
otherwise the value of CIBW_COOL_COLOR, otherwise
|
||||
'tool.cibuildwheel.macos.cool-color' or 'tool.cibuildwheel.cool-color'
|
||||
from pyproject.toml, or from cibuildwheel/resources/defaults.toml. An
|
||||
error is thrown if there are any unexpected keys or sections in
|
||||
tool.cibuildwheel.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
package_path: Path,
|
||||
config_file: Optional[str] = None,
|
||||
*,
|
||||
platform: str,
|
||||
disallow: Optional[Dict[str, Set[str]]] = None,
|
||||
) -> None:
|
||||
self.platform = platform
|
||||
self.disallow = disallow or {}
|
||||
|
||||
# Open defaults.toml, loading both global and platform sections
|
||||
defaults_path = resources_dir / "defaults.toml"
|
||||
self.default_options, self.default_platform_options = self._load_file(defaults_path)
|
||||
|
||||
# load the project config file
|
||||
config_options: Dict[str, Any] = {}
|
||||
config_platform_options: Dict[str, Any] = {}
|
||||
|
||||
if config_file is not None:
|
||||
config_path = Path(config_file.format(package=package_path))
|
||||
config_options, config_platform_options = self._load_file(config_path)
|
||||
else:
|
||||
# load pyproject.toml, if it's available
|
||||
pyproject_toml_path = package_path / "pyproject.toml"
|
||||
if pyproject_toml_path.exists():
|
||||
config_options, config_platform_options = self._load_file(pyproject_toml_path)
|
||||
|
||||
# validate project config
|
||||
for option_name in config_options:
|
||||
if not self._is_valid_global_option(option_name):
|
||||
raise ConfigOptionError(f'Option "{option_name}" not supported in a config file')
|
||||
|
||||
for option_name in config_platform_options:
|
||||
if not self._is_valid_platform_option(option_name):
|
||||
raise ConfigOptionError(
|
||||
f'Option "{option_name}" not supported in the "{self.platform}" section'
|
||||
)
|
||||
|
||||
self.config_options = config_options
|
||||
self.config_platform_options = config_platform_options
|
||||
|
||||
def _is_valid_global_option(self, name: str) -> bool:
|
||||
"""
|
||||
Returns True if an option with this name is allowed in the
|
||||
[tool.cibuildwheel] section of a config file.
|
||||
"""
|
||||
allowed_option_names = self.default_options.keys() | PLATFORMS
|
||||
|
||||
return name in allowed_option_names
|
||||
|
||||
def _is_valid_platform_option(self, name: str) -> bool:
|
||||
"""
|
||||
Returns True if an option with this name is allowed in the
|
||||
[tool.cibuildwheel.<current-platform>] section of a config file.
|
||||
"""
|
||||
disallowed_platform_options = self.disallow.get(self.platform, set())
|
||||
if name in disallowed_platform_options:
|
||||
return False
|
||||
|
||||
allowed_option_names = self.default_options.keys() | self.default_platform_options.keys()
|
||||
|
||||
return name in allowed_option_names
|
||||
|
||||
def _load_file(self, filename: Path) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||
"""
|
||||
Load a toml file, returns global and platform as separate dicts.
|
||||
"""
|
||||
config = toml.load(filename)
|
||||
|
||||
global_options = config.get("tool", {}).get("cibuildwheel", {})
|
||||
platform_options = global_options.get(self.platform, {})
|
||||
|
||||
return global_options, platform_options
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
env_plat: bool = True,
|
||||
sep: Optional[str] = None,
|
||||
table: Optional[TableFmt] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get and return the value for the named option from environment,
|
||||
configuration file, or the default. If env_plat is False, then don't
|
||||
accept platform versions of the environment variable. If this is an
|
||||
array it will be merged with "sep" before returning. If it is a table,
|
||||
it will be formatted with "table['item']" using {k} and {v} and merged
|
||||
with "table['sep']".
|
||||
"""
|
||||
|
||||
if name not in self.default_options and name not in self.default_platform_options:
|
||||
raise ConfigOptionError(f"{name} must be in cibuildwheel/resources/defaults.toml file")
|
||||
|
||||
# Environment variable form
|
||||
envvar = f"CIBW_{name.upper().replace('-', '_')}"
|
||||
plat_envvar = f"{envvar}_{self.platform.upper()}"
|
||||
|
||||
# get the option from the environment, then the config file, then finally the default.
|
||||
# platform-specific options are preferred, if they're allowed.
|
||||
result = _dig_first(
|
||||
(os.environ if env_plat else {}, plat_envvar), # type: ignore
|
||||
(os.environ, envvar),
|
||||
(self.config_platform_options, name),
|
||||
(self.config_options, name),
|
||||
(self.default_platform_options, name),
|
||||
(self.default_options, name),
|
||||
)
|
||||
|
||||
if isinstance(result, dict):
|
||||
if table is None:
|
||||
raise ConfigOptionError(f"{name} does not accept a table")
|
||||
return table["sep"].join(table["item"].format(k=k, v=v) for k, v in result.items())
|
||||
elif isinstance(result, list):
|
||||
if sep is None:
|
||||
raise ConfigOptionError(f"{name} does not accept a list")
|
||||
return sep.join(result)
|
||||
elif isinstance(result, int):
|
||||
return str(result)
|
||||
else:
|
||||
return result
|
||||
@@ -0,0 +1,39 @@
|
||||
[tool.cibuildwheel]
|
||||
build = "*"
|
||||
skip = ""
|
||||
test-skip = ""
|
||||
|
||||
archs = ["auto"]
|
||||
dependency-versions = "pinned"
|
||||
environment = {}
|
||||
build-verbosity = ""
|
||||
|
||||
before-all = ""
|
||||
before-build = ""
|
||||
repair-wheel-command = ""
|
||||
|
||||
test-command = ""
|
||||
before-test = ""
|
||||
test-requires = []
|
||||
test-extras = []
|
||||
|
||||
manylinux-x86_64-image = "manylinux2010"
|
||||
manylinux-i686-image = "manylinux2010"
|
||||
manylinux-aarch64-image = "manylinux2014"
|
||||
manylinux-ppc64le-image = "manylinux2014"
|
||||
manylinux-s390x-image = "manylinux2014"
|
||||
manylinux-pypy_x86_64-image = "manylinux2010"
|
||||
manylinux-pypy_i686-image = "manylinux2010"
|
||||
manylinux-pypy_aarch64-image = "manylinux2014"
|
||||
|
||||
|
||||
[tool.cibuildwheel.linux]
|
||||
repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}"
|
||||
|
||||
[tool.cibuildwheel.macos]
|
||||
repair-wheel-command = [
|
||||
"delocate-listdeps {wheel}",
|
||||
"delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}",
|
||||
]
|
||||
|
||||
[tool.cibuildwheel.windows]
|
||||
+73
-8
@@ -1,6 +1,10 @@
|
||||
|
||||
/* Global styles */
|
||||
|
||||
body {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 12px;
|
||||
line-height: 26px;
|
||||
@@ -20,9 +24,9 @@ code, pre, tt {
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
padding: 1em;
|
||||
background-color: #f6f8fa;
|
||||
pre code, pre code.hljs {
|
||||
padding: 1em;
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@@ -112,7 +116,21 @@ h1, h2, h3, h4, h5, h6 {
|
||||
background-color: white;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
border-bottom: 2px solid #ccc;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.tabs-header::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: #ccc;
|
||||
}
|
||||
.tabs-header > * {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Style the buttons that are used to open the tab content */
|
||||
@@ -126,15 +144,13 @@ h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
padding: 8px 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
/* put the border on top of the parent border */
|
||||
margin-bottom: -2px;
|
||||
border-bottom: 2px solid #ccc;
|
||||
}
|
||||
|
||||
.tabs-header button:focus-visible {
|
||||
/* preserve an outline for accesibility purposes */
|
||||
outline: 1px solid currentColor;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* Change background color of buttons on hover */
|
||||
@@ -153,4 +169,53 @@ h1, h2, h3, h4, h5, h6 {
|
||||
background-color: #f1f6fa;
|
||||
padding: 1px 1em;
|
||||
padding-top: 0.8em;
|
||||
|
||||
/* don't collapse inner margins */
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
/* Examples tabs styling */
|
||||
|
||||
.tabs.examples .tabs-header {
|
||||
justify-content: flex-end;
|
||||
background-color: transparent;
|
||||
border-bottom-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.tabs.examples .tabs-header::before {
|
||||
height: 0;
|
||||
}
|
||||
.tabs.examples .tabs-header button {
|
||||
padding: 6px 12px;
|
||||
border-bottom: none;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
font-size: 0.8em;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.tabs-header button:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.tabs.examples .tabs-header button.active {
|
||||
border-bottom-style: none;
|
||||
background: #f6f8fa;
|
||||
color: inherit;
|
||||
}
|
||||
.tabs.examples .tabs-content {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
.tabs.examples .tabs-content .tab > * {
|
||||
padding-left: 0.8em;
|
||||
padding-right: 0.8em;
|
||||
}
|
||||
.tabs.examples .tabs-content .tab > pre {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.tabs.examples pre:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,17 @@ while (true) {
|
||||
tabContainer.insertBefore(firstTab);
|
||||
tabContainer.append(headerContainer, contentContainer)
|
||||
|
||||
// add extra classes from the first tab to the container
|
||||
const classes = Array.from(firstTab[0].classList)
|
||||
|
||||
for (let i = 0; i < classes.length; i++) {
|
||||
const element = classes[i];
|
||||
if (element == 'tab' || element == 'admonition') {
|
||||
continue
|
||||
}
|
||||
tabContainer.addClass(element)
|
||||
}
|
||||
|
||||
const selectTab = function (index) {
|
||||
headerContainer.children().removeClass('active')
|
||||
headerContainer.children().eq(index).addClass('active')
|
||||
|
||||
+561
-174
File diff suppressed because it is too large
Load Diff
@@ -200,7 +200,7 @@ def test_test_command(
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.args[0].test_command == test_command
|
||||
assert intercepted_build_args.args[0].test_command == (test_command or "")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("before_build", [None, "before --build"])
|
||||
@@ -217,7 +217,7 @@ def test_before_build(
|
||||
|
||||
main()
|
||||
|
||||
assert intercepted_build_args.args[0].before_build == before_build
|
||||
assert intercepted_build_args.args[0].before_build == (before_build or "")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("build_verbosity", [None, 0, 2, -2, 4, -4])
|
||||
@@ -286,7 +286,4 @@ def test_before_all(before_all, platform_specific, platform, intercepted_build_a
|
||||
|
||||
main()
|
||||
|
||||
if before_all is None:
|
||||
before_all = ""
|
||||
|
||||
assert intercepted_build_args.args[0].before_all == before_all
|
||||
assert intercepted_build_args.args[0].before_all == (before_all or "")
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
import pytest
|
||||
|
||||
from cibuildwheel.options import ConfigOptionError, ConfigOptions
|
||||
|
||||
PYPROJECT_1 = """
|
||||
[tool.cibuildwheel]
|
||||
build = "cp39*"
|
||||
environment = {THING = "OTHER", FOO="BAR"}
|
||||
|
||||
test-command = "pyproject"
|
||||
test-requires = "something"
|
||||
test-extras = ["one", "two"]
|
||||
|
||||
manylinux-x86_64-image = "manylinux1"
|
||||
|
||||
[tool.cibuildwheel.macos]
|
||||
test-requires = "else"
|
||||
|
||||
[tool.cibuildwheel.linux]
|
||||
test-requires = ["other", "many"]
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture(params=["linux", "macos", "windows"])
|
||||
def platform(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fname", ["pyproject.toml", "cibuildwheel.toml"])
|
||||
def test_simple_settings(tmp_path, platform, fname):
|
||||
with tmp_path.joinpath(fname).open("w") as f:
|
||||
f.write(PYPROJECT_1)
|
||||
|
||||
options = ConfigOptions(tmp_path, f"{{package}}/{fname}", platform=platform)
|
||||
|
||||
assert options("build", env_plat=False, sep=" ") == "cp39*"
|
||||
|
||||
assert options("test-command") == "pyproject"
|
||||
assert options("archs", sep=" ") == "auto"
|
||||
assert (
|
||||
options("test-requires", sep=" ")
|
||||
== {"windows": "something", "macos": "else", "linux": "other many"}[platform]
|
||||
)
|
||||
|
||||
# Also testing options for support for both lists and tables
|
||||
assert (
|
||||
options("environment", table={"item": '{k}="{v}"', "sep": " "}) == 'THING="OTHER" FOO="BAR"'
|
||||
)
|
||||
assert (
|
||||
options("environment", sep="x", table={"item": '{k}="{v}"', "sep": " "})
|
||||
== 'THING="OTHER" FOO="BAR"'
|
||||
)
|
||||
assert options("test-extras", sep=",") == "one,two"
|
||||
assert options("test-extras", sep=",", table={"item": '{k}="{v}"', "sep": " "}) == "one,two"
|
||||
|
||||
assert options("manylinux-x86_64-image") == "manylinux1"
|
||||
assert options("manylinux-i686-image") == "manylinux2010"
|
||||
|
||||
with pytest.raises(ConfigOptionError):
|
||||
options("environment", sep=" ")
|
||||
|
||||
with pytest.raises(ConfigOptionError):
|
||||
options("test-extras", table={"item": '{k}="{v}"', "sep": " "})
|
||||
|
||||
|
||||
def test_envvar_override(tmp_path, platform, monkeypatch):
|
||||
monkeypatch.setenv("CIBW_BUILD", "cp38*")
|
||||
monkeypatch.setenv("CIBW_MANYLINUX_X86_64_IMAGE", "manylinux2014")
|
||||
monkeypatch.setenv("CIBW_TEST_COMMAND", "mytest")
|
||||
monkeypatch.setenv("CIBW_TEST_REQUIRES", "docs")
|
||||
monkeypatch.setenv("CIBW_TEST_REQUIRES_LINUX", "scod")
|
||||
|
||||
with tmp_path.joinpath("pyproject.toml").open("w") as f:
|
||||
f.write(PYPROJECT_1)
|
||||
|
||||
options = ConfigOptions(tmp_path, platform=platform)
|
||||
|
||||
assert options("archs", sep=" ") == "auto"
|
||||
|
||||
assert options("build", sep=" ") == "cp38*"
|
||||
assert options("manylinux-x86_64-image") == "manylinux2014"
|
||||
assert options("manylinux-i686-image") == "manylinux2010"
|
||||
|
||||
assert (
|
||||
options("test-requires", sep=" ")
|
||||
== {"windows": "docs", "macos": "docs", "linux": "scod"}[platform]
|
||||
)
|
||||
assert options("test-command") == "mytest"
|
||||
|
||||
|
||||
def test_project_global_override_default_platform(tmp_path, platform):
|
||||
tmp_path.joinpath("pyproject.toml").write_text(
|
||||
"""
|
||||
[tool.cibuildwheel]
|
||||
repair-wheel-command = "repair-project-global"
|
||||
"""
|
||||
)
|
||||
options = ConfigOptions(tmp_path, platform=platform)
|
||||
assert options("repair-wheel-command") == "repair-project-global"
|
||||
|
||||
|
||||
def test_env_global_override_default_platform(tmp_path, platform, monkeypatch):
|
||||
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "repair-env-global")
|
||||
options = ConfigOptions(tmp_path, platform=platform)
|
||||
assert options("repair-wheel-command") == "repair-env-global"
|
||||
|
||||
|
||||
def test_env_global_override_project_platform(tmp_path, platform, monkeypatch):
|
||||
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "repair-env-global")
|
||||
tmp_path.joinpath("pyproject.toml").write_text(
|
||||
"""
|
||||
[tool.cibuildwheel.linux]
|
||||
repair-wheel-command = "repair-project-linux"
|
||||
[tool.cibuildwheel.windows]
|
||||
repair-wheel-command = "repair-project-windows"
|
||||
[tool.cibuildwheel.macos]
|
||||
repair-wheel-command = "repair-project-macos"
|
||||
"""
|
||||
)
|
||||
options = ConfigOptions(tmp_path, platform=platform)
|
||||
assert options("repair-wheel-command") == "repair-env-global"
|
||||
|
||||
|
||||
def test_global_platform_order(tmp_path, platform):
|
||||
tmp_path.joinpath("pyproject.toml").write_text(
|
||||
"""
|
||||
[tool.cibuildwheel.linux]
|
||||
repair-wheel-command = "repair-project-linux"
|
||||
[tool.cibuildwheel.windows]
|
||||
repair-wheel-command = "repair-project-windows"
|
||||
[tool.cibuildwheel.macos]
|
||||
repair-wheel-command = "repair-project-macos"
|
||||
[tool.cibuildwheel]
|
||||
repair-wheel-command = "repair-project-global"
|
||||
"""
|
||||
)
|
||||
options = ConfigOptions(tmp_path, platform=platform)
|
||||
assert options("repair-wheel-command") == f"repair-project-{platform}"
|
||||
|
||||
|
||||
def test_unexpected_key(tmp_path):
|
||||
# Note that platform contents are only checked when running
|
||||
# for that platform.
|
||||
tmp_path.joinpath("pyproject.toml").write_text(
|
||||
"""
|
||||
[tool.cibuildwheel]
|
||||
repairs-wheel-command = "repair-project-linux"
|
||||
"""
|
||||
)
|
||||
|
||||
with pytest.raises(ConfigOptionError):
|
||||
ConfigOptions(tmp_path, platform="linux")
|
||||
|
||||
|
||||
def test_unexpected_table(tmp_path):
|
||||
tmp_path.joinpath("pyproject.toml").write_text(
|
||||
"""
|
||||
[tool.cibuildwheel.linus]
|
||||
repair-wheel-command = "repair-project-linux"
|
||||
"""
|
||||
)
|
||||
with pytest.raises(ConfigOptionError):
|
||||
ConfigOptions(tmp_path, platform="linux")
|
||||
|
||||
|
||||
def test_unsupported_join(tmp_path):
|
||||
tmp_path.joinpath("pyproject.toml").write_text(
|
||||
"""
|
||||
[tool.cibuildwheel]
|
||||
build = ["1", "2"]
|
||||
"""
|
||||
)
|
||||
options = ConfigOptions(tmp_path, platform="linux")
|
||||
|
||||
assert "1, 2" == options("build", sep=", ")
|
||||
with pytest.raises(ConfigOptionError):
|
||||
options("build")
|
||||
|
||||
|
||||
def test_disallowed_a(tmp_path):
|
||||
tmp_path.joinpath("pyproject.toml").write_text(
|
||||
"""
|
||||
[tool.cibuildwheel.windows]
|
||||
manylinux-x64_86-image = "manylinux1"
|
||||
"""
|
||||
)
|
||||
disallow = {"windows": {"manylinux-x64_86-image"}}
|
||||
ConfigOptions(tmp_path, platform="linux", disallow=disallow)
|
||||
with pytest.raises(ConfigOptionError):
|
||||
ConfigOptions(tmp_path, platform="windows", disallow=disallow)
|
||||
Reference in New Issue
Block a user