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 2022-01-06 15:51:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyupgrade (Old) and /work/SRC/openSUSE:Factory/.python-pyupgrade.new.1896 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyupgrade" Thu Jan 6 15:51:19 2022 rev:21 rq:944190 version:2.31.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyupgrade/python-pyupgrade.changes 2021-11-20 02:39:56.684620864 +0100 +++ /work/SRC/openSUSE:Factory/.python-pyupgrade.new.1896/python-pyupgrade.changes 2022-01-06 15:52:04.152997148 +0100 @@ -1,0 +2,32 @@ +Mon Jan 3 11:37:47 UTC 2022 - Sebastian Wagner <sebix+novell....@sebix.at> + +- - update to version 2.31.0: + - rewrite string formatting with **locals() +- update to version 2.30.1: + - don't rewrite six.reraise with named args +- update to version 2.30.0: + - rewrite abspath(__file__) to __file__ in py39+ + - fix __path__ type annotation + - fix the diff output of `forced str("native") literals` section + - improve coverage pragmas with covdefaults 2.1 + - Use org-default .github/FUNDING.yml + Committed via https://github.com/asottile/all-repos + - Rewrite docs examples with commented code to use diffs + +------------------------------------------------------------------- +Tue Dec 28 16:15:14 UTC 2021 - Sebastian Wagner <sebix+novell....@sebix.at> + +- - update to version 2.30.0: + - rewrite abspath(__file__) to __file__ in py39+ + - fix __path__ type annotation + - fix the diff output of `forced str("native") literals` section + - improve coverage pragmas with covdefaults 2.1 + - Use org-default .github/FUNDING.yml + Committed via https://github.com/asottile/all-repos + - Rewrite docs examples with commented code to use diffs +- update to version 2.29.1: + - prevent rewriting union types with forward annotations + - replace exit(main()) with raise SystemExit(main()) + Committed via https://github.com/asottile/all-repos + +------------------------------------------------------------------- Old: ---- python-pyupgrade-2.29.1.tar.gz New: ---- python-pyupgrade-2.31.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyupgrade.spec ++++++ --- /var/tmp/diff_new_pack.knMMx4/_old 2022-01-06 15:52:04.476997326 +0100 +++ /var/tmp/diff_new_pack.knMMx4/_new 2022-01-06 15:52:04.484997330 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-pyupgrade # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-pyupgrade -Version: 2.29.1 +Version: 2.31.0 Release: 0 Summary: A tool to automatically upgrade syntax for newer versions License: MIT ++++++ python-pyupgrade-2.29.1.tar.gz -> python-pyupgrade-2.31.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/.github/FUNDING.yml new/pyupgrade-2.31.0/.github/FUNDING.yml --- old/pyupgrade-2.29.1/.github/FUNDING.yml 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/.github/FUNDING.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -github: asottile diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/.pre-commit-config.yaml new/pyupgrade-2.31.0/.pre-commit-config.yaml --- old/pyupgrade-2.29.1/.pre-commit-config.yaml 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/.pre-commit-config.yaml 2022-01-01 01:43:09.000000000 +0100 @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.1.0 hooks: - id: check-docstring-first - id: check-yaml @@ -11,7 +11,7 @@ - id: requirements-txt-fixer - id: trailing-whitespace - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.19.0 + rev: v1.20.0 hooks: - id: setup-cfg-fmt - repo: https://github.com/PyCQA/flake8 @@ -29,16 +29,16 @@ - id: reorder-python-imports args: [--py3-plus] - repo: https://github.com/asottile/add-trailing-comma - rev: v2.2.0 + rev: v2.2.1 hooks: - id: add-trailing-comma args: [--py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 + rev: v2.31.0 hooks: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.910-1 + rev: v0.930 hooks: - id: mypy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/README.md new/pyupgrade-2.31.0/README.md --- old/pyupgrade-2.29.1/README.md 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/README.md 2022-01-01 01:43:09.000000000 +0100 @@ -20,7 +20,7 @@ ```yaml - repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 + rev: v2.31.0 hooks: - id: pyupgrade ``` @@ -29,38 +29,53 @@ ### Set literals -```python -set(()) # set() -set([]) # set() -set((1,)) # {1} -set((1, 2)) # {1, 2} -set([1, 2]) # {1, 2} -set(x for x in y) # {x for x in y} -set([x for x in y]) # {x for x in y} +```diff +-set(()) ++set() +-set([]) ++set() +-set((1,)) ++{1} +-set((1, 2)) ++{1, 2} +-set([1, 2]) ++{1, 2} +-set(x for x in y) ++{x for x in y} +-set([x for x in y]) ++{x for x in y} ``` ### Dictionary comprehensions -```python -dict((a, b) for a, b in y) # {a: b for a, b in y} -dict([(a, b) for a, b in y]) # {a: b for a, b in y} +```diff +-dict((a, b) for a, b in y) ++{a: b for a, b in y} +-dict([(a, b) for a, b in y]) ++{a: b for a, b in y} ``` ### Generator expressions for some built-in functions (pep 289) -```python -min([i for i in range(3)]) # min(i for i in range(3)) -max([i for i in range(3)]) # max(i for i in range(3)) -sum([i for i in range(3)]) # sum(i for i in range(3)) -''.join([str(i) for i in range(3)]) # ''.join(str(i) for i in range(3)) +```diff +-min([i for i in range(3)]) ++min(i for i in range(3)) +-max([i for i in range(3)]) ++max(i for i in range(3)) +-sum([i for i in range(3)]) ++sum(i for i in range(3)) +-''.join([str(i) for i in range(3)]) ++''.join(str(i) for i in range(3)) ``` ### Python2.7+ Format Specifiers -```python -'{0} {1}'.format(1, 2) # '{} {}'.format(1, 2) -'{0}' '{1}'.format(1, 2) # '{}' '{}'.format(1, 2) +```diff +-'{0} {1}'.format(1, 2) ++'{} {}'.format(1, 2) +-'{0}' '{1}'.format(1, 2) ++'{}' '{}'.format(1, 2) ``` ### printf-style string formatting @@ -68,10 +83,13 @@ Availability: - Unless `--keep-percent-format` is passed. -```python -'%s %s' % (a, b) # '{} {}'.format(a, b) -'%r %2f' % (a, b) # '{!r} {:2f}'.format(a, b) -'%(a)s %(b)s' % {'a': 1, 'b': 2} # '{a} {b}'.format(a=1, b=2) +```diff +-'%s %s' % (a, b) ++'{} {}'.format(a, b) +-'%r %2f' % (a, b) ++'{!r} {:2f}'.format(a, b) +-'%(a)s %(b)s' % {'a': 1, 'b': 2} ++'{a} {b}'.format(a=1, b=2) ``` ### Unicode literals @@ -80,24 +98,30 @@ - File imports `from __future__ import unicode_literals` - `--py3-plus` is passed on the commandline. -```python -u'foo' # 'foo' -u"foo" # 'foo' -u'''foo''' # '''foo''' +```diff +-u'foo' ++'foo' +-u"foo" ++'foo' +-u'''foo''' ++'''foo''' ``` ### Invalid escape sequences -```python -# strings with only invalid sequences become raw strings -'\d' # r'\d' -# strings with mixed valid / invalid sequences get escaped -'\n\d' # '\n\\d' -# `ur` is not a valid string prefix in python3 -u'\d' # u'\\d' - -# this fixes a syntax error in python3.3+ -'\N' # r'\N' +```diff + # strings with only invalid sequences become raw strings +-'\d' ++r'\d' + # strings with mixed valid / invalid sequences get escaped +-'\n\d' ++'\n\\d' + # `ur` is not a valid string prefix in python3 +-u'\d' ++u'\\d' + # this fixes a syntax error in python3.3+ +-'\N' ++r'\N' # note: pyupgrade is timid in one case (that's usually a mistake) # in python2.x `'\u2603'` is the same as `'\\u2603'` without `unicode_literals` @@ -109,58 +133,80 @@ In python3.8+, comparison to literals becomes a `SyntaxWarning` as the success of those comparisons is implementation specific (due to common object caching). -```python -x is 5 # x == 5 -x is not 5 # x != 5 -x is 'foo' # x == 'foo' +```diff +-x is 5 ++x == 5 +-x is not 5 ++x != 5 +-x is 'foo' ++x == 'foo' ``` ### `ur` string literals `ur'...'` literals are not valid in python 3.x -```python -ur'foo' # u'foo' -ur'\s' # u'\\s' -# unicode escapes are left alone -ur'\u2603' # u'\u2603' -ur'\U0001f643' # u'\U0001f643' +```diff +-ur'foo' ++u'foo' +-ur'\s' ++u'\\s' + # unicode escapes are left alone +-ur'\u2603' ++u'\u2603' +-ur'\U0001f643' ++u'\U0001f643' ``` ### `.encode()` to bytes literals -```python -'foo'.encode() # b'foo' -'foo'.encode('ascii') # b'foo' -'foo'.encode('utf-8') # b'foo' -u'foo'.encode() # b'foo' -'\xa0'.encode('latin1') # b'\xa0' +```diff +-'foo'.encode() ++b'foo' +-'foo'.encode('ascii') ++b'foo' +-'foo'.encode('utf-8') ++b'foo' +-u'foo'.encode() ++b'foo' +-'\xa0'.encode('latin1') ++b'\xa0' ``` ### Long literals -```python -5L # 5 -5l # 5 -123456789123456789123456789L # 123456789123456789123456789 +```diff +-5L ++5 +-5l ++5 +-123456789123456789123456789L ++123456789123456789123456789 ``` ### Octal literals -``` -0755 # 0o755 -05 # 5 +```diff +-0755 ++0o755 +-05 ++5 ``` ### extraneous parens in `print(...)` A fix for [python-modernize/python-modernize#178] -```python -print(()) # ok: printing an empty tuple -print((1,)) # ok: printing a tuple -sum((i for i in range(3)), []) # ok: parenthesized generator argument -print(("foo")) # print("foo") +```diff + # ok: printing an empty tuple + print(()) + # ok: printing a tuple + print((1,)) + # ok: parenthesized generator argument + sum((i for i in range(3)), []) + # fixed: +-print(("foo")) ++print("foo") ``` [python-modernize/python-modernize#178]: https://github.com/python-modernize/python-modernize/issues/178 @@ -189,10 +235,11 @@ Availability: - `--py3-plus` is passed on the commandline. -```python -class C(Base): - def f(self): - super(C, self).f() # super().f() +```diff + class C(Base): + def f(self): +- super(C, self).f() ++ super().f() ``` ### "new style" classes @@ -202,15 +249,18 @@ #### rewrites class declaration -```python -class C(object): pass # class C: pass -class C(B, object): pass # class C(B): pass +```diff +-class C(object): pass ++class C: pass +-class C(B, object): pass ++class C(B): pass ``` #### removes `__metaclass__ = type` declaration ```diff --__metaclass__ = type + class C: +- __metaclass__ = type ``` ### forced `str("native")` literals @@ -218,9 +268,11 @@ Availability: - `--py3-plus` is passed on the commandline. -```python -str() # "''" -str("foo") # "foo" +```diff +-str() ++'' +-str("foo") ++"foo" ``` ### `.encode("utf-8")` @@ -228,8 +280,9 @@ Availability: - `--py3-plus` is passed on the commandline. -```python -"foo".encode("utf-8") # "foo".encode() +```diff +-"foo".encode("utf-8") ++"foo".encode() ``` ### `# coding: ...` comment @@ -285,13 +338,14 @@ Availability: - `--py3-plus` is passed on the commandline. -```python -def f(): - for x in y: # yield from y - yield x - - for a, b in c: # yield from c - yield (a, b) +```diff + def f(): +- for x in y: +- yield x ++ yield from y +- for a, b in c: +- yield (a, b) ++ yield from c ``` ### Python2 and old Python3.x blocks @@ -351,74 +405,127 @@ Availability: - `--py3-plus` is passed on the commandline. -```python -six.text_type # str -six.binary_type # bytes -six.class_types # (type,) -six.string_types # (str,) -six.integer_types # (int,) -six.unichr # chr -six.iterbytes # iter -six.print_(...) # print(...) -six.exec_(c, g, l) # exec(c, g, l) -six.advance_iterator(it) # next(it) -six.next(it) # next(it) -six.callable(x) # callable(x) -six.moves.range(x) # range(x) -six.moves.xrange(x) # range(x) - -from six import text_type -text_type # str - -@six.python_2_unicode_compatible # decorator is removed -class C: - def __str__(self): - return u'C()' - -class C(six.Iterator): pass # class C: pass - -class C(six.with_metaclass(M, B)): pass # class C(B, metaclass=M): pass - -@six.add_metaclass(M) # class C(B, metaclass=M): pass -class C(B): pass - -isinstance(..., six.class_types) # isinstance(..., type) -issubclass(..., six.integer_types) # issubclass(..., int) -isinstance(..., six.string_types) # isinstance(..., str) - -six.b('...') # b'...' -six.u('...') # '...' -six.byte2int(bs) # bs[0] -six.indexbytes(bs, i) # bs[i] -six.int2byte(i) # bytes((i,)) -six.iteritems(dct) # dct.items() -six.iterkeys(dct) # dct.keys() -six.itervalues(dct) # dct.values() -next(six.iteritems(dct)) # next(iter(dct.items())) -next(six.iterkeys(dct)) # next(iter(dct.keys())) -next(six.itervalues(dct)) # next(iter(dct.values())) -six.viewitems(dct) # dct.items() -six.viewkeys(dct) # dct.keys() -six.viewvalues(dct) # dct.values() -six.create_unbound_method(fn, cls) # fn -six.get_unbound_function(meth) # meth -six.get_method_function(meth) # meth.__func__ -six.get_method_self(meth) # meth.__self__ -six.get_function_closure(fn) # fn.__closure__ -six.get_function_code(fn) # fn.__code__ -six.get_function_defaults(fn) # fn.__defaults__ -six.get_function_globals(fn) # fn.__globals__ -six.raise_from(exc, exc_from) # raise exc from exc_from -six.reraise(tp, exc, tb) # raise exc.with_traceback(tb) -six.reraise(*sys.exc_info()) # raise -six.assertCountEqual(self, a1, a2) # self.assertCountEqual(a1, a2) -six.assertRaisesRegex(self, e, r, fn) # self.assertRaisesRegex(e, r, fn) -six.assertRegex(self, s, r) # self.assertRegex(s, r) - -# note: only for *literals* -six.ensure_binary('...') # b'...' -six.ensure_str('...') # '...' -six.ensure_text('...') # '...' +```diff +-six.text_type ++str +-six.binary_type ++bytes +-six.class_types ++(type,) +-six.string_types ++(str,) +-six.integer_types ++(int,) +-six.unichr ++chr +-six.iterbytes ++iter +-six.print_(...) ++print(...) +-six.exec_(c, g, l) ++exec(c, g, l) +-six.advance_iterator(it) ++next(it) +-six.next(it) ++next(it) +-six.callable(x) ++callable(x) +-six.moves.range(x) ++range(x) +-six.moves.xrange(x) ++range(x) + + +-from six import text_type +-text_type ++str + +-@six.python_2_unicode_compatible + class C: + def __str__(self): + return u'C()' + +-class C(six.Iterator): pass ++class C: pass + +-class C(six.with_metaclass(M, B)): pass ++class C(B, metaclass=M): pass + +-@six.add_metaclass(M) +-class C(B): pass ++class C(B, metaclass=M): pass + +-isinstance(..., six.class_types) ++isinstance(..., type) +-issubclass(..., six.integer_types) ++issubclass(..., int) +-isinstance(..., six.string_types) ++isinstance(..., str) + +-six.b('...') ++b'...' +-six.u('...') ++'...' +-six.byte2int(bs) ++bs[0] +-six.indexbytes(bs, i) ++bs[i] +-six.int2byte(i) ++bytes((i,)) +-six.iteritems(dct) ++dct.items() +-six.iterkeys(dct) ++dct.keys() +-six.itervalues(dct) ++dct.values() +-next(six.iteritems(dct)) ++next(iter(dct.items())) +-next(six.iterkeys(dct)) ++next(iter(dct.keys())) +-next(six.itervalues(dct)) ++next(iter(dct.values())) +-six.viewitems(dct) ++dct.items() +-six.viewkeys(dct) ++dct.keys() +-six.viewvalues(dct) ++dct.values() +-six.create_unbound_method(fn, cls) ++fn +-six.get_unbound_function(meth) ++meth +-six.get_method_function(meth) ++meth.__func__ +-six.get_method_self(meth) ++meth.__self__ +-six.get_function_closure(fn) ++fn.__closure__ +-six.get_function_code(fn) ++fn.__code__ +-six.get_function_defaults(fn) ++fn.__defaults__ +-six.get_function_globals(fn) ++fn.__globals__ +-six.raise_from(exc, exc_from) ++raise exc from exc_from +-six.reraise(tp, exc, tb) ++raise exc.with_traceback(tb) +-six.reraise(*sys.exc_info()) ++raise +-six.assertCountEqual(self, a1, a2) ++self.assertCountEqual(a1, a2) +-six.assertRaisesRegex(self, e, r, fn) ++self.assertRaisesRegex(e, r, fn) +-six.assertRegex(self, s, r) ++self.assertRegex(s, r) + + # note: only for *literals* +-six.ensure_binary('...') ++b'...' +-six.ensure_str('...') ++'...' +-six.ensure_text('...') ++'...' ``` ### `open` alias @@ -438,14 +545,21 @@ Availability: - `--py3-plus` is passed on the commandline. -```python -open("foo", "U") # open("foo") -open("foo", "Ur") # open("foo") -open("foo", "Ub") # open("foo", "rb") -open("foo", "rUb") # open("foo", "rb") -open("foo", "r") # open("foo") -open("foo", "rt") # open("foo") -open("f", "r", encoding="UTF-8") # open("f", encoding="UTF-8") +```diff +-open("foo", "U") ++open("foo") +-open("foo", "Ur") ++open("foo") +-open("foo", "Ub") ++open("foo", "rb") +-open("foo", "rUb") ++open("foo", "rb") +-open("foo", "r") ++open("foo") +-open("foo", "rt") ++open("foo") +-open("f", "r", encoding="UTF-8") ++open("f", encoding="UTF-8") ``` @@ -560,11 +674,17 @@ Availability: - `--py36-plus` is passed on the commandline. -```python -'{foo} {bar}'.format(foo=foo, bar=bar) # f'{foo} {bar}' -'{} {}'.format(foo, bar) # f'{foo} {bar}' -'{} {}'.format(foo.bar, baz.womp) # f'{foo.bar} {baz.womp}' -'{} {}'.format(f(), g()) # f'{f()} {g()}' +```diff +-'{foo} {bar}'.format(foo=foo, bar=bar) ++f'{foo} {bar}' +-'{} {}'.format(foo, bar) ++f'{foo} {bar}' +-'{} {}'.format(foo.bar, baz.womp) ++f'{foo.bar} {baz.womp}' +-'{} {}'.format(f(), g()) ++f'{f()} {g()}' +-'{x}'.format(**locals()) ++f'{x}' ``` _note_: `pyupgrade` is intentionally timid and will not create an f-string @@ -638,6 +758,19 @@ ``` +### remove unnecessary abspath + +Availability: +- `--py39-plus` is passed on the commandline. + +```diff + from os.path import abspath + +-abspath(__file__) ++__file__ +``` + + ### pep 604 typing rewrites Availability: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_data.py new/pyupgrade-2.31.0/pyupgrade/_data.py --- old/pyupgrade-2.29.1/pyupgrade/_data.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_data.py 2022-01-01 01:43:09.000000000 +0100 @@ -44,6 +44,7 @@ RECORD_FROM_IMPORTS = frozenset(( '__future__', + 'os.path', 'functools', 'mmap', 'select', @@ -116,8 +117,7 @@ def _import_plugins() -> None: - # https://github.com/python/mypy/issues/1422 - plugins_path: str = _plugins.__path__ # type: ignore + plugins_path = _plugins.__path__ mod_infos = pkgutil.walk_packages(plugins_path, f'{_plugins.__name__}.') for _, name, _ in mod_infos: __import__(name, fromlist=['_trash']) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_main.py new/pyupgrade-2.31.0/pyupgrade/_main.py --- old/pyupgrade-2.29.1/pyupgrade/_main.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_main.py 2022-01-01 01:43:09.000000000 +0100 @@ -694,9 +694,9 @@ elif isinstance(node, ast.Attribute): return ''.join((_unparse(node.value), '.', node.attr)) elif isinstance(node, ast.Subscript): - if sys.version_info >= (3, 9): # pragma: no cover (py39+) + if sys.version_info >= (3, 9): # pragma: >=3.9 cover node_slice: ast.expr = node.slice - elif isinstance(node.slice, ast.Index): # pragma: no cover (<py39) + elif isinstance(node.slice, ast.Index): # pragma: <3.9 cover node_slice = node.slice.value else: raise AssertionError(f'expected Slice: {ast.dump(node)}') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_plugins/abspath_file.py new/pyupgrade-2.31.0/pyupgrade/_plugins/abspath_file.py --- old/pyupgrade-2.29.1/pyupgrade/_plugins/abspath_file.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_plugins/abspath_file.py 2022-01-01 01:43:09.000000000 +0100 @@ -0,0 +1,53 @@ +import ast +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._data import register +from pyupgrade._data import State +from pyupgrade._data import TokenFunc +from pyupgrade._token_helpers import find_closing_bracket +from pyupgrade._token_helpers import find_open_paren + + +def _remove_abspath(i: int, tokens: List[Token]) -> None: + paren_start = find_open_paren(tokens, i + 1) + paren_end = find_closing_bracket(tokens, paren_start) + while i <= paren_start: + tokens[i] = Token('PLACEHOLDER', '') + i += 1 + tokens[paren_end] = Token('PLACEHOLDER', '') + + +@register(ast.Call) +def visit_Call( + state: State, + node: ast.Call, + parent: ast.AST, +) -> Iterable[Tuple[Offset, TokenFunc]]: + if ( + state.settings.min_version >= (3, 9) and + ( + ( + isinstance(node.func, ast.Name) and + node.func.id == 'abspath' and + node.func.id in state.from_imports['os.path'] + ) or + ( + isinstance(node.func, ast.Attribute) and + isinstance(node.func.value, ast.Attribute) and + isinstance(node.func.value.value, ast.Name) and + node.func.value.value.id == 'os' and + node.func.value.attr == 'path' and + node.func.attr == 'abspath' + ) + ) and + len(node.args) == 1 and + isinstance(node.args[0], ast.Name) and + node.args[0].id == '__file__' + ): + yield ast_to_offset(node), _remove_abspath diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_plugins/format_locals.py new/pyupgrade-2.31.0/pyupgrade/_plugins/format_locals.py --- old/pyupgrade-2.29.1/pyupgrade/_plugins/format_locals.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_plugins/format_locals.py 2022-01-01 01:43:09.000000000 +0100 @@ -0,0 +1,49 @@ +import ast +from typing import Iterable +from typing import List +from typing import Tuple + +from tokenize_rt import Offset +from tokenize_rt import rfind_string_parts +from tokenize_rt import Token + +from pyupgrade._ast_helpers import ast_to_offset +from pyupgrade._data import register +from pyupgrade._data import State +from pyupgrade._data import TokenFunc +from pyupgrade._token_helpers import find_closing_bracket +from pyupgrade._token_helpers import find_open_paren +from pyupgrade._token_helpers import find_token + + +def _fix(i: int, tokens: List[Token]) -> None: + dot_pos = find_token(tokens, i, '.') + open_pos = find_open_paren(tokens, dot_pos) + close_pos = find_closing_bracket(tokens, open_pos) + for string_idx in rfind_string_parts(tokens, dot_pos - 1): + tok = tokens[string_idx] + tokens[string_idx] = tok._replace(src=f'f{tok.src}') + del tokens[dot_pos:close_pos + 1] + + +@register(ast.Call) +def visit_Call( + state: State, + node: ast.Call, + parent: ast.AST, +) -> Iterable[Tuple[Offset, TokenFunc]]: + if ( + state.settings.min_version >= (3, 6) and + isinstance(node.func, ast.Attribute) and + isinstance(node.func.value, ast.Str) and + node.func.attr == 'format' and + len(node.args) == 0 and + len(node.keywords) == 1 and + node.keywords[0].arg is None and + isinstance(node.keywords[0].value, ast.Call) and + isinstance(node.keywords[0].value.func, ast.Name) and + node.keywords[0].value.func.id == 'locals' and + len(node.keywords[0].value.args) == 0 and + len(node.keywords[0].value.keywords) == 0 + ): + yield ast_to_offset(node), _fix diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_plugins/six_calls.py new/pyupgrade-2.31.0/pyupgrade/_plugins/six_calls.py --- old/pyupgrade-2.29.1/pyupgrade/_plugins/six_calls.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_plugins/six_calls.py 2022-01-01 01:43:09.000000000 +0100 @@ -25,7 +25,7 @@ ast.Await, ast.BinOp, ast.BoolOp, ast.Compare, ast.GeneratorExp, ast.IfExp, ast.Lambda, ast.UnaryOp, ) -if sys.version_info >= (3, 8): # pragma: no cover (py38+) +if sys.version_info >= (3, 8): # pragma: >=3.8 cover _EXPR_NEEDS_PARENS += (ast.NamedExpr,) SIX_CALLS = { @@ -172,13 +172,21 @@ ('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 @@ 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( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_plugins/typing_pep563.py new/pyupgrade-2.31.0/pyupgrade/_plugins/typing_pep563.py --- old/pyupgrade-2.29.1/pyupgrade/_plugins/typing_pep563.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_plugins/typing_pep563.py 2022-01-01 01:43:09.000000000 +0100 @@ -94,18 +94,15 @@ def _process_subscript(node: ast.Subscript) -> Iterable[ast.AST]: name = _get_name(node.value) if name == 'Annotated': - if sys.version_info >= (3, 9): # pragma: no cover (py39+) - node_slice: ast.expr = node.slice - elif isinstance(node.slice, ast.Index): # pragma: no cover (<py39) - node_slice = node.slice.value - else: # pragma: no cover (<py39) - pass # unexpected slice type + if sys.version_info >= (3, 9): # pragma: >=3.9 cover + node_slice = node.slice + elif isinstance(node.slice, ast.Index): # pragma: <3.9 cover + node_slice: ast.AST = node.slice.value + else: # pragma: <3.9 cover + node_slice = node.slice - if isinstance(node_slice, ast.Tuple): - if node_slice.elts: - yield node_slice.elts[0] - else: - raise AssertionError(f'expected ast.Tuple: {ast.dump(node_slice)}') + if isinstance(node_slice, ast.Tuple) and node_slice.elts: + yield node_slice.elts[0] elif name != 'Literal': yield node.slice diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_plugins/typing_pep604.py new/pyupgrade-2.31.0/pyupgrade/_plugins/typing_pep604.py --- old/pyupgrade-2.29.1/pyupgrade/_plugins/typing_pep604.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_plugins/typing_pep604.py 2022-01-01 01:43:09.000000000 +0100 @@ -158,12 +158,15 @@ if is_name_attr(node.value, state.from_imports, 'typing', ('Optional',)): yield ast_to_offset(node), _fix_optional elif is_name_attr(node.value, state.from_imports, 'typing', ('Union',)): - if sys.version_info >= (3, 9): # pragma: no cover (py39+) - node_slice: ast.expr = node.slice - elif isinstance(node.slice, ast.Index): # pragma: no cover (<py39) - node_slice = node.slice.value - else: # pragma: no cover (<py39) - return # unexpected slice type + if sys.version_info >= (3, 9): # pragma: >=3.9 cover + node_slice = node.slice + elif isinstance(node.slice, ast.Index): # pragma: <3.9 cover + node_slice: ast.AST = node.slice.value + else: # pragma: <3.9 cover + node_slice = node.slice # unexpected slice type + + if isinstance(node_slice, ast.Slice): # not a valid annotation + return if isinstance(node_slice, ast.Tuple): if node_slice.elts: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_string_helpers.py new/pyupgrade-2.31.0/pyupgrade/_string_helpers.py --- old/pyupgrade-2.29.1/pyupgrade/_string_helpers.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_string_helpers.py 2022-01-01 01:43:09.000000000 +0100 @@ -3,9 +3,9 @@ import string import sys -if sys.version_info >= (3, 7): # pragma: no cover (py37+) +if sys.version_info >= (3, 7): # pragma: >=3.7 cover is_ascii = str.isascii -else: # pragma: no cover (<py37) +else: # pragma: <3.7 cover def is_ascii(s: str) -> bool: return all(c in string.printable for c in s) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/pyupgrade/_token_helpers.py new/pyupgrade-2.31.0/pyupgrade/_token_helpers.py --- old/pyupgrade-2.29.1/pyupgrade/_token_helpers.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/pyupgrade/_token_helpers.py 2022-01-01 01:43:09.000000000 +0100 @@ -65,14 +65,14 @@ return i -if sys.version_info >= (3, 8): # pragma: no cover (py38+) +if sys.version_info >= (3, 8): # pragma: >=3.8 cover # python 3.8 fixed the offsets of generators / tuples def _arg_token_index(tokens: List[Token], i: int, arg: ast.expr) -> int: idx = _search_until(tokens, i, arg) + 1 while idx < len(tokens) and tokens[idx].name in NON_CODING_TOKENS: idx += 1 return idx -else: # pragma: no cover (<py38) +else: # pragma: <3.8 cover def _arg_token_index(tokens: List[Token], i: int, arg: ast.expr) -> int: # lists containing non-tuples report the first element correctly if isinstance(arg, ast.List): @@ -502,12 +502,12 @@ def find_comprehension_opening_bracket(i: int, tokens: List[Token]) -> int: """Find opening bracket of comprehension given first argument.""" - if sys.version_info < (3, 8): # pragma: no cover (py38+) + if sys.version_info < (3, 8): # pragma: <3.8 cover i -= 1 while not (tokens[i].name == 'OP' and tokens[i].src == '[') and i: i -= 1 return i - else: # pragma: no cover (<py38) + else: # pragma: >=3.8 cover return i diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/requirements-dev.txt new/pyupgrade-2.31.0/requirements-dev.txt --- old/pyupgrade-2.29.1/requirements-dev.txt 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/requirements-dev.txt 2022-01-01 01:43:09.000000000 +0100 @@ -1,3 +1,3 @@ -covdefaults +covdefaults>=2.1.0 coverage pytest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/setup.cfg new/pyupgrade-2.31.0/setup.cfg --- old/pyupgrade-2.29.1/setup.cfg 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/setup.cfg 2022-01-01 01:43:09.000000000 +0100 @@ -1,6 +1,6 @@ [metadata] name = pyupgrade -version = 2.29.1 +version = 2.31.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.29.1/tests/features/abspath_file_test.py new/pyupgrade-2.31.0/tests/features/abspath_file_test.py --- old/pyupgrade-2.29.1/tests/features/abspath_file_test.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyupgrade-2.31.0/tests/features/abspath_file_test.py 2022-01-01 01:43:09.000000000 +0100 @@ -0,0 +1,52 @@ +import pytest + +from pyupgrade._data import Settings +from pyupgrade._main import _fix_plugins + + +@pytest.mark.parametrize( + ('s', 'expected'), + ( + pytest.param( + 'from os.path import abspath\n' + 'abspath(__file__)', + 'from os.path import abspath\n' + '__file__', + id='abspath', + ), + pytest.param( + 'import os\n' + 'os.path.abspath(__file__)', + 'import os\n' + '__file__', + id='os.path.abspath', + ), + ), +) +def test_fix_abspath_file(s, expected): + ret = _fix_plugins(s, settings=Settings(min_version=(3, 9))) + assert ret == expected + + +@pytest.mark.parametrize( + 's, min_version', + ( + pytest.param( + 'abspath(__file__)', + (3, 8), + id='Not Python3.9+', + ), + pytest.param( + 'os.path.abspath(file)', + (3, 9), + id='Abspath of not-__file__', + ), + pytest.param( + 'os.path.abspath(file, foo)', + (3, 9), + id='Garbage (don\'t rewrite)', + ), + ), +) +def test_fix_abspath_file_noop(s, min_version): + assert _fix_plugins(s, settings=Settings(min_version=min_version)) == s diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/tests/features/format_locals_test.py new/pyupgrade-2.31.0/tests/features/format_locals_test.py --- old/pyupgrade-2.29.1/tests/features/format_locals_test.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyupgrade-2.31.0/tests/features/format_locals_test.py 2022-01-01 01:43:09.000000000 +0100 @@ -0,0 +1,53 @@ +import pytest + +from pyupgrade._data import Settings +from pyupgrade._main import _fix_plugins + + +@pytest.mark.parametrize( + ('s', 'version'), + ( + pytest.param( + '"{x}".format(**locals())', + (3,), + id='not 3.6+', + ), + pytest.param( + '"{x} {y}".format(x, **locals())', + (3, 6), + id='mixed locals() and params', + ), + ), +) +def test_fix_format_locals_noop(s, version): + assert _fix_plugins(s, settings=Settings(min_version=version)) == s + + +@pytest.mark.parametrize( + ('s', 'expected'), + ( + pytest.param( + '"{x}".format(**locals())', + 'f"{x}"', + id='normal case', + ), + pytest.param( + '"{x}" "{y}".format(**locals())', + 'f"{x}" f"{y}"', + id='joined strings', + ), + pytest.param( + '(\n' + ' "{x}"\n' + ' "{y}"\n' + ').format(**locals())\n', + '(\n' + ' f"{x}"\n' + ' f"{y}"\n' + ')\n', + id='joined strings with parens', + ), + ), +) +def test_fix_format_locals(s, expected): + assert _fix_plugins(s, settings=Settings(min_version=(3, 6))) == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/tests/features/fstrings_test.py new/pyupgrade-2.31.0/tests/features/fstrings_test.py --- old/pyupgrade-2.29.1/tests/features/fstrings_test.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/tests/features/fstrings_test.py 2022-01-01 01:43:09.000000000 +0100 @@ -61,8 +61,6 @@ r'f"\N{snowman} {a}"', id='named escape sequences', ), - # TODO: poor man's f-strings? - # '"{foo}".format(**locals())' ), ) def test_fix_fstrings(s, expected): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/tests/features/six_test.py new/pyupgrade-2.31.0/tests/features/six_test.py --- old/pyupgrade-2.29.1/tests/features/six_test.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/tests/features/six_test.py 2022-01-01 01:43:09.000000000 +0100 @@ -24,6 +24,7 @@ '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', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/tests/features/typing_pep563_test.py new/pyupgrade-2.31.0/tests/features/typing_pep563_test.py --- old/pyupgrade-2.29.1/tests/features/typing_pep563_test.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/tests/features/typing_pep563_test.py 2022-01-01 01:43:09.000000000 +0100 @@ -57,6 +57,12 @@ (3,), id='Kwonly, untyped', ), + pytest.param( + 'from __future__ import annotations\n' + 'x: Annotated[1:2] = ...\n', + (3,), + id='Annotated with invalid slice', + ), ), ) def test_fix_typing_pep563_noop(s, version): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyupgrade-2.29.1/tests/features/typing_pep604_test.py new/pyupgrade-2.31.0/tests/features/typing_pep604_test.py --- old/pyupgrade-2.29.1/tests/features/typing_pep604_test.py 2021-11-16 02:13:55.000000000 +0100 +++ new/pyupgrade-2.31.0/tests/features/typing_pep604_test.py 2022-01-01 01:43:09.000000000 +0100 @@ -54,6 +54,12 @@ (3, 10), id='3.10+ Union of forward reference', ), + pytest.param( + 'from typing import Union\n' + 'def f() -> Union[1:2]: ...\n', + (3, 10), + id='invalid Union slicing', + ), ), ) def test_fix_pep604_types_noop(s, version):