FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
python-mode/pymode/ruff_integration.py at develop · python-mode/python-mode · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
python-mode
/
python-mode
Public
Uh oh!
There was an error while loading.
Please reload this page
.
Notifications
You must be signed in to change notification settings
Fork
758
Star
5.5k
Code
Issues
38
Pull requests
1
Actions
Projects
Wiki
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security and quality
Insights
Expand file tree
Breadcrumbs
python-mode
/
pymode
/
ruff_integration.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
573 lines (469 loc) · 21.7 KB
Breadcrumbs
python-mode
/
pymode
/
ruff_integration.py
Copy path
File metadata and controls
573 lines (469 loc) · 21.7 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
"""Ruff integration for Python-mode.
This module provides integration with Ruff, a fast Python linter and formatter.
It replaces the previous pylama-based linting system with a single, modern tool.
"""
import
json
import
os
import
subprocess
import
tempfile
from
typing
import
Dict
,
List
,
Optional
,
Any
from
.
environment
import
env
from
.
utils
import
silence_stderr
class
RuffError
:
"""Represents a Ruff linting error/warning."""
def
__init__
(
self
,
data
:
Dict
[
str
,
Any
]):
"""Initialize from Ruff JSON output."""
self
.
filename
=
data
.
get
(
'filename'
,
''
)
self
.
line
=
data
.
get
(
'location'
, {}).
get
(
'row'
,
1
)
self
.
col
=
data
.
get
(
'location'
, {}).
get
(
'column'
,
1
)
self
.
code
=
data
.
get
(
'code'
,
''
)
self
.
message
=
data
.
get
(
'message'
,
''
)
self
.
severity
=
data
.
get
(
'severity'
,
'error'
)
self
.
rule
=
data
.
get
(
'rule'
,
''
)
def
to_dict
(
self
)
->
Dict
[
str
,
Any
]:
"""Convert to vim-compatible error dictionary."""
return
{
'filename'
:
self
.
filename
,
'lnum'
:
self
.
line
,
'col'
:
self
.
col
,
'text'
:
f"
{
self
.
code
}
:
{
self
.
message
}
"
,
'type'
:
'E'
if
self
.
severity
==
'error'
else
'W'
,
'code'
:
self
.
code
,
}
def
_get_ruff_executable
()
->
str
:
"""Get the ruff executable path."""
# Try to get from vim configuration first
ruff_path
=
env
.
var
(
'g:pymode_ruff_executable'
,
silence
=
True
,
default
=
'ruff'
)
# Verify ruff is available
try
:
subprocess
.
run
([
ruff_path
,
'--version'
],
capture_output
=
True
,
check
=
True
,
timeout
=
5
)
return
ruff_path
except
(
subprocess
.
CalledProcessError
,
FileNotFoundError
,
subprocess
.
TimeoutExpired
):
env
.
error
(
"Ruff not found. Please install ruff: pip install ruff"
)
raise
RuntimeError
(
"Ruff executable not found"
)
def
_find_local_ruff_config
(
file_path
:
str
)
->
Optional
[
str
]:
"""Find local Ruff configuration file starting from file's directory.
Ruff searches for config files in this order (highest priority first):
1. .ruff.toml
2. ruff.toml
3. pyproject.toml (with [tool.ruff] section)
Args:
file_path: Path to the Python file being checked
Returns:
Path to the first Ruff config file found, or None if none found
"""
# Start from the file's directory
current_dir
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
file_path
))
# Config file names in priority order
config_files
=
[
'.ruff.toml'
,
'ruff.toml'
,
'pyproject.toml'
]
# Walk up the directory tree
while
True
:
# Check for config files in current directory
for
config_file
in
config_files
:
config_path
=
os
.
path
.
join
(
current_dir
,
config_file
)
if
os
.
path
.
exists
(
config_path
):
# For pyproject.toml, check if it contains [tool.ruff] section
if
config_file
==
'pyproject.toml'
:
try
:
with
open
(
config_path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
content
=
f
.
read
()
if
'[tool.ruff]'
in
content
:
return
config_path
except
(
IOError
,
UnicodeDecodeError
):
# If we can't read it, let Ruff handle it
pass
else
:
return
config_path
# Move to parent directory
parent_dir
=
os
.
path
.
dirname
(
current_dir
)
if
parent_dir
==
current_dir
:
# Reached root directory
break
current_dir
=
parent_dir
return
None
def
_build_ruff_config
(
linters
:
List
[
str
],
ignore
:
List
[
str
],
select
:
List
[
str
])
->
Dict
[
str
,
Any
]:
"""Build ruff configuration from pymode settings."""
config
=
{}
# Map old linter names to ruff rule categories
linter_mapping
=
{
'pyflakes'
: [
'F'
],
# Pyflakes rules
'pycodestyle'
: [
'E'
,
'W'
],
# pycodestyle rules
'pep8'
: [
'E'
,
'W'
],
# Legacy pep8 (same as pycodestyle)
'mccabe'
: [
'C90'
],
# McCabe complexity (C901 is specific, C90 is category)
'pylint'
: [
'PL'
],
# Pylint rules
'pydocstyle'
: [
'D'
],
# pydocstyle rules
'pep257'
: [
'D'
],
# Legacy pep257 (same as pydocstyle)
'autopep8'
: [
'E'
,
'W'
],
# Same as pycodestyle for checking
}
# Build select rules from linters and explicit select
select_rules
=
set
()
# Add rules from explicit select first
if
select
:
select_rules
.
update
(
select
)
# Add rules from enabled linters
for
linter
in
linters
:
if
linter
in
linter_mapping
:
select_rules
.
update
(
linter_mapping
[
linter
])
# If no specific rules selected, use a sensible default
if
not
select_rules
:
select_rules
=
{
'F'
,
'E'
,
'W'
}
# Pyflakes + pycodestyle by default
config
[
'select'
]
=
list
(
select_rules
)
# Add ignore rules
if
ignore
:
config
[
'ignore'
]
=
ignore
# Handle tool-specific options
_add_tool_specific_options
(
config
,
linters
)
# Add other common settings
max_line_length
=
env
.
var
(
'g:pymode_options_max_line_length'
,
silence
=
True
,
default
=
79
)
if
max_line_length
:
config
[
'line-length'
]
=
int
(
max_line_length
)
return
config
def
_add_tool_specific_options
(
config
:
Dict
[
str
,
Any
],
linters
:
List
[
str
])
->
None
:
"""Add tool-specific configuration options."""
# Handle mccabe complexity
if
'mccabe'
in
linters
:
mccabe_opts
=
env
.
var
(
'g:pymode_lint_options_mccabe'
,
silence
=
True
,
default
=
{})
if
mccabe_opts
and
'complexity'
in
mccabe_opts
:
# Ruff uses mccabe.max-complexity
config
[
'mccabe'
]
=
{
'max-complexity'
:
mccabe_opts
[
'complexity'
]}
# Handle pycodestyle options
if
'pycodestyle'
in
linters
or
'pep8'
in
linters
:
pycodestyle_opts
=
env
.
var
(
'g:pymode_lint_options_pycodestyle'
,
silence
=
True
,
default
=
{})
if
pycodestyle_opts
:
if
'max_line_length'
in
pycodestyle_opts
:
config
[
'line-length'
]
=
pycodestyle_opts
[
'max_line_length'
]
# Handle pylint options
if
'pylint'
in
linters
:
pylint_opts
=
env
.
var
(
'g:pymode_lint_options_pylint'
,
silence
=
True
,
default
=
{})
if
pylint_opts
:
if
'max-line-length'
in
pylint_opts
:
config
[
'line-length'
]
=
pylint_opts
[
'max-line-length'
]
# Handle pydocstyle/pep257 options
if
'pydocstyle'
in
linters
or
'pep257'
in
linters
:
pydocstyle_opts
=
env
.
var
(
'g:pymode_lint_options_pep257'
,
silence
=
True
,
default
=
{})
# Most pydocstyle options don't have direct ruff equivalents
# Users should configure ruff directly for advanced docstring checking
# Handle pyflakes options
if
'pyflakes'
in
linters
:
pyflakes_opts
=
env
.
var
(
'g:pymode_lint_options_pyflakes'
,
silence
=
True
,
default
=
{})
# Pyflakes builtins option doesn't have a direct ruff equivalent
# Users can use ruff's built-in handling or per-file ignores
def
_build_ruff_args
(
config
:
Dict
[
str
,
Any
])
->
List
[
str
]:
"""Build ruff command line arguments from configuration."""
args
=
[]
# Add select rules
if
'select'
in
config
:
# Join multiple rules with comma for efficiency
select_str
=
','
.
join
(
config
[
'select'
])
args
.
extend
([
'--select'
,
select_str
])
# Add ignore rules
if
'ignore'
in
config
:
# Join multiple rules with comma for efficiency
ignore_str
=
','
.
join
(
config
[
'ignore'
])
args
.
extend
([
'--ignore'
,
ignore_str
])
# Add line length
if
'line-length'
in
config
:
args
.
extend
([
'--line-length'
,
str
(
config
[
'line-length'
])])
# Note: mccabe complexity needs to be set in pyproject.toml or ruff.toml
# We can't easily set it via command line args, so we'll document this limitation
return
args
def
validate_configuration
()
->
List
[
str
]:
"""Validate pymode configuration for ruff compatibility.
Returns:
List of warning messages about configuration issues
"""
warnings
=
[]
# Check if ruff is available
if
not
check_ruff_available
():
warnings
.
append
(
"Ruff is not installed. Please install with: pip install ruff"
)
return
warnings
# Check linter configuration
linters
=
env
.
var
(
'g:pymode_lint_checkers'
,
default
=
[
'pyflakes'
,
'pycodestyle'
])
supported_linters
=
{
'pyflakes'
,
'pycodestyle'
,
'pep8'
,
'mccabe'
,
'pylint'
,
'pydocstyle'
,
'pep257'
}
for
linter
in
linters
:
if
linter
not
in
supported_linters
:
warnings
.
append
(
f"Linter '
{
linter
}
' is not supported by ruff integration"
)
# Check mccabe complexity configuration
if
'mccabe'
in
linters
:
mccabe_opts
=
env
.
var
(
'g:pymode_lint_options_mccabe'
,
silence
=
True
,
default
=
{})
if
mccabe_opts
and
'complexity'
in
mccabe_opts
:
warnings
.
append
(
"McCabe complexity setting requires ruff configuration file (pyproject.toml or ruff.toml)"
)
# Check for deprecated pep8 linter
if
'pep8'
in
linters
:
warnings
.
append
(
"'pep8' linter is deprecated, use 'pycodestyle' instead"
)
# Check for deprecated pep257 linter
if
'pep257'
in
linters
:
warnings
.
append
(
"'pep257' linter is deprecated, use 'pydocstyle' instead"
)
return
warnings
def
run_ruff_check
(
file_path
:
str
,
content
:
str
=
None
)
->
List
[
RuffError
]:
"""Run ruff check on a file and return errors.
Args:
file_path: Path to the file to check
content: Optional file content (for checking unsaved buffers)
Returns:
List of RuffError objects
"""
# Check if Ruff is enabled
if
not
env
.
var
(
'g:pymode_ruff_enabled'
,
silence
=
True
,
default
=
True
):
return
[]
try
:
ruff_path
=
_get_ruff_executable
()
except
RuntimeError
:
return
[]
# Get configuration mode
config_mode
=
env
.
var
(
'g:pymode_ruff_config_mode'
,
silence
=
True
,
default
=
'local_override'
)
# Prepare command
cmd
=
[
ruff_path
,
'check'
,
'--output-format=json'
]
# Check for local config file (used in multiple modes)
local_config
=
_find_local_ruff_config
(
file_path
)
# Determine which config to use based on mode
if
config_mode
==
'local'
:
# Use only local config - don't pass any CLI config args
# If local config exists and we'll use a temp file, explicitly point to it
if
local_config
and
content
is
not
None
:
cmd
.
extend
([
'--config'
,
local_config
])
# Otherwise, Ruff will auto-discover local config files
elif
config_mode
==
'local_override'
:
# Check if local config exists
if
local_config
:
# Local config found - use it
# If we'll use a temp file, explicitly point to the config
if
content
is
not
None
:
cmd
.
extend
([
'--config'
,
local_config
])
# Otherwise, Ruff will auto-discover and use local config
else
:
# No local config - use pymode settings as fallback
ruff_select
=
env
.
var
(
'g:pymode_ruff_select'
,
silence
=
True
,
default
=
[])
ruff_ignore
=
env
.
var
(
'g:pymode_ruff_ignore'
,
silence
=
True
,
default
=
[])
if
ruff_select
or
ruff_ignore
:
# Use Ruff-specific configuration
linters
=
env
.
var
(
'g:pymode_lint_checkers'
,
default
=
[
'pyflakes'
,
'pycodestyle'
])
ignore
=
ruff_ignore
if
ruff_ignore
else
env
.
var
(
'g:pymode_lint_ignore'
,
default
=
[])
select
=
ruff_select
if
ruff_select
else
env
.
var
(
'g:pymode_lint_select'
,
default
=
[])
else
:
# Use legacy configuration (backward compatibility)
linters
=
env
.
var
(
'g:pymode_lint_checkers'
,
default
=
[
'pyflakes'
,
'pycodestyle'
])
ignore
=
env
.
var
(
'g:pymode_lint_ignore'
,
default
=
[])
select
=
env
.
var
(
'g:pymode_lint_select'
,
default
=
[])
# Build ruff configuration
config
=
_build_ruff_config
(
linters
,
ignore
,
select
)
# Add configuration arguments
if
config
:
cmd
.
extend
(
_build_ruff_args
(
config
))
elif
config_mode
==
'global'
:
# Use only pymode settings - ignore local configs
cmd
.
append
(
'--isolated'
)
# Get pymode configuration
ruff_select
=
env
.
var
(
'g:pymode_ruff_select'
,
silence
=
True
,
default
=
[])
ruff_ignore
=
env
.
var
(
'g:pymode_ruff_ignore'
,
silence
=
True
,
default
=
[])
if
ruff_select
or
ruff_ignore
:
# Use Ruff-specific configuration
linters
=
env
.
var
(
'g:pymode_lint_checkers'
,
default
=
[
'pyflakes'
,
'pycodestyle'
])
ignore
=
ruff_ignore
if
ruff_ignore
else
env
.
var
(
'g:pymode_lint_ignore'
,
default
=
[])
select
=
ruff_select
if
ruff_select
else
env
.
var
(
'g:pymode_lint_select'
,
default
=
[])
else
:
# Use legacy configuration (backward compatibility)
linters
=
env
.
var
(
'g:pymode_lint_checkers'
,
default
=
[
'pyflakes'
,
'pycodestyle'
])
ignore
=
env
.
var
(
'g:pymode_lint_ignore'
,
default
=
[])
select
=
env
.
var
(
'g:pymode_lint_select'
,
default
=
[])
# Build ruff configuration
config
=
_build_ruff_config
(
linters
,
ignore
,
select
)
# Add configuration arguments
if
config
:
cmd
.
extend
(
_build_ruff_args
(
config
))
else
:
# Invalid mode - default to local_override behavior
env
.
debug
(
f"Invalid g:pymode_ruff_config_mode:
{
config_mode
}
, using 'local_override'"
)
if
not
local_config
:
# No local config - use pymode settings
ruff_select
=
env
.
var
(
'g:pymode_ruff_select'
,
silence
=
True
,
default
=
[])
ruff_ignore
=
env
.
var
(
'g:pymode_ruff_ignore'
,
silence
=
True
,
default
=
[])
if
ruff_select
or
ruff_ignore
:
linters
=
env
.
var
(
'g:pymode_lint_checkers'
,
default
=
[
'pyflakes'
,
'pycodestyle'
])
ignore
=
ruff_ignore
if
ruff_ignore
else
env
.
var
(
'g:pymode_lint_ignore'
,
default
=
[])
select
=
ruff_select
if
ruff_select
else
env
.
var
(
'g:pymode_lint_select'
,
default
=
[])
else
:
linters
=
env
.
var
(
'g:pymode_lint_checkers'
,
default
=
[
'pyflakes'
,
'pycodestyle'
])
ignore
=
env
.
var
(
'g:pymode_lint_ignore'
,
default
=
[])
select
=
env
.
var
(
'g:pymode_lint_select'
,
default
=
[])
config
=
_build_ruff_config
(
linters
,
ignore
,
select
)
if
config
:
cmd
.
extend
(
_build_ruff_args
(
config
))
# Handle content checking (for unsaved buffers)
temp_file_path
=
None
if
content
is
not
None
:
# Write content to temporary file
fd
,
temp_file_path
=
tempfile
.
mkstemp
(
suffix
=
'.py'
,
prefix
=
'pymode_'
)
try
:
with
os
.
fdopen
(
fd
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
content
)
cmd
.
append
(
temp_file_path
)
except
Exception
:
os
.
close
(
fd
)
if
temp_file_path
:
os
.
unlink
(
temp_file_path
)
raise
else
:
cmd
.
append
(
file_path
)
errors
=
[]
try
:
with
silence_stderr
():
# Run ruff
result
=
subprocess
.
run
(
cmd
,
capture_output
=
True
,
text
=
True
,
timeout
=
30
,
cwd
=
env
.
curdir
)
# Ruff returns non-zero exit code when issues are found
if
result
.
stdout
:
try
:
# Parse JSON output
ruff_output
=
json
.
loads
(
result
.
stdout
)
for
item
in
ruff_output
:
# Map temp file path back to original if needed
if
temp_file_path
and
item
.
get
(
'filename'
)
==
temp_file_path
:
item
[
'filename'
]
=
file_path
errors
.
append
(
RuffError
(
item
))
except
json
.
JSONDecodeError
as
e
:
env
.
debug
(
f"Failed to parse ruff JSON output:
{
e
}
"
)
env
.
debug
(
f"Raw output:
{
result
.
stdout
}
"
)
if
result
.
stderr
:
env
.
debug
(
f"Ruff stderr:
{
result
.
stderr
}
"
)
except
subprocess
.
TimeoutExpired
:
env
.
error
(
"Ruff check timed out"
)
except
Exception
as
e
:
env
.
debug
(
f"Ruff check failed:
{
e
}
"
)
finally
:
# Clean up temporary file
if
temp_file_path
:
try
:
os
.
unlink
(
temp_file_path
)
except
OSError
:
pass
return
errors
def
run_ruff_format
(
file_path
:
str
,
content
:
str
=
None
)
->
Optional
[
str
]:
"""Run ruff format on a file and return formatted content.
Args:
file_path: Path to the file to format
content: Optional file content (for formatting unsaved buffers)
Returns:
Formatted content as string, or None if formatting failed
"""
try
:
ruff_path
=
_get_ruff_executable
()
except
RuntimeError
:
return
None
# Check if formatting is enabled
if
not
env
.
var
(
'g:pymode_ruff_format_enabled'
,
silence
=
True
,
default
=
True
):
return
None
# Get configuration mode
config_mode
=
env
.
var
(
'g:pymode_ruff_config_mode'
,
silence
=
True
,
default
=
'local_override'
)
# Check for local config file (used in multiple modes)
local_config
=
_find_local_ruff_config
(
file_path
)
# Prepare command
cmd
=
[
ruff_path
,
'format'
,
'--stdin-filename'
,
file_path
]
# Determine which config to use based on mode
if
config_mode
==
'local'
:
# Use only local config - Ruff will use --stdin-filename to discover config
# If local config exists, explicitly point to it for consistency
if
local_config
:
cmd
.
extend
([
'--config'
,
local_config
])
elif
config_mode
==
'local_override'
:
# Check if local config exists
if
local_config
:
# Local config found - explicitly use it
cmd
.
extend
([
'--config'
,
local_config
])
else
:
# No local config - use pymode config file if specified
config_file
=
env
.
var
(
'g:pymode_ruff_config_file'
,
silence
=
True
,
default
=
''
)
if
config_file
and
os
.
path
.
exists
(
config_file
):
cmd
.
extend
([
'--config'
,
config_file
])
elif
config_mode
==
'global'
:
# Use only pymode settings - ignore local configs
cmd
.
append
(
'--isolated'
)
# Use pymode config file if specified
config_file
=
env
.
var
(
'g:pymode_ruff_config_file'
,
silence
=
True
,
default
=
''
)
if
config_file
and
os
.
path
.
exists
(
config_file
):
cmd
.
extend
([
'--config'
,
config_file
])
else
:
# Invalid mode - default to local_override behavior
env
.
debug
(
f"Invalid g:pymode_ruff_config_mode:
{
config_mode
}
, using 'local_override'"
)
if
not
local_config
:
config_file
=
env
.
var
(
'g:pymode_ruff_config_file'
,
silence
=
True
,
default
=
''
)
if
config_file
and
os
.
path
.
exists
(
config_file
):
cmd
.
extend
([
'--config'
,
config_file
])
try
:
with
silence_stderr
():
# Run ruff format
result
=
subprocess
.
run
(
cmd
,
input
=
content
if
content
is
not
None
else
open
(
file_path
).
read
(),
capture_output
=
True
,
text
=
True
,
timeout
=
30
,
cwd
=
env
.
curdir
)
if
result
.
returncode
==
0
:
return
result
.
stdout
else
:
# If ruff fails due to syntax errors, return original content
# This maintains backward compatibility with autopep8 behavior
# if "Failed to parse" in result.stderr or "SyntaxError" in result.stderr:
# env.debug(f"Ruff format skipped due to syntax errors: {result.stderr}")
# return content if content else None
env
.
debug
(
f"Ruff format failed:
{
result
.
stderr
}
"
)
return
None
except
subprocess
.
TimeoutExpired
:
env
.
error
(
"Ruff format timed out"
)
return
None
except
Exception
as
e
:
env
.
debug
(
f"Ruff format failed:
{
e
}
"
)
return
None
def
check_ruff_available
()
->
bool
:
"""Check if ruff is available and working."""
try
:
_get_ruff_executable
()
return
True
except
RuntimeError
:
return
False
# Legacy compatibility function
def
code_check
():
"""Run ruff check on current buffer (replaces pylama integration).
This function maintains compatibility with the existing pymode interface.
"""
if
not
env
.
curbuf
.
name
:
return
env
.
stop
()
# Get file content from current buffer
content
=
'
\n
'
.
join
(
env
.
curbuf
)
+
'
\n
'
file_path
=
env
.
curbuf
.
name
# Use relpath if possible, but handle Windows drive letter differences
try
:
rel_path
=
os
.
path
.
relpath
(
file_path
,
env
.
curdir
)
env
.
debug
(
"Start ruff code check: "
,
rel_path
)
except
ValueError
:
# On Windows, relpath fails if paths are on different drives
# Fall back to absolute path in this case
env
.
debug
(
"Start ruff code check (abs path): "
,
file_path
)
# Run ruff check
errors
=
run_ruff_check
(
file_path
,
content
)
env
.
debug
(
"Find errors: "
,
len
(
errors
))
# Convert to vim-compatible format
errors_list
=
[]
for
error
in
errors
:
err_dict
=
error
.
to_dict
()
err_dict
[
'bufnr'
]
=
env
.
curbuf
.
number
errors_list
.
append
(
err_dict
)
# Apply sorting if configured
sort_rules
=
env
.
var
(
'g:pymode_lint_sort'
,
default
=
[])
if
sort_rules
:
def
__sort
(
e
):
try
:
return
sort_rules
.
index
(
e
.
get
(
'type'
))
except
ValueError
:
return
999
errors_list
=
sorted
(
errors_list
,
key
=
__sort
)
# Add to location list
env
.
run
(
'g:PymodeLocList.current().extend'
,
errors_list
)
Back
|
FazBrowse Home
|
New Git URL