Skip to content

Commit f602ffa

Browse files
committed
address comments
1 parent 7f53d6c commit f602ffa

File tree

4 files changed

+94
-23
lines changed

4 files changed

+94
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ END_UNRELEASED_TEMPLATE
6969
* (toolchain) Python 3.13 now references 3.13.5
7070
* (gazelle) Switched back to smacker/go-tree-sitter, fixing
7171
[#2630](https://github.com/bazel-contrib/rules_python/issues/2630)
72-
* (gazelle) Cleanup multi-line python imports modules
7372

7473
{#v0-0-0-fixed}
7574
### Fixed
@@ -87,6 +86,7 @@ END_UNRELEASED_TEMPLATE
8786
({gh-issue}`3043`).
8887
* (pypi) The pipstar `defaults` configuration now supports any custom platform
8988
name.
89+
* Multi-line python imports (e.g. with escaped newlines) are now correctly processed by Gazelle.
9090

9191
{#v0-0-0-added}
9292
### Added

gazelle/python/file_parser.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,11 @@ func parseImportStatement(node *sitter.Node, code []byte) (Module, bool) {
146146

147147
// cleanImportString removes backslashes and all whitespace from the string.
148148
func cleanImportString(s string) string {
149+
s = strings.ReplaceAll(s, "\r\n", "")
149150
s = strings.ReplaceAll(s, "\\", "")
150151
s = strings.ReplaceAll(s, " ", "")
151152
s = strings.ReplaceAll(s, "\n", "")
153+
s = strings.ReplaceAll(s, "\t", "")
152154
return s
153155
}
154156

@@ -163,6 +165,7 @@ func (p *FileParser) parseImportStatements(node *sitter.Node) bool {
163165
continue
164166
}
165167
m.From = cleanImportString(m.From)
168+
m.Name = cleanImportString(m.Name)
166169
m.Filepath = p.relFilepath
167170
m.TypeCheckingOnly = p.inTypeCheckingBlock
168171
if strings.HasPrefix(m.Name, ".") {
@@ -185,6 +188,7 @@ func (p *FileParser) parseImportStatements(node *sitter.Node) bool {
185188
}
186189
m.Filepath = p.relFilepath
187190
m.From = from
191+
m.Name = cleanImportString(m.Name)
188192
m.Name = fmt.Sprintf("%s.%s", from, m.Name)
189193
m.TypeCheckingOnly = p.inTypeCheckingBlock
190194
p.output.Modules = append(p.output.Modules, m)

gazelle/python/file_parser_test.go

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -293,30 +293,93 @@ def example_function():
293293
}
294294

295295
func TestParseImportStatements_MultilineWithBackslashAndWhitespace(t *testing.T) {
296-
p := NewFileParser()
297-
code := []byte(`from foo.bar.\
296+
t.Parallel()
297+
t.Run("multiline from import", func(t *testing.T) {
298+
p := NewFileParser()
299+
code := []byte(`from foo.bar.\
298300
baz import (
299301
Something,
300302
AnotherThing
301303
)
304+
305+
from foo\
306+
.test import (
307+
Foo,
308+
Bar
309+
)
302310
`)
303-
p.SetCodeAndFile(code, "", "test.py")
304-
output, err := p.Parse(context.Background())
305-
assert.NoError(t, err)
306-
// Should parse as: from foo.bar.baz import Something, AnotherThing
307-
expected := []Module{
308-
{
309-
Name: "foo.bar.baz.Something",
310-
LineNumber: 3,
311-
Filepath: "test.py",
312-
From: "foo.bar.baz",
313-
},
314-
{
315-
Name: "foo.bar.baz.AnotherThing",
316-
LineNumber: 4,
317-
Filepath: "test.py",
318-
From: "foo.bar.baz",
319-
},
320-
}
321-
assert.Equal(t, expected, output.Modules)
311+
p.SetCodeAndFile(code, "", "test.py")
312+
output, err := p.Parse(context.Background())
313+
assert.NoError(t, err)
314+
// Updated expected to match parser output
315+
expected := []Module{
316+
{
317+
Name: "foo.bar.baz.Something",
318+
LineNumber: 3,
319+
Filepath: "test.py",
320+
From: "foo.bar.baz",
321+
},
322+
{
323+
Name: "foo.bar.baz.AnotherThing",
324+
LineNumber: 4,
325+
Filepath: "test.py",
326+
From: "foo.bar.baz",
327+
},
328+
{
329+
Name: "foo.test.Foo",
330+
LineNumber: 9,
331+
Filepath: "test.py",
332+
From: "foo.test",
333+
},
334+
{
335+
Name: "foo.test.Bar",
336+
LineNumber: 10,
337+
Filepath: "test.py",
338+
From: "foo.test",
339+
},
340+
}
341+
assert.ElementsMatch(t, expected, output.Modules)
342+
})
343+
t.Run("multiline import", func(t *testing.T) {
344+
p := NewFileParser()
345+
code := []byte(`import foo.bar.\
346+
baz
347+
`)
348+
p.SetCodeAndFile(code, "", "test.py")
349+
output, err := p.Parse(context.Background())
350+
assert.NoError(t, err)
351+
// Updated expected to match parser output
352+
expected := []Module{
353+
{
354+
Name: "foo.bar.baz",
355+
LineNumber: 1,
356+
Filepath: "test.py",
357+
From: "",
358+
},
359+
}
360+
assert.ElementsMatch(t, expected, output.Modules)
361+
})
362+
t.Run("windows line endings", func(t *testing.T) {
363+
p := NewFileParser()
364+
code := []byte("from foo.bar.\r\n baz import (\r\n Something,\r\n AnotherThing\r\n)\r\n")
365+
p.SetCodeAndFile(code, "", "test.py")
366+
output, err := p.Parse(context.Background())
367+
assert.NoError(t, err)
368+
// Updated expected to match parser output
369+
expected := []Module{
370+
{
371+
Name: "foo.bar.baz.Something",
372+
LineNumber: 3,
373+
Filepath: "test.py",
374+
From: "foo.bar.baz",
375+
},
376+
{
377+
Name: "foo.bar.baz.AnotherThing",
378+
LineNumber: 4,
379+
Filepath: "test.py",
380+
From: "foo.bar.baz",
381+
},
382+
}
383+
assert.ElementsMatch(t, expected, output.Modules)
384+
})
322385
}

gazelle/python/testdata/from_imports/import_nested_var/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@
1313
# limitations under the License.
1414

1515
# baz is a variable in foo/bar/baz.py
16-
from foo.bar.baz import baz
16+
from foo\
17+
.bar.\
18+
baz import (
19+
baz
20+
)

0 commit comments

Comments
 (0)