Repository: asottile/pyupgrade

Issue:
six.text_type('') takes two passes to resolve
For example:

```console
$ pip freeze | grep pyupgrade
pyupgrade==1.26.1
$ cat 1.py
six.text_type('')
$ pyupgrade 1.py --py3-plus
Rewriting 1.py
$ cat 1.py
str('')
$ pyupgrade 1.py --py3-plus
Rewriting 1.py
$ cat 1.py
''
$ pyupgrade 1.py --py3-plus
$
```

Perhaps rewrite directly to `''`?

Discussion:

Patch:
diff --git a/pyupgrade.py b/pyupgrade.py
index 4a0f9c1..6f99694 100644
--- a/pyupgrade.py
+++ b/pyupgrade.py
@@ -1140,6 +1140,7 @@ WITH_METACLASS_BASES_TMPL = '{rest}, metaclass={args[0]}'
 RAISE_FROM_TMPL = 'raise {args[0]} from {rest}'
 RERAISE_2_TMPL = 'raise {args[1]}.with_traceback(None)'
 RERAISE_3_TMPL = 'raise {args[1]}.with_traceback({args[2]})'
+SIX_NATIVE_STR = frozenset(('ensure_str', 'ensure_text', 'text_type'))
 
 
 def _all_isinstance(vals, tp):
@@ -1473,7 +1474,7 @@ class FindPy3Plus(ast.NodeVisitor):
             self.super_calls[_ast_to_offset(node)] = node
         elif (
                 (
-                    self._is_six(node.func, ('ensure_str', 'ensure_text')) or
+                    self._is_six(node.func, SIX_NATIVE_STR) or
                     isinstance(node.func, ast.Name) and node.func.id == 'str'
                 ) and
                 not node.keywords and
@@ -1953,6 +1954,15 @@ def _fix_py3_plus(contents_text):  # type: (str) -> str
                 if_block, else_block = _find_if_else_block(tokens, j)
                 del tokens[if_block.end:else_block.end]
                 if_block.replace_condition(tokens, [Token('NAME', 'else')])
+        elif token.offset in visitor.native_literals:
+            j = _find_open_paren(tokens, i)
+            func_args, end = _parse_call_args(tokens, j)
+            if any(tok.name == 'NL' for tok in tokens[i:end]):
+                continue
+            if func_args:
+                _replace_call(tokens, i, end, func_args, '{args[0]}')
+            else:
+                tokens[i:end] = [token._replace(name='STRING', src="''")]
         elif token.offset in visitor.six_type_ctx:
             _replace(i, SIX_TYPE_CTX_ATTRS, visitor.six_type_ctx[token.offset])
         elif token.offset in visitor.six_simple:
@@ -2042,15 +2052,6 @@ def _fix_py3_plus(contents_text):  # type: (str) -> str
             call = visitor.encode_calls[token.offset]
             victims = _victims(tokens, i, call, gen=False)
             del tokens[victims.starts[0] + 1:victims.ends[-1]]
-        elif token.offset in visitor.native_literals:
-            j = _find_open_paren(tokens, i)
-            func_args, end = _parse_call_args(tokens, j)
-            if any(tok.name == 'NL' for tok in tokens[i:end]):
-                continue
-            if func_args:
-                _replace_call(tokens, i, end, func_args, '{args[0]}')
-            else:
-                tokens[i:end] = [token._replace(name='STRING', src="''")]
         elif token.offset in visitor.io_open_calls:
             j = _find_open_paren(tokens, i)
             tokens[i:j] = [token._replace(name='NAME', src='open')]


Test patch:
diff --git a/tests/native_literals_test.py b/tests/native_literals_test.py
index 7b6c8f0..7f52126 100644
--- a/tests/native_literals_test.py
+++ b/tests/native_literals_test.py
@@ -29,6 +29,7 @@ def test_fix_native_literals_noop(s):
         ('str("""\nfoo""")', '"""\nfoo"""'),
         ('six.ensure_str("foo")', '"foo"'),
         ('six.ensure_text("foo")', '"foo"'),
+        ('six.text_type("foo")', '"foo"'),
     ),
 )
 def test_fix_native_literals(s, expected):