aboutsummaryrefslogtreecommitdiff
path: root/gazelle/python/configure.go
diff options
context:
space:
mode:
Diffstat (limited to 'gazelle/python/configure.go')
-rw-r--r--gazelle/python/configure.go178
1 files changed, 178 insertions, 0 deletions
diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go
new file mode 100644
index 0000000..32f9ab0
--- /dev/null
+++ b/gazelle/python/configure.go
@@ -0,0 +1,178 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package python
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/bazelbuild/bazel-gazelle/config"
+ "github.com/bazelbuild/bazel-gazelle/rule"
+
+ "github.com/bazelbuild/rules_python/gazelle/manifest"
+ "github.com/bazelbuild/rules_python/gazelle/pythonconfig"
+)
+
+// Configurer satisfies the config.Configurer interface. It's the
+// language-specific configuration extension.
+type Configurer struct{}
+
+// RegisterFlags registers command-line flags used by the extension. This
+// method is called once with the root configuration when Gazelle
+// starts. RegisterFlags may set an initial values in Config.Exts. When flags
+// are set, they should modify these values.
+func (py *Configurer) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {}
+
+// CheckFlags validates the configuration after command line flags are parsed.
+// This is called once with the root configuration when Gazelle starts.
+// CheckFlags may set default values in flags or make implied changes.
+func (py *Configurer) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
+ return nil
+}
+
+// KnownDirectives returns a list of directive keys that this Configurer can
+// interpret. Gazelle prints errors for directives that are not recoginized by
+// any Configurer.
+func (py *Configurer) KnownDirectives() []string {
+ return []string{
+ pythonconfig.PythonExtensionDirective,
+ pythonconfig.PythonRootDirective,
+ pythonconfig.PythonManifestFileNameDirective,
+ pythonconfig.IgnoreFilesDirective,
+ pythonconfig.IgnoreDependenciesDirective,
+ pythonconfig.ValidateImportStatementsDirective,
+ pythonconfig.GenerationMode,
+ pythonconfig.LibraryNamingConvention,
+ pythonconfig.BinaryNamingConvention,
+ pythonconfig.TestNamingConvention,
+ }
+}
+
+// Configure modifies the configuration using directives and other information
+// extracted from a build file. Configure is called in each directory.
+//
+// c is the configuration for the current directory. It starts out as a copy
+// of the configuration for the parent directory.
+//
+// rel is the slash-separated relative path from the repository root to
+// the current directory. It is "" for the root directory itself.
+//
+// f is the build file for the current directory or nil if there is no
+// existing build file.
+func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
+ // Create the root config.
+ if _, exists := c.Exts[languageName]; !exists {
+ rootConfig := pythonconfig.New(c.RepoRoot, "")
+ c.Exts[languageName] = pythonconfig.Configs{"": rootConfig}
+ }
+
+ configs := c.Exts[languageName].(pythonconfig.Configs)
+
+ config, exists := configs[rel]
+ if !exists {
+ parent := configs.ParentForPackage(rel)
+ config = parent.NewChild()
+ configs[rel] = config
+ }
+
+ if f == nil {
+ return
+ }
+
+ gazelleManifestFilename := "gazelle_python.yaml"
+
+ for _, d := range f.Directives {
+ switch d.Key {
+ case "exclude":
+ // We record the exclude directive for coarse-grained packages
+ // since we do manual tree traversal in this mode.
+ config.AddExcludedPattern(filepath.Join(rel, strings.TrimSpace(d.Value)))
+ case pythonconfig.PythonExtensionDirective:
+ switch d.Value {
+ case "enabled":
+ config.SetExtensionEnabled(true)
+ case "disabled":
+ config.SetExtensionEnabled(false)
+ default:
+ err := fmt.Errorf("invalid value for directive %q: %s: possible values are enabled/disabled",
+ pythonconfig.PythonExtensionDirective, d.Value)
+ log.Fatal(err)
+ }
+ case pythonconfig.PythonRootDirective:
+ config.SetPythonProjectRoot(rel)
+ case pythonconfig.PythonManifestFileNameDirective:
+ gazelleManifestFilename = strings.TrimSpace(d.Value)
+ case pythonconfig.IgnoreFilesDirective:
+ for _, ignoreFile := range strings.Split(d.Value, ",") {
+ config.AddIgnoreFile(ignoreFile)
+ }
+ case pythonconfig.IgnoreDependenciesDirective:
+ for _, ignoreDependency := range strings.Split(d.Value, ",") {
+ config.AddIgnoreDependency(ignoreDependency)
+ }
+ case pythonconfig.ValidateImportStatementsDirective:
+ v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
+ if err != nil {
+ log.Fatal(err)
+ }
+ config.SetValidateImportStatements(v)
+ case pythonconfig.GenerationMode:
+ switch pythonconfig.GenerationModeType(strings.TrimSpace(d.Value)) {
+ case pythonconfig.GenerationModePackage:
+ config.SetCoarseGrainedGeneration(false)
+ case pythonconfig.GenerationModeProject:
+ config.SetCoarseGrainedGeneration(true)
+ default:
+ err := fmt.Errorf("invalid value for directive %q: %s",
+ pythonconfig.GenerationMode, d.Value)
+ log.Fatal(err)
+ }
+ case pythonconfig.LibraryNamingConvention:
+ config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
+ case pythonconfig.BinaryNamingConvention:
+ config.SetBinaryNamingConvention(strings.TrimSpace(d.Value))
+ case pythonconfig.TestNamingConvention:
+ config.SetTestNamingConvention(strings.TrimSpace(d.Value))
+ }
+ }
+
+ gazelleManifestPath := filepath.Join(c.RepoRoot, rel, gazelleManifestFilename)
+ gazelleManifest, err := py.loadGazelleManifest(gazelleManifestPath)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if gazelleManifest != nil {
+ config.SetGazelleManifest(gazelleManifest)
+ }
+}
+
+func (py *Configurer) loadGazelleManifest(gazelleManifestPath string) (*manifest.Manifest, error) {
+ if _, err := os.Stat(gazelleManifestPath); err != nil {
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("failed to load Gazelle manifest at %q: %w", gazelleManifestPath, err)
+ }
+ manifestFile := new(manifest.File)
+ if err := manifestFile.Decode(gazelleManifestPath); err != nil {
+ return nil, fmt.Errorf("failed to load Gazelle manifest at %q: %w", gazelleManifestPath, err)
+ }
+ return manifestFile.Manifest, nil
+}