Add support for defining extra builtins (#1747)

Resolves #1745.
This commit is contained in:
Charlie Marsh 2023-01-09 12:24:28 -05:00 committed by GitHub
parent 59155ce9f6
commit 2729f3d207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 131 additions and 1 deletions

View File

@ -1739,6 +1739,24 @@ allowed-confusables = ["", "ρ", ""]
--- ---
#### [`builtins`](#builtins)
A list of builtins to treat as defined references, in addition to the
system builtins.
**Default value**: `[]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff]
builtins = ["_"]
```
---
#### [`cache-dir`](#cache-dir) #### [`cache-dir`](#cache-dir)
A path to the cache directory. A path to the cache directory.

View File

@ -0,0 +1,19 @@
[flake8]
# Ignore style and complexity
# E: style errors
# W: style warnings
# C: complexity
# D: docstring warnings (unused pydocstyle extension)
# F841: local variable assigned but never used
ignore = E, C, W, D, F841
builtins = c, get_config
exclude =
.cache,
.github,
docs,
jupyterhub/alembic*,
onbuild,
scripts,
share,
tools,
setup.py

View File

@ -99,6 +99,9 @@ pub fn convert(
if let Some(value) = value { if let Some(value) = value {
match key.as_str() { match key.as_str() {
// flake8 // flake8
"builtins" => {
options.builtins = Some(parser::parse_strings(value.as_ref()));
}
"max-line-length" | "max_line_length" => match value.clone().parse::<usize>() { "max-line-length" | "max_line_length" => match value.clone().parse::<usize>() {
Ok(line_length) => options.line_length = Some(line_length), Ok(line_length) => options.line_length = Some(line_length),
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"), Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
@ -362,6 +365,7 @@ mod tests {
)?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -425,6 +429,7 @@ mod tests {
)?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -488,6 +493,7 @@ mod tests {
)?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -551,6 +557,7 @@ mod tests {
)?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -614,6 +621,7 @@ mod tests {
)?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -685,6 +693,7 @@ mod tests {
)?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -751,6 +760,7 @@ mod tests {
)?; )?;
let expected = Pyproject::new(Options { let expected = Pyproject::new(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,

View File

@ -0,0 +1 @@
_("Translations")

View File

@ -15,6 +15,16 @@
"minLength": 1 "minLength": 1
} }
}, },
"builtins": {
"description": "A list of builtins to treat as defined references, in addition to the system builtins.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"cache-dir": { "cache-dir": {
"description": "A path to the cache directory.\n\nBy default, Ruff stores cache results in a `.ruff_cache` directory in the current project root.\n\nHowever, Ruff will also respect the `RUFF_CACHE_DIR` environment variable, which takes precedence over that default.\n\nThis setting will override even the `RUFF_CACHE_DIR` environment variable, if set.", "description": "A path to the cache directory.\n\nBy default, Ruff stores cache results in a `.ruff_cache` directory in the current project root.\n\nHowever, Ruff will also respect the `RUFF_CACHE_DIR` environment variable, which takes precedence over that default.\n\nThis setting will override even the `RUFF_CACHE_DIR` environment variable, if set.",
"type": [ "type": [

View File

@ -3291,7 +3291,12 @@ impl<'a> Checker<'a> {
fn bind_builtins(&mut self) { fn bind_builtins(&mut self) {
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))]; let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
for builtin in BUILTINS.iter().chain(MAGIC_GLOBALS.iter()) { for builtin in BUILTINS
.iter()
.chain(MAGIC_GLOBALS.iter())
.copied()
.chain(self.settings.builtins.iter().map(String::as_str))
{
let index = self.bindings.len(); let index = self.bindings.len();
self.bindings.push(Binding { self.bindings.push(Binding {
kind: BindingKind::Builtin, kind: BindingKind::Builtin,

View File

@ -140,6 +140,29 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn default_builtins() -> Result<()> {
let diagnostics = test_path(
Path::new("./resources/test/fixtures/pyflakes/builtins.py"),
&settings::Settings::for_rules(vec![RuleCode::F821]),
)?;
insta::assert_yaml_snapshot!(diagnostics);
Ok(())
}
#[test]
fn extra_builtins() -> Result<()> {
let diagnostics = test_path(
Path::new("./resources/test/fixtures/pyflakes/builtins.py"),
&settings::Settings {
builtins: vec!["_".to_string()],
..settings::Settings::for_rules(vec![RuleCode::F821])
},
)?;
insta::assert_yaml_snapshot!(diagnostics);
Ok(())
}
#[test] #[test]
fn future_annotations() -> Result<()> { fn future_annotations() -> Result<()> {
let diagnostics = test_path( let diagnostics = test_path(

View File

@ -0,0 +1,15 @@
---
source: src/pyflakes/mod.rs
expression: diagnostics
---
- kind:
UndefinedName: _
location:
row: 1
column: 0
end_location:
row: 1
column: 1
fix: ~
parent: ~

View File

@ -0,0 +1,6 @@
---
source: src/pyflakes/mod.rs
expression: diagnostics
---
[]

View File

@ -28,6 +28,7 @@ use crate::{
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Configuration { pub struct Configuration {
pub allowed_confusables: Option<Vec<char>>, pub allowed_confusables: Option<Vec<char>>,
pub builtins: Option<Vec<String>>,
pub cache_dir: Option<PathBuf>, pub cache_dir: Option<PathBuf>,
pub dummy_variable_rgx: Option<Regex>, pub dummy_variable_rgx: Option<Regex>,
pub exclude: Option<Vec<FilePattern>>, pub exclude: Option<Vec<FilePattern>>,
@ -80,6 +81,7 @@ impl Configuration {
pub fn from_options(options: Options, project_root: &Path) -> Result<Self> { pub fn from_options(options: Options, project_root: &Path) -> Result<Self> {
Ok(Configuration { Ok(Configuration {
allowed_confusables: options.allowed_confusables, allowed_confusables: options.allowed_confusables,
builtins: options.builtins,
cache_dir: options cache_dir: options
.cache_dir .cache_dir
.map(|dir| { .map(|dir| {
@ -177,6 +179,7 @@ impl Configuration {
pub fn combine(self, config: Configuration) -> Self { pub fn combine(self, config: Configuration) -> Self {
Self { Self {
allowed_confusables: self.allowed_confusables.or(config.allowed_confusables), allowed_confusables: self.allowed_confusables.or(config.allowed_confusables),
builtins: self.builtins.or(config.builtins),
cache_dir: self.cache_dir.or(config.cache_dir), cache_dir: self.cache_dir.or(config.cache_dir),
dummy_variable_rgx: self.dummy_variable_rgx.or(config.dummy_variable_rgx), dummy_variable_rgx: self.dummy_variable_rgx.or(config.dummy_variable_rgx),
exclude: self.exclude.or(config.exclude), exclude: self.exclude.or(config.exclude),

View File

@ -40,6 +40,7 @@ const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct Settings { pub struct Settings {
pub allowed_confusables: FxHashSet<char>, pub allowed_confusables: FxHashSet<char>,
pub builtins: Vec<String>,
pub cache_dir: PathBuf, pub cache_dir: PathBuf,
pub dummy_variable_rgx: Regex, pub dummy_variable_rgx: Regex,
pub enabled: FxHashSet<RuleCode>, pub enabled: FxHashSet<RuleCode>,
@ -113,6 +114,7 @@ impl Settings {
.allowed_confusables .allowed_confusables
.map(FxHashSet::from_iter) .map(FxHashSet::from_iter)
.unwrap_or_default(), .unwrap_or_default(),
builtins: config.builtins.unwrap_or_default(),
cache_dir: config.cache_dir.unwrap_or_else(|| cache_dir(project_root)), cache_dir: config.cache_dir.unwrap_or_else(|| cache_dir(project_root)),
dummy_variable_rgx: config dummy_variable_rgx: config
.dummy_variable_rgx .dummy_variable_rgx
@ -216,6 +218,7 @@ impl Settings {
pub fn for_rule(rule_code: RuleCode) -> Self { pub fn for_rule(rule_code: RuleCode) -> Self {
Self { Self {
allowed_confusables: FxHashSet::from_iter([]), allowed_confusables: FxHashSet::from_iter([]),
builtins: vec![],
cache_dir: cache_dir(path_dedot::CWD.as_path()), cache_dir: cache_dir(path_dedot::CWD.as_path()),
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(), dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
enabled: FxHashSet::from_iter([rule_code.clone()]), enabled: FxHashSet::from_iter([rule_code.clone()]),
@ -258,6 +261,7 @@ impl Settings {
pub fn for_rules(rule_codes: Vec<RuleCode>) -> Self { pub fn for_rules(rule_codes: Vec<RuleCode>) -> Self {
Self { Self {
allowed_confusables: FxHashSet::from_iter([]), allowed_confusables: FxHashSet::from_iter([]),
builtins: vec![],
cache_dir: cache_dir(path_dedot::CWD.as_path()), cache_dir: cache_dir(path_dedot::CWD.as_path()),
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(), dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
enabled: FxHashSet::from_iter(rule_codes.clone()), enabled: FxHashSet::from_iter(rule_codes.clone()),

View File

@ -30,6 +30,16 @@ pub struct Options {
/// A list of allowed "confusable" Unicode characters to ignore when /// A list of allowed "confusable" Unicode characters to ignore when
/// enforcing `RUF001`, `RUF002`, and `RUF003`. /// enforcing `RUF001`, `RUF002`, and `RUF003`.
pub allowed_confusables: Option<Vec<char>>, pub allowed_confusables: Option<Vec<char>>,
#[option(
default = r#"[]"#,
value_type = "Vec<String>",
example = r#"
builtins = ["_"]
"#
)]
/// A list of builtins to treat as defined references, in addition to the
/// system builtins.
pub builtins: Option<Vec<String>>,
#[option( #[option(
default = ".ruff_cache", default = ".ruff_cache",
value_type = "PathBuf", value_type = "PathBuf",

View File

@ -164,6 +164,7 @@ mod tests {
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -221,6 +222,7 @@ line-length = 79
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
extend: None, extend: None,
@ -278,6 +280,7 @@ exclude = ["foo.py"]
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: Some(vec!["foo.py".to_string()]), exclude: Some(vec!["foo.py".to_string()]),
@ -335,6 +338,7 @@ select = ["E501"]
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -393,6 +397,7 @@ ignore = ["E501"]
Some(Tools { Some(Tools {
ruff: Some(Options { ruff: Some(Options {
allowed_confusables: None, allowed_confusables: None,
builtins: None,
cache_dir: None, cache_dir: None,
dummy_variable_rgx: None, dummy_variable_rgx: None,
exclude: None, exclude: None,
@ -485,6 +490,7 @@ other-attribute = 1
config, config,
Options { Options {
allowed_confusables: Some(vec!['', 'ρ', '']), allowed_confusables: Some(vec!['', 'ρ', '']),
builtins: None,
line_length: Some(88), line_length: Some(88),
fix: None, fix: None,
fix_only: None, fix_only: None,