fix: make conftest.py special with gazelle (#879)
* fix: add conftest.py to py_test generated targets
Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
* fix: use separate py_library for conftest.py
This allows the conftest.py to be used on sub-directories
as pytest would pick them up.
Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
* fix: add testonly to conftest py_library
Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
* fix: testonly is a boolean, not a string
Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
diff --git a/gazelle/generate.go b/gazelle/generate.go
index 077acb8..c7b0709 100644
--- a/gazelle/generate.go
+++ b/gazelle/generate.go
@@ -26,6 +26,8 @@
pyBinaryEntrypointFilename = "__main__.py"
pyTestEntrypointFilename = "__test__.py"
pyTestEntrypointTargetname = "__test__"
+ conftestFilename = "conftest.py"
+ conftestTargetname = "conftest"
)
var (
@@ -71,6 +73,7 @@
// be generated for this package or not.
hasPyTestFile := false
hasPyTestTarget := false
+ hasConftestFile := false
for _, f := range args.RegularFiles {
if cfg.IgnoresFile(filepath.Base(f)) {
@@ -81,6 +84,8 @@
hasPyBinary = true
} else if !hasPyTestFile && f == pyTestEntrypointFilename {
hasPyTestFile = true
+ } else if f == conftestFilename {
+ hasConftestFile = true
} else if strings.HasSuffix(f, "_test.py") || (strings.HasPrefix(f, "test_") && ext == ".py") {
pyTestFilenames.Add(f)
} else if ext == ".py" {
@@ -196,10 +201,10 @@
pyLibraryTargetName := cfg.RenderLibraryName(packageName)
- // Check if a target with the same name we are generating alredy exists,
- // and if it is of a different kind from the one we are generating. If
- // so, we have to throw an error since Gazelle won't generate it
- // correctly.
+ // Check if a target with the same name we are generating already
+ // exists, and if it is of a different kind from the one we are
+ // generating. If so, we have to throw an error since Gazelle won't
+ // generate it correctly.
if args.File != nil {
for _, t := range args.File.Rules {
if t.Name() == pyLibraryTargetName && t.Kind() != pyLibraryKind {
@@ -233,10 +238,10 @@
pyBinaryTargetName := cfg.RenderBinaryName(packageName)
- // Check if a target with the same name we are generating alredy exists,
- // and if it is of a different kind from the one we are generating. If
- // so, we have to throw an error since Gazelle won't generate it
- // correctly.
+ // Check if a target with the same name we are generating already
+ // exists, and if it is of a different kind from the one we are
+ // generating. If so, we have to throw an error since Gazelle won't
+ // generate it correctly.
if args.File != nil {
for _, t := range args.File.Rules {
if t.Name() == pyBinaryTargetName && t.Kind() != pyBinaryKind {
@@ -267,6 +272,43 @@
result.Imports = append(result.Imports, pyBinary.PrivateAttr(config.GazelleImportsKey))
}
+ var conftest *rule.Rule
+ if hasConftestFile {
+ deps, err := parser.parseSingle(conftestFilename)
+ if err != nil {
+ log.Fatalf("ERROR: %v\n", err)
+ }
+
+ // Check if a target with the same name we are generating already
+ // exists, and if it is of a different kind from the one we are
+ // generating. If so, we have to throw an error since Gazelle won't
+ // generate it correctly.
+ if args.File != nil {
+ for _, t := range args.File.Rules {
+ if t.Name() == conftestTargetname && t.Kind() != pyLibraryKind {
+ fqTarget := label.New("", args.Rel, conftestTargetname)
+ err := fmt.Errorf("failed to generate target %q of kind %q: "+
+ "a target of kind %q with the same name already exists.",
+ fqTarget.String(), pyLibraryKind, t.Kind())
+ collisionErrors.Add(err)
+ }
+ }
+ }
+
+ conftestTarget := newTargetBuilder(pyLibraryKind, conftestTargetname, pythonProjectRoot, args.Rel).
+ setUUID(uuid.Must(uuid.NewUUID()).String()).
+ addSrc(conftestFilename).
+ addModuleDependencies(deps).
+ addVisibility(visibility).
+ setTestonly().
+ generateImportsAttribute()
+
+ conftest = conftestTarget.build()
+
+ result.Gen = append(result.Gen, conftest)
+ result.Imports = append(result.Imports, conftest.PrivateAttr(config.GazelleImportsKey))
+ }
+
if hasPyTestFile || hasPyTestTarget {
if hasPyTestFile {
// Only add the pyTestEntrypointFilename to the pyTestFilenames if
@@ -280,10 +322,10 @@
pyTestTargetName := cfg.RenderTestName(packageName)
- // Check if a target with the same name we are generating alredy exists,
- // and if it is of a different kind from the one we are generating. If
- // so, we have to throw an error since Gazelle won't generate it
- // correctly.
+ // Check if a target with the same name we are generating already
+ // exists, and if it is of a different kind from the one we are
+ // generating. If so, we have to throw an error since Gazelle won't
+ // generate it correctly.
if args.File != nil {
for _, t := range args.File.Rules {
if t.Name() == pyTestTargetName && t.Kind() != pyTestKind {
@@ -317,6 +359,10 @@
pyTestTarget.addModuleDependency(module{Name: pyLibrary.PrivateAttr(uuidKey).(string)})
}
+ if conftest != nil {
+ pyTestTarget.addModuleDependency(module{Name: conftest.PrivateAttr(uuidKey).(string)})
+ }
+
pyTest := pyTestTarget.build()
result.Gen = append(result.Gen, pyTest)
diff --git a/gazelle/target.go b/gazelle/target.go
index 2b26067..eef3aed 100644
--- a/gazelle/target.go
+++ b/gazelle/target.go
@@ -22,6 +22,7 @@
visibility *treeset.Set
main *string
imports []string
+ testonly bool
}
// newTargetBuilder constructs a new targetBuilder.
@@ -96,6 +97,12 @@
return t
}
+// setTestonly sets the testonly attribute to true.
+func (t *targetBuilder) setTestonly() *targetBuilder {
+ t.testonly = true
+ return t
+}
+
// generateImportsAttribute generates the imports attribute.
// These are a list of import directories to be added to the PYTHONPATH. In our
// case, the value we add is on Bazel sub-packages to be able to perform imports
@@ -131,6 +138,9 @@
if !t.deps.Empty() {
r.SetPrivateAttr(config.GazelleImportsKey, t.deps)
}
+ if t.testonly {
+ r.SetAttr("testonly", true)
+ }
r.SetPrivateAttr(resolvedDepsKey, t.resolvedDeps)
return r
}
diff --git a/gazelle/testdata/simple_test_with_conftest/BUILD.in b/gazelle/testdata/simple_test_with_conftest/BUILD.in
new file mode 100644
index 0000000..3f2beb3
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/BUILD.in
@@ -0,0 +1 @@
+load("@rules_python//python:defs.bzl", "py_library")
diff --git a/gazelle/testdata/simple_test_with_conftest/BUILD.out b/gazelle/testdata/simple_test_with_conftest/BUILD.out
new file mode 100644
index 0000000..18079bf
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/BUILD.out
@@ -0,0 +1,27 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+py_library(
+ name = "simple_test_with_conftest",
+ srcs = [
+ "__init__.py",
+ "foo.py",
+ ],
+ visibility = ["//:__subpackages__"],
+)
+
+py_library(
+ name = "conftest",
+ testonly = True,
+ srcs = ["conftest.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "simple_test_with_conftest_test",
+ srcs = ["__test__.py"],
+ main = "__test__.py",
+ deps = [
+ ":conftest",
+ ":simple_test_with_conftest",
+ ],
+)
diff --git a/gazelle/testdata/simple_test_with_conftest/README.md b/gazelle/testdata/simple_test_with_conftest/README.md
new file mode 100644
index 0000000..0ff245f
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/README.md
@@ -0,0 +1,4 @@
+# Simple test with conftest.py
+
+This test case asserts that a simple `py_test` is generated as expected when a
+`conftest.py` is present.
diff --git a/gazelle/testdata/simple_test_with_conftest/WORKSPACE b/gazelle/testdata/simple_test_with_conftest/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/testdata/simple_test_with_conftest/__init__.py b/gazelle/testdata/simple_test_with_conftest/__init__.py
new file mode 100644
index 0000000..6a49193
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/__init__.py
@@ -0,0 +1,3 @@
+from foo import foo
+
+_ = foo
diff --git a/gazelle/testdata/simple_test_with_conftest/__test__.py b/gazelle/testdata/simple_test_with_conftest/__test__.py
new file mode 100644
index 0000000..d6085a4
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/__test__.py
@@ -0,0 +1,12 @@
+import unittest
+
+from __init__ import foo
+
+
+class FooTest(unittest.TestCase):
+ def test_foo(self):
+ self.assertEqual("foo", foo())
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/gazelle/testdata/simple_test_with_conftest/conftest.py b/gazelle/testdata/simple_test_with_conftest/conftest.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/conftest.py
diff --git a/gazelle/testdata/simple_test_with_conftest/foo.py b/gazelle/testdata/simple_test_with_conftest/foo.py
new file mode 100644
index 0000000..cf68624
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/foo.py
@@ -0,0 +1,2 @@
+def foo():
+ return "foo"
diff --git a/gazelle/testdata/simple_test_with_conftest/test.yaml b/gazelle/testdata/simple_test_with_conftest/test.yaml
new file mode 100644
index 0000000..36dd656
--- /dev/null
+++ b/gazelle/testdata/simple_test_with_conftest/test.yaml
@@ -0,0 +1,3 @@
+---
+expect:
+ exit_code: 0