22
33import ast
44import functools
5+ import itertools
56from typing import Iterable
67from typing import NamedTuple
78
1819from pyupgrade ._token_helpers import find_open_paren
1920from pyupgrade ._token_helpers import parse_call_args
2021
21- U_MODE_REMOVE = frozenset (('U' , 'Ur' , 'rU' , 'r' , 'rt' , 'tr' ))
22- U_MODE_REPLACE_R = frozenset (('Ub' , 'bU' ))
23- U_MODE_REMOVE_U = frozenset (('rUb' , 'Urb' , 'rbU' , 'Ubr' , 'bUr' , 'brU' ))
24- U_MODE_REPLACE = U_MODE_REPLACE_R | U_MODE_REMOVE_U
22+
23+ def _plus (args : tuple [str , ...]) -> tuple [str , ...]:
24+ return args + tuple (f'{ arg } +' for arg in args )
25+
26+
27+ def _permute (* args : str ) -> tuple [str , ...]:
28+ return tuple ('' .join (p ) for s in args for p in itertools .permutations (s ))
29+
30+
31+ MODE_REMOVE = frozenset (_permute ('U' , 'r' , 'rU' , 'rt' ))
32+ MODE_REPLACE_R = frozenset (_permute ('Ub' ))
33+ MODE_REMOVE_T = frozenset (_plus (_permute ('at' , 'rt' , 'wt' , 'xt' )))
34+ MODE_REMOVE_U = frozenset (_permute ('rUb' ))
35+ MODE_REPLACE = MODE_REPLACE_R | MODE_REMOVE_T | MODE_REMOVE_U
2536
2637
2738class FunctionArg (NamedTuple ):
@@ -35,12 +46,15 @@ def _fix_open_mode(i: int, tokens: list[Token], *, arg_idx: int) -> None:
3546 mode = tokens_to_src (tokens [slice (* func_args [arg_idx ])])
3647 mode_stripped = mode .split ('=' )[- 1 ]
3748 mode_stripped = ast .literal_eval (mode_stripped .strip ())
38- if mode_stripped in U_MODE_REMOVE :
49+ if mode_stripped in MODE_REMOVE :
3950 delete_argument (arg_idx , tokens , func_args )
40- elif mode_stripped in U_MODE_REPLACE_R :
51+ elif mode_stripped in MODE_REPLACE_R :
4152 new_mode = mode .replace ('U' , 'r' )
4253 tokens [slice (* func_args [arg_idx ])] = [Token ('SRC' , new_mode )]
43- elif mode_stripped in U_MODE_REMOVE_U :
54+ elif mode_stripped in MODE_REMOVE_T :
55+ new_mode = mode .replace ('t' , '' )
56+ tokens [slice (* func_args [arg_idx ])] = [Token ('SRC' , new_mode )]
57+ elif mode_stripped in MODE_REMOVE_U :
4458 new_mode = mode .replace ('U' , '' )
4559 tokens [slice (* func_args [arg_idx ])] = [Token ('SRC' , new_mode )]
4660 else :
@@ -69,13 +83,10 @@ def visit_Call(
6983 ):
7084 if len (node .args ) >= 2 and isinstance (node .args [1 ], ast .Str ):
7185 if (
72- node .args [1 ].s in U_MODE_REPLACE or
73- (len (node .args ) == 2 and node .args [1 ].s in U_MODE_REMOVE )
86+ node .args [1 ].s in MODE_REPLACE or
87+ (len (node .args ) == 2 and node .args [1 ].s in MODE_REMOVE )
7488 ):
75- func = functools .partial (
76- _fix_open_mode ,
77- arg_idx = 1 ,
78- )
89+ func = functools .partial (_fix_open_mode , arg_idx = 1 )
7990 yield ast_to_offset (node ), func
8091 elif node .keywords and (len (node .keywords ) + len (node .args ) > 1 ):
8192 mode = next (
@@ -90,8 +101,8 @@ def visit_Call(
90101 mode is not None and
91102 isinstance (mode .value , ast .Str ) and
92103 (
93- mode .value .s in U_MODE_REMOVE or
94- mode .value .s in U_MODE_REPLACE
104+ mode .value .s in MODE_REMOVE or
105+ mode .value .s in MODE_REPLACE
95106 )
96107 ):
97108 func = functools .partial (
0 commit comments