175 lines
5.6 KiB
Python
175 lines
5.6 KiB
Python
from rope.base import ast, evaluate, builtins, pyobjects
|
|
from rope.refactor import patchedast, occurrences
|
|
|
|
|
|
class Wildcard(object):
|
|
def get_name(self):
|
|
"""Return the name of this wildcard"""
|
|
|
|
def matches(self, suspect, arg):
|
|
"""Return `True` if `suspect` matches this wildcard"""
|
|
|
|
|
|
class Suspect(object):
|
|
def __init__(self, pymodule, node, name):
|
|
self.name = name
|
|
self.pymodule = pymodule
|
|
self.node = node
|
|
|
|
|
|
class DefaultWildcard(object):
|
|
"""The default restructuring wildcard
|
|
|
|
The argument passed to this wildcard is in the
|
|
``key1=value1,key2=value2,...`` format. Possible keys are:
|
|
|
|
* name - for checking the reference
|
|
* type - for checking the type
|
|
* object - for checking the object
|
|
* instance - for checking types but similar to builtin isinstance
|
|
* exact - matching only occurrences with the same name as the wildcard
|
|
* unsure - matching unsure occurrences
|
|
|
|
"""
|
|
|
|
def __init__(self, project):
|
|
self.project = project
|
|
|
|
def get_name(self):
|
|
return "default"
|
|
|
|
def matches(self, suspect, arg=""):
|
|
args = parse_arg(arg)
|
|
|
|
if not self._check_exact(args, suspect):
|
|
return False
|
|
if not self._check_object(args, suspect):
|
|
return False
|
|
return True
|
|
|
|
def _check_object(self, args, suspect):
|
|
kind = None
|
|
expected = None
|
|
unsure = args.get("unsure", False)
|
|
for check in ["name", "object", "type", "instance"]:
|
|
if check in args:
|
|
kind = check
|
|
expected = args[check]
|
|
if expected is not None:
|
|
checker = _CheckObject(self.project, expected, kind, unsure=unsure)
|
|
return checker(suspect.pymodule, suspect.node)
|
|
return True
|
|
|
|
def _check_exact(self, args, suspect):
|
|
node = suspect.node
|
|
if args.get("exact"):
|
|
if not isinstance(node, ast.Name) or not node.id == suspect.name:
|
|
return False
|
|
else:
|
|
if not isinstance(node, ast.expr):
|
|
return False
|
|
return True
|
|
|
|
|
|
def parse_arg(arg):
|
|
if isinstance(arg, dict):
|
|
return arg
|
|
result = {}
|
|
tokens = arg.split(",")
|
|
for token in tokens:
|
|
if "=" in token:
|
|
parts = token.split("=", 1)
|
|
result[parts[0].strip()] = parts[1].strip()
|
|
else:
|
|
result[token.strip()] = True
|
|
return result
|
|
|
|
|
|
class _CheckObject(object):
|
|
def __init__(self, project, expected, kind="object", unsure=False):
|
|
self.project = project
|
|
self.kind = kind
|
|
self.unsure = unsure
|
|
self.expected = self._evaluate(expected)
|
|
|
|
def __call__(self, pymodule, node):
|
|
pyname = self._evaluate_node(pymodule, node)
|
|
if pyname is None or self.expected is None:
|
|
return self.unsure
|
|
if self._unsure_pyname(pyname, unbound=self.kind == "name"):
|
|
return True
|
|
if self.kind == "name":
|
|
return self._same_pyname(self.expected, pyname)
|
|
else:
|
|
pyobject = pyname.get_object()
|
|
if self.kind == "object":
|
|
objects = [pyobject]
|
|
if self.kind == "type":
|
|
objects = [pyobject.get_type()]
|
|
if self.kind == "instance":
|
|
objects = [pyobject]
|
|
objects.extend(self._get_super_classes(pyobject))
|
|
objects.extend(self._get_super_classes(pyobject.get_type()))
|
|
for pyobject in objects:
|
|
if self._same_pyobject(self.expected.get_object(), pyobject):
|
|
return True
|
|
return False
|
|
|
|
def _get_super_classes(self, pyobject):
|
|
result = []
|
|
if isinstance(pyobject, pyobjects.AbstractClass):
|
|
for superclass in pyobject.get_superclasses():
|
|
result.append(superclass)
|
|
result.extend(self._get_super_classes(superclass))
|
|
return result
|
|
|
|
def _same_pyobject(self, expected, pyobject):
|
|
return expected == pyobject
|
|
|
|
def _same_pyname(self, expected, pyname):
|
|
return occurrences.same_pyname(expected, pyname)
|
|
|
|
def _unsure_pyname(self, pyname, unbound=True):
|
|
return self.unsure and occurrences.unsure_pyname(pyname, unbound)
|
|
|
|
def _split_name(self, name):
|
|
parts = name.split(".")
|
|
expression, kind = parts[0], parts[-1]
|
|
if len(parts) == 1:
|
|
kind = "name"
|
|
return expression, kind
|
|
|
|
def _evaluate_node(self, pymodule, node):
|
|
scope = pymodule.get_scope().get_inner_scope_for_line(node.lineno)
|
|
expression = node
|
|
if isinstance(expression, ast.Name) and isinstance(expression.ctx, ast.Store):
|
|
start, end = patchedast.node_region(expression)
|
|
text = pymodule.source_code[start:end]
|
|
return evaluate.eval_str(scope, text)
|
|
else:
|
|
return evaluate.eval_node(scope, expression)
|
|
|
|
def _evaluate(self, code):
|
|
attributes = code.split(".")
|
|
pyname = None
|
|
if attributes[0] in ("__builtin__", "__builtins__"):
|
|
|
|
class _BuiltinsStub(object):
|
|
def get_attribute(self, name):
|
|
return builtins.builtins[name]
|
|
|
|
def __getitem__(self, name):
|
|
return builtins.builtins[name]
|
|
|
|
def __contains__(self, name):
|
|
return name in builtins.builtins
|
|
|
|
pyobject = _BuiltinsStub()
|
|
else:
|
|
pyobject = self.project.get_module(attributes[0])
|
|
for attribute in attributes[1:]:
|
|
pyname = pyobject[attribute]
|
|
if pyname is None:
|
|
return None
|
|
pyobject = pyname.get_object()
|
|
return pyname
|