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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,9 @@ A brief description of the categories of changes:
### Added

* (docs) bzlmod extensions are now documented on rules-python.readthedocs.io
* (gazelle) `file` generation mode can now also add `__init__.py` to the srcs
attribute for every target in the package. This is enabled through a separate
directive `python_generation_mode_per_file_include_init`.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0

Expand Down
2 changes: 2 additions & 0 deletions gazelle/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,6 +184,8 @@ Python-specific directives are as follows:
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_generation_mode_per_file_include_init`| `package` |
| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/configure.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.IgnoreDependenciesDirective,
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand DownExpand Up@@ -149,6 +150,12 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
pythonconfig.GenerationMode, d.Value)
log.Fatal(err)
}
case pythonconfig.GenerationModePerFileIncludeInit:
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
if err != nil {
log.Fatal(err)
}
config.SetPerFileGenerationIncludeInit(v)
case pythonconfig.LibraryNamingConvention:
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
case pythonconfig.BinaryNamingConvention:
Expand Down
29 changes: 20 additions & 9 deletions gazelle/python/generate.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,18 +272,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
if cfg.PerFileGeneration() {
hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir)
pyLibraryFilenames.Each(func(index int, filename interface{}) {
if filename == pyLibraryEntrypointFilename {
stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
if stat.Size() == 0 {
return // ignore empty __init__.py
}
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if filename == pyLibraryEntrypointFilename && !nonEmptyInit {
return // ignore empty __init__.py.
}
srcs := treeset.NewWith(godsutils.StringComparator, filename)
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit {
srcs.Add(pyLibraryEntrypointFilename)
}
appendPyLibrary(srcs, pyLibraryTargetName)
})
} else if !pyLibraryFilenames.Empty() {
Expand DownExpand Up@@ -468,6 +466,19 @@ func hasEntrypointFile(dir string) bool {
return false
}

// hasLibraryEntrypointFile returns if the given directory has the library
// entrypoint file, and if it is non-empty.
func hasLibraryEntrypointFile(dir string) (bool, bool) {
stat, err := os.Stat(filepath.Join(dir, pyLibraryEntrypointFilename))
if os.IsNotExist(err) {
return false, false
}
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
return true, stat.Size() != 0
}

// isEntrypointFile returns whether the given path is an entrypoint file. The
// given path can be absolute or relative.
func isEntrypointFile(path string) bool {
Expand Down
14 changes: 10 additions & 4 deletions gazelle/python/resolve.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,11 +61,17 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides := make([]resolve.ImportSpec, 0, len(srcs)+1)
for _, src := range srcs {
ext := filepath.Ext(src)
if ext == ".py" {
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
if ext != ".py" {
continue
}
if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename {
// Do not provide import spec from __init__.py when it is being included as
// part of another module.
continue
}
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
Expand Down
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_non_empty_init/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true
6 changes: 5 additions & 1 deletion gazelle/python/testdata/per_file_non_empty_init/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true

py_library(
name = "__init__",
Expand All@@ -11,6 +12,9 @@ py_library(

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
Comment thread
siddharthab marked this conversation as resolved.
"foo.py",
],
visibility = ["//:__subpackages__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# gazelle:python_generation_mode_per_file_include_init true
16 changes: 15 additions & 1 deletion gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

# gazelle:python_generation_mode_per_file_include_init true

py_library(
Comment thread
siddharthab marked this conversation as resolved.
name = "__init__",
srcs = ["__init__.py"],
visibility = ["//:__subpackages__"],
)

py_library(
name = "bar",
srcs = [
"__init__.py",
"bar.py",
],
visibility = ["//:__subpackages__"],
)

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
"foo.py",
],
visibility = ["//:__subpackages__"],
)

Expand Down
Empty file.
87 changes: 53 additions & 34 deletions gazelle/pythonconfig/pythonconfig.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
// GenerationMode represents the directive that controls the target generation
// mode. See below for the GenerationModeType constants.
GenerationMode = "python_generation_mode"
// GenerationModePerFileIncludeInit represents the directive that augments
// 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"
Comment thread
siddharthab marked this conversation as resolved.
// 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@@ -122,15 +126,16 @@ 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
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
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
}

// New creates a new Config.
Expand All@@ -139,18 +144,19 @@ 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,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
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),
}
}

Expand All@@ -163,19 +169,20 @@ func (c *Config) Parent() *Config {
// current Config and sets itself as the parent to the child.
func (c *Config) NewChild() *Config {
return &Config{
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
perFileGenerationIncludeInit: c.perFileGenerationIncludeInit,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
}
}

Expand DownExpand Up@@ -344,6 +351,18 @@ func (c *Config) PerFileGeneration() bool {
return c.perFileGeneration
}

// SetPerFileGenerationIncludeInit sets whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) SetPerFileGenerationIncludeInit(includeInit bool) {
c.perFileGenerationIncludeInit = includeInit
}

// PerFileGenerationIncludeInit returns whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) PerFileGenerationIncludeInit() bool {
return c.perFileGenerationIncludeInit
}

// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention
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" + '
fix(gazelle): __init__.py in per-file targets by siddharthab · Pull Request #1582 · 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,9 @@ A brief description of the categories of changes:
### Added

* (docs) bzlmod extensions are now documented on rules-python.readthedocs.io
* (gazelle) `file` generation mode can now also add `__init__.py` to the srcs
attribute for every target in the package. This is enabled through a separate
directive `python_generation_mode_per_file_include_init`.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0

Expand Down
2 changes: 2 additions & 0 deletions gazelle/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,6 +184,8 @@ Python-specific directives are as follows:
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_generation_mode_per_file_include_init`| `package` |
| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/configure.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.IgnoreDependenciesDirective,
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand DownExpand Up@@ -149,6 +150,12 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
pythonconfig.GenerationMode, d.Value)
log.Fatal(err)
}
case pythonconfig.GenerationModePerFileIncludeInit:
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
if err != nil {
log.Fatal(err)
}
config.SetPerFileGenerationIncludeInit(v)
case pythonconfig.LibraryNamingConvention:
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
case pythonconfig.BinaryNamingConvention:
Expand Down
29 changes: 20 additions & 9 deletions gazelle/python/generate.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,18 +272,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
if cfg.PerFileGeneration() {
hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir)
pyLibraryFilenames.Each(func(index int, filename interface{}) {
if filename == pyLibraryEntrypointFilename {
stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
if stat.Size() == 0 {
return // ignore empty __init__.py
}
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if filename == pyLibraryEntrypointFilename && !nonEmptyInit {
return // ignore empty __init__.py.
}
srcs := treeset.NewWith(godsutils.StringComparator, filename)
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit {
srcs.Add(pyLibraryEntrypointFilename)
}
appendPyLibrary(srcs, pyLibraryTargetName)
})
} else if !pyLibraryFilenames.Empty() {
Expand DownExpand Up@@ -468,6 +466,19 @@ func hasEntrypointFile(dir string) bool {
return false
}

// hasLibraryEntrypointFile returns if the given directory has the library
// entrypoint file, and if it is non-empty.
func hasLibraryEntrypointFile(dir string) (bool, bool) {
stat, err := os.Stat(filepath.Join(dir, pyLibraryEntrypointFilename))
if os.IsNotExist(err) {
return false, false
}
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
return true, stat.Size() != 0
}

// isEntrypointFile returns whether the given path is an entrypoint file. The
// given path can be absolute or relative.
func isEntrypointFile(path string) bool {
Expand Down
14 changes: 10 additions & 4 deletions gazelle/python/resolve.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,11 +61,17 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides := make([]resolve.ImportSpec, 0, len(srcs)+1)
for _, src := range srcs {
ext := filepath.Ext(src)
if ext == ".py" {
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
if ext != ".py" {
continue
}
if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename {
// Do not provide import spec from __init__.py when it is being included as
// part of another module.
continue
}
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
Expand Down
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_non_empty_init/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true
6 changes: 5 additions & 1 deletion gazelle/python/testdata/per_file_non_empty_init/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true

py_library(
name = "__init__",
Expand All@@ -11,6 +12,9 @@ py_library(

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
Comment thread
siddharthab marked this conversation as resolved.
"foo.py",
],
visibility = ["//:__subpackages__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# gazelle:python_generation_mode_per_file_include_init true
16 changes: 15 additions & 1 deletion gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

# gazelle:python_generation_mode_per_file_include_init true

py_library(
Comment thread
siddharthab marked this conversation as resolved.
name = "__init__",
srcs = ["__init__.py"],
visibility = ["//:__subpackages__"],
)

py_library(
name = "bar",
srcs = [
"__init__.py",
"bar.py",
],
visibility = ["//:__subpackages__"],
)

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
"foo.py",
],
visibility = ["//:__subpackages__"],
)

Expand Down
Empty file.
87 changes: 53 additions & 34 deletions gazelle/pythonconfig/pythonconfig.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
// GenerationMode represents the directive that controls the target generation
// mode. See below for the GenerationModeType constants.
GenerationMode = "python_generation_mode"
// GenerationModePerFileIncludeInit represents the directive that augments
// 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"
Comment thread
siddharthab marked this conversation as resolved.
// 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@@ -122,15 +126,16 @@ 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
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
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
}

// New creates a new Config.
Expand All@@ -139,18 +144,19 @@ 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,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
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),
}
}

Expand All@@ -163,19 +169,20 @@ func (c *Config) Parent() *Config {
// current Config and sets itself as the parent to the child.
func (c *Config) NewChild() *Config {
return &Config{
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
perFileGenerationIncludeInit: c.perFileGenerationIncludeInit,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
}
}

Expand DownExpand Up@@ -344,6 +351,18 @@ func (c *Config) PerFileGeneration() bool {
return c.perFileGeneration
}

// SetPerFileGenerationIncludeInit sets whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) SetPerFileGenerationIncludeInit(includeInit bool) {
c.perFileGenerationIncludeInit = includeInit
}

// PerFileGenerationIncludeInit returns whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) PerFileGenerationIncludeInit() bool {
return c.perFileGenerationIncludeInit
}

// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention
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('^' + ".*" + ' fix(gazelle): __init__.py in per-file targets by siddharthab · Pull Request #1582 · 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,9 @@ A brief description of the categories of changes:
### Added

* (docs) bzlmod extensions are now documented on rules-python.readthedocs.io
* (gazelle) `file` generation mode can now also add `__init__.py` to the srcs
attribute for every target in the package. This is enabled through a separate
directive `python_generation_mode_per_file_include_init`.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0

Expand Down
2 changes: 2 additions & 0 deletions gazelle/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,6 +184,8 @@ Python-specific directives are as follows:
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_generation_mode_per_file_include_init`| `package` |
| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/configure.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.IgnoreDependenciesDirective,
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand DownExpand Up@@ -149,6 +150,12 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
pythonconfig.GenerationMode, d.Value)
log.Fatal(err)
}
case pythonconfig.GenerationModePerFileIncludeInit:
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
if err != nil {
log.Fatal(err)
}
config.SetPerFileGenerationIncludeInit(v)
case pythonconfig.LibraryNamingConvention:
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
case pythonconfig.BinaryNamingConvention:
Expand Down
29 changes: 20 additions & 9 deletions gazelle/python/generate.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,18 +272,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
if cfg.PerFileGeneration() {
hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir)
pyLibraryFilenames.Each(func(index int, filename interface{}) {
if filename == pyLibraryEntrypointFilename {
stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
if stat.Size() == 0 {
return // ignore empty __init__.py
}
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if filename == pyLibraryEntrypointFilename && !nonEmptyInit {
return // ignore empty __init__.py.
}
srcs := treeset.NewWith(godsutils.StringComparator, filename)
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit {
srcs.Add(pyLibraryEntrypointFilename)
}
appendPyLibrary(srcs, pyLibraryTargetName)
})
} else if !pyLibraryFilenames.Empty() {
Expand DownExpand Up@@ -468,6 +466,19 @@ func hasEntrypointFile(dir string) bool {
return false
}

// hasLibraryEntrypointFile returns if the given directory has the library
// entrypoint file, and if it is non-empty.
func hasLibraryEntrypointFile(dir string) (bool, bool) {
stat, err := os.Stat(filepath.Join(dir, pyLibraryEntrypointFilename))
if os.IsNotExist(err) {
return false, false
}
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
return true, stat.Size() != 0
}

// isEntrypointFile returns whether the given path is an entrypoint file. The
// given path can be absolute or relative.
func isEntrypointFile(path string) bool {
Expand Down
14 changes: 10 additions & 4 deletions gazelle/python/resolve.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,11 +61,17 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides := make([]resolve.ImportSpec, 0, len(srcs)+1)
for _, src := range srcs {
ext := filepath.Ext(src)
if ext == ".py" {
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
if ext != ".py" {
continue
}
if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename {
// Do not provide import spec from __init__.py when it is being included as
// part of another module.
continue
}
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
Expand Down
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_non_empty_init/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true
6 changes: 5 additions & 1 deletion gazelle/python/testdata/per_file_non_empty_init/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true

py_library(
name = "__init__",
Expand All@@ -11,6 +12,9 @@ py_library(

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
Comment thread
siddharthab marked this conversation as resolved.
"foo.py",
],
visibility = ["//:__subpackages__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# gazelle:python_generation_mode_per_file_include_init true
16 changes: 15 additions & 1 deletion gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

# gazelle:python_generation_mode_per_file_include_init true

py_library(
Comment thread
siddharthab marked this conversation as resolved.
name = "__init__",
srcs = ["__init__.py"],
visibility = ["//:__subpackages__"],
)

py_library(
name = "bar",
srcs = [
"__init__.py",
"bar.py",
],
visibility = ["//:__subpackages__"],
)

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
"foo.py",
],
visibility = ["//:__subpackages__"],
)

Expand Down
Empty file.
87 changes: 53 additions & 34 deletions gazelle/pythonconfig/pythonconfig.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
// GenerationMode represents the directive that controls the target generation
// mode. See below for the GenerationModeType constants.
GenerationMode = "python_generation_mode"
// GenerationModePerFileIncludeInit represents the directive that augments
// 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"
Comment thread
siddharthab marked this conversation as resolved.
// 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@@ -122,15 +126,16 @@ 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
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
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
}

// New creates a new Config.
Expand All@@ -139,18 +144,19 @@ 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,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
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),
}
}

Expand All@@ -163,19 +169,20 @@ func (c *Config) Parent() *Config {
// current Config and sets itself as the parent to the child.
func (c *Config) NewChild() *Config {
return &Config{
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
perFileGenerationIncludeInit: c.perFileGenerationIncludeInit,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
}
}

Expand DownExpand Up@@ -344,6 +351,18 @@ func (c *Config) PerFileGeneration() bool {
return c.perFileGeneration
}

// SetPerFileGenerationIncludeInit sets whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) SetPerFileGenerationIncludeInit(includeInit bool) {
c.perFileGenerationIncludeInit = includeInit
}

// PerFileGenerationIncludeInit returns whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) PerFileGenerationIncludeInit() bool {
return c.perFileGenerationIncludeInit
}

// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention
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('^' + ".*" + ' fix(gazelle): __init__.py in per-file targets by siddharthab · Pull Request #1582 · 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,9 @@ A brief description of the categories of changes:
### Added

* (docs) bzlmod extensions are now documented on rules-python.readthedocs.io
* (gazelle) `file` generation mode can now also add `__init__.py` to the srcs
attribute for every target in the package. This is enabled through a separate
directive `python_generation_mode_per_file_include_init`.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0

Expand Down
2 changes: 2 additions & 0 deletions gazelle/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,6 +184,8 @@ Python-specific directives are as follows:
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_generation_mode_per_file_include_init`| `package` |
| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/configure.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.IgnoreDependenciesDirective,
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand DownExpand Up@@ -149,6 +150,12 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
pythonconfig.GenerationMode, d.Value)
log.Fatal(err)
}
case pythonconfig.GenerationModePerFileIncludeInit:
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
if err != nil {
log.Fatal(err)
}
config.SetPerFileGenerationIncludeInit(v)
case pythonconfig.LibraryNamingConvention:
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
case pythonconfig.BinaryNamingConvention:
Expand Down
29 changes: 20 additions & 9 deletions gazelle/python/generate.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,18 +272,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
if cfg.PerFileGeneration() {
hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir)
pyLibraryFilenames.Each(func(index int, filename interface{}) {
if filename == pyLibraryEntrypointFilename {
stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
if stat.Size() == 0 {
return // ignore empty __init__.py
}
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if filename == pyLibraryEntrypointFilename && !nonEmptyInit {
return // ignore empty __init__.py.
}
srcs := treeset.NewWith(godsutils.StringComparator, filename)
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit {
srcs.Add(pyLibraryEntrypointFilename)
}
appendPyLibrary(srcs, pyLibraryTargetName)
})
} else if !pyLibraryFilenames.Empty() {
Expand DownExpand Up@@ -468,6 +466,19 @@ func hasEntrypointFile(dir string) bool {
return false
}

// hasLibraryEntrypointFile returns if the given directory has the library
// entrypoint file, and if it is non-empty.
func hasLibraryEntrypointFile(dir string) (bool, bool) {
stat, err := os.Stat(filepath.Join(dir, pyLibraryEntrypointFilename))
if os.IsNotExist(err) {
return false, false
}
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
return true, stat.Size() != 0
}

// isEntrypointFile returns whether the given path is an entrypoint file. The
// given path can be absolute or relative.
func isEntrypointFile(path string) bool {
Expand Down
14 changes: 10 additions & 4 deletions gazelle/python/resolve.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,11 +61,17 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides := make([]resolve.ImportSpec, 0, len(srcs)+1)
for _, src := range srcs {
ext := filepath.Ext(src)
if ext == ".py" {
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
if ext != ".py" {
continue
}
if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename {
// Do not provide import spec from __init__.py when it is being included as
// part of another module.
continue
}
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
Expand Down
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_non_empty_init/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true
6 changes: 5 additions & 1 deletion gazelle/python/testdata/per_file_non_empty_init/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true

py_library(
name = "__init__",
Expand All@@ -11,6 +12,9 @@ py_library(

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
Comment thread
siddharthab marked this conversation as resolved.
"foo.py",
],
visibility = ["//:__subpackages__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# gazelle:python_generation_mode_per_file_include_init true
16 changes: 15 additions & 1 deletion gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

# gazelle:python_generation_mode_per_file_include_init true

py_library(
Comment thread
siddharthab marked this conversation as resolved.
name = "__init__",
srcs = ["__init__.py"],
visibility = ["//:__subpackages__"],
)

py_library(
name = "bar",
srcs = [
"__init__.py",
"bar.py",
],
visibility = ["//:__subpackages__"],
)

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
"foo.py",
],
visibility = ["//:__subpackages__"],
)

Expand Down
Empty file.
87 changes: 53 additions & 34 deletions gazelle/pythonconfig/pythonconfig.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
// GenerationMode represents the directive that controls the target generation
// mode. See below for the GenerationModeType constants.
GenerationMode = "python_generation_mode"
// GenerationModePerFileIncludeInit represents the directive that augments
// 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"
Comment thread
siddharthab marked this conversation as resolved.
// 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@@ -122,15 +126,16 @@ 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
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
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
}

// New creates a new Config.
Expand All@@ -139,18 +144,19 @@ 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,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
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),
}
}

Expand All@@ -163,19 +169,20 @@ func (c *Config) Parent() *Config {
// current Config and sets itself as the parent to the child.
func (c *Config) NewChild() *Config {
return &Config{
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
perFileGenerationIncludeInit: c.perFileGenerationIncludeInit,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
}
}

Expand DownExpand Up@@ -344,6 +351,18 @@ func (c *Config) PerFileGeneration() bool {
return c.perFileGeneration
}

// SetPerFileGenerationIncludeInit sets whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) SetPerFileGenerationIncludeInit(includeInit bool) {
c.perFileGenerationIncludeInit = includeInit
}

// PerFileGenerationIncludeInit returns whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) PerFileGenerationIncludeInit() bool {
return c.perFileGenerationIncludeInit
}

// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention
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" + ' fix(gazelle): __init__.py in per-file targets by siddharthab · Pull Request #1582 · 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,9 @@ A brief description of the categories of changes:
### Added

* (docs) bzlmod extensions are now documented on rules-python.readthedocs.io
* (gazelle) `file` generation mode can now also add `__init__.py` to the srcs
attribute for every target in the package. This is enabled through a separate
directive `python_generation_mode_per_file_include_init`.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0

Expand Down
2 changes: 2 additions & 0 deletions gazelle/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,6 +184,8 @@ Python-specific directives are as follows:
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_generation_mode_per_file_include_init`| `package` |
| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/configure.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.IgnoreDependenciesDirective,
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand DownExpand Up@@ -149,6 +150,12 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
pythonconfig.GenerationMode, d.Value)
log.Fatal(err)
}
case pythonconfig.GenerationModePerFileIncludeInit:
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
if err != nil {
log.Fatal(err)
}
config.SetPerFileGenerationIncludeInit(v)
case pythonconfig.LibraryNamingConvention:
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
case pythonconfig.BinaryNamingConvention:
Expand Down
29 changes: 20 additions & 9 deletions gazelle/python/generate.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,18 +272,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
if cfg.PerFileGeneration() {
hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir)
pyLibraryFilenames.Each(func(index int, filename interface{}) {
if filename == pyLibraryEntrypointFilename {
stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
if stat.Size() == 0 {
return // ignore empty __init__.py
}
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if filename == pyLibraryEntrypointFilename && !nonEmptyInit {
return // ignore empty __init__.py.
}
srcs := treeset.NewWith(godsutils.StringComparator, filename)
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit {
srcs.Add(pyLibraryEntrypointFilename)
}
appendPyLibrary(srcs, pyLibraryTargetName)
})
} else if !pyLibraryFilenames.Empty() {
Expand DownExpand Up@@ -468,6 +466,19 @@ func hasEntrypointFile(dir string) bool {
return false
}

// hasLibraryEntrypointFile returns if the given directory has the library
// entrypoint file, and if it is non-empty.
func hasLibraryEntrypointFile(dir string) (bool, bool) {
stat, err := os.Stat(filepath.Join(dir, pyLibraryEntrypointFilename))
if os.IsNotExist(err) {
return false, false
}
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
return true, stat.Size() != 0
}

// isEntrypointFile returns whether the given path is an entrypoint file. The
// given path can be absolute or relative.
func isEntrypointFile(path string) bool {
Expand Down
14 changes: 10 additions & 4 deletions gazelle/python/resolve.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,11 +61,17 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides := make([]resolve.ImportSpec, 0, len(srcs)+1)
for _, src := range srcs {
ext := filepath.Ext(src)
if ext == ".py" {
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
if ext != ".py" {
continue
}
if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename {
// Do not provide import spec from __init__.py when it is being included as
// part of another module.
continue
}
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
Expand Down
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_non_empty_init/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true
6 changes: 5 additions & 1 deletion gazelle/python/testdata/per_file_non_empty_init/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true

py_library(
name = "__init__",
Expand All@@ -11,6 +12,9 @@ py_library(

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
Comment thread
siddharthab marked this conversation as resolved.
"foo.py",
],
visibility = ["//:__subpackages__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# gazelle:python_generation_mode_per_file_include_init true
16 changes: 15 additions & 1 deletion gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

# gazelle:python_generation_mode_per_file_include_init true

py_library(
Comment thread
siddharthab marked this conversation as resolved.
name = "__init__",
srcs = ["__init__.py"],
visibility = ["//:__subpackages__"],
)

py_library(
name = "bar",
srcs = [
"__init__.py",
"bar.py",
],
visibility = ["//:__subpackages__"],
)

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
"foo.py",
],
visibility = ["//:__subpackages__"],
)

Expand Down
Empty file.
87 changes: 53 additions & 34 deletions gazelle/pythonconfig/pythonconfig.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
// GenerationMode represents the directive that controls the target generation
// mode. See below for the GenerationModeType constants.
GenerationMode = "python_generation_mode"
// GenerationModePerFileIncludeInit represents the directive that augments
// 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"
Comment thread
siddharthab marked this conversation as resolved.
// 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@@ -122,15 +126,16 @@ 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
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
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
}

// New creates a new Config.
Expand All@@ -139,18 +144,19 @@ 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,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
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),
}
}

Expand All@@ -163,19 +169,20 @@ func (c *Config) Parent() *Config {
// current Config and sets itself as the parent to the child.
func (c *Config) NewChild() *Config {
return &Config{
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
perFileGenerationIncludeInit: c.perFileGenerationIncludeInit,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
}
}

Expand DownExpand Up@@ -344,6 +351,18 @@ func (c *Config) PerFileGeneration() bool {
return c.perFileGeneration
}

// SetPerFileGenerationIncludeInit sets whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) SetPerFileGenerationIncludeInit(includeInit bool) {
c.perFileGenerationIncludeInit = includeInit
}

// PerFileGenerationIncludeInit returns whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) PerFileGenerationIncludeInit() bool {
return c.perFileGenerationIncludeInit
}

// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention
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('^' + ".*" + ' fix(gazelle): __init__.py in per-file targets by siddharthab · Pull Request #1582 · 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,9 @@ A brief description of the categories of changes:
### Added

* (docs) bzlmod extensions are now documented on rules-python.readthedocs.io
* (gazelle) `file` generation mode can now also add `__init__.py` to the srcs
attribute for every target in the package. This is enabled through a separate
directive `python_generation_mode_per_file_include_init`.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0

Expand Down
2 changes: 2 additions & 0 deletions gazelle/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,6 +184,8 @@ Python-specific directives are as follows:
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_generation_mode_per_file_include_init`| `package` |
| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/configure.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.IgnoreDependenciesDirective,
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand DownExpand Up@@ -149,6 +150,12 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
pythonconfig.GenerationMode, d.Value)
log.Fatal(err)
}
case pythonconfig.GenerationModePerFileIncludeInit:
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
if err != nil {
log.Fatal(err)
}
config.SetPerFileGenerationIncludeInit(v)
case pythonconfig.LibraryNamingConvention:
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
case pythonconfig.BinaryNamingConvention:
Expand Down
29 changes: 20 additions & 9 deletions gazelle/python/generate.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,18 +272,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
if cfg.PerFileGeneration() {
hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir)
pyLibraryFilenames.Each(func(index int, filename interface{}) {
if filename == pyLibraryEntrypointFilename {
stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
if stat.Size() == 0 {
return // ignore empty __init__.py
}
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if filename == pyLibraryEntrypointFilename && !nonEmptyInit {
return // ignore empty __init__.py.
}
srcs := treeset.NewWith(godsutils.StringComparator, filename)
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit {
srcs.Add(pyLibraryEntrypointFilename)
}
appendPyLibrary(srcs, pyLibraryTargetName)
})
} else if !pyLibraryFilenames.Empty() {
Expand DownExpand Up@@ -468,6 +466,19 @@ func hasEntrypointFile(dir string) bool {
return false
}

// hasLibraryEntrypointFile returns if the given directory has the library
// entrypoint file, and if it is non-empty.
func hasLibraryEntrypointFile(dir string) (bool, bool) {
stat, err := os.Stat(filepath.Join(dir, pyLibraryEntrypointFilename))
if os.IsNotExist(err) {
return false, false
}
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
return true, stat.Size() != 0
}

// isEntrypointFile returns whether the given path is an entrypoint file. The
// given path can be absolute or relative.
func isEntrypointFile(path string) bool {
Expand Down
14 changes: 10 additions & 4 deletions gazelle/python/resolve.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,11 +61,17 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides := make([]resolve.ImportSpec, 0, len(srcs)+1)
for _, src := range srcs {
ext := filepath.Ext(src)
if ext == ".py" {
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
if ext != ".py" {
continue
}
if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename {
// Do not provide import spec from __init__.py when it is being included as
// part of another module.
continue
}
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
Expand Down
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_non_empty_init/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true
6 changes: 5 additions & 1 deletion gazelle/python/testdata/per_file_non_empty_init/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true

py_library(
name = "__init__",
Expand All@@ -11,6 +12,9 @@ py_library(

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
Comment thread
siddharthab marked this conversation as resolved.
"foo.py",
],
visibility = ["//:__subpackages__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# gazelle:python_generation_mode_per_file_include_init true
16 changes: 15 additions & 1 deletion gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

# gazelle:python_generation_mode_per_file_include_init true

py_library(
Comment thread
siddharthab marked this conversation as resolved.
name = "__init__",
srcs = ["__init__.py"],
visibility = ["//:__subpackages__"],
)

py_library(
name = "bar",
srcs = [
"__init__.py",
"bar.py",
],
visibility = ["//:__subpackages__"],
)

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
"foo.py",
],
visibility = ["//:__subpackages__"],
)

Expand Down
Empty file.
87 changes: 53 additions & 34 deletions gazelle/pythonconfig/pythonconfig.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
// GenerationMode represents the directive that controls the target generation
// mode. See below for the GenerationModeType constants.
GenerationMode = "python_generation_mode"
// GenerationModePerFileIncludeInit represents the directive that augments
// 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"
Comment thread
siddharthab marked this conversation as resolved.
// 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@@ -122,15 +126,16 @@ 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
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
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
}

// New creates a new Config.
Expand All@@ -139,18 +144,19 @@ 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,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
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),
}
}

Expand All@@ -163,19 +169,20 @@ func (c *Config) Parent() *Config {
// current Config and sets itself as the parent to the child.
func (c *Config) NewChild() *Config {
return &Config{
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
perFileGenerationIncludeInit: c.perFileGenerationIncludeInit,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
}
}

Expand DownExpand Up@@ -344,6 +351,18 @@ func (c *Config) PerFileGeneration() bool {
return c.perFileGeneration
}

// SetPerFileGenerationIncludeInit sets whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) SetPerFileGenerationIncludeInit(includeInit bool) {
c.perFileGenerationIncludeInit = includeInit
}

// PerFileGenerationIncludeInit returns whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) PerFileGenerationIncludeInit() bool {
return c.perFileGenerationIncludeInit
}

// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention
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); } })(); })(); fix(gazelle): __init__.py in per-file targets by siddharthab · Pull Request #1582 · 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,9 @@ A brief description of the categories of changes:
### Added

* (docs) bzlmod extensions are now documented on rules-python.readthedocs.io
* (gazelle) `file` generation mode can now also add `__init__.py` to the srcs
attribute for every target in the package. This is enabled through a separate
directive `python_generation_mode_per_file_include_init`.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0

Expand Down
2 changes: 2 additions & 0 deletions gazelle/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,6 +184,8 @@ Python-specific directives are as follows:
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_generation_mode_per_file_include_init`| `package` |
| Controls whether `__init__.py` files are included as srcs in each generated target when target generation mode is "file". Can be "true", or "false" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/configure.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.IgnoreDependenciesDirective,
pythonconfig.ValidateImportStatementsDirective,
pythonconfig.GenerationMode,
pythonconfig.GenerationModePerFileIncludeInit,
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
Expand DownExpand Up@@ -149,6 +150,12 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
pythonconfig.GenerationMode, d.Value)
log.Fatal(err)
}
case pythonconfig.GenerationModePerFileIncludeInit:
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
if err != nil {
log.Fatal(err)
}
config.SetPerFileGenerationIncludeInit(v)
case pythonconfig.LibraryNamingConvention:
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
case pythonconfig.BinaryNamingConvention:
Expand Down
29 changes: 20 additions & 9 deletions gazelle/python/generate.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,18 +272,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
if cfg.PerFileGeneration() {
hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir)
pyLibraryFilenames.Each(func(index int, filename interface{}) {
if filename == pyLibraryEntrypointFilename {
stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
if stat.Size() == 0 {
return // ignore empty __init__.py
}
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if filename == pyLibraryEntrypointFilename && !nonEmptyInit {
return // ignore empty __init__.py.
}
srcs := treeset.NewWith(godsutils.StringComparator, filename)
pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit {
srcs.Add(pyLibraryEntrypointFilename)
}
appendPyLibrary(srcs, pyLibraryTargetName)
})
} else if !pyLibraryFilenames.Empty() {
Expand DownExpand Up@@ -468,6 +466,19 @@ func hasEntrypointFile(dir string) bool {
return false
}

// hasLibraryEntrypointFile returns if the given directory has the library
// entrypoint file, and if it is non-empty.
func hasLibraryEntrypointFile(dir string) (bool, bool) {
stat, err := os.Stat(filepath.Join(dir, pyLibraryEntrypointFilename))
if os.IsNotExist(err) {
return false, false
}
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
return true, stat.Size() != 0
}

// isEntrypointFile returns whether the given path is an entrypoint file. The
// given path can be absolute or relative.
func isEntrypointFile(path string) bool {
Expand Down
14 changes: 10 additions & 4 deletions gazelle/python/resolve.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,11 +61,17 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
provides := make([]resolve.ImportSpec, 0, len(srcs)+1)
for _, src := range srcs {
ext := filepath.Ext(src)
if ext == ".py" {
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
if ext != ".py" {
continue
}
if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename {
// Do not provide import spec from __init__.py when it is being included as
// part of another module.
continue
}
pythonProjectRoot := cfg.PythonProjectRoot()
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
provides = append(provides, provide)
}
if len(provides) == 0 {
return nil
Expand Down
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_non_empty_init/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true
6 changes: 5 additions & 1 deletion gazelle/python/testdata/per_file_non_empty_init/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_generation_mode file
# gazelle:python_generation_mode_per_file_include_init true

py_library(
name = "__init__",
Expand All@@ -11,6 +12,9 @@ py_library(

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
Comment thread
siddharthab marked this conversation as resolved.
"foo.py",
],
visibility = ["//:__subpackages__"],
)
1 change: 1 addition & 0 deletions gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# gazelle:python_generation_mode_per_file_include_init true
16 changes: 15 additions & 1 deletion gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
load("@rules_python//python:defs.bzl", "py_library", "py_test")

# gazelle:python_generation_mode_per_file_include_init true

py_library(
Comment thread
siddharthab marked this conversation as resolved.
name = "__init__",
srcs = ["__init__.py"],
visibility = ["//:__subpackages__"],
)

py_library(
name = "bar",
srcs = [
"__init__.py",
"bar.py",
],
visibility = ["//:__subpackages__"],
)

py_library(
name = "foo",
srcs = ["foo.py"],
srcs = [
"__init__.py",
"foo.py",
],
visibility = ["//:__subpackages__"],
)

Expand Down
Empty file.
87 changes: 53 additions & 34 deletions gazelle/pythonconfig/pythonconfig.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,10 @@ const (
// GenerationMode represents the directive that controls the target generation
// mode. See below for the GenerationModeType constants.
GenerationMode = "python_generation_mode"
// GenerationModePerFileIncludeInit represents the directive that augments
// 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"
Comment thread
siddharthab marked this conversation as resolved.
// 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@@ -122,15 +126,16 @@ 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
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
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
}

// New creates a new Config.
Expand All@@ -139,18 +144,19 @@ 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,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
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),
}
}

Expand All@@ -163,19 +169,20 @@ func (c *Config) Parent() *Config {
// current Config and sets itself as the parent to the child.
func (c *Config) NewChild() *Config {
return &Config{
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
parent: c,
extensionEnabled: c.extensionEnabled,
repoRoot: c.repoRoot,
pythonProjectRoot: c.pythonProjectRoot,
excludedPatterns: c.excludedPatterns,
ignoreFiles: make(map[string]struct{}),
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
perFileGeneration: c.perFileGeneration,
perFileGenerationIncludeInit: c.perFileGenerationIncludeInit,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
}
}

Expand DownExpand Up@@ -344,6 +351,18 @@ func (c *Config) PerFileGeneration() bool {
return c.perFileGeneration
}

// SetPerFileGenerationIncludeInit sets whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) SetPerFileGenerationIncludeInit(includeInit bool) {
c.perFileGenerationIncludeInit = includeInit
}

// PerFileGenerationIncludeInit returns whether py_library targets should
// include __init__.py files when PerFileGeneration() is true.
func (c *Config) PerFileGenerationIncludeInit() bool {
return c.perFileGenerationIncludeInit
}

// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention
Expand Down