Compare commits

...
7 Commits
Author SHA1 Message Date
Joe Rickerby 225387a9d5 Bump version: v2.10.1 2022-09-18 12:57:10 +01:00
Joe Rickerby ad172699f1 Merge pull request #1273 from pypa/toml-environment-quoting 2022-09-18 10:20:09 +03:00
Joe Rickerby 5dcb363679 Fix typing-extensions requirement 2022-09-17 11:29:39 +01:00
Joe Rickerby 13fc768552 Revert to the previous quoting behaviour for environment option 2022-09-17 11:15:00 +01:00
Joe Rickerby 7b43ed453f Add failing test 2022-09-17 09:58:19 +01:00
Henry Schreiner 00b2600cca chore: cleanup types (#1263)
chore: fix typing Final usage for PyLance

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

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2022-09-16 11:05:05 -04:00
Joe Rickerby 6181943e32 Increase Cirrus CI timeout (#1267)
Gives us a little more flex for the Cirrus build. I've set these to the maximum because we already use pytest-timeout to catch stalled tests, so there's little need to be conservative. MacOS ARM and aarch64 linux are currently running at 5 and 20 minutes respectively, so no need to increase their limits.

See https://github.com/pypa/cibuildwheel/runs/8321188914 for an example timeout on Linux x86
2022-09-13 22:58:09 +02:00
26 changed files with 133 additions and 65 deletions
+2 -1
View File
@@ -6,6 +6,7 @@ run_tests: &RUN_TESTS
linux_x86_task: linux_x86_task:
timeout_in: 120m
compute_engine_instance: compute_engine_instance:
image_project: cirrus-images image_project: cirrus-images
image: family/docker-builder image: family/docker-builder
@@ -33,7 +34,7 @@ linux_aarch64_task:
windows_x86_task: windows_x86_task:
# The task takes ~55 minutes while the timeout happens # The task takes ~55 minutes while the timeout happens
# after 60 minutes by default, let's allow some wiggle room. # after 60 minutes by default, let's allow some wiggle room.
timeout_in: 90m timeout_in: 120m
windows_container: windows_container:
image: cirrusci/windowsservercore:visualstudio2022 image: cirrusci/windowsservercore:visualstudio2022
cpu: 8 cpu: 8
+7 -10
View File
@@ -92,7 +92,7 @@ jobs:
- uses: actions/setup-python@v3 - uses: actions/setup-python@v3
- name: Install cibuildwheel - name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.10.0 run: python -m pip install cibuildwheel==2.10.1
- name: Build wheels - name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse run: python -m cibuildwheel --output-dir wheelhouse
@@ -208,6 +208,12 @@ Changelog
<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually --> <!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
### v2.10.1
_18 September 2022_
- 🐛 Fix a bug that stopped environment variables specified in TOML from being expanded. (#1273)
### v2.10.0 ### v2.10.0
_13 September 2022_ _13 September 2022_
@@ -251,15 +257,6 @@ _5 July 2022_
- 🛠 cibuildwheel will now error if multiple builds in a single run produce the same wheel filename, as this indicates a misconfiguration. (#1152) - 🛠 cibuildwheel will now error if multiple builds in a single run produce the same wheel filename, as this indicates a misconfiguration. (#1152)
- 📚 A few docs improvements and updates to keep things up-to-date. - 📚 A few docs improvements and updates to keep things up-to-date.
### v2.7.0
_17 June 2022_
- 🌟 Added support for the new `manylinux_2_28` images. These new images are based on AlmaLinux, the community-driven successor to CentOS, unlike manylinux_2_24, which was based on Debian. To build on these images, set your [`CIBW_MANYLINUX_*_IMAGE`](https://cibuildwheel.readthedocs.io/en/stable/options/#linux-image) option to `manylinux_2_28`. (#1026)
- 🐛 Fix a bug where tests were not being run on CPython 3.11 (when CIBW_PRERELEASE_PYTHONS was set) (#1138)
- ✨ You can now build Linux wheels on Windows, as long as you have Docker installed and set to 'Linux containers' (#1117)
- 🐛 Fix a bug on macOS that caused cibuildwheel to crash trying to overwrite a previously-built wheel of the same name. (#1129)
<!-- END bin/update_readme_changelog.py --> <!-- END bin/update_readme_changelog.py -->
--- ---
+1 -1
View File
@@ -1,3 +1,3 @@
from __future__ import annotations from __future__ import annotations
__version__ = "2.10.0" __version__ = "2.10.1"
+6 -1
View File
@@ -4,6 +4,7 @@ import dataclasses
from typing import Any, Mapping, Sequence from typing import Any, Mapping, Sequence
import bashlex import bashlex
import bashlex.errors
from cibuildwheel.typing import Protocol from cibuildwheel.typing import Protocol
@@ -33,7 +34,11 @@ def split_env_items(env_string: str) -> list[str]:
if not env_string: if not env_string:
return [] return []
command_node = bashlex.parsesingle(env_string) try:
command_node = bashlex.parsesingle(env_string)
except bashlex.errors.ParsingError as e:
raise EnvironmentParseError(env_string) from e
result = [] result = []
for word_node in command_node.parts: for word_node in command_node.parts:
+13 -10
View File
@@ -10,7 +10,7 @@ from configparser import ConfigParser
from contextlib import contextmanager from contextlib import contextmanager
from dataclasses import asdict, dataclass from dataclasses import asdict, dataclass
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Generator, Iterator, List, Mapping, Union, cast from typing import Any, Callable, Dict, Generator, Iterator, List, Mapping, Union, cast
if sys.version_info >= (3, 11): if sys.version_info >= (3, 11):
import tomllib import tomllib
@@ -23,7 +23,7 @@ from .architecture import Architecture
from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment
from .oci_container import ContainerEngine from .oci_container import ContainerEngine
from .projectfiles import get_requires_python_str from .projectfiles import get_requires_python_str
from .typing import PLATFORMS, Literal, PlatformName, TypedDict from .typing import PLATFORMS, Literal, NotRequired, PlatformName, TypedDict
from .util import ( from .util import (
MANYLINUX_ARCHS, MANYLINUX_ARCHS,
MUSLLINUX_ARCHS, MUSLLINUX_ARCHS,
@@ -123,6 +123,7 @@ DISALLOWED_OPTIONS = {
class TableFmt(TypedDict): class TableFmt(TypedDict):
item: str item: str
sep: str sep: str
quote: NotRequired[Callable[[str], str]]
class ConfigOptionError(KeyError): class ConfigOptionError(KeyError):
@@ -329,7 +330,7 @@ class OptionsReader:
if table is None: if table is None:
raise ConfigOptionError(f"{name!r} does not accept a table") raise ConfigOptionError(f"{name!r} does not accept a table")
return table["sep"].join( return table["sep"].join(
item for k, v in result.items() for item in _inner_fmt(k, v, table["item"]) item for k, v in result.items() for item in _inner_fmt(k, v, table)
) )
if isinstance(result, list): if isinstance(result, list):
@@ -343,14 +344,16 @@ class OptionsReader:
return result return result
def _inner_fmt(k: str, v: Any, table_item: str) -> Iterator[str]: def _inner_fmt(k: str, v: Any, table: TableFmt) -> Iterator[str]:
quote_function = table.get("quote", lambda a: a)
if isinstance(v, list): if isinstance(v, list):
for inner_v in v: for inner_v in v:
qv = shlex.quote(inner_v) qv = quote_function(inner_v)
yield table_item.format(k=k, v=qv) yield table["item"].format(k=k, v=qv)
else: else:
qv = shlex.quote(v) qv = quote_function(v)
yield table_item.format(k=k, v=qv) yield table["item"].format(k=k, v=qv)
class Options: class Options:
@@ -449,13 +452,13 @@ class Options:
build_frontend_str = self.reader.get("build-frontend", env_plat=False) build_frontend_str = self.reader.get("build-frontend", env_plat=False)
environment_config = self.reader.get( environment_config = self.reader.get(
"environment", table={"item": "{k}={v}", "sep": " "} "environment", table={"item": '{k}="{v}"', "sep": " "}
) )
environment_pass = self.reader.get("environment-pass", sep=" ").split() environment_pass = self.reader.get("environment-pass", sep=" ").split()
before_build = self.reader.get("before-build", sep=" && ") before_build = self.reader.get("before-build", sep=" && ")
repair_command = self.reader.get("repair-wheel-command", sep=" && ") repair_command = self.reader.get("repair-wheel-command", sep=" && ")
config_settings = self.reader.get( config_settings = self.reader.get(
"config-settings", table={"item": "{k}={v}", "sep": " "} "config-settings", table={"item": "{k}={v}", "sep": " ", "quote": shlex.quote}
) )
dependency_versions = self.reader.get("dependency-versions") dependency_versions = self.reader.get("dependency-versions")
+5
View File
@@ -10,6 +10,10 @@ if sys.version_info < (3, 8):
else: else:
from typing import Final, Literal, OrderedDict, Protocol, TypedDict from typing import Final, Literal, OrderedDict, Protocol, TypedDict
if sys.version_info < (3, 11):
from typing_extensions import NotRequired
else:
from typing import NotRequired
__all__ = ( __all__ = (
"Final", "Final",
@@ -26,6 +30,7 @@ __all__ = (
"OrderedDict", "OrderedDict",
"Union", "Union",
"assert_never", "assert_never",
"NotRequired",
) )
+9 -7
View File
@@ -63,13 +63,13 @@ __all__ = [
"split_config_settings", "split_config_settings",
] ]
resources_dir: Final = Path(__file__).parent / "resources" resources_dir: Final[Path] = Path(__file__).parent / "resources"
install_certifi_script: Final = resources_dir / "install_certifi.py" install_certifi_script: Final[Path] = resources_dir / "install_certifi.py"
BuildFrontend = Literal["pip", "build"] BuildFrontend = Literal["pip", "build"]
MANYLINUX_ARCHS: Final = ( MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64", "x86_64",
"i686", "i686",
"pypy_x86_64", "pypy_x86_64",
@@ -80,7 +80,7 @@ MANYLINUX_ARCHS: Final = (
"pypy_i686", "pypy_i686",
) )
MUSLLINUX_ARCHS: Final = ( MUSLLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64", "x86_64",
"i686", "i686",
"aarch64", "aarch64",
@@ -88,10 +88,12 @@ MUSLLINUX_ARCHS: Final = (
"s390x", "s390x",
) )
DEFAULT_CIBW_CACHE_PATH: Final = user_cache_path(appname="cibuildwheel", appauthor="pypa") DEFAULT_CIBW_CACHE_PATH: Final[Path] = user_cache_path(appname="cibuildwheel", appauthor="pypa")
CIBW_CACHE_PATH: Final = Path(os.environ.get("CIBW_CACHE_PATH", DEFAULT_CIBW_CACHE_PATH)).resolve() CIBW_CACHE_PATH: Final[Path] = Path(
os.environ.get("CIBW_CACHE_PATH", DEFAULT_CIBW_CACHE_PATH)
).resolve()
IS_WIN: Final = sys.platform.startswith("win") IS_WIN: Final[bool] = sys.platform.startswith("win")
@overload @overload
+6
View File
@@ -4,6 +4,12 @@ title: Changelog
# Changelog # Changelog
### v2.10.1
_18 September 2022_
- 🐛 Fix a bug that stopped environment variables specified in TOML from being expanded. (#1273)
### v2.10.0 ### v2.10.0
_13 September 2022_ _13 September 2022_
+2 -2
View File
@@ -142,7 +142,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
If you use GitHub Actions for builds, you can use cibuildwheel as an action: If you use GitHub Actions for builds, you can use cibuildwheel as an action:
```yaml ```yaml
uses: pypa/cibuildwheel@v2.10.0 uses: pypa/cibuildwheel@v2.10.1
``` ```
This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal. This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -164,7 +164,7 @@ The second option, and the only one that supports other CI systems, is using a `
```bash ```bash
# requirements-cibw.txt # requirements-cibw.txt
cibuildwheel==2.10.0 cibuildwheel==2.10.1
``` ```
Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this: Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
+2 -2
View File
@@ -184,7 +184,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build wheels - name: Build wheels
run: pipx run cibuildwheel==2.10.0 run: pipx run cibuildwheel==2.10.1
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
with: with:
@@ -219,7 +219,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
- uses: actions/setup-python@v3 - uses: actions/setup-python@v3
- name: Install cibuildwheel - name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.10.0 run: python -m pip install cibuildwheel==2.10.1
- name: Build wheels - name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse run: python -m cibuildwheel --output-dir wheelhouse
+1 -1
View File
@@ -12,7 +12,7 @@ stack: python 3.7
init: init:
- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH% - cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
install: python -m pip install cibuildwheel==2.10.0 install: python -m pip install cibuildwheel==2.10.1
build_script: python -m cibuildwheel --output-dir wheelhouse build_script: python -m cibuildwheel --output-dir wheelhouse
+3 -3
View File
@@ -6,7 +6,7 @@ jobs:
- bash: | - bash: |
set -o errexit set -o errexit
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
pip3 install cibuildwheel==2.10.0 pip3 install cibuildwheel==2.10.1
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
@@ -20,7 +20,7 @@ jobs:
- bash: | - bash: |
set -o errexit set -o errexit
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
python3 -m pip install cibuildwheel==2.10.0 python3 -m pip install cibuildwheel==2.10.1
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
- bash: | - bash: |
set -o errexit set -o errexit
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install cibuildwheel==2.10.0 pip install cibuildwheel==2.10.1
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
+2 -2
View File
@@ -11,7 +11,7 @@ jobs:
- run: - run:
name: Build the Linux wheels. name: Build the Linux wheels.
command: | command: |
pip3 install --user cibuildwheel==2.10.0 pip3 install --user cibuildwheel==2.10.1
cibuildwheel --output-dir wheelhouse cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
@@ -25,7 +25,7 @@ jobs:
- run: - run:
name: Build the OS X wheels. name: Build the OS X wheels.
command: | command: |
pip3 install cibuildwheel==2.10.0 pip3 install cibuildwheel==2.10.1
cibuildwheel --output-dir wheelhouse cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
+1 -1
View File
@@ -1,6 +1,6 @@
build_and_store_wheels: &BUILD_AND_STORE_WHEELS build_and_store_wheels: &BUILD_AND_STORE_WHEELS
install_cibuildwheel_script: install_cibuildwheel_script:
- python -m pip install cibuildwheel==2.10.0 - python -m pip install cibuildwheel==2.10.1
run_cibuildwheel_script: run_cibuildwheel_script:
- cibuildwheel - cibuildwheel
wheels_artifacts: wheels_artifacts:
+1 -1
View File
@@ -1,6 +1,6 @@
build_and_store_wheels: &BUILD_AND_STORE_WHEELS build_and_store_wheels: &BUILD_AND_STORE_WHEELS
install_cibuildwheel_script: install_cibuildwheel_script:
- python -m pip install cibuildwheel==2.10.0 - python -m pip install cibuildwheel==2.10.1
run_cibuildwheel_script: run_cibuildwheel_script:
- cibuildwheel - cibuildwheel
wheels_artifacts: wheels_artifacts:
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.10.0 uses: pypa/cibuildwheel@v2.10.1
env: env:
CIBW_ARCHS_MACOS: x86_64 arm64 CIBW_ARCHS_MACOS: x86_64 arm64
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.10.0 uses: pypa/cibuildwheel@v2.10.1
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
with: with:
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.10.0 uses: pypa/cibuildwheel@v2.10.1
# env: # env:
# CIBW_SOME_OPTION: value # CIBW_SOME_OPTION: value
# ... # ...
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
platforms: all platforms: all
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.10.0 uses: pypa/cibuildwheel@v2.10.1
env: env:
# configure cibuildwheel to build native archs ('auto'), and some # configure cibuildwheel to build native archs ('auto'), and some
# emulated ones # emulated ones
+1 -1
View File
@@ -12,7 +12,7 @@ linux:
DOCKER_TLS_CERTDIR: "" DOCKER_TLS_CERTDIR: ""
script: script:
- curl -sSL https://get.docker.com/ | sh - curl -sSL https://get.docker.com/ | sh
- python -m pip install cibuildwheel==2.10.0 - python -m pip install cibuildwheel==2.10.1
- cibuildwheel --output-dir wheelhouse - cibuildwheel --output-dir wheelhouse
artifacts: artifacts:
paths: paths:
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- ln -s /c/Python38/python.exe /c/Python38/python3.exe - ln -s /c/Python38/python.exe /c/Python38/python3.exe
install: install:
- python3 -m pip install cibuildwheel==2.10.0 - python3 -m pip install cibuildwheel==2.10.1
script: script:
# build the wheels, put them into './dist' # build the wheels, put them into './dist'
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
- ln -s /c/Python38/python.exe /c/Python38/python3.exe - ln -s /c/Python38/python.exe /c/Python38/python3.exe
install: install:
- python3 -m pip install cibuildwheel==2.10.0 - python3 -m pip install cibuildwheel==2.10.1
script: script:
# build the wheels, put them into './wheelhouse' # build the wheels, put them into './wheelhouse'
+2 -2
View File
@@ -54,7 +54,7 @@ jobs:
- stage: deploy - stage: deploy
name: Build and deploy Linux wheels name: Build and deploy Linux wheels
services: docker services: docker
install: python3 -m pip install cibuildwheel==2.10.0 twine install: python3 -m pip install cibuildwheel==2.10.1 twine
script: python3 -m cibuildwheel --output-dir wheelhouse script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
# Deploy on windows # Deploy on windows
@@ -62,7 +62,7 @@ jobs:
name: Build and deploy Windows wheels name: Build and deploy Windows wheels
os: windows os: windows
language: shell language: shell
install: python3 -m pip install cibuildwheel==2.10.0 twine install: python3 -m pip install cibuildwheel==2.10.1 twine
script: python3 -m cibuildwheel --output-dir wheelhouse script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
+1
View File
@@ -66,6 +66,7 @@ module = [
"setuptools", "setuptools",
"pytest", # ignored in pre-commit to speed up check "pytest", # ignored in pre-commit to speed up check
"bashlex", "bashlex",
"bashlex.*",
"importlib_resources", "importlib_resources",
"ghapi.*", "ghapi.*",
] ]
+2 -2
View File
@@ -1,6 +1,6 @@
[metadata] [metadata]
name = cibuildwheel name = cibuildwheel
version = 2.10.0 version = 2.10.1
description = Build Python wheels on CI with minimal configuration. description = Build Python wheels on CI with minimal configuration.
long_description = file: README.md long_description = file: README.md
long_description_content_type = text/markdown long_description_content_type = text/markdown
@@ -37,7 +37,7 @@ install_requires =
packaging>=20.9 packaging>=20.9
platformdirs platformdirs
tomli;python_version < '3.11' tomli;python_version < '3.11'
typing-extensions>=3.10.0.0;python_version < '3.8' typing-extensions>=4.1.0;python_version < '3.11'
python_requires = >=3.7 python_requires = >=3.7
include_package_data = True include_package_data = True
zip_safe = False zip_safe = False
+60 -12
View File
@@ -1,11 +1,14 @@
from __future__ import annotations from __future__ import annotations
import os
import platform as platform_module import platform as platform_module
import textwrap import textwrap
from pathlib import Path
import pytest import pytest
from cibuildwheel.__main__ import get_build_identifiers from cibuildwheel.__main__ import get_build_identifiers
from cibuildwheel.bashlex_eval import local_environment_executor
from cibuildwheel.environment import parse_environment from cibuildwheel.environment import parse_environment
from cibuildwheel.options import Options, _get_pinned_container_images from cibuildwheel.options import Options, _get_pinned_container_images
@@ -59,7 +62,7 @@ test_command: 'pyproject'
default_build_options = options.build_options(identifier=None) default_build_options = options.build_options(identifier=None)
assert default_build_options.environment == parse_environment("FOO=BAR") assert default_build_options.environment == parse_environment('FOO="BAR"')
all_pinned_container_images = _get_pinned_container_images() all_pinned_container_images = _get_pinned_container_images()
pinned_x86_64_container_image = all_pinned_container_images["x86_64"] pinned_x86_64_container_image = all_pinned_container_images["x86_64"]
@@ -119,30 +122,75 @@ def test_passthrough_evil(tmp_path, monkeypatch, env_var_value):
assert parsed_environment.as_dictionary(prev_environment={}) == {"ENV_VAR": env_var_value} assert parsed_environment.as_dictionary(prev_environment={}) == {"ENV_VAR": env_var_value}
xfail_env_parse = pytest.mark.xfail(
raises=SystemExit, reason="until we can figure out the right way to quote these values"
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"env_var_value", "env_var_value",
[ [
"normal value", "normal value",
'"value wrapped in quotes"', pytest.param('"value wrapped in quotes"', marks=[xfail_env_parse]),
'an unclosed double-quote: "', pytest.param('an unclosed double-quote: "', marks=[xfail_env_parse]),
"string\nwith\ncarriage\nreturns\n", "string\nwith\ncarriage\nreturns\n",
"a trailing backslash \\", pytest.param("a trailing backslash \\", marks=[xfail_env_parse]),
], ],
) )
def test_toml_environment_evil(tmp_path, monkeypatch, env_var_value): def test_toml_environment_evil(tmp_path, monkeypatch, env_var_value):
args = get_default_command_line_arguments() args = get_default_command_line_arguments()
args.package_dir = tmp_path args.package_dir = tmp_path
with tmp_path.joinpath("pyproject.toml").open("w") as f: tmp_path.joinpath("pyproject.toml").write_text(
f.write( textwrap.dedent(
textwrap.dedent( f"""\
f"""\ [tool.cibuildwheel.environment]
[tool.cibuildwheel.environment] EXAMPLE='''{env_var_value}'''
EXAMPLE='''{env_var_value}''' """
"""
)
) )
)
options = Options(platform="linux", command_line_arguments=args) options = Options(platform="linux", command_line_arguments=args)
parsed_environment = options.build_options(identifier=None).environment parsed_environment = options.build_options(identifier=None).environment
assert parsed_environment.as_dictionary(prev_environment={}) == {"EXAMPLE": env_var_value} assert parsed_environment.as_dictionary(prev_environment={}) == {"EXAMPLE": env_var_value}
@pytest.mark.parametrize(
"toml_assignment,result_value",
[
('TEST_VAR="simple_value"', "simple_value"),
# spaces
('TEST_VAR="simple value"', "simple value"),
# env var
('TEST_VAR="$PARAM"', "spam"),
('TEST_VAR="$PARAM $PARAM"', "spam spam"),
# env var extension
('TEST_VAR="before:$PARAM:after"', "before:spam:after"),
# env var extension with spaces
('TEST_VAR="before $PARAM after"', "before spam after"),
# literal $ - this test is just for reference, I'm not sure if this
# syntax will work if we change the TOML quoting behaviour
(r'TEST_VAR="before\\$after"', "before$after"),
],
)
def test_toml_environment_quoting(tmp_path: Path, toml_assignment, result_value):
args = get_default_command_line_arguments()
args.package_dir = tmp_path
tmp_path.joinpath("pyproject.toml").write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel.environment]
{toml_assignment}
"""
)
)
options = Options(platform="linux", command_line_arguments=args)
parsed_environment = options.build_options(identifier=None).environment
environment_values = parsed_environment.as_dictionary(
prev_environment={**os.environ, "PARAM": "spam"},
executor=local_environment_executor,
)
assert environment_values["TEST_VAR"] == result_value