Skip to content

Commit 9e5f12d

Browse files
committed
build: Add support for PEP-735 dependency groups
Using overlapping install UX with extras as described in https://peps.python.org/pep-0735/#overlapping-install-ux-with-extras
1 parent ef64376 commit 9e5f12d

File tree

4 files changed

+74
-43
lines changed

4 files changed

+74
-43
lines changed

build/checks/default.nix

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,19 @@ in
150150
);
151151

152152
venv = testSet.mkVirtualEnv "render-mkderivation-env" {
153-
myapp = [ "toml" ];
153+
myapp = [
154+
"toml" # Extra
155+
"round" # PEP-735 dependency group
156+
];
154157
};
155158
in
156159
pkgs.runCommand "render-mkderivation-test" { nativeBuildInputs = [ venv ]; } ''
157160
# Assert that extra was enabled
158161
python -c "import tomli_w"
159162
163+
# Assert that dependency group was enabled
164+
python -c "import wheel"
165+
160166
# Script from myapp
161167
hello
162168

build/checks/fixtures/myapp/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ dependencies = [
1111
[project.optional-dependencies]
1212
toml = ["tomli-w"]
1313

14+
[dependency-groups]
15+
round = ["wheel"]
16+
1417
[project.scripts]
1518
hello = "myapp:hello"
1619

build/lib/renderers.nix

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ in
5656
passthru = {
5757
dependencies = mkSpec filteredDeps.dependencies;
5858
optional-dependencies = mapAttrs (_: mkSpec) filteredDeps.extras;
59+
dependency-groups = mapAttrs (_: mkSpec) filteredDeps.groups;
5960
};
6061

6162
nativeBuildInputs = [
@@ -125,41 +126,46 @@ in
125126
# Synthetic pyproject.toml
126127
#
127128
# We don't use the provided build-system to build an editable package, we use hatchling.
128-
pyproject = {
129-
# PEP-621 project table
130-
project =
131-
{
132-
# Both name and version are required.
133-
inherit (project') name version;
134-
}
135-
// optionalAttrs (project' ? dependencies) {
136-
inherit (project') dependencies;
137-
}
138-
// optionalAttrs (project' ? optional-dependencies) {
139-
inherit (project') optional-dependencies;
140-
}
141-
// optionalAttrs (project' ? scripts) {
142-
inherit (project') scripts;
143-
}
144-
// optionalAttrs (project' ? gui-scripts) {
145-
inherit (project') gui-scripts;
146-
}
147-
// optionalAttrs (project' ? entry-points) {
148-
inherit (project') entry-points;
129+
pyproject =
130+
{
131+
# PEP-621 project table
132+
project =
133+
{
134+
# Both name and version are required.
135+
inherit (project') name version;
136+
}
137+
// optionalAttrs (project' ? dependencies) {
138+
inherit (project') dependencies;
139+
}
140+
// optionalAttrs (project' ? optional-dependencies) {
141+
inherit (project') optional-dependencies;
142+
}
143+
// optionalAttrs (project' ? scripts) {
144+
inherit (project') scripts;
145+
}
146+
// optionalAttrs (project' ? gui-scripts) {
147+
inherit (project') gui-scripts;
148+
}
149+
// optionalAttrs (project' ? entry-points) {
150+
inherit (project') entry-points;
151+
};
152+
153+
# Allow empty package
154+
tool.hatch.build.targets.wheel.bypass-selection = true;
155+
156+
# Include our editable pointer file in build
157+
tool.hatch.build.targets.wheel.force-include."_${pname}.pth" = "_${pname}.pth";
158+
159+
# Build editable package using hatchling
160+
build-system = {
161+
requires = [ "hatchling" ];
162+
build-backend = "hatchling.build";
149163
};
150-
151-
# Allow empty package
152-
tool.hatch.build.targets.wheel.bypass-selection = true;
153-
154-
# Include our editable pointer file in build
155-
tool.hatch.build.targets.wheel.force-include."_${pname}.pth" = "_${pname}.pth";
156-
157-
# Build editable package using hatchling
158-
build-system = {
159-
requires = [ "hatchling" ];
160-
build-backend = "hatchling.build";
164+
}
165+
// optionalAttrs (project.pyproject ? dependency-groups) {
166+
# PEP-735 dependency groups
167+
inherit (project.pyproject) dependency-groups;
161168
};
162-
};
163169
in
164170
{
165171
inherit pname;
@@ -172,6 +178,7 @@ in
172178
)
173179
);
174180
optional-dependencies = mapAttrs (_: mkSpec) filteredDeps.extras;
181+
dependency-groups = mapAttrs (_: mkSpec) filteredDeps.groups;
175182
};
176183

177184
# Convert created JSON format pyproject.toml into TOML and include a generated pth file

build/lib/resolvers.nix

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ let
88
genericClosure
99
match
1010
genAttrs
11+
throwIf
1112
;
1213

1314
in
@@ -30,16 +31,21 @@ in
3031
let
3132
pkg = set.${name};
3233
dependencies = pkg.passthru.dependencies or { };
33-
optional-dependencies = pkg.passthru.optional-dependencies or { };
3434
in
3535
[ name ]
3636
++ concatMap (name: recurse name dependencies.${name}) (attrNames dependencies)
3737
++ concatMap (
38-
extra:
38+
name':
3939
let
40-
extra' = optional-dependencies.${extra};
40+
extra' = pkg.passthru.optional-dependencies.${name'} or { };
41+
group' = pkg.passthru.dependency-groups.${name'} or { };
4142
in
42-
concatMap (name: recurse name extra'.${name}) (attrNames extra')
43+
throwIf (extra' == { } && group' == { })
44+
"Extra/group name '${name'}' does not match either extra or dependency group"
45+
concatMap
46+
(name: recurse name extra'.${name})
47+
(attrNames extra')
48+
++ concatMap (name: recurse name group'.${name}) (attrNames group')
4349
) extras;
4450

4551
# Memoise known build systems with no extras enabled for better performance
@@ -74,16 +80,25 @@ in
7480
let
7581
m = match "(.+)@(.*)" key;
7682
in
77-
# We're looking for a package with extra
83+
# We're looking for a package with extra or dependency group
7884
if m != null then
7985
(
8086
let
8187
pkg = set.${elemAt m 0};
82-
dependencies = pkg.passthru.optional-dependencies.${elemAt m 1};
88+
name' = elemAt m 1;
89+
extras' = pkg.passthru.optional-dependencies.${name'} or { };
90+
groups' = pkg.passthru.dependency-groups.${name'} or { };
8391
in
84-
concatMap (name: [ (mkKey name) ] ++ map (extra: mkKey "${name}@${extra}") dependencies.${name}) (
85-
attrNames dependencies
86-
)
92+
throwIf (extras' == { } && groups' == { })
93+
"Extra/group name '${name'}' does not match either extra or dependency group"
94+
(
95+
concatMap (name: [ (mkKey name) ] ++ map (extra: mkKey "${name}@${extra}") extras'.${name}) (
96+
attrNames extras'
97+
)
98+
++ concatMap (name: [ (mkKey name) ] ++ map (group: mkKey "${name}@${group}") groups'.${name}) (
99+
attrNames groups'
100+
)
101+
)
87102
)
88103
# Root package with no extra
89104
else

0 commit comments

Comments
 (0)