1from rope.base import ast, evaluate, builtins, pyobjects 2from rope.refactor import patchedast, occurrences 3 4 5class Wildcard(object): 6 7 def get_name(self): 8 """Return the name of this wildcard""" 9 10 def matches(self, suspect, arg): 11 """Return `True` if `suspect` matches this wildcard""" 12 13 14class Suspect(object): 15 16 def __init__(self, pymodule, node, name): 17 self.name = name 18 self.pymodule = pymodule 19 self.node = node 20 21 22class DefaultWildcard(object): 23 """The default restructuring wildcard 24 25 The argument passed to this wildcard is in the 26 ``key1=value1,key2=value2,...`` format. Possible keys are: 27 28 * name - for checking the reference 29 * type - for checking the type 30 * object - for checking the object 31 * instance - for checking types but similar to builtin isinstance 32 * exact - matching only occurrences with the same name as the wildcard 33 * unsure - matching unsure occurrences 34 35 """ 36 37 def __init__(self, project): 38 self.project = project 39 40 def get_name(self): 41 return 'default' 42 43 def matches(self, suspect, arg=''): 44 args = parse_arg(arg) 45 46 if not self._check_exact(args, suspect): 47 return False 48 if not self._check_object(args, suspect): 49 return False 50 return True 51 52 def _check_object(self, args, suspect): 53 kind = None 54 expected = None 55 unsure = args.get('unsure', False) 56 for check in ['name', 'object', 'type', 'instance']: 57 if check in args: 58 kind = check 59 expected = args[check] 60 if expected is not None: 61 checker = _CheckObject(self.project, expected, 62 kind, unsure=unsure) 63 return checker(suspect.pymodule, suspect.node) 64 return True 65 66 def _check_exact(self, args, suspect): 67 node = suspect.node 68 if args.get('exact'): 69 if not isinstance(node, ast.Name) or not node.id == suspect.name: 70 return False 71 else: 72 if not isinstance(node, ast.expr): 73 return False 74 return True 75 76 77def parse_arg(arg): 78 if isinstance(arg, dict): 79 return arg 80 result = {} 81 tokens = arg.split(',') 82 for token in tokens: 83 if '=' in token: 84 parts = token.split('=', 1) 85 result[parts[0].strip()] = parts[1].strip() 86 else: 87 result[token.strip()] = True 88 return result 89 90 91class _CheckObject(object): 92 93 def __init__(self, project, expected, kind='object', unsure=False): 94 self.project = project 95 self.kind = kind 96 self.unsure = unsure 97 self.expected = self._evaluate(expected) 98 99 def __call__(self, pymodule, node): 100 pyname = self._evaluate_node(pymodule, node) 101 if pyname is None or self.expected is None: 102 return self.unsure 103 if self._unsure_pyname(pyname, unbound=self.kind == 'name'): 104 return True 105 if self.kind == 'name': 106 return self._same_pyname(self.expected, pyname) 107 else: 108 pyobject = pyname.get_object() 109 if self.kind == 'object': 110 objects = [pyobject] 111 if self.kind == 'type': 112 objects = [pyobject.get_type()] 113 if self.kind == 'instance': 114 objects = [pyobject] 115 objects.extend(self._get_super_classes(pyobject)) 116 objects.extend(self._get_super_classes(pyobject.get_type())) 117 for pyobject in objects: 118 if self._same_pyobject(self.expected.get_object(), pyobject): 119 return True 120 return False 121 122 def _get_super_classes(self, pyobject): 123 result = [] 124 if isinstance(pyobject, pyobjects.AbstractClass): 125 for superclass in pyobject.get_superclasses(): 126 result.append(superclass) 127 result.extend(self._get_super_classes(superclass)) 128 return result 129 130 def _same_pyobject(self, expected, pyobject): 131 return expected == pyobject 132 133 def _same_pyname(self, expected, pyname): 134 return occurrences.same_pyname(expected, pyname) 135 136 def _unsure_pyname(self, pyname, unbound=True): 137 return self.unsure and occurrences.unsure_pyname(pyname, unbound) 138 139 def _split_name(self, name): 140 parts = name.split('.') 141 expression, kind = parts[0], parts[-1] 142 if len(parts) == 1: 143 kind = 'name' 144 return expression, kind 145 146 def _evaluate_node(self, pymodule, node): 147 scope = pymodule.get_scope().get_inner_scope_for_line(node.lineno) 148 expression = node 149 if isinstance(expression, ast.Name) and \ 150 isinstance(expression.ctx, ast.Store): 151 start, end = patchedast.node_region(expression) 152 text = pymodule.source_code[start:end] 153 return evaluate.eval_str(scope, text) 154 else: 155 return evaluate.eval_node(scope, expression) 156 157 def _evaluate(self, code): 158 attributes = code.split('.') 159 pyname = None 160 if attributes[0] in ('__builtin__', '__builtins__'): 161 class _BuiltinsStub(object): 162 def get_attribute(self, name): 163 return builtins.builtins[name] 164 165 def __getitem__(self, name): 166 return builtins.builtins[name] 167 168 def __contains__(self, name): 169 return name in builtins.builtins 170 pyobject = _BuiltinsStub() 171 else: 172 pyobject = self.project.get_module(attributes[0]) 173 for attribute in attributes[1:]: 174 pyname = pyobject[attribute] 175 if pyname is None: 176 return None 177 pyobject = pyname.get_object() 178 return pyname 179