Skip to content

Commit

Permalink
feat: adding overrides
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <[email protected]>
  • Loading branch information
henryiii committed Sep 28, 2023
1 parent 236faa2 commit ec560d3
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ repos:
rev: 1.16.0
hooks:
- id: blacken-docs
additional_dependencies: [black==23.3.0]
additional_dependencies: [black==23.9.1]

- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13
Expand Down
86 changes: 86 additions & 0 deletions src/scikit_build_core/settings/skbuild_read_settings.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import annotations

import copy
import difflib
import re
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Any

from packaging.specifiers import SpecifierSet
from packaging.version import Version

from .. import __version__
Expand All @@ -25,6 +28,73 @@ def __dir__() -> list[str]:
return __all__


def version_match(value: str, match: str, name: str) -> str:
"""
Returns a non-empty string if a version matches a specifier.
"""
matcher = SpecifierSet(match)
did_match = matcher.contains(value)
return f"{match!r} matched {name} {value}" if did_match else ""


def regex_match(value: str, match: str) -> str:
"""
Returns a non-empty string if a value matches a regex.
"""
did_match = re.compile(match).match(value) is not None
return f"{match!r} matched {value}" if did_match else ""


def override_match(
python_version: str | None = None,
implementation_name: str | None = None,
implementation_version: str | None = None,
platform_system: str | None = None,
platform_machine: str | None = None,
) -> bool:
matches = []

if python_version is not None:
current_python_version = ".".join(str(x) for x in sys.version_info[:2])
match_msg = version_match(current_python_version, python_version, "Python")
matches.append(match_msg)

if implementation_name is not None:
current_impementation_name = sys.implementation.name
match_msg = regex_match(current_impementation_name, implementation_name)
matches.append(match_msg)

if implementation_version is not None:
current_implementation_version = ".".join(
str(x) for x in sys.implementation.version
)
match_msg = version_match(
current_implementation_version,
implementation_version,
"Python implementation",
)
matches.append(match_msg)

if platform_system is not None:
current_platform_system = sys.platform
match_msg = regex_match(current_platform_system, platform_system)
matches.append(match_msg)

if platform_machine is not None:
current_platform_machine = sys.platform
match_msg = regex_match(current_platform_machine, platform_machine)
matches.append(match_msg)

if not matches:
msg = "At least one override must be provided"
raise ValueError(msg)

matched = all(matches)
if matched:
logger.info("Overrides {}", " and ".join(matches))
return matched


class SettingsReader:
def __init__(
self,
Expand All @@ -33,6 +103,22 @@ def __init__(
*,
verify_conf: bool = True,
) -> None:
pyproject = copy.deepcopy(pyproject)

# Process overrides into the main dictionary if they match
tool_skb = pyproject.get("tool", {}).get("scikit-build", {})
for override in tool_skb.pop("overrides", []):
select = {k.replace("-", "_"): v for k, v in override.pop("select").items()}
if override_match(**select):
for key, value in override.items():
if isinstance(value, dict):
for key2, value2 in value.items():
inner = tool_skb.get(key, {})
inner[key2] = value2
tool_skb[key] = inner
else:
tool_skb[key] = value

self.sources = SourceChain(
EnvSource("SKBUILD"),
ConfSource(settings=config_settings, verify=verify_conf),
Expand Down
78 changes: 78 additions & 0 deletions tests/test_settings_overrides.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from pathlib import Path
from textwrap import dedent

import pytest

from scikit_build_core.settings.skbuild_read_settings import SettingsReader


@pytest.mark.parametrize("python_version", ["3.9", "3.10"])
def test_skbuild_overrides_pyver(
python_version: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
):
monkeypatch.setattr("sys.version_info", (*map(int, python_version.split(".")), 0))
pyproject_toml = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
dedent(
"""\
[[tool.scikit-build.overrides]]
select = {python-version = ">=3.10"}
cmake.args = ["-DFOO=BAR"]
experimental = true
cmake.define.SPAM = "EGGS"
sdist.cmake = true
"""
),
encoding="utf-8",
)

settings_reader = SettingsReader.from_file(pyproject_toml, {})
settings = settings_reader.settings

if python_version == "3.10":
assert settings.cmake.args == ["-DFOO=BAR"]
assert settings.cmake.define == {"SPAM": "EGGS"}
assert settings.experimental
assert settings.sdist.cmake
else:
assert not settings.cmake.args
assert not settings.cmake.define
assert not settings.experimental
assert not settings.sdist.cmake


@pytest.mark.parametrize("implementation_name", ["cpython", "pypy"])
@pytest.mark.parametrize("platform_system", ["darwin", "linux"])
def test_skbuild_overrides_dual(
implementation_name: str,
platform_system: str,
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
):
monkeypatch.setattr(
"sys.implementation", type("Mock", (), {"name": implementation_name})
)
monkeypatch.setattr("sys.platform", platform_system)

pyproject_toml = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
dedent(
"""\
[[tool.scikit-build.overrides]]
select = {implementation-name = "pypy", platform-system = "darwin"}
editable.verbose = false
install.components = ["headers"]
"""
),
encoding="utf-8",
)

settings_reader = SettingsReader.from_file(pyproject_toml, {})
settings = settings_reader.settings

if implementation_name == "pypy" and platform_system == "darwin":
assert not settings.editable.verbose
assert settings.install.components == ["headers"]
else:
assert settings.editable.verbose
assert not settings.install.components

0 comments on commit ec560d3

Please sign in to comment.