feat: support passenv (#914)

* feat: support passenv

* refactor: environment-pass and spaces

* docs: document environment pass

* Add unit test for passing through awkward env variable values

* Remove unused as_shell_commands method from environment

* Fix passthrough edge case by avoiding bash for passthrough assignments

* Fix mypy error in test

* Fix unit test

* Apply suggestions from code review

Co-authored-by: Joe Rickerby <joerick@mac.com>
This commit is contained in:
Henry Schreiner
2021-11-21 15:19:45 -05:00
committed by GitHub
co-authored by Joe Rickerby
parent 1176024473
commit f45de3f24e
8 changed files with 141 additions and 26 deletions
+45 -10
View File
@@ -1,8 +1,10 @@
import dataclasses
from typing import Dict, List, Mapping, Optional
from typing import Any, Dict, List, Mapping, Optional, Sequence
import bashlex
from cibuildwheel.typing import Protocol
from . import bashlex_eval
@@ -39,7 +41,41 @@ def split_env_items(env_string: str) -> List[str]:
return result
class EnvironmentAssignment:
class EnvironmentAssignment(Protocol):
name: str
def evaluated_value(
self,
*,
environment: Dict[str, str],
executor: Optional[bashlex_eval.EnvironmentExecutor] = None,
) -> str:
"""Returns the value of this assignment, as evaluated in the environment"""
...
class EnvironmentAssignmentRaw:
"""
An environment variable - a simple name/value pair
"""
def __init__(self, name: str, value: str):
self.name = name
self.value = value
def __repr__(self) -> str:
return f"{self.name}: {self.value}"
def evaluated_value(self, **kwargs: Any) -> str:
return self.value
class EnvironmentAssignmentBash:
"""
An environment variable, in bash syntax. The value can use bash constructs
like "$OTHER_VAR" and "$(command arg1 arg2)".
"""
def __init__(self, assignment: str):
name, equals, value = assignment.partition("=")
if not equals:
@@ -52,17 +88,13 @@ class EnvironmentAssignment:
environment: Dict[str, str],
executor: Optional[bashlex_eval.EnvironmentExecutor] = None,
) -> str:
"""Returns the value of this assignment, as evaluated in the environment"""
return bashlex_eval.evaluate(self.value, environment=environment, executor=executor)
def as_shell_assignment(self) -> str:
return f"export {self.name}={self.value}"
def __repr__(self) -> str:
return f"{self.name}={self.value}"
def __eq__(self, other: object) -> bool:
if isinstance(other, EnvironmentAssignment):
if isinstance(other, EnvironmentAssignmentBash):
return self.name == other.name and self.value == other.value
return False
@@ -71,6 +103,9 @@ class EnvironmentAssignment:
class ParsedEnvironment:
assignments: List[EnvironmentAssignment]
def __init__(self, assignments: Sequence[EnvironmentAssignment]) -> None:
self.assignments = list(assignments)
def as_dictionary(
self,
prev_environment: Mapping[str, str],
@@ -84,8 +119,8 @@ class ParsedEnvironment:
return environment
def as_shell_commands(self) -> List[str]:
return [a.as_shell_assignment() for a in self.assignments]
def add(self, name: str, value: str) -> None:
self.assignments.append(EnvironmentAssignmentRaw(name=name, value=value))
def __repr__(self) -> str:
return f"{self.__class__.__name__}({[repr(a) for a in self.assignments]!r})"
@@ -93,5 +128,5 @@ class ParsedEnvironment:
def parse_environment(env_string: str) -> ParsedEnvironment:
env_items = split_env_items(env_string)
assignments = [EnvironmentAssignment(item) for item in env_items]
assignments = [EnvironmentAssignmentBash(item) for item in env_items]
return ParsedEnvironment(assignments=assignments)
+9
View File
@@ -408,6 +408,7 @@ class Options:
environment_config = self.reader.get(
"environment", table={"item": '{k}="{v}"', "sep": " "}
)
environment_pass = self.reader.get("environment-pass", sep=" ").split()
before_build = self.reader.get("before-build", sep=" && ")
repair_command = self.reader.get("repair-wheel-command", sep=" && ")
@@ -438,6 +439,14 @@ class Options:
traceback.print_exc(None, sys.stderr)
sys.exit(2)
# Pass through environment variables
if self.platform == "linux":
for env_var_name in environment_pass:
try:
environment.add(env_var_name, os.environ[env_var_name])
except KeyError:
pass
if dependency_versions == "pinned":
dependency_constraints: Optional[
DependencyConstraints
+1
View File
@@ -7,6 +7,7 @@ archs = ["auto"]
build-frontend = "pip"
dependency-versions = "pinned"
environment = {}
environment-pass = []
build-verbosity = ""
before-all = ""