]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | from jedi import debug |
2 | from jedi.inference.base_value import ValueSet, \ | |
3 | NO_VALUES | |
4 | from jedi.inference.utils import to_list | |
5 | from jedi.inference.gradual.stub_value import StubModuleValue | |
6 | from jedi.inference.gradual.typeshed import try_to_load_stub_cached | |
7 | from jedi.inference.value.decorator import Decoratee | |
8 | ||
9 | ||
10 | def _stub_to_python_value_set(stub_value, ignore_compiled=False): | |
11 | stub_module_context = stub_value.get_root_context() | |
12 | if not stub_module_context.is_stub(): | |
13 | return ValueSet([stub_value]) | |
14 | ||
15 | decorates = None | |
16 | if isinstance(stub_value, Decoratee): | |
17 | decorates = stub_value._original_value | |
18 | ||
19 | was_instance = stub_value.is_instance() | |
20 | if was_instance: | |
21 | arguments = getattr(stub_value, '_arguments', None) | |
22 | stub_value = stub_value.py__class__() | |
23 | ||
24 | qualified_names = stub_value.get_qualified_names() | |
25 | if qualified_names is None: | |
26 | return NO_VALUES | |
27 | ||
28 | was_bound_method = stub_value.is_bound_method() | |
29 | if was_bound_method: | |
30 | # Infer the object first. We can infer the method later. | |
31 | method_name = qualified_names[-1] | |
32 | qualified_names = qualified_names[:-1] | |
33 | was_instance = True | |
34 | arguments = None | |
35 | ||
36 | values = _infer_from_stub(stub_module_context, qualified_names, ignore_compiled) | |
37 | if was_instance: | |
38 | values = ValueSet.from_sets( | |
39 | c.execute_with_values() if arguments is None else c.execute(arguments) | |
40 | for c in values | |
41 | if c.is_class() | |
42 | ) | |
43 | if was_bound_method: | |
44 | # Now that the instance has been properly created, we can simply get | |
45 | # the method. | |
46 | values = values.py__getattribute__(method_name) | |
47 | if decorates is not None: | |
48 | values = ValueSet(Decoratee(v, decorates) for v in values) | |
49 | return values | |
50 | ||
51 | ||
52 | def _infer_from_stub(stub_module_context, qualified_names, ignore_compiled): | |
53 | from jedi.inference.compiled.mixed import MixedObject | |
54 | stub_module = stub_module_context.get_value() | |
55 | assert isinstance(stub_module, (StubModuleValue, MixedObject)), stub_module_context | |
56 | non_stubs = stub_module.non_stub_value_set | |
57 | if ignore_compiled: | |
58 | non_stubs = non_stubs.filter(lambda c: not c.is_compiled()) | |
59 | for name in qualified_names: | |
60 | non_stubs = non_stubs.py__getattribute__(name) | |
61 | return non_stubs | |
62 | ||
63 | ||
64 | @to_list | |
65 | def _try_stub_to_python_names(names, prefer_stub_to_compiled=False): | |
66 | for name in names: | |
67 | module_context = name.get_root_context() | |
68 | if not module_context.is_stub(): | |
69 | yield name | |
70 | continue | |
71 | ||
72 | if name.api_type == 'module': | |
73 | values = convert_values(name.infer(), ignore_compiled=prefer_stub_to_compiled) | |
74 | if values: | |
75 | for v in values: | |
76 | yield v.name | |
77 | continue | |
78 | else: | |
79 | v = name.get_defining_qualified_value() | |
80 | if v is not None: | |
81 | converted = _stub_to_python_value_set(v, ignore_compiled=prefer_stub_to_compiled) | |
82 | if converted: | |
83 | converted_names = converted.goto(name.get_public_name()) | |
84 | if converted_names: | |
85 | for n in converted_names: | |
86 | if n.get_root_context().is_stub(): | |
87 | # If it's a stub again, it means we're going in | |
88 | # a circle. Probably some imports make it a | |
89 | # stub again. | |
90 | yield name | |
91 | else: | |
92 | yield n | |
93 | continue | |
94 | yield name | |
95 | ||
96 | ||
97 | def _load_stub_module(module): | |
98 | if module.is_stub(): | |
99 | return module | |
100 | return try_to_load_stub_cached( | |
101 | module.inference_state, | |
102 | import_names=module.string_names, | |
103 | python_value_set=ValueSet([module]), | |
104 | parent_module_value=None, | |
105 | sys_path=module.inference_state.get_sys_path(), | |
106 | ) | |
107 | ||
108 | ||
109 | @to_list | |
110 | def _python_to_stub_names(names, fallback_to_python=False): | |
111 | for name in names: | |
112 | module_context = name.get_root_context() | |
113 | if module_context.is_stub(): | |
114 | yield name | |
115 | continue | |
116 | ||
117 | if name.api_type == 'module': | |
118 | found_name = False | |
119 | for n in name.goto(): | |
120 | if n.api_type == 'module': | |
121 | values = convert_values(n.infer(), only_stubs=True) | |
122 | for v in values: | |
123 | yield v.name | |
124 | found_name = True | |
125 | else: | |
126 | for x in _python_to_stub_names([n], fallback_to_python=fallback_to_python): | |
127 | yield x | |
128 | found_name = True | |
129 | if found_name: | |
130 | continue | |
131 | else: | |
132 | v = name.get_defining_qualified_value() | |
133 | if v is not None: | |
134 | converted = to_stub(v) | |
135 | if converted: | |
136 | converted_names = converted.goto(name.get_public_name()) | |
137 | if converted_names: | |
138 | yield from converted_names | |
139 | continue | |
140 | if fallback_to_python: | |
141 | # This is the part where if we haven't found anything, just return | |
142 | # the stub name. | |
143 | yield name | |
144 | ||
145 | ||
146 | def convert_names(names, only_stubs=False, prefer_stubs=False, prefer_stub_to_compiled=True): | |
147 | if only_stubs and prefer_stubs: | |
148 | raise ValueError("You cannot use both of only_stubs and prefer_stubs.") | |
149 | ||
150 | with debug.increase_indent_cm('convert names'): | |
151 | if only_stubs or prefer_stubs: | |
152 | return _python_to_stub_names(names, fallback_to_python=prefer_stubs) | |
153 | else: | |
154 | return _try_stub_to_python_names( | |
155 | names, prefer_stub_to_compiled=prefer_stub_to_compiled) | |
156 | ||
157 | ||
158 | def convert_values(values, only_stubs=False, prefer_stubs=False, ignore_compiled=True): | |
159 | assert not (only_stubs and prefer_stubs) | |
160 | with debug.increase_indent_cm('convert values'): | |
161 | if only_stubs or prefer_stubs: | |
162 | return ValueSet.from_sets( | |
163 | to_stub(value) | |
164 | or (ValueSet({value}) if prefer_stubs else NO_VALUES) | |
165 | for value in values | |
166 | ) | |
167 | else: | |
168 | return ValueSet.from_sets( | |
169 | _stub_to_python_value_set(stub_value, ignore_compiled=ignore_compiled) | |
170 | or ValueSet({stub_value}) | |
171 | for stub_value in values | |
172 | ) | |
173 | ||
174 | ||
175 | def to_stub(value): | |
176 | if value.is_stub(): | |
177 | return ValueSet([value]) | |
178 | ||
179 | was_instance = value.is_instance() | |
180 | if was_instance: | |
181 | value = value.py__class__() | |
182 | ||
183 | qualified_names = value.get_qualified_names() | |
184 | stub_module = _load_stub_module(value.get_root_context().get_value()) | |
185 | if stub_module is None or qualified_names is None: | |
186 | return NO_VALUES | |
187 | ||
188 | was_bound_method = value.is_bound_method() | |
189 | if was_bound_method: | |
190 | # Infer the object first. We can infer the method later. | |
191 | method_name = qualified_names[-1] | |
192 | qualified_names = qualified_names[:-1] | |
193 | was_instance = True | |
194 | ||
195 | stub_values = ValueSet([stub_module]) | |
196 | for name in qualified_names: | |
197 | stub_values = stub_values.py__getattribute__(name) | |
198 | ||
199 | if was_instance: | |
200 | stub_values = ValueSet.from_sets( | |
201 | c.execute_with_values() | |
202 | for c in stub_values | |
203 | if c.is_class() | |
204 | ) | |
205 | if was_bound_method: | |
206 | # Now that the instance has been properly created, we can simply get | |
207 | # the method. | |
208 | stub_values = stub_values.py__getattribute__(method_name) | |
209 | return stub_values |