aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Thor <dougthor42@users.noreply.github.com>2024-04-05 15:44:12 -0700
committerGitHub <noreply@github.com>2024-04-05 22:44:12 +0000
commit5667a8652cca9a3a090a8c12c9824b24d25543f8 (patch)
tree283460574f3293e019727e79f21b486cf8fefd95
parent9e38b65ed21e9f9076acef341cb82c5993d84285 (diff)
downloadbazelbuild-rules_python-5667a8652cca9a3a090a8c12c9824b24d25543f8.tar.gz
feat(gazelle): Add "python_test_file_pattern" directive (#1819)
Add the `python_test_file_pattern` directive. This directive allows users to configure what python files get mapped to the `py_test` rule. The default behavior is unchanged: both `test_*` and `*_test.py` files generate `py_test` targets if the directive is unset. The directive supports multiple glob patterns, separated by a comma. Note: The original code used, effectively, `test_*` for one of the patterns. This code uses `test_*.py` instead. These are equivalent because of the `.py` extension check prior to pattern matching. Fixes #1816.
-rw-r--r--CHANGELOG.md6
-rw-r--r--gazelle/MODULE.bazel2
-rw-r--r--gazelle/README.md68
-rw-r--r--gazelle/deps.bzl8
-rw-r--r--gazelle/go.mod2
-rw-r--r--gazelle/go.sum4
-rw-r--r--gazelle/python/BUILD.bazel2
-rw-r--r--gazelle/python/configure.go14
-rw-r--r--gazelle/python/generate.go19
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/README.md19
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/WORKSPACE0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test.yaml0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.in1
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.out18
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/hello_test.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_goodbye.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_hello.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.in1
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.out17
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/hello_test.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_goodbye.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_hello.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.in2
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.out20
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/hello_test.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_goodbye.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_hello.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.in2
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.out20
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_helloworld_A_testA.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_my_filename_B_test1.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_nota_test0_Z1.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.in3
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.out34
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_hello.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.pyc0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/hello_foo.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib2.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/test_bar0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/unittest_foo.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.in2
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.out15
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/hello_unittest.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/not_a_test.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.in2
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.out21
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_test.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_unittest.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/test_bar.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.in1
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.out1
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/README.md4
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/WORKSPACE0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/test.yaml19
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.in1
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.out1
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_no_value/README.md8
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_no_value/WORKSPACE0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_no_value/foo_test.py0
-rw-r--r--gazelle/python/testdata/directive_python_test_file_pattern_no_value/test.yaml19
-rw-r--r--gazelle/pythonconfig/pythonconfig.go20
63 files changed, 362 insertions, 14 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f8e273..5f0c2e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,10 @@ A brief description of the categories of changes:
* (gazelle) Added a new `python_default_visibility` directive to control the
_default_ visibility of generated targets. See the [docs][python_default_visibility]
for details.
+* (gazelle) Added a new `python_test_file_pattern` directive. This directive tells
+ gazelle which python files should be mapped to the `py_test` rule. See the
+ [original issue][test_file_pattern_issue] and the [docs][test_file_pattern_docs]
+ for details.
* (wheel) Add support for `data_files` attributes in py_wheel rule
([#1777](https://github.com/bazelbuild/rules_python/issues/1777))
* (py_wheel) `bzlmod` installations now provide a `twine` setup for the default
@@ -66,6 +70,8 @@ A brief description of the categories of changes:
[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0
[python_default_visibility]: gazelle/README.md#directive-python_default_visibility
+[test_file_pattern_issue]: https://github.com/bazelbuild/rules_python/issues/1816
+[test_file_pattern_docs]: gazelle/README.md#directive-python_test_file_pattern
### Changed
diff --git a/gazelle/MODULE.bazel b/gazelle/MODULE.bazel
index 940a263..1d01f49 100644
--- a/gazelle/MODULE.bazel
+++ b/gazelle/MODULE.bazel
@@ -14,7 +14,7 @@ go_deps.from_file(go_mod = "//:go.mod")
use_repo(
go_deps,
"com_github_bazelbuild_buildtools",
- "com_github_bmatcuk_doublestar",
+ "com_github_bmatcuk_doublestar_v4",
"com_github_emirpasic_gods",
"com_github_ghodss_yaml",
"in_gopkg_yaml_v2",
diff --git a/gazelle/README.md b/gazelle/README.md
index 1caa677..e4fd3d8 100644
--- a/gazelle/README.md
+++ b/gazelle/README.md
@@ -202,6 +202,8 @@ Python-specific directives are as follows:
| Instructs gazelle to use these visibility labels on all python targets. `labels` is a comma-separated list of labels (without spaces). | `//$python_root:__subpackages__` |
| [`# gazelle:python_visibility label`](#directive-python_visibility) | |
| Appends additional visibility labels to each generated target. This directive can be set multiple times. | |
+| [`# gazelle:python_test_file_pattern`](#directive-python_test_file_pattern) | `*_test.py,test_*.py` |
+| Filenames matching these comma-separated `glob`s will be mapped to `py_test` targets. |
#### Directive: `python_root`:
@@ -359,6 +361,70 @@ py_library(
```
+#### Directive: `python_test_file_pattern`:
+
+This directive adjusts which python files will be mapped to the `py_test` rule.
+
++ The default is `*_test.py,test_*.py`: both `test_*.py` and `*_test.py` files
+ will generate `py_test` targets.
++ This directive must have a value. If no value is given, an error will be raised.
++ It is recommended, though not necessary, to include the `.py` extension in
+ the `glob`s: `foo*.py,?at.py`.
++ Like most directives, it applies to the current Bazel package and all subpackages
+ until the directive is set again.
++ This directive accepts multiple `glob` patterns, separated by commas without spaces:
+
+```starlark
+# gazelle:python_test_file_pattern foo*.py,?at
+
+py_library(
+ name = "mylib",
+ srcs = ["mylib.py"],
+)
+
+py_test(
+ name = "foo_bar",
+ srcs = ["foo_bar.py"],
+)
+
+py_test(
+ name = "cat",
+ srcs = ["cat.py"],
+)
+
+py_test(
+ name = "hat",
+ srcs = ["hat.py"],
+)
+```
+
+
+##### Notes
+
+Resetting to the default value (such as in a subpackage) is manual. Set:
+
+```starlark
+# gazelle:python_test_file_pattern *_test.py,test_*.py
+```
+
+There currently is no way to tell gazelle that _no_ files in a package should
+be mapped to `py_test` targets (see [Issue #1826][issue-1826]). The workaround
+is to set this directive to a pattern that will never match a `.py` file, such
+as `foo.bar`:
+
+```starlark
+# No files in this package should be mapped to py_test targets.
+# gazelle:python_test_file_pattern foo.bar
+
+py_library(
+ name = "my_test",
+ srcs = ["my_test.py"],
+)
+```
+
+[issue-1826]: https://github.com/bazelbuild/rules_python/issues/1826
+
+
### Libraries
Python source files are those ending in `.py` but not ending in `_test.py`.
@@ -438,7 +504,7 @@ for more information on extending Gazelle.
If you add new Go dependencies to the plugin source code, you need to "tidy" the go.mod file.
After changing that file, run `go mod tidy` or `bazel run @go_sdk//:bin/go -- mod tidy`
-to update the go.mod and go.sum files. Then run `bazel run //:update_go_deps` to have gazelle
+to update the go.mod and go.sum files. Then run `bazel run //:gazelle_update_repos` to have gazelle
add the new dependenies to the deps.bzl file. The deps.bzl file is used as defined in our /WORKSPACE
to include the external repos Bazel loads Go dependencies from.
diff --git a/gazelle/deps.bzl b/gazelle/deps.bzl
index 26f8c66..d9d3881 100644
--- a/gazelle/deps.bzl
+++ b/gazelle/deps.bzl
@@ -38,10 +38,10 @@ def gazelle_deps():
)
go_repository(
- name = "com_github_bmatcuk_doublestar",
- importpath = "github.com/bmatcuk/doublestar",
- sum = "h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=",
- version = "v1.3.4",
+ name = "com_github_bmatcuk_doublestar_v4",
+ importpath = "github.com/bmatcuk/doublestar/v4",
+ sum = "h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=",
+ version = "v4.6.1",
)
go_repository(
diff --git a/gazelle/go.mod b/gazelle/go.mod
index 6789aa1..b9b79ac 100644
--- a/gazelle/go.mod
+++ b/gazelle/go.mod
@@ -6,7 +6,7 @@ require (
github.com/bazelbuild/bazel-gazelle v0.31.1
github.com/bazelbuild/buildtools v0.0.0-20230510134650-37bd1811516d
github.com/bazelbuild/rules_go v0.41.0
- github.com/bmatcuk/doublestar v1.3.4
+ github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/emirpasic/gods v1.18.1
github.com/ghodss/yaml v1.0.0
gopkg.in/yaml.v2 v2.4.0
diff --git a/gazelle/go.sum b/gazelle/go.sum
index 5617f9b..fcfcb28 100644
--- a/gazelle/go.sum
+++ b/gazelle/go.sum
@@ -6,8 +6,8 @@ github.com/bazelbuild/buildtools v0.0.0-20230510134650-37bd1811516d h1:Fl1FfItZp
github.com/bazelbuild/buildtools v0.0.0-20230510134650-37bd1811516d/go.mod h1:689QdV3hBP7Vo9dJMmzhoYIyo/9iMhEmHkJcnaPRCbo=
github.com/bazelbuild/rules_go v0.41.0 h1:JzlRxsFNhlX+g4drDRPhIaU5H5LnI978wdMJ0vK4I+k=
github.com/bazelbuild/rules_go v0.41.0/go.mod h1:TMHmtfpvyfsxaqfL9WnahCsXMWDMICTw7XeK9yVb+YU=
-github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
-github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
+github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
+github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
diff --git a/gazelle/python/BUILD.bazel b/gazelle/python/BUILD.bazel
index fd051eb..4cca8b3 100644
--- a/gazelle/python/BUILD.bazel
+++ b/gazelle/python/BUILD.bazel
@@ -38,7 +38,7 @@ go_library(
"@bazel_gazelle//resolve:go_default_library",
"@bazel_gazelle//rule:go_default_library",
"@com_github_bazelbuild_buildtools//build:go_default_library",
- "@com_github_bmatcuk_doublestar//:doublestar",
+ "@com_github_bmatcuk_doublestar_v4//:doublestar",
"@com_github_emirpasic_gods//lists/singlylinkedlist",
"@com_github_emirpasic_gods//sets/treeset",
"@com_github_emirpasic_gods//utils",
diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go
index 8436096..2a0e142 100644
--- a/gazelle/python/configure.go
+++ b/gazelle/python/configure.go
@@ -25,6 +25,7 @@ import (
"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/rule"
+ "github.com/bmatcuk/doublestar/v4"
"github.com/bazelbuild/rules_python/gazelle/manifest"
"github.com/bazelbuild/rules_python/gazelle/pythonconfig"
@@ -65,6 +66,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.TestNamingConvention,
pythonconfig.DefaultVisibilty,
pythonconfig.Visibility,
+ pythonconfig.TestFilePattern,
}
}
@@ -181,6 +183,18 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
}
case pythonconfig.Visibility:
config.AppendVisibility(strings.TrimSpace(d.Value))
+ case pythonconfig.TestFilePattern:
+ value := strings.TrimSpace(d.Value)
+ if value == "" {
+ log.Fatal("directive 'python_test_file_pattern' requires a value")
+ }
+ globStrings := strings.Split(value, ",")
+ for _, g := range globStrings {
+ if !doublestar.ValidatePattern(g) {
+ log.Fatalf("invalid glob pattern '%s'", g)
+ }
+ }
+ config.SetTestFilePattern(globStrings)
}
}
diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go
index 673076d..1937831 100644
--- a/gazelle/python/generate.go
+++ b/gazelle/python/generate.go
@@ -28,7 +28,7 @@ import (
"github.com/bazelbuild/bazel-gazelle/language"
"github.com/bazelbuild/bazel-gazelle/rule"
"github.com/bazelbuild/rules_python/gazelle/pythonconfig"
- "github.com/bmatcuk/doublestar"
+ "github.com/bmatcuk/doublestar/v4"
"github.com/emirpasic/gods/lists/singlylinkedlist"
"github.com/emirpasic/gods/sets/treeset"
godsutils "github.com/emirpasic/gods/utils"
@@ -54,6 +54,17 @@ func GetActualKindName(kind string, args language.GenerateArgs) string {
return kind
}
+func matchesAnyGlob(s string, globs []string) bool {
+ // This function assumes that the globs have already been validated. If a glob is
+ // invalid, it's considered a non-match and we move on to the next pattern.
+ for _, g := range globs {
+ if ok, _ := doublestar.Match(g, s); ok {
+ return true
+ }
+ }
+ return false
+}
+
// GenerateRules extracts build metadata from source files in a directory.
// GenerateRules is called in each directory where an update is requested
// in depth-first post-order.
@@ -100,6 +111,8 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
hasPyTestEntryPointTarget := false
hasConftestFile := false
+ testFileGlobs := cfg.TestFilePattern()
+
for _, f := range args.RegularFiles {
if cfg.IgnoresFile(filepath.Base(f)) {
continue
@@ -113,7 +126,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
hasPyTestEntryPointFile = true
} else if f == conftestFilename {
hasConftestFile = true
- } else if strings.HasSuffix(f, "_test.py") || strings.HasPrefix(f, "test_") {
+ } else if matchesAnyGlob(f, testFileGlobs) {
pyTestFilenames.Add(f)
} else {
pyLibraryFilenames.Add(f)
@@ -195,7 +208,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
}
}
baseName := filepath.Base(path)
- if strings.HasSuffix(baseName, "_test.py") || strings.HasPrefix(baseName, "test_") {
+ if matchesAnyGlob(baseName, testFileGlobs) {
pyTestFilenames.Add(srcPath)
} else {
pyLibraryFilenames.Add(srcPath)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/README.md b/gazelle/python/testdata/directive_python_test_file_pattern/README.md
new file mode 100644
index 0000000..99142f7
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/README.md
@@ -0,0 +1,19 @@
+# Directive: `python_test_file_pattern`
+
+This test case asserts that the `# gazelle:python_test_file_pattern` directive
+works as intended.
+
+It consists of 6 cases:
+
+1. When not set, both `*_test.py` and `test_*.py` files are mapped to the `py_test`
+ rule.
+2. When set to a single value `*_test.py`, `test_*.py` files are mapped to the
+ `py_library` rule.
+3. When set to a single value `test_*.py`, `*_test.py` files are mapped to the
+ `py_library` rule (ie: the inverse of case 2, but also with "file" generation
+ mode).
+4. Arbitrary `glob` patterns are supported.
+5. Multiple `glob` patterns are supported and that patterns don't technically
+ need to end in `.py` if they end in a wildcard (eg: we won't make a `py_test`
+ target for the extensionless file `test_foo`).
+6. Sub-packages can override the directive's value.
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/WORKSPACE b/gazelle/python/testdata/directive_python_test_file_pattern/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/WORKSPACE
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test.yaml b/gazelle/python/testdata/directive_python_test_file_pattern/test.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test.yaml
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.in
new file mode 100644
index 0000000..af2c2ce
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.in
@@ -0,0 +1 @@
+# gazelle:python_generation_mode file
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.out
new file mode 100644
index 0000000..724b913
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/BUILD.out
@@ -0,0 +1,18 @@
+load("@rules_python//python:defs.bzl", "py_test")
+
+# gazelle:python_generation_mode file
+
+py_test(
+ name = "hello_test",
+ srcs = ["hello_test.py"],
+)
+
+py_test(
+ name = "test_goodbye",
+ srcs = ["test_goodbye.py"],
+)
+
+py_test(
+ name = "test_hello",
+ srcs = ["test_hello.py"],
+)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/hello_test.py b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/hello_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/hello_test.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_goodbye.py b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_goodbye.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_goodbye.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_hello.py b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_hello.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test1_unset/test_hello.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.in
new file mode 100644
index 0000000..57becc6
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.in
@@ -0,0 +1 @@
+# gazelle:python_test_file_pattern *_test.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.out
new file mode 100644
index 0000000..be5917b
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/BUILD.out
@@ -0,0 +1,17 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_test_file_pattern *_test.py
+
+py_library(
+ name = "test2_star_test_py",
+ srcs = [
+ "test_goodbye.py",
+ "test_hello.py",
+ ],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "hello_test",
+ srcs = ["hello_test.py"],
+)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/hello_test.py b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/hello_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/hello_test.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_goodbye.py b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_goodbye.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_goodbye.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_hello.py b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_hello.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test2_star_test_py/test_hello.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.in
new file mode 100644
index 0000000..cc91589
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern test_*.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.out
new file mode 100644
index 0000000..7ff0d5d
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/BUILD.out
@@ -0,0 +1,20 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern test_*.py
+
+py_library(
+ name = "hello_test",
+ srcs = ["hello_test.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "test_goodbye",
+ srcs = ["test_goodbye.py"],
+)
+
+py_test(
+ name = "test_hello",
+ srcs = ["test_hello.py"],
+)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/hello_test.py b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/hello_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/hello_test.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_goodbye.py b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_goodbye.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_goodbye.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_hello.py b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_hello.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test3_test_star_py/test_hello.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.in
new file mode 100644
index 0000000..8bffaa1
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern foo_*_[A-Z]_test?.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.out
new file mode 100644
index 0000000..ff0034c
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/BUILD.out
@@ -0,0 +1,20 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern foo_*_[A-Z]_test?.py
+
+py_library(
+ name = "foo_nota_test0_Z1",
+ srcs = ["foo_nota_test0_Z1.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "foo_helloworld_A_testA",
+ srcs = ["foo_helloworld_A_testA.py"],
+)
+
+py_test(
+ name = "foo_my_filename_B_test1",
+ srcs = ["foo_my_filename_B_test1.py"],
+)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_helloworld_A_testA.py b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_helloworld_A_testA.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_helloworld_A_testA.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_my_filename_B_test1.py b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_my_filename_B_test1.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_my_filename_B_test1.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_nota_test0_Z1.py b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_nota_test0_Z1.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test4_glob/foo_nota_test0_Z1.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.in
new file mode 100644
index 0000000..a0e25aa
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.in
@@ -0,0 +1,3 @@
+# gazelle:python_test_file_pattern *_hello.py,hello_*,unittest_*,*_unittest.py
+
+# Note that "foo_unittest.pyc" and "test_bar" files are ignored.
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.out
new file mode 100644
index 0000000..1dcf9a4
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/BUILD.out
@@ -0,0 +1,34 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_test_file_pattern *_hello.py,hello_*,unittest_*,*_unittest.py
+
+# Note that "foo_unittest.pyc" and "test_bar" files are ignored.
+
+py_library(
+ name = "test5_multiple_patterns",
+ srcs = [
+ "mylib.py",
+ "mylib2.py",
+ ],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "foo_hello",
+ srcs = ["foo_hello.py"],
+)
+
+py_test(
+ name = "foo_unittest",
+ srcs = ["foo_unittest.py"],
+)
+
+py_test(
+ name = "hello_foo",
+ srcs = ["hello_foo.py"],
+)
+
+py_test(
+ name = "unittest_foo",
+ srcs = ["unittest_foo.py"],
+)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_hello.py b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_hello.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_hello.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.py b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.pyc b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.pyc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/foo_unittest.pyc
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/hello_foo.py b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/hello_foo.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/hello_foo.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib.py b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib2.py b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib2.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/mylib2.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/test_bar b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/test_bar
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/test_bar
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/unittest_foo.py b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/unittest_foo.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test5_multiple_patterns/unittest_foo.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.in
new file mode 100644
index 0000000..2acff9b
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern *_unittest.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.out
new file mode 100644
index 0000000..7b9f557
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/BUILD.out
@@ -0,0 +1,15 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern *_unittest.py
+
+py_library(
+ name = "not_a_test",
+ srcs = ["not_a_test.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "hello_unittest",
+ srcs = ["hello_unittest.py"],
+)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/hello_unittest.py b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/hello_unittest.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/hello_unittest.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/not_a_test.py b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/not_a_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/not_a_test.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.in
new file mode 100644
index 0000000..cc91589
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern test_*.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.out
new file mode 100644
index 0000000..49107ee
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/BUILD.out
@@ -0,0 +1,21 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_generation_mode file
+# gazelle:python_test_file_pattern test_*.py
+
+py_library(
+ name = "not_a_test",
+ srcs = ["not_a_test.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_library(
+ name = "not_a_unittest",
+ srcs = ["not_a_unittest.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "test_bar",
+ srcs = ["test_bar.py"],
+)
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_test.py b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_test.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_unittest.py b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_unittest.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/not_a_unittest.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/test_bar.py b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/test_bar.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern/test6_nesting/subpkg/test_bar.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.in
new file mode 100644
index 0000000..19ed002
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.in
@@ -0,0 +1 @@
+# gazelle:python_test_file_pattern foo_*_[A-Z_test?.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.out
new file mode 100644
index 0000000..19ed002
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/BUILD.out
@@ -0,0 +1 @@
+# gazelle:python_test_file_pattern foo_*_[A-Z_test?.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/README.md b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/README.md
new file mode 100644
index 0000000..42ff635
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/README.md
@@ -0,0 +1,4 @@
+# Directive: `python_test_file_pattern`
+
+This test case asserts that the `# gazelle:python_test_file_pattern` directive
+fails with a nice message (rather than panicking) if the glob pattern is invalid.
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/WORKSPACE b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/WORKSPACE
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/test.yaml b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/test.yaml
new file mode 100644
index 0000000..6bae723
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_bad_glob/test.yaml
@@ -0,0 +1,19 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+---
+expect:
+ exit_code: 1
+ stderr: |
+ gazelle: invalid glob pattern 'foo_*_[A-Z_test?.py'
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.in b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.in
new file mode 100644
index 0000000..4e2b4cc
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.in
@@ -0,0 +1 @@
+# gazelle:python_test_file_pattern
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.out b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.out
new file mode 100644
index 0000000..4e2b4cc
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/BUILD.out
@@ -0,0 +1 @@
+# gazelle:python_test_file_pattern
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_no_value/README.md b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/README.md
new file mode 100644
index 0000000..2c38eb7
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/README.md
@@ -0,0 +1,8 @@
+# Directive: `python_test_file_pattern`
+
+This test case asserts that the `# gazelle:python_test_file_pattern` directive
+fails with a nice message if the directive has no value.
+
+See discussion in [PR #1819 (comment)][comment].
+
+[comment]: https://github.com/bazelbuild/rules_python/pull/1819#discussion_r1536906287
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_no_value/WORKSPACE b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/WORKSPACE
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_no_value/foo_test.py b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/foo_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/foo_test.py
diff --git a/gazelle/python/testdata/directive_python_test_file_pattern_no_value/test.yaml b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/test.yaml
new file mode 100644
index 0000000..8eaa659
--- /dev/null
+++ b/gazelle/python/testdata/directive_python_test_file_pattern_no_value/test.yaml
@@ -0,0 +1,19 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+---
+expect:
+ exit_code: 1
+ stderr: |
+ gazelle: directive 'python_test_file_pattern' requires a value
diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go
index a0bc9f6..726b145 100644
--- a/gazelle/pythonconfig/pythonconfig.go
+++ b/gazelle/pythonconfig/pythonconfig.go
@@ -74,6 +74,9 @@ const (
// visibility labels are added to generated targets. It mimics the behavior
// of the `go_visibility` directive.
Visibility = "python_visibility"
+ // TestFilePattern represents the directive that controls which python
+ // files are mapped to `py_test` targets.
+ TestFilePattern = "python_test_file_pattern"
)
// GenerationModeType represents one of the generation modes for the Python
@@ -96,9 +99,11 @@ const (
packageNameNamingConventionSubstitution = "$package_name$"
)
-// The default visibility label, including a format placeholder for `python_root`.
const (
+ // The default visibility label, including a format placeholder for `python_root`.
DefaultVisibilityFmtString = "//%s:__subpackages__"
+ // The default globs used to determine pt_test targets.
+ DefaultTestFilePatternString = "*_test.py,test_*.py"
)
// defaultIgnoreFiles is the list of default values used in the
@@ -150,6 +155,7 @@ type Config struct {
testNamingConvention string
defaultVisibility []string
visibility []string
+ testFilePattern []string
}
// New creates a new Config.
@@ -173,6 +179,7 @@ func New(
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
defaultVisibility: []string{fmt.Sprintf(DefaultVisibilityFmtString, "")},
visibility: []string{},
+ testFilePattern: strings.Split(DefaultTestFilePatternString, ","),
}
}
@@ -201,6 +208,7 @@ func (c *Config) NewChild() *Config {
testNamingConvention: c.testNamingConvention,
defaultVisibility: c.defaultVisibility,
visibility: c.visibility,
+ testFilePattern: c.testFilePattern,
}
}
@@ -425,3 +433,13 @@ func (c *Config) SetDefaultVisibility(visibility []string) {
func (c *Config) DefaultVisibilty() []string {
return c.defaultVisibility
}
+
+// SetTestFilePattern sets the file patterns that should be mapped to 'py_test' rules.
+func (c *Config) SetTestFilePattern(patterns []string) {
+ c.testFilePattern = patterns
+}
+
+// TestFilePattern returns the patterns that should be mapped to 'py_test' rules.
+func (c *Config) TestFilePattern() []string {
+ return c.testFilePattern
+}