@@ -207,6 +207,7 @@ def pylsp_lint(
207
207
args .append ("--strict" )
208
208
209
209
overrides = settings .get ("overrides" , [True ])
210
+ exit_status = 0
210
211
211
212
if not dmypy :
212
213
args .extend (["--incremental" , "--follow-imports" , "silent" ])
@@ -221,11 +222,12 @@ def pylsp_lint(
221
222
)
222
223
report = completed_process .stdout .decode ()
223
224
errors = completed_process .stderr .decode ()
225
+ exit_status = completed_process .returncode
224
226
else :
225
227
# mypy does not exist on path, but must exist in the env pylsp-mypy is installed in
226
228
# -> use mypy via api
227
229
log .info ("executing mypy args = %s via api" , args )
228
- report , errors , _ = mypy_api .run (args )
230
+ report , errors , exit_status = mypy_api .run (args )
229
231
else :
230
232
# If dmypy daemon is non-responsive calls to run will block.
231
233
# Check daemon status, if non-zero daemon is dead or hung.
@@ -239,20 +241,24 @@ def pylsp_lint(
239
241
completed_process = subprocess .run (
240
242
["dmypy" , * apply_overrides (args , overrides )], stderr = subprocess .PIPE , ** windows_flag
241
243
)
242
- _err = completed_process .stderr .decode ()
243
- _status = completed_process .returncode
244
- if _status != 0 :
244
+ errors = completed_process .stderr .decode ()
245
+ exit_status = completed_process .returncode
246
+ if exit_status != 0 :
245
247
log .info (
246
- "restarting dmypy from status: %s message: %s via path" , _status , _err .strip ()
248
+ "restarting dmypy from status: %s message: %s via path" ,
249
+ exit_status ,
250
+ errors .strip (),
247
251
)
248
252
subprocess .run (["dmypy" , "kill" ], ** windows_flag )
249
253
else :
250
254
# dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
251
255
# -> use dmypy via api
252
- _ , _err , _status = mypy_api .run_dmypy (["status" ])
253
- if _status != 0 :
256
+ _ , errors , exit_status = mypy_api .run_dmypy (["status" ])
257
+ if exit_status != 0 :
254
258
log .info (
255
- "restarting dmypy from status: %s message: %s via api" , _status , _err .strip ()
259
+ "restarting dmypy from status: %s message: %s via api" ,
260
+ exit_status ,
261
+ errors .strip (),
256
262
)
257
263
mypy_api .run_dmypy (["kill" ])
258
264
@@ -268,16 +274,33 @@ def pylsp_lint(
268
274
)
269
275
report = completed_process .stdout .decode ()
270
276
errors = completed_process .stderr .decode ()
277
+ exit_status = completed_process .returncode
271
278
else :
272
279
# dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
273
280
# -> use dmypy via api
274
281
log .info ("dmypy run args = %s via api" , args )
275
- report , errors , _ = mypy_api .run_dmypy (args )
282
+ report , errors , exit_status = mypy_api .run_dmypy (args )
276
283
277
284
log .debug ("report:\n %s" , report )
278
285
log .debug ("errors:\n %s" , errors )
279
286
280
287
diagnostics = []
288
+
289
+ # Expose generic mypy error on the first line.
290
+ if errors :
291
+ diagnostics .append (
292
+ {
293
+ "source" : "mypy" ,
294
+ "range" : {
295
+ "start" : {"line" : 0 , "character" : 0 },
296
+ # Client is supposed to clip end column to line length.
297
+ "end" : {"line" : 0 , "character" : 1000 },
298
+ },
299
+ "message" : errors ,
300
+ "severity" : 1 if exit_status != 0 else 2 , # Error if exited with error or warning.
301
+ }
302
+ )
303
+
281
304
for line in report .splitlines ():
282
305
log .debug ("parsing: line = %r" , line )
283
306
diag = parse_line (line , document )
0 commit comments