json_reporter.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
  4. """JSON reporter."""
  5. from __future__ import annotations
  6. import json
  7. import sys
  8. from typing import TYPE_CHECKING, Optional
  9. from pylint.interfaces import UNDEFINED
  10. from pylint.message import Message
  11. from pylint.reporters.base_reporter import BaseReporter
  12. from pylint.typing import MessageLocationTuple
  13. if sys.version_info >= (3, 8):
  14. from typing import TypedDict
  15. else:
  16. from typing_extensions import TypedDict
  17. if TYPE_CHECKING:
  18. from pylint.lint.pylinter import PyLinter
  19. from pylint.reporters.ureports.nodes import Section
  20. # Since message-id is an invalid name we need to use the alternative syntax
  21. OldJsonExport = TypedDict(
  22. "OldJsonExport",
  23. {
  24. "type": str,
  25. "module": str,
  26. "obj": str,
  27. "line": int,
  28. "column": int,
  29. "endLine": Optional[int],
  30. "endColumn": Optional[int],
  31. "path": str,
  32. "symbol": str,
  33. "message": str,
  34. "message-id": str,
  35. },
  36. )
  37. class BaseJSONReporter(BaseReporter):
  38. """Report messages and layouts in JSON."""
  39. name = "json"
  40. extension = "json"
  41. def display_messages(self, layout: Section | None) -> None:
  42. """Launch layouts display."""
  43. json_dumpable = [self.serialize(message) for message in self.messages]
  44. print(json.dumps(json_dumpable, indent=4), file=self.out)
  45. def display_reports(self, layout: Section) -> None:
  46. """Don't do anything in this reporter."""
  47. def _display(self, layout: Section) -> None:
  48. """Do nothing."""
  49. @staticmethod
  50. def serialize(message: Message) -> OldJsonExport:
  51. raise NotImplementedError
  52. @staticmethod
  53. def deserialize(message_as_json: OldJsonExport) -> Message:
  54. raise NotImplementedError
  55. class JSONReporter(BaseJSONReporter):
  56. """
  57. TODO: 3.0: Remove this JSONReporter in favor of the new one handling abs-path
  58. and confidence.
  59. TODO: 2.16: Add a new JSONReporter handling abs-path, confidence and scores.
  60. (Ultimately all other breaking change related to json for 3.0).
  61. """
  62. @staticmethod
  63. def serialize(message: Message) -> OldJsonExport:
  64. return {
  65. "type": message.category,
  66. "module": message.module,
  67. "obj": message.obj,
  68. "line": message.line,
  69. "column": message.column,
  70. "endLine": message.end_line,
  71. "endColumn": message.end_column,
  72. "path": message.path,
  73. "symbol": message.symbol,
  74. "message": message.msg or "",
  75. "message-id": message.msg_id,
  76. }
  77. @staticmethod
  78. def deserialize(message_as_json: OldJsonExport) -> Message:
  79. return Message(
  80. msg_id=message_as_json["message-id"],
  81. symbol=message_as_json["symbol"],
  82. msg=message_as_json["message"],
  83. location=MessageLocationTuple(
  84. # TODO: 3.0: Add abs-path and confidence in a new JSONReporter
  85. abspath=message_as_json["path"],
  86. path=message_as_json["path"],
  87. module=message_as_json["module"],
  88. obj=message_as_json["obj"],
  89. line=message_as_json["line"],
  90. column=message_as_json["column"],
  91. end_line=message_as_json["endLine"],
  92. end_column=message_as_json["endColumn"],
  93. ),
  94. # TODO: 3.0: Make confidence available in a new JSONReporter
  95. confidence=UNDEFINED,
  96. )
  97. def register(linter: PyLinter) -> None:
  98. linter.register_reporter(JSONReporter)