1from jedi.inference.base_value import ValueWrapper
2from jedi.inference.value.module import ModuleValue
3from jedi.inference.filters import ParserTreeFilter
4from jedi.inference.names import StubName, StubModuleName
5from jedi.inference.gradual.typing import TypingModuleFilterWrapper
6from jedi.inference.context import ModuleContext
7
8
9class StubModuleValue(ModuleValue):
10 _module_name_class = StubModuleName
11
12 def __init__(self, non_stub_value_set, *args, **kwargs):
13 super().__init__(*args, **kwargs)
14 self.non_stub_value_set = non_stub_value_set
15
16 def is_stub(self):
17 return True
18
19 def sub_modules_dict(self):
20 """
21 We have to overwrite this, because it's possible to have stubs that
22 don't have code for all the child modules. At the time of writing this
23 there are for example no stubs for `json.tool`.
24 """
25 names = {}
26 for value in self.non_stub_value_set:
27 try:
28 method = value.sub_modules_dict
29 except AttributeError:
30 pass
31 else:
32 names.update(method())
33 names.update(super().sub_modules_dict())
34 return names
35
36 def _get_stub_filters(self, origin_scope):
37 return [StubFilter(
38 parent_context=self.as_context(),
39 origin_scope=origin_scope
40 )] + list(self.iter_star_filters())
41
42 def get_filters(self, origin_scope=None):
43 filters = super().get_filters(origin_scope)
44 next(filters, None) # Ignore the first filter and replace it with our own
45 stub_filters = self._get_stub_filters(origin_scope=origin_scope)
46 yield from stub_filters
47 yield from filters
48
49 def _as_context(self):
50 return StubModuleContext(self)
51
52
53class StubModuleContext(ModuleContext):
54 def get_filters(self, until_position=None, origin_scope=None):
55 # Make sure to ignore the position, because positions are not relevant
56 # for stubs.
57 return super().get_filters(origin_scope=origin_scope)
58
59
60class TypingModuleWrapper(StubModuleValue):
61 def get_filters(self, *args, **kwargs):
62 filters = super().get_filters(*args, **kwargs)
63 f = next(filters, None)
64 assert f is not None
65 yield TypingModuleFilterWrapper(f)
66 yield from filters
67
68 def _as_context(self):
69 return TypingModuleContext(self)
70
71
72class TypingModuleContext(ModuleContext):
73 def get_filters(self, *args, **kwargs):
74 filters = super().get_filters(*args, **kwargs)
75 yield TypingModuleFilterWrapper(next(filters, None))
76 yield from filters
77
78
79class StubFilter(ParserTreeFilter):
80 name_class = StubName
81
82 def _is_name_reachable(self, name):
83 if not super()._is_name_reachable(name):
84 return False
85
86 # Imports in stub files are only public if they have an "as"
87 # export.
88 definition = name.get_definition()
89 if definition is None:
90 return False
91 if definition.type in ('import_from', 'import_name'):
92 if name.parent.type not in ('import_as_name', 'dotted_as_name'):
93 return False
94 n = name.value
95 # TODO rewrite direct return
96 if n.startswith('_') and not (n.startswith('__') and n.endswith('__')):
97 return False
98 return True
99
100
101class VersionInfo(ValueWrapper):
102 pass