Skip to content
Open
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: 2 additions & 1 deletion ms_agent/utils/parser_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ def parse(self, code_content: str) -> List[ImportInfo]:
imports.append(info)

# Pattern 2: import ...
import_pattern = r'^\s*import\s+([\w.,\s]+)'
# Horizontal whitespace keeps the match within the import's line.
import_pattern = r'^[ \t\f]*import[ \t\f]+([\w., \t\f]+)'
for match in re.finditer(import_pattern, code_content, re.MULTILINE):
infos = self._extract_simple_import(match)
imports.extend(infos)
Expand Down
52 changes: 52 additions & 0 deletions tests/utils/test_python_import_lines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
"""Project imports must not consume subsequent Python statements."""
import ast

import pytest
from ms_agent.utils.parser_utils import parse_imports


@pytest.fixture
def project(tmp_path):
for name in ('first_module.py', 'second_module.py', 'main.py'):
(tmp_path / name).touch()
return tmp_path


@pytest.mark.parametrize('newline', ['\n', '\r\n'])
@pytest.mark.parametrize('lines, expected', [
(['import first_module', 'import second_module'],
{'first_module.py': ('first_module', None),
'second_module.py': ('second_module', None)}),
(['import first_module as first', 'import second_module as second'],
{'first_module.py': ('first_module', 'first'),
'second_module.py': ('second_module', 'second')}),
(['import first_module', 'from second_module import helper'],
{'first_module.py': ('first_module', None),
'second_module.py': ('helper', None)}),
(['import first_module', '', 'first_module.run()'],
{'first_module.py': ('first_module', None)}),
])
def test_import_stops_at_line_end(project, newline, lines, expected):
content = newline.join(lines) + newline
ast.parse(content)

imports = parse_imports(str(project / 'main.py'), content, str(project))

assert len(imports) == len(expected)
assert {item.source_file: (item.imported_items[0], item.alias)
for item in imports} == expected


@pytest.mark.parametrize('whitespace', [' ', '\t', '\f'])
def test_comma_separated_imports_keep_aliases(project, whitespace):
content = f'import{whitespace}first_module as first, second_module as second'
ast.parse(content)

imports = parse_imports(str(project / 'main.py'), content, str(project))

assert [(item.source_file, item.imported_items, item.alias)
for item in imports] == [
('first_module.py', ['first_module'], 'first'),
('second_module.py', ['second_module'], 'second'),
]