|
10 | 10 |
|
11 | 11 | https://stackoverflow.com/questions/49715881/how-to-pickle-inherited-exceptions |
12 | 12 | """ |
13 | | -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union |
| 13 | +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union, cast |
14 | 14 |
|
15 | 15 | if TYPE_CHECKING: # pragma: no cover |
16 | 16 | from sqlfluff.core.parser import BaseSegment, PositionMarker |
17 | 17 | from sqlfluff.core.rules import BaseRule, LintFix |
18 | 18 |
|
19 | 19 | CheckTuple = Tuple[str, int, int] |
| 20 | +SerializedObject = Dict[str, Union[str, int, bool, List["SerializedObject"]]] |
| 21 | + |
| 22 | + |
| 23 | +def _extract_position(segment: Optional["BaseSegment"]) -> Dict[str, int]: |
| 24 | + """If a segment is present and is a literal, return it's source length.""" |
| 25 | + if segment: |
| 26 | + position = segment.pos_marker |
| 27 | + assert position |
| 28 | + if position.is_literal(): |
| 29 | + return position.to_source_dict() |
| 30 | + # An empty location is an indicator of not being able to accurately |
| 31 | + # represent the location. |
| 32 | + return {} # pragma: no cover |
20 | 33 |
|
21 | 34 |
|
22 | 35 | class SQLBaseError(ValueError): |
@@ -83,17 +96,18 @@ def desc(self) -> str: |
83 | 96 |
|
84 | 97 | return self.__class__.__name__ # pragma: no cover |
85 | 98 |
|
86 | | - def get_info_dict(self) -> Dict[str, Union[str, int]]: |
| 99 | + def to_dict(self) -> SerializedObject: |
87 | 100 | """Return a dict of properties. |
88 | 101 |
|
89 | 102 | This is useful in the API for outputting violations. |
90 | 103 | """ |
91 | 104 | return { |
92 | | - "line_no": self.line_no, |
93 | | - "line_pos": self.line_pos, |
| 105 | + "start_line_no": self.line_no, |
| 106 | + "start_line_pos": self.line_pos, |
94 | 107 | "code": self.rule_code(), |
95 | 108 | "description": self.desc(), |
96 | 109 | "name": getattr(self, "rule").name if hasattr(self, "rule") else "", |
| 110 | + "warning": self.warning, |
97 | 111 | } |
98 | 112 |
|
99 | 113 | def check_tuple(self) -> CheckTuple: |
@@ -212,6 +226,19 @@ def __reduce__( |
212 | 226 | self.warning, |
213 | 227 | ) |
214 | 228 |
|
| 229 | + def to_dict(self) -> SerializedObject: |
| 230 | + """Return a dict of properties. |
| 231 | +
|
| 232 | + This is useful in the API for outputting violations. |
| 233 | +
|
| 234 | + For parsing errors we additionally add the length of the unparsable segment. |
| 235 | + """ |
| 236 | + _base_dict = super().to_dict() |
| 237 | + _base_dict.update( |
| 238 | + **_extract_position(self.segment), |
| 239 | + ) |
| 240 | + return _base_dict |
| 241 | + |
215 | 242 |
|
216 | 243 | class SQLLintError(SQLBaseError): |
217 | 244 | """An error which occurred during linting. |
@@ -264,6 +291,40 @@ def __reduce__( |
264 | 291 | self.warning, |
265 | 292 | ) |
266 | 293 |
|
| 294 | + def to_dict(self) -> SerializedObject: |
| 295 | + """Return a dict of properties. |
| 296 | +
|
| 297 | + This is useful in the API for outputting violations. |
| 298 | +
|
| 299 | + For linting errors we additionally add details of any fixes. |
| 300 | + """ |
| 301 | + _base_dict = super().to_dict() |
| 302 | + _base_dict.update( |
| 303 | + fixes=[fix.to_dict() for fix in self.fixes], |
| 304 | + **_extract_position(self.segment), |
| 305 | + ) |
| 306 | + # Edge case: If the base error doesn't have an end position |
| 307 | + # but we only have one fix and it _does_. Then use use that in the |
| 308 | + # overall fix. |
| 309 | + _fixes = cast(List[SerializedObject], _base_dict.get("fixes", [])) |
| 310 | + if "end_line_pos" not in _base_dict and len(_fixes) == 1: |
| 311 | + _fix = _fixes[0] |
| 312 | + # If the mandatory keys match... |
| 313 | + if ( |
| 314 | + _fix["start_line_no"] == _base_dict["start_line_no"] |
| 315 | + and _fix["start_line_pos"] == _base_dict["start_line_pos"] |
| 316 | + ): |
| 317 | + # ...then hoist all the optional ones from the fix. |
| 318 | + for key in [ |
| 319 | + "start_file_pos", |
| 320 | + "end_line_no", |
| 321 | + "end_line_pos", |
| 322 | + "end_file_pos", |
| 323 | + ]: |
| 324 | + _base_dict[key] = _fix[key] |
| 325 | + |
| 326 | + return _base_dict |
| 327 | + |
267 | 328 | @property |
268 | 329 | def fixable(self) -> bool: |
269 | 330 | """Should this error be considered fixable?""" |
|
0 commit comments