1import os
2from pathlib import Path
3from typing import Any, Mapping, Optional, Union
4
5from pep517.wrappers import HookMissing
6
7from pdm.builders.base import EnvBuilder
8from pdm.models.environment import Environment
9
10
11class EditableBuilder(EnvBuilder):
12    """Build egg-info in isolated env with managed Python."""
13
14    FALLBACK_BACKEND = {
15        "build-backend": "setuptools_pep660",
16        "requires": ["setuptools_pep660"],
17    }
18
19    def __init__(self, src_dir: Union[str, Path], environment: Environment) -> None:
20        super().__init__(src_dir, environment)
21        if self._hook.build_backend.startswith(
22            "pdm.pep517"
23        ) and environment.interpreter.version_tuple < (3, 6):
24            # pdm.pep517 backend is not available on Python 2, use the fallback backend
25            self.init_build_system(self.FALLBACK_BACKEND)
26
27    def prepare_metadata(
28        self, out_dir: str, config_settings: Optional[Mapping[str, Any]] = None
29    ) -> str:
30        self.install(self._requires, shared=True)
31        try:
32            requires = self._hook.get_requires_for_build_editable(config_settings)
33            self.install(requires)
34            filename = self._hook.prepare_metadata_for_build_editable(
35                out_dir, config_settings
36            )
37        except HookMissing:
38            self.init_build_system(self.FALLBACK_BACKEND)
39            return self.prepare_metadata(out_dir, config_settings)
40        return os.path.join(out_dir, filename)
41
42    def build(
43        self,
44        out_dir: str,
45        config_settings: Optional[Mapping[str, Any]] = None,
46        metadata_directory: Optional[str] = None,
47    ) -> str:
48        self.install(self._requires, shared=True)
49        try:
50            requires = self._hook.get_requires_for_build_editable(config_settings)
51            self.install(requires)
52            filename = self._hook.build_editable(
53                out_dir, config_settings, metadata_directory
54            )
55        except HookMissing:
56            self.init_build_system(self.FALLBACK_BACKEND)
57            return self.build(out_dir, config_settings, metadata_directory)
58        return os.path.join(out_dir, filename)
59