Skip to content

Commit c256c67

Browse files
committed
Start implementation of support for pyproject.yml
Related to #1427
1 parent ac4f14d commit c256c67

File tree

1 file changed

+125
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)