Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pyupgrade for openSUSE:Factory checked in at 2021-05-16 23:41:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyupgrade (Old) and /work/SRC/openSUSE:Factory/.python-pyupgrade.new.2988 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyupgrade" Sun May 16 23:41:58 2021 rev:10 rq:893450 version:2.15.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyupgrade/python-pyupgrade.changes 2021-05-10 15:41:10.965132527 +0200 +++ /work/SRC/openSUSE:Factory/.python-pyupgrade.new.2988/python-pyupgrade.changes 2021-05-16 23:44:15.609105466 +0200 @@ -1,0 +2,8 @@ +Sun May 9 09:50:40 UTC 2021 - Sebastian Wagner <sebix+novell....@sebix.at> + +- update to version 2.15.0: + - only replace argument token + - CLN only replace argument token + - replace stdout=PIPE and stderr=PIPE with capture_output=True + +------------------------------------------------------------------- Old: ---- python-pyupgrade-2.14.0.tar.gz New: ---- python-pyupgrade-2.15.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyupgrade.spec ++++++ --- /var/tmp/diff_new_pack.3I3Fgk/_old 2021-05-16 23:44:16.101103558 +0200 +++ /var/tmp/diff_new_pack.3I3Fgk/_new 2021-05-16 23:44:16.105103543 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-pyupgrade -Version: 2.14.0 +Version: 2.15.0 Release: 0 Summary: A tool to automatically upgrade syntax for newer versions License: MIT ++++++ python-pyupgrade-2.14.0.tar.gz -> python-pyupgrade-2.15.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/.pre-commit-config.yaml new/pyupgrade-2.15.0/.pre-commit-config.yaml --- old/pyupgrade-2.14.0/.pre-commit-config.yaml 2021-05-01 22:38:32.000000000 +0200 +++ new/pyupgrade-2.15.0/.pre-commit-config.yaml 2021-05-08 23:13:59.000000000 +0200 @@ -20,7 +20,7 @@ - id: flake8 additional_dependencies: [flake8-typing-imports==1.7.0] - repo: https://github.com/pre-commit/mirrors-autopep8 - rev: v1.5.6 + rev: v1.5.7 hooks: - id: autopep8 - repo: https://github.com/asottile/reorder_python_imports @@ -34,7 +34,7 @@ - id: add-trailing-comma args: [--py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.14.0 + rev: v2.15.0 hooks: - id: pyupgrade args: [--py36-plus] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/README.md new/pyupgrade-2.15.0/README.md --- old/pyupgrade-2.14.0/README.md 2021-05-01 22:38:32.000000000 +0200 +++ new/pyupgrade-2.15.0/README.md 2021-05-08 23:13:59.000000000 +0200 @@ -20,7 +20,7 @@ ```yaml - repo: https://github.com/asottile/pyupgrade - rev: v2.14.0 + rev: v2.15.0 hooks: - id: pyupgrade ``` @@ -474,6 +474,17 @@ ``` +### `subprocess.run`: replace `stdout=subprocess.PIPE, stderr=subprocess.PIPE` with `capture_output=True` + +Availability: +- `--py37-plus` is passed on the commandline. + +```diff +-output = subprocess.run(['foo'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) ++output = subprocess.run(['foo'], capture_output=True) +``` + + ### remove parentheses from `@functools.lru_cache()` Availability: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/pyupgrade/_plugins/open_mode.py new/pyupgrade-2.15.0/pyupgrade/_plugins/open_mode.py --- old/pyupgrade-2.14.0/pyupgrade/_plugins/open_mode.py 2021-05-01 22:38:32.000000000 +0200 +++ new/pyupgrade-2.15.0/pyupgrade/_plugins/open_mode.py 2021-05-08 23:13:59.000000000 +0200 @@ -14,6 +14,7 @@ from pyupgrade._data import register from pyupgrade._data import State from pyupgrade._data import TokenFunc +from pyupgrade._token_helpers import delete_argument from pyupgrade._token_helpers import find_open_paren from pyupgrade._token_helpers import parse_call_args @@ -35,10 +36,7 @@ mode_stripped = mode.split('=')[-1] mode_stripped = mode_stripped.strip().strip('"\'') if mode_stripped in U_MODE_REMOVE: - if arg_idx == 0: - del tokens[func_args[arg_idx][0]: func_args[arg_idx + 1][0]] - else: - del tokens[func_args[arg_idx - 1][1]:func_args[arg_idx][1]] + delete_argument(arg_idx, tokens, func_args) elif mode_stripped in U_MODE_REPLACE_R: new_mode = mode.replace('U', 'r') tokens[slice(*func_args[arg_idx])] = [Token('SRC', new_mode)] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/pyupgrade/_plugins/subprocess_run.py new/pyupgrade-2.15.0/pyupgrade/_plugins/subprocess_run.py --- old/pyupgrade-2.14.0/pyupgrade/_plugins/subprocess_run.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyupgrade-2.15.0/pyupgrade/_plugins/subprocess_run.py 2021-05-08 23:13:59.000000000 +0200 @@ -0,0 +1,111 @@ +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 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 delete_argument +from pyupgrade._token_helpers import find_open_paren +from pyupgrade._token_helpers import parse_call_args +from pyupgrade._token_helpers import replace_argument + + +def _use_capture_output( + i: int, + tokens: List[Token], + *, + stdout_arg_idx: int, + stderr_arg_idx: int, +) -> None: + j = find_open_paren(tokens, i) + func_args, _ = parse_call_args(tokens, j) + if stdout_arg_idx < stderr_arg_idx: + delete_argument(stderr_arg_idx, tokens, func_args) + replace_argument( + stdout_arg_idx, + tokens, + func_args, + new='capture_output=True', + ) + else: + replace_argument( + stdout_arg_idx, + tokens, + func_args, + new='capture_output=True', + ) + delete_argument(stderr_arg_idx, tokens, func_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) + for i in range(*func_args[arg_idx]): + if tokens[i].src == 'universal_newlines': + tokens[i] = tokens[i]._replace(src='text') + break + else: + raise AssertionError('`universal_newlines` argument not found') + + +@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',), + ) + ): + stdout_idx = None + stderr_idx = None + universal_newlines_idx = None + for n, keyword in enumerate(node.keywords): + if keyword.arg == 'stdout' and is_name_attr( + keyword.value, + state.from_imports, + 'subprocess', + ('PIPE',), + ): + stdout_idx = n + elif keyword.arg == 'stderr' and is_name_attr( + keyword.value, + state.from_imports, + 'subprocess', + ('PIPE',), + ): + stderr_idx = n + elif keyword.arg == 'universal_newlines': + universal_newlines_idx = n + if universal_newlines_idx is not None: + func = functools.partial( + _replace_universal_newlines_with_text, + arg_idx=len(node.args) + universal_newlines_idx, + ) + yield ast_to_offset(node), func + if stdout_idx is not None and stderr_idx is not None: + func = functools.partial( + _use_capture_output, + stdout_arg_idx=len(node.args) + stdout_idx, + stderr_arg_idx=len(node.args) + stderr_idx, + ) + yield ast_to_offset(node), func diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/pyupgrade/_plugins/universal_newlines_to_text.py new/pyupgrade-2.15.0/pyupgrade/_plugins/universal_newlines_to_text.py --- old/pyupgrade-2.14.0/pyupgrade/_plugins/universal_newlines_to_text.py 2021-05-01 22:38:32.000000000 +0200 +++ new/pyupgrade-2.15.0/pyupgrade/_plugins/universal_newlines_to_text.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,61 +0,0 @@ -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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/pyupgrade/_token_helpers.py new/pyupgrade-2.15.0/pyupgrade/_token_helpers.py --- old/pyupgrade-2.14.0/pyupgrade/_token_helpers.py 2021-05-01 22:38:32.000000000 +0200 +++ new/pyupgrade-2.15.0/pyupgrade/_token_helpers.py 2021-05-08 23:13:59.000000000 +0200 @@ -434,3 +434,32 @@ return j += 1 tokens[i:j + 1] = [new_token] + + +def delete_argument( + i: int, tokens: List[Token], + func_args: Sequence[Tuple[int, int]], +) -> None: + if i == 0: + # delete leading whitespace before next token + end_idx, _ = func_args[i + 1] + while tokens[end_idx].name == 'UNIMPORTANT_WS': + end_idx += 1 + + del tokens[func_args[i][0]:end_idx] + else: + del tokens[func_args[i - 1][1]:func_args[i][1]] + + +def replace_argument( + i: int, + tokens: List[Token], + func_args: Sequence[Tuple[int, int]], + *, + new: str, +) -> None: + start_idx, end_idx = func_args[i] + # don't replace leading whitespace / newlines + while tokens[start_idx].name in {'UNIMPORTANT_WS', 'NL'}: + start_idx += 1 + tokens[start_idx:end_idx] = [Token('SRC', new)] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/setup.cfg new/pyupgrade-2.15.0/setup.cfg --- old/pyupgrade-2.14.0/setup.cfg 2021-05-01 22:38:32.000000000 +0200 +++ new/pyupgrade-2.15.0/setup.cfg 2021-05-08 23:13:59.000000000 +0200 @@ -1,6 +1,6 @@ [metadata] name = pyupgrade -version = 2.14.0 +version = 2.15.0 description = A tool to automatically upgrade syntax for newer versions. long_description = file: README.md long_description_content_type = text/markdown diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/tests/features/capture_output_test.py new/pyupgrade-2.15.0/tests/features/capture_output_test.py --- old/pyupgrade-2.14.0/tests/features/capture_output_test.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyupgrade-2.15.0/tests/features/capture_output_test.py 2021-05-08 23:13:59.000000000 +0200 @@ -0,0 +1,116 @@ +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"], stdout=subprocess.PIPE, ' + 'stderr=subprocess.PIPE)\n', + (3,), + id='not Python3.7+', + ), + pytest.param( + 'from foo import run\n' + 'import subprocess\n' + 'run(["foo"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n', + (3, 7), + id='run imported, but not from subprocess', + ), + pytest.param( + 'from foo import PIPE\n' + 'from subprocess import run\n' + 'subprocess.run(["foo"], stdout=PIPE, stderr=PIPE)\n', + (3, 7), + id='PIPE imported, but not from subprocess', + ), + pytest.param( + 'from subprocess import run\n' + 'run(["foo"], stdout=None, stderr=PIPE)\n', + (3, 7), + id='stdout not subprocess.PIPE', + ), + ), +) +def test_fix_capture_output_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"], stdout=subprocess.PIPE, ' + 'stderr=subprocess.PIPE)\n', + 'import subprocess\n' + 'subprocess.run(["foo"], capture_output=True)\n', + id='subprocess.run and subprocess.PIPE attributes', + ), + pytest.param( + 'from subprocess import run, PIPE\n' + 'run(["foo"], stdout=PIPE, stderr=PIPE)\n', + 'from subprocess import run, PIPE\n' + 'run(["foo"], capture_output=True)\n', + id='run and PIPE imported from subprocess', + ), + pytest.param( + 'from subprocess import run, PIPE\n' + 'run(["foo"], shell=True, stdout=PIPE, stderr=PIPE)\n', + 'from subprocess import run, PIPE\n' + 'run(["foo"], shell=True, capture_output=True)\n', + id='other argument used too', + ), + pytest.param( + 'import subprocess\n' + 'subprocess.run(["foo"], stderr=subprocess.PIPE, ' + 'stdout=subprocess.PIPE)\n', + 'import subprocess\n' + 'subprocess.run(["foo"], capture_output=True)\n', + id='stderr used before stdout', + ), + pytest.param( + 'import subprocess\n' + 'subprocess.run(stderr=subprocess.PIPE, args=["foo"], ' + 'stdout=subprocess.PIPE)\n', + 'import subprocess\n' + 'subprocess.run(args=["foo"], capture_output=True)\n', + id='stdout is first argument', + ), + pytest.param( + 'import subprocess\n' + 'subprocess.run(\n' + ' stderr=subprocess.PIPE, \n' + ' args=["foo"], \n' + ' stdout=subprocess.PIPE,\n' + ')\n', + 'import subprocess\n' + 'subprocess.run(\n' + ' args=["foo"], \n' + ' capture_output=True,\n' + ')\n', + id='stdout is first argument, multiline', + ), + pytest.param( + 'subprocess.run(\n' + ' "foo",\n' + ' stdout=subprocess.PIPE,\n' + ' stderr=subprocess.PIPE,\n' + ' universal_newlines=True,\n' + ')', + 'subprocess.run(\n' + ' "foo",\n' + ' capture_output=True,\n' + ' text=True,\n' + ')', + id='both universal_newlines and capture_output rewrite', + ), + ), +) +def test_fix_capture_output(s, expected): + ret = _fix_plugins(s, settings=Settings(min_version=(3, 7))) + assert ret == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.14.0/tests/features/open_mode_test.py new/pyupgrade-2.15.0/tests/features/open_mode_test.py --- old/pyupgrade-2.14.0/tests/features/open_mode_test.py 2021-05-01 22:38:32.000000000 +0200 +++ new/pyupgrade-2.15.0/tests/features/open_mode_test.py 2021-05-08 23:13:59.000000000 +0200 @@ -60,7 +60,7 @@ ), ( 'open(mode="r", encoding="UTF-8", file="t.py")', - 'open( encoding="UTF-8", file="t.py")', + 'open(encoding="UTF-8", file="t.py")', ), ), )