9
9
import json
10
10
from typing import TYPE_CHECKING , Optional , TypedDict
11
11
12
- from pylint .interfaces import UNDEFINED
12
+ from pylint .interfaces import CONFIDENCE_MAP , UNDEFINED
13
13
from pylint .message import Message
14
14
from pylint .reporters .base_reporter import BaseReporter
15
15
from pylint .typing import MessageLocationTuple
37
37
)
38
38
39
39
40
- class BaseJSONReporter (BaseReporter ):
41
- """Report messages and layouts in JSON."""
40
+ class JSONReporter (BaseReporter ):
41
+ """Report messages and layouts in JSON.
42
+
43
+ Consider using JSON2Reporter instead, as it is superior and this reporter
44
+ is no longer maintained.
45
+ """
42
46
43
47
name = "json"
44
48
extension = "json"
@@ -54,25 +58,6 @@ def display_reports(self, layout: Section) -> None:
54
58
def _display (self , layout : Section ) -> None :
55
59
"""Do nothing."""
56
60
57
- @staticmethod
58
- def serialize (message : Message ) -> OldJsonExport :
59
- raise NotImplementedError
60
-
61
- @staticmethod
62
- def deserialize (message_as_json : OldJsonExport ) -> Message :
63
- raise NotImplementedError
64
-
65
-
66
- class JSONReporter (BaseJSONReporter ):
67
-
68
- """
69
- TODO: 3.0: Remove this JSONReporter in favor of the new one handling abs-path
70
- and confidence.
71
-
72
- TODO: 3.0: Add a new JSONReporter handling abs-path, confidence and scores.
73
- (Ultimately all other breaking change related to json for 3.0).
74
- """
75
-
76
61
@staticmethod
77
62
def serialize (message : Message ) -> OldJsonExport :
78
63
return {
@@ -96,7 +81,6 @@ def deserialize(message_as_json: OldJsonExport) -> Message:
96
81
symbol = message_as_json ["symbol" ],
97
82
msg = message_as_json ["message" ],
98
83
location = MessageLocationTuple (
99
- # TODO: 3.0: Add abs-path and confidence in a new JSONReporter
100
84
abspath = message_as_json ["path" ],
101
85
path = message_as_json ["path" ],
102
86
module = message_as_json ["module" ],
@@ -106,10 +90,112 @@ def deserialize(message_as_json: OldJsonExport) -> Message:
106
90
end_line = message_as_json ["endLine" ],
107
91
end_column = message_as_json ["endColumn" ],
108
92
),
109
- # TODO: 3.0: Make confidence available in a new JSONReporter
110
93
confidence = UNDEFINED ,
111
94
)
112
95
113
96
97
+ class JSONMessage (TypedDict ):
98
+ type : str
99
+ message : str
100
+ messageId : str
101
+ symbol : str
102
+ confidence : str
103
+ module : str
104
+ path : str
105
+ absolutePath : str
106
+ line : int
107
+ endLine : int | None
108
+ column : int
109
+ endColumn : int | None
110
+ obj : str
111
+
112
+
113
+ class JSON2Reporter (BaseReporter ):
114
+ name = "json2"
115
+ extension = "json2"
116
+
117
+ def display_reports (self , layout : Section ) -> None :
118
+ """Don't do anything in this reporter."""
119
+
120
+ def _display (self , layout : Section ) -> None :
121
+ """Do nothing."""
122
+
123
+ def display_messages (self , layout : Section | None ) -> None :
124
+ """Launch layouts display."""
125
+ output = {
126
+ "messages" : [self .serialize (message ) for message in self .messages ],
127
+ "statistics" : self .serialize_stats (),
128
+ }
129
+ print (json .dumps (output , indent = 4 ), file = self .out )
130
+
131
+ @staticmethod
132
+ def serialize (message : Message ) -> JSONMessage :
133
+ return JSONMessage (
134
+ type = message .category ,
135
+ symbol = message .symbol ,
136
+ message = message .msg or "" ,
137
+ messageId = message .msg_id ,
138
+ confidence = message .confidence .name ,
139
+ module = message .module ,
140
+ obj = message .obj ,
141
+ line = message .line ,
142
+ column = message .column ,
143
+ endLine = message .end_line ,
144
+ endColumn = message .end_column ,
145
+ path = message .path ,
146
+ absolutePath = message .abspath ,
147
+ )
148
+
149
+ @staticmethod
150
+ def deserialize (message_as_json : JSONMessage ) -> Message :
151
+ return Message (
152
+ msg_id = message_as_json ["messageId" ],
153
+ symbol = message_as_json ["symbol" ],
154
+ msg = message_as_json ["message" ],
155
+ location = MessageLocationTuple (
156
+ abspath = message_as_json ["absolutePath" ],
157
+ path = message_as_json ["path" ],
158
+ module = message_as_json ["module" ],
159
+ obj = message_as_json ["obj" ],
160
+ line = message_as_json ["line" ],
161
+ column = message_as_json ["column" ],
162
+ end_line = message_as_json ["endLine" ],
163
+ end_column = message_as_json ["endColumn" ],
164
+ ),
165
+ confidence = CONFIDENCE_MAP [message_as_json ["confidence" ]],
166
+ )
167
+
168
+ def serialize_stats (self ) -> dict [str , str | int | dict [str , int ]]:
169
+ """Serialize the linter stats into something JSON dumpable."""
170
+ stats = self .linter .stats
171
+
172
+ counts_dict = {
173
+ "fatal" : stats .fatal ,
174
+ "error" : stats .error ,
175
+ "warning" : stats .warning ,
176
+ "refactor" : stats .refactor ,
177
+ "convention" : stats .convention ,
178
+ "info" : stats .info ,
179
+ }
180
+
181
+ # Calculate score based on the evaluation option
182
+ evaluation = self .linter .config .evaluation
183
+ try :
184
+ note : int = eval ( # pylint: disable=eval-used
185
+ evaluation , {}, {** counts_dict , "statement" : stats .statement or 1 }
186
+ )
187
+ except Exception as ex : # pylint: disable=broad-except
188
+ score : str | int = f"An exception occurred while rating: { ex } "
189
+ else :
190
+ score = round (note , 2 )
191
+
192
+ return {
193
+ "messageTypeCount" : counts_dict ,
194
+ "modulesLinted" : len (stats .by_module ),
195
+ "score" : score ,
196
+ }
197
+
198
+
114
199
def register (linter : PyLinter ) -> None :
115
200
linter .register_reporter (JSONReporter )
201
+ linter .register_reporter (JSON2Reporter )
0 commit comments