Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Following generation mode when generating test targets #2044

Merged
merged 7 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
99 changes: 64 additions & 35 deletions gazelle/README.md

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions gazelle/python/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.GenerationModePerPackageRequireTestEntryPoint,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand Down Expand Up @@ -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:
Expand Down
7 changes: 5 additions & 2 deletions gazelle/python/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.PerPackageGenerationRequireTestEntryPoint() || hasPyTestEntryPointFile || hasPyTestEntryPointTarget || cfg.CoarseGrainedGeneration()) && !cfg.PerFileGeneration() {
// Create one py_test target per package
if hasPyTestEntryPointFile {
// Only add the pyTestEntrypointFilename to the pyTestFilenames if
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# gazelle:python_generation_mode package
# gazelle:python_generation_mode_per_package_require_test_entry_point false
Original file line number Diff line number Diff line change
@@ -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",
],
)
Original file line number Diff line number Diff line change
@@ -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`
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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
101 changes: 58 additions & 43 deletions gazelle/pythonconfig/pythonconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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,
}
}

Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down