{ lib, pkgs, ... }: rec { _mkPy = type: value: { _pyType = type; _value = value; }; _isPy = value: lib.isAttrs value && lib.hasAttr "_pyType" value; _mkValue = value: if _isPy value then value else if lib.isStringLike value then _mkPy "str" value else if lib.isInt value then _mkPy "int" value else if lib.isBool value then _mkPy "bool" value else if value == null then _mkPy "null" null else if lib.isList value then _mkPy "list" (lib.map _mkValue value) else if lib.isAttrs value then _mkPy "dict" (lib.mapAttrs (_: _mkValue) value) else abort "unsupported type"; _mkVars = lib.mapAttrs (_: _mkValue); mkReadFile = path: _mkPy "read-file" (if lib.isStringLike path then path else abort "invalid path type"); generate = name: value: pkgs.callPackage ({ runCommand, python3, black }: runCommand name { nativeBuildInputs = [ python3 black ]; value = builtins.toJSON (_mkVars value); codegen = builtins.readFile ./codegen.py; passAsFile = [ "codegen" "value" ]; preferLocalBuild = true; } '' python3 $codegenPath $valuePath > $out black $out '') { }; }