|
53 | 53 | from mesonpy._compat import cached_property, read_binary |
54 | 54 |
|
55 | 55 |
|
| 56 | +try: |
| 57 | + from packaging.licenses import InvalidLicenseExpression, canonicalize_license_expression |
| 58 | +except ImportError: |
| 59 | + # PEP-639 support requires packaging >= 24.2. |
| 60 | + def canonicalize_license_expression(s: str) -> str: # type: ignore[misc] |
| 61 | + warnings.warn( |
| 62 | + 'canonicalization and validation of license expression in "project.license" ' |
| 63 | + 'as defined by PEP-639 requires packaging version 24.2 or later.', stacklevel=2) |
| 64 | + return s |
| 65 | + |
| 66 | + class InvalidLicenseExpression(Exception): # type: ignore[no-redef] |
| 67 | + pass |
| 68 | + |
| 69 | + |
56 | 70 | if typing.TYPE_CHECKING: # pragma: no cover |
57 | 71 | from typing import Any, Callable, DefaultDict, Dict, List, Literal, Optional, Sequence, TextIO, Tuple, Type, TypeVar, Union |
58 | 72 |
|
@@ -251,6 +265,10 @@ def from_pyproject( # type: ignore[override] |
251 | 265 | fields = ', '.join(f'"{x}"' for x in unsupported_dynamic) |
252 | 266 | raise pyproject_metadata.ConfigurationError(f'Unsupported dynamic fields: {fields}') |
253 | 267 |
|
| 268 | + # Validate license field to be a valid SDPX license expression. |
| 269 | + if isinstance(metadata.license, str): |
| 270 | + metadata.license = canonicalize_license_expression(metadata.license) |
| 271 | + |
254 | 272 | return metadata |
255 | 273 |
|
256 | 274 | @property |
@@ -339,13 +357,6 @@ def _data_dir(self) -> str: |
339 | 357 | def _libs_dir(self) -> str: |
340 | 358 | return f'.{self._metadata.distribution_name}.mesonpy.libs' |
341 | 359 |
|
342 | | - @property |
343 | | - def _license_file(self) -> Optional[pathlib.Path]: |
344 | | - license_ = self._metadata.license |
345 | | - if license_ and isinstance(license_, pyproject_metadata.License): |
346 | | - return license_.file |
347 | | - return None |
348 | | - |
349 | 360 | @property |
350 | 361 | def wheel(self) -> bytes: |
351 | 362 | """Return WHEEL file for dist-info.""" |
@@ -428,9 +439,17 @@ def _wheel_write_metadata(self, whl: mesonpy._wheelfile.WheelFile) -> None: |
428 | 439 | if self.entrypoints_txt: |
429 | 440 | whl.writestr(f'{self._distinfo_dir}/entry_points.txt', self.entrypoints_txt) |
430 | 441 |
|
431 | | - # add license (see https://github.com/mesonbuild/meson-python/issues/88) |
432 | | - if self._license_file: |
433 | | - whl.write(self._license_file, f'{self._distinfo_dir}/{os.path.basename(self._license_file)}') |
| 442 | + # Add pre-PEP-639 license files. |
| 443 | + if isinstance(self._metadata.license, pyproject_metadata.License): |
| 444 | + license_file = self._metadata.license.file |
| 445 | + if license_file: |
| 446 | + whl.write(license_file, f'{self._distinfo_dir}/{os.path.basename(license_file)}') |
| 447 | + |
| 448 | + # Add PEP-639 license-files. Use ``getattr()`` for compatibility with pyproject-metadata < 0.9.0. |
| 449 | + license_files = getattr(self._metadata, 'license_files', None) |
| 450 | + if license_files: |
| 451 | + for f in license_files: |
| 452 | + whl.write(f, f'{self._distinfo_dir}/licenses/{pathlib.Path(f).as_posix()}') |
434 | 453 |
|
435 | 454 | def build(self, directory: Path) -> pathlib.Path: |
436 | 455 | wheel_file = pathlib.Path(directory, f'{self.name}.whl') |
@@ -1023,7 +1042,7 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: |
1023 | 1042 | warnings.showwarning = _showwarning |
1024 | 1043 | try: |
1025 | 1044 | return func(*args, **kwargs) |
1026 | | - except (Error, pyproject_metadata.ConfigurationError) as exc: |
| 1045 | + except (Error, InvalidLicenseExpression, pyproject_metadata.ConfigurationError) as exc: |
1027 | 1046 | prefix = f'{style.ERROR}meson-python: error:{style.RESET} ' |
1028 | 1047 | _log('\n' + textwrap.indent(str(exc), prefix)) |
1029 | 1048 | raise SystemExit(1) from exc |
|
0 commit comments