Skip to content

Commit

Permalink
Resolving sibling modules with absolute imports (#1029)
Browse files Browse the repository at this point in the history
* Resolving sibling modules with absolute imports

* unconditionally importing conftest

* handle from statements

* adding tests

* adding readme for the new test case
  • Loading branch information
linzhp authored Jan 31, 2023
1 parent b122f3a commit 9fc7cfa
Show file tree
Hide file tree
Showing 26 changed files with 66 additions and 48 deletions.
19 changes: 4 additions & 15 deletions gazelle/python/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
}

pyLibrary = newTargetBuilder(pyLibraryKind, pyLibraryTargetName, pythonProjectRoot, args.Rel, pyLibraryFilenames.Union(pyTestFilenames)).
setUUID(label.New("", args.Rel, pyLibraryTargetName).String()).
addVisibility(visibility).
addSrcs(pyLibraryFilenames).
addModuleDependencies(deps).
Expand Down Expand Up @@ -267,10 +266,6 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
addModuleDependencies(deps).
generateImportsAttribute()

if pyLibrary != nil {
pyBinaryTarget.addModuleDependency(module{Name: pyLibrary.PrivateAttr(uuidKey).(string)})
}

pyBinary := pyBinaryTarget.build()

result.Gen = append(result.Gen, pyBinary)
Expand Down Expand Up @@ -301,7 +296,6 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
}

conftestTarget := newTargetBuilder(pyLibraryKind, conftestTargetname, pythonProjectRoot, args.Rel, pyLibraryFilenames.Union(pyTestFilenames)).
setUUID(label.New("", args.Rel, conftestTargetname).String()).
addSrc(conftestFilename).
addModuleDependencies(deps).
addVisibility(visibility).
Expand All @@ -315,8 +309,8 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
}

var pyTestTargets []*targetBuilder
newPyTestTargetBuilder := func(pyTestFilenames *treeset.Set, pyTestTargetName string) *targetBuilder {
deps, err := parser.parse(pyTestFilenames)
newPyTestTargetBuilder := func(srcs *treeset.Set, pyTestTargetName string) *targetBuilder {
deps, err := parser.parse(srcs)
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
Expand All @@ -337,7 +331,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
}
}
return newTargetBuilder(pyTestKind, pyTestTargetName, pythonProjectRoot, args.Rel, pyLibraryFilenames.Union(pyTestFilenames)).
addSrcs(pyTestFilenames).
addSrcs(srcs).
addModuleDependencies(deps).
generateImportsAttribute()
}
Expand Down Expand Up @@ -371,14 +365,9 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
}

for _, pyTestTarget := range pyTestTargets {
if pyLibrary != nil {
pyTestTarget.addModuleDependency(module{Name: pyLibrary.PrivateAttr(uuidKey).(string)})
}

if conftest != nil {
pyTestTarget.addModuleDependency(module{Name: conftest.PrivateAttr(uuidKey).(string)})
pyTestTarget.addModuleDependency(module{Name: strings.TrimSuffix(conftestFilename, ".py")})
}

pyTest := pyTestTarget.build()

result.Gen = append(result.Gen, pyTest)
Expand Down
11 changes: 0 additions & 11 deletions gazelle/python/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ const (
// resolvedDepsKey is the attribute key used to pass dependencies that don't
// need to be resolved by the dependency resolver in the Resolver step.
resolvedDepsKey = "_gazelle_python_resolved_deps"
// uuidKey is the attribute key used to uniquely identify a py_library
// target that should be imported by a py_test or py_binary in the same
// Bazel package.
uuidKey = "_gazelle_python_library_uuid"
)

// Resolver satisfies the resolve.Resolver interface. It resolves dependencies
Expand Down Expand Up @@ -71,13 +67,6 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides = append(provides, provide)
}
}
if r.PrivateAttr(uuidKey) != nil {
provide := resolve.ImportSpec{
Lang: languageName,
Imp: r.PrivateAttr(uuidKey).(string),
}
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
}
Expand Down
27 changes: 10 additions & 17 deletions gazelle/python/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@
package python

import (
"path/filepath"

"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/rule"
"github.com/emirpasic/gods/sets/treeset"
godsutils "github.com/emirpasic/gods/utils"
"path/filepath"
)

// targetBuilder builds targets to be generated by Gazelle.
Expand All @@ -29,7 +28,6 @@ type targetBuilder struct {
name string
pythonProjectRoot string
bzlPackage string
uuid string
srcs *treeset.Set
siblingSrcs *treeset.Set
deps *treeset.Set
Expand All @@ -55,15 +53,6 @@ func newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string, siblingS
}
}

// setUUID sets the given UUID for the target. It's used to index the generated
// target based on this value in addition to the other ways the targets can be
// imported. py_{binary,test} targets in the same Bazel package can add a
// virtual dependency to this UUID that gets resolved in the Resolver interface.
func (t *targetBuilder) setUUID(uuid string) *targetBuilder {
t.uuid = uuid
return t
}

// addSrc adds a single src to the target.
func (t *targetBuilder) addSrc(src string) *targetBuilder {
t.srcs.Add(src)
Expand All @@ -81,9 +70,16 @@ func (t *targetBuilder) addSrcs(srcs *treeset.Set) *targetBuilder {

// addModuleDependency adds a single module dep to the target.
func (t *targetBuilder) addModuleDependency(dep module) *targetBuilder {
if dep.Name+".py" == filepath.Base(dep.Filepath) || !t.siblingSrcs.Contains(dep.Name+".py") {
t.deps.Add(dep)
fileName := dep.Name + ".py"
if dep.From != "" {
fileName = dep.From + ".py"
}
if t.siblingSrcs.Contains(fileName) && fileName != filepath.Base(dep.Filepath) {
// importing another module from the same package, converting to absolute imports to make
// dependency resolution easier
dep.Name = importSpecFromSrc(t.pythonProjectRoot, t.bzlPackage, fileName).Imp
}
t.deps.Add(dep)
return t
}

Expand Down Expand Up @@ -138,9 +134,6 @@ func (t *targetBuilder) generateImportsAttribute() *targetBuilder {
// build returns the assembled *rule.Rule for the target.
func (t *targetBuilder) build() *rule.Rule {
r := rule.NewRule(t.kind, t.name)
if t.uuid != "" {
r.SetPrivateAttr(uuidKey, t.uuid)
}
if !t.srcs.Empty() {
r.SetAttr("srcs", t.srcs.Values())
}
Expand Down
5 changes: 1 addition & 4 deletions gazelle/python/testdata/generated_test_entrypoint/BUILD.out
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,5 @@ py_test(
name = "generated_test_entrypoint_test",
srcs = [":__test__"],
main = ":__test__.py",
deps = [
":__test__",
":generated_test_entrypoint",
],
deps = [":__test__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/naming_convention/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import __init__
1 change: 1 addition & 0 deletions gazelle/python/testdata/naming_convention/__test__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import __init__
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import __init__
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import __init__
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import __init__
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import __init__
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
# limitations under the License.

import boto3
import __init__

_ = boto3
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import __init__
3 changes: 3 additions & 0 deletions gazelle/python/testdata/sibling_imports/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sibling imports

This test case asserts that imports from sibling modules are resolved correctly. It covers 3 different types of imports in `pkg/unit_test.py`
1 change: 1 addition & 0 deletions gazelle/python/testdata/sibling_imports/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Empty file.
29 changes: 29 additions & 0 deletions gazelle/python/testdata/sibling_imports/pkg/BUILD.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

py_library(
name = "pkg",
srcs = [
"__init__.py",
"a.py",
"b.py",
],
imports = [".."],
visibility = ["//:__subpackages__"],
)

py_test(
name = "test_util",
srcs = ["test_util.py"],
imports = [".."],
)

py_test(
name = "unit_test",
srcs = ["unit_test.py"],
imports = [".."],
deps = [
":pkg",
":test_util",
],
)

Empty file.
Empty file.
2 changes: 2 additions & 0 deletions gazelle/python/testdata/sibling_imports/pkg/b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def run():
pass
Empty file.
3 changes: 3 additions & 0 deletions gazelle/python/testdata/sibling_imports/pkg/unit_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import a
from b import run
import test_util
1 change: 1 addition & 0 deletions gazelle/python/testdata/sibling_imports/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
---
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import foo
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import foo.has_main.python.my_module
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

# For test purposes only.
import foo.has_test.python.my_module
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ py_binary(
srcs = ["__main__.py"],
main = "__main__.py",
visibility = ["//:__subpackages__"],
deps = [":with_third_party_requirements"],
deps = ["@gazelle_python_test_baz//:pkg"],
)

0 comments on commit 9fc7cfa

Please sign in to comment.