Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,6 @@ jobs:
- norun
# Playwright test
- ui
include:
# The earliest actions/setup-python versions depend on the runner.
- ubuntu_version: "22.04"
python_version: "3.9"
repo_type: venv

steps:
- uses: actions/checkout@v5
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ sphinx:
build:
os: ubuntu-22.04
tools:
python: "3.10"
python: "3.11"

python:
install:
Expand Down
12 changes: 12 additions & 0 deletions docs/source/configuration/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,22 @@ environment. Our
[example `requirements.txt` file](https://github.com/binder-examples/requirements/blob/HEAD/requirements.txt)
on GitHub shows a typical requirements file.

(pyproject)=

## `pyproject.toml` - Install Python packages

To install your reprository like a Python package, you may include a
`pyproject.toml` file. `repo2docker`install `pyproject.toml` files by running
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To install your reprository like a Python package, you may include a
`pyproject.toml` file. `repo2docker`install `pyproject.toml` files by running
To install your repository like a Python package, you may include a
`pyproject.toml` file. `repo2docker`installs `pyproject.toml` files by running

`pip install -e .`.

(setup-py)=

## `setup.py` - Install Python packages

```{note}
We recommend to use `pyproject.toml` as it is the recommended way since 2020 when PEPs [621](https://peps.python.org/pep-0621/) and [631](https://peps.python.org/pep-0631/) were accepted.
```

To install your repository like a Python package, you may include a
`setup.py` file. `repo2docker` installs `setup.py` files by running
`pip install -e .`.
2 changes: 2 additions & 0 deletions repo2docker/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
LegacyBinderDockerBuildPack,
NixBuildPack,
PipfileBuildPack,
PyprojectBuildPack,
PythonBuildPack,
RBuildPack,
)
Expand Down Expand Up @@ -100,6 +101,7 @@ def _default_log_level(self):
RBuildPack,
CondaBuildPack,
PipfileBuildPack,
PyprojectBuildPack,
PythonBuildPack,
],
config=True,
Expand Down
1 change: 1 addition & 0 deletions repo2docker/buildpacks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
from .legacy import LegacyBinderDockerBuildPack
from .nix import NixBuildPack
from .pipfile import PipfileBuildPack
from .pyproject import PyprojectBuildPack
from .python import PythonBuildPack
from .r import RBuildPack
148 changes: 148 additions & 0 deletions repo2docker/buildpacks/pyproject/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
"""Buildpack for Git repositories with pyproject.toml
Support to pyproject.toml was added to pip v10.0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Support to pyproject.toml was added to pip v10.0,
Support for pyproject.toml was added to pip v10.0,

see https://pip.pypa.io/en/latest/reference/build-system/pyproject-toml/.
"""

import os
import re
from functools import lru_cache

try:
import tomllib
except ImportError:
import tomli as tomllib

from ..conda import CondaBuildPack

VERSION_PAT = re.compile(r"\d+(\.\d+)*")


class PyprojectBuildPack(CondaBuildPack):
"""Setup Python with pyproject.toml for use with a repository."""

@property
def python_version(self):
"""
Detect the Python version declared in a `pyproject.toml`.
Will return 'x.y' if version is found (e.g '3.6'),
or a Falsy empty string '' if not found.
"""

if hasattr(self, "_python_version"):
return self._python_version

name, version, _ = self.runtime

if name == "python":
runtime_python_version = version
else:
runtime_python_version = self.major_pythons["3"]
self.log.warning(
f"Python version unspecified in runtime.txt, using current default Python version {runtime_python_version}. This will change in the future."
)

runtime_python_version_info = runtime_python_version.split(".")

pyproject_file = self.binder_path("pyproject.toml")
with open(pyproject_file, "rb") as _pyproject_file:
pyproject_toml = tomllib.load(_pyproject_file)

if "project" in pyproject_toml:
if "requires-python" in pyproject_toml["project"]:
# This is the minumum version!
raw_pyproject_minimum_version = pyproject_toml["project"][
"requires-python"
]

match = VERSION_PAT.match(raw_pyproject_minimum_version)
if match:
pyproject_minimum_version = match.group()
pyproject_minimum_version_info = pyproject_minimum_version.split(
"."
)

if (
runtime_python_version_info[0]
< pyproject_minimum_version_info[0]
) or (
runtime_python_version_info[1]
< pyproject_minimum_version_info[1]
):
raise RuntimeError(
"runtime.txt version not supported by pyproject.toml."
)

self._python_version = runtime_python_version
return self._python_version

@lru_cache
def get_preassemble_script_files(self):
"""Return files needed for preassembly"""
files = super().get_preassemble_script_files()
path = self.binder_path("pyproject.toml")
if os.path.exists(path):
files[path] = path
return files

@lru_cache
def get_preassemble_scripts(self):
"""scripts to run prior to staging the repo contents"""
scripts = super().get_preassemble_scripts()
return scripts

@lru_cache
def get_assemble_scripts(self):
"""Return series of build-steps specific to this repository."""
# If we have pyproject.toml declare the
# use of Python 2, Python 2.7 will be made available in the *kernel*
# environment. The notebook servers environment on the other hand
# requires Python 3 but may require something additional installed in it
# still such as `nbgitpuller`. For this purpose, a "requirements3.txt"
# file will be used to install dependencies for the notebook servers
# environment, if Python 2 had been specified for the kernel
# environment.
assemble_scripts = super().get_assemble_scripts()

if self.separate_kernel_env:
# using legacy Python (e.g. 2.7) as a kernel

# requirements3.txt allows for packages to be installed to the
# notebook servers Python environment
nb_requirements_file = self.binder_path("requirements3.txt")
if os.path.exists(nb_requirements_file):
assemble_scripts.append(
(
"${NB_USER}",
f'${{NB_PYTHON_PREFIX}}/bin/pip install --no-cache-dir -r "{nb_requirements_file}"',
)
)

assemble_scripts.append(
(
"${NB_USER}",
"${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache-dir --editable .",
)
)

return assemble_scripts

def detect(self):
"""Check if current repo should be built with the pyproject.toml buildpack."""
# first make sure python is not explicitly unwanted
name, _, _ = self.runtime
if name != "python":
return False

pyproject_file = self.binder_path("pyproject.toml")
with open(pyproject_file, "rb") as _pyproject_file:
pyproject_toml = tomllib.load(_pyproject_file)

if (
("project" in pyproject_toml)
and ("build-system" in pyproject_toml)
and ("requires" in pyproject_toml["build-system"])
):
return os.path.exists(pyproject_file)

return False
4 changes: 4 additions & 0 deletions tests/pyproject/pyproject-toml/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Python - pyproject.toml
-----------------------

``pyproject.toml`` should be used over ``setup.py``.
Empty file.
26 changes: 26 additions & 0 deletions tests/pyproject/pyproject-toml/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "mwt"
version = "1.0.0"
dependencies = [
"numpy",
]
requires-python = ">=3.12"
authors = [
{name = "Project Jupyter Contributors", email = "[email protected]"},
]
maintainers = [
{name = "Project Jupyter Contributors", email = "[email protected]"},
]
description = "Test for repo2docker"
readme = "README.rst"
license = "MIT"

[project.urls]
Homepage = "https://repo2docker.readthedocs.io/"
Documentation = "https://repo2docker.readthedocs.io/"
Repository = "https://github.com/jupyterhub/repo2docker.git"
"Bug Tracker" = "https://github.com/jupyterhub/repo2docker/issues"
5 changes: 5 additions & 0 deletions tests/pyproject/pyproject-toml/verify
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python
try:
import mwt
except ImportError:
raise Exception("'mwt' shouldn't have been installed!")
Loading