Skip to content

0.6.0

Choose a tag to compare

@isidentical isidentical released this 22 Oct 21:59

0.6.0

Major

This release adds experimental support for chained actions, a long awaited
feature. This would mean that each match() can now return multiple actions (in
the form of an iterator), and they will be applied gradually.

import ast
from refactor import Rule, actions
from refactor.context import Representative, Scope
from typing import Iterator


class Usages(Representative):
    context_providers = (Scope,)

    def find(self, name: str, needle: ast.AST) -> Iterator[ast.AST]:
        """Iterate all possible usage sites of ``name``."""
        for node in ast.walk(self.context.tree):
            if isinstance(node, ast.Name) and node.id == name:
                scope = self.context.scope.resolve(node)
                if needle in scope.get_definitions(name):
                    yield node


class PropagateAndDelete(Rule):
    context_providers = (Usages,)

    def match(self, node: ast.Import) -> Iterator[actions.BaseAction]:
        # Check if this is a single import with no alias.
        assert isinstance(node, ast.Import)
        assert len(node.names) == 1

        [name] = node.names
        assert name.asname is None

        # Replace each usage of this module with its own __import__() call.
        import_call = ast.Call(
            func=ast.Name("__import__"),
            args=[ast.Constant(name.name)],
            keywords=[],
        )
        for usage in self.context.usages.find(name.name, node):
            yield actions.Replace(usage, import_call)

        # And finally remove the import itself
        yield actions.Erase(node)

Other Changes

  • Encoding is now preserved when using the refactor.Session.run_file API (which means if you use the refactor.Change.apply_diff or using the -a flag in the CLI, the generated source code will be encoded
    with the original encoding before getting written to the file.)
  • Offset tracking has been changed to be at the encoded byte stream level (mirroring CPython behavior here). This fixes some unicode related problems (with actions like refactor.actions.Replace).
  • refactor.context.ScopeInfo.get_definitions now always returns a list, even
    if it can't find any definitions.