Repository: hhatto/autopep8

Issue:
misformatted `type` keyword
---

## Python Code
[musicxml2ly.py](https://github.com/user-attachments/files/16302295/musicxml2ly.py.txt) – this is a very large script, sorry; however, I was not able to simplify it in a meaningful way and still triggering the problem.

## Command Line and Configuration
Command Line
```console
$ autopep8 musicxml2ly.py > musicxml2ly.py.new
```

## Your Environment
* Python version: 3.11
* autopep8 version: 2.1.0 (pycodestyle: 2.11.1)
* Platform: linux

Running `autopep8` on this file and comparing the created output with the original file I see the following differences (among some other, correct fixes).

```diff
--- musicxml2ly.py      2024-07-18 06:12:40.000000000 +0200
+++ musicxml2ly.py.new  2024-07-19 06:33:31.237821934 +0200
@@ -1266,8 +1266,9 @@
     ]
     for (k, func) in attr_dispatch:
         f = None
-        if type(k) == tuple:
-            children1 = attrs.get_named_children(k[0])
+
+
+if isinstance(k,         if )            children1 = attrs.get_named_children(k[0])
             children2 = attrs.get_named_children(k[1])
             if children1 or children2:
                 f = func
@@ -2419,8 +2421,7 @@
                         maybe_dashes_stop_index = n
                         if elem.paired_with is not None:
                             paired = elem.paired_with.spanner_event
-                            if type(paired) == musicexp.DynamicsSpannerEvent:
-                                # Don't add `<words>` at the right of a
+if isinstance(paired,                             if )                                # Don't add `<words>` at the right of a
                                 # dynamics spanner.
                                 state = 'cresc-dim-stop'
                     else:
```

This looks plain wrong to me.

Discussion:
@hhatto: This problem has been fixed in [v2.3.2](https://github.com/hhatto/autopep8/releases/tag/v2.3.2)@lemzwerg: It works just fine, thanks!

Solution patch:
diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index 6534c64..5a74464 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "pypy3.10"]
+        python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "pypy3.10"]
 
     steps:
     - uses: actions/checkout@v4
diff --git a/README.rst b/README.rst
index 559aa19..db92dcf 100644
--- a/README.rst
+++ b/README.rst
@@ -404,7 +404,7 @@ Testing
 Test cases are in ``test/test_autopep8.py``. They can be run directly via
 ``python test/test_autopep8.py`` or via tox_. The latter is useful for
 testing against multiple Python interpreters. (We currently test against
-CPython versions 3.8, 3.9, 3.10, 3.11 and 3.12. We also test against PyPy.)
+CPython versions 3.9, 3.10, 3.11, 3.12 and 3.13. We also test against PyPy.)
 
 .. _`tox`: https://pypi.org/project/tox/
 
diff --git a/autopep8.py b/autopep8.py
index 95b6465..0cdfbfb 100755
--- a/autopep8.py
+++ b/autopep8.py
@@ -1300,6 +1300,7 @@ class FixPEP8(object):
             # NOTE: match objects
             #  * type(a) == type(b)  -> (None, None, 'a', '==')
             #  * str == type(b)      -> ('==', 'b', None, None)
+            #  * type(b) == str      -> (None, None, 'b', '==')
             #  * type("") != type(b) -> (None, None, '""', '!=')
             start = match.start()
             end = match.end()
@@ -1320,6 +1321,10 @@ class FixPEP8(object):
                 isinstance_stmt = " not isinstance"
 
             _type_comp = f"{_target_obj}, {target[:start]}"
+            indent_match = re.match(r'^\s+', target)
+            indent = ""
+            if indent_match:
+                indent = indent_match.group()
 
             _prefix_tmp = target[:start].split()
             if len(_prefix_tmp) >= 1:
@@ -1332,15 +1337,28 @@ class FixPEP8(object):
 
             _suffix_tmp = target[end:]
             _suffix_type_match = TYPE_REGEX.search(_suffix_tmp)
-            if len(_suffix_tmp.split()) >= 1 and _suffix_type_match:
-                if _suffix_type_match:
+            if _suffix_type_match:
+                if len(_suffix_tmp.split()) >= 1:
                     type_match_end = _suffix_type_match.end()
                     _suffix = _suffix_tmp[type_match_end:]
-            if _suffix_type_match:
                 cmp_b = _suffix_type_match.groups()[0]
                 _type_comp = f"{_target_obj}, {cmp_b}"
-
-            fix_line = f"{_prefix}{isinstance_stmt}({_type_comp}){_suffix}"
+            else:
+                _else_suffix_match = re.match(
+                    r"^\s*([^\s:]+)(.*)$",
+                    _suffix_tmp,
+                )
+                if _else_suffix_match:
+                    _else_suffix = _else_suffix_match.group(1)
+                    _else_suffix_other = _else_suffix_match.group(2)
+                    _type_comp = f"{_target_obj}, {_else_suffix}"
+                    _else_suffix_end = _suffix_tmp[_else_suffix_match.end():]
+                    _suffix = f"{_else_suffix_other}{_else_suffix_end}"
+                # `else` route is not care
+
+            fix_line = (
+                f"{indent}{_prefix}{isinstance_stmt}({_type_comp}){_suffix}"
+            )
             self.source[line_index] = fix_line
 
     def fix_e722(self, result):
diff --git a/pyproject.toml b/pyproject.toml
index f242fed..e075da3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,15 +20,15 @@ classifiers = [
     "Operating System :: OS Independent",
     "Programming Language :: Python",
     "Programming Language :: Python :: 3",
-    "Programming Language :: Python :: 3.8",
     "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
     "Topic :: Software Development :: Libraries :: Python Modules",
     "Topic :: Software Development :: Quality Assurance",
 ]
-requires-python = ">=3.8"
+requires-python = ">=3.9"
 dependencies = [
     "pycodestyle >= 2.12.0",
     "tomli; python_version < '3.11'",


Test patch:
diff --git a/test/test_autopep8.py b/test/test_autopep8.py
index 2c206f5..3f8b052 100755
--- a/test/test_autopep8.py
+++ b/test/test_autopep8.py
@@ -4419,6 +4419,18 @@ if role not in ("domaincontroller_master",
         with autopep8_context(line, options=['--aggressive']) as result:
             self.assertEqual(fixed, result)
 
+    def test_e721_in_conditional_pat3(self):
+        line = "if type(res) == str:\n    pass\n"
+        fixed = "if isinstance(res, str):\n    pass\n"
+        with autopep8_context(line, options=['--aggressive']) as result:
+            self.assertEqual(fixed, result)
+
+    def test_e721_in_conditional_with_indent(self):
+        line = "if True:\n    if str == type(''):\n        pass\n"
+        fixed = "if True:\n    if isinstance('', str):\n        pass\n"
+        with autopep8_context(line, options=['--aggressive']) as result:
+            self.assertEqual(fixed, result)
+
     def test_e721_in_not_conditional(self):
         line = "if type(res) != type(''):\n    pass\n"
         fixed = "if not isinstance(res, type('')):\n    pass\n"