From 57066da97913298b2aab09c88026fee17cc827ec Mon Sep 17 00:00:00 2001 From: adisbladis Date: Sun, 29 Sep 2024 03:49:19 +0000 Subject: [PATCH] packages: Add tool to build an editable package impurely This is to be used with build-systems such as `meson-python` that builds native extensions. It's purpose is the same as that of `python setup.py build_ext -i`, just using PEP-660. Of course this is not a perfect analogy to using something like `meson-python` directly. We just use the `.pth` machinery directly, while `meson-python` uses a more complex hook machinery: - Automatic inference of Python `meson-python` puts the editable build in `build/cp312`. Users will have to manually override any automatically inferred editable root to point to the correct build directory. - Automatic rebuilds Thanks to it's hook machinery `meson-python` can rebuild packages as necessary on load. This automatic rebuild behaviour will not work, and users will have to call `build-editable` manually. These are just two examples of build-system specific behaviour that will not work. --- doc/src/SUMMARY.md | 3 ++ packages/build-editable/.python-version | 1 + packages/build-editable/README.md | 23 +++++++++ packages/build-editable/pyproject.toml | 17 +++++++ .../src/build_editable/__init__.py | 49 +++++++++++++++++++ packages/build-editable/uv.lock | 35 +++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 packages/build-editable/.python-version create mode 100644 packages/build-editable/README.md create mode 100644 packages/build-editable/pyproject.toml create mode 100644 packages/build-editable/src/build_editable/__init__.py create mode 100644 packages/build-editable/uv.lock diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 31117ed..2c881d9 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -45,6 +45,9 @@ - [resolvers](./build/lib/resolvers.nix) - [hooks](./build/hooks/default.nix) +- [Packages](./packages.md) + - [build-editable](./packages/build-editable/README.md) + # Contributing - [Hacking](./HACKING.md) diff --git a/packages/build-editable/.python-version b/packages/build-editable/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/packages/build-editable/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/packages/build-editable/README.md b/packages/build-editable/README.md new file mode 100644 index 0000000..9e85a87 --- /dev/null +++ b/packages/build-editable/README.md @@ -0,0 +1,23 @@ +# build-editable + +This package is intended to aid Nix-based Python development using [editable installs](https://pip.pypa.io/en/latest/topics/local-project-installs/#editable-installs). + +This is to be used with build-systems such as `meson-python` that builds native extensions. +It's purpose is similar to that of `python setup.py build_ext -i`, just using PEP-660. + +## Problems + +Of course this is not a perfect analogy to using something like `meson-python` directly. +We just use the `.pth` machinery directly, while `meson-python` uses a more complex hook machinery: + +- Automatic inference of Python + + `meson-python` puts the editable build in `build/cp312`. + Users will have to manually override any editable root automatically inferred by Nix to point to the correct build directory. + +- Automatic rebuilds + + Thanks to it's hook machinery `meson-python` can rebuild packages as necessary on load. + This automatic rebuild behaviour will not work, and users will have to call `build-editable` manually. + +These are just two examples of build-system specific behaviour that will not work out of the box. diff --git a/packages/build-editable/pyproject.toml b/packages/build-editable/pyproject.toml new file mode 100644 index 0000000..38a7ce9 --- /dev/null +++ b/packages/build-editable/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "build-editable" +version = "0.1.0" +description = "Build editable package" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "editables>=0.5", + "pyproject-hooks>=1.1.0", +] + +[project.scripts] +build-editable = "build_editable:build-editable" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/packages/build-editable/src/build_editable/__init__.py b/packages/build-editable/src/build_editable/__init__.py new file mode 100644 index 0000000..a02c90e --- /dev/null +++ b/packages/build-editable/src/build_editable/__init__.py @@ -0,0 +1,49 @@ +import argparse +import os +import os.path +import tempfile +from pathlib import Path + +import pyproject_hooks +import tomllib + +argparser = argparse.ArgumentParser( + description="Trigger in-place builds required for building editable packages with native extensions" +) +_ = argparser.add_argument( + "--wheel-dir", + help="Output wheel to directory instead of a temporary directory that gets immediately deleted", + type=str, +) + + +def main() -> None: + args = argparser.parse_args() + + cwd = Path(os.getcwd()) + if args.wheel_dir is not None: + out: str = str(args.wheel_dir) + else: + out_temp = tempfile.TemporaryDirectory() + out = out_temp.name + + with open(cwd.joinpath("pyproject.toml"), "rb") as pyproject_file: + pyproject = tomllib.load(pyproject_file) + + # Get build backend with fallback behaviour + # https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/#fallback-behaviour + try: + build_backend: str = pyproject["build-system"]["build-backend"] + except KeyError: + build_backend = "setuptools.build_meta:__legacy__" + + # Call editable build hooks using pyproject-hooks + hook_caller = pyproject_hooks.BuildBackendHookCaller( + source_dir=cwd, + build_backend=build_backend, + ) + hook_caller.build_editable(out) + + +if __name__ == "__main__": + main() diff --git a/packages/build-editable/uv.lock b/packages/build-editable/uv.lock new file mode 100644 index 0000000..891798a --- /dev/null +++ b/packages/build-editable/uv.lock @@ -0,0 +1,35 @@ +version = 1 +requires-python = ">=3.11" + +[[package]] +name = "build-editable" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "editables" }, + { name = "pyproject-hooks" }, +] + +[package.metadata] +requires-dist = [ + { name = "editables", specifier = ">=0.5" }, + { name = "pyproject-hooks", specifier = ">=1.1.0" }, +] + +[[package]] +name = "editables" +version = "0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/4a/986d35164e2033ddfb44515168a281a7986e260d344cf369c3f52d4c3275/editables-0.5.tar.gz", hash = "sha256:309627d9b5c4adc0e668d8c6fa7bac1ba7c8c5d415c2d27f60f081f8e80d1de2", size = 14744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/be/0f2f4a5e8adc114a02b63d92bf8edbfa24db6fc602fca83c885af2479e0e/editables-0.5-py3-none-any.whl", hash = "sha256:61e5ffa82629e0d8bfe09bc44a07db3c1ab8ed1ce78a6980732870f19b5e7d4c", size = 5098 }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/07/6f63dda440d4abb191b91dc383b472dae3dd9f37e4c1e4a5c3db150531c6/pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965", size = 7838 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl", hash = "sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2", size = 9184 }, +]