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-02 18:36:06
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyupgrade (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyupgrade.new.1947 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyupgrade"

Sun May  2 18:36:06 2021 rev:8 rq:889751 version:2.13.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyupgrade/python-pyupgrade.changes        
2021-04-01 14:18:32.080094240 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-pyupgrade.new.1947/python-pyupgrade.changes  
    2021-05-02 18:39:14.108150348 +0200
@@ -1,0 +2,12 @@
+Sat Apr 24 18:16:48 UTC 2021 - Sebastian Wagner <sebix+novell....@sebix.at>
+
+- Update to version 2.13.0:
+ - move pep563 rewrite to py311
+- Update to version 2.12.0:
+ - document annotation unquote
+ - use dict comprehension
+ - rewrite typeddict even with total= option
+ - Update azure-pipelines template repositories
+ - _to_fstring: Use original token stream instead of unparsed AST
+
+-------------------------------------------------------------------

Old:
----
  python-pyupgrade-2.11.0.tar.gz

New:
----
  python-pyupgrade-2.13.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pyupgrade.spec ++++++
--- /var/tmp/diff_new_pack.6RqbS3/_old  2021-05-02 18:39:14.592148285 +0200
+++ /var/tmp/diff_new_pack.6RqbS3/_new  2021-05-02 18:39:14.596148268 +0200
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-pyupgrade
-Version:        2.11.0
+Version:        2.13.0
 Release:        0
 Summary:        A tool to automatically upgrade syntax for newer versions
 License:        MIT

++++++ python-pyupgrade-2.11.0.tar.gz -> python-pyupgrade-2.13.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyupgrade-2.11.0/.pre-commit-config.yaml 
new/pyupgrade-2.13.0/.pre-commit-config.yaml
--- old/pyupgrade-2.11.0/.pre-commit-config.yaml        2021-03-20 
22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/.pre-commit-config.yaml        2021-04-20 
23:21:45.000000000 +0200
@@ -14,13 +14,13 @@
     rev: v1.17.0
     hooks:
     -   id: setup-cfg-fmt
--   repo: https://gitlab.com/pycqa/flake8
-    rev: 3.9.0
+-   repo: https://github.com/PyCQA/flake8
+    rev: 3.9.1
     hooks:
     -   id: flake8
         additional_dependencies: [flake8-typing-imports==1.7.0]
 -   repo: https://github.com/pre-commit/mirrors-autopep8
-    rev: v1.5.5
+    rev: v1.5.6
     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.11.0
+    rev: v2.13.0
     hooks:
     -   id: pyupgrade
         args: [--py36-plus]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyupgrade-2.11.0/README.md 
new/pyupgrade-2.13.0/README.md
--- old/pyupgrade-2.11.0/README.md      2021-03-20 22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/README.md      2021-04-20 23:21:45.000000000 +0200
@@ -20,7 +20,7 @@
 
 ```yaml
 -   repo: https://github.com/asottile/pyupgrade
-    rev: v2.11.0
+    rev: v2.13.0
     hooks:
     -   id: pyupgrade
 ```
@@ -505,6 +505,7 @@
      ...
 ```
 
+
 ### pep 604 typing rewrites
 
 Availability:
@@ -523,3 +524,15 @@
 +def f() -> int | str:
      ...
 ```
+
+
+### remove quoted annotations
+
+Availability:
+- File imports `from __future__ import annotations`
+- `--py311-plus` is passed on the commandline.
+
+```diff
+-def f(x: 'queue.Queue[int]') -> C:
++def f(x: queue.Queue[int]) -> C:
+```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyupgrade-2.11.0/azure-pipelines.yml 
new/pyupgrade-2.13.0/azure-pipelines.yml
--- old/pyupgrade-2.11.0/azure-pipelines.yml    2021-03-20 22:13:59.000000000 
+0100
+++ new/pyupgrade-2.13.0/azure-pipelines.yml    2021-04-20 23:21:45.000000000 
+0200
@@ -10,7 +10,7 @@
       type: github
       endpoint: github
       name: asottile/azure-pipeline-templates
-      ref: refs/tags/v2.0.0
+      ref: refs/tags/v2.1.0
 
 jobs:
 - template: job--python-tox.yml@asottile
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyupgrade-2.11.0/pyupgrade/_main.py 
new/pyupgrade-2.13.0/pyupgrade/_main.py
--- old/pyupgrade-2.11.0/pyupgrade/_main.py     2021-03-20 22:13:59.000000000 
+0100
+++ new/pyupgrade-2.13.0/pyupgrade/_main.py     2021-04-20 23:21:45.000000000 
+0200
@@ -35,8 +35,8 @@
 from pyupgrade._token_helpers import CLOSING
 from pyupgrade._token_helpers import KEYWORDS
 from pyupgrade._token_helpers import OPENING
+from pyupgrade._token_helpers import parse_call_args
 from pyupgrade._token_helpers import remove_brace
-from pyupgrade._token_helpers import victims
 
 DotFormatPart = Tuple[str, Optional[str], Optional[str], Optional[str]]
 
@@ -510,26 +510,12 @@
     return tokens_to_src(tokens).lstrip()
 
 
-def _simple_arg(arg: ast.expr) -> bool:
-    return (
-        isinstance(arg, ast.Name) or
-        (isinstance(arg, ast.Attribute) and _simple_arg(arg.value)) or
-        (
-            isinstance(arg, ast.Call) and
-            _simple_arg(arg.func) and
-            not arg.args and not arg.keywords
-        )
-    )
-
-
-def _format_params(call: ast.Call) -> Dict[str, str]:
-    params = {}
-    for i, arg in enumerate(call.args):
-        params[str(i)] = _unparse(arg)
+def _format_params(call: ast.Call) -> Set[str]:
+    params = {str(i) for i, arg in enumerate(call.args)}
     for kwd in call.keywords:
         # kwd.arg can't be None here because we exclude starargs
         assert kwd.arg is not None
-        params[kwd.arg] = _unparse(kwd.value)
+        params.add(kwd.arg)
     return params
 
 
@@ -568,8 +554,6 @@
                 isinstance(node.func, ast.Attribute) and
                 isinstance(node.func.value, ast.Str) and
                 node.func.attr == 'format' and
-                all(_simple_arg(arg) for arg in node.args) and
-                all(_simple_arg(k.value) for k in node.keywords) and
                 not has_starargs(node)
         ):
             return None
@@ -646,7 +630,11 @@
                         'TypedDict',
                     ) and
                     len(node.value.args) == 1 and
-                    len(node.value.keywords) > 0
+                    len(node.value.keywords) > 0 and
+                    not any(
+                        keyword.arg == 'total'
+                        for keyword in node.value.keywords
+                    )
             ):
                 self.kw_typed_dicts[ast_to_offset(node)] = node.value
             elif (
@@ -656,7 +644,17 @@
                         'TypedDict',
                     ) and
                     len(node.value.args) == 2 and
-                    not node.value.keywords and
+                    (
+                        not node.value.keywords or
+                        (
+                            len(node.value.keywords) == 1 and
+                            node.value.keywords[0].arg == 'total' and
+                            isinstance(
+                                node.value.keywords[0].value,
+                                (ast.Constant, ast.NameConstant),
+                            )
+                        )
+                    ) and
                     isinstance(node.value.args[1], ast.Dict) and
                     node.value.args[1].keys and
                     all(
@@ -676,8 +674,6 @@
         return node.id
     elif isinstance(node, ast.Attribute):
         return ''.join((_unparse(node.value), '.', node.attr))
-    elif isinstance(node, ast.Call):
-        return '{}()'.format(_unparse(node.func))
     elif isinstance(node, ast.Subscript):
         if sys.version_info >= (3, 9):  # pragma: no cover (py39+)
             node_slice: ast.expr = node.slice
@@ -692,7 +688,7 @@
                 slice_s = ', '.join(_unparse(elt) for elt in node_slice.elts)
         else:
             slice_s = _unparse(node_slice)
-        return '{}[{}]'.format(_unparse(node.value), slice_s)
+        return f'{_unparse(node.value)}[{slice_s}]'
     elif isinstance(node, ast.Str):
         return repr(node.s)
     elif isinstance(node, ast.Ellipsis):
@@ -705,8 +701,28 @@
         raise NotImplementedError(ast.dump(node))
 
 
-def _to_fstring(src: str, call: ast.Call) -> str:
-    params = _format_params(call)
+def _skip_unimportant_ws(tokens: List[Token], i: int) -> int:
+    while tokens[i].name == 'UNIMPORTANT_WS':
+        i += 1
+    return i
+
+
+def _to_fstring(
+    src: str, tokens: List[Token], args: List[Tuple[int, int]],
+) -> str:
+    params = {}
+    i = 0
+    for start, end in args:
+        start = _skip_unimportant_ws(tokens, start)
+        if tokens[start].name == 'NAME':
+            after = _skip_unimportant_ws(tokens, start + 1)
+            if tokens[after].src == '=':  # keyword argument
+                params[tokens[start].src] = tokens_to_src(
+                    tokens[after + 1:end],
+                ).strip()
+                continue
+        params[str(i)] = tokens_to_src(tokens[start:end]).strip()
+        i += 1
 
     parts = []
     i = 0
@@ -720,12 +736,12 @@
     return unparse_parsed_string(parts)
 
 
-def _replace_typed_class(
+def _typed_class_replacement(
         tokens: List[Token],
         i: int,
         call: ast.Call,
         types: Dict[str, ast.expr],
-) -> None:
+) -> Tuple[int, str]:
     if i > 0 and tokens[i - 1].name in {'INDENT', UNIMPORTANT_WS}:
         indent = f'{tokens[i - 1].src}{" " * 4}'
     else:
@@ -738,8 +754,7 @@
         end += 1
 
     attrs = '\n'.join(f'{indent}{k}: {_unparse(v)}' for k, v in types.items())
-    src = f'class {tokens[i].src}({_unparse(call.func)}):\n{attrs}'
-    tokens[i:end] = [Token('CODE', src)]
+    return end, attrs
 
 
 def _fix_py36_plus(contents_text: str) -> str:
@@ -765,8 +780,6 @@
         return contents_text
     for i, token in reversed_enumerate(tokens):
         if token.offset in visitor.fstrings:
-            node = visitor.fstrings[token.offset]
-
             # TODO: handle \N escape sequences
             if r'\N' in token.src:
                 continue
@@ -775,29 +788,37 @@
             if tokens_to_src(tokens[i + 1:paren + 1]) != '.format(':
                 continue
 
-            # we don't actually care about arg position, so we pass `node`
-            fmt_victims = victims(tokens, paren, node, gen=False)
-            end = fmt_victims.ends[-1]
+            args, end = parse_call_args(tokens, paren)
             # if it spans more than one line, bail
-            if tokens[end].line != token.line:
+            if tokens[end - 1].line != token.line:
+                continue
+
+            args_src = tokens_to_src(tokens[paren:end])
+            if '\\' in args_src or '"' in args_src or "'" in args_src:
                 continue
 
-            tokens[i] = token._replace(src=_to_fstring(token.src, node))
-            del tokens[i + 1:end + 1]
+            tokens[i] = token._replace(
+                src=_to_fstring(token.src, tokens, args),
+            )
+            del tokens[i + 1:end]
         elif token.offset in visitor.named_tuples and token.name == 'NAME':
             call = visitor.named_tuples[token.offset]
             types: Dict[str, ast.expr] = {
                 tup.elts[0].s: tup.elts[1]  # type: ignore  # (checked above)
                 for tup in call.args[1].elts  # type: ignore  # (checked above)
             }
-            _replace_typed_class(tokens, i, call, types)
+            end, attrs = _typed_class_replacement(tokens, i, call, types)
+            src = f'class {tokens[i].src}({_unparse(call.func)}):\n{attrs}'
+            tokens[i:end] = [Token('CODE', src)]
         elif token.offset in visitor.kw_typed_dicts and token.name == 'NAME':
             call = visitor.kw_typed_dicts[token.offset]
             types = {
                 arg.arg: arg.value  # type: ignore  # (checked above)
                 for arg in call.keywords
             }
-            _replace_typed_class(tokens, i, call, types)
+            end, attrs = _typed_class_replacement(tokens, i, call, types)
+            src = f'class {tokens[i].src}({_unparse(call.func)}):\n{attrs}'
+            tokens[i:end] = [Token('CODE', src)]
         elif token.offset in visitor.dict_typed_dicts and token.name == 'NAME':
             call = visitor.dict_typed_dicts[token.offset]
             types = {
@@ -807,7 +828,20 @@
                     call.args[1].values,  # type: ignore  # (checked above)
                 )
             }
-            _replace_typed_class(tokens, i, call, types)
+            if call.keywords:
+                total = call.keywords[0].value.value  # type: ignore # 
(checked above)  # noqa: E501
+                end, attrs = _typed_class_replacement(tokens, i, call, types)
+                src = (
+                    f'class {tokens[i].src}('
+                    f'{_unparse(call.func)}, total={total}'
+                    f'):\n'
+                    f'{attrs}'
+                )
+                tokens[i:end] = [Token('CODE', src)]
+            else:
+                end, attrs = _typed_class_replacement(tokens, i, call, types)
+                src = f'class {tokens[i].src}({_unparse(call.func)}):\n{attrs}'
+                tokens[i:end] = [Token('CODE', src)]
 
     return tokens_to_src(tokens)
 
@@ -882,6 +916,10 @@
         '--py310-plus',
         action='store_const', dest='min_version', const=(3, 10),
     )
+    parser.add_argument(
+        '--py311-plus',
+        action='store_const', dest='min_version', const=(3, 11),
+    )
     args = parser.parse_args(argv)
 
     ret = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pyupgrade-2.11.0/pyupgrade/_plugins/oserror_aliases.py 
new/pyupgrade-2.13.0/pyupgrade/_plugins/oserror_aliases.py
--- old/pyupgrade-2.11.0/pyupgrade/_plugins/oserror_aliases.py  2021-03-20 
22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/pyupgrade/_plugins/oserror_aliases.py  2021-04-20 
23:21:45.000000000 +0200
@@ -62,7 +62,7 @@
     if len(unique_args) > 1:
         joined = '({})'.format(', '.join(unique_args))
     elif tokens[start - 1].name != 'UNIMPORTANT_WS':
-        joined = ' {}'.format(unique_args[0])
+        joined = f' {unique_args[0]}'
     else:
         joined = unique_args[0]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyupgrade-2.11.0/pyupgrade/_plugins/typing_pep563.py 
new/pyupgrade-2.13.0/pyupgrade/_plugins/typing_pep563.py
--- old/pyupgrade-2.11.0/pyupgrade/_plugins/typing_pep563.py    2021-03-20 
22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/pyupgrade/_plugins/typing_pep563.py    2021-04-20 
23:21:45.000000000 +0200
@@ -18,7 +18,7 @@
 
 def _supported_version(state: State) -> bool:
     return (
-        state.settings.min_version >= (3, 10) or
+        state.settings.min_version >= (3, 11) or
         'annotations' in state.from_imports['__future__']
     )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyupgrade-2.11.0/setup.cfg 
new/pyupgrade-2.13.0/setup.cfg
--- old/pyupgrade-2.11.0/setup.cfg      2021-03-20 22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/setup.cfg      2021-04-20 23:21:45.000000000 +0200
@@ -1,6 +1,6 @@
 [metadata]
 name = pyupgrade
-version = 2.11.0
+version = 2.13.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.11.0/tests/features/fstrings_test.py 
new/pyupgrade-2.13.0/tests/features/fstrings_test.py
--- old/pyupgrade-2.11.0/tests/features/fstrings_test.py        2021-03-20 
22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/tests/features/fstrings_test.py        2021-04-20 
23:21:45.000000000 +0200
@@ -30,6 +30,10 @@
         r'"\N{snowman} {}".format(a)',
         # not enough placeholders / placeholders missing
         '"{}{}".format(a)', '"{a}{b}".format(a=a)',
+        # backslashes and quotes cannot nest
+        r'''"{}".format(a['\\'])''',
+        '"{}".format(a["b"])',
+        "'{}'.format(a['b'])",
     ),
 )
 def test_fix_fstrings_noop(s):
@@ -50,6 +54,7 @@
         ('"hello {}!".format(name)', 'f"hello {name}!"'),
         ('"{}{{}}{}".format(escaped, y)', 'f"{escaped}{{}}{y}"'),
         ('"{}{b}{}".format(a, c, b=b)', 'f"{a}{b}{c}"'),
+        ('"{}".format(0x0)', 'f"{0x0}"'),
         # TODO: poor man's f-strings?
         # '"{foo}".format(**locals())'
     ),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pyupgrade-2.11.0/tests/features/typing_pep563_test.py 
new/pyupgrade-2.13.0/tests/features/typing_pep563_test.py
--- old/pyupgrade-2.11.0/tests/features/typing_pep563_test.py   2021-03-20 
22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/tests/features/typing_pep563_test.py   2021-04-20 
23:21:45.000000000 +0200
@@ -13,7 +13,7 @@
             'from typing import Literal\n'
             'x: "str"\n',
             (2, 7),
-            id='not python 3.10+',
+            id='not python 3.11+',
         ),
         pytest.param(
             'from __future__ import annotations\n'
@@ -346,6 +346,11 @@
     assert ret == expected
 
 
+def test_replaced_for_minimum_version():
+    ret = _fix_plugins('x: "int"', settings=Settings(min_version=(3, 11)))
+    assert ret == 'x: int'
+
+
 @pytest.mark.xfail(
     sys.version_info < (3, 8),
     reason='posonly args not available in Python3.7',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pyupgrade-2.11.0/tests/features/typing_typed_dict_test.py 
new/pyupgrade-2.13.0/tests/features/typing_typed_dict_test.py
--- old/pyupgrade-2.11.0/tests/features/typing_typed_dict_test.py       
2021-03-20 22:13:59.000000000 +0100
+++ new/pyupgrade-2.13.0/tests/features/typing_typed_dict_test.py       
2021-04-20 23:21:45.000000000 +0200
@@ -39,6 +39,10 @@
             'D = typing.TypedDict("D", **types)',
             id='starstarkwargs',
         ),
+        pytest.param(
+            'D = typing.TypedDict("D", x=int, total=False)',
+            id='kw_typed_dict with total',
+        ),
     ),
 )
 def test_typing_typed_dict_noop(s):
@@ -79,6 +83,16 @@
             id='TypedDict from dict literal',
         ),
         pytest.param(
+            'import typing\n'
+            'D = typing.TypedDict("D", {"a": int}, total=False)\n',
+
+            'import typing\n'
+            'class D(typing.TypedDict, total=False):\n'
+            '    a: int\n',
+
+            id='TypedDict from dict literal with total',
+        ),
+        pytest.param(
             'from typing_extensions import TypedDict\n'
             'D = TypedDict("D", a=int)\n',
 
@@ -99,6 +113,16 @@
             id='keyword TypedDict from typing_extensions',
         ),
         pytest.param(
+            'import typing_extensions\n'
+            'D = typing_extensions.TypedDict("D", {"a": int}, total=True)\n',
+
+            'import typing_extensions\n'
+            'class D(typing_extensions.TypedDict, total=True):\n'
+            '    a: int\n',
+
+            id='keyword TypedDict from typing_extensions, with total',
+        ),
+        pytest.param(
             'from typing import List\n'
             'from typing_extensions import TypedDict\n'
             'Foo = TypedDict("Foo", {"lsts": List[List[int]]})',

Reply via email to