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 <[email protected]>
+
+- 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]]})',