-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add per-file-target-version
option
#16257
base: main
Are you sure you want to change the base?
Conversation
|
We should respect the |
That makes sense to me. How does the approach of making the current I wasn't sure how many changes to put in this PR, but I think it definitely makes sense to wire it up somehow for useful tests. |
I'll convert to a draft because I think it's incomplete based on your suggestion. |
Hmm. I haven't really thought about it. We probably want to avoid calling |
Reopening for review. I added
Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great! I suggest renaming the field on the setting structs to e.g. default_target_version
or global_target_version
so that its name raises questions: Hmm, why global? maybe this is not the field I want. Let me check the documentation
We should also test this feature in the editor. I think it currently doesn't work for the formatter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave this one mostly to Micha, just a quick nit I spotted
@@ -129,7 +133,7 @@ fn get_prefix<'a>(settings: &'a LinterSettings, path: &Path) -> Option<&'a PathB | |||
prefix | |||
} | |||
|
|||
fn is_allowed_module(settings: &LinterSettings, module: &str) -> bool { | |||
fn is_allowed_module(settings: &LinterSettings, minor_version: u8, module: &str) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to pass around a PythonVersion
in the ruff_linter
crate where possible (only converting it to a u8
representing the minor version when crossing the crate boundary and calling a function from the ruff_python_stdlib
crate) as it's more strongly typed
fn is_allowed_module(settings: &LinterSettings, minor_version: u8, module: &str) -> bool { | |
fn is_allowed_module(settings: &LinterSettings, version: PythonVersion, module: &str) -> bool { |
f8ab420
to
778c559
Compare
Wow I didn't know eliding the lifetime in a const was a recent feature. I thought clippy used to suggest that 🤷 Maybe I need to start building with 1.80 locally instead of 1.82... |
Thanks @MichaReiser for the thorough review. I think it's in a much better place now. The only open question I see now is how to add tests for the editor integration (if that's possible). I also feel like there might be a more elegant solution to the generic |
/// The non-path-resolved Python version specified by the `target-version` input option. | ||
/// | ||
/// See [`LinterSettings::resolve_target_version`] for a way to obtain the Python version for a | ||
/// given file, while respecting the overrides in `per_file_target_version`. | ||
pub unresolved_target_version: PythonVersion, | ||
/// Path-specific overrides to `unresolved_target_version`. | ||
/// | ||
/// See [`LinterSettings::resolve_target_version`] for a way to check a given [`Path`] | ||
/// against these patterns, while falling back to `unresolved_target_version` if none of them | ||
/// match. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably mention Checker.target_version
first because that's what most devs should use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, of course
} | ||
} | ||
|
||
impl Display for CompiledPerFileIgnoreList { | ||
impl<T: CacheKey + std::fmt::Debug + PerFileKind> CompiledPerFileList<T> { | ||
pub(crate) fn iter_matches<'a, 'p>(&'a self, path: &'p Path) -> impl Iterator<Item = &'p T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably add a third argument: debug_label: &'static str
over introducing my own trait. Seems simpler
|
||
impl<T> Display for CompiledPerFileList<T> | ||
where | ||
T: Display + CacheKey, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
T: Display + CacheKey, | |
T: Display, |
pub struct CompiledPerFileIgnoreList { | ||
// Ordered as (absolute path matcher, basename matcher, rules) | ||
ignores: Vec<CompiledPerFileIgnore>, | ||
pub struct CompiledPerFileList<T: CacheKey> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub struct CompiledPerFileList<T: CacheKey> { | |
pub struct CompiledPerFileList<T> { |
I prefer to avoid having type constraints in struct and instead, only add them on the impls where needed.
I think the downside of this is that you'd have to implement CacheKey
manually (because we aren't using Rust edition 2024 yet)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that makes sense, I did find it annoying to have to put : CacheKey
everywhere. I took a brief look at adding the trait bounds in the derive macro, but I probably need a little more proc macro practice before tackling that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if that's something that you even can do in the proc macro? I'd suggest to simply implement CacheKey
manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what Clone
does, so I thought it was possible. But I looked for the source code, and it said Clone
was a compiler builtin, so maybe you're right. I already switched to the manual impl locally, I was just curious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think serde might be able to do it too, again just for my curiositiy, I don't think we need to do this here. CacheKey
is easy to implement.
There are a few end to end tests in the VS code extension but it isn't something we have good infrastructure today. But @dhruvmanila knows better than I. |
I've talked with Brent in regards to this on Discord. Looking at the change that fixed the issue which you were pointing out earlier there are two options:
I'd suggest (1) but I'm longing to create a mock server xD |
Yes, thanks @dhruvmanila! I've added unit tests for It looks like notebooks also go through this code path, but I'm happy to add notebook-specific tests if either of you want. It shouldn't be too much trouble if I reuse the Similarly, it looks like linting goes straight through |
Summary
This PR is another step in preparing to detect syntax errors in the parser. It introduces the new
per-file-target-version
top-level configuration option, which holds a mapping of compiled glob patterns to Python versions. I intend to use theLinterSettings::resolve_target_version
method here to pass to the parser:ruff/crates/ruff_linter/src/linter.rs
Lines 491 to 493 in f50849a
Test Plan
I added two new CLI tests to show that the
per-file-target-version
is respected in both the formatter and the linter.