mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 22:55:08 +00:00
Add task-tags & ignore-overlong-task-comments settings (#1550)
This commit is contained in:
parent
7c23701b62
commit
ca48492137
17 changed files with 436 additions and 105 deletions
42
README.md
42
README.md
|
@ -2224,6 +2224,27 @@ target-version = "py37"
|
|||
|
||||
---
|
||||
|
||||
#### [`task-tags`](#task-tags)
|
||||
|
||||
A list of task tags to recognize (e.g., "TODO", "FIXME", "XXX").
|
||||
|
||||
Comments starting with these tags will be ignored by commented-out code
|
||||
detection (`ERA`), and skipped by line-length checks (`E501`) if
|
||||
`ignore-overlong-task-comments` is set to `true`.
|
||||
|
||||
**Default value**: `["TODO", "FIXME", "XXX"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
task-tags = ["HACK"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`unfixable`](#unfixable)
|
||||
|
||||
A list of check code prefixes to consider un-autofix-able.
|
||||
|
@ -2970,6 +2991,27 @@ staticmethod-decorators = ["staticmethod", "stcmthd"]
|
|||
|
||||
---
|
||||
|
||||
### `pycodestyle`
|
||||
|
||||
#### [`ignore-overlong-task-comments`](#ignore-overlong-task-comments)
|
||||
|
||||
Whether or not line-length checks (`E501`) should be triggered for
|
||||
comments starting with `task-tags` (by default: ["TODO", "FIXME",
|
||||
and "XXX"]).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.pycodestyle]
|
||||
ignore-overlong-task-comments = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `pydocstyle`
|
||||
|
||||
#### [`convention`](#convention)
|
||||
|
|
|
@ -390,6 +390,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -402,6 +403,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
|
@ -450,6 +452,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -462,6 +465,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
|
@ -510,6 +514,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -522,6 +527,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
|
@ -570,6 +576,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -582,6 +589,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
|
@ -630,6 +638,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -647,6 +656,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
|
@ -699,6 +709,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -711,6 +722,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: Some(pydocstyle::settings::Options {
|
||||
convention: Some(Convention::Numpy),
|
||||
}),
|
||||
|
@ -762,6 +774,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -779,6 +792,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
});
|
||||
|
|
6
resources/test/fixtures/pycodestyle/E501_1.py
vendored
Normal file
6
resources/test/fixtures/pycodestyle/E501_1.py
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
# TODO: comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# TODO comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# TODO comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# FIXME: comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# FIXME comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
||||
# FIXME comments starting with one of the configured task-tags sometimes are longer than line-length so that you can easily find them with `git grep`
|
|
@ -288,6 +288,17 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"pycodestyle": {
|
||||
"description": "Options for the `pycodestyle` plugin.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Pycodestyle"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pydocstyle": {
|
||||
"description": "Options for the `pydocstyle` plugin.",
|
||||
"anyOf": [
|
||||
|
@ -366,6 +377,16 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"task-tags": {
|
||||
"description": "A list of task tags to recognize (e.g., \"TODO\", \"FIXME\", \"XXX\").\n\nComments starting with these tags will be ignored by commented-out code detection (`ERA`), and skipped by line-length checks (`E501`) if `ignore-overlong-task-comments` is set to `true`.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"unfixable": {
|
||||
"description": "A list of check code prefixes to consider un-autofix-able.",
|
||||
"type": [
|
||||
|
@ -1443,6 +1464,19 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Pycodestyle": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ignore-overlong-task-comments": {
|
||||
"description": "Whether or not line-length checks (`E501`) should be triggered for comments starting with `task-tags` (by default: [\"TODO\", \"FIXME\", and \"XXX\"]).",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Pydocstyle": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -57,7 +57,7 @@ pub fn check_lines(
|
|||
}
|
||||
|
||||
if enforce_line_too_long {
|
||||
if let Some(check) = line_too_long(index, line, settings.line_length) {
|
||||
if let Some(check) = line_too_long(index, line, settings) {
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ pub fn commented_out_code(
|
|||
let line = locator.slice_source_code_range(&Range::new(location, end_location));
|
||||
|
||||
// Verify that the comment is on its own line, and that it contains code.
|
||||
if is_standalone_comment(&line) && comment_contains_code(&line) {
|
||||
if is_standalone_comment(&line) && comment_contains_code(&line, &settings.task_tags[..]) {
|
||||
let mut check = Check::new(CheckKind::CommentedOutCode, Range::new(start, end));
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.fixable.contains(&CheckCode::ERA001)
|
||||
|
|
|
@ -4,7 +4,7 @@ use regex::Regex;
|
|||
|
||||
static ALLOWLIST_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
r"^(?i)(?:pylint|pyright|noqa|nosec|type:\s*ignore|fmt:\s*(on|off)|isort:\s*(on|off|skip|skip_file|split|dont-add-imports(:\s*\[.*?])?)|TODO|FIXME|XXX)"
|
||||
r"^(?i)(?:pylint|pyright|noqa|nosec|type:\s*ignore|fmt:\s*(on|off)|isort:\s*(on|off|skip|skip_file|split|dont-add-imports(:\s*\[.*?])?))"
|
||||
).unwrap()
|
||||
});
|
||||
static BRACKET_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[()\[\]{}\s]+$").unwrap());
|
||||
|
@ -30,7 +30,7 @@ static PARTIAL_DICTIONARY_REGEX: Lazy<Regex> =
|
|||
static PRINT_RETURN_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(print|return)\b\s*").unwrap());
|
||||
|
||||
/// Returns `true` if a comment contains Python code.
|
||||
pub fn comment_contains_code(line: &str) -> bool {
|
||||
pub fn comment_contains_code(line: &str, task_tags: &[String]) -> bool {
|
||||
let line = if let Some(line) = line.trim().strip_prefix('#') {
|
||||
line.trim()
|
||||
} else {
|
||||
|
@ -47,6 +47,12 @@ pub fn comment_contains_code(line: &str) -> bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
if let Some(first) = line.split(&[' ', ':']).next() {
|
||||
if task_tags.iter().any(|tag| tag == first) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if CODING_COMMENT_REGEX.is_match(line) {
|
||||
return false;
|
||||
}
|
||||
|
@ -97,142 +103,166 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn comment_contains_code_basic() {
|
||||
assert!(comment_contains_code("# x = 1"));
|
||||
assert!(comment_contains_code("#from foo import eradicate"));
|
||||
assert!(comment_contains_code("#import eradicate"));
|
||||
assert!(comment_contains_code(r#"#"key": value,"#));
|
||||
assert!(comment_contains_code(r#"#"key": "value","#));
|
||||
assert!(comment_contains_code(r#"#"key": 1 + 1,"#));
|
||||
assert!(comment_contains_code("#'key': 1 + 1,"));
|
||||
assert!(comment_contains_code(r#"#"key": {"#));
|
||||
assert!(comment_contains_code("#}"));
|
||||
assert!(comment_contains_code("#} )]"));
|
||||
assert!(comment_contains_code("# x = 1", &[]));
|
||||
assert!(comment_contains_code("#from foo import eradicate", &[]));
|
||||
assert!(comment_contains_code("#import eradicate", &[]));
|
||||
assert!(comment_contains_code(r#"#"key": value,"#, &[]));
|
||||
assert!(comment_contains_code(r#"#"key": "value","#, &[]));
|
||||
assert!(comment_contains_code(r#"#"key": 1 + 1,"#, &[]));
|
||||
assert!(comment_contains_code("#'key': 1 + 1,", &[]));
|
||||
assert!(comment_contains_code(r#"#"key": {"#, &[]));
|
||||
assert!(comment_contains_code("#}", &[]));
|
||||
assert!(comment_contains_code("#} )]", &[]));
|
||||
|
||||
assert!(!comment_contains_code("#"));
|
||||
assert!(!comment_contains_code("# This is a (real) comment."));
|
||||
assert!(!comment_contains_code("# 123"));
|
||||
assert!(!comment_contains_code("# 123.1"));
|
||||
assert!(!comment_contains_code("# 1, 2, 3"));
|
||||
assert!(!comment_contains_code("x = 1 # x = 1"));
|
||||
assert!(!comment_contains_code("#", &[]));
|
||||
assert!(!comment_contains_code("# This is a (real) comment.", &[]));
|
||||
assert!(!comment_contains_code("# 123", &[]));
|
||||
assert!(!comment_contains_code("# 123.1", &[]));
|
||||
assert!(!comment_contains_code("# 1, 2, 3", &[]));
|
||||
assert!(!comment_contains_code("x = 1 # x = 1", &[]));
|
||||
assert!(!comment_contains_code(
|
||||
"# pylint: disable=redefined-outer-name"
|
||||
"# pylint: disable=redefined-outer-name",
|
||||
&[]
|
||||
),);
|
||||
assert!(!comment_contains_code(
|
||||
"# Issue #999: This is not code",
|
||||
&[]
|
||||
));
|
||||
assert!(!comment_contains_code("# Issue #999: This is not code"));
|
||||
|
||||
// TODO(charlie): This should be `true` under aggressive mode.
|
||||
assert!(!comment_contains_code("#},"));
|
||||
assert!(!comment_contains_code("#},", &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_print() {
|
||||
assert!(comment_contains_code("#print"));
|
||||
assert!(comment_contains_code("#print(1)"));
|
||||
assert!(comment_contains_code("#print 1"));
|
||||
assert!(comment_contains_code("#print", &[]));
|
||||
assert!(comment_contains_code("#print(1)", &[]));
|
||||
assert!(comment_contains_code("#print 1", &[]));
|
||||
|
||||
assert!(!comment_contains_code("#to print"));
|
||||
assert!(!comment_contains_code("#to print", &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_return() {
|
||||
assert!(comment_contains_code("#return x"));
|
||||
assert!(comment_contains_code("#return x", &[]));
|
||||
|
||||
assert!(!comment_contains_code("#to print"));
|
||||
assert!(!comment_contains_code("#to print", &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_multiline() {
|
||||
assert!(comment_contains_code("#else:"));
|
||||
assert!(comment_contains_code("# else : "));
|
||||
assert!(comment_contains_code(r#"# "foo %d" % \\"#));
|
||||
assert!(comment_contains_code("#elif True:"));
|
||||
assert!(comment_contains_code("#x = foo("));
|
||||
assert!(comment_contains_code("#except Exception:"));
|
||||
assert!(comment_contains_code("#else:", &[]));
|
||||
assert!(comment_contains_code("# else : ", &[]));
|
||||
assert!(comment_contains_code(r#"# "foo %d" % \\"#, &[]));
|
||||
assert!(comment_contains_code("#elif True:", &[]));
|
||||
assert!(comment_contains_code("#x = foo(", &[]));
|
||||
assert!(comment_contains_code("#except Exception:", &[]));
|
||||
|
||||
assert!(!comment_contains_code("# this is = to that :("));
|
||||
assert!(!comment_contains_code("#else"));
|
||||
assert!(!comment_contains_code("#or else:"));
|
||||
assert!(!comment_contains_code("#else True:"));
|
||||
assert!(!comment_contains_code("# this is = to that :(", &[]));
|
||||
assert!(!comment_contains_code("#else", &[]));
|
||||
assert!(!comment_contains_code("#or else:", &[]));
|
||||
assert!(!comment_contains_code("#else True:", &[]));
|
||||
|
||||
// Unpacking assignments
|
||||
assert!(comment_contains_code(
|
||||
"# user_content_type, _ = TimelineEvent.objects.using(db_alias).get_or_create("
|
||||
"# user_content_type, _ = TimelineEvent.objects.using(db_alias).get_or_create(",
|
||||
&[]
|
||||
),);
|
||||
assert!(comment_contains_code(
|
||||
"# (user_content_type, _) = TimelineEvent.objects.using(db_alias).get_or_create(",
|
||||
&[]
|
||||
),);
|
||||
assert!(comment_contains_code(
|
||||
"# ( user_content_type , _ )= TimelineEvent.objects.using(db_alias).get_or_create(",
|
||||
&[]
|
||||
));
|
||||
assert!(comment_contains_code(
|
||||
"# (user_content_type, _) = TimelineEvent.objects.using(db_alias).get_or_create("
|
||||
"# app_label=\"core\", model=\"user\"",
|
||||
&[]
|
||||
));
|
||||
assert!(comment_contains_code(
|
||||
"# ( user_content_type , _ )= TimelineEvent.objects.using(db_alias).get_or_create("
|
||||
));
|
||||
assert!(comment_contains_code(
|
||||
"# app_label=\"core\", model=\"user\""
|
||||
));
|
||||
assert!(comment_contains_code("# )"));
|
||||
assert!(comment_contains_code("# )", &[]));
|
||||
|
||||
// TODO(charlie): This should be `true` under aggressive mode.
|
||||
assert!(!comment_contains_code("#def foo():"));
|
||||
assert!(!comment_contains_code("#def foo():", &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_sentences() {
|
||||
assert!(!comment_contains_code("#code is good"));
|
||||
assert!(!comment_contains_code("#code is good", &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_encoding() {
|
||||
assert!(comment_contains_code("# codings=utf-8"));
|
||||
assert!(comment_contains_code("# codings=utf-8", &[]));
|
||||
|
||||
assert!(!comment_contains_code("# coding=utf-8"));
|
||||
assert!(!comment_contains_code("#coding= utf-8"));
|
||||
assert!(!comment_contains_code("# coding: utf-8"));
|
||||
assert!(!comment_contains_code("# encoding: utf8"));
|
||||
assert!(!comment_contains_code("# coding=utf-8", &[]));
|
||||
assert!(!comment_contains_code("#coding= utf-8", &[]));
|
||||
assert!(!comment_contains_code("# coding: utf-8", &[]));
|
||||
assert!(!comment_contains_code("# encoding: utf8", &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_default_allowlist() {
|
||||
assert!(!comment_contains_code("# pylint: disable=A0123"));
|
||||
assert!(!comment_contains_code("# pylint:disable=A0123"));
|
||||
assert!(!comment_contains_code("# pylint: disable = A0123"));
|
||||
assert!(!comment_contains_code("# pylint:disable = A0123"));
|
||||
assert!(!comment_contains_code("# pyright: reportErrorName=true"));
|
||||
assert!(!comment_contains_code("# noqa"));
|
||||
assert!(!comment_contains_code("# NOQA"));
|
||||
assert!(!comment_contains_code("# noqa: A123"));
|
||||
assert!(!comment_contains_code("# noqa:A123"));
|
||||
assert!(!comment_contains_code("# nosec"));
|
||||
assert!(!comment_contains_code("# fmt: on"));
|
||||
assert!(!comment_contains_code("# fmt: off"));
|
||||
assert!(!comment_contains_code("# fmt:on"));
|
||||
assert!(!comment_contains_code("# fmt:off"));
|
||||
assert!(!comment_contains_code("# isort: on"));
|
||||
assert!(!comment_contains_code("# isort:on"));
|
||||
assert!(!comment_contains_code("# isort: off"));
|
||||
assert!(!comment_contains_code("# isort:off"));
|
||||
assert!(!comment_contains_code("# isort: skip"));
|
||||
assert!(!comment_contains_code("# isort:skip"));
|
||||
assert!(!comment_contains_code("# isort: skip_file"));
|
||||
assert!(!comment_contains_code("# isort:skip_file"));
|
||||
assert!(!comment_contains_code("# isort: split"));
|
||||
assert!(!comment_contains_code("# isort:split"));
|
||||
assert!(!comment_contains_code("# isort: dont-add-imports"));
|
||||
assert!(!comment_contains_code("# isort:dont-add-imports"));
|
||||
assert!(!comment_contains_code("# pylint: disable=A0123", &[]));
|
||||
assert!(!comment_contains_code("# pylint:disable=A0123", &[]));
|
||||
assert!(!comment_contains_code("# pylint: disable = A0123", &[]));
|
||||
assert!(!comment_contains_code("# pylint:disable = A0123", &[]));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort: dont-add-imports: [\"import os\"]"
|
||||
"# pyright: reportErrorName=true",
|
||||
&[]
|
||||
));
|
||||
assert!(!comment_contains_code("# noqa", &[]));
|
||||
assert!(!comment_contains_code("# NOQA", &[]));
|
||||
assert!(!comment_contains_code("# noqa: A123", &[]));
|
||||
assert!(!comment_contains_code("# noqa:A123", &[]));
|
||||
assert!(!comment_contains_code("# nosec", &[]));
|
||||
assert!(!comment_contains_code("# fmt: on", &[]));
|
||||
assert!(!comment_contains_code("# fmt: off", &[]));
|
||||
assert!(!comment_contains_code("# fmt:on", &[]));
|
||||
assert!(!comment_contains_code("# fmt:off", &[]));
|
||||
assert!(!comment_contains_code("# isort: on", &[]));
|
||||
assert!(!comment_contains_code("# isort:on", &[]));
|
||||
assert!(!comment_contains_code("# isort: off", &[]));
|
||||
assert!(!comment_contains_code("# isort:off", &[]));
|
||||
assert!(!comment_contains_code("# isort: skip", &[]));
|
||||
assert!(!comment_contains_code("# isort:skip", &[]));
|
||||
assert!(!comment_contains_code("# isort: skip_file", &[]));
|
||||
assert!(!comment_contains_code("# isort:skip_file", &[]));
|
||||
assert!(!comment_contains_code("# isort: split", &[]));
|
||||
assert!(!comment_contains_code("# isort:split", &[]));
|
||||
assert!(!comment_contains_code("# isort: dont-add-imports", &[]));
|
||||
assert!(!comment_contains_code("# isort:dont-add-imports", &[]));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort: dont-add-imports: [\"import os\"]",
|
||||
&[]
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort:dont-add-imports: [\"import os\"]"
|
||||
"# isort:dont-add-imports: [\"import os\"]",
|
||||
&[]
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort: dont-add-imports:[\"import os\"]"
|
||||
"# isort: dont-add-imports:[\"import os\"]",
|
||||
&[]
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort:dont-add-imports:[\"import os\"]"
|
||||
"# isort:dont-add-imports:[\"import os\"]",
|
||||
&[]
|
||||
));
|
||||
assert!(!comment_contains_code("# type: ignore", &[]));
|
||||
assert!(!comment_contains_code("# type:ignore", &[]));
|
||||
assert!(!comment_contains_code("# type: ignore[import]", &[]));
|
||||
assert!(!comment_contains_code("# type:ignore[import]", &[]));
|
||||
assert!(!comment_contains_code(
|
||||
"# TODO: Do that",
|
||||
&["TODO".to_string()]
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# FIXME: Fix that",
|
||||
&["FIXME".to_string()]
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# XXX: What ever",
|
||||
&["XXX".to_string()]
|
||||
));
|
||||
assert!(!comment_contains_code("# type: ignore"));
|
||||
assert!(!comment_contains_code("# type:ignore"));
|
||||
assert!(!comment_contains_code("# type: ignore[import]"));
|
||||
assert!(!comment_contains_code("# type:ignore[import]"));
|
||||
assert!(!comment_contains_code("# TODO: Do that"));
|
||||
assert!(!comment_contains_code("# FIXME: Fix that"));
|
||||
assert!(!comment_contains_code("# XXX: What ever"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::source_code_style::SourceCodeStyleDetector;
|
|||
use crate::{
|
||||
directives, flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_tidy_imports, flake8_unused_arguments, isort,
|
||||
mccabe, pep8_naming, pydocstyle, pyupgrade,
|
||||
mccabe, pep8_naming, pycodestyle, pydocstyle, pyupgrade,
|
||||
};
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
@ -112,6 +112,7 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
|||
show_source: None,
|
||||
src: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
// Use default options for all plugins.
|
||||
flake8_annotations: Some(flake8_annotations::settings::Settings::default().into()),
|
||||
|
@ -129,6 +130,7 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
|||
isort: Some(isort::settings::Settings::default().into()),
|
||||
mccabe: Some(mccabe::settings::Settings::default().into()),
|
||||
pep8_naming: Some(pep8_naming::settings::Settings::default().into()),
|
||||
pycodestyle: Some(pycodestyle::settings::Settings::default().into()),
|
||||
pydocstyle: Some(pydocstyle::settings::Settings::default().into()),
|
||||
pyupgrade: Some(pyupgrade::settings::Settings::default().into()),
|
||||
})?)
|
||||
|
|
|
@ -8,34 +8,44 @@ use crate::ast::helpers::except_range;
|
|||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::registry::{Check, CheckKind};
|
||||
use crate::settings::Settings;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap());
|
||||
|
||||
/// E501
|
||||
pub fn line_too_long(lineno: usize, line: &str, max_line_length: usize) -> Option<Check> {
|
||||
pub fn line_too_long(lineno: usize, line: &str, settings: &Settings) -> Option<Check> {
|
||||
let line_length = line.chars().count();
|
||||
|
||||
if line_length <= max_line_length {
|
||||
if line_length <= settings.line_length {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut chunks = line.split_whitespace();
|
||||
let (Some(first), Some(_)) = (chunks.next(), chunks.next()) else {
|
||||
let (Some(first), Some(second)) = (chunks.next(), chunks.next()) else {
|
||||
// Single word / no printable chars - no way to make the line shorter
|
||||
return None;
|
||||
};
|
||||
|
||||
if first == "#" {
|
||||
if settings.pycodestyle.ignore_overlong_task_comments {
|
||||
let second = second.trim_end_matches(':');
|
||||
if settings.task_tags.iter().any(|tag| tag == second) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not enforce the line length for commented lines that end with a URL
|
||||
// or contain only a single word.
|
||||
if first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)) {
|
||||
if chunks.last().map_or(true, |c| URL_REGEX.is_match(c)) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(Check::new(
|
||||
CheckKind::LineTooLong(line_length, max_line_length),
|
||||
CheckKind::LineTooLong(line_length, settings.line_length),
|
||||
Range::new(
|
||||
Location::new(lineno + 1, max_line_length),
|
||||
Location::new(lineno + 1, settings.line_length),
|
||||
Location::new(lineno + 1, line_length),
|
||||
),
|
||||
))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
pub mod checks;
|
||||
pub mod plugins;
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -9,6 +10,7 @@ mod tests {
|
|||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use super::settings::Settings;
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::CheckCode;
|
||||
use crate::settings;
|
||||
|
@ -56,4 +58,21 @@ mod tests {
|
|||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(false)]
|
||||
#[test_case(true)]
|
||||
fn task_tags(ignore_overlong_task_comments: bool) -> Result<()> {
|
||||
let snapshot = format!("task_tags_{ignore_overlong_task_comments}");
|
||||
let checks = test_path(
|
||||
Path::new("./resources/test/fixtures/pycodestyle/E501_1.py"),
|
||||
&settings::Settings {
|
||||
pycodestyle: Settings {
|
||||
ignore_overlong_task_comments,
|
||||
},
|
||||
..settings::Settings::for_rule(CheckCode::E501)
|
||||
},
|
||||
)?;
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
46
src/pycodestyle/settings.rs
Normal file
46
src/pycodestyle/settings.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
//! Settings for the `pycodestyle` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, JsonSchema,
|
||||
)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case", rename = "Pycodestyle")]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
ignore-overlong-task-comments = true
|
||||
"#
|
||||
)]
|
||||
/// Whether or not line-length checks (`E501`) should be triggered for
|
||||
/// comments starting with `task-tags` (by default: ["TODO", "FIXME",
|
||||
/// and "XXX"]).
|
||||
pub ignore_overlong_task_comments: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
pub struct Settings {
|
||||
pub ignore_overlong_task_comments: bool,
|
||||
}
|
||||
|
||||
impl From<Options> for Settings {
|
||||
fn from(options: Options) -> Self {
|
||||
Self {
|
||||
ignore_overlong_task_comments: options
|
||||
.ignore_overlong_task_comments
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Settings> for Options {
|
||||
fn from(settings: Settings) -> Self {
|
||||
Self {
|
||||
ignore_overlong_task_comments: Some(settings.ignore_overlong_task_comments),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
source: src/pycodestyle/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
LineTooLong:
|
||||
- 149
|
||||
- 88
|
||||
location:
|
||||
row: 1
|
||||
column: 88
|
||||
end_location:
|
||||
row: 1
|
||||
column: 149
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
LineTooLong:
|
||||
- 148
|
||||
- 88
|
||||
location:
|
||||
row: 2
|
||||
column: 88
|
||||
end_location:
|
||||
row: 2
|
||||
column: 148
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
LineTooLong:
|
||||
- 155
|
||||
- 88
|
||||
location:
|
||||
row: 3
|
||||
column: 88
|
||||
end_location:
|
||||
row: 3
|
||||
column: 155
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
LineTooLong:
|
||||
- 150
|
||||
- 88
|
||||
location:
|
||||
row: 4
|
||||
column: 88
|
||||
end_location:
|
||||
row: 4
|
||||
column: 150
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
LineTooLong:
|
||||
- 149
|
||||
- 88
|
||||
location:
|
||||
row: 5
|
||||
column: 88
|
||||
end_location:
|
||||
row: 5
|
||||
column: 149
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
LineTooLong:
|
||||
- 156
|
||||
- 88
|
||||
location:
|
||||
row: 6
|
||||
column: 88
|
||||
end_location:
|
||||
row: 6
|
||||
column: 156
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pycodestyle/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -22,7 +22,7 @@ use crate::settings::types::{
|
|||
use crate::{
|
||||
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_tidy_imports, flake8_unused_arguments, fs, isort,
|
||||
mccabe, pep8_naming, pydocstyle, pyupgrade,
|
||||
mccabe, pep8_naming, pycodestyle, pydocstyle, pyupgrade,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -52,6 +52,7 @@ pub struct Configuration {
|
|||
pub src: Option<Vec<PathBuf>>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
pub unfixable: Option<Vec<CheckCodePrefix>>,
|
||||
pub task_tags: Option<Vec<String>>,
|
||||
pub update_check: Option<bool>,
|
||||
// Plugins
|
||||
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||
|
@ -65,6 +66,7 @@ pub struct Configuration {
|
|||
pub isort: Option<isort::settings::Options>,
|
||||
pub mccabe: Option<mccabe::settings::Options>,
|
||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||
pub pycodestyle: Option<pycodestyle::settings::Options>,
|
||||
pub pydocstyle: Option<pydocstyle::settings::Options>,
|
||||
pub pyupgrade: Option<pyupgrade::settings::Options>,
|
||||
}
|
||||
|
@ -149,6 +151,7 @@ impl Configuration {
|
|||
.transpose()?,
|
||||
target_version: options.target_version,
|
||||
unfixable: options.unfixable,
|
||||
task_tags: options.task_tags,
|
||||
update_check: options.update_check,
|
||||
// Plugins
|
||||
flake8_annotations: options.flake8_annotations,
|
||||
|
@ -162,6 +165,7 @@ impl Configuration {
|
|||
isort: options.isort,
|
||||
mccabe: options.mccabe,
|
||||
pep8_naming: options.pep8_naming,
|
||||
pycodestyle: options.pycodestyle,
|
||||
pydocstyle: options.pydocstyle,
|
||||
pyupgrade: options.pyupgrade,
|
||||
})
|
||||
|
@ -209,6 +213,7 @@ impl Configuration {
|
|||
src: self.src.or(config.src),
|
||||
target_version: self.target_version.or(config.target_version),
|
||||
unfixable: self.unfixable.or(config.unfixable),
|
||||
task_tags: self.task_tags.or(config.task_tags),
|
||||
update_check: self.update_check.or(config.update_check),
|
||||
// Plugins
|
||||
flake8_annotations: self.flake8_annotations.or(config.flake8_annotations),
|
||||
|
@ -226,6 +231,7 @@ impl Configuration {
|
|||
isort: self.isort.or(config.isort),
|
||||
mccabe: self.mccabe.or(config.mccabe),
|
||||
pep8_naming: self.pep8_naming.or(config.pep8_naming),
|
||||
pycodestyle: self.pycodestyle.or(config.pycodestyle),
|
||||
pydocstyle: self.pydocstyle.or(config.pydocstyle),
|
||||
pyupgrade: self.pyupgrade.or(config.pyupgrade),
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ use crate::settings::types::{
|
|||
use crate::{
|
||||
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_tidy_imports, flake8_unused_arguments, isort,
|
||||
mccabe, one_time_warning, pep8_naming, pydocstyle, pyupgrade,
|
||||
mccabe, one_time_warning, pep8_naming, pycodestyle, pydocstyle, pyupgrade,
|
||||
};
|
||||
|
||||
pub mod configuration;
|
||||
|
@ -60,6 +60,7 @@ pub struct Settings {
|
|||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
pub task_tags: Vec<String>,
|
||||
pub update_check: bool,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
|
@ -73,6 +74,7 @@ pub struct Settings {
|
|||
pub isort: isort::settings::Settings,
|
||||
pub mccabe: mccabe::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
pub pycodestyle: pycodestyle::settings::Settings,
|
||||
pub pydocstyle: pydocstyle::settings::Settings,
|
||||
pub pyupgrade: pyupgrade::settings::Settings,
|
||||
}
|
||||
|
@ -173,6 +175,9 @@ impl Settings {
|
|||
.src
|
||||
.unwrap_or_else(|| vec![project_root.to_path_buf()]),
|
||||
target_version: config.target_version.unwrap_or_default(),
|
||||
task_tags: config.task_tags.unwrap_or_else(|| {
|
||||
vec!["TODO".to_string(), "FIXME".to_string(), "XXX".to_string()]
|
||||
}),
|
||||
update_check: config.update_check.unwrap_or(true),
|
||||
// Plugins
|
||||
flake8_annotations: config
|
||||
|
@ -219,6 +224,10 @@ impl Settings {
|
|||
.pep8_naming
|
||||
.map(std::convert::Into::into)
|
||||
.unwrap_or_default(),
|
||||
pycodestyle: config
|
||||
.pycodestyle
|
||||
.map(std::convert::Into::into)
|
||||
.unwrap_or_default(),
|
||||
pydocstyle: config
|
||||
.pydocstyle
|
||||
.map(std::convert::Into::into)
|
||||
|
@ -252,6 +261,7 @@ impl Settings {
|
|||
show_source: false,
|
||||
src: vec![path_dedot::CWD.clone()],
|
||||
target_version: PythonVersion::Py310,
|
||||
task_tags: vec!["TODO".to_string(), "FIXME".to_string()],
|
||||
update_check: false,
|
||||
flake8_annotations: flake8_annotations::settings::Settings::default(),
|
||||
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
|
||||
|
@ -264,6 +274,7 @@ impl Settings {
|
|||
isort: isort::settings::Settings::default(),
|
||||
mccabe: mccabe::settings::Settings::default(),
|
||||
pep8_naming: pep8_naming::settings::Settings::default(),
|
||||
pycodestyle: pycodestyle::settings::Settings::default(),
|
||||
pydocstyle: pydocstyle::settings::Settings::default(),
|
||||
pyupgrade: pyupgrade::settings::Settings::default(),
|
||||
}
|
||||
|
@ -291,6 +302,7 @@ impl Settings {
|
|||
show_source: false,
|
||||
src: vec![path_dedot::CWD.clone()],
|
||||
target_version: PythonVersion::Py310,
|
||||
task_tags: vec!["TODO".to_string()],
|
||||
update_check: false,
|
||||
flake8_annotations: flake8_annotations::settings::Settings::default(),
|
||||
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
|
||||
|
@ -303,6 +315,7 @@ impl Settings {
|
|||
isort: isort::settings::Settings::default(),
|
||||
mccabe: mccabe::settings::Settings::default(),
|
||||
pep8_naming: pep8_naming::settings::Settings::default(),
|
||||
pycodestyle: pycodestyle::settings::Settings::default(),
|
||||
pydocstyle: pydocstyle::settings::Settings::default(),
|
||||
pyupgrade: pyupgrade::settings::Settings::default(),
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::settings::types::{PythonVersion, SerializationFormat, Version};
|
|||
use crate::{
|
||||
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_tidy_imports, flake8_unused_arguments, isort,
|
||||
mccabe, pep8_naming, pydocstyle, pyupgrade,
|
||||
mccabe, pep8_naming, pycodestyle, pydocstyle, pyupgrade,
|
||||
};
|
||||
|
||||
#[derive(
|
||||
|
@ -339,6 +339,17 @@ pub struct Options {
|
|||
)]
|
||||
/// A list of check code prefixes to consider un-autofix-able.
|
||||
pub unfixable: Option<Vec<CheckCodePrefix>>,
|
||||
#[option(
|
||||
default = r#"["TODO", "FIXME", "XXX"]"#,
|
||||
value_type = "Vec<String>",
|
||||
example = r#"task-tags = ["HACK"]"#
|
||||
)]
|
||||
/// A list of task tags to recognize (e.g., "TODO", "FIXME", "XXX").
|
||||
///
|
||||
/// Comments starting with these tags will be ignored by commented-out code
|
||||
/// detection (`ERA`), and skipped by line-length checks (`E501`) if
|
||||
/// `ignore-overlong-task-comments` is set to `true`.
|
||||
pub task_tags: Option<Vec<String>>,
|
||||
#[option(
|
||||
default = "true",
|
||||
value_type = "bool",
|
||||
|
@ -381,6 +392,9 @@ pub struct Options {
|
|||
/// Options for the `pep8-naming` plugin.
|
||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||
#[option_group]
|
||||
/// Options for the `pycodestyle` plugin.
|
||||
pub pycodestyle: Option<pycodestyle::settings::Options>,
|
||||
#[option_group]
|
||||
/// Options for the `pydocstyle` plugin.
|
||||
pub pydocstyle: Option<pydocstyle::settings::Options>,
|
||||
#[option_group]
|
||||
|
|
|
@ -188,6 +188,7 @@ mod tests {
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -200,6 +201,7 @@ mod tests {
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
})
|
||||
|
@ -241,6 +243,7 @@ line-length = 79
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
cache_dir: None,
|
||||
flake8_annotations: None,
|
||||
|
@ -254,6 +257,7 @@ line-length = 79
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
})
|
||||
|
@ -296,6 +300,7 @@ exclude = ["foo.py"]
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_errmsg: None,
|
||||
|
@ -308,6 +313,7 @@ exclude = ["foo.py"]
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
})
|
||||
|
@ -350,6 +356,7 @@ select = ["E501"]
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -362,6 +369,7 @@ select = ["E501"]
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
})
|
||||
|
@ -405,6 +413,7 @@ ignore = ["E501"]
|
|||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
|
@ -417,6 +426,7 @@ ignore = ["E501"]
|
|||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
})
|
||||
|
@ -490,6 +500,7 @@ other-attribute = 1
|
|||
format: None,
|
||||
force_exclude: None,
|
||||
unfixable: None,
|
||||
task_tags: None,
|
||||
update_check: None,
|
||||
cache_dir: None,
|
||||
per_file_ignores: Some(FxHashMap::from_iter([(
|
||||
|
@ -592,6 +603,7 @@ other-attribute = 1
|
|||
]),
|
||||
staticmethod_decorators: Some(vec!["staticmethod".to_string()]),
|
||||
}),
|
||||
pycodestyle: None,
|
||||
pydocstyle: None,
|
||||
pyupgrade: None,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue