-
-
Notifications
You must be signed in to change notification settings - Fork 177
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #429 from MarcoGorelli/universal_newlines_to_text
upgrade subprocess.run(universal_newlines=True) to subprocess.run(text=True) in --py37-plus
- Loading branch information
Showing
4 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,7 @@ class State(NamedTuple): | |
'select', | ||
'six', | ||
'socket', | ||
'subprocess', | ||
'sys', | ||
'typing', | ||
)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import ast | ||
import functools | ||
from typing import Iterable | ||
from typing import List | ||
from typing import Tuple | ||
|
||
from tokenize_rt import Offset | ||
from tokenize_rt import Token | ||
from tokenize_rt import tokens_to_src | ||
|
||
from pyupgrade._ast_helpers import ast_to_offset | ||
from pyupgrade._ast_helpers import is_name_attr | ||
from pyupgrade._data import register | ||
from pyupgrade._data import State | ||
from pyupgrade._data import TokenFunc | ||
from pyupgrade._token_helpers import find_open_paren | ||
from pyupgrade._token_helpers import parse_call_args | ||
|
||
|
||
def _replace_universal_newlines_with_text( | ||
i: int, | ||
tokens: List[Token], | ||
*, | ||
arg_idx: int, | ||
) -> None: | ||
j = find_open_paren(tokens, i) | ||
func_args, _ = parse_call_args(tokens, j) | ||
src = tokens_to_src(tokens[slice(*func_args[arg_idx])]) | ||
new_src = src.replace('universal_newlines', 'text', 1) | ||
tokens[slice(*func_args[arg_idx])] = [Token('SRC', new_src)] | ||
|
||
|
||
@register(ast.Call) | ||
def visit_Call( | ||
state: State, | ||
node: ast.Call, | ||
parent: ast.AST, | ||
) -> Iterable[Tuple[Offset, TokenFunc]]: | ||
if ( | ||
state.settings.min_version >= (3, 7) and | ||
is_name_attr( | ||
node.func, | ||
state.from_imports, | ||
'subprocess', | ||
('run',), | ||
) | ||
): | ||
kwarg_idx = next( | ||
( | ||
n | ||
for n, keyword in enumerate(node.keywords) | ||
if keyword.arg == 'universal_newlines' | ||
), | ||
None, | ||
) | ||
if kwarg_idx is not None: | ||
func = functools.partial( | ||
_replace_universal_newlines_with_text, | ||
arg_idx=len(node.args) + kwarg_idx, | ||
) | ||
yield ast_to_offset(node), func |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import pytest | ||
|
||
from pyupgrade._data import Settings | ||
from pyupgrade._main import _fix_plugins | ||
|
||
|
||
@pytest.mark.parametrize( | ||
('s', 'version'), | ||
( | ||
pytest.param( | ||
'import subprocess\n' | ||
'subprocess.run(["foo"], universal_newlines=True)\n', | ||
(3,), | ||
id='not Python3.7+', | ||
), | ||
pytest.param( | ||
'from foo import run\n' | ||
'run(["foo"], universal_newlines=True)\n', | ||
(3, 7), | ||
id='run imported, but not from subprocess', | ||
), | ||
pytest.param( | ||
'from subprocess import run\n' | ||
'run(["foo"], shell=True)\n', | ||
(3, 7), | ||
id='universal_newlines not used', | ||
), | ||
), | ||
) | ||
def test_fix_universal_newlines_to_text_noop(s, version): | ||
assert _fix_plugins(s, settings=Settings(min_version=version)) == s | ||
|
||
|
||
@pytest.mark.parametrize( | ||
('s', 'expected'), | ||
( | ||
pytest.param( | ||
'import subprocess\n' | ||
'subprocess.run(["foo"], universal_newlines=True)\n', | ||
'import subprocess\n' | ||
'subprocess.run(["foo"], text=True)\n', | ||
id='subprocess.run attribute', | ||
), | ||
pytest.param( | ||
'from subprocess import run\n' | ||
'run(["foo"], universal_newlines=True)\n', | ||
'from subprocess import run\n' | ||
'run(["foo"], text=True)\n', | ||
id='run imported from subprocess', | ||
), | ||
pytest.param( | ||
'from subprocess import run\n' | ||
'run(["foo"], universal_newlines=universal_newlines)\n', | ||
'from subprocess import run\n' | ||
'run(["foo"], text=universal_newlines)\n', | ||
id='universal_newlines appears as value', | ||
), | ||
pytest.param( | ||
'from subprocess import run\n' | ||
'run(["foo"], *foo, universal_newlines=universal_newlines)\n', | ||
'from subprocess import run\n' | ||
'run(["foo"], *foo, text=universal_newlines)\n', | ||
id='with starargs', | ||
), | ||
), | ||
) | ||
def test_fix_universal_newlines_to_text(s, expected): | ||
ret = _fix_plugins(s, settings=Settings(min_version=(3, 7))) | ||
assert ret == expected |