From 6df804a23274517e2d782c61279b385a69acf097 Mon Sep 17 00:00:00 2001 From: "Daniel F. Murcia Rivera" Date: Tue, 26 Sep 2023 10:41:26 -0500 Subject: [PATCH] feat(back): #1168 python vscode settings - adjust python docs - add makePythonVscodeSettings util - fix pythonOverrideUtils compose Signed-off-by: Daniel F. Murcia Rivera --- docs/src/api/extensions/python.md | 191 +++++++++++++----- src/args/agnostic.nix | 1 + .../make-python-vscode-settings/default.nix | 26 +++ .../make-python-vscode-settings/template.sh | 4 + .../vs_settings.py | 51 +++++ src/args/python-override-utils/default.nix | 16 +- 6 files changed, 237 insertions(+), 52 deletions(-) create mode 100644 src/args/make-python-vscode-settings/default.nix create mode 100644 src/args/make-python-vscode-settings/template.sh create mode 100644 src/args/make-python-vscode-settings/vs_settings.py diff --git a/docs/src/api/extensions/python.md b/docs/src/api/extensions/python.md index c629cb4c..bbf0aa06 100644 --- a/docs/src/api/extensions/python.md +++ b/docs/src/api/extensions/python.md @@ -4,9 +4,9 @@ Get a specific Python interpreter. Types: -- makePythonVersion (`function str -> package`): - - (`enum ["3.8" "3.9" "3.10" "3.11"]`): - Python version of the interpreter to return. +- makePythonVersion: `SupportedVersions -> Derivation` + - `SupportedVersions` = `enum ["3.8" "3.9" "3.10" "3.11"]` + Supported Python versions. Example: @@ -50,18 +50,19 @@ Pre-requisites: Types: -- makePythonPypiEnvironment (`function { ... } -> package`): - - name (`str`): +- makePythonPypiEnvironment: `Input -> SourceAble` +- `Input` = `Attrs` + - name: `str` Custom name to assign to the build step, be creative, it helps in debugging. - - searchPathsBuild (`asIn makeSearchPaths`): Optional. + - searchPathsBuild: `makeSearchPaths` (Optional Attr) Arguments here will be passed as-is to `makeSearchPaths` and used while installing the Python dependencies. Defaults to `makeSearchPaths`'s defaults. - - searchPathsRuntime (`asIn makeSearchPaths`): Optional. + - searchPathsRuntime: `makeSearchPaths` (Optional Attr) Arguments here will be passed as-is to `makeSearchPaths` and propagated to the runtime environment. Defaults to `makeSearchPaths`'s defaults. - - sourcesYaml (`package`): + - sourcesYaml: `NixPath` `sources.yaml` file computed as explained in the pre-requisites section. @@ -69,19 +70,19 @@ Types: dependencies in the build environment. The following flags are available for convenience: - - withCython_0_29_24 (`bool`): Optional. + - withCython_0_29_24: `bool` (Optional Attr) Bootstrap cython 0.29.24 to the environment Defaults to `false`. - - withNumpy_1_24_0 (`bool`): Optional. + - withNumpy_1_24_0: `bool` (Optional Attr) Bootstrap numpy 1.24.0 to the environment Defaults to `false`. - - withSetuptools_67_7_2 (`bool`): Optional. + - withSetuptools_67_7_2: `bool` (Optional Attr) Bootstrap setuptools 67.7.2 to the environment Defaults to `false`. - - withSetuptoolsScm_7_1_0 (`bool`) Optional. + - withSetuptoolsScm_7_1_0: `bool` (Optional Attr) Bootstrap setuptools-scm 7.1.0 to the environment Defaults to `false`. - - withWheel_0_40_0 (`bool`): Optional. + - withWheel_0_40_0: `bool` (Optional Attr) Bootstrap wheel 0.40.0 to the environment Defaults to `false`. @@ -131,41 +132,42 @@ over the tests and its python environments. Types: -- makePythonPypiEnvironment: (`function Input -> Bundle`): - - Input: `AttrsOf` - - buildEnv: `function {...} -> Derivation` - The nixpkgs buildEnv.override function. - Commonly found at `nixpkgs."${python_version}".buildEnv.override` - - buildPythonPackage: `function {...} -> Derivation` - The nixpkgs buildPythonPackage function. - Commonly found at `nixpkgs."${python_version}".pkgs.buildPythonPackage` - - pkgDeps: `AttrsOf` - The package dependencies. - Usually other python packages build with nix, - but can be also a nix derivation of a binary. - - runtime_deps: `List[Derivation]` - - build_deps: `List[Derivation]` - - test_deps: `List[Derivation]` +- makePythonPyprojectPackage: `Input -> Bundle` +- Input: `Attrs` + - buildEnv: `Attrs -> PythonEnvDerivation` + The nixpkgs buildEnv.override function. + Commonly found at `nixpkgs."${python_version}".buildEnv.override` + - buildPythonPackage: `Attrs -> PythonPkgDerivation` + The nixpkgs buildPythonPackage function. + Commonly found at `nixpkgs."${python_version}".pkgs.buildPythonPackage` + - pkgDeps: `Attrs` + The package dependencies. + Usually other python packages build with nix, + but can be also a nix derivation of a binary. + + - runtime_deps: `listOf Derivation` + - build_deps: `listOf Derivation` + - test_deps: `listOf Derivation` - src: `NixPath` - The nix path to the source code of the python package. - i.e. not only be the package itself, it should also contain - a tests folder/module, the pyproject conf and any other meta-package - data that the build or tests requires (e.g. custom mypy conf). - - Bundle: `AttrsOf` - - check: `AttrsOf` - Builds of the package only including one test. - - tests:`Derivation` - - types: `Derivation` - - env: `AttrsOf` - - dev: `Derivation` - The python environment containing only - runtime_deps and test_deps - - runtime: `Derivation` - The python environment containing only - the package itself and its runtime_deps. - - pkg: `Derivation` - The output of the nixpkgs buildPythonPackage function - i.e. the python package + The nix path to the source code of the python package. + i.e. not only the package itself, it should also contain + a tests folder/module, the pyproject conf and any other meta-package + data that the build or tests requires (e.g. custom mypy conf). +- Bundle: `Attrs` + - check: `Attrs` + Builds of the package only including one test. + - tests: `Derivation` + - types: `Derivation` + - env: `Attrs` + - dev: `PythonEnvDerivation` + The python environment containing only + runtime_deps and test_deps + - runtime: `PythonEnvDerivation` + The python environment containing only + the package itself and its runtime_deps. + - pkg: `PythonPkgDerivation` + The output of the nixpkgs buildPythonPackage function + i.e. the python package ???+ tip @@ -233,3 +235,98 @@ Example: executed only once (or never). This can also help on performance over heavy compilation/build processes. + +## makePythonVscodeSettings + +Generate visual studio code configuration for python development. + +Types: + +- makePythonVscodeSettings: `Input -> SourceAble` +- `Input` = `Attrs` + - name: `str` + - env: `PythonEnvDerivation` + A python environment derivation. e.g. can be builded from nixpkgs + standard builders or from some `env` of the outputs of `makePythonPyprojectPackage` + - bins: `listOf Derivation` + Derivations to include on the `searchPaths.bins` input + +Example: + +=== "my-env/makes.nix" + + ```nix + { + inputs, + makePythonPyprojectPackage, + makePythonVscodeSettings, + projectPath, + ... + }: let + root = projectPath "/my_package"; + bundle = makePythonPyprojectPackage { + inherit (inputs) buildEnv buildPythonPackage; + pkgDeps = { + runtime_deps = []; + build_deps = []; + test_deps = with inputs.python_pkgs; [ + mypy + pytest + ]; + }; + src = root; + }; + in + makePythonVscodeSettings { + env = bundle.env.dev; + bins = []; + name = "my-package-env-dev"; + } + ``` + +=== "makes.nix" + + ```nix + {outputs, ...}: { + dev = { + myPackage = { + source = [outputs."/my-env"]; + }; + }; + } + ``` + +=== ".envrc" + + ```bash + source "$(m . /dev/myPackage)/template" + ``` + +## pythonOverrideUtils + +Integrating python packages built with nix can create conflicts when +integrating various into one environment. This utils helps unifying +the dependencies into one and only one version per package. + +Types: + +- `PythonOverride` = `PythonPkgDerivation -> PythonPkgDerivation` + A functions that creates a new modified `PythonPkgDerivation` from the original. + +- pythonOverrideUtils: `Attrs` + - compose: `(listOf functions) -> _A -> _Z` + Function composition, the last function on the list is the first applied. + For each function `_R -> _S` on the list, its predecessor must match + their domain with the range of the function i.e. `_S -> _T`. + - no_check_override: `PythonOverride` + Skips the python package tests that are triggered on the build process. + This override is defined through `recursive_python_pkg_override`. + - recursive_python_pkg_override: `(Derivation -> bool) -> PythonOverride -> PythonOverride` + Search over all the tree of sub-dependencies the derivation + that evaluates to true as defined by the supplied first argument + filter `Derivation -> bool`. + If match, the supplied `PythonOverride` (second arg) is applied. + - replace_pkg: `(listOf str) -> PythonPkgDerivation -> PythonOverride` + Replace all python packages that match the supplied list of names, + with the supplied python package. + The returned override is defined through `recursive_python_pkg_override` diff --git a/src/args/agnostic.nix b/src/args/agnostic.nix index 20e81d6b..03935e9b 100644 --- a/src/args/agnostic.nix +++ b/src/args/agnostic.nix @@ -83,6 +83,7 @@ makePythonPypiEnvironment = import ./make-python-pypi-environment/default.nix self; makePythonPyprojectPackage = import ./make-python-pyproject-package/default.nix; makePythonVersion = import ./make-python-version/default.nix self; + makePythonVscodeSettings = import ./make-python-vscode-settings/default.nix self; makeRubyGemsEnvironment = import ./make-ruby-gems-environment/default.nix self; makeRubyGemsInstall = import ./make-ruby-gems-install/default.nix self; makeRubyVersion = import ./make-ruby-version/default.nix self; diff --git a/src/args/make-python-vscode-settings/default.nix b/src/args/make-python-vscode-settings/default.nix new file mode 100644 index 00000000..7832e7db --- /dev/null +++ b/src/args/make-python-vscode-settings/default.nix @@ -0,0 +1,26 @@ +{ + __nixpkgs__, + makeTemplate, + ... +}: +{ + name, + env, + bins, +}: +makeTemplate { + inherit name; + searchPaths = { + bin = + bins + ++ [ + env + ]; + }; + replace = { + __argPython__ = __nixpkgs__.python310; + __argPythonEnv__ = env; + __argPythonEntry__ = ./vs_settings.py; + }; + template = ./template.sh; +} diff --git a/src/args/make-python-vscode-settings/template.sh b/src/args/make-python-vscode-settings/template.sh new file mode 100644 index 00000000..d03ddc22 --- /dev/null +++ b/src/args/make-python-vscode-settings/template.sh @@ -0,0 +1,4 @@ +# shellcheck shell=bash + +mkdir -p ./.vscode \ + && "__argPython__"/bin/python "__argPythonEntry__" ./.vscode/settings.json "__argPythonEnv__" diff --git a/src/args/make-python-vscode-settings/vs_settings.py b/src/args/make-python-vscode-settings/vs_settings.py new file mode 100644 index 00000000..45809b0f --- /dev/null +++ b/src/args/make-python-vscode-settings/vs_settings.py @@ -0,0 +1,51 @@ +from json import ( + dump, + load, +) +from pathlib import ( + Path, +) +import sys +from typing import ( + Tuple, +) + + +def set_settings(settings_path: Path, python_env: Path) -> None: + settings_path.touch(exist_ok=True) + env_settings = { + "python.pythonPath": (python_env / "bin/python").as_posix(), + "python.defaultInterpreterPath": ( + python_env / "bin/python" + ).as_posix(), + "mypy.dmypyExecutable": (python_env / "bin/dmypy").as_posix(), + "python.linting.pylintPath": (python_env / "bin/pylint").as_posix(), + "python.testing.pytestArgs": ["tests"], + "python.testing.unittestEnabled": False, + "python.testing.pytestEnabled": True, + "python.linting.enabled": True, + "python.linting.mypyEnabled": False, + "python.linting.pylintEnabled": True, + } + with open(settings_path, "r+", encoding="UTF-8") as file: + if file.read() == "": + settings = {} + else: + file.seek(0) + settings = load(file) # type: ignore[misc] + with open(settings_path, "w+", encoding="UTF-8") as file: + new_settings = settings | env_settings + dump(new_settings, file, indent=2) + + +def main(args: Tuple[str, ...]) -> None: + settings = Path(args[1]) + env = Path(args[2]) + if env.exists(): + set_settings(settings, env) + else: + raise Exception(f"Non existent env: {env}") + + +if __name__ == "__main__": + main(tuple(sys.argv)) diff --git a/src/args/python-override-utils/default.nix b/src/args/python-override-utils/default.nix index 6338dafc..64cd4098 100644 --- a/src/args/python-override-utils/default.nix +++ b/src/args/python-override-utils/default.nix @@ -1,9 +1,11 @@ # python override utils, # useful when overriding pkgs from an environment to ensure no collisions +# PythonOverride = PythonPkgDerivation -> PythonPkgDerivation let recursive_python_pkg_override = is_pkg: override: let # is_pkg: Derivation -> Bool - # override: Derivation -> Derivation + # override: PythonPkgDerivation -> PythonPkgDerivation + # return: PythonOverride self = recursive_python_pkg_override is_pkg override; in pkg: @@ -19,7 +21,7 @@ let ) else pkg; - # no_check_override: Derivation -> Derivation + # no_check_override: PythonOverride no_check_override = recursive_python_pkg_override (pkg: pkg ? overridePythonAttrs && pkg ? pname) ( pkg: pkg.overridePythonAttrs ( @@ -37,12 +39,16 @@ let ) ); - # replace_pkg: List[str] -> Derivation -> Derivation + # replace_pkg: List[str] -> PythonPkgDerivation -> PythonOverride replace_pkg = names: new_pkg: recursive_python_pkg_override ( x: x ? overridePythonAttrs && x ? pname && builtins.elem x.pname names ) (_: new_pkg); + + compose = let + # definition from nixpkgs.lib.reverseList + reverseList = xs: let l = builtins.length xs; in builtins.genList (n: builtins.elemAt xs (l - n - 1)) l; + in functions: val: builtins.foldl' (x: f: f x) val (reverseList functions); in { - inherit recursive_python_pkg_override no_check_override replace_pkg; - compose = functions: val: builtins.foldl' (x: f: f x) val functions; + inherit compose recursive_python_pkg_override no_check_override replace_pkg; }