From daa4d4df6d4c9ec5bbca0983fc9c76ddd5758d82 Mon Sep 17 00:00:00 2001 From: Zhongpeng Lin Date: Sat, 6 Jul 2024 17:37:09 -0700 Subject: [PATCH 1/6] Following generation mode when generating test targets --- gazelle/python/configure.go | 9 +++++++ gazelle/python/generate.go | 7 ++++-- .../annotation_include_dep/subpkg/BUILD.out | 2 +- .../binary_without_entrypoint/BUILD.out | 2 +- .../test2_star_test_py/BUILD.out | 2 +- .../test5_multiple_patterns/BUILD.out | 24 ++++++------------- .../python/testdata/multiple_tests/BUILD.in | 2 +- .../python/testdata/multiple_tests/BUILD.out | 12 ++++------ .../python/testdata/multiple_tests/README.md | 2 +- .../python_target_with_test_in_name/BUILD.in | 1 + .../python_target_with_test_in_name/BUILD.out | 8 ++++--- .../testdata/sibling_imports/pkg/BUILD.out | 15 ++++-------- gazelle/pythonconfig/pythonconfig.go | 22 +++++++++++++++-- 13 files changed, 62 insertions(+), 46 deletions(-) diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go index b82dd81f8f..c6b5fbf393 100644 --- a/gazelle/python/configure.go +++ b/gazelle/python/configure.go @@ -61,6 +61,7 @@ func (py *Configurer) KnownDirectives() []string { pythonconfig.ValidateImportStatementsDirective, pythonconfig.GenerationMode, pythonconfig.GenerationModePerFileIncludeInit, + pythonconfig.TestGenerationMode, pythonconfig.LibraryNamingConvention, pythonconfig.BinaryNamingConvention, pythonconfig.TestNamingConvention, @@ -163,6 +164,14 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) { log.Fatal(err) } config.SetPerFileGenerationIncludeInit(v) + case pythonconfig.TestGenerationMode: + testGenerationMode := strings.TrimSpace(d.Value) + switch testGenerationMode { + case pythonconfig.TestGenerationModeAuto, pythonconfig.TestGenerationModeFile, pythonconfig.TestGenerationModePackage: + config.SetTestGenerationMode(testGenerationMode) + default: + log.Printf("invalid value for diretive %q at %q: %q", pythonconfig.TestGenerationMode, rel, testGenerationMode) + } case pythonconfig.LibraryNamingConvention: config.SetLibraryNamingConvention(strings.TrimSpace(d.Value)) case pythonconfig.BinaryNamingConvention: diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go index ef49dd74c4..6bac716666 100644 --- a/gazelle/python/generate.go +++ b/gazelle/python/generate.go @@ -421,7 +421,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes addResolvedDependencies(annotations.includeDeps). generateImportsAttribute() } - if (hasPyTestEntryPointFile || hasPyTestEntryPointTarget || cfg.CoarseGrainedGeneration()) && !cfg.PerFileGeneration() { + if !cfg.PerFileGeneration() { // Create one py_test target per package if hasPyTestEntryPointFile { // Only add the pyTestEntrypointFilename to the pyTestFilenames if @@ -441,7 +441,10 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes setMain(main) } else if hasPyTestEntryPointFile { pyTestTarget.setMain(pyTestEntrypointFilename) - } + } /* else: + main is not set, assuming there is a test file with the same name + as the target name, or there is a macro wrapping py_test and setting its main attribute. + */ pyTestTargets = append(pyTestTargets, pyTestTarget) } } else { diff --git a/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out b/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out index 921c892889..e574e979c3 100644 --- a/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out +++ b/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out @@ -20,7 +20,7 @@ py_library( ) py_test( - name = "module1_test", + name = "subpkg_test", srcs = ["module1_test.py"], deps = [ ":subpkg", diff --git a/gazelle/python/testdata/binary_without_entrypoint/BUILD.out b/gazelle/python/testdata/binary_without_entrypoint/BUILD.out index 9af815286b..72f498f89e 100644 --- a/gazelle/python/testdata/binary_without_entrypoint/BUILD.out +++ b/gazelle/python/testdata/binary_without_entrypoint/BUILD.out @@ -42,6 +42,6 @@ py_library( ) py_test( - name = "main_test", + name = "binary_without_entrypoint_test", srcs = ["main_test.py"], ) \ No newline at end of file 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 index be5917b356..b4d9190c67 100644 --- 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 @@ -12,6 +12,6 @@ py_library( ) py_test( - name = "hello_test", + name = "test2_star_test_py_test", srcs = ["hello_test.py"], ) 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 index 1dcf9a4554..20eb547944 100644 --- 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 @@ -14,21 +14,11 @@ py_library( ) 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"], + name = "test5_multiple_patterns_test", + srcs = [ + "foo_hello.py", + "foo_unittest.py", + "hello_foo.py", + "unittest_foo.py", + ], ) diff --git a/gazelle/python/testdata/multiple_tests/BUILD.in b/gazelle/python/testdata/multiple_tests/BUILD.in index 9e84e5dc32..b10d786268 100644 --- a/gazelle/python/testdata/multiple_tests/BUILD.in +++ b/gazelle/python/testdata/multiple_tests/BUILD.in @@ -7,6 +7,6 @@ py_library( ) py_test( - name = "bar_test", + name = "multiple_tests_test", srcs = ["bar_test.py"], ) diff --git a/gazelle/python/testdata/multiple_tests/BUILD.out b/gazelle/python/testdata/multiple_tests/BUILD.out index fd67724e3b..57b2da8ab3 100644 --- a/gazelle/python/testdata/multiple_tests/BUILD.out +++ b/gazelle/python/testdata/multiple_tests/BUILD.out @@ -7,11 +7,9 @@ py_library( ) py_test( - name = "bar_test", - srcs = ["bar_test.py"], -) - -py_test( - name = "foo_test", - srcs = ["foo_test.py"], + name = "multiple_tests_test", + srcs = [ + "bar_test.py", + "foo_test.py", + ], ) diff --git a/gazelle/python/testdata/multiple_tests/README.md b/gazelle/python/testdata/multiple_tests/README.md index 8220f6112d..1af7133518 100644 --- a/gazelle/python/testdata/multiple_tests/README.md +++ b/gazelle/python/testdata/multiple_tests/README.md @@ -1,3 +1,3 @@ # Multiple tests -This test case asserts that a second `py_test` rule is correctly created when a second `*_test.py` file is added to a package with an existing `py_test` rule. +This test case asserts that a new `*_test.py` file is added to a package with an existing `py_test` rule. diff --git a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in index e69de29bb2..9358e2ff65 100644 --- a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in +++ b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in @@ -0,0 +1 @@ +# gazelle:python_generation_mode file \ No newline at end of file diff --git a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out index 32e899b9e8..3372760362 100644 --- a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out +++ b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out @@ -1,7 +1,9 @@ load("@rules_python//python:defs.bzl", "py_library", "py_test") +# gazelle:python_generation_mode file + py_library( - name = "python_target_with_test_in_name", + name = "__init__", srcs = ["__init__.py"], visibility = ["//:__subpackages__"], ) @@ -10,7 +12,7 @@ py_test( name = "real_test", srcs = ["real_test.py"], deps = [ - ":python_target_with_test_in_name", + ":__init__", "@gazelle_python_test//boto3", ], ) @@ -18,5 +20,5 @@ py_test( py_test( name = "test_reality", srcs = ["test_reality.py"], - deps = [":python_target_with_test_in_name"], + deps = [":__init__"], ) diff --git a/gazelle/python/testdata/sibling_imports/pkg/BUILD.out b/gazelle/python/testdata/sibling_imports/pkg/BUILD.out index cae6c3f17a..0ec890540e 100644 --- a/gazelle/python/testdata/sibling_imports/pkg/BUILD.out +++ b/gazelle/python/testdata/sibling_imports/pkg/BUILD.out @@ -11,16 +11,11 @@ py_library( ) py_test( - name = "test_util", - srcs = ["test_util.py"], -) - -py_test( - name = "unit_test", - srcs = ["unit_test.py"], - deps = [ - ":pkg", - ":test_util", + name = "pkg_test", + srcs = [ + "test_util.py", + "unit_test.py", ], + deps = [":pkg"], ) diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go index 41a470a940..2f85465798 100644 --- a/gazelle/pythonconfig/pythonconfig.go +++ b/gazelle/pythonconfig/pythonconfig.go @@ -55,6 +55,7 @@ const ( // the "per_file" GenerationMode by including the package's __init__.py file. // This is a boolean directive. GenerationModePerFileIncludeInit = "python_generation_mode_per_file_include_init" + TestGenerationMode = "python_test_generation_mode" // LibraryNamingConvention represents the directive that controls the // py_library naming convention. It interpolates $package_name$ with the // Bazel package name. E.g. if the Bazel package name is `foo`, setting this @@ -103,6 +104,12 @@ const ( GenerationModeFile GenerationModeType = "file" ) +const ( + TestGenerationModeAuto = "auto" + TestGenerationModePackage = "package" + TestGenerationModeFile = "file" +) + const ( packageNameNamingConventionSubstitution = "$package_name$" distributionNameLabelConventionSubstitution = "$distribution_name$" @@ -161,6 +168,7 @@ type Config struct { defaultVisibility []string visibility []string testFilePattern []string + testGenerationMode string labelConvention string labelNormalization LabelNormalizationType } @@ -195,6 +203,7 @@ func New( defaultVisibility: []string{fmt.Sprintf(DefaultVisibilityFmtString, "")}, visibility: []string{}, testFilePattern: strings.Split(DefaultTestFilePatternString, ","), + testGenerationMode: TestGenerationModeAuto, labelConvention: DefaultLabelConvention, labelNormalization: DefaultLabelNormalizationType, } @@ -226,6 +235,7 @@ func (c *Config) NewChild() *Config { defaultVisibility: c.defaultVisibility, visibility: c.visibility, testFilePattern: c.testFilePattern, + testGenerationMode: c.testGenerationMode, labelConvention: c.labelConvention, labelNormalization: c.labelNormalization, } @@ -461,6 +471,14 @@ func (c *Config) TestFilePattern() []string { return c.testFilePattern } +func (c *Config) SetTestGenerationMode(value string) { + c.testGenerationMode = value +} + +func (c *Config) TestGenerationMode() string { + return c.testGenerationMode +} + // SetLabelConvention sets the label convention used for third-party dependencies. func (c *Config) SetLabelConvention(convention string) { c.labelConvention = convention @@ -494,9 +512,9 @@ func (c *Config) FormatThirdPartyDependency(repositoryName string, distributionN normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "_") case Pep503LabelNormalizationType: // See https://packaging.python.org/en/latest/specifications/name-normalization/#name-format - normConventionalDistributionName = strings.ToLower(conventionalDistributionName) // ... "should be lowercased" + normConventionalDistributionName = strings.ToLower(conventionalDistributionName) // ... "should be lowercased" normConventionalDistributionName = regexp.MustCompile(`[-_.]+`).ReplaceAllString(normConventionalDistributionName, "-") // ... "all runs of the characters ., -, or _ replaced with a single -" - normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "-") // ... "must start and end with a letter or number" + normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "-") // ... "must start and end with a letter or number" default: fallthrough case NoLabelNormalizationType: From 630d83b5be2f1bffdf3118785202c81230038a92 Mon Sep 17 00:00:00 2001 From: Zhongpeng Lin Date: Sat, 6 Jul 2024 17:37:57 -0700 Subject: [PATCH 2/6] reverting changes to confg --- gazelle/python/configure.go | 9 --------- gazelle/pythonconfig/pythonconfig.go | 22 ++-------------------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go index c6b5fbf393..b82dd81f8f 100644 --- a/gazelle/python/configure.go +++ b/gazelle/python/configure.go @@ -61,7 +61,6 @@ func (py *Configurer) KnownDirectives() []string { pythonconfig.ValidateImportStatementsDirective, pythonconfig.GenerationMode, pythonconfig.GenerationModePerFileIncludeInit, - pythonconfig.TestGenerationMode, pythonconfig.LibraryNamingConvention, pythonconfig.BinaryNamingConvention, pythonconfig.TestNamingConvention, @@ -164,14 +163,6 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) { log.Fatal(err) } config.SetPerFileGenerationIncludeInit(v) - case pythonconfig.TestGenerationMode: - testGenerationMode := strings.TrimSpace(d.Value) - switch testGenerationMode { - case pythonconfig.TestGenerationModeAuto, pythonconfig.TestGenerationModeFile, pythonconfig.TestGenerationModePackage: - config.SetTestGenerationMode(testGenerationMode) - default: - log.Printf("invalid value for diretive %q at %q: %q", pythonconfig.TestGenerationMode, rel, testGenerationMode) - } case pythonconfig.LibraryNamingConvention: config.SetLibraryNamingConvention(strings.TrimSpace(d.Value)) case pythonconfig.BinaryNamingConvention: diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go index 2f85465798..41a470a940 100644 --- a/gazelle/pythonconfig/pythonconfig.go +++ b/gazelle/pythonconfig/pythonconfig.go @@ -55,7 +55,6 @@ const ( // the "per_file" GenerationMode by including the package's __init__.py file. // This is a boolean directive. GenerationModePerFileIncludeInit = "python_generation_mode_per_file_include_init" - TestGenerationMode = "python_test_generation_mode" // LibraryNamingConvention represents the directive that controls the // py_library naming convention. It interpolates $package_name$ with the // Bazel package name. E.g. if the Bazel package name is `foo`, setting this @@ -104,12 +103,6 @@ const ( GenerationModeFile GenerationModeType = "file" ) -const ( - TestGenerationModeAuto = "auto" - TestGenerationModePackage = "package" - TestGenerationModeFile = "file" -) - const ( packageNameNamingConventionSubstitution = "$package_name$" distributionNameLabelConventionSubstitution = "$distribution_name$" @@ -168,7 +161,6 @@ type Config struct { defaultVisibility []string visibility []string testFilePattern []string - testGenerationMode string labelConvention string labelNormalization LabelNormalizationType } @@ -203,7 +195,6 @@ func New( defaultVisibility: []string{fmt.Sprintf(DefaultVisibilityFmtString, "")}, visibility: []string{}, testFilePattern: strings.Split(DefaultTestFilePatternString, ","), - testGenerationMode: TestGenerationModeAuto, labelConvention: DefaultLabelConvention, labelNormalization: DefaultLabelNormalizationType, } @@ -235,7 +226,6 @@ func (c *Config) NewChild() *Config { defaultVisibility: c.defaultVisibility, visibility: c.visibility, testFilePattern: c.testFilePattern, - testGenerationMode: c.testGenerationMode, labelConvention: c.labelConvention, labelNormalization: c.labelNormalization, } @@ -471,14 +461,6 @@ func (c *Config) TestFilePattern() []string { return c.testFilePattern } -func (c *Config) SetTestGenerationMode(value string) { - c.testGenerationMode = value -} - -func (c *Config) TestGenerationMode() string { - return c.testGenerationMode -} - // SetLabelConvention sets the label convention used for third-party dependencies. func (c *Config) SetLabelConvention(convention string) { c.labelConvention = convention @@ -512,9 +494,9 @@ func (c *Config) FormatThirdPartyDependency(repositoryName string, distributionN normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "_") case Pep503LabelNormalizationType: // See https://packaging.python.org/en/latest/specifications/name-normalization/#name-format - normConventionalDistributionName = strings.ToLower(conventionalDistributionName) // ... "should be lowercased" + normConventionalDistributionName = strings.ToLower(conventionalDistributionName) // ... "should be lowercased" normConventionalDistributionName = regexp.MustCompile(`[-_.]+`).ReplaceAllString(normConventionalDistributionName, "-") // ... "all runs of the characters ., -, or _ replaced with a single -" - normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "-") // ... "must start and end with a letter or number" + normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "-") // ... "must start and end with a letter or number" default: fallthrough case NoLabelNormalizationType: From b52129202c85dc0e3b684bab365ff6d615c9a102 Mon Sep 17 00:00:00 2001 From: Zhongpeng Lin Date: Tue, 9 Jul 2024 09:26:32 -0700 Subject: [PATCH 3/6] gate the new behavior with a directive --- gazelle/python/configure.go | 9 ++ gazelle/python/generate.go | 2 +- .../annotation_include_dep/subpkg/BUILD.out | 2 +- .../binary_without_entrypoint/BUILD.out | 2 +- .../test2_star_test_py/BUILD.out | 2 +- .../test5_multiple_patterns/BUILD.out | 24 +++-- .../python/testdata/multiple_tests/BUILD.in | 2 +- .../python/testdata/multiple_tests/BUILD.out | 12 ++- .../python/testdata/multiple_tests/README.md | 2 +- .../python_target_with_test_in_name/BUILD.in | 1 - .../python_target_with_test_in_name/BUILD.out | 8 +- .../testdata/sibling_imports/pkg/BUILD.out | 15 ++- gazelle/pythonconfig/pythonconfig.go | 101 ++++++++++-------- 13 files changed, 110 insertions(+), 72 deletions(-) diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go index b82dd81f8f..a369a64b8e 100644 --- a/gazelle/python/configure.go +++ b/gazelle/python/configure.go @@ -61,6 +61,7 @@ func (py *Configurer) KnownDirectives() []string { pythonconfig.ValidateImportStatementsDirective, pythonconfig.GenerationMode, pythonconfig.GenerationModePerFileIncludeInit, + pythonconfig.GenerationModePerPackageRequireTestEntryPoint, pythonconfig.LibraryNamingConvention, pythonconfig.BinaryNamingConvention, pythonconfig.TestNamingConvention, @@ -163,6 +164,14 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) { log.Fatal(err) } config.SetPerFileGenerationIncludeInit(v) + case pythonconfig.GenerationModePerPackageRequireTestEntryPoint: + v, err := strconv.ParseBool(strings.TrimSpace(d.Value)) + if err != nil { + log.Printf("invalid value for gazelle:%s in %q: %q", + pythonconfig.GenerationModePerPackageRequireTestEntryPoint, rel, d.Value) + } else { + config.SetPerPackageGenerationRequireTestEntryPoint(v) + } case pythonconfig.LibraryNamingConvention: config.SetLibraryNamingConvention(strings.TrimSpace(d.Value)) case pythonconfig.BinaryNamingConvention: diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go index 6bac716666..c563b47bf3 100644 --- a/gazelle/python/generate.go +++ b/gazelle/python/generate.go @@ -421,7 +421,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes addResolvedDependencies(annotations.includeDeps). generateImportsAttribute() } - if !cfg.PerFileGeneration() { + if (!cfg.PerPackageGenerationRequireTestEntryPoint() || hasPyTestEntryPointFile || hasPyTestEntryPointTarget || cfg.CoarseGrainedGeneration()) && !cfg.PerFileGeneration() { // Create one py_test target per package if hasPyTestEntryPointFile { // Only add the pyTestEntrypointFilename to the pyTestFilenames if diff --git a/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out b/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out index e574e979c3..921c892889 100644 --- a/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out +++ b/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out @@ -20,7 +20,7 @@ py_library( ) py_test( - name = "subpkg_test", + name = "module1_test", srcs = ["module1_test.py"], deps = [ ":subpkg", diff --git a/gazelle/python/testdata/binary_without_entrypoint/BUILD.out b/gazelle/python/testdata/binary_without_entrypoint/BUILD.out index 72f498f89e..9af815286b 100644 --- a/gazelle/python/testdata/binary_without_entrypoint/BUILD.out +++ b/gazelle/python/testdata/binary_without_entrypoint/BUILD.out @@ -42,6 +42,6 @@ py_library( ) py_test( - name = "binary_without_entrypoint_test", + name = "main_test", srcs = ["main_test.py"], ) \ No newline at end of file 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 index b4d9190c67..be5917b356 100644 --- 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 @@ -12,6 +12,6 @@ py_library( ) py_test( - name = "test2_star_test_py_test", + name = "hello_test", srcs = ["hello_test.py"], ) 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 index 20eb547944..1dcf9a4554 100644 --- 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 @@ -14,11 +14,21 @@ py_library( ) py_test( - name = "test5_multiple_patterns_test", - srcs = [ - "foo_hello.py", - "foo_unittest.py", - "hello_foo.py", - "unittest_foo.py", - ], + 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/multiple_tests/BUILD.in b/gazelle/python/testdata/multiple_tests/BUILD.in index b10d786268..9e84e5dc32 100644 --- a/gazelle/python/testdata/multiple_tests/BUILD.in +++ b/gazelle/python/testdata/multiple_tests/BUILD.in @@ -7,6 +7,6 @@ py_library( ) py_test( - name = "multiple_tests_test", + name = "bar_test", srcs = ["bar_test.py"], ) diff --git a/gazelle/python/testdata/multiple_tests/BUILD.out b/gazelle/python/testdata/multiple_tests/BUILD.out index 57b2da8ab3..fd67724e3b 100644 --- a/gazelle/python/testdata/multiple_tests/BUILD.out +++ b/gazelle/python/testdata/multiple_tests/BUILD.out @@ -7,9 +7,11 @@ py_library( ) py_test( - name = "multiple_tests_test", - srcs = [ - "bar_test.py", - "foo_test.py", - ], + name = "bar_test", + srcs = ["bar_test.py"], +) + +py_test( + name = "foo_test", + srcs = ["foo_test.py"], ) diff --git a/gazelle/python/testdata/multiple_tests/README.md b/gazelle/python/testdata/multiple_tests/README.md index 1af7133518..8220f6112d 100644 --- a/gazelle/python/testdata/multiple_tests/README.md +++ b/gazelle/python/testdata/multiple_tests/README.md @@ -1,3 +1,3 @@ # Multiple tests -This test case asserts that a new `*_test.py` file is added to a package with an existing `py_test` rule. +This test case asserts that a second `py_test` rule is correctly created when a second `*_test.py` file is added to a package with an existing `py_test` rule. diff --git a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in index 9358e2ff65..e69de29bb2 100644 --- a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in +++ b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in @@ -1 +0,0 @@ -# gazelle:python_generation_mode file \ No newline at end of file diff --git a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out index 3372760362..32e899b9e8 100644 --- a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out +++ b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out @@ -1,9 +1,7 @@ load("@rules_python//python:defs.bzl", "py_library", "py_test") -# gazelle:python_generation_mode file - py_library( - name = "__init__", + name = "python_target_with_test_in_name", srcs = ["__init__.py"], visibility = ["//:__subpackages__"], ) @@ -12,7 +10,7 @@ py_test( name = "real_test", srcs = ["real_test.py"], deps = [ - ":__init__", + ":python_target_with_test_in_name", "@gazelle_python_test//boto3", ], ) @@ -20,5 +18,5 @@ py_test( py_test( name = "test_reality", srcs = ["test_reality.py"], - deps = [":__init__"], + deps = [":python_target_with_test_in_name"], ) diff --git a/gazelle/python/testdata/sibling_imports/pkg/BUILD.out b/gazelle/python/testdata/sibling_imports/pkg/BUILD.out index 0ec890540e..cae6c3f17a 100644 --- a/gazelle/python/testdata/sibling_imports/pkg/BUILD.out +++ b/gazelle/python/testdata/sibling_imports/pkg/BUILD.out @@ -11,11 +11,16 @@ py_library( ) py_test( - name = "pkg_test", - srcs = [ - "test_util.py", - "unit_test.py", + name = "test_util", + srcs = ["test_util.py"], +) + +py_test( + name = "unit_test", + srcs = ["unit_test.py"], + deps = [ + ":pkg", + ":test_util", ], - deps = [":pkg"], ) diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go index 41a470a940..a24a90efeb 100644 --- a/gazelle/pythonconfig/pythonconfig.go +++ b/gazelle/pythonconfig/pythonconfig.go @@ -55,6 +55,10 @@ const ( // the "per_file" GenerationMode by including the package's __init__.py file. // This is a boolean directive. GenerationModePerFileIncludeInit = "python_generation_mode_per_file_include_init" + // GenerationModePerPackageRequireTestEntryPoint represents the directive that + // requires a test entry point to generate test targets in "package" GenerationMode. + // This is a boolean directive. + GenerationModePerPackageRequireTestEntryPoint = "python_generation_mode_per_package_require_test_entry_point" // LibraryNamingConvention represents the directive that controls the // py_library naming convention. It interpolates $package_name$ with the // Bazel package name. E.g. if the Bazel package name is `foo`, setting this @@ -148,21 +152,22 @@ type Config struct { pythonProjectRoot string gazelleManifest *manifest.Manifest - excludedPatterns *singlylinkedlist.List - ignoreFiles map[string]struct{} - ignoreDependencies map[string]struct{} - validateImportStatements bool - coarseGrainedGeneration bool - perFileGeneration bool - perFileGenerationIncludeInit bool - libraryNamingConvention string - binaryNamingConvention string - testNamingConvention string - defaultVisibility []string - visibility []string - testFilePattern []string - labelConvention string - labelNormalization LabelNormalizationType + excludedPatterns *singlylinkedlist.List + ignoreFiles map[string]struct{} + ignoreDependencies map[string]struct{} + validateImportStatements bool + coarseGrainedGeneration bool + perFileGeneration bool + perFileGenerationIncludeInit bool + perPackageGenerationRequireTestEntryPoint bool + libraryNamingConvention string + binaryNamingConvention string + testNamingConvention string + defaultVisibility []string + visibility []string + testFilePattern []string + labelConvention string + labelNormalization LabelNormalizationType } type LabelNormalizationType int @@ -179,24 +184,25 @@ func New( pythonProjectRoot string, ) *Config { return &Config{ - extensionEnabled: true, - repoRoot: repoRoot, - pythonProjectRoot: pythonProjectRoot, - excludedPatterns: singlylinkedlist.New(), - ignoreFiles: make(map[string]struct{}), - ignoreDependencies: make(map[string]struct{}), - validateImportStatements: true, - coarseGrainedGeneration: false, - perFileGeneration: false, - perFileGenerationIncludeInit: false, - libraryNamingConvention: packageNameNamingConventionSubstitution, - binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution), - testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution), - defaultVisibility: []string{fmt.Sprintf(DefaultVisibilityFmtString, "")}, - visibility: []string{}, - testFilePattern: strings.Split(DefaultTestFilePatternString, ","), - labelConvention: DefaultLabelConvention, - labelNormalization: DefaultLabelNormalizationType, + extensionEnabled: true, + repoRoot: repoRoot, + pythonProjectRoot: pythonProjectRoot, + excludedPatterns: singlylinkedlist.New(), + ignoreFiles: make(map[string]struct{}), + ignoreDependencies: make(map[string]struct{}), + validateImportStatements: true, + coarseGrainedGeneration: false, + perFileGeneration: false, + perFileGenerationIncludeInit: false, + perPackageGenerationRequireTestEntryPoint: true, + libraryNamingConvention: packageNameNamingConventionSubstitution, + binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution), + testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution), + defaultVisibility: []string{fmt.Sprintf(DefaultVisibilityFmtString, "")}, + visibility: []string{}, + testFilePattern: strings.Split(DefaultTestFilePatternString, ","), + labelConvention: DefaultLabelConvention, + labelNormalization: DefaultLabelNormalizationType, } } @@ -220,14 +226,15 @@ func (c *Config) NewChild() *Config { coarseGrainedGeneration: c.coarseGrainedGeneration, perFileGeneration: c.perFileGeneration, perFileGenerationIncludeInit: c.perFileGenerationIncludeInit, - libraryNamingConvention: c.libraryNamingConvention, - binaryNamingConvention: c.binaryNamingConvention, - testNamingConvention: c.testNamingConvention, - defaultVisibility: c.defaultVisibility, - visibility: c.visibility, - testFilePattern: c.testFilePattern, - labelConvention: c.labelConvention, - labelNormalization: c.labelNormalization, + perPackageGenerationRequireTestEntryPoint: c.perPackageGenerationRequireTestEntryPoint, + libraryNamingConvention: c.libraryNamingConvention, + binaryNamingConvention: c.binaryNamingConvention, + testNamingConvention: c.testNamingConvention, + defaultVisibility: c.defaultVisibility, + visibility: c.visibility, + testFilePattern: c.testFilePattern, + labelConvention: c.labelConvention, + labelNormalization: c.labelNormalization, } } @@ -398,6 +405,14 @@ func (c *Config) PerFileGenerationIncludeInit() bool { return c.perFileGenerationIncludeInit } +func (c *Config) SetPerPackageGenerationRequireTestEntryPoint(perPackageGenerationRequireTestEntryPoint bool) { + c.perPackageGenerationRequireTestEntryPoint = perPackageGenerationRequireTestEntryPoint +} + +func (c *Config) PerPackageGenerationRequireTestEntryPoint() bool { + return c.perPackageGenerationRequireTestEntryPoint +} + // SetLibraryNamingConvention sets the py_library target naming convention. func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) { c.libraryNamingConvention = libraryNamingConvention @@ -494,9 +509,9 @@ func (c *Config) FormatThirdPartyDependency(repositoryName string, distributionN normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "_") case Pep503LabelNormalizationType: // See https://packaging.python.org/en/latest/specifications/name-normalization/#name-format - normConventionalDistributionName = strings.ToLower(conventionalDistributionName) // ... "should be lowercased" + normConventionalDistributionName = strings.ToLower(conventionalDistributionName) // ... "should be lowercased" normConventionalDistributionName = regexp.MustCompile(`[-_.]+`).ReplaceAllString(normConventionalDistributionName, "-") // ... "all runs of the characters ., -, or _ replaced with a single -" - normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "-") // ... "must start and end with a letter or number" + normConventionalDistributionName = strings.Trim(normConventionalDistributionName, "-") // ... "must start and end with a letter or number" default: fallthrough case NoLabelNormalizationType: From d8e6df9fd9d88f5822f988cee3fe56ba81c38f25 Mon Sep 17 00:00:00 2001 From: Zhongpeng Lin Date: Tue, 9 Jul 2024 09:40:36 -0700 Subject: [PATCH 4/6] add tests --- .../BUILD.in | 2 ++ .../BUILD.out | 18 ++++++++++++++ .../README.md | 3 +++ .../WORKSPACE | 1 + .../__init__.py | 0 .../bar_test.py | 24 +++++++++++++++++++ .../foo_test.py | 24 +++++++++++++++++++ .../test.yaml | 17 +++++++++++++ 8 files changed, 89 insertions(+) create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.in create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.out create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/README.md create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/WORKSPACE create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/__init__.py create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/bar_test.py create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/foo_test.py create mode 100644 gazelle/python/testdata/per_package_test_target_without_entry_point/test.yaml diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.in b/gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.in new file mode 100644 index 0000000000..27120f3255 --- /dev/null +++ b/gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.in @@ -0,0 +1,2 @@ +# gazelle:python_generation_mode package +# gazelle:python_generation_mode_per_package_require_test_entry_point false \ No newline at end of file diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.out b/gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.out new file mode 100644 index 0000000000..c4ec331583 --- /dev/null +++ b/gazelle/python/testdata/per_package_test_target_without_entry_point/BUILD.out @@ -0,0 +1,18 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +# gazelle:python_generation_mode package +# gazelle:python_generation_mode_per_package_require_test_entry_point false + +py_library( + name = "per_package_test_target_without_entry_point", + srcs = ["__init__.py"], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "per_package_test_target_without_entry_point_test", + srcs = [ + "bar_test.py", + "foo_test.py", + ], +) diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/README.md b/gazelle/python/testdata/per_package_test_target_without_entry_point/README.md new file mode 100644 index 0000000000..8decb00cfa --- /dev/null +++ b/gazelle/python/testdata/per_package_test_target_without_entry_point/README.md @@ -0,0 +1,3 @@ +# One test target per package without entry point + +This test case asserts that one test target is generated per package without entry point when `gazelle:python_generation_mode_per_package_require_test_entry_point false` diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/WORKSPACE b/gazelle/python/testdata/per_package_test_target_without_entry_point/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/per_package_test_target_without_entry_point/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/__init__.py b/gazelle/python/testdata/per_package_test_target_without_entry_point/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/bar_test.py b/gazelle/python/testdata/per_package_test_target_without_entry_point/bar_test.py new file mode 100644 index 0000000000..9948f1ccd4 --- /dev/null +++ b/gazelle/python/testdata/per_package_test_target_without_entry_point/bar_test.py @@ -0,0 +1,24 @@ +# 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. + +import unittest + + +class BarTest(unittest.TestCase): + def test_foo(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/foo_test.py b/gazelle/python/testdata/per_package_test_target_without_entry_point/foo_test.py new file mode 100644 index 0000000000..a128adf67f --- /dev/null +++ b/gazelle/python/testdata/per_package_test_target_without_entry_point/foo_test.py @@ -0,0 +1,24 @@ +# 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. + +import unittest + + +class FooTest(unittest.TestCase): + def test_foo(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/per_package_test_target_without_entry_point/test.yaml b/gazelle/python/testdata/per_package_test_target_without_entry_point/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/per_package_test_target_without_entry_point/test.yaml @@ -0,0 +1,17 @@ +# 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: 0 From f6671cf9ed0f4f1bd83f36a05f7fc34fbd05cf50 Mon Sep 17 00:00:00 2001 From: Zhongpeng Lin Date: Tue, 9 Jul 2024 10:19:01 -0700 Subject: [PATCH 5/6] updated readme --- gazelle/README.md | 99 ++++++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/gazelle/README.md b/gazelle/README.md index d68b94de26..c0494d141b 100644 --- a/gazelle/README.md +++ b/gazelle/README.md @@ -172,42 +172,44 @@ Examples of these directives in use can be found in the Python-specific directives are as follows: -| **Directive** | **Default value** | -|--------------------------------------|-------------------| -| `# gazelle:python_extension` | `enabled` | -| Controls whether the Python extension is enabled or not. Sub-packages inherit this value. Can be either "enabled" or "disabled". | | -| [`# gazelle:python_root`](#directive-python_root) | n/a | -| Sets a Bazel package as a Python root. This is used on monorepos with multiple Python projects that don't share the top-level of the workspace as the root. See [Directive: `python_root`](#directive-python_root) below. | | -| `# gazelle:python_manifest_file_name`| `gazelle_python.yaml` | -| Overrides the default manifest file name. | | -| `# gazelle:python_ignore_files` | n/a | -| Controls the files which are ignored from the generated targets. | | -| `# gazelle:python_ignore_dependencies`| n/a | -| Controls the ignored dependencies from the generated targets. | | -| `# gazelle:python_validate_import_statements`| `true` | -| Controls whether the Python import statements should be validated. Can be "true" or "false" | | -| `# gazelle:python_generation_mode`| `package` | -| Controls the target generation mode. Can be "file", "package", or "project" | | -| `# gazelle:python_generation_mode_per_file_include_init`| `false` | -| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | | -| `# gazelle:python_library_naming_convention`| `$package_name$` | -| Controls the `py_library` naming convention. It interpolates `$package_name$` with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | | -| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` | -| Controls the `py_binary` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | | -| `# gazelle:python_test_naming_convention` | `$package_name$_test` | -| Controls the `py_test` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | | -| `# gazelle:resolve py ...` | n/a | +| **Directive** | **Default value** | +|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------| +| `# gazelle:python_extension` | `enabled` | +| Controls whether the Python extension is enabled or not. Sub-packages inherit this value. Can be either "enabled" or "disabled". | | +| [`# gazelle:python_root`](#directive-python_root) | n/a | +| Sets a Bazel package as a Python root. This is used on monorepos with multiple Python projects that don't share the top-level of the workspace as the root. See [Directive: `python_root`](#directive-python_root) below. | | +| `# gazelle:python_manifest_file_name` | `gazelle_python.yaml` | +| Overrides the default manifest file name. | | +| `# gazelle:python_ignore_files` | n/a | +| Controls the files which are ignored from the generated targets. | | +| `# gazelle:python_ignore_dependencies` | n/a | +| Controls the ignored dependencies from the generated targets. | | +| `# gazelle:python_validate_import_statements` | `true` | +| Controls whether the Python import statements should be validated. Can be "true" or "false" | | +| `# gazelle:python_generation_mode` | `package` | +| Controls the target generation mode. Can be "file", "package", or "project" | | +| `# gazelle:python_generation_mode_per_file_include_init` | `false` | +| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | | +| [`# gazelle:python_generation_mode_per_package_require_test_entry_point`](#directive-python_generation_mode_per_package_require_test_entry_point) | `true` | +| Controls whether a file called `__test__.py` or a target called `__test__` is required to generate one test target per package in package mode. || +| `# gazelle:python_library_naming_convention` | `$package_name$` | +| Controls the `py_library` naming convention. It interpolates `$package_name$` with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | | +| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` | +| Controls the `py_binary` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | | +| `# gazelle:python_test_naming_convention` | `$package_name$_test` | +| Controls the `py_test` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | | +| `# gazelle:resolve py ...` | n/a | | Instructs the plugin what target to add as a dependency to satisfy a given import statement. The syntax is `# gazelle:resolve py import-string label` where `import-string` is the symbol in the python `import` statement, and `label` is the Bazel label that Gazelle should write in `deps`. | | -| [`# gazelle:python_default_visibility labels`](#directive-python_default_visibility) | | -| 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. | -| `# gazelle:python_label_convention` | `$distribution_name$` | -| Defines the format of the distribution name in labels to third-party deps. Useful for using Gazelle plugin with other rules with different repository conventions (e.g. `rules_pycross`). Full label is always prepended with (pip) repository name, e.g. `@pip//numpy`. | -| `# gazelle:python_label_normalization` | `snake_case` | -| Controls how distribution names in labels to third-party deps are normalized. Useful for using Gazelle plugin with other rules with different label conventions (e.g. `rules_pycross` uses PEP-503). Can be "snake_case", "none", or "pep503". | +| [`# gazelle:python_default_visibility labels`](#directive-python_default_visibility) | | +| 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. | +| `# gazelle:python_label_convention` | `$distribution_name$` | +| Defines the format of the distribution name in labels to third-party deps. Useful for using Gazelle plugin with other rules with different repository conventions (e.g. `rules_pycross`). Full label is always prepended with (pip) repository name, e.g. `@pip//numpy`. | +| `# gazelle:python_label_normalization` | `snake_case` | +| Controls how distribution names in labels to third-party deps are normalized. Useful for using Gazelle plugin with other rules with different label conventions (e.g. `rules_pycross` uses PEP-503). Can be "snake_case", "none", or "pep503". | #### Directive: `python_root`: @@ -440,6 +442,33 @@ py_library( [issue-1826]: https://github.com/bazelbuild/rules_python/issues/1826 +#### Directive: `python_generation_mode_per_package_require_test_entry_point`: +When `# gazelle:python_generation_mode package`, whether a file called `__test__.py` or a target called `__test__`, a.k.a., entry point, is required to generate one test target per package. If this is set to true but no entry point is found, Gazelle will fall back to file mode and generate one test target per file. Setting this directive to false forces Gazelle to generate one test target per package even without entry point. However, this means the `main` attribute of the `py_test` will not be set and the target will not be runnable unless either: +1. there happen to be a file in the `srcs` with the same name as the `py_test` target, or +2. a macro populating the `main` attribute of `py_test` is configured with `gazelle:map_kind` to replace `py_test` when Gazelle is generating Python test targets. For example, user can provide such a macro to Gazelle: + +```starlark +load("@rules_python//python:defs.bzl", _py_test="py_test") +load("@aspect_rules_py//py:defs.bzl", "py_pytest_main") + +def py_test(name, main=None, **kwargs): + deps = kwargs.pop("deps", []) + if not main: + py_pytest_main( + name = "__test__", + deps = ["@pip_pytest//:pkg"], # change this to the pytest target in your repo. + ) + + deps.append(":__test__") + main = ":__test__.py" + + _py_test( + name = name, + main = main, + deps = deps, + **kwargs, +) +``` ### Annotations From 3d847ef48e9fb1028f3ab98d362cbe1bdda553a1 Mon Sep 17 00:00:00 2001 From: aignas <240938+aignas@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:26:58 +0900 Subject: [PATCH 6/6] doc: add CHANGELOG entry --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d924f650ca..560c04ea8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,16 @@ A brief description of the categories of changes: containing ">" sign ### Added -* Nothing yet +* (gazelle) Added `python_generation_mode_per_package_require_test_entry_point` + in order to better accommodate users who use a custom macro, + [`pytest-bazel`][pytest_bazel], [rules_python_pytest] or `rules_py` + [py_test_main] in order to integrate with `pytest`. Currently the default + flag value is set to `true` for backwards compatible behaviour, but in the + future the flag will be flipped be `false` by default. + +[rules_python_pytest]: https://github.com/caseyduquettesc/rules_python_pytest +[py_test_main]: https://docs.aspect.build/rulesets/aspect_rules_py/docs/rules/#py_pytest_main +[pytest_bazel]: https://pypi.org/project/pytest-bazel ### Removed * Nothing yet