API Reference#


class refactor.actions.BaseAction#

A source code transformation action.

apply(context: refactor.context.Context, source: str) str#

Takes the bound refactor.context.Context and the source code of the current module and returns the transformed version.

class refactor.actions.Erase#

Erases the given node statement from source code. Be careful when using this action, as it can’t remove required statements (e.g. if the node is the only child statement of the parent node).


If you want to quickly get rid of a statement without doing your own analysis first (in order to determine whether it is required or not), you can use the EraseOrReplace.

__init__(node: ast.stmt) None#
class refactor.actions.EraseOrReplace#

Erases the given node statement if it is not required (e.g. if it is not the only child statement of the parent node). Otherwise replaces it with the re-synthesized version of the given target statement (by default, it is pass).

__init__(node: ast.stmt, target: ast.stmt = <factory>) None#
class refactor.actions.InsertAfter#

Inserts the re-synthesized version of given target right after the given node.


This action requires both the node and target to be a statements.

__init__(node: refactor.actions.K, target: ast.stmt) None#
build() ast.stmt#

Create the new node.

exception refactor.actions.InvalidActionError#

An improper usage of an action.

class refactor.actions.LazyInsertAfter#

Inserts the re-synthesized version LazyInsertAfter.build()’s output right after the given node.


Subclasses of LazyInsertAfter must override LazyInsertAfter.build().


This action requires both the node and the built target to be statements.

__init__(node: refactor.actions.K) None#
apply(context: refactor.context.Context, source: str) str#

Takes the bound refactor.context.Context and the source code of the current module and returns the transformed version.

class refactor.actions.LazyReplace#

Transforms the code segment of the given node with the re-synthesized version LazyReplace.build()’s output.


Subclasses of LazyReplace must override LazyReplace.build().

__init__(node: refactor.actions.K) None#
class refactor.actions.Replace#

Transforms the code segment of the given node with the re-synthesized version of target.

__init__(node: refactor.actions.K, target: ast.AST) None#
build() ast.AST#

Create the new node.


class refactor.ast.BaseUnparser#

A public ast._Unparser API that can be used to customize the AST re-synthesis process.

__init__(*args: Any, **kwargs: Any) None#
class refactor.ast.Lines#

Lines(lines: ‘List[StringType]’)

__init__(lines: List[refactor.ast.StringType]) None#
apply_indentation(indentation: refactor.ast.StringType, *, start_prefix: Union[str, refactor.ast.SourceSegment] = '', end_suffix: Union[str, refactor.ast.SourceSegment] = '') None#

Apply the given indentation, optionally with start and end prefixes to the bound source lines.

join() str#

Return the combined source code.

class refactor.ast.PreciseUnparser#

A more precise version of the default unparser, with various improvements such as comment handling for major statements and child node recovery.

__init__(*args: Any, **kwargs: Any) None#
class refactor.ast.SourceSegment#

Adapter for holding a line of source code that can be sliced with AST-native column offsets. Internally on every partitioning operation, the offsets will be assumed as UTF-8 encoded byte offsets (which is the default Refactor operates on).

__init__(data: str, encoding: str = 'utf-8') None#
class refactor.ast.Unparser#
__init__(*args, **kwargs)#
refactor.ast.split_lines(source: str, *, encoding: Optional[str] = None) refactor.ast.Lines#

Split the given source code into lines and return a list-like object (refactor.ast.Lines).


class refactor.change.Change#

The result of a transformation in the bound file.

Includes both the original source code, and the transformed variant.

__init__(file_info: refactor.common._FileInfo, original_source: str, refactored_source: str) None#
apply_diff() None#

Apply the transformed version to the bound file.

compute_diff() str#

Compute the line-based diff between original and the refactored source lines.

property file: pathlib.Path#

Returns the bound file.


refactor.common.apply_condition(condition: bool, node: ast.expr) ast.expr#

Negate the given node if the given condition is a falsy value.

refactor.common.clone(node: refactor.common.T) refactor.common.T#

Clone the given node.

refactor.common.compare_ast(left: ast.AST, right: ast.AST, /) bool#

Compare 2 AST nodes.

refactor.common.extract_from_text(text: str) ast.AST#

Extract the first AST node from the given text’s parsed AST.

refactor.common.find_closest(node: ast.AST, *targets: ast.AST) ast.AST#

Find the closest node to the given node from the given sequence of targets (uses absolute distance from starting points).

refactor.common.find_indent(source: str) Tuple[str, str]#

Split the given line into the current indentation and the remaining characters.

refactor.common.get_source_segment(source: str, node: ast.AST) Optional[str]#

Faster (and less-precise) implementation of ast.get_source_segment

refactor.common.has_positions(node_type: Type[ast.AST]) bool#

Return True if the given node_type tracks source positions.

refactor.common.is_truthy(op: ast.cmpop) Optional[bool]#

Return True for truth-based comparison operators (e.g ==, is, in), False for falsity-based operators (e.g !=, is not, not in) and None for others.

refactor.common.negate(node: ast.expr) ast.UnaryOp#

Negate the given node.

refactor.common.next_statement_of(node: ast.stmt, context: Context) Optional[ast.stmt]#

Get the statement that follows node in the same syntactical block.

refactor.common.pascal_to_snake(name: str) str#

Convert the given name from pascal case to snake case.

refactor.common.position_for(node: ast.AST) Tuple[int, int, int, int]#

Return a 4-item tuple of positions for the given node.

refactor.common.unpack_lhs(node: ast.AST) Iterator[str]#

Unpack assignment targets to individual identifiers

refactor.common.walk_scope(node: ast.AST) Iterator[ast.AST]#

Like regular ast.walk() but only walks within the current scope.

refactor.common.wrap_with_parens(text: str) str#

Wrap the given text with parens.


class refactor.context.Ancestry#

A context provider that helps you to backtrack nodes using their ancestral chain in AST.

get_parent(node: ast.AST) Optional[ast.AST]#

Return the parent AST node of the given node.

get_parents(node: ast.AST) Iterable[ast.AST]#

Recursively yield all the parent AST nodes of the given node.

infer(node: ast.AST) Tuple[Optional[str], Optional[ast.AST]]#

Return the given node’s parent field (the field name in parent which this node is stored in) and the parent.

traverse(node: ast.AST) Iterable[Tuple[str, ast.AST]]#

Recursively infer a node’s parent field and parent.

class refactor.context.Configuration#

Configuration settings for a refactoring session.

unparser: precise, fast, or a BaseUnparser subclass. debug_mode: whether to output more debug information.

__init__(unparser: Union[str, Type[refactor.ast.BaseUnparser]] = 'precise', debug_mode: bool = False) None#
class refactor.context.Context#

The knowledge base of the currently processed module. Includes the original source code, the full AST, as well as the all the representatives.

__init__(source: str, tree: ast.AST, file_info: refactor.common._FileInfo = <factory>, config: refactor.context.Configuration = <factory>, metadata: typing.Dict[str, refactor.context.Representative] = <factory>) None#
replace(*args: Any, **kwargs: Any) refactor.context.Context#

Return a new context object with the given arguments replaced.

unparse(node: ast.AST) str#

Re-synthesize the source code for the given node.

class refactor.context.Representative#

A tree-scoped metadata collector.

__init__(context: refactor.context.Context) None#
property name: str#

Name of the representative (to be used when accessing from the scope). By default, it is the snake case version of the class’ name.

class refactor.context.Scope#

A context provider for working with semantical Python scopes.

property global_scope: refactor.context.ScopeInfo#

Return the global scope for the current module.

resolve(node: ast.AST) refactor.context.ScopeInfo#

Return the scope record of the given node.

class refactor.context.ScopeInfo#

ScopeInfo(node: ‘ast.AST’, scope_type: ‘ScopeType’, parent: ‘Optional[ScopeInfo]’ = None)

__init__(node: ast.AST, scope_type: refactor.context.ScopeType, parent: Optional[refactor.context.ScopeInfo] = None) None#
can_reach(other: refactor.context.ScopeInfo) bool#

Return whether this scope can access definitions from other scope.

defines(name: str) bool#

Return whether this scope defines the given name.

property definitions: Dict[str, List[ast.AST]]#

Return all the definitions made inside this scope.


It doesn’t include definitions made in child scopes.

get_definitions(name: str) List[ast.AST]#

Return all the definitions of the given name that this scope can reach. Returns an empty list if there is None.

property name: str#

Return the name of this scope.

class refactor.context.ScopeType#

An enumeration.


exception refactor.core.MaybeOverlappingActions#
class refactor.core.Rule#

Rule(context: ‘Context’)

__init__(context: refactor.context.Context) None#
check_file(path: Optional[pathlib.Path]) bool#

Check whether to process the given path. If path is None, that means the user has submitted a string to be processed.

By default it will always be True but can be overridden in subclasses.

match(node: ast.AST) Union[refactor.actions.BaseAction, None, Iterator[refactor.actions.BaseAction]]#

Match the given node against current rule’s scope.

On success, it will return a source code transformation action (an instance of refactor.actions.BaseAction). On failure it might either raise an AssertionError or return None.

class refactor.core.Session#

A refactoring session that consists of a set of rules and a configuration.

__init__(rules: typing.List[typing.Type[refactor.core.Rule]] = <factory>, config: refactor.context.Configuration = <factory>) None#
run(source: str) str#

Apply all the rules from this session to the given source and return the transformed version.

In case of the given source is not parsable, it will return it unchanged.

run_file(file: pathlib.Path) Optional[refactor.change.Change]#

Apply all the rules from this session to the given file and return a refactor.Change if any changes were made.

In case of the given file is not parsable, it will return None.