Coverage for src/debputy/lsp/languages/lsp_debian_debputy_manifest.py: 80%

54 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2026-07-22 10:58 +0000

1from typing import Any 

2from collections.abc import Sequence, Container 

3 

4from debputy.linting.lint_util import LintState, DebputyMetadata 

5from debputy.lsp.lsp_features import ( 

6 lint_diagnostics, 

7 lsp_standard_handler, 

8 lsp_hover, 

9 lsp_completer, 

10 SecondaryLanguage, 

11 LanguageDispatchRule, 

12) 

13from debputy.lsp.lsp_generic_yaml import ( 

14 yaml_key_range, 

15 generic_yaml_hover, 

16 LSPYAMLHelper, 

17 generic_yaml_lint, 

18 generic_yaml_completer, 

19 _is_empty_range, 

20) 

21from debputy.lsprotocol.types import ( 

22 TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, 

23 HoverParams, 

24 Hover, 

25 TEXT_DOCUMENT_CODE_ACTION, 

26 CompletionParams, 

27 CompletionList, 

28 CompletionItem, 

29) 

30from debputy.plugin.api.impl_types import ( 

31 PluginProvidedParser, 

32) 

33from debputy.plugin.api.parser_tables import OPARSER_MANIFEST_ROOT 

34from debputy.plugin.api.spec import DebputyIntegrationMode 

35from debputy.plugins.debputy.private_api import Capability, load_libcap 

36 

37try: 

38 from pygls.server import LanguageServer 

39 from debputy.lsp.debputy_ls import DebputyLanguageServer 

40except ImportError: 

41 pass 

42 

43 

44_DISPATCH_RULE = LanguageDispatchRule.new_rule( 

45 "debian/debputy.manifest", 

46 "debputy.manifest", 

47 "debian/debputy.manifest", 

48 [ 

49 SecondaryLanguage("debputy.manifest"), 

50 # LSP's official language ID for YAML files 

51 SecondaryLanguage("yaml", secondary_lookup="path-name"), 

52 ], 

53) 

54 

55 

56lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_CODE_ACTION) 

57lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL) 

58 

59 

60class DebputyManifestLSPYAMLHelper(LSPYAMLHelper[DebputyMetadata]): 

61 

62 def _validate_subparser_is_valid_here( 

63 self, 

64 subparser: PluginProvidedParser, 

65 orig_key: str, 

66 line: int, 

67 col: int, 

68 ) -> None: 

69 _integration_mode_allows_key( 

70 self.lint_state, 

71 self.custom_data.debputy_integration_mode, 

72 subparser.parser.expected_debputy_integration_mode, 

73 orig_key, 

74 line, 

75 col, 

76 ) 

77 

78 def _type_based_value_check( 

79 self, 

80 target_attr_type: type, 

81 value: Any, 

82 value_pos: tuple[int, int], 

83 *, 

84 key: str | int | None = None, 

85 ) -> bool: 

86 if issubclass(target_attr_type, Capability): 86 ↛ 87line 86 didn't jump to line 87 because the condition on line 86 was never true

87 has_libcap, _, is_valid_cap = load_libcap() 

88 if has_libcap and not is_valid_cap(value): 

89 line_no, cursor_pos = value_pos 

90 cap_range = self._remaining_line(line_no, cursor_pos) 

91 if _is_empty_range(cap_range): 

92 # FIXME: We cannot report an empty range, but there is still a problem here. 

93 return True 

94 self.lint_state.emit_diagnostic( 

95 cap_range, 

96 "The value could not be parsed as a capability via cap_from_text on this system", 

97 "warning", 

98 "debputy", 

99 ) 

100 return True 

101 

102 return super()._type_based_value_check( 

103 target_attr_type, 

104 value, 

105 value_pos, 

106 key=key, 

107 ) 

108 

109 

110def _initialize_yaml_helper(lint_state: LintState) -> DebputyManifestLSPYAMLHelper: 

111 return DebputyManifestLSPYAMLHelper( 

112 lint_state, 

113 lint_state.plugin_feature_set.manifest_parser_generator, 

114 lint_state.debputy_metadata, 

115 ) 

116 

117 

118@lint_diagnostics(_DISPATCH_RULE) 

119async def _lint_debian_debputy_manifest(lint_state: LintState) -> None: 

120 pg = lint_state.plugin_feature_set.manifest_parser_generator 

121 root_parser = pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT] 

122 await generic_yaml_lint( 

123 lint_state, 

124 root_parser, 

125 _initialize_yaml_helper, 

126 ) 

127 

128 

129def _integration_mode_allows_key( 

130 lint_state: LintState, 

131 debputy_integration_mode: DebputyIntegrationMode | None, 

132 expected_debputy_integration_modes: Container[DebputyIntegrationMode] | None, 

133 key: str, 

134 line: int, 

135 col: int, 

136) -> None: 

137 if debputy_integration_mode is None or expected_debputy_integration_modes is None: 

138 return 

139 if debputy_integration_mode in expected_debputy_integration_modes: 

140 return 

141 key_range = yaml_key_range(key, line, col) 

142 lint_state.emit_diagnostic( 

143 key_range, 

144 f'Feature "{key}" not supported in integration mode {debputy_integration_mode}', 

145 "error", 

146 "debputy", 

147 ) 

148 

149 

150@lsp_completer(_DISPATCH_RULE) 

151def debputy_manifest_completer( 

152 ls: "DebputyLanguageServer", 

153 params: CompletionParams, 

154) -> CompletionList | Sequence[CompletionItem] | None: 

155 pg = ls.plugin_feature_set.manifest_parser_generator 

156 root_parser = pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT] 

157 return generic_yaml_completer( 

158 ls, 

159 params, 

160 root_parser, 

161 ) 

162 

163 

164@lsp_hover(_DISPATCH_RULE) 

165def debputy_manifest_hover( 

166 ls: "DebputyLanguageServer", 

167 params: HoverParams, 

168) -> Hover | None: 

169 return generic_yaml_hover( 

170 ls, 

171 params, 

172 lambda pg: pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT], 

173 show_integration_mode=True, 

174 )