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