Skip to content

Commit 1d1e393

Browse files
committed
Start implementation of support for pyproject.yml
#1427
1 parent 1f1fab0 commit 1d1e393

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"""Buildpack for Git repositories with pyproject.toml
2+
3+
Support to pyproject.toml was added to pip v10.0,
4+
see https://pip.pypa.io/en/latest/reference/build-system/pyproject-toml/.
5+
"""
6+
7+
import os
8+
import re
9+
import tomllib
10+
11+
from ..conda import CondaBuildPack
12+
13+
VERSION_PAT = re.compile(r"\d+(\.\d+)*")
14+
15+
16+
class PyprojectBuildPack(CondaBuildPack):
17+
"""Setup Python with pyproject.toml for use with a repository."""
18+
19+
@property
20+
def python_version(self):
21+
"""
22+
Detect the Python version declared in a `pyproject.toml`.
23+
Will return 'x.y' if version is found (e.g '3.6'),
24+
or a Falsy empty string '' if not found.
25+
"""
26+
27+
if hasattr(self, "_python_version"):
28+
return self._python_version
29+
30+
pyproject_file = self.binder_path("pyproject.toml")
31+
with open(pyproject_file, "rb") as _pyproject_file:
32+
pyproject_toml = tomllib.load(_pyproject_file)
33+
34+
if "project" in pyproject_toml:
35+
if "requires-python" in pyproject_toml["project"]:
36+
raw_version = pyproject_toml["project"]["requires-python"]
37+
38+
match = VERSION_PAT.match(raw_version)
39+
if match:
40+
return match.group()
41+
42+
return ""
43+
44+
@lru_cache
45+
def get_preassemble_script_files(self):
46+
"""Return files needed for preassembly"""
47+
files = super().get_preassemble_script_files()
48+
path = self.binder_path("pyproject.toml")
49+
if os.path.exists(path):
50+
files[path] = path
51+
return files
52+
53+
@lru_cache
54+
def get_preassemble_scripts(self):
55+
"""scripts to run prior to staging the repo contents"""
56+
scripts = super().get_preassemble_scripts()
57+
# install pipenv to install dependencies within Pipfile.lock or Pipfile
58+
if V(self.python_version) < V("3.6"):
59+
# last pipenv version to support 2.7, 3.5
60+
pipenv_version = "2021.5.29"
61+
else:
62+
pipenv_version = "2022.1.8"
63+
scripts.append(
64+
(
65+
"${NB_USER}",
66+
f"${{KERNEL_PYTHON_PREFIX}}/bin/pip install --no-cache-dir pipenv=={pipenv_version}",
67+
)
68+
)
69+
return scripts
70+
71+
@lru_cache
72+
def get_assemble_scripts(self):
73+
"""Return series of build-steps specific to this repository."""
74+
# If we have pyproject.toml declare the
75+
# use of Python 2, Python 2.7 will be made available in the *kernel*
76+
# environment. The notebook servers environment on the other hand
77+
# requires Python 3 but may require something additional installed in it
78+
# still such as `nbgitpuller`. For this purpose, a "requirements3.txt"
79+
# file will be used to install dependencies for the notebook servers
80+
# environment, if Python 2 had been specified for the kernel
81+
# environment.
82+
assemble_scripts = super().get_assemble_scripts()
83+
84+
if self.separate_kernel_env:
85+
# using legacy Python (e.g. 2.7) as a kernel
86+
87+
# requirements3.txt allows for packages to be installed to the
88+
# notebook servers Python environment
89+
nb_requirements_file = self.binder_path("requirements3.txt")
90+
if os.path.exists(nb_requirements_file):
91+
assemble_scripts.append(
92+
(
93+
"${NB_USER}",
94+
f'${{NB_PYTHON_PREFIX}}/bin/pip install --no-cache-dir -r "{nb_requirements_file}"',
95+
)
96+
)
97+
98+
assemble_scripts.append(
99+
(
100+
"${NB_USER}",
101+
"""(cd && \\
102+
PATH="${{KERNEL_PYTHON_PREFIX}}/bin:$PATH" \\
103+
pip install --no-cache-dir --editable {working_directory}
104+
)""".format(
105+
working_directory=working_directory,
106+
),
107+
)
108+
)
109+
110+
return assemble_scripts
111+
112+
def detect(self):
113+
"""Check if current repo should be built with the pyproject.toml buildpack."""
114+
# first make sure python is not explicitly unwanted
115+
runtime_txt = self.binder_path("runtime.txt")
116+
if os.path.exists(runtime_txt):
117+
with open(runtime_txt) as f:
118+
runtime = f.read().strip()
119+
if not runtime.startswith("python-"):
120+
return False
121+
122+
pyproject_file = self.binder_path("pyproject.toml")
123+
124+
return os.path.exists(pyproject_file)

0 commit comments

Comments
 (0)