hhatto / autopep8

A tool that automatically formats Python code to conform to the PEP 8 style guide.
https://pypi.org/project/autopep8/
MIT License
4.56k stars 290 forks source link

KeyError in function continued_indentation #625

Closed karthiknadig closed 3 months ago

karthiknadig commented 2 years ago

I ran into this issue when building formatting server (over Language server protocol) using autopep8 as a formatter. see here https://github.com/hhatto/autopep8/issues/624


Python Code

Let's call this autopep8_test.py

import contextlib
import io
import runpy
import sys

class redirect_io(contextlib.AbstractContextManager):
    def __init__(self, stream, new_target):
        self._stream = stream
        self._new_target = new_target
        # We use a list of old targets to make this CM re-entrant
        self._old_targets = []

    def __enter__(self):
        self._old_targets.append(getattr(sys, self._stream))
        setattr(sys, self._stream, self._new_target)
        return self._new_target

    def __exit__(self, exctype, excinst, exctb):
        setattr(sys, self._stream, self._old_targets.pop())

class formatter_result(object):
    def __init__(self):
        self.stdout = None
        self.stderr = None

class custom_io(io.TextIOWrapper):
    name = None

    def __init__(self, name, encoding="utf-8", newline=None):
        super().__init__(io.BytesIO(), encoding=encoding)
        self.name = name

    def close(self):
        pass

def _format_using_module(content):
    str_output = custom_io("<stdout>")
    str_error = custom_io("<stderr>")

    run_args = ['autopep8', "-"]

    original_argv = sys.argv[:]
    try:
        sys.argv = run_args

        with redirect_io("stdout", str_output):
            with redirect_io("stderr", str_error):
                str_input = custom_io("<input>")
                with redirect_io("stdin", str_input):
                    # Force line endings to be `\n`, this makes the diff
                    # easier to work with
                    str_input.write(content.replace("\r\n", "\n"))
                    str_input.flush()
                    str_input.seek(0)
                    runpy.run_module(
                        'autopep8', run_name="__main__", alter_sys=True
                    )
    except SystemExit:
        pass

    finally:
        sys.argv = original_argv

contents = "import sys;print(sys.argv)"
_format_using_module(contents) # Runs fine
_format_using_module(contents) # This one fails

Command Line and Configuration

None

Command Line

> python autopep8_test.py
Traceback (most recent call last):
  File "c:/GIT/repro/tpi1/conda1/autopep8_test.py", line 72, in <module>
    _format_using_module(contents) # This one fails
  File "c:/GIT/repro/tpi1/conda1/autopep8_test.py", line 61, in _format_using_module
    'autopep8', run_name="__main__", alter_sys=True
  File "c:\GIT\repro\tpi1\conda1\.myenv1\lib\runpy.py", line 205, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "c:\GIT\repro\tpi1\conda1\.myenv1\lib\runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "c:\GIT\repro\tpi1\conda1\.myenv1\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "c:\GIT\repro\tpi1\conda1\.myenv1\lib\site-packages\autopep8.py", line 430, in <module>
    del pycodestyle._checks['logical_line'][pycodestyle.continued_indentation]
KeyError: <function continued_indentation at 0x000001FB175E0828>

Your Environment

hhatto commented 2 years ago

@karthiknadig autopep8 calls pycodestyle.register_check internally, but since pycodestyle.register_check is a destructive function, calling it multiple times as a module results in unexpected behavior. You can work around this problem by running importlib.reload(autopep8). Specifically, in this case, it is to call importlib.reload() between the first and second _format_using_modules.

$ diff -u example.py  example_fix.py
--- example.py  2022-02-27 20:14:46.000000000 +0900
+++ example_fix.py  2022-02-27 20:15:23.000000000 +0900
@@ -1,8 +1,11 @@
 import contextlib
 import io
+import importlib
 import runpy
 import sys

+import autopep8
+

 class redirect_io(contextlib.AbstractContextManager):
     def __init__(self, stream, new_target):
@@ -69,4 +72,5 @@

 contents = "import sys;print(sys.argv)"
 _format_using_module(contents) # Runs fine
+importlib.reload(autopep8)
 _format_using_module(contents) # This one fails
karthiknadig commented 1 year ago

@hhatto Sorry for the delayed response on this. I finally got to working on this. with the latest version of autopep8 running to above code leads to:

Traceback (most recent call last):
  File "c:/GIT/formatters/vscode-autopep8/src/test/python_tests/autopep8_test.py", line 74, in <module>
    _format_using_module(contents) # Runs fine
  File "c:/GIT/formatters/vscode-autopep8/src/test/python_tests/autopep8_test.py", line 64, in _format_using_module
    'autopep8', run_name="__main__", alter_sys=True
  File "C:\all_pys\Python37\lib\runpy.py", line 205, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "C:\all_pys\Python37\lib\runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "C:\all_pys\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "c:\GIT\formatters\vscode-autopep8\.venv\lib\site-packages\autopep8.py", line 419, in <module>   
    del pycodestyle._checks['logical_line'][pycodestyle.continued_indentation]
KeyError: <function continued_indentation at 0x000002113C4178B8>
omega1644 commented 1 year ago

I ran into the same issue, had to restart, delete cache and reload many times but sometimes it doesn't work, is there any alternative solution for this?

karthiknadig commented 1 year ago

@hhatto This is now hitting more frequently. The reload mitigation does not help in some cases (I am still investigating the reason).

agherasim commented 11 months ago

+1 on this issue, happens on a specific file. Any updates on having a fix for this issue, @karthiknadig?

karthiknadig commented 11 months ago

@agherasim I am not the owner of this repo. @hhatto should provide more details.

hhatto commented 3 months ago

this is fixed in v2.1.1+.

Please try again with the latest version.