Skip to content

Commit e0c71f7

Browse files
FFY00mgorny
authored andcommitted
python: validate python.build_config version and expand relative paths
Signed-off-by: Filipe Laíns <[email protected]>
1 parent 79a78a0 commit e0c71f7

File tree

1 file changed

+89
-9
lines changed

1 file changed

+89
-9
lines changed

mesonbuild/dependencies/python.py

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from __future__ import annotations
55

6-
import functools, json, os, textwrap
6+
import functools, json, os, sys, textwrap
77
from pathlib import Path
88
import typing as T
99

@@ -74,6 +74,93 @@ def __init__(self, name: str, environment: Environment, kwargs: T.Dict[str, T.An
7474
self.compile_args = self.get_config_value(['--cflags'], 'compile_args')
7575

7676

77+
class PythonBuildConfig:
78+
"""PEP 739 build-details.json config file."""
79+
80+
"""Schema version currently implemented."""
81+
IMPLEMENTED_VERSION: T.Final[str] = '1.0'
82+
"""Path keys — may be relative, need to be expanded."""
83+
_PATH_KEYS = (
84+
'base_interpreter',
85+
'libpython.dynamic',
86+
'libpython.dynamic_stableabi',
87+
'libpython.static',
88+
'c_api.headers',
89+
'c_api.pkgconfig_path',
90+
)
91+
92+
def __init__(self, path: str) -> None:
93+
self._path = Path(path)
94+
95+
try:
96+
self._data = json.loads(self._path.read_text(encoding='utf8'))
97+
except OSError as e:
98+
raise DependencyException(f'Failed to read python.build_config: {e}') from e
99+
100+
self._validate_data()
101+
self._expand_paths()
102+
103+
def __getitem__(self, key: str) -> T.Any:
104+
value = self._data
105+
for part in key.split('.'):
106+
value = value[part]
107+
return value
108+
109+
def __contains__(self, key: str) -> bool:
110+
try:
111+
self[key]
112+
except KeyError:
113+
return False
114+
else:
115+
return True
116+
117+
def get(self, key: str, default: T.Any = None) -> T.Any:
118+
try:
119+
self[key]
120+
except KeyError:
121+
return default
122+
123+
def _validate_data(self) -> None:
124+
schema_version = self._data['schema_version']
125+
if mesonlib.version_compare(schema_version, '< 1.0'):
126+
raise DependencyException(f'Invalid schema_version in python.build_config: {schema_version}')
127+
if mesonlib.version_compare(schema_version, '>= 2.0'):
128+
raise DependencyException(
129+
f'Unsupported schema_version {schema_version!r} in python.build_config, '
130+
f'but we only implement support for {self.IMPLEMENTED_VERSION!r}'
131+
)
132+
# Schema version that we currently understand
133+
if mesonlib.version_compare(schema_version, f'> {self.IMPLEMENTED_VERSION}'):
134+
mlog.log(
135+
f'python.build_config has schema_version {schema_version!r}, '
136+
f'but we only implement support for {self.IMPLEMENTED_VERSION!r}, '
137+
'new functionality might be missing'
138+
)
139+
140+
def _expand_paths(self) -> None:
141+
"""Expand relative path (they're relative to base_prefix)."""
142+
for key in self._PATH_KEYS:
143+
if key not in self:
144+
continue
145+
parent, _, child = key.rpartition('.')
146+
container = self[parent] if parent else self._data
147+
path = Path(container[child])
148+
if not path.is_absolute():
149+
container[child] = os.fspath(self.base_prefix / path)
150+
151+
@property
152+
def config_path(self) -> Path:
153+
return self._path
154+
155+
@mesonlib.lazy_property
156+
def base_prefix(self) -> Path:
157+
path = Path(self._data['base_prefix'])
158+
if path.is_absolute():
159+
return path
160+
# Non-absolute paths are relative to the build config directory
161+
return self.config_path.parent / path
162+
163+
77164
class BasicPythonExternalProgram(ExternalProgram):
78165
def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
79166
ext_prog: T.Optional[ExternalProgram] = None,
@@ -87,14 +174,7 @@ def __init__(self, name: str, command: T.Optional[T.List[str]] = None,
87174
self.cached_version = None
88175
self.version_arg = '--version'
89176

90-
self.build_config = None
91-
92-
if build_config_path:
93-
try:
94-
with open(build_config_path, encoding='utf8') as f:
95-
self.build_config = json.load(f)
96-
except OSError as e:
97-
raise DependencyException(f'Failed to read python.build_config: {e}') from e
177+
self.build_config = PythonBuildConfig(build_config_path) if build_config_path else None
98178

99179
# We want strong key values, so we always populate this with bogus data.
100180
# Otherwise to make the type checkers happy we'd have to do .get() for

0 commit comments

Comments
 (0)