Repository: asottile/add-trailing-comma
Pull request #236 (2023-07-01 17:36:10)

only supports 3.6+ mode, --py35-plus / --py36-plus are noops


Patch:
diff --git a/add_trailing_comma/_data.py b/add_trailing_comma/_data.py
index cfe9905..cbfa114 100644
--- a/add_trailing_comma/_data.py
+++ b/add_trailing_comma/_data.py
@@ -21,11 +21,8 @@ if TYPE_CHECKING:
 else:
     Protocol = object
 
-Version = Tuple[int, ...]
-
 
 class State(NamedTuple):
-    min_version: Version
     in_fstring: bool = False
 
 
@@ -50,9 +47,8 @@ class ASTCallbackMapping(Protocol):
 def visit(
         funcs: ASTCallbackMapping,
         tree: ast.AST,
-        version: Version,
 ) -> dict[Offset, list[TokenFunc]]:
-    nodes = [(tree, State(min_version=version))]
+    nodes = [(tree, State())]
 
     ret = collections.defaultdict(list)
     while nodes:
diff --git a/add_trailing_comma/_main.py b/add_trailing_comma/_main.py
index 7d2f8a4..ab4583a 100644
--- a/add_trailing_comma/_main.py
+++ b/add_trailing_comma/_main.py
@@ -24,13 +24,13 @@ def _changing_list(lst: list[Token]) -> Iterable[tuple[int, Token]]:
         i += 1
 
 
-def _fix_src(contents_text: str, min_version: tuple[int, ...]) -> str:
+def _fix_src(contents_text: str) -> str:
     try:
         ast_obj = ast_parse(contents_text)
     except SyntaxError:
         return contents_text
 
-    callbacks = visit(FUNCS, ast_obj, min_version)
+    callbacks = visit(FUNCS, ast_obj)
 
     tokens = src_to_tokens(contents_text)
     for i, token in _changing_list(tokens):
@@ -67,7 +67,7 @@ def fix_file(filename: str, args: argparse.Namespace) -> int:
         print(msg, file=sys.stderr)
         return 1
 
-    contents_text = _fix_src(contents_text, args.min_version)
+    contents_text = _fix_src(contents_text)
 
     if filename == '-':
         print(contents_text, end='')
@@ -86,21 +86,12 @@ def main(argv: Sequence[str] | None = None) -> int:
     parser = argparse.ArgumentParser()
     parser.add_argument('filenames', nargs='*')
     parser.add_argument('--exit-zero-even-if-changed', action='store_true')
-    parser.add_argument(
-        '--py35-plus',
-        action='store_const', dest='min_version', const=(3, 5), default=(2, 7),
-    )
-    parser.add_argument(
-        '--py36-plus',
-        action='store_const', dest='min_version', const=(3, 6),
-    )
+    parser.add_argument('--py35-plus', action='store_true')
+    parser.add_argument('--py36-plus', action='store_true')
     args = parser.parse_args(argv)
 
-    if args.min_version < (3, 6):
-        print(
-            'WARNING: add-trailing-comma will only use 3.6+ mode after 3.0',
-            file=sys.stderr,
-        )
+    if args.py35_plus or args.py36_plus:
+        print('WARNING: --py35-plus / --py36-plus do nothing', file=sys.stderr)
 
     ret = 0
     for filename in args.filenames:
diff --git a/add_trailing_comma/_plugins/calls.py b/add_trailing_comma/_plugins/calls.py
index ddc744b..933daf9 100644
--- a/add_trailing_comma/_plugins/calls.py
+++ b/add_trailing_comma/_plugins/calls.py
@@ -19,13 +19,12 @@ def _fix_call(
         i: int,
         tokens: list[Token],
         *,
-        add_comma: bool,
         arg_offsets: set[Offset],
 ) -> None:
     return fix_brace(
         tokens,
         find_call(arg_offsets, i, tokens),
-        add_comma=add_comma,
+        add_comma=True,
         remove_comma=True,
     )
 
@@ -37,13 +36,7 @@ def visit_Call(
 ) -> Iterable[tuple[Offset, TokenFunc]]:
     argnodes = [*node.args, *node.keywords]
     arg_offsets = set()
-    has_starargs = False
     for argnode in argnodes:
-        if isinstance(argnode, ast.Starred):
-            has_starargs = True
-        if isinstance(argnode, ast.keyword) and argnode.arg is None:
-            has_starargs = True
-
         offset = ast_to_offset(argnode)
         # multiline strings have invalid position, ignore them
         if offset.utf8_byte_offset != -1:  # pragma: no branch (cpy bug)
@@ -58,7 +51,6 @@ def visit_Call(
     if arg_offsets and not only_a_generator and not state.in_fstring:
         func = functools.partial(
             _fix_call,
-            add_comma=not has_starargs or state.min_version >= (3, 5),
             arg_offsets=arg_offsets,
         )
         yield ast_to_offset(node), func
diff --git a/add_trailing_comma/_plugins/functions.py b/add_trailing_comma/_plugins/functions.py
index 258b22c..7331ad7 100644
--- a/add_trailing_comma/_plugins/functions.py
+++ b/add_trailing_comma/_plugins/functions.py
@@ -19,13 +19,12 @@ def _fix_func(
         i: int,
         tokens: list[Token],
         *,
-        add_comma: bool,
         arg_offsets: set[Offset],
 ) -> None:
     fix_brace(
         tokens,
         find_call(arg_offsets, i, tokens),
-        add_comma=add_comma,
+        add_comma=True,
         remove_comma=True,
     )
 
@@ -34,27 +33,19 @@ def visit_FunctionDef(
         state: State,
         node: ast.AsyncFunctionDef | ast.FunctionDef,
 ) -> Iterable[tuple[Offset, TokenFunc]]:
-    has_starargs = False
     args = [*getattr(node.args, 'posonlyargs', ()), *node.args.args]
 
     if node.args.vararg:
         args.append(node.args.vararg)
-        has_starargs = True
     if node.args.kwarg:
         args.append(node.args.kwarg)
-        has_starargs = True
     if node.args.kwonlyargs:
         args.extend(node.args.kwonlyargs)
-        has_starargs = True
 
     arg_offsets = {ast_to_offset(arg) for arg in args}
 
     if arg_offsets:
-        func = functools.partial(
-            _fix_func,
-            add_comma=not has_starargs or state.min_version >= (3, 6),
-            arg_offsets=arg_offsets,
-        )
+        func = functools.partial(_fix_func, arg_offsets=arg_offsets)
         yield ast_to_offset(node), func
 
 


Test patch:
diff --git a/tests/features/align_braces_test.py b/tests/features/align_braces_test.py
index 6dceb01..11af89c 100644
--- a/tests/features/align_braces_test.py
+++ b/tests/features/align_braces_test.py
@@ -33,7 +33,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_noop_trailing_brace(src):
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 @pytest.mark.parametrize(
@@ -75,4 +75,4 @@ def test_noop_trailing_brace(src):
     ),
 )
 def test_fix_trailing_brace(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/features/calls_test.py b/tests/features/calls_test.py
index ba0fc4d..8f62e71 100644
--- a/tests/features/calls_test.py
+++ b/tests/features/calls_test.py
@@ -16,13 +16,6 @@ from add_trailing_comma._main import _fix_src
         'tuple(\n'
         '    a for a in b\n'
         ')',
-        # Don't rewrite *args or **kwargs unless --py35-plus
-        'x(\n'
-        '    *args\n'
-        ')',
-        'x(\n'
-        '    **kwargs\n'
-        ')',
         # The ast tells us that the inner call starts on line 2, but the first
         # paren (and last paren) are actually both on line 3.
         'x(\n'
@@ -47,7 +40,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_fix_calls_noops(src):
-    ret = _fix_src(src, min_version=(2, 7))
+    ret = _fix_src(src)
     assert ret == src
 
 
@@ -57,7 +50,7 @@ def test_multiline_string_with_call():
         '   y\n'
         '    """.format(x, y)\n'
     )
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 def test_py35_plus_rewrite():
@@ -66,7 +59,7 @@ def test_py35_plus_rewrite():
         '    *args\n'
         ')'
     )
-    ret = _fix_src(src, min_version=(3, 5))
+    ret = _fix_src(src)
     assert ret == (
         'x(\n'
         '    *args,\n'
@@ -117,4 +110,4 @@ def test_py35_plus_rewrite():
     ),
 )
 def test_fixes_calls(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/features/classes_test.py b/tests/features/classes_test.py
index d98084e..c7d4963 100644
--- a/tests/features/classes_test.py
+++ b/tests/features/classes_test.py
@@ -17,7 +17,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_fix_classes_noop(src):
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 @pytest.mark.parametrize(
@@ -34,7 +34,7 @@ def test_fix_classes_noop(src):
     ),
 )
 def test_fix_classes(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
 
 
 @pytest.mark.parametrize(
@@ -71,4 +71,4 @@ def test_fix_classes(src, expected):
     ),
 )
 def test_fix_classes_py3_only_syntax(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/features/functions_test.py b/tests/features/functions_test.py
index 8ce4884..7536c1f 100644
--- a/tests/features/functions_test.py
+++ b/tests/features/functions_test.py
@@ -14,22 +14,10 @@ from add_trailing_comma._main import _fix_src
         '        arg1,\n'
         '        arg2,\n'
         '): pass',
-        # *args forbid trailing commas
-        'def f(\n'
-        '        *args\n'
-        '): pass',
-        # **kwargs forbid trailing commas
-        'def f(\n'
-        '        **kwargs\n'
-        '): pass',
-        # keyword-only args forbid trailing commas (or are py2 syntax error)
-        'def f(\n'
-        '        *, arg=1\n'
-        '): pass',
     ),
 )
 def test_noop_function_defs(src):
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 @pytest.mark.parametrize(
@@ -56,7 +44,7 @@ def test_noop_function_defs(src):
     ),
 )
 def test_fixes_defs(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
 
 
 @pytest.mark.parametrize(
@@ -92,7 +80,7 @@ def test_fixes_defs(src, expected):
     ),
 )
 def test_fixes_defs_py36_plus(src, expected):
-    assert _fix_src(src, min_version=(3, 6)) == expected
+    assert _fix_src(src) == expected
 
 
 @pytest.mark.parametrize(
@@ -110,4 +98,4 @@ def test_fixes_defs_py36_plus(src, expected):
     ),
 )
 def test_fix_posonlyargs(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/features/imports_test.py b/tests/features/imports_test.py
index 384f884..8b79cbd 100644
--- a/tests/features/imports_test.py
+++ b/tests/features/imports_test.py
@@ -17,7 +17,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_fix_from_import_noop(src):
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 @pytest.mark.parametrize(
@@ -68,4 +68,4 @@ def test_fix_from_import_noop(src):
     ),
 )
 def test_fix_from_import(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/features/literals_test.py b/tests/features/literals_test.py
index 302e7e0..20799c7 100644
--- a/tests/features/literals_test.py
+++ b/tests/features/literals_test.py
@@ -26,7 +26,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_noop_literals(src):
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 @pytest.mark.parametrize(
@@ -121,7 +121,7 @@ def test_noop_literals(src):
     ),
 )
 def test_fixes_literals(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
 
 
 @pytest.mark.parametrize(
@@ -166,7 +166,7 @@ def test_fixes_literals(src, expected):
     ),
 )
 def test_fixes_py35_plus_literals(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
 
 
 def test_noop_tuple_literal_without_braces():
@@ -176,4 +176,4 @@ def test_noop_tuple_literal_without_braces():
         '    2, \\\n'
         '    3'
     )
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
diff --git a/tests/features/match_test.py b/tests/features/match_test.py
index 30b13d9..234c98e 100644
--- a/tests/features/match_test.py
+++ b/tests/features/match_test.py
@@ -25,7 +25,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_noop(s):
-    assert _fix_src(s, min_version=(2, 7)) == s
+    assert _fix_src(s) == s
 
 
 @pytest.mark.xfail(sys.version_info < (3, 10), reason='py310+')
@@ -122,4 +122,4 @@ def test_noop(s):
     ),
 )
 def test_fix(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/features/remove_commas_test.py b/tests/features/remove_commas_test.py
index 2f6e68b..4c06821 100644
--- a/tests/features/remove_commas_test.py
+++ b/tests/features/remove_commas_test.py
@@ -21,4 +21,4 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_remove_extra_comma(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/features/unhug_test.py b/tests/features/unhug_test.py
index 66d9c9d..beac2b3 100644
--- a/tests/features/unhug_test.py
+++ b/tests/features/unhug_test.py
@@ -26,7 +26,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_noop_unhugs(src):
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 @pytest.mark.parametrize(
@@ -74,7 +74,7 @@ def test_noop_unhugs(src):
             '    *args): pass',
 
             'def f(\n'
-            '    *args\n'
+            '    *args,\n'
             '): pass',
         ),
         (
@@ -82,7 +82,7 @@ def test_noop_unhugs(src):
             '    **kwargs): pass',
 
             'def f(\n'
-            '    **kwargs\n'
+            '    **kwargs,\n'
             '): pass',
         ),
         # if there's already a trailing comma, don't add a new one
@@ -155,7 +155,7 @@ def test_noop_unhugs(src):
             '    *args)',
 
             'f(\n'
-            '    *args\n'
+            '    *args,\n'
             ')',
         ),
         (
@@ -274,25 +274,15 @@ def test_noop_unhugs(src):
 
             id='#42: listcomp unhug ends in brace',
         ),
-    ),
-)
-def test_fix_unhugs(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
-
-
-@pytest.mark.parametrize(
-    ('src', 'expected'),
-    (
-        # python 2 doesn't kwonlyargs
         (
             'def f(\n'
             '    *, kw=1, kw2=2): pass',
 
             'def f(\n'
-            '    *, kw=1, kw2=2\n'
+            '    *, kw=1, kw2=2,\n'
             '): pass',
         ),
     ),
 )
-def test_fix_unhugs_py3_only(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+def test_fix_unhugs(src, expected):
+    assert _fix_src(src) == expected
diff --git a/tests/features/with_test.py b/tests/features/with_test.py
index 2f64fa2..c878080 100644
--- a/tests/features/with_test.py
+++ b/tests/features/with_test.py
@@ -42,7 +42,7 @@ from add_trailing_comma._main import _fix_src
     ),
 )
 def test_noop(src):
-    assert _fix_src(src, min_version=(2, 7)) == src
+    assert _fix_src(src) == src
 
 
 @pytest.mark.xfail(sys.version_info < (3, 9), reason='py39+')
@@ -103,4 +103,4 @@ def test_noop(src):
     ),
 )
 def test_py39_multiwith(src, expected):
-    assert _fix_src(src, min_version=(2, 7)) == expected
+    assert _fix_src(src) == expected
diff --git a/tests/main_test.py b/tests/main_test.py
index bf66943..4666f30 100644
--- a/tests/main_test.py
+++ b/tests/main_test.py
@@ -21,7 +21,7 @@ def test_main_noop(tmpdir):
 def test_main_changes_a_file(tmpdir, capsys):
     f = tmpdir.join('f.py')
     f.write('x(\n    1\n)\n')
-    assert main((f.strpath, '--py36-plus')) == 1
+    assert main((f.strpath,)) == 1
     _, err = capsys.readouterr()
     assert err == f'Rewriting {f}\n'
     assert f.read() == 'x(\n    1,\n)\n'
@@ -30,7 +30,7 @@ def test_main_changes_a_file(tmpdir, capsys):
 def test_main_preserves_line_endings(tmpdir, capsys):
     f = tmpdir.join('f.py')
     f.write_binary(b'x(\r\n    1\r\n)\r\n')
-    assert main((f.strpath, '--py36-plus')) == 1
+    assert main((f.strpath,)) == 1
     _, err = capsys.readouterr()
     assert err == f'Rewriting {f}\n'
     assert f.read_binary() == b'x(\r\n    1,\r\n)\r\n'
@@ -45,7 +45,7 @@ def test_main_syntax_error(tmpdir):
 def test_main_non_utf8_bytes(tmpdir, capsys):
     f = tmpdir.join('f.py')
     f.write_binary('# -*- coding: cp1252 -*-\nx = €\n'.encode('cp1252'))
-    assert main((f.strpath, '--py36-plus')) == 1
+    assert main((f.strpath,)) == 1
     _, err = capsys.readouterr()
     assert err == f'{f} is non-utf-8 (not supported)\n'
 
@@ -53,47 +53,34 @@ def test_main_non_utf8_bytes(tmpdir, capsys):
 def test_main_py27_syntaxerror_coding(tmpdir):
     f = tmpdir.join('f.py')
     f.write('# -*- coding: utf-8 -*-\n[1, 2,]\n')
-    assert main((f.strpath, '--py36-plus')) == 1
+    assert main((f.strpath,)) == 1
     assert f.read() == '# -*- coding: utf-8 -*-\n[1, 2]\n'
 
 
-def test_main_py35_plus_argument_star_args(tmpdir, capsys):
+def test_main_py35_plus_py36_plus_deprecated(tmpdir, capsys):
     f = tmpdir.join('f.py')
     f.write('x(\n    *args\n)\n')
-    assert main((f.strpath,)) == 0
-    assert f.read() == 'x(\n    *args\n)\n'
-    out, err = capsys.readouterr()
-    assert err.startswith('WARNING: add-trailing-comma will only use 3.6+')
     assert main((f.strpath, '--py35-plus')) == 1
     assert f.read() == 'x(\n    *args,\n)\n'
     out, err = capsys.readouterr()
-    assert err.startswith('WARNING: add-trailing-comma will only use 3.6+')
+    assert err.startswith('WARNING: --py35-plus / --py36-plus do nothing')
+    assert main((f.strpath, '--py36-plus')) == 0
+    assert f.read() == 'x(\n    *args,\n)\n'
+    out, err = capsys.readouterr()
+    assert err.startswith('WARNING: --py35-plus / --py36-plus do nothing')
 
 
 def test_main_py35_plus_argument_star_star_kwargs(tmpdir):
     f = tmpdir.join('f.py')
     f.write('x(\n    **args\n)\n')
-    assert main((f.strpath,)) == 0
-    assert f.read() == 'x(\n    **args\n)\n'
-    assert main((f.strpath, '--py35-plus')) == 1
+    assert main((f.strpath,)) == 1
     assert f.read() == 'x(\n    **args,\n)\n'
 
 
-def test_main_py36_plus_implies_py35_plus(tmpdir):
-    f = tmpdir.join('f.py')
-    f.write('x(\n    **kwargs\n)\n')
-    assert main((f.strpath,)) == 0
-    assert f.read() == 'x(\n    **kwargs\n)\n'
-    assert main((f.strpath, '--py36-plus')) == 1
-    assert f.read() == 'x(\n    **kwargs,\n)\n'
-
-
 def test_main_py36_plus_function_trailing_commas(tmpdir):
     f = tmpdir.join('f.py')
     f.write('def f(\n    **kwargs\n): pass\n')
-    assert main((f.strpath,)) == 0
-    assert f.read() == 'def f(\n    **kwargs\n): pass\n'
-    assert main((f.strpath, '--py36-plus')) == 1
+    assert main((f.strpath,)) == 1
     assert f.read() == 'def f(\n    **kwargs,\n): pass\n'