stub_value.py 3.3 KB

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