Implement pyupgrade check for io.open alias (#1399)

This commit is contained in:
Reiner Gerecke 2022-12-27 13:47:40 +01:00 committed by GitHub
parent 320a48977b
commit e0b39fa63e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 62 additions and 3 deletions

View File

@ -80,7 +80,7 @@ Then, run `cargo test`. Your test will fail, but you'll be prompted to follow-up
rest of your changes. rest of your changes.
Finally, to update the documentation, run `cargo dev generate-rules-table` from the repo root. To Finally, to update the documentation, run `cargo dev generate-rules-table` from the repo root. To
update the generated prefix map, run `cargo dev generate-check-code-prefix`. Both of these commands update the generated prefix map, run `cargo +nightly dev generate-check-code-prefix`. Both of these commands
should be run whenever a new check is added to the codebase. should be run whenever a new check is added to the codebase.
### Example: Adding a new configuration option ### Example: Adding a new configuration option

View File

@ -659,6 +659,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| UP017 | DatetimeTimezoneUTC | Use `datetime.UTC` alias | 🛠 | | UP017 | DatetimeTimezoneUTC | Use `datetime.UTC` alias | 🛠 |
| UP018 | NativeLiterals | Unnecessary call to `str` and `bytes` | 🛠 | | UP018 | NativeLiterals | Unnecessary call to `str` and `bytes` | 🛠 |
| UP019 | TypingTextStrAlias | `typing.Text` is deprecated, use `str` | 🛠 | | UP019 | TypingTextStrAlias | `typing.Text` is deprecated, use `str` | 🛠 |
| UP020 | OpenAlias | Use builtin `open` instead | 🛠 |
### pep8-naming (N) ### pep8-naming (N)
@ -1262,7 +1263,7 @@ natively, including:
- [`pep8-naming`](https://pypi.org/project/pep8-naming/) - [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`pydocstyle`](https://pypi.org/project/pydocstyle/) - [`pydocstyle`](https://pypi.org/project/pydocstyle/)
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10) - [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (19/33) - [`pyupgrade`](https://pypi.org/project/pyupgrade/) (20/33)
- [`yesqa`](https://github.com/asottile/yesqa) - [`yesqa`](https://github.com/asottile/yesqa)
Note that, in some cases, Ruff uses different error code prefixes than would be found in the Note that, in some cases, Ruff uses different error code prefixes than would be found in the
@ -1319,7 +1320,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
Ruff can also replace [`isort`](https://pypi.org/project/isort/), Ruff can also replace [`isort`](https://pypi.org/project/isort/),
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/), [`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
[`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10), and a subset of the rules [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (3/10), and a subset of the rules
implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (19/33). implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (20/33).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue. If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.

View File

@ -0,0 +1,9 @@
from io import open
with open("f.txt") as f:
print(f.read())
import io
with io.open("f.txt", mode="r", buffering=-1, **kwargs) as f:
print(f.read())

View File

@ -865,6 +865,8 @@
"UP017", "UP017",
"UP018", "UP018",
"UP019", "UP019",
"UP02",
"UP020",
"W", "W",
"W2", "W2",
"W29", "W29",

View File

@ -1926,6 +1926,10 @@ where
pyupgrade::plugins::redundant_open_modes(self, expr); pyupgrade::plugins::redundant_open_modes(self, expr);
} }
if self.settings.enabled.contains(&CheckCode::UP020) {
pyupgrade::plugins::open_alias(self, expr, func);
}
// flake8-boolean-trap // flake8-boolean-trap
if self.settings.enabled.contains(&CheckCode::FBT003) { if self.settings.enabled.contains(&CheckCode::FBT003) {
flake8_boolean_trap::plugins::check_boolean_positional_value_in_function_call( flake8_boolean_trap::plugins::check_boolean_positional_value_in_function_call(

View File

@ -227,6 +227,7 @@ pub enum CheckCode {
UP017, UP017,
UP018, UP018,
UP019, UP019,
UP020,
// pydocstyle // pydocstyle
D100, D100,
D101, D101,
@ -839,6 +840,7 @@ pub enum CheckKind {
RemoveSixCompat, RemoveSixCompat,
DatetimeTimezoneUTC, DatetimeTimezoneUTC,
NativeLiterals, NativeLiterals,
OpenAlias,
// pydocstyle // pydocstyle
BlankLineAfterLastSection(String), BlankLineAfterLastSection(String),
BlankLineAfterSection(String), BlankLineAfterSection(String),
@ -1215,6 +1217,7 @@ impl CheckCode {
CheckCode::UP017 => CheckKind::DatetimeTimezoneUTC, CheckCode::UP017 => CheckKind::DatetimeTimezoneUTC,
CheckCode::UP018 => CheckKind::NativeLiterals, CheckCode::UP018 => CheckKind::NativeLiterals,
CheckCode::UP019 => CheckKind::TypingTextStrAlias, CheckCode::UP019 => CheckKind::TypingTextStrAlias,
CheckCode::UP020 => CheckKind::OpenAlias,
// pydocstyle // pydocstyle
CheckCode::D100 => CheckKind::PublicModule, CheckCode::D100 => CheckKind::PublicModule,
CheckCode::D101 => CheckKind::PublicClass, CheckCode::D101 => CheckKind::PublicClass,
@ -1635,6 +1638,7 @@ impl CheckCode {
CheckCode::UP017 => CheckCategory::Pyupgrade, CheckCode::UP017 => CheckCategory::Pyupgrade,
CheckCode::UP018 => CheckCategory::Pyupgrade, CheckCode::UP018 => CheckCategory::Pyupgrade,
CheckCode::UP019 => CheckCategory::Pyupgrade, CheckCode::UP019 => CheckCategory::Pyupgrade,
CheckCode::UP020 => CheckCategory::Pyupgrade,
CheckCode::W292 => CheckCategory::Pycodestyle, CheckCode::W292 => CheckCategory::Pycodestyle,
CheckCode::W605 => CheckCategory::Pycodestyle, CheckCode::W605 => CheckCategory::Pycodestyle,
CheckCode::YTT101 => CheckCategory::Flake82020, CheckCode::YTT101 => CheckCategory::Flake82020,
@ -1848,6 +1852,7 @@ impl CheckKind {
CheckKind::DatetimeTimezoneUTC => &CheckCode::UP017, CheckKind::DatetimeTimezoneUTC => &CheckCode::UP017,
CheckKind::NativeLiterals => &CheckCode::UP018, CheckKind::NativeLiterals => &CheckCode::UP018,
CheckKind::TypingTextStrAlias => &CheckCode::UP019, CheckKind::TypingTextStrAlias => &CheckCode::UP019,
CheckKind::OpenAlias => &CheckCode::UP020,
// pydocstyle // pydocstyle
CheckKind::BlankLineAfterLastSection(..) => &CheckCode::D413, CheckKind::BlankLineAfterLastSection(..) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(..) => &CheckCode::D410, CheckKind::BlankLineAfterSection(..) => &CheckCode::D410,
@ -2573,6 +2578,7 @@ impl CheckKind {
CheckKind::RemoveSixCompat => "Unnecessary `six` compatibility usage".to_string(), CheckKind::RemoveSixCompat => "Unnecessary `six` compatibility usage".to_string(),
CheckKind::DatetimeTimezoneUTC => "Use `datetime.UTC` alias".to_string(), CheckKind::DatetimeTimezoneUTC => "Use `datetime.UTC` alias".to_string(),
CheckKind::NativeLiterals => "Unnecessary call to `str` and `bytes`".to_string(), CheckKind::NativeLiterals => "Unnecessary call to `str` and `bytes`".to_string(),
CheckKind::OpenAlias => "Use builtin `open` instead".to_string(),
CheckKind::ConvertTypedDictFunctionalToClass(name) => { CheckKind::ConvertTypedDictFunctionalToClass(name) => {
format!("Convert `{name}` from `TypedDict` functional to class syntax") format!("Convert `{name}` from `TypedDict` functional to class syntax")
} }
@ -3015,6 +3021,7 @@ impl CheckKind {
| CheckKind::MisplacedComparisonConstant(..) | CheckKind::MisplacedComparisonConstant(..)
| CheckKind::MissingReturnTypeSpecialMethod(..) | CheckKind::MissingReturnTypeSpecialMethod(..)
| CheckKind::NativeLiterals | CheckKind::NativeLiterals
| CheckKind::OpenAlias
| CheckKind::NewLineAfterLastParagraph | CheckKind::NewLineAfterLastParagraph
| CheckKind::NewLineAfterSectionName(..) | CheckKind::NewLineAfterSectionName(..)
| CheckKind::NoBlankLineAfterFunction(..) | CheckKind::NoBlankLineAfterFunction(..)

View File

@ -527,6 +527,8 @@ pub enum CheckCodePrefix {
UP017, UP017,
UP018, UP018,
UP019, UP019,
UP02,
UP020,
W, W,
W2, W2,
W29, W29,
@ -2104,6 +2106,7 @@ impl CheckCodePrefix {
CheckCode::UP017, CheckCode::UP017,
CheckCode::UP018, CheckCode::UP018,
CheckCode::UP019, CheckCode::UP019,
CheckCode::UP020,
] ]
} }
CheckCodePrefix::U0 => { CheckCodePrefix::U0 => {
@ -2132,6 +2135,7 @@ impl CheckCodePrefix {
CheckCode::UP017, CheckCode::UP017,
CheckCode::UP018, CheckCode::UP018,
CheckCode::UP019, CheckCode::UP019,
CheckCode::UP020,
] ]
} }
CheckCodePrefix::U00 => { CheckCodePrefix::U00 => {
@ -2344,6 +2348,7 @@ impl CheckCodePrefix {
CheckCode::UP017, CheckCode::UP017,
CheckCode::UP018, CheckCode::UP018,
CheckCode::UP019, CheckCode::UP019,
CheckCode::UP020,
], ],
CheckCodePrefix::UP0 => vec![ CheckCodePrefix::UP0 => vec![
CheckCode::UP001, CheckCode::UP001,
@ -2364,6 +2369,7 @@ impl CheckCodePrefix {
CheckCode::UP017, CheckCode::UP017,
CheckCode::UP018, CheckCode::UP018,
CheckCode::UP019, CheckCode::UP019,
CheckCode::UP020,
], ],
CheckCodePrefix::UP00 => vec![ CheckCodePrefix::UP00 => vec![
CheckCode::UP001, CheckCode::UP001,
@ -2405,6 +2411,8 @@ impl CheckCodePrefix {
CheckCodePrefix::UP017 => vec![CheckCode::UP017], CheckCodePrefix::UP017 => vec![CheckCode::UP017],
CheckCodePrefix::UP018 => vec![CheckCode::UP018], CheckCodePrefix::UP018 => vec![CheckCode::UP018],
CheckCodePrefix::UP019 => vec![CheckCode::UP019], CheckCodePrefix::UP019 => vec![CheckCode::UP019],
CheckCodePrefix::UP02 => vec![CheckCode::UP020],
CheckCodePrefix::UP020 => vec![CheckCode::UP020],
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605], CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292], CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292], CheckCodePrefix::W29 => vec![CheckCode::W292],
@ -2963,6 +2971,8 @@ impl CheckCodePrefix {
CheckCodePrefix::UP017 => SuffixLength::Three, CheckCodePrefix::UP017 => SuffixLength::Three,
CheckCodePrefix::UP018 => SuffixLength::Three, CheckCodePrefix::UP018 => SuffixLength::Three,
CheckCodePrefix::UP019 => SuffixLength::Three, CheckCodePrefix::UP019 => SuffixLength::Three,
CheckCodePrefix::UP02 => SuffixLength::Two,
CheckCodePrefix::UP020 => SuffixLength::Three,
CheckCodePrefix::W => SuffixLength::Zero, CheckCodePrefix::W => SuffixLength::Zero,
CheckCodePrefix::W2 => SuffixLength::One, CheckCodePrefix::W2 => SuffixLength::One,
CheckCodePrefix::W29 => SuffixLength::Two, CheckCodePrefix::W29 => SuffixLength::Two,

View File

@ -3,6 +3,7 @@ pub use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to
pub use datetime_utc_alias::datetime_utc_alias; pub use datetime_utc_alias::datetime_utc_alias;
pub use deprecated_unittest_alias::deprecated_unittest_alias; pub use deprecated_unittest_alias::deprecated_unittest_alias;
pub use native_literals::native_literals; pub use native_literals::native_literals;
pub use open_alias::open_alias;
pub use redundant_open_modes::redundant_open_modes; pub use redundant_open_modes::redundant_open_modes;
pub use remove_six_compat::remove_six_compat; pub use remove_six_compat::remove_six_compat;
pub use super_call_with_parameters::super_call_with_parameters; pub use super_call_with_parameters::super_call_with_parameters;
@ -21,6 +22,7 @@ mod convert_typed_dict_functional_to_class;
mod datetime_utc_alias; mod datetime_utc_alias;
mod deprecated_unittest_alias; mod deprecated_unittest_alias;
mod native_literals; mod native_literals;
mod open_alias;
mod redundant_open_modes; mod redundant_open_modes;
mod remove_six_compat; mod remove_six_compat;
mod super_call_with_parameters; mod super_call_with_parameters;

View File

@ -0,0 +1,24 @@
use rustpython_ast::Expr;
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind};
/// UP020
pub fn open_alias(checker: &mut Checker, expr: &Expr, func: &Expr) {
let call_path = dealias_call_path(collect_call_paths(expr), &checker.import_aliases);
if match_call_path(&call_path, "io", "open", &checker.from_imports) {
let mut check = Check::new(CheckKind::OpenAlias, Range::from_located(expr));
if checker.patch(&CheckCode::UP020) {
check.amend(Fix::replacement(
"open".to_string(),
func.location,
func.end_location.unwrap(),
));
}
checker.add_check(check);
}
}