Skip to content

Commit

Permalink
Add --required-version
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Dec 26, 2022
1 parent ec80d1c commit fe6057e
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 17 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -50,6 +50,7 @@ rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "1b6cb170e925a43d605b3fed9f6b878e63e47744" }
schemars = { version = "0.8.11" }
semver = { version = "1.0.16" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
shellexpand = { version = "3.0.0" }
Expand Down
18 changes: 18 additions & 0 deletions README.md
Expand Up @@ -1948,6 +1948,24 @@ when considering any matching files.

---

#### [`required-version`](#required-version)

Require a specific version of Ruff to be running (useful for unifying results across
many environments, e.g., with a `pyproject.toml` file).

**Default value**: `None`

**Type**: `String`

**Example usage**:

```toml
[tool.ruff]
required-version = "0.0.193"
```

---

#### [`respect-gitignore`](#respect-gitignore)

Whether to automatically exclude files that are ignored by `.ignore`,
Expand Down
7 changes: 7 additions & 0 deletions flake8_to_ruff/src/converter.rs
Expand Up @@ -302,6 +302,7 @@ mod tests {
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
Expand Down Expand Up @@ -357,6 +358,7 @@ mod tests {
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
Expand Down Expand Up @@ -412,6 +414,7 @@ mod tests {
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
Expand Down Expand Up @@ -467,6 +470,7 @@ mod tests {
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
Expand Down Expand Up @@ -522,6 +526,7 @@ mod tests {
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
Expand Down Expand Up @@ -585,6 +590,7 @@ mod tests {
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::D100,
Expand Down Expand Up @@ -676,6 +682,7 @@ mod tests {
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
required_version: None,
respect_gitignore: None,
select: Some(vec![
CheckCodePrefix::E,
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Expand Up @@ -34,7 +34,11 @@ bindings = "bin"
strip = true

[tool.ruff]
#required-version = "0.0.192"

[tool.ruff.isort]
force-wrap-aliases = true
combine-as-imports = true

[tool.black]
required-version = "22.12.1"
7 changes: 7 additions & 0 deletions ruff.schema.json
Expand Up @@ -281,6 +281,13 @@
}
]
},
"required-version": {
"description": "Require a specific version of Ruff to be running (useful for unifying results across many environments, e.g., with a `pyproject.toml` file).",
"type": [
"string",
"null"
]
},
"respect-gitignore": {
"description": "Whether to automatically exclude files that are ignored by `.ignore`, `.gitignore`, `.git/info/exclude`, and global `gitignore` files. Enabled by default.",
"type": [
Expand Down
33 changes: 24 additions & 9 deletions src/commands.rs
Expand Up @@ -38,14 +38,8 @@ pub fn run(
let duration = start.elapsed();
debug!("Identified files to lint in: {:?}", duration);

// Discover the package root for each Python file.
let package_roots = packages::detect_package_roots(
&paths
.iter()
.flatten()
.map(ignore::DirEntry::path)
.collect::<Vec<_>>(),
);
// Validate the `Settings` and return any errors.
resolver.validate(pyproject_strategy)?;

// Initialize the cache.
if matches!(cache, flags::Cache::Enabled) {
Expand All @@ -71,6 +65,15 @@ pub fn run(
}
};

// Discover the package root for each Python file.
let package_roots = packages::detect_package_roots(
&paths
.iter()
.flatten()
.map(ignore::DirEntry::path)
.collect::<Vec<_>>(),
);

let start = Instant::now();
let mut diagnostics: Diagnostics = par_iter(&paths)
.map(|entry| {
Expand Down Expand Up @@ -176,6 +179,9 @@ pub fn add_noqa(
let duration = start.elapsed();
debug!("Identified files to lint in: {:?}", duration);

// Validate the `Settings` and return any errors.
resolver.validate(pyproject_strategy)?;

let start = Instant::now();
let modifications: usize = par_iter(&paths)
.flatten()
Expand Down Expand Up @@ -212,6 +218,9 @@ pub fn autoformat(
let duration = start.elapsed();
debug!("Identified files to lint in: {:?}", duration);

// Validate the `Settings` and return any errors.
resolver.validate(pyproject_strategy)?;

let start = Instant::now();
let modifications = par_iter(&paths)
.flatten()
Expand Down Expand Up @@ -245,6 +254,9 @@ pub fn show_settings(
let (paths, resolver) =
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;

// Validate the `Settings` and return any errors.
resolver.validate(pyproject_strategy)?;

// Print the list of files.
let Some(entry) = paths
.iter()
Expand All @@ -268,9 +280,12 @@ pub fn show_files(
overrides: &Overrides,
) -> Result<()> {
// Collect all files in the hierarchy.
let (paths, _resolver) =
let (paths, resolver) =
resolver::python_files_in_path(files, pyproject_strategy, file_strategy, overrides)?;

// Validate the `Settings` and return any errors.
resolver.validate(pyproject_strategy)?;

// Print the list of files.
for entry in paths
.iter()
Expand Down
17 changes: 16 additions & 1 deletion src/linter.rs
Expand Up @@ -59,6 +59,9 @@ pub(crate) fn check_path(
autofix: flags::Autofix,
noqa: flags::Noqa,
) -> Result<Vec<Check>> {
// Validate the `Settings` and return any errors.
settings.validate()?;

// Aggregate all checks.
let mut checks: Vec<Check> = vec![];

Expand Down Expand Up @@ -175,6 +178,9 @@ pub fn lint_path(
cache: flags::Cache,
autofix: fixer::Mode,
) -> Result<Diagnostics> {
// Validate the `Settings` and return any errors.
settings.validate()?;

let metadata = path.metadata()?;

// Check the cache.
Expand Down Expand Up @@ -202,6 +208,9 @@ pub fn lint_path(

/// Add any missing `#noqa` pragmas to the source code at the given `Path`.
pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
// Validate the `Settings` and return any errors.
settings.validate()?;

// Read the file from disk.
let contents = fs::read_file(path)?;

Expand Down Expand Up @@ -241,7 +250,10 @@ pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
}

/// Apply autoformatting to the source code at the given `Path`.
pub fn autoformat_path(path: &Path, _settings: &Settings) -> Result<()> {
pub fn autoformat_path(path: &Path, settings: &Settings) -> Result<()> {
// Validate the `Settings` and return any errors.
settings.validate()?;

// Read the file from disk.
let contents = fs::read_file(path)?;

Expand All @@ -266,6 +278,9 @@ pub fn lint_stdin(
settings: &Settings,
autofix: fixer::Mode,
) -> Result<Diagnostics> {
// Validate the `Settings` and return any errors.
settings.validate()?;

// Read the file from disk.
let contents = stdin.to_string();

Expand Down
6 changes: 6 additions & 0 deletions src/main.rs
Expand Up @@ -100,6 +100,12 @@ fn inner_main() -> Result<ExitCode> {
cli.stdin_filename.as_deref(),
)?;

// Validate the `Settings` and return any errors.
match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => settings.validate()?,
PyprojectDiscovery::Hierarchical(settings) => settings.validate()?,
};

// Extract options that are included in `Settings`, but only apply at the top
// level.
let file_strategy = FileDiscovery {
Expand Down
19 changes: 19 additions & 0 deletions src/resolver.rs
Expand Up @@ -91,6 +91,25 @@ impl Resolver {
pub fn iter(&self) -> impl Iterator<Item = &Settings> {
self.settings.values()
}

/// Validate all resolved `Settings` in this `Resolver`.
pub fn validate(&self, strategy: &PyprojectDiscovery) -> Result<()> {
// TODO(charlie): This risks false positives (but not false negatives), since
// some of the `Settings` in the path may ultimately be unused. It'd be
// preferable to validate once we've determined the `Settings` for each
// path, but that's more expensive.
match &strategy {
PyprojectDiscovery::Fixed(settings) => {
settings.validate()?;
}
PyprojectDiscovery::Hierarchical(default) => {
for settings in std::iter::once(default).chain(self.iter()) {
settings.validate()?;
}
}
}
Ok(())
}
}

/// Recursively resolve a `Configuration` from a `pyproject.toml` file at the
Expand Down
9 changes: 7 additions & 2 deletions src/settings/configuration.rs
Expand Up @@ -16,7 +16,9 @@ use crate::checks_gen::CheckCodePrefix;
use crate::cli::{collect_per_file_ignores, Overrides};
use crate::settings::options::Options;
use crate::settings::pyproject::load_options;
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion, SerializationFormat};
use crate::settings::types::{
FilePattern, PerFileIgnore, PythonVersion, SerializationFormat, Version,
};
use crate::{
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions, flake8_quotes,
flake8_tidy_imports, flake8_unused_arguments, fs, isort, mccabe, pep8_naming, pyupgrade,
Expand All @@ -40,6 +42,7 @@ pub struct Configuration {
pub ignore_init_module_imports: Option<bool>,
pub line_length: Option<usize>,
pub per_file_ignores: Option<Vec<PerFileIgnore>>,
pub required_version: Option<Version>,
pub respect_gitignore: Option<bool>,
pub select: Option<Vec<CheckCodePrefix>>,
pub show_source: Option<bool>,
Expand Down Expand Up @@ -122,6 +125,7 @@ impl Configuration {
})
.collect()
}),
required_version: options.required_version,
respect_gitignore: options.respect_gitignore,
select: options.select,
show_source: options.show_source,
Expand Down Expand Up @@ -160,7 +164,6 @@ impl Configuration {
allowed_confusables: self.allowed_confusables.or(config.allowed_confusables),
dummy_variable_rgx: self.dummy_variable_rgx.or(config.dummy_variable_rgx),
exclude: self.exclude.or(config.exclude),
respect_gitignore: self.respect_gitignore.or(config.respect_gitignore),
extend: self.extend.or(config.extend),
extend_exclude: config
.extend_exclude
Expand Down Expand Up @@ -188,6 +191,8 @@ impl Configuration {
.or(config.ignore_init_module_imports),
line_length: self.line_length.or(config.line_length),
per_file_ignores: self.per_file_ignores.or(config.per_file_ignores),
required_version: self.required_version.or(config.required_version),
respect_gitignore: self.respect_gitignore.or(config.respect_gitignore),
select: self.select.or(config.select),
show_source: self.show_source.or(config.show_source),
src: self.src.or(config.src),
Expand Down

0 comments on commit fe6057e

Please sign in to comment.