@@ -122,6 +122,36 @@ def apply_overrides(args: List[str], overrides: List[Any]) -> List[str]:
122
122
@hookimpl
123
123
def pylsp_lint (
124
124
config : Config , workspace : Workspace , document : Document , is_saved : bool
125
+ ) -> List [Dict [str , Any ]]:
126
+ settings = config .plugin_settings ("pylsp_mypy" )
127
+ oldSettings1 = config .plugin_settings ("mypy-ls" )
128
+ if oldSettings1 != {}:
129
+ raise DeprecationWarning (
130
+ "Your configuration uses the namespace mypy-ls, this should be changed to pylsp_mypy"
131
+ )
132
+ oldSettings2 = config .plugin_settings ("mypy_ls" )
133
+ if oldSettings2 != {}:
134
+ raise DeprecationWarning (
135
+ "Your configuration uses the namespace mypy_ls, this should be changed to pylsp_mypy"
136
+ )
137
+ if settings == {}:
138
+ settings = oldSettings1
139
+ if settings == {}:
140
+ settings = oldSettings2
141
+
142
+ if settings .get ("report_progress" , False ):
143
+ with workspace .report_progress ("lint: mypy" ):
144
+ return get_diagnostics (config , workspace , document , settings , is_saved )
145
+ else :
146
+ return get_diagnostics (config , workspace , document , settings , is_saved )
147
+
148
+
149
+ def get_diagnostics (
150
+ config : Config ,
151
+ workspace : Workspace ,
152
+ document : Document ,
153
+ settings : Dict [str , Any ],
154
+ is_saved : bool ,
125
155
) -> List [Dict [str , Any ]]:
126
156
"""
127
157
Lints.
@@ -143,176 +173,159 @@ def pylsp_lint(
143
173
List of the linting data.
144
174
145
175
"""
146
- with workspace .report_progress ("lint: mypy" ):
147
- settings = config .plugin_settings ("pylsp_mypy" )
148
- oldSettings1 = config .plugin_settings ("mypy-ls" )
149
- if oldSettings1 != {}:
150
- raise DeprecationWarning (
151
- "Your configuration uses the namespace mypy-ls, this should be changed to pylsp_mypy"
152
- )
153
- oldSettings2 = config .plugin_settings ("mypy_ls" )
154
- if oldSettings2 != {}:
155
- raise DeprecationWarning (
156
- "Your configuration uses the namespace mypy_ls, this should be changed to pylsp_mypy"
157
- )
158
- if settings == {}:
159
- settings = oldSettings1
160
- if settings == {}:
161
- settings = oldSettings2
176
+ log .info (
177
+ "lint settings = %s document.path = %s is_saved = %s" ,
178
+ settings ,
179
+ document .path ,
180
+ is_saved ,
181
+ )
182
+
183
+ live_mode = settings .get ("live_mode" , True )
184
+ dmypy = settings .get ("dmypy" , False )
185
+
186
+ if dmypy and live_mode :
187
+ # dmypy can only be efficiently run on files that have been saved, see:
188
+ # https://github.com/python/mypy/issues/9309
189
+ log .warning ("live_mode is not supported with dmypy, disabling" )
190
+ live_mode = False
162
191
192
+ args = ["--show-column-numbers" ]
193
+
194
+ global tmpFile
195
+ if live_mode and not is_saved :
196
+ if tmpFile :
197
+ tmpFile = open (tmpFile .name , "w" )
198
+ else :
199
+ tmpFile = tempfile .NamedTemporaryFile ("w" , delete = False )
200
+ log .info ("live_mode tmpFile = %s" , tmpFile .name )
201
+ tmpFile .write (document .source )
202
+ tmpFile .close ()
203
+ args .extend (["--shadow-file" , document .path , tmpFile .name ])
204
+ elif not is_saved and document .path in last_diagnostics :
205
+ # On-launch the document isn't marked as saved, so fall through and run
206
+ # the diagnostics anyway even if the file contents may be out of date.
163
207
log .info (
164
- "lint settings = %s document.path = %s is_saved = %s" ,
165
- settings ,
166
- document .path ,
167
- is_saved ,
208
+ "non-live, returning cached diagnostics len(cached) = %s" ,
209
+ last_diagnostics [document .path ],
168
210
)
211
+ return last_diagnostics [document .path ]
169
212
170
- live_mode = settings .get ("live_mode" , True )
171
- dmypy = settings .get ("dmypy" , False )
172
-
173
- if dmypy and live_mode :
174
- # dmypy can only be efficiently run on files that have been saved, see:
175
- # https://github.com/python/mypy/issues/9309
176
- log .warning ("live_mode is not supported with dmypy, disabling" )
177
- live_mode = False
178
-
179
- args = ["--show-column-numbers" ]
180
-
181
- global tmpFile
182
- if live_mode and not is_saved :
183
- if tmpFile :
184
- tmpFile = open (tmpFile .name , "w" )
185
- else :
186
- tmpFile = tempfile .NamedTemporaryFile ("w" , delete = False )
187
- log .info ("live_mode tmpFile = %s" , tmpFile .name )
188
- tmpFile .write (document .source )
189
- tmpFile .close ()
190
- args .extend (["--shadow-file" , document .path , tmpFile .name ])
191
- elif not is_saved and document .path in last_diagnostics :
192
- # On-launch the document isn't marked as saved, so fall through and run
193
- # the diagnostics anyway even if the file contents may be out of date.
194
- log .info (
195
- "non-live, returning cached diagnostics len(cached) = %s" ,
196
- last_diagnostics [document .path ],
197
- )
198
- return last_diagnostics [document .path ]
199
-
200
- mypyConfigFile = mypyConfigFileMap .get (workspace .root_path )
201
- if mypyConfigFile :
202
- args .append ("--config-file" )
203
- args .append (mypyConfigFile )
213
+ mypyConfigFile = mypyConfigFileMap .get (workspace .root_path )
214
+ if mypyConfigFile :
215
+ args .append ("--config-file" )
216
+ args .append (mypyConfigFile )
204
217
205
- args .append (document .path )
218
+ args .append (document .path )
206
219
207
- if settings .get ("strict" , False ):
208
- args .append ("--strict" )
220
+ if settings .get ("strict" , False ):
221
+ args .append ("--strict" )
209
222
210
- overrides = settings .get ("overrides" , [True ])
211
- exit_status = 0
223
+ overrides = settings .get ("overrides" , [True ])
224
+ exit_status = 0
212
225
213
- if not dmypy :
214
- args .extend (["--incremental" , "--follow-imports" , "silent" ])
215
- args = apply_overrides (args , overrides )
226
+ if not dmypy :
227
+ args .extend (["--incremental" , "--follow-imports" , "silent" ])
228
+ args = apply_overrides (args , overrides )
216
229
217
- if shutil .which ("mypy" ):
218
- # mypy exists on path
219
- # -> use mypy on path
220
- log .info ("executing mypy args = %s on path" , args )
221
- completed_process = subprocess .run (
222
- ["mypy" , * args ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , ** windows_flag
230
+ if shutil .which ("mypy" ):
231
+ # mypy exists on path
232
+ # -> use mypy on path
233
+ log .info ("executing mypy args = %s on path" , args )
234
+ completed_process = subprocess .run (
235
+ ["mypy" , * args ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , ** windows_flag
236
+ )
237
+ report = completed_process .stdout .decode ()
238
+ errors = completed_process .stderr .decode ()
239
+ exit_status = completed_process .returncode
240
+ else :
241
+ # mypy does not exist on path, but must exist in the env pylsp-mypy is installed in
242
+ # -> use mypy via api
243
+ log .info ("executing mypy args = %s via api" , args )
244
+ report , errors , exit_status = mypy_api .run (args )
245
+ else :
246
+ # If dmypy daemon is non-responsive calls to run will block.
247
+ # Check daemon status, if non-zero daemon is dead or hung.
248
+ # If daemon is hung, kill will reset
249
+ # If daemon is dead/absent, kill will no-op.
250
+ # In either case, reset to fresh state
251
+
252
+ if shutil .which ("dmypy" ):
253
+ # dmypy exists on path
254
+ # -> use mypy on path
255
+ completed_process = subprocess .run (
256
+ ["dmypy" , "status" ], stderr = subprocess .PIPE , ** windows_flag
257
+ )
258
+ errors = completed_process .stderr .decode ()
259
+ exit_status = completed_process .returncode
260
+ if exit_status != 0 :
261
+ log .info (
262
+ "restarting dmypy from status: %s message: %s via path" ,
263
+ exit_status ,
264
+ errors .strip (),
223
265
)
224
- report = completed_process .stdout .decode ()
225
- errors = completed_process .stderr .decode ()
226
- exit_status = completed_process .returncode
227
- else :
228
- # mypy does not exist on path, but must exist in the env pylsp-mypy is installed in
229
- # -> use mypy via api
230
- log .info ("executing mypy args = %s via api" , args )
231
- report , errors , exit_status = mypy_api .run (args )
266
+ subprocess .run (["dmypy" , "restart" ], ** windows_flag )
232
267
else :
233
- # If dmypy daemon is non-responsive calls to run will block.
234
- # Check daemon status, if non-zero daemon is dead or hung.
235
- # If daemon is hung, kill will reset
236
- # If daemon is dead/absent, kill will no-op.
237
- # In either case, reset to fresh state
238
-
239
- if shutil .which ("dmypy" ):
240
- # dmypy exists on path
241
- # -> use mypy on path
242
- completed_process = subprocess .run (
243
- ["dmypy" , "status" ], stderr = subprocess .PIPE , ** windows_flag
268
+ # dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
269
+ # -> use dmypy via api
270
+ _ , errors , exit_status = mypy_api .run_dmypy (["status" ])
271
+ if exit_status != 0 :
272
+ log .info (
273
+ "restarting dmypy from status: %s message: %s via api" ,
274
+ exit_status ,
275
+ errors .strip (),
244
276
)
245
- errors = completed_process .stderr .decode ()
246
- exit_status = completed_process .returncode
247
- if exit_status != 0 :
248
- log .info (
249
- "restarting dmypy from status: %s message: %s via path" ,
250
- exit_status ,
251
- errors .strip (),
252
- )
253
- subprocess .run (["dmypy" , "restart" ], ** windows_flag )
254
- else :
255
- # dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
256
- # -> use dmypy via api
257
- _ , errors , exit_status = mypy_api .run_dmypy (["status" ])
258
- if exit_status != 0 :
259
- log .info (
260
- "restarting dmypy from status: %s message: %s via api" ,
261
- exit_status ,
262
- errors .strip (),
263
- )
264
- mypy_api .run_dmypy (["restart" ])
277
+ mypy_api .run_dmypy (["restart" ])
265
278
266
- # run to use existing daemon or restart if required
267
- args = ["run" , "--" ] + apply_overrides (args , overrides )
279
+ # run to use existing daemon or restart if required
280
+ args = ["run" , "--" ] + apply_overrides (args , overrides )
268
281
269
- if shutil .which ("dmypy" ):
270
- # dmypy exists on path
271
- # -> use mypy on path
272
- log .info ("dmypy run args = %s via path" , args )
273
- completed_process = subprocess .run (
274
- ["dmypy" , * args ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , ** windows_flag
275
- )
276
- report = completed_process .stdout .decode ()
277
- errors = completed_process .stderr .decode ()
278
- exit_status = completed_process .returncode
279
- else :
280
- # dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
281
- # -> use dmypy via api
282
- log .info ("dmypy run args = %s via api" , args )
283
- report , errors , exit_status = mypy_api .run_dmypy (args )
284
-
285
- log .debug ("report:\n %s" , report )
286
- log .debug ("errors:\n %s" , errors )
287
-
288
- diagnostics = []
289
-
290
- # Expose generic mypy error on the first line.
291
- if errors :
292
- diagnostics .append (
293
- {
294
- "source" : "mypy" ,
295
- "range" : {
296
- "start" : {"line" : 0 , "character" : 0 },
297
- # Client is supposed to clip end column to line length.
298
- "end" : {"line" : 0 , "character" : 1000 },
299
- },
300
- "message" : errors ,
301
- # Error if exited with error or warning.
302
- "severity" : 1 if exit_status != 0 else 2 ,
303
- }
282
+ if shutil .which ("dmypy" ):
283
+ # dmypy exists on path
284
+ # -> use mypy on path
285
+ log .info ("dmypy run args = %s via path" , args )
286
+ completed_process = subprocess .run (
287
+ ["dmypy" , * args ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , ** windows_flag
304
288
)
289
+ report = completed_process .stdout .decode ()
290
+ errors = completed_process .stderr .decode ()
291
+ exit_status = completed_process .returncode
292
+ else :
293
+ # dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
294
+ # -> use dmypy via api
295
+ log .info ("dmypy run args = %s via api" , args )
296
+ report , errors , exit_status = mypy_api .run_dmypy (args )
297
+
298
+ log .debug ("report:\n %s" , report )
299
+ log .debug ("errors:\n %s" , errors )
300
+
301
+ diagnostics = []
302
+
303
+ # Expose generic mypy error on the first line.
304
+ if errors :
305
+ diagnostics .append (
306
+ {
307
+ "source" : "mypy" ,
308
+ "range" : {
309
+ "start" : {"line" : 0 , "character" : 0 },
310
+ # Client is supposed to clip end column to line length.
311
+ "end" : {"line" : 0 , "character" : 1000 },
312
+ },
313
+ "message" : errors ,
314
+ # Error if exited with error or warning.
315
+ "severity" : 1 if exit_status != 0 else 2 ,
316
+ }
317
+ )
305
318
306
- for line in report .splitlines ():
307
- log .debug ("parsing: line = %r" , line )
308
- diag = parse_line (line , document )
309
- if diag :
310
- diagnostics .append (diag )
319
+ for line in report .splitlines ():
320
+ log .debug ("parsing: line = %r" , line )
321
+ diag = parse_line (line , document )
322
+ if diag :
323
+ diagnostics .append (diag )
311
324
312
- log .info ("pylsp-mypy len(diagnostics) = %s" , len (diagnostics ))
325
+ log .info ("pylsp-mypy len(diagnostics) = %s" , len (diagnostics ))
313
326
314
- last_diagnostics [document .path ] = diagnostics
315
- return diagnostics
327
+ last_diagnostics [document .path ] = diagnostics
328
+ return diagnostics
316
329
317
330
318
331
@hookimpl
0 commit comments