Skip to content

Commit

Permalink
feat(back): fluidattacks#1168 python vscode settings
Browse files Browse the repository at this point in the history
- adjust python docs
- add makePythonVscodeSettings util
- fix pythonOverrideUtils compose

Signed-off-by: Daniel F. Murcia Rivera <danmur97@outlook.com>
  • Loading branch information
danmur97 committed Sep 26, 2023
1 parent d82de92 commit 6df804a
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 52 deletions.
191 changes: 144 additions & 47 deletions docs/src/api/extensions/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -50,38 +50,39 @@ 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.

For building a few special packages you may need to boostrap
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`.

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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`
1 change: 1 addition & 0 deletions src/args/agnostic.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 26 additions & 0 deletions src/args/make-python-vscode-settings/default.nix
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 4 additions & 0 deletions src/args/make-python-vscode-settings/template.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# shellcheck shell=bash

mkdir -p ./.vscode \
&& "__argPython__"/bin/python "__argPythonEntry__" ./.vscode/settings.json "__argPythonEnv__"
51 changes: 51 additions & 0 deletions src/args/make-python-vscode-settings/vs_settings.py
Original file line number Diff line number Diff line change
@@ -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))
16 changes: 11 additions & 5 deletions src/args/python-override-utils/default.nix
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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 (
Expand All @@ -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;
}

0 comments on commit 6df804a

Please sign in to comment.