Repository navigation
Expand file tree
/
Copy pathpyright.nix
More file actions
115 lines (112 loc) · 3.72 KB
/
Copy pathpyright.nix
File metadata and controls
115 lines (112 loc) · 3.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
{
lib,
pkgs,
config,
...
}:
let
escapePath = lib.replaceStrings [ "/" "." ] [ "-" "" ];
in
{
meta.maintainers = [ "otavio" ];
# Example contains store paths
meta.skipExample = true;
options.programs.pyright = {
enable = lib.mkEnableOption "pyright";
package = lib.mkPackageOption pkgs "pyright" { };
directories = lib.mkOption {
description = ''
Directories to run pyright in.
Pyright auto-discovers `pyrightconfig.json` or `pyproject.toml`
(`[tool.pyright]`) starting from the directory it is invoked in.
Use `extraPaths` in that config to add module search paths —
pyright does not read the `PYTHONPATH` environment variable.
'';
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = {
directory = lib.mkOption {
type = lib.types.str;
default = name;
description = ''
Directory to run pyright in, relative to the project root.
The empty string means the project root itself — no `cd`
is performed.
'';
};
options = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "--warnings" ];
description = "Options to pass to pyright";
};
modules = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "" ];
example = [
"mymodule"
"tests"
];
description = "Modules to check (passed as positional file/directory arguments)";
};
};
}
)
);
default = {
"" = { };
};
example = {
"" = {
modules = [
"mymodule"
"tests"
];
};
"subdir" = { };
};
};
};
config = lib.mkIf config.programs.pyright.enable {
settings.formatter = lib.mapAttrs' (
name: cfg:
let
nonEmptyModules = lib.filter (m: m != "") cfg.modules;
cdLine = lib.optionalString (cfg.directory != "") "cd ${lib.escapeShellArg cfg.directory}";
args = lib.escapeShellArgs (cfg.options ++ nonEmptyModules);
in
lib.nameValuePair "pyright${lib.optionalString (name != "") "-${escapePath name}"}" {
command = pkgs.bash;
# Pyright resolves imports from a whole-module context, not per file,
# so the wrapper ignores the file path treefmt passes as `$0` and always
# invokes pyright on the configured `modules` (or the whole `directory`
# when `modules` is empty). With treefmt's batch-size limit this means
# pyright may be invoked more than once per directory on large file sets
# — each call redundantly type-checks the same modules.
#
# `treefmt --stdin` is not supported: pyright type-checks the on-disk
# modules regardless of what is piped in, so editor integrations should
# use pyright's language server directly.
options = [
"-eucx"
''
${cdLine}
exec ${lib.getExe config.programs.pyright.package} ${args}
''
];
includes = lib.concatMap (
module:
let
prefix = lib.optional (cfg.directory != "") cfg.directory ++ lib.optional (module != "") module;
in
[
(lib.concatStringsSep "/" (prefix ++ [ "*.py" ]))
(lib.concatStringsSep "/" (prefix ++ [ "*.pyi" ]))
]
) cfg.modules;
}
) config.programs.pyright.directories;
};
}