|
1 | 1 | """Generates Dockerfiles based on an input matrix based on Python.""" |
2 | 2 |
|
3 | 3 | import os |
| 4 | +import re |
4 | 5 | from functools import lru_cache |
5 | 6 |
|
| 7 | +try: |
| 8 | + import tomllib |
| 9 | +except ImportError: |
| 10 | + import tomli as tomllib |
| 11 | + |
6 | 12 | from ...utils import is_local_pip_requirement, open_guess_encoding |
7 | 13 | from ..conda import CondaBuildPack |
8 | 14 |
|
@@ -34,6 +40,37 @@ def python_version(self): |
34 | 40 | # get major.minor |
35 | 41 | py_version = ".".join(py_version_info[:2]) |
36 | 42 | self._python_version = py_version |
| 43 | + |
| 44 | + pyproject_toml = "pyproject.toml" |
| 45 | + if not self.binder_dir and os.path.exists(pyproject_toml): |
| 46 | + with open(pyproject_toml, "rb") as _pyproject_file: |
| 47 | + pyproject = tomllib.load(_pyproject_file) |
| 48 | + |
| 49 | + if "project" in pyproject: |
| 50 | + if "requires-python" in pyproject["project"]: |
| 51 | + # This is the minumum version! |
| 52 | + raw_pyproject_minimum_version = pyproject["project"][ |
| 53 | + "requires-python" |
| 54 | + ] |
| 55 | + |
| 56 | + match = re.compile(r"\d+(\.\d+)*").match(raw_pyproject_minimum_version) |
| 57 | + if match: |
| 58 | + pyproject_minimum_version = match.group() |
| 59 | + pyproject_minimum_version_info = pyproject_minimum_version.split( |
| 60 | + "." |
| 61 | + ) |
| 62 | + |
| 63 | + if ( |
| 64 | + py_version_info[0] |
| 65 | + < pyproject_minimum_version_info[0] |
| 66 | + ) or ( |
| 67 | + py_version_info[1] |
| 68 | + < pyproject_minimum_version_info[1] |
| 69 | + ): |
| 70 | + raise RuntimeError( |
| 71 | + "runtime.txt version not supported by pyproject.toml." |
| 72 | + ) |
| 73 | + |
37 | 74 | return self._python_version |
38 | 75 |
|
39 | 76 | def _get_pip_scripts(self): |
@@ -78,9 +115,11 @@ def _should_preassemble_pip(self): |
78 | 115 | If there are any local references, e.g. `-e .`, |
79 | 116 | stage the whole repo prior to installation. |
80 | 117 | """ |
81 | | - if not os.path.exists("binder") and os.path.exists("setup.py"): |
82 | | - # can't install from subset if we're using setup.py |
83 | | - return False |
| 118 | + # can't install from subset |
| 119 | + for _configuration_file in ("pyproject.toml", "setup.py"): |
| 120 | + if not os.path.exists("binder") and os.path.exists(): |
| 121 | + return False |
| 122 | + |
84 | 123 | for name in ("requirements.txt", "requirements3.txt"): |
85 | 124 | requirements_txt = self.binder_path(name) |
86 | 125 | if not os.path.exists(requirements_txt): |
@@ -119,26 +158,39 @@ def get_assemble_scripts(self): |
119 | 158 | # and requirements3.txt (if it exists) |
120 | 159 | # will be installed in the python 3 notebook server env. |
121 | 160 | assemble_scripts = super().get_assemble_scripts() |
122 | | - setup_py = "setup.py" |
123 | 161 | # KERNEL_PYTHON_PREFIX is the env with the kernel, |
124 | 162 | # whether it's distinct from the notebook or the same. |
125 | 163 | pip = "${KERNEL_PYTHON_PREFIX}/bin/pip" |
126 | 164 | if not self._should_preassemble_pip: |
127 | 165 | assemble_scripts.extend(self._get_pip_scripts()) |
128 | 166 |
|
129 | | - # setup.py exists *and* binder dir is not used |
130 | | - if not self.binder_dir and os.path.exists(setup_py): |
131 | | - assemble_scripts.append(("${NB_USER}", f"{pip} install --no-cache-dir .")) |
| 167 | + for _configuration_file in ("pyproject.toml", "setup.py"): |
| 168 | + if not self.binder_dir and os.path.exists(_configuration_file): |
| 169 | + assemble_scripts.append(("${NB_USER}", f"{pip} install --no-cache-dir .")) |
| 170 | + break |
| 171 | + |
132 | 172 | return assemble_scripts |
133 | 173 |
|
134 | 174 | def detect(self): |
135 | 175 | """Check if current repo should be built with the Python buildpack.""" |
136 | 176 | requirements_txt = self.binder_path("requirements.txt") |
| 177 | + pyproject_toml = "pyproject.toml" |
137 | 178 | setup_py = "setup.py" |
138 | 179 |
|
139 | 180 | name = self.runtime[0] |
140 | 181 | if name: |
141 | 182 | return name == "python" |
| 183 | + if not self.binder_dir and os.path.exists(pyproject_toml): |
| 184 | + with open(pyproject_toml, "rb") as _pyproject_file: |
| 185 | + pyproject = tomllib.load(_pyproject_file) |
| 186 | + |
| 187 | + if ( |
| 188 | + ("project" in pyproject) |
| 189 | + and ("build-system" in pyproject) |
| 190 | + and ("requires" in pyproject["build-system"]) |
| 191 | + ): |
| 192 | + return True |
| 193 | + |
142 | 194 | if not self.binder_dir and os.path.exists(setup_py): |
143 | 195 | return True |
144 | 196 | return os.path.exists(requirements_txt) |
0 commit comments