From f46bf326580ab6f7ee254578f11c1bafe6601389 Mon Sep 17 00:00:00 2001 From: Emmett Butler Date: Sat, 20 May 2023 07:43:19 -0700 Subject: [PATCH 1/3] add basic support for pep517 build backends current supported backends are hatchling and flit_core --- pynixify/base.py | 1 + pynixify/data/flitcore_patch.diff | 40 +++++++++++ pynixify/data/hatchling_patch.diff | 88 +++++++++++++++++++++++ pynixify/data/old_setuptools_patch.diff | 9 +-- pynixify/data/parse_setuppy_data.nix | 92 +++++++++++++++++++++++-- pynixify/data/setuptools_patch.diff | 9 +-- pynixify/expression_builder.py | 1 + 7 files changed, 228 insertions(+), 12 deletions(-) create mode 100644 pynixify/data/flitcore_patch.diff create mode 100644 pynixify/data/hatchling_patch.diff diff --git a/pynixify/base.py b/pynixify/base.py index 39eee63..1c8d5c1 100644 --- a/pynixify/base.py +++ b/pynixify/base.py @@ -25,6 +25,7 @@ class PackageMetadata: description: Optional[str] license: Optional[str] url: Optional[str] + _fmt: Optional[str] = "pyproject" @dataclass class Package: diff --git a/pynixify/data/flitcore_patch.diff b/pynixify/data/flitcore_patch.diff new file mode 100644 index 0000000..bb74c57 --- /dev/null +++ b/pynixify/data/flitcore_patch.diff @@ -0,0 +1,40 @@ +diff --git a/flit_core/flit_core/buildapi.py b/flit_core/flit_core/buildapi.py +index 963bf61..5190b7e 100644 +--- a/flit_core/flit_core/buildapi.py ++++ b/flit_core/flit_core/buildapi.py +@@ -3,6 +3,7 @@ import logging + import io + import os + import os.path as osp ++import sys + from pathlib import Path + + from .common import ( +@@ -13,6 +14,19 @@ from .config import read_flit_config + from .wheel import make_wheel_in, _write_wheel_file + from .sdist import SdistBuilder + ++def _write_pynixify_files(config_settings, deps): ++ if config_settings is not None and "PYNIXIFY_OUT" in config_settings: ++ from pathlib import Path ++ import json ++ pynix_out = Path(config_settings['PYNIXIFY_OUT']) ++ for target in ("tests", "setup", "install"): ++ fp = (pynix_out / ("%s_requires.txt" % target)).open("w") ++ fp.write('\n'.join([str(req) for req in deps])) ++ fp.write('\nflit_core') ++ fp.close() ++ with (pynix_out / 'meta.json').open('w') as fp: ++ json.dump({"version": None, "url": None, "license": None, "description": None}, fp) ++ + log = logging.getLogger(__name__) + + # PEP 517 specifies that the CWD will always be the source tree +@@ -70,6 +84,7 @@ prepare_metadata_for_build_editable = prepare_metadata_for_build_wheel + def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): + """Builds a wheel, places it in wheel_directory""" + info = make_wheel_in(pyproj_toml, Path(wheel_directory)) ++ _write_pynixify_files(config_settings, []) + return info.file.name + + def build_editable(wheel_directory, config_settings=None, metadata_directory=None): diff --git a/pynixify/data/hatchling_patch.diff b/pynixify/data/hatchling_patch.diff new file mode 100644 index 0000000..bb1f3a4 --- /dev/null +++ b/pynixify/data/hatchling_patch.diff @@ -0,0 +1,88 @@ +diff --git a/src/hatchling/build.py b/src/hatchling/build.py +index d79c1e2e..c85a837e 100644 +--- a/src/hatchling/build.py ++++ b/src/hatchling/build.py +@@ -1,6 +1,20 @@ + import os + + ++def _write_pynixify_files(config_settings, deps): ++ if config_settings is not None and "PYNIXIFY_OUT" in config_settings: ++ from pathlib import Path ++ import json ++ pynix_out = Path(config_settings['PYNIXIFY_OUT']) ++ for target in ("tests", "setup", "install"): ++ fp = (pynix_out / ("%s_requires.txt" % target)).open("w") ++ fp.write('\n'.join([str(req) for req in deps])) ++ fp.write('\nhatchling\nhatch-vcs') ++ fp.close() ++ with (pynix_out / 'meta.json').open('w') as fp: ++ json.dump({"version": None, "url": None, "license": None, "description": None}, fp) ++ ++ + def get_requires_for_build_sdist(config_settings=None): + """ + https://peps.python.org/pep-0517/#get-requires-for-build-sdist +@@ -8,6 +22,7 @@ def get_requires_for_build_sdist(config_settings=None): + from hatchling.builders.sdist import SdistBuilder + + builder = SdistBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return builder.config.dependencies + + +@@ -18,6 +33,7 @@ def build_sdist(sdist_directory, config_settings=None): + from hatchling.builders.sdist import SdistBuilder + + builder = SdistBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return os.path.basename(next(builder.build(sdist_directory, ['standard']))) + + +@@ -28,6 +44,7 @@ def get_requires_for_build_wheel(config_settings=None): + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return builder.config.dependencies + + +@@ -38,6 +55,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return os.path.basename(next(builder.build(wheel_directory, ['standard']))) + + +@@ -48,6 +66,7 @@ def get_requires_for_build_editable(config_settings=None): + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return builder.config.dependencies + + +@@ -58,6 +77,7 @@ def build_editable(wheel_directory, config_settings=None, metadata_directory=Non + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return os.path.basename(next(builder.build(wheel_directory, ['editable']))) + + +@@ -89,6 +109,7 @@ if 'PIP_BUILD_TRACKER' not in os.environ: + + with open(os.path.join(directory, 'METADATA'), 'w', encoding='utf-8') as f: + f.write(builder.config.core_metadata_constructor(builder.metadata)) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + + return os.path.basename(directory) + +@@ -110,5 +131,6 @@ if 'PIP_BUILD_TRACKER' not in os.environ: + + with open(os.path.join(directory, 'METADATA'), 'w', encoding='utf-8') as f: + f.write(builder.config.core_metadata_constructor(builder.metadata, extra_dependencies=extra_dependencies)) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + + return os.path.basename(directory) diff --git a/pynixify/data/old_setuptools_patch.diff b/pynixify/data/old_setuptools_patch.diff index a3e4532..4b0a17f 100644 --- a/pynixify/data/old_setuptools_patch.diff +++ b/pynixify/data/old_setuptools_patch.diff @@ -2,8 +2,8 @@ diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 83882511..259effd5 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py -@@ -155,14 +155,63 @@ def _install_setup_requires(attrs): - +@@ -155,14 +155,64 @@ def _install_setup_requires(attrs): + # Honor setup.cfg's options. dist.parse_config_files(ignore_option_errors=True) - if dist.setup_requires: @@ -56,6 +56,7 @@ index 83882511..259effd5 100644 + meta_attrs = {'description', 'url', 'license', 'version'} + for meta_attr in meta_attrs: + meta[meta_attr] = attrs.get(meta_attr) ++ meta['_fmt'] = 'setuptools' + with (out / 'meta.json').open('w') as fp: + json.dump(meta, fp) + else: @@ -69,6 +70,6 @@ index 83882511..259effd5 100644 - return distutils.core.setup(**attrs) + if 'PYNIXIFY' not in os.environ: + return distutils.core.setup(**attrs) - - + + setup.__doc__ = distutils.core.setup.__doc__ diff --git a/pynixify/data/parse_setuppy_data.nix b/pynixify/data/parse_setuppy_data.nix index 7ffbe22..e6b99d3 100644 --- a/pynixify/data/parse_setuppy_data.nix +++ b/pynixify/data/parse_setuppy_data.nix @@ -21,8 +21,88 @@ let ]; }); + setuptoolsscm = python.pkgs.buildPythonPackage rec { + pname = "setuptools-scm"; + version = "7.0.5"; - pythonWithPackages = python.withPackages (ps: [ patchedSetuptools ]); + src = python.pkgs.fetchPypi { + pname = "setuptools_scm"; + inherit version; + sha256 = "sha256-Ax4Tr3cdb4krlBrbbqBFRbv5Hrxc5ox4qvP/9uH7SEQ="; + }; + + propagatedBuildInputs = [ + python.pkgs.packaging + python.pkgs.typing-extensions + python.pkgs.tomli + patchedSetuptools + ]; + + pythonImportsCheck = [ "setuptools_scm" ]; + + # check in passthru.tests.pytest to escape infinite recursion on pytest + doCheck = false; + }; + hatchling = python.pkgs.hatchling.overrideAttrs + (ps: { patches = [ ./hatchling_patch.diff ]; }); + hatchvcs = python.pkgs.buildPythonPackage rec { + pname = "hatch-vcs"; + version = "0.2.0"; + format = "pyproject"; + + disabled = python.pkgs.pythonOlder "3.7"; + + src = python.pkgs.fetchPypi { + pname = "hatch_vcs"; + inherit version; + sha256 = "sha256-mRPXM7NO7JuwNF0GJsoyFlpK0t4V0c5kPDbQnKkIq/8="; + }; + + nativeBuildInputs = [ hatchling ]; + + propagatedBuildInputs = [ hatchling setuptoolsscm ]; + + checkInputs = [ pkgs.git python.pkgs.pytestCheckHook ]; + + disabledTests = [ + # incompatible with setuptools-scm>=7 + # https://github.com/ofek/hatch-vcs/issues/8 + "test_write" + ]; + + pythonImportsCheck = [ "hatch_vcs" ]; + }; + patchedflitcore = python.pkgs.flit-core.overrideAttrs + (ps: { patches = [ ./flitcore_patch.diff ]; }); + flitscm = python.pkgs.buildPythonPackage rec { + pname = "flit-scm"; + version = "1.7.0"; + + format = "pyproject"; + + src = pkgs.fetchFromGitLab { + owner = "WillDaSilva"; + repo = "flit_scm"; + rev = version; + sha256 = "sha256-K5sH+oHgX/ftvhkY+vIg6wUokAP96YxrTWds3tnEtyg="; + leaveDotGit = true; + }; + + nativeBuildInputs = + [ patchedflitcore setuptoolsscm python.pkgs.tomli pkgs.git ]; + propagatedBuildInputs = [ patchedflitcore setuptoolsscm ] + ++ pkgs.lib.optionals (python.pkgs.pythonOlder "3.11") + [ python.pkgs.tomli ]; + }; + + pythonWithPackages = python.withPackages (ps: [ + patchedSetuptools + setuptoolsscm + hatchling + hatchvcs + flitscm + python.pkgs.pip + ]); cleanSource = src: lib.cleanSourceWith { @@ -43,10 +123,14 @@ in stdenv.mkDerivation { ''; buildPhase = '' mkdir -p $out - if ! PYNIXIFY=1 python setup.py install; then - # Indicate that fetching the result failed, but let the build succeed - touch $out/failed + if PYNIXIFY=1 python setup.py install; then + exit 0 + fi + if ${python.pkgs.pip}/bin/pip --no-cache-dir wheel --config-settings PYNIXIFY_OUT=$out --no-build-isolation $PWD; then + exit 0 fi + # Indicate that fetching the result failed, but let the build succeed + touch $out/failed ''; dontInstall = true; } diff --git a/pynixify/data/setuptools_patch.diff b/pynixify/data/setuptools_patch.diff index 6a2d123..3c05aea 100644 --- a/pynixify/data/setuptools_patch.diff +++ b/pynixify/data/setuptools_patch.diff @@ -2,8 +2,8 @@ diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 89f6f06e..2d772eed 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py -@@ -76,36 +76,64 @@ def _install_setup_requires(attrs): - +@@ -76,36 +76,65 @@ def _install_setup_requires(attrs): + # Honor setup.cfg's options. dist.parse_config_files(ignore_option_errors=True) - if dist.setup_requires: @@ -77,6 +77,7 @@ index 89f6f06e..2d772eed 100644 + meta_attrs = {'description', 'url', 'license', 'version'} + for meta_attr in meta_attrs: + meta[meta_attr] = attrs.get(meta_attr) ++ meta['_fmt'] = 'setuptools' + with (out / 'meta.json').open('w') as fp: + json.dump(meta, fp) + else: @@ -91,6 +92,6 @@ index 89f6f06e..2d772eed 100644 - return distutils.core.setup(**attrs) + if 'PYNIXIFY' not in os.environ: + return distutils.core.setup(**attrs) - - + + setup.__doc__ = distutils.core.setup.__doc__ diff --git a/pynixify/expression_builder.py b/pynixify/expression_builder.py index 11c20a6..2874b2c 100644 --- a/pynixify/expression_builder.py +++ b/pynixify/expression_builder.py @@ -38,6 +38,7 @@ buildPythonPackage rec { pname = ${package.pypi_name | nix}; version = ${version | nix}; + format = ${metadata._fmt | nix}; % if package.local_source: src = lib.cleanSource ../../..; From 23d106ff0b1478ac13280082a859bf47a8bf06bb Mon Sep 17 00:00:00 2001 From: Emmett Butler Date: Sat, 20 May 2023 08:17:30 -0700 Subject: [PATCH 2/3] fix import errors --- pynixify/data/parse_setuppy_data.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pynixify/data/parse_setuppy_data.nix b/pynixify/data/parse_setuppy_data.nix index e6b99d3..650edbd 100644 --- a/pynixify/data/parse_setuppy_data.nix +++ b/pynixify/data/parse_setuppy_data.nix @@ -1,5 +1,6 @@ { file, stdenv ? (import { }).stdenv, lib ? (import { }).lib , unzip ? (import { }).unzip, python ? (import { }).python3 +, git ? (import { }).git, fetchFromGitLab ? (import { }).fetchFromGitLab }: let @@ -62,7 +63,7 @@ let propagatedBuildInputs = [ hatchling setuptoolsscm ]; - checkInputs = [ pkgs.git python.pkgs.pytestCheckHook ]; + checkInputs = [ git python.pkgs.pytestCheckHook ]; disabledTests = [ # incompatible with setuptools-scm>=7 @@ -80,7 +81,7 @@ let format = "pyproject"; - src = pkgs.fetchFromGitLab { + src = fetchFromGitLab { owner = "WillDaSilva"; repo = "flit_scm"; rev = version; @@ -89,9 +90,9 @@ let }; nativeBuildInputs = - [ patchedflitcore setuptoolsscm python.pkgs.tomli pkgs.git ]; + [ patchedflitcore setuptoolsscm python.pkgs.tomli git ]; propagatedBuildInputs = [ patchedflitcore setuptoolsscm ] - ++ pkgs.lib.optionals (python.pkgs.pythonOlder "3.11") + ++ lib.optionals (python.pkgs.pythonOlder "3.11") [ python.pkgs.tomli ]; }; From 4516e05cb524b1aea25f28274bc6d5097f215a6d Mon Sep 17 00:00:00 2001 From: Emmett Butler Date: Sat, 20 May 2023 08:17:40 -0700 Subject: [PATCH 3/3] use the right pip binary --- pynixify/data/parse_setuppy_data.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynixify/data/parse_setuppy_data.nix b/pynixify/data/parse_setuppy_data.nix index 650edbd..eb17a4c 100644 --- a/pynixify/data/parse_setuppy_data.nix +++ b/pynixify/data/parse_setuppy_data.nix @@ -127,7 +127,7 @@ in stdenv.mkDerivation { if PYNIXIFY=1 python setup.py install; then exit 0 fi - if ${python.pkgs.pip}/bin/pip --no-cache-dir wheel --config-settings PYNIXIFY_OUT=$out --no-build-isolation $PWD; then + if pip --no-cache-dir wheel --config-settings PYNIXIFY_OUT=$out --no-build-isolation $PWD; then exit 0 fi # Indicate that fetching the result failed, but let the build succeed