Files
cibuildwheel/cibuildwheel/environment.py
T

145 lines
4.2 KiB
Python
Raw Normal View History

from __future__ import annotations
2021-09-19 00:19:28 -04:00
import dataclasses
2023-04-18 12:38:21 -04:00
from collections.abc import Mapping, Sequence
from typing import Any, Protocol
2019-11-12 23:51:27 +00:00
2021-01-06 13:50:58 -05:00
import bashlex
import bashlex.errors
2021-01-06 13:50:58 -05:00
2017-09-01 13:24:12 +01:00
from . import bashlex_eval
class EnvironmentParseError(Exception):
pass
def split_env_items(env_string: str) -> list[str]:
2021-05-03 11:45:43 -04:00
"""Splits space-separated variable assignments into a list of individual assignments.
2017-09-01 13:24:12 +01:00
>>> split_env_items('VAR=abc')
['VAR=abc']
>>> split_env_items('VAR="a string" THING=3')
['VAR="a string"', 'THING=3']
>>> split_env_items('VAR="a string" THING=\\'single "quotes"\\'')
['VAR="a string"', 'THING=\\'single "quotes"\\'']
>>> split_env_items('VAR="dont \\\\"forget\\\\" about backslashes"')
['VAR="dont \\\\"forget\\\\" about backslashes"']
2017-09-06 18:27:06 +01:00
>>> split_env_items('PATH="$PATH;/opt/cibw_test_path"')
['PATH="$PATH;/opt/cibw_test_path"']
>>> split_env_items('PATH2="something with spaces"')
['PATH2="something with spaces"']
2021-05-03 11:45:43 -04:00
"""
2017-09-06 18:35:42 +01:00
if not env_string:
return []
try:
command_node = bashlex.parsesingle(env_string)
except bashlex.errors.ParsingError as e:
raise EnvironmentParseError(env_string) from e
2017-09-06 18:27:06 +01:00
result = []
for word_node in command_node.parts:
2021-04-30 17:56:34 -04:00
part_string = env_string[word_node.pos[0] : word_node.pos[1]]
2017-09-06 18:27:06 +01:00
result.append(part_string)
return result
2017-09-01 13:24:12 +01:00
2021-11-21 15:19:45 -05:00
class EnvironmentAssignment(Protocol):
name: str
def evaluated_value(
self,
*,
environment: Mapping[str, str],
executor: bashlex_eval.EnvironmentExecutor | None = None,
2021-11-21 15:19:45 -05:00
) -> 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:
2022-11-26 15:54:08 +00:00
return f"{self.name}={self.value}"
2021-11-21 15:19:45 -05:00
def evaluated_value(self, **_: Any) -> str:
2021-11-21 15:19:45 -05:00
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):
2021-05-03 11:45:43 -04:00
name, equals, value = assignment.partition("=")
2017-09-01 13:24:12 +01:00
if not equals:
raise EnvironmentParseError(assignment)
self.name = name
self.value = value
2021-04-30 17:56:34 -04:00
def evaluated_value(
self,
environment: Mapping[str, str],
executor: bashlex_eval.EnvironmentExecutor | None = None,
2021-04-30 17:56:34 -04:00
) -> str:
return bashlex_eval.evaluate(self.value, environment=environment, executor=executor)
2017-09-01 13:24:12 +01:00
def __repr__(self) -> str:
2021-05-03 11:45:43 -04:00
return f"{self.name}={self.value}"
2017-09-01 13:33:29 +01:00
2021-09-19 00:19:28 -04:00
def __eq__(self, other: object) -> bool:
2021-11-21 15:19:45 -05:00
if isinstance(other, EnvironmentAssignmentBash):
2021-09-19 00:19:28 -04:00
return self.name == other.name and self.value == other.value
return False
2017-09-01 13:24:12 +01:00
2021-09-19 00:19:28 -04:00
@dataclasses.dataclass
2020-02-03 20:42:55 +01:00
class ParsedEnvironment:
assignments: list[EnvironmentAssignment]
2017-09-01 13:24:12 +01:00
2021-11-21 15:19:45 -05:00
def __init__(self, assignments: Sequence[EnvironmentAssignment]) -> None:
self.assignments = list(assignments)
2021-04-30 17:56:34 -04:00
def as_dictionary(
self,
prev_environment: Mapping[str, str],
executor: bashlex_eval.EnvironmentExecutor | None = None,
) -> dict[str, str]:
environment = {**prev_environment}
2017-09-01 13:24:12 +01:00
for assignment in self.assignments:
value = assignment.evaluated_value(environment=environment, executor=executor)
2017-09-01 13:24:12 +01:00
environment[assignment.name] = value
return environment
def add(self, name: str, value: str, prepend: bool = False) -> None:
assignment = EnvironmentAssignmentRaw(name=name, value=value)
if prepend:
self.assignments.insert(0, assignment)
else:
self.assignments.append(assignment)
2017-09-01 13:33:29 +01:00
def __repr__(self) -> str:
2021-09-19 00:19:28 -04:00
return f"{self.__class__.__name__}({[repr(a) for a in self.assignments]!r})"
2022-11-26 15:54:08 +00:00
def options_summary(self) -> Any:
return self.assignments
def parse_environment(env_string: str) -> ParsedEnvironment:
env_items = split_env_items(env_string)
2021-11-21 15:19:45 -05:00
assignments = [EnvironmentAssignmentBash(item) for item in env_items]
return ParsedEnvironment(assignments=assignments)