Skip to content

Commit a0e53f3

Browse files
committed
Address comments and add directive
1 parent 7d1f434 commit a0e53f3

File tree

9 files changed

+48
-17
lines changed

9 files changed

+48
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ END_UNRELEASED_TEMPLATE
7070
and will be removed in the next major release.
7171
([#2794](https://github.com/bazel-contrib/rules_python/issues/2794)
7272
* (py_wheel) py_wheel always creates zip64-capable wheel zips
73+
* (gazelle) For package mode, resolve dependencies when imports are relative to the package path. (https://github.com/bazel-contrib/rules_python/issues/2203)
7374

7475
{#v0-0-0-fixed}
7576
### Fixed

gazelle/python/configure.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func (py *Configurer) KnownDirectives() []string {
6868
pythonconfig.TestFilePattern,
6969
pythonconfig.LabelConvention,
7070
pythonconfig.LabelNormalization,
71+
pythonconfig.ExperimentalAllowRelativeImports,
7172
}
7273
}
7374

@@ -222,6 +223,13 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
222223
default:
223224
config.SetLabelNormalization(pythonconfig.DefaultLabelNormalizationType)
224225
}
226+
case pythonconfig.ExperimentalAllowRelativeImports:
227+
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
228+
if err != nil {
229+
log.Printf("invalid value for gazelle:%s in %q: %q",
230+
pythonconfig.ExperimentalAllowRelativeImports, rel, d.Value)
231+
}
232+
config.SetExperimentalAllowRelativeImports(v)
225233
}
226234
}
227235

gazelle/python/resolve.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []reso
7171
}
7272
pythonProjectRoot := cfg.PythonProjectRoot()
7373
provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src)
74-
// log.Printf("import to index: %v", provide.Imp)
7574
provides = append(provides, provide)
7675
}
7776
if len(provides) == 0 {
@@ -158,18 +157,14 @@ func (py *Resolver) Resolve(
158157
moduleName := mod.Name
159158
// Transform relative imports `.` or `..foo.bar` into the package path from root.
160159
if strings.HasPrefix(mod.From, ".") {
161-
if !isPackageGeneration {
160+
if !cfg.ExperimentalAllowRelativeImports() || !isPackageGeneration {
162161
continue MODULES_LOOP
163162
}
164163

165164
// Count number of leading dots in mod.From (e.g., ".." = 2, "...foo.bar" = 3)
166-
relativeDepth := 0
167-
for i := 0; i < len(mod.From); i++ {
168-
if mod.From[i] == '.' {
169-
relativeDepth++
170-
} else {
171-
break
172-
}
165+
relativeDepth := strings.IndexFunc(mod.From, func(r rune) bool { return r != '.' })
166+
if relativeDepth == -1 {
167+
relativeDepth = len(mod.From)
173168
}
174169

175170
// Extract final symbol (e.g., "some_function") from mod.Name
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
# gazelle:python_generation_mode package
2+
# gazelle:experimental_allow_relative_imports true

gazelle/python/testdata/relative_imports_package_mode/BUILD.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
load("@rules_python//python:defs.bzl", "py_binary")
22

33
# gazelle:python_generation_mode package
4+
# gazelle:experimental_allow_relative_imports true
45

56
py_binary(
67
name = "relative_imports_package_mode_bin",
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
# Resolve deps for relative imports
22

3-
This test case verifies that the generated targets correctly handle relative imports in Python. Specifically, when the Python generation mode is set to "package," it ensures that relative import statements such as from .foo import X are properly resolved to their corresponding modules.
3+
This test case verifies that the generated targets correctly handle relative imports in
4+
Python. Specifically, when the Python generation mode is set to "package," it ensures
5+
that relative import statements such as from .foo import X are properly resolved to
6+
their corresponding modules.
Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
2-
from ...my_library import some_function # Import path should be package1.my_library.some_function
3-
from ...my_library.foo import some_function # Import path should be package1.my_library.foo.some_function
4-
from .library import other_module # Import path should be package1.subpackage1.subpackage2.library.other_module
5-
from .. import some_module # Import path should be package1.subpackage1.some_module
6-
from .. import some_function # Import path should be package1.subpackage1.some_function
1+
from ...my_library import (
2+
some_function,
3+
) # Import path should be package1.my_library.some_function
4+
from ...my_library.foo import (
5+
some_function,
6+
) # Import path should be package1.my_library.foo.some_function
7+
from .library import (
8+
other_module,
9+
) # Import path should be package1.subpackage1.subpackage2.library.other_module
10+
from .. import some_module # Import path should be package1.subpackage1.some_module
11+
from .. import some_function # Import path should be package1.subpackage1.some_function
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Relative imports
22

33
This test case asserts that the generated targets handle relative imports in
4-
Python correctly. This tests that if python generation mode is project, the relative paths are included in the subdirectories.
4+
Python correctly. This tests that if python generation mode is project,
5+
the relative paths are included in the subdirectories.

gazelle/pythonconfig/pythonconfig.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ const (
9191
// names of labels to third-party dependencies are normalized. Supported values
9292
// are 'none', 'pep503' and 'snake_case' (default). See LabelNormalizationType.
9393
LabelNormalization = "python_label_normalization"
94+
// ExperimentalAllowRelativeImports represents the directive that controls
95+
// whether relative imports are allowed.
96+
ExperimentalAllowRelativeImports = "experimental_allow_relative_imports"
9497
)
9598

9699
// GenerationModeType represents one of the generation modes for the Python
@@ -177,6 +180,7 @@ type Config struct {
177180
testFilePattern []string
178181
labelConvention string
179182
labelNormalization LabelNormalizationType
183+
experimentalAllowRelativeImports bool
180184
}
181185

182186
type LabelNormalizationType int
@@ -212,6 +216,7 @@ func New(
212216
testFilePattern: strings.Split(DefaultTestFilePatternString, ","),
213217
labelConvention: DefaultLabelConvention,
214218
labelNormalization: DefaultLabelNormalizationType,
219+
experimentalAllowRelativeImports: false,
215220
}
216221
}
217222

@@ -244,6 +249,7 @@ func (c *Config) NewChild() *Config {
244249
testFilePattern: c.testFilePattern,
245250
labelConvention: c.labelConvention,
246251
labelNormalization: c.labelNormalization,
252+
experimentalAllowRelativeImports: c.experimentalAllowRelativeImports,
247253
}
248254
}
249255

@@ -520,6 +526,16 @@ func (c *Config) LabelNormalization() LabelNormalizationType {
520526
return c.labelNormalization
521527
}
522528

529+
// SetExperimentalAllowRelativeImports sets whether relative imports are allowed.
530+
func (c *Config) SetExperimentalAllowRelativeImports(allowRelativeImports bool) {
531+
c.experimentalAllowRelativeImports = allowRelativeImports
532+
}
533+
534+
// ExperimentalAllowRelativeImports returns whether relative imports are allowed.
535+
func (c *Config) ExperimentalAllowRelativeImports() bool {
536+
return c.experimentalAllowRelativeImports
537+
}
538+
523539
// FormatThirdPartyDependency returns a label to a third-party dependency performing all formating and normalization.
524540
func (c *Config) FormatThirdPartyDependency(repositoryName string, distributionName string) label.Label {
525541
conventionalDistributionName := strings.ReplaceAll(c.labelConvention, distributionNameLabelConventionSubstitution, distributionName)

0 commit comments

Comments
 (0)