diff --git a/pyupgrade/_plugins/versioned_branches.py b/pyupgrade/_plugins/versioned_branches.py index df66b744..6cc7a1c4 100644 --- a/pyupgrade/_plugins/versioned_branches.py +++ b/pyupgrade/_plugins/versioned_branches.py @@ -14,6 +14,7 @@ from pyupgrade._data import register from pyupgrade._data import State from pyupgrade._data import TokenFunc +from pyupgrade._data import Version from pyupgrade._token_helpers import Block @@ -77,6 +78,7 @@ def _eq(test: ast.Compare, n: int) -> bool: def _compare_to_3( test: ast.Compare, op: Union[Type[ast.cmpop], Tuple[Type[ast.cmpop], ...]], + minor: int = 0, ) -> bool: if not ( isinstance(test.ops[0], op) and @@ -87,9 +89,11 @@ def _compare_to_3( return False # checked above but mypy needs help - elts = cast('List[ast.Num]', test.comparators[0].elts) + ast_elts = cast('List[ast.Num]', test.comparators[0].elts) + # padding a 0 for compatibility with (3,) used as a spec + elts = tuple(e.n for e in ast_elts) + (0,) - return elts[0].n == 3 and all(n.n == 0 for n in elts[1:]) + return elts[:2] == (3, minor) and all(n == 0 for n in elts[2:]) @register(ast.If) @@ -98,8 +102,16 @@ def visit_If( node: ast.If, parent: ast.AST, ) -> Iterable[Tuple[Offset, TokenFunc]]: + + min_version: Version + if state.settings.min_version == (3,): + min_version = (3, 0) + else: + min_version = state.settings.min_version + assert len(min_version) >= 2 + if ( - state.settings.min_version >= (3,) and ( + min_version >= (3,) and ( # if six.PY2: is_name_attr(node.test, state.from_imports, 'six', ('PY2',)) or # if not six.PY3: @@ -114,6 +126,7 @@ def visit_If( ) ) or # sys.version_info == 2 or < (3,) + # or < (3, n) or <= (3, n) (with n= (3,) and ( + min_version >= (3,) and ( # if six.PY3: is_name_attr(node.test, state.from_imports, 'six', ('PY3',)) or # if not six.PY2: @@ -147,6 +164,8 @@ def visit_If( ) ) or # sys.version_info == 3 or >= (3,) or > (3,) + # sys.version_info >= (3, n) (with n<=m) + # or sys.version_info > (3, n) (with n (3, 5):\n' + ' 3+6\n' + 'else:\n' + ' 3-5\n', + + 'import sys\n' + '3+6\n', + id='sys.version_info > (3, 5)', + ), + pytest.param( + 'from sys import version_info\n' + 'if version_info > (3, 5):\n' + ' 3+6\n' + 'else:\n' + ' 3-5\n', + + 'from sys import version_info\n' + '3+6\n', + id='from sys import version_info, > (3, 5)', + ), + pytest.param( + 'import sys\n' + 'if sys.version_info >= (3, 6):\n' + ' 3+6\n' + 'else:\n' + ' 3-5\n', + + 'import sys\n' + '3+6\n', + id='sys.version_info >= (3, 6)', + ), + pytest.param( + 'from sys import version_info\n' + 'if version_info >= (3, 6):\n' + ' 3+6\n' + 'else:\n' + ' 3-5\n', + + 'from sys import version_info\n' + '3+6\n', + id='from sys import version_info, >= (3, 6)', + ), + pytest.param( + 'import sys\n' + 'if sys.version_info < (3, 6):\n' + ' 3-5\n' + 'else:\n' + ' 3+6\n', + + 'import sys\n' + '3+6\n', + id='sys.version_info < (3, 6)', + ), + pytest.param( + 'from sys import version_info\n' + 'if version_info < (3, 6):\n' + ' 3-5\n' + 'else:\n' + ' 3+6\n', + + 'from sys import version_info\n' + '3+6\n', + id='from sys import version_info, < (3, 6)', + ), + pytest.param( + 'import sys\n' + 'if sys.version_info <= (3, 5):\n' + ' 3-5\n' + 'else:\n' + ' 3+6\n', + + 'import sys\n' + '3+6\n', + id='sys.version_info <= (3, 5)', + ), + pytest.param( + 'from sys import version_info\n' + 'if version_info <= (3, 5):\n' + ' 3-5\n' + 'else:\n' + ' 3+6\n', + + 'from sys import version_info\n' + '3+6\n', + id='from sys import version_info, <= (3, 5)', + ), + ), +) +def test_fix_py3x_only_code(s, expected): + ret = _fix_plugins(s, settings=Settings(min_version=(3, 6))) + assert ret == expected + + +@pytest.mark.parametrize( + 's', + ( + # we timidly skip `if` without `else` as it could cause a SyntaxError + 'import sys' + 'if sys.version_info >= (3, 6):\n' + ' pass', + # here's the case where it causes a SyntaxError + 'import sys' + 'if True' + ' if sys.version_info >= (3, 6):\n' + ' pass\n', + # both branches are still relevant in the following cases + 'import sys\n' + 'if sys.version_info > (3, 7):\n' + ' 3-6\n' + 'else:\n' + ' 3+7\n', + + 'import sys\n' + 'if sys.version_info < (3, 7):\n' + ' 3-6\n' + 'else:\n' + ' 3+7\n', + + 'import sys\n' + 'if sys.version_info >= (3, 7):\n' + ' 3+7\n' + 'else:\n' + ' 3-6\n', + + 'import sys\n' + 'if sys.version_info <= (3, 7):\n' + ' 3-7\n' + 'else:\n' + ' 3+8\n', + + 'import sys\n' + 'if sys.version_info <= (3, 6):\n' + ' 3-6\n' + 'else:\n' + ' 3+7\n', + + 'import sys\n' + 'if sys.version_info > (3, 6):\n' + ' 3+7\n' + 'else:\n' + ' 3-6\n', + ), +) +def test_fix_py3x_only_noop(s): + assert _fix_plugins(s, settings=Settings(min_version=(3, 6))) == s