Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat!: Following generation mode when generating test targets by linzhp · Pull Request #2044 · bazel-contrib/rules_python · GitHub
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat!: Following generation mode when generating test targets by linzhp · Pull Request #2044 · bazel-contrib/rules_python · GitHub
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat!: Following generation mode when generating test targets by linzhp · Pull Request #2044 · bazel-contrib/rules_python · GitHub
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat!: Following generation mode when generating test targets by linzhp · Pull Request #2044 · bazel-contrib/rules_python · GitHub
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat!: Following generation mode when generating test targets by linzhp · Pull Request #2044 · bazel-contrib/rules_python · GitHub
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat!: Following generation mode when generating test targets by linzhp · Pull Request #2044 · bazel-contrib/rules_python · GitHub
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat!: Following generation mode when generating test targets by linzhp · Pull Request #2044 · bazel-contrib/rules_python · GitHub
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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