[pyupgrade]: Remove outdated sys.version_info blocks (#2099)

This commit is contained in:
Colin Delahunty 2023-02-02 07:49:24 -05:00 committed by GitHub
parent 1c2fc38853
commit b032f50775
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 2113 additions and 2 deletions

View file

@ -77,7 +77,7 @@ wasm-bindgen = { version = "0.2.83" }
is_executable = "1.0.1"
[dev-dependencies]
insta = { version = "1.19.1", features = ["yaml", "redactions"] }
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
test-case = { version = "2.2.2" }
wasm-bindgen-test = { version = "0.3.33" }

View file

@ -850,6 +850,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/) on PyPI.
| UP033 | functools-cache | Use `@functools.cache` instead of `@functools.lru_cache(maxsize=None)` | 🛠 |
| UP034 | extraneous-parentheses | Avoid extraneous parentheses | 🛠 |
| UP035 | import-replacements | Import from `{module}` instead: {names} | 🛠 |
| UP036 | outdated-version-block | Version block is outdated for minimum Python version | 🛠 |
### flake8-2020 (YTT)

4
foo.py Normal file
View file

@ -0,0 +1,4 @@
import sys
expected_error = \
[]

View file

@ -0,0 +1,180 @@
import sys
if sys.version_info < (3,0):
print("py2")
else:
print("py3")
if sys.version_info < (3,0):
if True:
print("py2!")
else:
print("???")
else:
print("py3")
if sys.version_info < (3,0): print("PY2!")
else: print("PY3!")
if True:
if sys.version_info < (3,0):
print("PY2")
else:
print("PY3")
if sys.version_info < (3,0): print(1 if True else 3)
else:
print("py3")
if sys.version_info < (3,0):
def f():
print("py2")
else:
def f():
print("py3")
print("This the next")
if sys.version_info > (3,0):
print("py3")
else:
print("py2")
x = 1
if sys.version_info > (3,0):
print("py3")
else:
print("py2")
# ohai
x = 1
if sys.version_info > (3,0): print("py3")
else: print("py2")
if sys.version_info > (3,):
print("py3")
else:
print("py2")
if True:
if sys.version_info > (3,):
print("py3")
else:
print("py2")
if sys.version_info < (3,):
print("py2")
else:
print("py3")
def f():
if sys.version_info < (3,0):
try:
yield
finally:
pass
else:
yield
class C:
def g():
pass
if sys.version_info < (3,0):
def f(py2):
pass
else:
def f(py3):
pass
def h():
pass
if True:
if sys.version_info < (3,0):
2
else:
3
# comment
if sys.version_info < (3,0):
def f():
print("py2")
def g():
print("py2")
else:
def f():
print("py3")
def g():
print("py3")
if True:
if sys.version_info > (3,):
print(3)
# comment
print(2+3)
if True:
if sys.version_info > (3,): print(3)
if True:
if sys.version_info > (3,):
print(3)
if True:
if sys.version_info <= (3, 0):
expected_error = []
else:
expected_error = [
"<stdin>:1:5: Generator expression must be parenthesized",
"max(1 for i in range(10), key=lambda x: x+1)",
" ^",
]
if sys.version_info <= (3, 0):
expected_error = []
else:
expected_error = [
"<stdin>:1:5: Generator expression must be parenthesized",
"max(1 for i in range(10), key=lambda x: x+1)",
" ^",
]
if sys.version_info > (3,0):
"""this
is valid"""
"""the indentation on
this line is significant"""
"this is" \
"allowed too"
("so is"
"this for some reason")
if sys.version_info > (3, 0): expected_error = \
[]
if sys.version_info > (3, 0): expected_error = []
if sys.version_info > (3, 0): \
expected_error = []
if True:
if sys.version_info > (3, 0): expected_error = \
[]
if True:
if sys.version_info > (3, 0): expected_error = []
if True:
if sys.version_info > (3, 0): \
expected_error = []

View file

@ -0,0 +1,76 @@
import sys
if sys.version_info == 2:
2
else:
3
if sys.version_info < (3,):
2
else:
3
if sys.version_info < (3,0):
2
else:
3
if sys.version_info == 3:
3
else:
2
if sys.version_info > (3,):
3
else:
2
if sys.version_info >= (3,):
3
else:
2
from sys import version_info
if version_info > (3,):
3
else:
2
if True:
print(1)
elif sys.version_info < (3,0):
print(2)
else:
print(3)
if True:
print(1)
elif sys.version_info > (3,):
print(3)
else:
print(2)
if True:
print(1)
elif sys.version_info > (3,):
print(3)
def f():
if True:
print(1)
elif sys.version_info > (3,):
print(3)
if True:
print(1)
elif sys.version_info < (3,0):
print(2)
else:
print(3)
def f():
if True:
print(1)
elif sys.version_info > (3,):
print(3)

View file

@ -0,0 +1,62 @@
import sys
from sys import version_info
if sys.version_info > (3, 5):
3+6
else:
3-5
if version_info > (3, 5):
3+6
else:
3-5
if sys.version_info >= (3,6):
3+6
else:
3-5
if version_info >= (3,6):
3+6
else:
3-5
if sys.version_info < (3,6):
3-5
else:
3+6
if sys.version_info <= (3,5):
3-5
else:
3+6
if sys.version_info <= (3, 5):
3-5
else:
3+6
if sys.version_info >= (3, 5):
pass
if sys.version_info < (3,0):
pass
if True:
if sys.version_info < (3,0):
pass
if sys.version_info < (3,0):
pass
elif False:
pass
if sys.version_info > (3,):
pass
elif False:
pass
if sys.version_info[0] > "2":
3
else:
2

View file

@ -0,0 +1,24 @@
import sys
if sys.version_info < (3,0):
print("py2")
for item in range(10):
print(f"PY2-{item}")
else :
print("py3")
for item in range(10):
print(f"PY3-{item}")
if False:
if sys.version_info < (3,0):
print("py2")
for item in range(10):
print(f"PY2-{item}")
else :
print("py3")
for item in range(10):
print(f"PY3-{item}")
if sys.version_info < (3,0): print("PY2!")
else : print("PY3!")

View file

@ -0,0 +1,45 @@
import sys
if True:
if sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
if True:
if foo:
pass
elif sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
if True:
if foo:
pass
elif sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
elif foo:
cmd = [sys.executable, "-m", "test", "-j0"]
if foo:
pass
elif sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
if sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
if foo:
pass
elif sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
else:
cmd = [sys.executable, "-m", "test", "-j0"]
if sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
else:
cmd = [sys.executable, "-m", "test", "-j0"]
if sys.version_info < (3, 3):
cmd = [sys.executable, "-m", "test.regrtest"]
elif foo:
cmd = [sys.executable, "-m", "test", "-j0"]

View file

@ -1905,6 +1905,7 @@
"UP033",
"UP034",
"UP035",
"UP036",
"W",
"W2",
"W29",

View file

@ -22,3 +22,5 @@ sys.exit(1)
# To be removed once GitHub catches up.
setup(name="ruff", install_requires=[])
if True: a = 1; \
b = 2

View file

@ -1500,6 +1500,9 @@ where
self.current_stmt_parent().map(Into::into),
);
}
if self.settings.rules.enabled(&Rule::OutdatedVersionBlock) {
pyupgrade::rules::outdated_version_block(self, stmt, test, body, orelse);
}
}
StmtKind::Assert { test, msg } => {
if self.settings.rules.enabled(&Rule::AssertTuple) {

View file

@ -256,6 +256,7 @@ ruff_macros::define_rule_mapping!(
UP033 => violations::FunctoolsCache,
UP034 => violations::ExtraneousParentheses,
UP035 => rules::pyupgrade::rules::ImportReplacements,
UP036 => rules::pyupgrade::rules::OutdatedVersionBlock,
// pydocstyle
D100 => violations::PublicModule,
D101 => violations::PublicClass,

View file

@ -1,5 +1,7 @@
use anyhow::{bail, Result};
use libcst_native::{
Codegen, CodegenState, Expression, ParenthesizableWhitespace, SmallStatement, Statement,
Codegen, CodegenState, CompoundStatement, Expression, ParenthesizableWhitespace,
SmallStatement, Statement, Suite,
};
use rustpython_ast::{Expr, Keyword, Location};
use rustpython_parser::lexer;
@ -7,9 +9,47 @@ use rustpython_parser::lexer::Tok;
use crate::ast::types::Range;
use crate::autofix::helpers::remove_argument;
use crate::cst::matchers::match_module;
use crate::fix::Fix;
use crate::source_code::{Locator, Stylist};
/// Safely adjust the indentation of the indented block at [`Range`].
pub fn adjust_indentation(
range: Range,
indentation: &str,
locator: &Locator,
stylist: &Stylist,
) -> Result<String> {
let contents = locator.slice_source_code_range(&range);
let module_text = format!("def f():{}{contents}", stylist.line_ending().as_str());
let mut tree = match_module(&module_text)?;
let [Statement::Compound(CompoundStatement::FunctionDef(embedding))] = &mut *tree.body else {
bail!("Expected statement to be embedded in a function definition")
};
let Suite::IndentedBlock(indented_block) = &mut embedding.body else {
bail!("Expected indented block")
};
indented_block.indent = Some(indentation);
let mut state = CodegenState {
default_newline: stylist.line_ending(),
default_indent: stylist.indentation(),
..Default::default()
};
indented_block.codegen(&mut state);
let module_text = state.to_string();
let module_text = module_text
.strip_prefix(stylist.line_ending().as_str())
.unwrap()
.to_string();
Ok(module_text)
}
/// Generate a fix to remove a base from a `ClassDef` statement.
pub fn remove_class_def_base(
locator: &Locator,

View file

@ -58,6 +58,11 @@ mod tests {
#[test_case(Rule::FunctoolsCache, Path::new("UP033.py"); "UP033")]
#[test_case(Rule::ExtraneousParentheses, Path::new("UP034.py"); "UP034")]
#[test_case(Rule::ImportReplacements, Path::new("UP035.py"); "UP035")]
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_0.py"); "UP036_0")]
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_1.py"); "UP036_1")]
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_2.py"); "UP036_2")]
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_3.py"); "UP036_3")]
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_4.py"); "UP036_4")]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
let diagnostics = test_path(

View file

@ -11,6 +11,7 @@ pub(crate) use lru_cache_without_parameters::lru_cache_without_parameters;
pub(crate) use native_literals::native_literals;
pub(crate) use open_alias::open_alias;
pub(crate) use os_error_alias::os_error_alias;
pub(crate) use outdated_version_block::{outdated_version_block, OutdatedVersionBlock};
pub(crate) use printf_string_formatting::printf_string_formatting;
pub(crate) use redundant_open_modes::redundant_open_modes;
pub(crate) use replace_stdout_stderr::replace_stdout_stderr;
@ -46,6 +47,7 @@ mod lru_cache_without_parameters;
mod native_literals;
mod open_alias;
mod os_error_alias;
mod outdated_version_block;
mod printf_string_formatting;
mod redundant_open_modes;
mod replace_stdout_stderr;

View file

@ -0,0 +1,442 @@
use std::cmp::Ordering;
use log::error;
use num_bigint::{BigInt, Sign};
use rustpython_ast::Location;
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Located, Stmt};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use ruff_macros::derive_message_formats;
use crate::ast::types::{Range, RefEquality};
use crate::ast::whitespace::indentation;
use crate::autofix::helpers::delete_stmt;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::rules::pyupgrade::fixes::adjust_indentation;
use crate::settings::types::PythonVersion;
use crate::source_code::Locator;
use crate::violation::AlwaysAutofixableViolation;
define_violation!(
pub struct OutdatedVersionBlock;
);
impl AlwaysAutofixableViolation for OutdatedVersionBlock {
#[derive_message_formats]
fn message(&self) -> String {
format!("Version block is outdated for minimum Python version")
}
fn autofix_title(&self) -> String {
"Remove outdated version block".to_string()
}
}
#[derive(Debug)]
struct BlockMetadata {
/// The first non-whitespace token in the block.
starter: Tok,
/// The location of the first `elif` token, if any.
elif: Option<Location>,
/// The location of the `else` token, if any.
else_: Option<Location>,
}
impl BlockMetadata {
fn new(starter: Tok, elif: Option<Location>, else_: Option<Location>) -> Self {
Self {
starter,
elif,
else_,
}
}
}
fn metadata<T>(locator: &Locator, located: &Located<T>) -> Option<BlockMetadata> {
indentation(locator, located)?;
// Start the selection at the start-of-line. This ensures consistent indentation in the
// token stream, in the event that the entire block is indented.
let text = locator.slice_source_code_range(&Range::new(
Location::new(located.location.row(), 0),
located.end_location.unwrap(),
));
let mut starter: Option<Tok> = None;
let mut elif = None;
let mut else_ = None;
for (start, tok, _) in
lexer::make_tokenizer_located(text, Location::new(located.location.row(), 0))
.flatten()
.filter(|(_, tok, _)| {
!matches!(
tok,
Tok::Indent
| Tok::Dedent
| Tok::NonLogicalNewline
| Tok::Newline
| Tok::Comment(..)
)
})
{
if starter.is_none() {
starter = Some(tok.clone());
} else {
if matches!(tok, Tok::Elif) && elif.is_none() {
elif = Some(start);
}
if matches!(tok, Tok::Else) && else_.is_none() {
else_ = Some(start);
}
}
if starter.is_some() && elif.is_some() && else_.is_some() {
break;
}
}
Some(BlockMetadata::new(starter.unwrap(), elif, else_))
}
/// Converts a `BigInt` to a `u32`, if the number is negative, it will return 0
fn bigint_to_u32(number: &BigInt) -> u32 {
let the_number = number.to_u32_digits();
match the_number.0 {
Sign::Minus | Sign::NoSign => 0,
Sign::Plus => *the_number.1.first().unwrap(),
}
}
/// Gets the version from the tuple
fn extract_version(elts: &[Expr]) -> Vec<u32> {
let mut version: Vec<u32> = vec![];
for elt in elts {
if let ExprKind::Constant {
value: Constant::Int(item),
..
} = &elt.node
{
let number = bigint_to_u32(item);
version.push(number);
} else {
return version;
}
}
version
}
/// Returns true if the `if_version` is less than the `PythonVersion`
fn compare_version(if_version: &[u32], py_version: PythonVersion, or_equal: bool) -> bool {
let mut if_version_iter = if_version.iter();
if let Some(if_major) = if_version_iter.next() {
let (py_major, py_minor) = py_version.as_tuple();
match if_major.cmp(&py_major) {
Ordering::Less => true,
Ordering::Equal => {
if let Some(if_minor) = if_version_iter.next() {
// Check the if_minor number (the minor version).
if or_equal {
*if_minor <= py_minor
} else {
*if_minor < py_minor
}
} else {
// Assume Python 3.0.
true
}
}
Ordering::Greater => false,
}
} else {
false
}
}
/// Convert a [`StmtKind::If`], retaining the `else`.
fn fix_py2_block(
checker: &mut Checker,
stmt: &Stmt,
body: &[Stmt],
orelse: &[Stmt],
block: &BlockMetadata,
) -> Option<Fix> {
if orelse.is_empty() {
// Delete the entire statement. If this is an `elif`, know it's the only child of its
// parent, so avoid passing in the parent at all. Otherwise, `delete_stmt` will erroneously
// include a `pass`.
let deleted: Vec<&Stmt> = checker
.deletions
.iter()
.map(std::convert::Into::into)
.collect();
let defined_by = checker.current_stmt();
let defined_in = checker.current_stmt_parent();
return match delete_stmt(
defined_by.into(),
if block.starter == Tok::If {
defined_in.map(std::convert::Into::into)
} else {
None
},
&deleted,
checker.locator,
checker.indexer,
checker.stylist,
) {
Ok(fix) => {
checker.deletions.insert(RefEquality(defined_by.into()));
Some(fix)
}
Err(err) => {
error!("Failed to remove block: {}", err);
None
}
};
}
// If we only have an `if` and an `else`, dedent the `else` block.
if block.starter == Tok::If && block.elif.is_none() {
let start = orelse.first().unwrap();
let end = orelse.last().unwrap();
if indentation(checker.locator, start).is_none() {
// Inline `else` block (e.g., `else: x = 1`).
Some(Fix::replacement(
checker
.locator
.slice_source_code_range(&Range::new(start.location, end.end_location.unwrap()))
.to_string(),
stmt.location,
stmt.end_location.unwrap(),
))
} else {
indentation(checker.locator, stmt)
.and_then(|indentation| {
adjust_indentation(
Range::new(
Location::new(start.location.row(), 0),
end.end_location.unwrap(),
),
indentation,
checker.locator,
checker.stylist,
)
.ok()
})
.map(|contents| {
Fix::replacement(
contents,
Location::new(stmt.location.row(), 0),
stmt.end_location.unwrap(),
)
})
}
} else {
let mut end_location = orelse.last().unwrap().location;
if block.starter == Tok::If && block.elif.is_some() {
// Turn the `elif` into an `if`.
end_location = block.elif.unwrap();
end_location.go_right();
end_location.go_right();
} else if block.starter == Tok::Elif {
if let Some(elif) = block.elif {
end_location = elif;
} else if let Some(else_) = block.else_ {
end_location = else_;
} else {
end_location = body.last().unwrap().end_location.unwrap();
}
}
Some(Fix::deletion(stmt.location, end_location))
}
}
/// Convert a [`StmtKind::If`], removing the `else` block.
fn fix_py3_block(
checker: &mut Checker,
stmt: &Stmt,
test: &Expr,
body: &[Stmt],
block: &BlockMetadata,
) -> Option<Fix> {
match block.starter {
Tok::If => {
// If the first statement is an if, use the body of this statement, and ignore the rest.
let start = body.first().unwrap();
let end = body.last().unwrap();
if indentation(checker.locator, start).is_none() {
// Inline `if` block (e.g., `if ...: x = 1`).
Some(Fix::replacement(
checker
.locator
.slice_source_code_range(&Range::new(
start.location,
end.end_location.unwrap(),
))
.to_string(),
stmt.location,
stmt.end_location.unwrap(),
))
} else {
indentation(checker.locator, stmt)
.and_then(|indentation| {
adjust_indentation(
Range::new(
Location::new(start.location.row(), 0),
end.end_location.unwrap(),
),
indentation,
checker.locator,
checker.stylist,
)
.ok()
})
.map(|contents| {
Fix::replacement(
contents,
Location::new(stmt.location.row(), 0),
stmt.end_location.unwrap(),
)
})
}
}
Tok::Elif => {
// Replace the `elif` with an `else, preserve the body of the elif, and remove the rest.
let end = body.last().unwrap();
let text = checker.locator.slice_source_code_range(&Range::new(
test.end_location.unwrap(),
end.end_location.unwrap(),
));
Some(Fix::replacement(
format!("else{text}"),
stmt.location,
stmt.end_location.unwrap(),
))
}
_ => None,
}
}
/// UP036
pub fn outdated_version_block(
checker: &mut Checker,
stmt: &Stmt,
test: &Expr,
body: &[Stmt],
orelse: &[Stmt],
) {
let ExprKind::Compare {
left,
ops,
comparators,
} = &test.node else {
return;
};
if !checker.resolve_call_path(left).map_or(false, |call_path| {
call_path.as_slice() == ["sys", "version_info"]
}) {
return;
}
if ops.len() == 1 && comparators.len() == 1 {
let comparison = &comparators[0].node;
let op = &ops[0];
match comparison {
ExprKind::Tuple { elts, .. } => {
let version = extract_version(elts);
let target = checker.settings.target_version;
if op == &Cmpop::Lt || op == &Cmpop::LtE {
if compare_version(&version, target, op == &Cmpop::LtE) {
let mut diagnostic =
Diagnostic::new(OutdatedVersionBlock, Range::from_located(stmt));
if checker.patch(diagnostic.kind.rule()) {
if let Some(block) = metadata(checker.locator, stmt) {
if let Some(fix) =
fix_py2_block(checker, stmt, body, orelse, &block)
{
diagnostic.amend(fix);
}
}
}
checker.diagnostics.push(diagnostic);
}
} else if op == &Cmpop::Gt || op == &Cmpop::GtE {
if compare_version(&version, target, op == &Cmpop::GtE) {
let mut diagnostic =
Diagnostic::new(OutdatedVersionBlock, Range::from_located(stmt));
if checker.patch(diagnostic.kind.rule()) {
if let Some(block) = metadata(checker.locator, stmt) {
if let Some(fix) = fix_py3_block(checker, stmt, test, body, &block)
{
diagnostic.amend(fix);
}
}
}
checker.diagnostics.push(diagnostic);
}
}
}
ExprKind::Constant {
value: Constant::Int(number),
..
} => {
let version_number = bigint_to_u32(number);
if version_number == 2 && op == &Cmpop::Eq {
let mut diagnostic =
Diagnostic::new(OutdatedVersionBlock, Range::from_located(stmt));
if checker.patch(diagnostic.kind.rule()) {
if let Some(block) = metadata(checker.locator, stmt) {
if let Some(fix) = fix_py2_block(checker, stmt, body, orelse, &block) {
diagnostic.amend(fix);
}
}
}
checker.diagnostics.push(diagnostic);
} else if version_number == 3 && op == &Cmpop::Eq {
let mut diagnostic =
Diagnostic::new(OutdatedVersionBlock, Range::from_located(stmt));
if checker.patch(diagnostic.kind.rule()) {
if let Some(block) = metadata(checker.locator, stmt) {
if let Some(fix) = fix_py3_block(checker, stmt, test, body, &block) {
diagnostic.amend(fix);
}
}
}
checker.diagnostics.push(diagnostic);
}
}
_ => (),
}
}
}
#[cfg(test)]
mod tests {
use test_case::test_case;
use super::*;
#[test_case(PythonVersion::Py37, &[2], true, true; "compare-2.0")]
#[test_case(PythonVersion::Py37, &[2, 0], true, true; "compare-2.0-whole")]
#[test_case(PythonVersion::Py37, &[3], true, true; "compare-3.0")]
#[test_case(PythonVersion::Py37, &[3, 0], true, true; "compare-3.0-whole")]
#[test_case(PythonVersion::Py37, &[3, 1], true, true; "compare-3.1")]
#[test_case(PythonVersion::Py37, &[3, 5], true, true; "compare-3.5")]
#[test_case(PythonVersion::Py37, &[3, 7], true, true; "compare-3.7")]
#[test_case(PythonVersion::Py37, &[3, 7], false, false; "compare-3.7-not-equal")]
#[test_case(PythonVersion::Py37, &[3, 8], false , false; "compare-3.8")]
#[test_case(PythonVersion::Py310, &[3,9], true, true; "compare-3.9")]
#[test_case(PythonVersion::Py310, &[3, 11], true, false; "compare-3.11")]
fn test_compare_version(
version: PythonVersion,
version_vec: &[u32],
or_equal: bool,
expected: bool,
) {
assert_eq!(compare_version(version_vec, version, or_equal), expected);
}
}

View file

@ -0,0 +1,535 @@
---
source: src/rules/pyupgrade/mod.rs
expression: diagnostics
---
- kind:
OutdatedVersionBlock: ~
location:
row: 3
column: 0
end_location:
row: 6
column: 16
fix:
content:
- "print(\"py3\")"
location:
row: 3
column: 0
end_location:
row: 6
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 8
column: 0
end_location:
row: 14
column: 16
fix:
content:
- "print(\"py3\")"
location:
row: 8
column: 0
end_location:
row: 14
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 16
column: 0
end_location:
row: 17
column: 19
fix:
content:
- "print(\"PY3!\")"
location:
row: 16
column: 0
end_location:
row: 17
column: 19
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 20
column: 4
end_location:
row: 23
column: 20
fix:
content:
- " print(\"PY3\")"
location:
row: 20
column: 0
end_location:
row: 23
column: 20
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 25
column: 0
end_location:
row: 27
column: 16
fix:
content:
- "print(\"py3\")"
location:
row: 25
column: 0
end_location:
row: 27
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 29
column: 0
end_location:
row: 35
column: 30
fix:
content:
- "def f():"
- " print(\"py3\")"
- " print(\"This the next\")"
location:
row: 29
column: 0
end_location:
row: 35
column: 30
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 37
column: 0
end_location:
row: 40
column: 16
fix:
content:
- "print(\"py3\")"
location:
row: 37
column: 0
end_location:
row: 40
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 45
column: 0
end_location:
row: 48
column: 16
fix:
content:
- "print(\"py3\")"
location:
row: 45
column: 0
end_location:
row: 48
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 53
column: 0
end_location:
row: 54
column: 18
fix:
content:
- "print(\"py3\")"
location:
row: 53
column: 0
end_location:
row: 54
column: 18
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 56
column: 0
end_location:
row: 59
column: 16
fix:
content:
- "print(\"py3\")"
location:
row: 56
column: 0
end_location:
row: 59
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 62
column: 4
end_location:
row: 65
column: 20
fix:
content:
- " print(\"py3\")"
location:
row: 62
column: 0
end_location:
row: 65
column: 20
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 67
column: 0
end_location:
row: 70
column: 16
fix:
content:
- "print(\"py3\")"
location:
row: 67
column: 0
end_location:
row: 70
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 73
column: 4
end_location:
row: 79
column: 13
fix:
content:
- " yield"
location:
row: 73
column: 0
end_location:
row: 79
column: 13
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 86
column: 4
end_location:
row: 91
column: 16
fix:
content:
- " def f(py3):"
- " pass"
location:
row: 86
column: 0
end_location:
row: 91
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 97
column: 4
end_location:
row: 100
column: 9
fix:
content:
- " 3"
location:
row: 97
column: 0
end_location:
row: 100
column: 9
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 104
column: 0
end_location:
row: 113
column: 20
fix:
content:
- "def f():"
- " print(\"py3\")"
- "def g():"
- " print(\"py3\")"
location:
row: 104
column: 0
end_location:
row: 113
column: 20
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 116
column: 4
end_location:
row: 117
column: 16
fix:
content:
- " print(3)"
location:
row: 116
column: 0
end_location:
row: 117
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 122
column: 4
end_location:
row: 122
column: 40
fix:
content:
- print(3)
location:
row: 122
column: 4
end_location:
row: 122
column: 40
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 125
column: 4
end_location:
row: 126
column: 16
fix:
content:
- " print(3)"
location:
row: 125
column: 0
end_location:
row: 126
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 130
column: 4
end_location:
row: 137
column: 9
fix:
content:
- " expected_error = ["
- "\"<stdin>:1:5: Generator expression must be parenthesized\","
- "\"max(1 for i in range(10), key=lambda x: x+1)\","
- "\" ^\","
- " ]"
location:
row: 130
column: 0
end_location:
row: 137
column: 9
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 140
column: 0
end_location:
row: 147
column: 5
fix:
content:
- "expected_error = ["
- "\"<stdin>:1:5: Generator expression must be parenthesized\","
- "\"max(1 for i in range(10), key=lambda x: x+1)\","
- "\" ^\","
- "]"
location:
row: 140
column: 0
end_location:
row: 147
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 150
column: 0
end_location:
row: 161
column: 28
fix:
content:
- "\"\"\"this"
- "is valid\"\"\""
- ""
- "\"\"\"the indentation on"
- " this line is significant\"\"\""
- ""
- "\"this is\" \\"
- " \"allowed too\""
- ""
- "(\"so is\""
- " \"this for some reason\")"
location:
row: 150
column: 0
end_location:
row: 161
column: 28
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 163
column: 0
end_location:
row: 164
column: 6
fix:
content:
- "expected_error = \\"
- " []"
location:
row: 163
column: 0
end_location:
row: 164
column: 6
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 166
column: 0
end_location:
row: 166
column: 49
fix:
content:
- "expected_error = []"
location:
row: 166
column: 0
end_location:
row: 166
column: 49
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 168
column: 0
end_location:
row: 169
column: 23
fix:
content:
- "expected_error = []"
location:
row: 168
column: 0
end_location:
row: 169
column: 23
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 172
column: 4
end_location:
row: 173
column: 6
fix:
content:
- "expected_error = \\"
- " []"
location:
row: 172
column: 4
end_location:
row: 173
column: 6
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 176
column: 4
end_location:
row: 176
column: 53
fix:
content:
- "expected_error = []"
location:
row: 176
column: 4
end_location:
row: 176
column: 53
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 179
column: 4
end_location:
row: 180
column: 23
fix:
content:
- " expected_error = []"
location:
row: 179
column: 0
end_location:
row: 180
column: 23
parent: ~

View file

@ -0,0 +1,243 @@
---
source: src/rules/pyupgrade/mod.rs
expression: diagnostics
---
- kind:
OutdatedVersionBlock: ~
location:
row: 3
column: 0
end_location:
row: 6
column: 5
fix:
content:
- "3"
location:
row: 3
column: 0
end_location:
row: 6
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 8
column: 0
end_location:
row: 11
column: 5
fix:
content:
- "3"
location:
row: 8
column: 0
end_location:
row: 11
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 13
column: 0
end_location:
row: 16
column: 5
fix:
content:
- "3"
location:
row: 13
column: 0
end_location:
row: 16
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 18
column: 0
end_location:
row: 21
column: 5
fix:
content:
- "3"
location:
row: 18
column: 0
end_location:
row: 21
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 23
column: 0
end_location:
row: 26
column: 5
fix:
content:
- "3"
location:
row: 23
column: 0
end_location:
row: 26
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 28
column: 0
end_location:
row: 31
column: 5
fix:
content:
- "3"
location:
row: 28
column: 0
end_location:
row: 31
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 35
column: 0
end_location:
row: 38
column: 5
fix:
content:
- "3"
location:
row: 35
column: 0
end_location:
row: 38
column: 5
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 42
column: 0
end_location:
row: 45
column: 12
fix:
content:
- ""
location:
row: 42
column: 0
end_location:
row: 44
column: 0
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 49
column: 0
end_location:
row: 52
column: 12
fix:
content:
- "else:"
- " print(3)"
location:
row: 49
column: 0
end_location:
row: 52
column: 12
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 56
column: 0
end_location:
row: 57
column: 12
fix:
content:
- "else:"
- " print(3)"
location:
row: 56
column: 0
end_location:
row: 57
column: 12
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 62
column: 4
end_location:
row: 63
column: 16
fix:
content:
- "else:"
- " print(3)"
location:
row: 62
column: 4
end_location:
row: 63
column: 16
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 67
column: 0
end_location:
row: 70
column: 12
fix:
content:
- ""
location:
row: 67
column: 0
end_location:
row: 69
column: 0
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 75
column: 4
end_location:
row: 76
column: 16
fix:
content:
- "else:"
- " print(3)"
location:
row: 75
column: 4
end_location:
row: 76
column: 16
parent: ~

View file

@ -0,0 +1,221 @@
---
source: src/rules/pyupgrade/mod.rs
expression: diagnostics
---
- kind:
OutdatedVersionBlock: ~
location:
row: 4
column: 0
end_location:
row: 7
column: 7
fix:
content:
- 3+6
location:
row: 4
column: 0
end_location:
row: 7
column: 7
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 9
column: 0
end_location:
row: 12
column: 7
fix:
content:
- 3+6
location:
row: 9
column: 0
end_location:
row: 12
column: 7
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 14
column: 0
end_location:
row: 17
column: 7
fix:
content:
- 3+6
location:
row: 14
column: 0
end_location:
row: 17
column: 7
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 19
column: 0
end_location:
row: 22
column: 7
fix:
content:
- 3+6
location:
row: 19
column: 0
end_location:
row: 22
column: 7
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 24
column: 0
end_location:
row: 27
column: 7
fix:
content:
- 3+6
location:
row: 24
column: 0
end_location:
row: 27
column: 7
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 29
column: 0
end_location:
row: 32
column: 7
fix:
content:
- 3+6
location:
row: 29
column: 0
end_location:
row: 32
column: 7
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 34
column: 0
end_location:
row: 37
column: 7
fix:
content:
- 3+6
location:
row: 34
column: 0
end_location:
row: 37
column: 7
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 39
column: 0
end_location:
row: 40
column: 8
fix:
content:
- pass
location:
row: 39
column: 0
end_location:
row: 40
column: 8
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 42
column: 0
end_location:
row: 43
column: 8
fix:
content:
- ""
location:
row: 42
column: 0
end_location:
row: 44
column: 0
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 46
column: 4
end_location:
row: 47
column: 12
fix:
content:
- pass
location:
row: 46
column: 4
end_location:
row: 47
column: 12
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 49
column: 0
end_location:
row: 52
column: 8
fix:
content:
- ""
location:
row: 49
column: 0
end_location:
row: 51
column: 2
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 54
column: 0
end_location:
row: 57
column: 8
fix:
content:
- pass
location:
row: 54
column: 0
end_location:
row: 57
column: 8
parent: ~

View file

@ -0,0 +1,63 @@
---
source: src/rules/pyupgrade/mod.rs
expression: diagnostics
---
- kind:
OutdatedVersionBlock: ~
location:
row: 3
column: 0
end_location:
row: 10
column: 28
fix:
content:
- "print(\"py3\")"
- "for item in range(10):"
- " print(f\"PY3-{item}\")"
location:
row: 3
column: 0
end_location:
row: 10
column: 28
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 13
column: 4
end_location:
row: 20
column: 32
fix:
content:
- " print(\"py3\")"
- " for item in range(10):"
- " print(f\"PY3-{item}\")"
location:
row: 13
column: 0
end_location:
row: 20
column: 32
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 23
column: 0
end_location:
row: 24
column: 50
fix:
content:
- "print(\"PY3!\")"
location:
row: 23
column: 0
end_location:
row: 24
column: 50
parent: ~

View file

@ -0,0 +1,149 @@
---
source: src/rules/pyupgrade/mod.rs
expression: diagnostics
---
- kind:
OutdatedVersionBlock: ~
location:
row: 4
column: 4
end_location:
row: 5
column: 53
fix:
content:
- pass
location:
row: 4
column: 4
end_location:
row: 5
column: 53
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 11
column: 4
end_location:
row: 12
column: 53
fix:
content:
- ""
location:
row: 11
column: 0
end_location:
row: 13
column: 0
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 17
column: 4
end_location:
row: 20
column: 51
fix:
content:
- ""
location:
row: 17
column: 4
end_location:
row: 19
column: 4
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 24
column: 4
end_location:
row: 25
column: 53
fix:
content:
- ""
location:
row: 24
column: 0
end_location:
row: 26
column: 0
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 27
column: 4
end_location:
row: 28
column: 53
fix:
content:
- ""
location:
row: 27
column: 0
end_location:
row: 29
column: 0
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 32
column: 4
end_location:
row: 35
column: 51
fix:
content:
- ""
location:
row: 32
column: 4
end_location:
row: 34
column: 4
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 37
column: 4
end_location:
row: 40
column: 51
fix:
content:
- " cmd = [sys.executable, \"-m\", \"test\", \"-j0\"]"
location:
row: 37
column: 0
end_location:
row: 40
column: 51
parent: ~
- kind:
OutdatedVersionBlock: ~
location:
row: 42
column: 4
end_location:
row: 45
column: 51
fix:
content:
- ""
location:
row: 42
column: 4
end_location:
row: 44
column: 6
parent: ~

View file

@ -49,6 +49,18 @@ impl FromStr for PythonVersion {
}
}
impl PythonVersion {
pub fn as_tuple(&self) -> (u32, u32) {
match self {
PythonVersion::Py37 => (3, 7),
PythonVersion::Py38 => (3, 8),
PythonVersion::Py39 => (3, 9),
PythonVersion::Py310 => (3, 10),
PythonVersion::Py311 => (3, 11),
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub enum FilePattern {
Builtin(&'static str),