Skip to content

Commit b528dd8

Browse files
committed
Add tests for project / package mode; make the exported treesets deterministic.
1 parent 9f3f1c8 commit b528dd8

File tree

20 files changed

+262
-6
lines changed

20 files changed

+262
-6
lines changed

gazelle/python/parser.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ func (p *python3Parser) parse(pyFilenames *treeset.Set) (*treeset.Set, map[strin
112112
continue
113113
}
114114

115-
modules.Add(m)
115+
addModuleToTreeSet(modules, m)
116116
if res.HasMain {
117-
mainModules[res.FileName].Add(m)
117+
addModuleToTreeSet(mainModules[res.FileName], m)
118118
}
119119
}
120120

@@ -167,6 +167,15 @@ func moduleComparator(a, b interface{}) int {
167167
return godsutils.StringComparator(a.(Module).Name, b.(Module).Name)
168168
}
169169

170+
// addModuleToTreeSet adds a module to a treeset.Set, ensuring that a TypeCheckingOnly=false module is
171+
// prefered over a TypeCheckingOnly=true module.
172+
func addModuleToTreeSet(set *treeset.Set, mod Module) {
173+
if mod.TypeCheckingOnly && set.Contains(mod) {
174+
return
175+
}
176+
set.Add(mod)
177+
}
178+
170179
// annotationKind represents Gazelle annotation kinds.
171180
type annotationKind string
172181

gazelle/python/target.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
package python
1616

1717
import (
18+
"path/filepath"
19+
1820
"github.com/bazelbuild/bazel-gazelle/config"
1921
"github.com/bazelbuild/bazel-gazelle/rule"
2022
"github.com/emirpasic/gods/sets/treeset"
2123
godsutils "github.com/emirpasic/gods/utils"
22-
"path/filepath"
2324
)
2425

2526
// targetBuilder builds targets to be generated by Gazelle.
@@ -31,6 +32,7 @@ type targetBuilder struct {
3132
srcs *treeset.Set
3233
siblingSrcs *treeset.Set
3334
deps *treeset.Set
35+
pyiDeps *treeset.Set
3436
resolvedDeps *treeset.Set
3537
visibility *treeset.Set
3638
main *string
@@ -48,6 +50,7 @@ func newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string, siblingS
4850
srcs: treeset.NewWith(godsutils.StringComparator),
4951
siblingSrcs: siblingSrcs,
5052
deps: treeset.NewWith(moduleComparator),
53+
pyiDeps: treeset.NewWith(moduleComparator),
5154
resolvedDeps: treeset.NewWith(godsutils.StringComparator),
5255
visibility: treeset.NewWith(godsutils.StringComparator),
5356
}
@@ -79,7 +82,13 @@ func (t *targetBuilder) addModuleDependency(dep Module) *targetBuilder {
7982
// dependency resolution easier
8083
dep.Name = importSpecFromSrc(t.pythonProjectRoot, t.bzlPackage, fileName).Imp
8184
}
82-
t.deps.Add(dep)
85+
86+
// Add to appropriate dependency set based on whether it's type-checking only
87+
if dep.TypeCheckingOnly {
88+
t.pyiDeps.Add(dep)
89+
} else {
90+
t.deps.Add(dep)
91+
}
8392
return t
8493
}
8594

@@ -162,12 +171,24 @@ func (t *targetBuilder) build() *rule.Rule {
162171
if t.imports != nil {
163172
r.SetAttr("imports", t.imports)
164173
}
165-
if !t.deps.Empty() {
166-
r.SetPrivateAttr(config.GazelleImportsKey, t.deps)
174+
if combinedDeps := t.combinedDeps(); !combinedDeps.Empty() {
175+
r.SetPrivateAttr(config.GazelleImportsKey, combinedDeps)
167176
}
168177
if t.testonly {
169178
r.SetAttr("testonly", true)
170179
}
171180
r.SetPrivateAttr(resolvedDepsKey, t.resolvedDeps)
172181
return r
173182
}
183+
184+
// Combine both regular and type-checking imports into a single set
185+
// for passing to the resolver. The resolver will distinguish them
186+
// based on the TypeCheckingOnly field.
187+
func (t *targetBuilder) combinedDeps() *treeset.Set {
188+
combinedDeps := treeset.NewWith(moduleComparator)
189+
// If an import is in both pyi_deps and deps, the one in deps will override the one in pyi_deps, resulting
190+
// in the resolver properly adding to deps instead of pyi_deps.
191+
combinedDeps.Add(t.pyiDeps.Values()...)
192+
combinedDeps.Add(t.deps.Values()...)
193+
return combinedDeps
194+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# gazelle:python_generation_mode package
2+
# gazelle:python_generate_pyi_deps true
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
load("@rules_python//python:defs.bzl", "py_library")
2+
3+
# gazelle:python_generation_mode package
4+
# gazelle:python_generate_pyi_deps true
5+
6+
py_library(
7+
name = "type_checking_imports_package",
8+
srcs = [
9+
"bar.py",
10+
"baz.py",
11+
"foo.py",
12+
],
13+
pyi_deps = [
14+
"@gazelle_python_test//boto3_stubs",
15+
"@gazelle_python_test//djangorestframework",
16+
],
17+
visibility = ["//:__subpackages__"],
18+
deps = ["@gazelle_python_test//boto3"],
19+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Type Checking Imports (package mode)
2+
3+
See `type_checking_imports`; this is the same test case, but using the package generation mode.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
workspace(name = "gazelle_python_test")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from typing import TYPE_CHECKING
2+
3+
# foo should be added as a pyi_deps, since it is only imported in a type-checking context, but baz should be
4+
# added as a deps.
5+
from baz import X
6+
7+
if TYPE_CHECKING:
8+
import baz
9+
import foo
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2023 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
# While this format is not official, it is supported by most type checkers and
17+
# is used in the wild to avoid importing the typing module.
18+
TYPE_CHECKING = False
19+
if TYPE_CHECKING:
20+
# Both boto3 and boto3_stubs should be added to pyi_deps.
21+
import boto3
22+
23+
X = 1
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright 2023 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import typing
16+
17+
# boto3 should be added to deps. boto3_stubs and djangorestframework should be added to pyi_deps.
18+
import boto3
19+
20+
if typing.TYPE_CHECKING:
21+
from rest_framework import serializers
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2023 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
manifest:
16+
modules_mapping:
17+
boto3: boto3
18+
boto3_stubs: boto3_stubs
19+
rest_framework: djangorestframework
20+
pip_deps_repository_name: gazelle_python_test

0 commit comments

Comments
 (0)