nebius/SWE-bench-extra huggingface dataset:
Repository: asottile/pyupgrade

Issue:
pyupgrade incorrect output
before
```python
expectedResult = (
    ":example.com \u0442\u0435\u0441\u0442 " "\u043d\u0438\u043a\r\n"
).encode("utf-8")
```

expected
```python
b':example.com \xd1\x82\xd0\xb5\xd1\x81\xd1\x82 \xd0\xbd\xd0\xb8\xd0\xba\r\n'
```

or maybe this?
```python
':example.com тест ник\r\n'.encode()
```

actual
```python
expectedResult = ().encode("utf-8")
```

Comments:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index 8fe8d11..79f2bb2 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -2233,7 +2233,7 @@ def _fix_py3_plus(
             victims = _victims(tokens, i, call, gen=False)
             del tokens[victims.starts[0] + 1:victims.ends[-1]]
         elif token.offset in visitor.encode_calls:
-            i = _find_open_paren(tokens, i)
+            i = _find_open_paren(tokens, i + 1)
             call = visitor.encode_calls[token.offset]
             victims = _victims(tokens, i, call, gen=False)
             del tokens[victims.starts[0] + 1:victims.ends[-1]]


Test patch:
diff --git a/tests/default_encoding_test.py b/tests/default_encoding_test.py
index 4385583..f39548d 100644
--- a/tests/default_encoding_test.py
+++ b/tests/default_encoding_test.py
@@ -13,6 +13,14 @@ from pyupgrade import _fix_py3_plus
             'sys.stdout.buffer.write(\n    "a"\n    "b".encode("utf-8")\n)',
             'sys.stdout.buffer.write(\n    "a"\n    "b".encode()\n)',
         ),
+        (
+            'x = (\n'
+            '    "y\\u2603"\n'
+            ').encode("utf-8")\n',
+            'x = (\n'
+            '    "y\\u2603"\n'
+            ').encode()\n',
+        ),
     ),
 )
 def test_fix_encode(s, expected):
Repository: asottile/pyupgrade

Issue:
Fails to run on python3.9
This is because there is no import removals defined for https://github.com/asottile/pyupgrade/blob/5e32666723/pyupgrade.py#L680-L693, and https://github.com/asottile/pyupgrade/blob/5e32666723/pyupgrade.py#L732 lookup fails. Perhaps instead of ``[]`` it should use ``.get``?

```
venv/bin/pyupgrade --py39-plus src/**.py 
Traceback (most recent call last):
  File "/Users/bgabor8/git/a/venv/bin/pyupgrade", line 8, in <module>
    sys.exit(main())
  File "/Users/bgabor8/git/a/venv/lib/python3.9/site-packages/pyupgrade.py", line 2823, in main
    ret |= _fix_file(filename, args)
  File "/Users/bgabor8/git/a/venv/lib/python3.9/site-packages/pyupgrade.py", line 2770, in _fix_file
    contents_text = _fix_tokens(contents_text, min_version=args.min_version)
  File "/Users/bgabor8/git/a/venv/lib/python3.9/site-packages/pyupgrade.py", line 808, in _fix_tokens
    _fix_import_removals(tokens, i, min_version)
  File "/Users/bgabor8/git/a/venv/lib/python3.9/site-packages/pyupgrade.py", line 732, in _fix_import_removals
    if modname not in IMPORT_REMOVALS[min_version]:
KeyError: (3, 9)
```

Comments:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index 796064f..254c9ff 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -690,6 +690,7 @@ def _build_import_removals() -> Dict[MinVersion, Dict[str, Tuple[str, ...]]]:
         ((3, 6), ()),
         ((3, 7), ('generator_stop',)),
         ((3, 8), ()),
+        ((3, 9), ()),
     )
 
     prev: Tuple[str, ...] = ()


Test patch:
diff --git a/tests/main_test.py b/tests/main_test.py
index cd992d1..997f682 100644
--- a/tests/main_test.py
+++ b/tests/main_test.py
@@ -19,6 +19,7 @@ def test_main_trivial():
         ('--py36-plus',),
         ('--py37-plus',),
         ('--py38-plus',),
+        ('--py39-plus',),
     ),
 )
 def test_main_noop(tmpdir, args):
Repository: asottile/pyupgrade

Issue:
`--py311-plus` option not working
`--py311-plus` was added in #426 but when I run with it I get this:
```
Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/molkree/orca-action-venv/lib/python3.8/site-packages/pyupgrade/__main__.py", line 4, in <module>
    exit(main())
  File "/home/molkree/orca-action-venv/lib/python3.8/site-packages/pyupgrade/_main.py", line 927, in main
    ret |= _fix_file(filename, args)
  File "/home/molkree/orca-action-venv/lib/python3.8/site-packages/pyupgrade/_main.py", line 871, in _fix_file
    contents_text = _fix_tokens(contents_text, min_version=args.min_version)
  File "/home/molkree/orca-action-venv/lib/python3.8/site-packages/pyupgrade/_main.py", line 509, in _fix_tokens
    _fix_import_removals(tokens, i, min_version)
  File "/home/molkree/orca-action-venv/lib/python3.8/site-packages/pyupgrade/_main.py", line 433, in _fix_import_removals
    if modname not in IMPORT_REMOVALS[min_version]:
KeyError: (3, 11)
```

I think it just wasn't added here:
https://github.com/asottile/pyupgrade/blob/4f1369bf114fd60cbe155e29a79fdd715e1e8f3e/pyupgrade/_main.py#L381-L394

There is still time to fix this before fall 2022 though 👀

Comments:

Patch:
diff --git a/pyupgrade/_main.py b/pyupgrade/_main.py
index d8e359d..b530008 100644
--- a/pyupgrade/_main.py
+++ b/pyupgrade/_main.py
@@ -391,6 +391,7 @@ def _build_import_removals() -> Dict[Version, Dict[str, Tuple[str, ...]]]:
         ((3, 8), ()),
         ((3, 9), ()),
         ((3, 10), ()),
+        ((3, 11), ()),
     )
 
     prev: Tuple[str, ...] = ()


Test patch:
diff --git a/tests/main_test.py b/tests/main_test.py
index 9f7f16b..b8f507f 100644
--- a/tests/main_test.py
+++ b/tests/main_test.py
@@ -1,4 +1,5 @@
 import io
+import re
 import sys
 from unittest import mock
 
@@ -11,19 +12,12 @@ def test_main_trivial():
     assert main(()) == 0
 
 
-@pytest.mark.parametrize(
-    'args',
-    (
-        (),
-        ('--py3-plus',),
-        ('--py36-plus',),
-        ('--py37-plus',),
-        ('--py38-plus',),
-        ('--py39-plus',),
-        ('--py310-plus',),
-    ),
-)
-def test_main_noop(tmpdir, args):
+def test_main_noop(tmpdir, capsys):
+    with pytest.raises(SystemExit):
+        main(('--help',))
+    out, err = capsys.readouterr()
+    version_options = sorted(set(re.findall(r'--py\d+-plus', out)))
+
     s = '''\
 from sys import version_info
 x=version_info
@@ -32,9 +26,14 @@ def f():
 '''
     f = tmpdir.join('f.py')
     f.write(s)
-    assert main((f.strpath, *args)) == 0
+
+    assert main((f.strpath,)) == 0
     assert f.read() == s
 
+    for version_option in version_options:
+        assert main((f.strpath, version_option)) == 0
+        assert f.read() == s
+
 
 def test_main_changes_a_file(tmpdir, capsys):
     f = tmpdir.join('f.py')
Repository: asottile/pyupgrade

Issue:
KeyError running with --py36-plus on matplotlib's ticker.py
Running pyupgrade 1.5 on matplotlib's ticker.py as of, say, mpl3.0rc2 (https://github.com/matplotlib/matplotlib/blob/v3.0.0rc2/lib/matplotlib/ticker.py) gives me
```
Traceback (most recent call last):
  File "/usr/bin/pyupgrade", line 11, in <module>
    sys.exit(main())
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 907, in main
    ret |= fix_file(filename, args)
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 884, in fix_file
    contents_text = _fix_fstrings(contents_text)
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 858, in _fix_fstrings
    tokens[i] = token._replace(src=_to_fstring(token.src, node))
  File "/usr/lib/python3.7/site-packages/pyupgrade.py", line 824, in _to_fstring
    name = ''.join((params[k or str(i)], dot, rest))
KeyError: '3'
```

Comments:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index 486a440..b9cb902 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -818,10 +818,12 @@ def _to_fstring(src, call):
         params[kwd.arg] = _unparse(kwd.value)
 
     parts = []
-    for i, (s, name, spec, conv) in enumerate(parse_format('f' + src)):
+    i = 0
+    for s, name, spec, conv in parse_format('f' + src):
         if name is not None:
             k, dot, rest = name.partition('.')
             name = ''.join((params[k or str(i)], dot, rest))
+            i += 1
         parts.append((s, name, spec, conv))
     return unparse_parsed_string(parts)
 


Test patch:
diff --git a/tests/pyupgrade_test.py b/tests/pyupgrade_test.py
index 80d0b3b..24b47f4 100644
--- a/tests/pyupgrade_test.py
+++ b/tests/pyupgrade_test.py
@@ -721,6 +721,7 @@ def test_fix_fstrings_noop(s):
         ('"{.x} {.y}".format(a, b)', 'f"{a.x} {b.y}"'),
         ('"{} {}".format(a.b, c.d)', 'f"{a.b} {c.d}"'),
         ('"hello {}!".format(name)', 'f"hello {name}!"'),
+        ('"{}{{}}{}".format(escaped, y)', 'f"{escaped}{{}}{y}"'),
 
         # TODO: poor man's f-strings?
         # '"{foo}".format(**locals())'
Repository: asottile/pyupgrade

Issue:
Incorrect rewrite of six.reraise with traceback as keyword argument
Reduced from real code in Launchpad:

```
$ cat t.py
import sys

from six import reraise

exc_info = sys.exc_info()
reraise(exc_info[0], exc_info[1], tb=exc_info[2])
$ pyupgrade --py3-plus t.py
Rewriting t.py
$ cat t.py
import sys

from six import reraise

exc_info = sys.exc_info()
raise exc_info[1].with_traceback(None)
```

pyupgrade has missed the keyword argument here; this should instead be rewritten to `raise exc_info[1].with_traceback(exc_info[2])`.

(In fact in Python 3 this can end up just being a plain `raise`: the original code was more complicated and had a second possible exception being raised and handled between `sys.exc_info()` and `reraise()`, which in Python 2 would have caused plain `raise` to raise the second exception, but the exception scoping changes in Python 3 mean that a plain `raise` will do the right thing.  I don't expect pyupgrade to do that sort of analysis, though.)

Comments:

Patch:
diff --git a/pyupgrade/_plugins/six_calls.py b/pyupgrade/_plugins/six_calls.py
index 90c4ee3..ac58fd5 100644
--- a/pyupgrade/_plugins/six_calls.py
+++ b/pyupgrade/_plugins/six_calls.py
@@ -172,13 +172,21 @@ def visit_Call(
                 ('reraise',),
             )
     ):
-        if len(node.args) == 2 and not has_starargs(node):
+        if (
+                len(node.args) == 2 and
+                not node.keywords and
+                not has_starargs(node)
+        ):
             func = functools.partial(
                 find_and_replace_call,
                 template=RERAISE_2_TMPL,
             )
             yield ast_to_offset(node), func
-        elif len(node.args) == 3 and not has_starargs(node):
+        elif (
+                len(node.args) == 3 and
+                not node.keywords and
+                not has_starargs(node)
+        ):
             func = functools.partial(
                 find_and_replace_call,
                 template=RERAISE_3_TMPL,
@@ -186,6 +194,7 @@ def visit_Call(
             yield ast_to_offset(node), func
         elif (
                 len(node.args) == 1 and
+                not node.keywords and
                 isinstance(node.args[0], ast.Starred) and
                 isinstance(node.args[0].value, ast.Call) and
                 is_name_attr(


Test patch:
diff --git a/tests/features/six_test.py b/tests/features/six_test.py
index e1f6cd8..4ea3f5b 100644
--- a/tests/features/six_test.py
+++ b/tests/features/six_test.py
@@ -24,6 +24,7 @@ from pyupgrade._main import _fix_plugins
         'class C(six.with_metaclass(Meta, B), D): pass',
         # cannot determine args to rewrite them
         'six.reraise(*err)', 'six.u(*a)',
+        'six.reraise(a, b, tb=c)',
         'class C(six.with_metaclass(*a)): pass',
         '@six.add_metaclass(*a)\n'
         'class C: pass\n',
Repository: asottile/pyupgrade

Issue:
universal_newlines conversion can lead to doubled "text"
If for some reason `universal_newlines` and `text` are both passed to a `subprocess.run()`, replacing the first with the latter causes `text` to be doubled. I suggest to in such case remove `universal_newlines`, if `text` has priority anyway with Python 3.6 and later.

Comments:

Patch:
diff --git a/pyupgrade/_plugins/subprocess_run.py b/pyupgrade/_plugins/subprocess_run.py
index 48affbe..53cc03d 100644
--- a/pyupgrade/_plugins/subprocess_run.py
+++ b/pyupgrade/_plugins/subprocess_run.py
@@ -79,6 +79,7 @@ def visit_Call(
         stdout_idx = None
         stderr_idx = None
         universal_newlines_idx = None
+        skip_universal_newlines_rewrite = False
         for n, keyword in enumerate(node.keywords):
             if keyword.arg == 'stdout' and is_name_attr(
                 keyword.value,
@@ -96,7 +97,12 @@ def visit_Call(
                 stderr_idx = n
             elif keyword.arg == 'universal_newlines':
                 universal_newlines_idx = n
-        if universal_newlines_idx is not None:
+            elif keyword.arg == 'text' or keyword.arg is None:
+                skip_universal_newlines_rewrite = True
+        if (
+                universal_newlines_idx is not None and
+                not skip_universal_newlines_rewrite
+        ):
             func = functools.partial(
                 _replace_universal_newlines_with_text,
                 arg_idx=len(node.args) + universal_newlines_idx,


Test patch:
diff --git a/tests/features/universal_newlines_to_text_test.py b/tests/features/universal_newlines_to_text_test.py
index 2e7e298..e7acd9e 100644
--- a/tests/features/universal_newlines_to_text_test.py
+++ b/tests/features/universal_newlines_to_text_test.py
@@ -27,6 +27,26 @@ from pyupgrade._main import _fix_plugins
             (3, 7),
             id='universal_newlines not used',
         ),
+        pytest.param(
+            'import subprocess\n'
+            'subprocess.run(\n'
+            '   ["foo"],\n'
+            '   text=True,\n'
+            '   universal_newlines=True\n'
+            ')\n',
+            (3, 7),
+            id='both text and universal_newlines',
+        ),
+        pytest.param(
+            'import subprocess\n'
+            'subprocess.run(\n'
+            '   ["foo"],\n'
+            '   universal_newlines=True,\n'
+            '   **kwargs,\n'
+            ')\n',
+            (3, 7),
+            id='both **kwargs and universal_newlines',
+        ),
     ),
 )
 def test_fix_universal_newlines_to_text_noop(s, version):
Repository: asottile/pyupgrade

Issue:
two steps to converge: io.open(..., 'r')
the first step goes to `open(..., 'r')` and the second step goes to `open(...)`

Comments:

Patch:
diff --git a/pyupgrade/_plugins/open_mode.py b/pyupgrade/_plugins/open_mode.py
index fb3f3b6..bc3169a 100644
--- a/pyupgrade/_plugins/open_mode.py
+++ b/pyupgrade/_plugins/open_mode.py
@@ -55,8 +55,17 @@ def visit_Call(
 ) -> Iterable[tuple[Offset, TokenFunc]]:
     if (
             state.settings.min_version >= (3,) and
-            isinstance(node.func, ast.Name) and
-            node.func.id == 'open' and
+            (
+                (
+                    isinstance(node.func, ast.Name) and
+                    node.func.id == 'open'
+                ) or (
+                    isinstance(node.func, ast.Attribute) and
+                    isinstance(node.func.value, ast.Name) and
+                    node.func.value.id == 'io' and
+                    node.func.attr == 'open'
+                )
+            ) and
             not has_starargs(node)
     ):
         if len(node.args) >= 2 and isinstance(node.args[1], ast.Str):


Test patch:
diff --git a/tests/features/open_mode_test.py b/tests/features/open_mode_test.py
index 9fafc7c..32a2fd1 100644
--- a/tests/features/open_mode_test.py
+++ b/tests/features/open_mode_test.py
@@ -65,6 +65,11 @@ def test_fix_open_mode_noop(s):
             'open(encoding="UTF-8", file="t.py")',
         ),
         pytest.param('open(f, u"r")', 'open(f)', id='string with u flag'),
+        pytest.param(
+            'io.open("foo", "r")',
+            'open("foo")',
+            id='io.open also rewrites modes in a single pass',
+        ),
     ),
 )
 def test_fix_open_mode(s, expected):
Repository: asottile/pyupgrade

Issue:
pyupgrade is introducing a raw string where a line continuation is being used.
Hi.

The pyupgrade tool is introducing a raw string where a line continuation is being used.

Check this example below:
```python
import textwrap


def test_importerror(tmpdir):
    print(tmpdir)
    p1 = tmpdir.join("test1.txt")
    p2 = tmpdir.join("test2.txt")
    p1.write(
        textwrap.dedent(
            """\
        import doesnotexist

        x = 1
        """
        )
    )
    p2.write(
        textwrap.dedent(
            """
        import doesnotexist

        x = 2
        """
        )
    )

```

after running pyupgrade on this file, a `r"""` is introduced as it's demonstrated above.

```python
def test_importerror(tmpdir):
    print(tmpdir)
    p1 = tmpdir.join("test1.txt")
    p2 = tmpdir.join("test2.txt")
    p1.write(
        textwrap.dedent(
            r"""\
        import doesnotexist

        x = 1
        """
        )
    )
    p2.write(
        textwrap.dedent(
            """
        import doesnotexist

        x = 2
        """
        )
    )
```

I'm running `pyupgrade (1.10.0)` with `python Python 3.6.2` on `Windows 10, version 1709`

Comments:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index d6a196c..928b97e 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -437,7 +437,7 @@ def _fix_unicode_literals(contents_text, py3_plus):
 
 # https://docs.python.org/3/reference/lexical_analysis.html
 ESCAPE_STARTS = frozenset((
-    '\n', '\\', "'", '"', 'a', 'b', 'f', 'n', 'r', 't', 'v',
+    '\n', '\r', '\\', "'", '"', 'a', 'b', 'f', 'n', 'r', 't', 'v',
     '0', '1', '2', '3', '4', '5', '6', '7',  # octal escapes
     'x',  # hex escapes
     # only valid in non-bytestrings


Test patch:
diff --git a/tests/pyupgrade_test.py b/tests/pyupgrade_test.py
index 82acd34..c571357 100644
--- a/tests/pyupgrade_test.py
+++ b/tests/pyupgrade_test.py
@@ -343,6 +343,8 @@ def test_unicode_literals(s, py3_plus, expected):
         '"\\u2603"',
         # don't touch already valid escapes
         r'"\r\n"',
+        # don't touch escaped newlines
+        '"""\\\n"""', '"""\\\r\n"""', '"""\\\r"""',
     ),
 )
 def test_fix_escape_sequences_noop(s):
Repository: asottile/pyupgrade

Issue:
Behaviour change after upgrading '%9s' to '{:9}'
This pyupgrade change (https://github.com/hugovk/yamllint/commit/25e8e9be0838bff930b3b204360fc24385b6edbf) fails indentation-related unit tests which previously passed:

```diff
-            output += '%9s %s\n' % (token_type,
+            output += '{:9} {}\n'.format(token_type,
                                     self.format_stack(context['stack']))
```


https://travis-ci.org/hugovk/yamllint/jobs/459076859#L860

Small example:

```python
Python 3.7.1 (default, Nov  6 2018, 18:45:35)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> '%9s %s\n' % ("abc", "def")
'      abc def\n'
>>> '{:9} {}\n'.format("abc", "def")
'abc       def\n'
```

It would have worked if they were integers:
```python
>>> '%9s %s\n' % (123, 456)
'      123 456\n'
>>> '{:9} {}\n'.format(123, 456)
'      123 456\n'
```

---

Another issue with this example, it'd be nice if the indentation of the second line was adjusted, as this often causes flake8 errors. But I understand if this is out of scope of pyupgrade.

For example, instead of this:

```diff
-            output += '%9s %s\n' % (token_type,
+            output += '{:9} {}\n'.format(token_type,
                                     self.format_stack(context['stack']))
```

Do this:

```diff
-            output += '%9s %s\n' % (token_type,
-                                    self.format_stack(context['stack']))
+            output += '{:9} {}\n'.format(token_type,
+                                         self.format_stack(context['stack']))
```



Comments:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index 66e1094..18345dc 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -609,6 +609,8 @@ def _percent_to_format(s):
             if conversion == '%':
                 return s + '%'
             parts = [s, '{']
+            if width and conversion == 's' and not conversion_flag:
+                conversion_flag = '>'
             if conversion == 's':
                 conversion = None
             if key:


Test patch:
diff --git a/tests/pyupgrade_test.py b/tests/pyupgrade_test.py
index ad9d751..3818f20 100644
--- a/tests/pyupgrade_test.py
+++ b/tests/pyupgrade_test.py
@@ -615,6 +615,7 @@ def test_percent_format_noop_if_bug_16806():
         ('"%s%% percent" % (15,)', '"{}% percent".format(15)'),
         ('"%3f" % (15,)', '"{:3f}".format(15)'),
         ('"%-5s" % ("hi",)', '"{:<5}".format("hi")'),
+        ('"%9s" % (5,)', '"{:>9}".format(5)'),
         ('"brace {} %s" % (1,)', '"brace {{}} {}".format(1)'),
         (
             '"%s" % (\n'
Repository: asottile/pyupgrade

Issue:
Error after upgrading a formatted sting starting with a variable
If I run pyupgrade on this string : 
  **'{}\N{NO-BREAK SPACE}'.format(currency.symbol or "")**
I get this line as an output : 
"\N{NO-BREAK SPACE}".format(currency.symbol or "") 
Wich causes an error 

Comments:

Patch:
diff --git a/pyupgrade/_string_helpers.py b/pyupgrade/_string_helpers.py
index 4271839..1c15655 100644
--- a/pyupgrade/_string_helpers.py
+++ b/pyupgrade/_string_helpers.py
@@ -19,7 +19,7 @@ def parse_format(s: str) -> list[DotFormatPart]:
 
     for part in NAMED_UNICODE_RE.split(s):
         if NAMED_UNICODE_RE.fullmatch(part):
-            if not ret:
+            if not ret or ret[-1][1:] != (None, None, None):
                 ret.append((part, None, None, None))
             else:
                 ret[-1] = (ret[-1][0] + part, None, None, None)


Test patch:
diff --git a/tests/features/format_literals_test.py b/tests/features/format_literals_test.py
index aaa6ef0..837e593 100644
--- a/tests/features/format_literals_test.py
+++ b/tests/features/format_literals_test.py
@@ -25,6 +25,8 @@ from pyupgrade._main import _fix_tokens
         '("{0}" # {1}\n"{2}").format(1, 2, 3)',
         # don't touch f-strings (these are wrong but don't make it worse)
         'f"{0}".format(a)',
+        # shouldn't touch the format spec
+        r'"{}\N{SNOWMAN}".format("")',
     ),
 )
 def test_format_literals_noop(s):