Skip to content

Commit

Permalink
fix: bug with scopes
Browse files Browse the repository at this point in the history
Fix #307
  • Loading branch information
Bruno Alla authored and browniebroke committed Jan 20, 2021
1 parent a403e90 commit 3dcee1d
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
20 changes: 20 additions & 0 deletions django_codemod/visitors/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
BaseExpression,
BaseSmallStatement,
Call,
CSTNode,
ImportAlias,
ImportFrom,
ImportStar,
Expand All @@ -18,6 +19,7 @@
from libcst import matchers as m
from libcst.codemod import ContextAwareTransformer
from libcst.codemod.visitors import AddImportsVisitor
from libcst.metadata import Scope, ScopeProvider


class BaseDjCodemodTransformer(ContextAwareTransformer, ABC):
Expand Down Expand Up @@ -93,6 +95,7 @@ def leave_ImportFrom(
return super().leave_ImportFrom(original_node, updated_node)
# This is a match
new_names = list(self.gen_new_imported_names(updated_node.names))
self.save_import_scope(original_node)
if not new_names:
# Nothing left in the import statement: remove it
return RemoveFromParent()
Expand All @@ -113,6 +116,22 @@ def gen_new_imported_names(
continue
yield import_alias

def resolve_scope(self, node: CSTNode) -> Scope:
scopes_map = self.context.wrapper.resolve(ScopeProvider)
return scopes_map[node]

def save_import_scope(self, import_from: ImportFrom) -> None:
scope = self.resolve_scope(import_from)
self.context.scratch[self.ctx_key_import_scope] = scope

@property
def import_scope(self) -> Optional[Scope]:
return self.context.scratch.get(self.ctx_key_import_scope, None)

@property
def ctx_key_import_scope(self) -> str:
return f"{self.rename_from}-import_scope"

@staticmethod
def tidy_new_imported_names(
new_names: Sequence[ImportAlias],
Expand Down Expand Up @@ -153,6 +172,7 @@ def leave_Name(self, original_node: Name, updated_node: Name) -> BaseExpression:
self.is_imported_with_old_name
and not self.entity_is_name_called
and m.matches(updated_node, m.Name(value=self.old_name))
and self.resolve_scope(original_node) == self.import_scope
):
return updated_node.with_changes(value=self.new_name)
return super().leave_Name(original_node, updated_node)
Expand Down
22 changes: 22 additions & 0 deletions tests/visitors/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,28 @@ def test_lambda_no_value(self) -> None:
"""
self.assertCodemod(before, after)

def test_name_from_outer_scope(self) -> None:
"""When import from outer scope has same name as function variable."""
before = """
from django.dummy.module import func
result = func()
def something():
func = get_func()
return func
"""
after = """
from django.dummy.module import better_func
result = better_func()
def something():
func = get_func()
return func
"""
self.assertCodemod(before, after)


class OtherModuleFuncRenameTransformer(BaseFuncRenameTransformer):
"""Transformer with different module."""
Expand Down

0 comments on commit 3dcee1d

Please sign in to comment.